mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-21 06:00:28 +02:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4687b24e2 | ||
|
|
9d65e7e63f | ||
|
|
f3ce8c3bf0 | ||
|
|
bce5c2b560 |
No files matched your search
@@ -24,8 +24,8 @@ named where code is taken over.
|
||||
| Scores | The menu button held shows the scores on the Quest (a press pauses, as it is let go); the Steam Frame's View button as before; the text chat's lines where the desktop has them (2026-10-18) |
|
||||
| Comfort | Snap or smooth turning, a comfort vignette in vehicles, VR options in the main and pause menus (Y), a page of every controller button |
|
||||
| Menus | The PC version's menus in VR (`vr.menus`), clicked with a laser; text typed on the on-screen keyboard |
|
||||
| Multiplayer | Up to date with OpenCE (network version 29, 2026-10-14): cross-play, its server browser, co-op campaign, password lobbies; join codes and the VR online list |
|
||||
| Bots | Bots in multiplayer: on foot (paths, items, objectives), in vehicles and at turrets; they make room for a joining person, on the LAN too (a lobby they fill is advertised open, 2026-10-18) |
|
||||
| Multiplayer | Up to date with OpenCE (network version 32, 2026-10-20; HDR kept off in VR): cross-play, its server browser, co-op campaign, password lobbies; join codes and the VR online list |
|
||||
| Bots | Bots in multiplayer (`host_bots.c`; OpenCE's own, `bots.c`, are local split screen games' and off in VR): on foot (paths, items, objectives), in vehicles and at turrets; they make room for a joining person, on the LAN too (2026-10-18); they climb ladders (Damnation's) and take paths as near their goal as a search reaches, as OpenCE's do: on Damnation 0 searches of 1747 failed, 1078 of 1605 before (2026-10-20). Later: OpenCE's bots' aiming (lead, bursts, taps for the pistol and sniper rifle, field of view), learnt obstacles and map fixes into ours |
|
||||
| Voice chat | Push to talk on any button (`vr.button_talk`, BUTTONS page; the free hand's grip by default, sending nothing while it reaches for the foregrip or the wheel), open mic, off (2026-10-13) |
|
||||
| Builds | The Android build's assembly conversion (`tools/android_asm_convert.py`) keeps string data as it is: strings and tables starting with "_" or holding \" lost a "_" (Opus's pulse cache among them: the Steam Frame's crashes in online games while talking), 2026-10-13 |
|
||||
| Languages | The European disc in Spanish and Italian (a maps folder for each, `maps_es` and `maps_it`, none called `maps`) imported on the Quest and the Steam Frame: the headset's language's, its language the game's and the menus' (`maps/language.txt`, unless `game.language` is set), 2026-10-17; not yet tried with that disc |
|
||||
|
||||
+251
-6
@@ -67,6 +67,9 @@ nearest person on its team.
|
||||
#include "game/player_queues_new.h"
|
||||
#include "objects/objects.h"
|
||||
#include "physics/collisions.h"
|
||||
#include "physics/bsp3d.h"
|
||||
#include "physics/collision_bsp.h"
|
||||
#include "physics/collision_bsp_definitions.h"
|
||||
#include "ai/path.h"
|
||||
#include "units/bipeds.h"
|
||||
#include "items/items.h"
|
||||
@@ -127,6 +130,15 @@ enum
|
||||
struct bot
|
||||
{
|
||||
long player_index;
|
||||
/* the ladder it climbs (bot_ladders: its index and one, 0 none), how
|
||||
long it has, how long past its top, and when it may take one again */
|
||||
short ladder_plus_one;
|
||||
/* its path ends short of its goal (no way all the way: the search's
|
||||
nearest) */
|
||||
boolean path_partial;
|
||||
short ladder_ticks;
|
||||
short ladder_top_ticks;
|
||||
long ladder_again_time;
|
||||
long target_unit_index;
|
||||
short target_seen_ticks;
|
||||
real yaw;
|
||||
@@ -240,6 +252,11 @@ static struct
|
||||
{
|
||||
boolean map_logged;
|
||||
boolean vehicles_logged;
|
||||
/* the level's ladders (bot_ladders_find), found as the game began */
|
||||
boolean ladders_found;
|
||||
short ladder_count;
|
||||
long climbs;
|
||||
long partial_paths;
|
||||
struct bot bots[MAXIMUM_BOTS];
|
||||
long seed;
|
||||
long logged_time;
|
||||
@@ -497,7 +514,9 @@ static boolean bot_find_path(
|
||||
/* (the goal's surface: under it, as a biped would stand there) */
|
||||
{
|
||||
struct collision_result collision;
|
||||
real_vector3d down = { 0.0f, 0.0f, -3.0f };
|
||||
/* (as far down as OpenCE's bots look, bots.c: a goal over a drop
|
||||
was none) */
|
||||
real_vector3d down = { 0.0f, 0.0f, -6.0f };
|
||||
real_point3d above = goal;
|
||||
|
||||
above.z += 0.5f;
|
||||
@@ -520,8 +539,17 @@ static boolean bot_find_path(
|
||||
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) &&
|
||||
/* (to as near the goal as the search reaches, as OpenCE's bots do,
|
||||
bots.c: a long way across a large level is more surfaces than a search
|
||||
holds, and a goal up where no walkable surface leads, as up a ladder,
|
||||
is reached nearer; the bot searches again from there) */
|
||||
path_state_destination(&bot_path_state, &goal, goal_surface, 1000.0f);
|
||||
path_state_find(&bot_path_state);
|
||||
bot->path_partial = bot_path_state.node_count > 0 &&
|
||||
path_node_from_hash_table(&bot_path_state, goal_surface) == NONE;
|
||||
if (bot->path_partial)
|
||||
bots.partial_paths++;
|
||||
if (bot_path_state.node_count > 0 && !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)
|
||||
{
|
||||
@@ -2332,6 +2360,192 @@ static void bot_walk_to(
|
||||
action->throttle.j = -dx * (real)sin(yaw) + dy * (real)cos(yaw);
|
||||
}
|
||||
|
||||
/* ---------- ladders
|
||||
|
||||
The level's ladders (Damnation's): surfaces it marks climbable, which a
|
||||
biped walking into, facing them, climbs (bipeds.c), and which the game's
|
||||
pathfinding does not take. A bot whose way is not found, or that is stuck,
|
||||
with the foot of one going up near it, climbs it: walks to the foot, turns
|
||||
to it and walks into it, and past its top a little; then it finds its way
|
||||
anew. Each ladder: the climbable surfaces of a near place and facing,
|
||||
merged. */
|
||||
|
||||
#define BOT_LADDERS 64
|
||||
/* collision_surface flags' climbable bit (collision_bsp.c) */
|
||||
#define BOT_SURFACE_CLIMBABLE 0x04
|
||||
/* how near its foot a bot takes a ladder (world units), and how long it
|
||||
tries before giving up on it (ticks), and on ladders a while */
|
||||
#define BOT_LADDER_RANGE 8.0f
|
||||
#define BOT_LADDER_TIMEOUT_TICKS 300
|
||||
#define BOT_LADDER_AGAIN_TICKS 450
|
||||
|
||||
static struct bot_ladder
|
||||
{
|
||||
/* the climbable surfaces' bounds, and the way into them (flat, unit) */
|
||||
real_point3d low, high;
|
||||
real into_x, into_y;
|
||||
} bot_ladders[BOT_LADDERS];
|
||||
|
||||
static void bot_ladders_find(void)
|
||||
{
|
||||
struct collision_bsp *bsp = global_collision_bsp_get();
|
||||
long surface_index;
|
||||
|
||||
bots.ladders_found = TRUE;
|
||||
bots.ladder_count = 0;
|
||||
if (!bsp)
|
||||
return;
|
||||
for (surface_index = 0; surface_index < bsp->surfaces.count; surface_index++)
|
||||
{
|
||||
struct collision_surface const *surface = TAG_BLOCK_GET_ELEMENT(&bsp->surfaces, surface_index,
|
||||
struct collision_surface);
|
||||
real_point3d points[MAXIMUM_VERTICES_PER_COLLISION_SURFACE];
|
||||
real_point3d low, high;
|
||||
real_plane3d plane;
|
||||
real flat;
|
||||
short count, point, ladder;
|
||||
|
||||
if (!(surface->flags & BOT_SURFACE_CLIMBABLE))
|
||||
continue;
|
||||
bsp3d_get_plane_from_designator(&bsp->bsp3d, surface->plane_designator, &plane);
|
||||
/* (upright: a ladder's face, not its rungs' tops) */
|
||||
flat = (real)sqrt(plane.n.i * plane.n.i + plane.n.j * plane.n.j);
|
||||
if (flat < 0.7f)
|
||||
continue;
|
||||
count = collision_surface_polygon(bsp, surface_index, points);
|
||||
if (count < 3)
|
||||
continue;
|
||||
low = high = points[0];
|
||||
for (point = 1; point < count; point++)
|
||||
{
|
||||
low.x = MIN(low.x, points[point].x);
|
||||
low.y = MIN(low.y, points[point].y);
|
||||
low.z = MIN(low.z, points[point].z);
|
||||
high.x = MAX(high.x, points[point].x);
|
||||
high.y = MAX(high.y, points[point].y);
|
||||
high.z = MAX(high.z, points[point].z);
|
||||
}
|
||||
/* (one of a ladder found already, near and facing alike: merged) */
|
||||
for (ladder = 0; ladder < bots.ladder_count; ladder++)
|
||||
{
|
||||
struct bot_ladder *found = &bot_ladders[ladder];
|
||||
|
||||
if (low.x < found->high.x + 0.5f && high.x > found->low.x - 0.5f &&
|
||||
low.y < found->high.y + 0.5f && high.y > found->low.y - 0.5f &&
|
||||
low.z < found->high.z + 0.5f && high.z > found->low.z - 0.5f &&
|
||||
found->into_x * -plane.n.i / flat + found->into_y * -plane.n.j / flat > 0.8f)
|
||||
{
|
||||
found->low.x = MIN(found->low.x, low.x);
|
||||
found->low.y = MIN(found->low.y, low.y);
|
||||
found->low.z = MIN(found->low.z, low.z);
|
||||
found->high.x = MAX(found->high.x, high.x);
|
||||
found->high.y = MAX(found->high.y, high.y);
|
||||
found->high.z = MAX(found->high.z, high.z);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ladder < bots.ladder_count || bots.ladder_count >= BOT_LADDERS)
|
||||
continue;
|
||||
bot_ladders[bots.ladder_count].low = low;
|
||||
bot_ladders[bots.ladder_count].high = high;
|
||||
bot_ladders[bots.ladder_count].into_x = -plane.n.i / flat;
|
||||
bot_ladders[bots.ladder_count].into_y = -plane.n.j / flat;
|
||||
bots.ladder_count++;
|
||||
}
|
||||
if (bots.ladder_count)
|
||||
platform_log("bots: %d ladders on this map", bots.ladder_count);
|
||||
}
|
||||
|
||||
/* a ladder's foot, before it on the floor its bottom is at */
|
||||
static void bot_ladder_foot(
|
||||
struct bot_ladder const *ladder,
|
||||
real_point3d *foot)
|
||||
{
|
||||
foot->x = (ladder->low.x + ladder->high.x) * 0.5f - ladder->into_x * 0.4f;
|
||||
foot->y = (ladder->low.y + ladder->high.y) * 0.5f - ladder->into_y * 0.4f;
|
||||
foot->z = ladder->low.z;
|
||||
}
|
||||
|
||||
/* the nearest ladder going up from where the bot stands, its foot in reach;
|
||||
NONE */
|
||||
static short bot_ladder_near(
|
||||
real_point3d const *position)
|
||||
{
|
||||
short ladder, best = NONE;
|
||||
real best_distance = BOT_LADDER_RANGE;
|
||||
|
||||
if (!bots.ladders_found)
|
||||
bot_ladders_find();
|
||||
for (ladder = 0; ladder < bots.ladder_count; ladder++)
|
||||
{
|
||||
real_point3d foot;
|
||||
real distance;
|
||||
|
||||
bot_ladder_foot(&bot_ladders[ladder], &foot);
|
||||
/* (its foot about the floor the bot is on, its top well above) */
|
||||
if (fabsf(foot.z - position->z) > 1.0f || bot_ladders[ladder].high.z < position->z + 0.8f)
|
||||
continue;
|
||||
distance = (real)sqrt((foot.x - position->x) * (foot.x - position->x) +
|
||||
(foot.y - position->y) * (foot.y - position->y));
|
||||
if (distance < best_distance)
|
||||
{
|
||||
best_distance = distance;
|
||||
best = ladder;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
/* climbing: the way it walks and faces this tick; FALSE once it is up (or
|
||||
gives up), its way to be found anew */
|
||||
static boolean bot_climb(
|
||||
struct bot *bot,
|
||||
real_point3d const *position,
|
||||
struct player_action *action,
|
||||
real *wanted_yaw)
|
||||
{
|
||||
struct bot_ladder const *ladder = &bot_ladders[bot->ladder_plus_one - 1];
|
||||
real_point3d foot;
|
||||
real dx, dy, distance;
|
||||
|
||||
bot_ladder_foot(ladder, &foot);
|
||||
dx = foot.x - position->x;
|
||||
dy = foot.y - position->y;
|
||||
distance = (real)sqrt(dx * dx + dy * dy);
|
||||
if (++bot->ladder_ticks > BOT_LADDER_TIMEOUT_TICKS)
|
||||
{
|
||||
bot->ladder_again_time = game_time_get() + BOT_LADDER_AGAIN_TICKS;
|
||||
return FALSE;
|
||||
}
|
||||
/* (up: on past the top a moment, onto the floor there) */
|
||||
if (position->z > ladder->high.z - 0.25f || bot->ladder_top_ticks > 0)
|
||||
{
|
||||
if (++bot->ladder_top_ticks > 20)
|
||||
{
|
||||
bots.climbs++;
|
||||
bot->ladder_again_time = game_time_get() + 90;
|
||||
return FALSE;
|
||||
}
|
||||
*wanted_yaw = (real)atan2(ladder->into_y, ladder->into_x);
|
||||
action->throttle.i = 1.0f;
|
||||
action->throttle.j = 0.0f;
|
||||
return TRUE;
|
||||
}
|
||||
/* (to its foot, then into it, facing it) */
|
||||
if (distance > 0.35f && position->z < ladder->low.z + 0.4f)
|
||||
{
|
||||
*wanted_yaw = (real)atan2(dy, dx);
|
||||
bot_walk_to(action, bot->yaw, position, &foot);
|
||||
return TRUE;
|
||||
}
|
||||
*wanted_yaw = (real)atan2(ladder->into_y, ladder->into_x);
|
||||
action->throttle.i = (real)cos(bot->yaw - *wanted_yaw);
|
||||
action->throttle.j = 0.0f;
|
||||
if (action->throttle.i < 0.3f)
|
||||
action->throttle.i = 0.3f;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void bot_think(
|
||||
struct bot *bot,
|
||||
struct player_datum *player,
|
||||
@@ -2751,6 +2965,35 @@ static void bot_think(
|
||||
bot->has_goal = TRUE;
|
||||
bot->goal_time = game_time_get();
|
||||
}
|
||||
/* (no way found, or stuck, by a ladder going up: up it) */
|
||||
if (!bot->ladder_plus_one &&
|
||||
(bot->path_failures >= 2 || bot->stuck_ticks >= 6 ||
|
||||
(bot->path_partial && bot->has_goal && bot->goal.z > position.z + 0.8f)) &&
|
||||
(game_time_get() >= bot->ladder_again_time || game_time_get() < bot->ladder_again_time - 1000))
|
||||
{
|
||||
short ladder = bot_ladder_near(&position);
|
||||
|
||||
if (ladder != NONE)
|
||||
{
|
||||
bot->ladder_plus_one = (short)(ladder + 1);
|
||||
bot->ladder_ticks = 0;
|
||||
bot->ladder_top_ticks = 0;
|
||||
}
|
||||
}
|
||||
if (bot->ladder_plus_one)
|
||||
{
|
||||
if (bot_climb(bot, &position, action, &wanted_yaw))
|
||||
{
|
||||
bot->stuck_ticks = 0;
|
||||
bot->sidestep_ticks = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
bot->ladder_plus_one = 0;
|
||||
bot->path_failures = 0;
|
||||
bot->path_time = game_time_get() - PATH_REFRESH_TICKS;
|
||||
}
|
||||
}
|
||||
if (bot->stuck_ticks > 20)
|
||||
{
|
||||
/* (stuck: somewhere else) */
|
||||
@@ -2954,6 +3197,8 @@ void bots_new_game(
|
||||
|
||||
bots.map_logged = FALSE;
|
||||
bots.vehicles_logged = FALSE;
|
||||
bots.ladders_found = FALSE;
|
||||
bots.ladder_count = 0;
|
||||
for (index = 0; index < MAXIMUM_BOTS; index++)
|
||||
bots.bots[index].player_index = NONE;
|
||||
bots.seed = 12345;
|
||||
@@ -3025,12 +3270,12 @@ void bots_tick(
|
||||
}
|
||||
}
|
||||
bots.logged_time = game_time_get();
|
||||
platform_log("bots: driving %d; paths found %ld, not %ld; weapon swaps asked %ld; grenades %ld; changes %ld; stuck %ld, long %ld, gave up %ld (no start %ld, no goal %ld, no way %ld); vehicles: in %ld, out %ld",
|
||||
(int)count, bots.paths_found, bots.paths_failed, bots.pickups, bots.grenades, bots.switches, bots.stucks, bots.long_stucks, bots.gave_up, bots.fail_start, bots.fail_goal, bots.fail_search,
|
||||
platform_log("bots: driving %d; paths found %ld, not %ld; weapon swaps asked %ld; grenades %ld; changes %ld; stuck %ld, long %ld, gave up %ld (no start %ld, no goal %ld, no way %ld); ladders climbed %ld; partial paths %ld; vehicles: in %ld, out %ld",
|
||||
(int)count, bots.paths_found, bots.paths_failed, bots.pickups, bots.grenades, bots.switches, bots.stucks, bots.long_stucks, bots.gave_up, bots.fail_start, bots.fail_goal, bots.fail_search, bots.climbs, bots.partial_paths,
|
||||
bots.vehicle_entries, bots.vehicle_exits);
|
||||
bots.vehicle_entries = bots.vehicle_exits = 0;
|
||||
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 = bots.gave_up = 0;
|
||||
bots.paths_found = bots.paths_failed = bots.pickups = bots.grenades = bots.switches = bots.stucks = bots.long_stucks = bots.gave_up = bots.climbs = bots.partial_paths = 0;
|
||||
for (index = 0; index < MAXIMUM_TRACKED_PLAYERS; index++)
|
||||
{
|
||||
struct player_datum *player = distributed_player(index);
|
||||
|
||||
Reference in new issue
Block a user