Compare commits

..
Author SHA1 Message Date
Stefan MaierandClaude Opus 5.5 d8034d37fd Raise the rocket launcher's scope disc 3.5 cm more (18 cm)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:03:19 +02:00
Stefan MaierandClaude Opus 5.5 23d14319a7 Raise the rocket launcher's scope disc 3.5 cm more
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 18:14:34 +02:00
Stefan MaierandClaude Opus 5.5 ff5b91f120 Raise the VR scope's disc over the rocket launcher's tube
The disc sits 7 cm over the hand, where a rifle's or pistol's scope is;
over the rocket launcher, carried higher on the shoulder, it was inside
the tube. A weapon may raise it (halo_vr_set_scope_raise): the rocket
launcher by 11 cm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 18:07:26 +02:00
10 changed files with 37 additions and 1908 deletions

No files matched your search

File diff suppressed because it is too large. Load diff
-2
View File
@@ -3051,7 +3051,6 @@ void network_distributed_new_game(
network_objects_new_game();
network_damage_new_game();
network_vr_new_game();
bots_new_game();
}
/* after each tick (game_time.c) */
@@ -3094,7 +3093,6 @@ void network_distributed_tick(
distributed_statistics_due = FALSE;
distributed_host_send_players();
network_vr_tick();
bots_tick();
distributed_send_pickups();
if (game_time_get() % GAME_STATE_INTERVAL_TICKS == 0)
distributed_send_game_state(NONE);
-4
View File
@@ -236,8 +236,4 @@ void network_vr_tick(void);
word network_vr_entry_size(void);
void network_vr_handle_poses(long machine_index, void const *entries, short count);
/* bots (bots.c) */
void bots_new_game(void);
void bots_tick(void);
#endif // __NETWORK_DISTRIBUTED_H
+3
View File
@@ -140,6 +140,9 @@ out (halo_vr_scope_pass). */
int halo_vr_scope(short local_player_index, float magnification, float position[3], float forward[3], float up[3],
float *vertical_field_of_view);
int halo_vr_scope_pass(void);
/* how much higher than a rifle's the weapon in hand has its scope's disc
(metres; render.c, before halo_vr_scope) */
void halo_vr_set_scope_raise(float metres);
void halo_vr_capture_scope(float left, float top, float right, float bottom);
/* where the weapon's gun node is as drawn, at the ready (world units;
first_person_weapons.c, each frame): shots, the crosshair and the laser
+5 -32
View File
@@ -304,12 +304,6 @@ static const struct config_setting config_settings[] =
"The Discord application internet play invites go through while the\n"
"Discord desktop client runs; empty for none." },
{ "bots.count", _config_integer, "0", "HALO_BOTS_COUNT", _environment_value, _platform_android,
"Bots in the multiplayer games this machine hosts (0 to 15; 0: none):\n"
"players it drives itself, which every machine sees as players. Set in\n"
"the lobby's host options (Y)." },
{ "bots.skill", _config_integer, "1", "HALO_BOTS_SKILL", _environment_value, _platform_android,
"How well the bots play: 0 easy, 1 normal, 2 hard." },
{ "update.auto", _config_boolean, "true", "HALO_UPDATE_AUTO", _environment_value, _platform_all,
"Look for a new version when the game starts, and offer to update to it;\n"
"false never looks (the game's \"Do not ask again\" writes false here)." },
@@ -959,9 +953,7 @@ static int config_line_section(const char *line, const char *end, char *section,
/* sets a boolean setting, for now and in config.toml: its line there is
changed (or added), the rest of the file kept as it is */
/* a boolean's or an integer's new value, kept and written into the file
(the line of its key replaced, else added to its section) */
static int config_write_value(const char *name, int type, long value)
int config_write_boolean(const char *name, int value)
{
const char *dot = strchr(name, '.');
long index = config_setting_index(name);
@@ -972,24 +964,15 @@ static int config_write_value(const char *name, int type, long value)
const char *line;
int written = 0, in_section = 0, succeeded;
if (index < 0 || config_settings[index].type != type || !dot || (size_t)(dot - name) >= sizeof(section))
if (index < 0 || config_settings[index].type != _config_boolean || !dot || (size_t)(dot - name) >= sizeof(section))
return 0;
/* (the file read first, as the other settings are) */
if (type == _config_boolean)
config_boolean(name);
else
config_integer(name);
config_boolean(name);
pthread_mutex_lock(&config_lock);
if (type == _config_boolean)
config_values[index].boolean = value != 0;
else
config_values[index].integer = value;
config_values[index].boolean = value != 0;
snprintf(section, sizeof(section), "%.*s", (int)(dot - name), name);
snprintf(key, sizeof(key), "%s", dot + 1);
if (type == _config_boolean)
snprintf(line_text, sizeof(line_text), "%s = %s\n", key, value ? "true" : "false");
else
snprintf(line_text, sizeof(line_text), "%s = %ld\n", key, value);
snprintf(line_text, sizeof(line_text), "%s = %s\n", key, value ? "true" : "false");
snprintf(wanted, sizeof(wanted), "%s", section);
config_path(path, sizeof(path));
text = config_read_file(path, &size);
@@ -1046,16 +1029,6 @@ static int config_write_value(const char *name, int type, long value)
return succeeded;
}
int config_write_boolean(const char *name, int value)
{
return config_write_value(name, _config_boolean, value != 0);
}
int config_write_integer(const char *name, long value)
{
return config_write_value(name, _config_integer, value);
}
/* ---------- public code */
int config_boolean(const char *name)
-1
View File
@@ -21,6 +21,5 @@ const char *config_string(const char *name);
/* sets a boolean setting, and writes it into config.toml (only its line
changes); 1 on success */
int config_write_boolean(const char *name, int value);
int config_write_integer(const char *name, long value);
#endif
+10 -1
View File
@@ -1957,6 +1957,14 @@ The world around it stays as it is (zooming the whole view sickens). */
#define SCOPE_ABOVE_HAND 0.07f
#define SCOPE_AHEAD_OF_HAND 0.22f
/* (the weapon in hand's: halo_vr_set_scope_raise) */
static float scope_raise;
void halo_vr_set_scope_raise(float metres)
{
scope_raise = metres;
}
int halo_vr_scope(short local_player_index, float magnification, float position[3], float forward[3], float up[3],
float *vertical_field_of_view)
{
@@ -1975,7 +1983,7 @@ int halo_vr_scope(short local_player_index, float magnification, float position[
game_to_local(right, local_right);
for (axis = 0; axis < 3; axis++)
{
vr.scope_pose.position[axis] = hand->aim.position[axis] + local_up[axis] * SCOPE_ABOVE_HAND +
vr.scope_pose.position[axis] = hand->aim.position[axis] + local_up[axis] * (SCOPE_ABOVE_HAND + scope_raise) +
local_forward[axis] * SCOPE_AHEAD_OF_HAND;
}
if (barrel_known())
@@ -2167,6 +2175,7 @@ int halo_vr_scope(short local_player_index, float magnification, float position[
return 0;
}
int halo_vr_scope_pass(void) { return 0; }
void halo_vr_set_scope_raise(float metres) { (void)metres; }
void halo_vr_capture_scope(float left, float top, float right, float bottom)
{
(void)left; (void)top; (void)right; (void)bottom;
+8 -149
View File
@@ -5643,8 +5643,6 @@ struct ui_public_game
void p2p_set_public_game(int public_game);
int p2p_public_game(void);
int p2p_public_games(struct ui_public_game *games, int maximum);
long config_integer(char const *name);
int config_write_integer(char const *name, long value);
enum
{
@@ -5663,18 +5661,8 @@ static struct
short scroll;
short game_count;
struct ui_public_game games[MAXIMUM_SHOWN_PUBLIC_GAMES];
/* the host options panel, over a hosted game's lobby */
boolean host_panel_open;
short host_selection;
} ui_join_code_globals;
enum
{
/* the host options: public, bots, their skill */
HOST_PANEL_ROWS = 3,
MAXIMUM_BOTS_SHOWN = 15,
};
/* the multiplayer maps' names, as the menus show them */
static char const *ui_join_code_map_name(
char const *map)
@@ -5774,94 +5762,15 @@ static void ui_join_code_panel_event(
}
}
/* the host options panel's input: the stick or the pad's arrows up and
down choose a row, left and right (or A) change it, B or Y closes it */
static void ui_join_code_host_panel_event(
struct event_record const *event)
{
short move = 0;
short change = 0;
if (event->type == _event_type_left_stick)
{
if (event->data.stick.y == SHORT_MAX)
move = -1;
else if (event->data.stick.y == SHORT_MIN)
move = 1;
else if (event->data.stick.x == SHORT_MAX)
change = 1;
else if (event->data.stick.x == SHORT_MIN)
change = -1;
}
else if (event->type == _event_type_button && event->data.button.value == 1)
{
switch (event->data.button.index)
{
case _gamepad_binary_button_dpad_up: move = -1; break;
case _gamepad_binary_button_dpad_down: move = 1; break;
case _gamepad_binary_button_dpad_right:
case _gamepad_analog_button_a:
change = 1;
break;
case _gamepad_binary_button_dpad_left: change = -1; break;
case _gamepad_analog_button_b:
case _gamepad_analog_button_y:
case _gamepad_binary_button_back:
ui_join_code_globals.host_panel_open = FALSE;
break;
}
}
if (move)
ui_join_code_globals.host_selection = (short)PIN(ui_join_code_globals.host_selection + move, 0, HOST_PANEL_ROWS - 1);
if (!change)
return;
switch (ui_join_code_globals.host_selection)
{
case 0:
p2p_set_public_game(!p2p_public_game());
break;
case 1:
{
/* (A goes round: past the most, none) */
long count = config_integer("bots.count") + change;
if (count > MAXIMUM_BOTS_SHOWN)
count = event->type == _event_type_button && event->data.button.index == _gamepad_analog_button_a ? 0 :
MAXIMUM_BOTS_SHOWN;
config_write_integer("bots.count", PIN(count, 0, MAXIMUM_BOTS_SHOWN));
break;
}
case 2:
{
long skill = config_integer("bots.skill") + change;
if (skill > 2)
skill = event->type == _event_type_button && event->data.button.index == _gamepad_analog_button_a ? 0 : 2;
config_write_integer("bots.skill", PIN(skill, 0, 2));
break;
}
}
}
/* X on the screen of games to join: the panel of online games (and in it,
everything); Y in a hosted game's lobby: the host options (public, bots).
TRUE: the event is taken. */
everything); Y in a hosted game's lobby: whether the game is public. TRUE:
the event is taken. */
static boolean ui_join_code_take_event(
struct widget_instance *widget,
struct event_record const *event)
{
if (widget != widget_globals.active_widgets[0])
return FALSE;
if (ui_join_code_globals.host_panel_open)
{
if (!ui_join_code_screen_active("connected_pregame_screen") || !global_network_game_server_get())
{
ui_join_code_globals.host_panel_open = FALSE;
return FALSE;
}
ui_join_code_host_panel_event(event);
return TRUE;
}
if (ui_join_code_globals.panel_open)
{
if (!ui_join_code_screen_active("server_list_screen"))
@@ -5884,8 +5793,7 @@ static boolean ui_join_code_take_event(
if (event->data.button.index == _gamepad_analog_button_y &&
ui_join_code_screen_active("connected_pregame_screen") && global_network_game_server_get())
{
ui_join_code_globals.host_panel_open = TRUE;
ui_join_code_globals.host_selection = 0;
p2p_set_public_game(!p2p_public_game());
return TRUE;
}
return FALSE;
@@ -6067,64 +5975,15 @@ static void ui_join_code_render(
if (status)
ui_join_code_draw_line(392, status, FALSE, FALSE);
}
else if (ui_join_code_screen_active("connected_pregame_screen") && global_network_game_server_get())
else if (ui_join_code_screen_active("connected_pregame_screen") && global_network_game_server_get() &&
p2p_join_code(code, sizeof(code)))
{
char line[128];
long bots = config_integer("bots.count");
char bots_text[24];
char line[96];
if (bots > 0)
snprintf(bots_text, sizeof(bots_text), "%ld bot%s", bots, bots == 1 ? "" : "s");
else
snprintf(bots_text, sizeof(bots_text), "no bots");
if (p2p_join_code(code, sizeof(code)))
{
snprintf(line, sizeof(line), "Online join code: %s %%y-button Host options (%s, %s)", code,
p2p_public_game() ? "public" : "private", bots_text);
}
else
{
snprintf(line, sizeof(line), "%%y-button Host options (%s)", bots_text);
}
snprintf(line, sizeof(line), "Online join code: %s %%y-button Public game: %s", code,
p2p_public_game() ? "Yes" : "No");
/* (under the buttons' key) */
ui_join_code_draw_line(448, line, TRUE, TRUE);
if (ui_join_code_globals.host_panel_open)
{
static char const *const skills[] = { "Easy", "Normal", "Hard" };
real_argb_color title_color = { 1.0f, 0.55f, 0.75f, 1.0f };
real_argb_color text_color = { 1.0f, 0.85f, 0.85f, 0.85f };
short row;
ui_join_code_draw_quad(140, 150, 500, 330, 0xF0050A18);
ui_join_code_draw_quad(140, 150, 500, 152, 0xFF3A6EA5);
ui_join_code_draw_quad(140, 328, 500, 330, 0xFF3A6EA5);
ui_join_code_draw_text(156, 484, 158, "HOST OPTIONS", FALSE, 0, &title_color);
for (row = 0; row < HOST_PANEL_ROWS; row++)
{
short y = (short)(190 + row * 30);
if (row == ui_join_code_globals.host_selection)
ui_join_code_draw_quad(148, (short)(y - 3), 492, (short)(y + 21), 0x803A6EA5);
switch (row)
{
case 0:
snprintf(line, sizeof(line), "Public game: %s", p2p_public_game() ? "Yes (listed online)" : "No");
break;
case 1:
snprintf(line, sizeof(line), "Bots: %s", bots > 0 ? bots_text : "None");
break;
default:
{
long skill = PIN(config_integer("bots.skill"), 0, 2);
snprintf(line, sizeof(line), "Bot skill: %s", skills[skill]);
break;
}
}
ui_join_code_draw_text(160, 484, y, line, FALSE, 0, &text_color);
}
ui_join_code_draw_text(156, 484, 300, "%a-button Change %b-button Done", TRUE, 0, &text_color);
}
}
}
+1 -238
View File
@@ -696,12 +696,6 @@ typedef char network_game_server_countdown_state_offset_assert[
offsetof(struct network_game_server, countdown_state) ==
8 + HALO_PORT_NETWORK_GAME_SIZE + MAXIMUM_NETWORK_MACHINE_COUNT * 0x10 + 0xC ? 1 : -1];
#ifdef HALO_ANDROID
/* bots' machines (below: the VR port's bots) */
static boolean network_game_server_bot_machines[MAXIMUM_NETWORK_MACHINE_COUNT];
static short network_game_server_bot_machine_count(struct network_game_server *server);
#endif
/* ---------- prototypes */
void countdown_timer_increment(
@@ -2901,10 +2895,6 @@ boolean server_has_enough_machines(
}
}
#ifdef HALO_ANDROID
/* (bots' machines count: the VR port's) */
machine_count += network_game_server_bot_machine_count(server);
#endif
has_enough_machines = machine_count >= minimum_machine_count;
return has_enough_machines;
@@ -3558,11 +3548,7 @@ void network_game_server_update_countdown(
else
{
if (network_game_should_accept_remote_connections() == FALSE ||
network_game_server_get_client_machine_count(server)
#ifdef HALO_ANDROID
+ network_game_server_bot_machine_count(server)
#endif
> 1)
network_game_server_get_client_machine_count(server) > 1)
{
unsigned long countdown;
@@ -3829,11 +3815,6 @@ static boolean network_game_server_add_new_client(
{
for (i = 0; i < MAXIMUM_NETWORK_MACHINE_COUNT; i++)
{
#ifdef HALO_ANDROID
/* (not a bot machine's slot) */
if (network_game_server_bot_machines[i] && server->game.machines[i].machine_index == i)
continue;
#endif
if (server->client_machines[i].machine_index == NONE)
{
struct transport_address client_address = { 0 };
@@ -4203,221 +4184,6 @@ static boolean network_game_server_have_all_machines_have_precached(
return all_machines_have_precached;
}
#ifdef HALO_ANDROID
/* ---------- bots (the VR port's: port/linux/game/bots.c)
Bots are the players of machines of the host's own that nothing connects
to: from the top slot down, four players each (a machine's most). The host
drives them (bots_tick); every other machine sees them as any other
machine's players. A new connection never takes a bot's slot, and in the
lobby they count as machines, so that a game of the host and bots starts. */
long config_integer(char const *name);
static void network_game_server_bot_copy_name(
wchar_t *destination,
wchar_t const *source,
short size)
{
short index;
for (index = 0; index < size - 1 && source[index]; index++)
destination[index] = source[index];
destination[index] = 0;
}
boolean network_game_server_is_bot_machine(
long machine_index)
{
struct network_game_server *server = global_network_game_server_get();
return server && VALID_INDEX(machine_index, MAXIMUM_NETWORK_MACHINE_COUNT) &&
network_game_server_bot_machines[machine_index] &&
server->game.machines[machine_index].machine_index == machine_index;
}
static short network_game_server_bot_machine_count(
struct network_game_server *server)
{
short count = 0;
long machine_index;
for (machine_index = 0; machine_index < MAXIMUM_NETWORK_MACHINE_COUNT; machine_index++)
{
if (network_game_server_bot_machines[machine_index] &&
server->game.machines[machine_index].machine_index == machine_index &&
network_game_server_machine_has_players(server, (short)machine_index))
{
count++;
}
}
return count;
}
static short network_game_server_bot_count(
struct network_game_server *server,
struct network_player **last)
{
short count = 0;
long player_index;
*last = NULL;
for (player_index = 0; player_index < MAXIMUM_NETWORK_PLAYER_COUNT; player_index++)
{
struct network_player *player = &server->game.players[player_index];
if (network_player_is_valid(player) && VALID_INDEX(player->machine_index, MAXIMUM_NETWORK_MACHINE_COUNT) &&
network_game_server_bot_machines[(long)player->machine_index])
{
count++;
*last = player;
}
}
return count;
}
static boolean network_game_server_add_bot(
struct network_game_server *server,
short number)
{
struct network_player bot;
long machine_index;
short controller_index;
if (!network_game_has_free_player_slot(&server->game))
return FALSE;
/* (a bot machine with room, else a new one from the top) */
for (machine_index = MAXIMUM_NETWORK_MACHINE_COUNT - 1; machine_index >= 0; machine_index--)
{
if (network_game_server_bot_machines[machine_index] &&
server->game.machines[machine_index].machine_index == machine_index)
{
for (controller_index = 0; controller_index < MAXIMUM_LOCAL_PLAYERS; controller_index++)
{
long player_index;
boolean taken = FALSE;
for (player_index = 0; player_index < MAXIMUM_NETWORK_PLAYER_COUNT && !taken; player_index++)
{
struct network_player *player = &server->game.players[player_index];
taken = network_player_is_valid(player) && player->machine_index == machine_index &&
player->controller_index == controller_index;
}
if (!taken)
goto found;
}
}
}
for (machine_index = MAXIMUM_NETWORK_MACHINE_COUNT - 1; machine_index >= 0; machine_index--)
{
if (!server->client_machines[machine_index].connection &&
server->client_machines[machine_index].machine_index == NONE &&
server->game.machines[machine_index].machine_index == NONE)
{
struct network_machine machine;
csmemset(&machine, 0, sizeof(machine));
network_game_server_bot_copy_name(machine.name, L"Bots", NUMBEROF(machine.name));
machine.machine_index = (char)machine_index;
if (!network_game_add_machine(&server->game, &machine))
return FALSE;
network_game_server_bot_machines[machine_index] = TRUE;
controller_index = 0;
goto found;
}
}
return FALSE;
found:
csmemset(&bot, 0, sizeof(bot));
{
wchar_t name[NETWORK_PLAYER_NAME_LENGTH];
short length = 0;
short digits = number < 10 ? 1 : number < 100 ? 2 : 3;
short digit;
network_game_server_bot_copy_name(name, L"Bot ", NUMBEROF(name));
while (name[length])
length++;
for (digit = digits - 1; digit >= 0 && length + digit < NETWORK_PLAYER_NAME_LENGTH - 1; digit--)
{
name[length + digit] = (wchar_t)(L'0' + number % 10);
number /= 10;
}
name[length + digits] = 0;
network_game_server_bot_copy_name(bot.name, name, NUMBEROF(bot.name));
}
bot.machine_index = (char)machine_index;
bot.controller_index = (char)controller_index;
bot.primary_color_index = NONE;
bot.icon_index = 0;
{
long player_count_by_team[NUMBER_OF_MULTIPLAYER_TEAMS] = { 0, 0 };
long player_index;
for (player_index = 0; player_index < MAXIMUM_NETWORK_PLAYER_COUNT; player_index++)
{
struct network_player *other_player = &server->game.players[player_index];
if (network_player_is_valid(other_player) && VALID_INDEX(other_player->team_index, NUMBER_OF_MULTIPLAYER_TEAMS))
player_count_by_team[other_player->team_index]++;
}
bot.team_index = player_count_by_team[1] < player_count_by_team[0] ? 1 : 0;
}
if (!player_name_is_unique(server, bot.name))
get_unique_random_name(server, &bot);
get_unique_random_color(server, &bot);
bot.player_list_index = NONE;
if (!network_game_add_player(&server->game, &bot))
return FALSE;
network_event("added a bot on machine %ld", machine_index);
return TRUE;
}
/* (the lobby) as many bots as bots.count asks for */
static void network_game_server_update_bots(
struct network_game_server *server)
{
long wanted = config_integer("bots.count");
struct network_player *last;
short count = network_game_server_bot_count(server, &last);
boolean changed = FALSE;
long machine_index;
/* (bot machines a new game no longer has) */
for (machine_index = 0; machine_index < MAXIMUM_NETWORK_MACHINE_COUNT; machine_index++)
{
if (network_game_server_bot_machines[machine_index] &&
server->game.machines[machine_index].machine_index != machine_index)
{
network_game_server_bot_machines[machine_index] = FALSE;
}
}
wanted = PIN(wanted, 0, 15);
while (count < wanted && network_game_server_add_bot(server, (short)(count + 1)))
{
count++;
changed = TRUE;
}
while (count > wanted && network_game_server_bot_count(server, &last) > 0 && last)
{
long bot_machine = last->machine_index;
network_game_remove_player(&server->game, last);
if (!network_game_server_machine_has_players(server, (short)bot_machine))
{
network_game_remove_machine(&server->game, &server->game.machines[bot_machine]);
network_game_server_bot_machines[bot_machine] = FALSE;
}
count--;
changed = TRUE;
}
if (changed)
network_game_server_send_game_data_pregame(server);
}
#endif
static boolean network_game_server_idle_pregame_tasks(
struct network_game_server *server)
{
@@ -4428,9 +4194,6 @@ static boolean network_game_server_idle_pregame_tasks(
{
long itr;
#ifdef HALO_ANDROID
network_game_server_update_bots(server);
#endif
/* send the lobby changes collected since the last settings update */
network_game_server_flush_game_data_pregame(server);
+10
View File
@@ -746,6 +746,16 @@ static void render_vr_scope(
if (zoom_level == NONE || player_index == NONE || player_get(player_index)->unit_index == NONE)
return;
magnification = unit_get_zoom_magnification(player_get(player_index)->unit_index, zoom_level);
{
/* (a weapon whose body stands higher over the hand than a rifle's
has its scope's disc higher: the rocket launcher's tube) */
struct unit_datum *unit = unit_get(player_get(player_index)->unit_index);
long weapon_index = unit->unit.current_weapon_index >= 0 ?
unit->unit.weapon_object_indices[unit->unit.current_weapon_index] : NONE;
char const *name = weapon_index != NONE ? tag_get_name(object_get(weapon_index)->definition_index) : NULL;
halo_vr_set_scope_raise(name && strstr(name, "rocket launcher") ? 0.18f : 0.0f);
}
if (!halo_vr_scope(window->local_player_index, magnification, position, forward, up, &vertical_field_of_view))
return;
scope_window = *window;