mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-20 10:00:28 +02:00
3549 lines
102 KiB
C
3549 lines
102 KiB
C
/*
|
|
AI_DEBUG.C
|
|
*/
|
|
|
|
/* ---------- headers */
|
|
|
|
#include "cseries.h"
|
|
#include "ai_debug.h"
|
|
|
|
#include "actions.h"
|
|
#include "actors.h"
|
|
#include "actor_definitions.h"
|
|
#include "ai_communication.h"
|
|
#include "ai_scenario_definitions.h"
|
|
#include "encounters.h"
|
|
#include "props.h"
|
|
|
|
#include "camera/observer.h"
|
|
#include "game/game.h"
|
|
#include "game/players.h"
|
|
#include "memory/data.h"
|
|
#include "objects/damage.h"
|
|
#include "physics/collision_bsp_definitions.h"
|
|
#include "rasterizer/rasterizer.h"
|
|
#include "render/render_debug.h"
|
|
#include "scenario/scenario.h"
|
|
#include "scenario/scenario_definitions.h"
|
|
#include "structures/structure_bsp_definitions.h"
|
|
#include "text/draw_string.h"
|
|
#include "units/bipeds.h"
|
|
#include "units/biped_definitions.h"
|
|
#include "units/units.h"
|
|
#include "units/unit_definitions.h"
|
|
|
|
/* ---------- constants */
|
|
|
|
/* ---------- macros */
|
|
|
|
#define actor_debug_print_threat(actor, threat_type, string, color) \
|
|
if (actor->situation.specific_threats[threat_type]) \
|
|
{ \
|
|
render_debug_string_at_point( \
|
|
TRUE, \
|
|
ai_debug_drawstack(), \
|
|
csprintf( \
|
|
temporary, \
|
|
string, \
|
|
(actor)->situation.specific_threats[threat_type], \
|
|
(actor)->situation.cumulative_threats[threat_type]), \
|
|
color); \
|
|
}
|
|
|
|
/* ---------- structures */
|
|
|
|
/* ---------- prototypes */
|
|
|
|
void ai_debug_drawstack_setup(union real_point3d const *drawstack_base);
|
|
static real_point3d *ai_debug_drawstack(void);
|
|
|
|
static void ai_debug_highlight_unit(long unit_index, boolean render_exclusive, union real_argb_color const *color);
|
|
|
|
static void ai_debug_render_path_nodes(
|
|
struct path_state *path_state,
|
|
boolean bsp_access_allowed,
|
|
boolean render_all_nodes,
|
|
boolean render_polygons,
|
|
boolean render_costs,
|
|
boolean render_closest);
|
|
static void ai_debug_render_surface(struct structure_bsp const *structure_bsp, long surface_index, real offset, union real_argb_color const *color);
|
|
|
|
static void ai_debug_render_actor(long actor_index, boolean render_exclusive, long *history_start_time);
|
|
static void ai_debug_render_path_storage(struct path_debug_storage *path);
|
|
|
|
/* ---------- globals */
|
|
|
|
struct ai_debug_state ai_debug;
|
|
|
|
struct actor_debug_info *actor_debug_array = NULL;
|
|
struct path_debug_storage *actor_path_debug_array = NULL;
|
|
|
|
real_point3d global_ai_debug_drawstack_next_position;
|
|
real_point3d global_ai_debug_drawstack_last_position;
|
|
real global_ai_debug_drawstack_height;
|
|
real_argb_color global_temporary_render_color;
|
|
long global_ai_debug_firing_position_color_count = NONE;
|
|
|
|
static char const *postcombat_type_strings[NUMBER_OF_ACTOR_POSTCOMBAT_ACTIONS] =
|
|
{
|
|
"none",
|
|
"alone",
|
|
"unscathed",
|
|
"wounded",
|
|
"massacre",
|
|
"triumph",
|
|
"run-to",
|
|
"check-enemy",
|
|
"check-friend",
|
|
"shoot-corpse",
|
|
"celebrate"
|
|
};
|
|
|
|
static long global_ai_debug_selected_squad_index = NONE;
|
|
static long global_ai_debug_selected_squad_time = NONE;
|
|
static unsigned long global_ai_debug_activation_cluster_bit_vector[16];
|
|
|
|
const real_argb_color global_ai_debug_firing_position_colors[] =
|
|
{
|
|
{ { 1.f, 1.f, 0.f, 1.f } },
|
|
{ { 1.f, 0.f, 1.f, 1.f } },
|
|
{ { 1.f, 1.f, 0.5f, 0.f } },
|
|
{ { 1.f, 0.f, 1.f, 0.5f } },
|
|
{ { 1.f, 0.5f, 0.f, 1.f } },
|
|
{ { 1.f, 1.f, 0.f, 0.5f } },
|
|
{ { 1.f, 0.5f, 1.f, 0.f } },
|
|
{ { 1.f, 0.f, 0.5f, 1.f } },
|
|
{ { 1.f, 0.5f, 0.f, 0.f } },
|
|
{ { 1.f, 0.f, 0.5f, 0.f } },
|
|
{ { 1.f, 0.f, 0.f, 0.5f } },
|
|
{ { 1.f, 1.f, 1.f, 0.5f } },
|
|
{ { 1.f, 1.f, 0.5f, 1.f } },
|
|
{ { 1.f, 0.5f, 1.f, 1.f } },
|
|
{ { 1.f, 0.5f, 0.5f, 0.f } },
|
|
{ { 1.f, 0.f, 0.5f, 0.5f } },
|
|
{ { 1.f, 0.5f, 0.f, 0.5f } },
|
|
{ { REAL_MAX, REAL_MAX, REAL_MAX, REAL_MAX } }
|
|
};
|
|
|
|
/* ---------- public code */
|
|
|
|
void ai_debug_initialize(
|
|
void)
|
|
{
|
|
memset(&ai_debug, 0, sizeof(ai_debug));
|
|
|
|
ai_debug.selected_actor_index = NONE;
|
|
ai_debug.selected_squad_index = NONE;
|
|
ai_debug.last_render_id = 1;
|
|
ai_debug.render = TRUE;
|
|
|
|
actor_debug_array = actor_debug_array==NULL ? (struct actor_debug_info *)debug_malloc(sizeof(*actor_debug_array) * MAXIMUM_NUMBER_OF_ACTORS, FALSE, "c:\\halo\\SOURCE\\ai\\ai_debug.c", 147) : actor_debug_array;
|
|
actor_path_debug_array = actor_path_debug_array==NULL ? (struct path_debug_storage *)debug_malloc(sizeof(*actor_path_debug_array) * MAXIMUM_NUMBER_OF_ACTOR_PATHS, FALSE, "c:\\halo\\SOURCE\\ai\\ai_debug.c", 148) : actor_path_debug_array;
|
|
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_debug.c", 150, actor_debug_array && actor_path_debug_array);
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_debug_dispose(
|
|
void)
|
|
{
|
|
if (actor_debug_array)
|
|
{
|
|
debug_free(actor_debug_array, "c:\\halo\\SOURCE\\ai\\ai_debug.c", 160);
|
|
actor_debug_array = NULL;
|
|
}
|
|
|
|
if (actor_path_debug_array)
|
|
{
|
|
debug_free(actor_path_debug_array, "c:\\halo\\SOURCE\\ai\\ai_debug.c", 166);
|
|
actor_path_debug_array = NULL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_debug_dispose_from_old_map(
|
|
void)
|
|
{
|
|
struct scenario *scenario = global_scenario_try_and_get();
|
|
|
|
if (scenario && ai_debug.selected_squad_index!=NONE)
|
|
{
|
|
struct encounter_definition* encounter = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->ai_encounters,
|
|
DATUM_INDEX_TO_ABSOLUTE_INDEX(ai_debug.selected_squad_index),
|
|
struct encounter_definition);
|
|
|
|
strncpy(ai_debug.selected_squad_name, encounter->name, NUMBEROF(ai_debug.selected_squad_name));
|
|
ai_debug.selected_squad_name[NUMBEROF(ai_debug.selected_squad_name)-1] = '\0';
|
|
}
|
|
else
|
|
{
|
|
strcpy(ai_debug.selected_squad_name, "");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_debug_clear_storage(
|
|
void)
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_debug.c", 208, actor_debug_array);
|
|
|
|
memset(actor_debug_array, 0, sizeof(*actor_debug_array) * MAXIMUM_NUMBER_OF_ACTORS);
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_debug.c", 211, actor_path_debug_array)
|
|
|
|
memset(actor_path_debug_array, 0, sizeof(*actor_path_debug_array) * MAXIMUM_NUMBER_OF_ACTOR_PATHS);
|
|
|
|
return;
|
|
}
|
|
|
|
void ai_debug_actor_deleted(
|
|
long actor_index)
|
|
{
|
|
short path_index;
|
|
|
|
for (path_index = 0; path_index<MAXIMUM_NUMBER_OF_ACTOR_PATHS; ++path_index)
|
|
{
|
|
struct path_debug_storage *path = &actor_path_debug_array[path_index];
|
|
|
|
if (path->valid)
|
|
{
|
|
if (path->actor_index==actor_index)
|
|
{
|
|
path->valid = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
struct path_debug_storage *ai_debug_get_last_path(
|
|
long actor_index)
|
|
{
|
|
short path_index;
|
|
|
|
short found_path_index = NONE;
|
|
long found_path_time = NONE;
|
|
|
|
for (path_index = 0; path_index<MAXIMUM_NUMBER_OF_ACTOR_PATHS; ++path_index)
|
|
{
|
|
struct path_debug_storage const *path = &actor_path_debug_array[path_index];
|
|
|
|
if (path->valid && path->actor_index==actor_index && path->path_time>found_path_time)
|
|
{
|
|
found_path_index = path_index;
|
|
found_path_time = path->path_time;
|
|
}
|
|
}
|
|
|
|
return found_path_index!=NONE ? &actor_path_debug_array[found_path_index] : NULL;
|
|
}
|
|
|
|
boolean ai_debug_highlight_cluster(
|
|
short index,
|
|
real_argb_color const **highlight_color)
|
|
{
|
|
boolean result = FALSE;
|
|
|
|
if (ai_debug.render_encounter_activeregion && ai_debug.selected_squad_index!=NONE)
|
|
{
|
|
if (global_ai_debug_selected_squad_time != game_time_get() ||
|
|
global_ai_debug_selected_squad_index != ai_debug.selected_squad_index)
|
|
{
|
|
encounter_compute_activation_cluster_bit_vector(
|
|
ai_debug.selected_squad_index,
|
|
FALSE,
|
|
SIZEOF_BITS(global_ai_debug_activation_cluster_bit_vector),
|
|
0,
|
|
global_ai_debug_activation_cluster_bit_vector);
|
|
global_ai_debug_selected_squad_time = game_time_get();
|
|
global_ai_debug_selected_squad_index = ai_debug.selected_squad_index;
|
|
}
|
|
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_debug.c", 4133, highlight_color);
|
|
|
|
if (BIT_VECTOR_TEST_FLAG(global_ai_debug_activation_cluster_bit_vector, index))
|
|
{
|
|
if (encounter_get(ai_debug.selected_squad_index)->active)
|
|
{
|
|
*highlight_color = global_real_argb_yellow;
|
|
}
|
|
else
|
|
{
|
|
*highlight_color = global_real_argb_blue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*highlight_color = global_real_argb_grey;
|
|
}
|
|
|
|
result = TRUE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// TODO: remove this!!!!!
|
|
void test(void)
|
|
{
|
|
ai_debug_render_actor(NULL, FALSE, NULL);
|
|
}
|
|
|
|
/* ---------- private code */
|
|
|
|
static void ai_debug_drawstack_setup(
|
|
real_point3d const *drawstack_base)
|
|
{
|
|
real_vector3d vector_to_stack;
|
|
|
|
struct observer_result const *camera = observer_get_camera(0);
|
|
|
|
global_ai_debug_drawstack_last_position = *drawstack_base;
|
|
global_ai_debug_drawstack_next_position = global_ai_debug_drawstack_last_position;
|
|
|
|
if (camera)
|
|
{
|
|
vector_from_points3d(&global_ai_debug_drawstack_last_position, &camera->position, &vector_to_stack);
|
|
global_ai_debug_drawstack_height = magnitude3d(&vector_to_stack) / 40.f;
|
|
}
|
|
else
|
|
{
|
|
global_ai_debug_drawstack_height = 0.05f;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static real_point3d *ai_debug_drawstack(
|
|
void)
|
|
{
|
|
global_ai_debug_drawstack_last_position = global_ai_debug_drawstack_next_position;
|
|
point_from_line3d(&global_ai_debug_drawstack_last_position, global_up3d, global_ai_debug_drawstack_height, &global_ai_debug_drawstack_next_position);
|
|
return &global_ai_debug_drawstack_last_position;
|
|
}
|
|
|
|
static void ai_debug_highlight_unit(
|
|
long unit_index,
|
|
boolean render_exclusive,
|
|
union real_argb_color const *color)
|
|
{
|
|
struct biped_datum *biped = biped_try_and_get(unit_index);
|
|
|
|
if (biped)
|
|
{
|
|
real_point3d base;
|
|
real width;
|
|
real pill_height;
|
|
|
|
struct unit_datum *unit = unit_try_and_get(biped->object.parent_object_index);
|
|
|
|
if (unit && unit->unit.driver_object_index==unit_index)
|
|
{
|
|
object_get_bounding_sphere(biped->object.parent_object_index, &base, &width);
|
|
pill_height = 0.f;
|
|
}
|
|
else
|
|
{
|
|
biped_get_physics_pill(unit_index, &base, &pill_height, &width);
|
|
}
|
|
|
|
if (render_exclusive && pill_height>0.f)
|
|
{
|
|
real_vector3d height;
|
|
set_real_vector3d(&height, 0.f, 0.f, pill_height);
|
|
render_debug_pill(TRUE, &base, &height, width, color);
|
|
}
|
|
else
|
|
{
|
|
render_debug_sphere(TRUE, &base, 0.75f * width, color);
|
|
}
|
|
|
|
if (render_exclusive)
|
|
{
|
|
render_debug_point(TRUE, &base, 1.8f * width, color);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// TODO: finish
|
|
static void ai_debug_render_path_nodes(
|
|
struct path_state *path_state,
|
|
boolean bsp_access_allowed,
|
|
boolean render_all_nodes,
|
|
boolean render_polygons,
|
|
boolean render_costs,
|
|
boolean render_closest)
|
|
{
|
|
real_argb_color const *attractor_distance_color;
|
|
real_argb_color const *closest_color;
|
|
real_point3d temp_point2;
|
|
real_argb_color const *polygon_color;
|
|
real_argb_color const *cost_color;
|
|
real_argb_color const *attractor_weight_color;
|
|
real_point3d temp_point;
|
|
static short current_traverse_index;
|
|
|
|
return;
|
|
}
|
|
|
|
static void ai_debug_render_surface(
|
|
struct structure_bsp const *structure_bsp,
|
|
long surface_index,
|
|
real offset,
|
|
union real_argb_color const *color)
|
|
{
|
|
struct collision_bsp const *collision_bsp = TAG_BLOCK_GET_ELEMENT(&structure_bsp->collision_bsp, 0, struct collision_bsp);
|
|
struct collision_surface const *collision_surface = TAG_BLOCK_GET_ELEMENT(&collision_bsp->surfaces, surface_index, struct collision_surface);
|
|
long edge_index = collision_surface->first_edge_index;
|
|
|
|
do
|
|
{
|
|
struct collision_edge const *edge = TAG_BLOCK_GET_ELEMENT(&collision_bsp->edges, edge_index, struct collision_edge);
|
|
const boolean next_index_belongs_to_surface = edge->surface_indices[1] == surface_index;
|
|
struct collision_vertex const *point0 = TAG_BLOCK_GET_ELEMENT(&collision_bsp->vertices, edge->vertex_indices[0], struct collision_vertex);
|
|
struct collision_vertex const *point1 = TAG_BLOCK_GET_ELEMENT(&collision_bsp->vertices, edge->vertex_indices[1], struct collision_vertex);
|
|
|
|
render_debug_line_offset(TRUE, &point0->point, &point1->point, color, offset + 0.015f);
|
|
edge_index = edge->edge_indices[next_index_belongs_to_surface];
|
|
}
|
|
while (edge_index!=collision_surface->first_edge_index);
|
|
|
|
return;
|
|
}
|
|
|
|
static void ai_debug_render_actor(
|
|
long actor_index,
|
|
boolean render_exclusive,
|
|
long *history_start_time)
|
|
{
|
|
struct actor_datum* actor = actor_get(actor_index);
|
|
struct actor_debug_info *actor_debug_info = &actor_debug_array[DATUM_INDEX_TO_ABSOLUTE_INDEX(actor_index)];
|
|
|
|
if (actor_debug_info->last_render_id!=ai_debug.last_render_id)
|
|
{
|
|
struct actor_definition *actor_definition = actor_definition_get(actor->meta.definition_index);
|
|
struct actor_variant_definition *actor_variant_definition = actor_variant_definition_get(actor->meta.variant_definition_index);
|
|
struct path_debug_storage *path = ai_debug_get_last_path(actor_index);
|
|
struct unit_datum *unit = NULL;
|
|
struct unit_definition *unit_definition = NULL;
|
|
|
|
/* Get unit info if the actor has an associated unit */
|
|
|
|
if (actor->meta.unit_index!=NONE)
|
|
{
|
|
unit = unit_get(actor->meta.unit_index);
|
|
unit_definition = unit_definition_get(unit->definition_index);
|
|
}
|
|
|
|
actor_debug_info->last_render_id = ai_debug.last_render_id;
|
|
|
|
/* Set history start time */
|
|
|
|
if (history_start_time)
|
|
{
|
|
if (actor->state.combat_status>=2 && actor->target.target_prop_index!=NONE)
|
|
{
|
|
struct prop_datum *prop = prop_get(actor->target.target_prop_index);
|
|
|
|
if (*history_start_time==NONE || prop->last_perceived_time<*history_start_time)
|
|
{
|
|
*history_start_time = prop->last_perceived_time;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Stack setup */
|
|
|
|
{
|
|
real_point3d stack_base;
|
|
|
|
point_from_line3d(&actor->input.position.head_position, global_up3d, 0.1f, &stack_base);
|
|
ai_debug_drawstack_setup(&stack_base);
|
|
}
|
|
|
|
/* Unit highlighting */
|
|
|
|
if (!actor->meta.swarm)
|
|
{
|
|
if (actor->meta.swarm_cache_index!=NONE)
|
|
{
|
|
short unit_num;
|
|
|
|
struct swarm_datum *swarm = swarm_get(actor->meta.swarm_cache_index);
|
|
|
|
for (unit_num = 0; unit_num<swarm->unit_count; ++unit_num)
|
|
{
|
|
ai_debug_highlight_unit(swarm->unit_indices[unit_num], render_exclusive, actor_action_debug_color(actor_index));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ai_debug_highlight_unit(actor->meta.unit_index, render_exclusive, actor_action_debug_color(actor_index));
|
|
}
|
|
|
|
/* Line of fire crouching status */
|
|
|
|
if (ai_debug.render_lineoffire_crouching &&
|
|
(TEST_FLAG(actor_definition->flags, _actor_definition_crouch_in_line_of_fire_bit) ||
|
|
TEST_FLAG(actor_definition->flags, _actor_definition_avoid_friend_line_of_fire_bit)))
|
|
{
|
|
/* Blocking messages */
|
|
|
|
if (actor->emotions.crouch_blocking_line_of_fire ||
|
|
actor->emotions.crouch_friends_in_line_of_fire ||
|
|
actor->emotions.crouch_blocking_player_line_of_fire)
|
|
{
|
|
char const *blocking_type;
|
|
|
|
char const *blocking_mode = actor->emotions.crouch_friends_in_line_of_fire ? "friends-blocking" : "";
|
|
|
|
if (actor->emotions.crouch_blocking_player_line_of_fire)
|
|
{
|
|
blocking_type = "blocking-player ";
|
|
}
|
|
else
|
|
{
|
|
blocking_type = actor->emotions.crouch_blocking_line_of_fire ? "blocking " : "";
|
|
}
|
|
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "%s%s", blocking_mode, blocking_type),
|
|
global_real_argb_orange);
|
|
}
|
|
|
|
/* Moving into fire */
|
|
|
|
if (actor->emotions.moving_into_player_line_of_fire ||
|
|
actor->emotions.moving_into_fire_timer > 0)
|
|
{
|
|
char const *string = actor->emotions.moving_into_player_line_of_fire ? "moving-into-fire" : "";
|
|
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "%sforce-stop %d", string, actor->emotions.moving_into_fire_timer),
|
|
global_real_argb_pink);
|
|
}
|
|
}
|
|
|
|
/* Player aiming blocked */
|
|
|
|
if (ai_debug.render_player_aiming_blocked)
|
|
{
|
|
struct prop_datum *prop;
|
|
struct prop_iterator iterator;
|
|
real_vector3d aiming_vector;
|
|
|
|
short blockage_type = 0;
|
|
boolean player_prop_found = FALSE;
|
|
|
|
prop_iterator_new(&iterator, actor_index);
|
|
|
|
while (prop = prop_iterator_next(&iterator))
|
|
{
|
|
if (prop->state >= _prop_state_becoming_unacknowledged &&
|
|
prop->state <= _prop_state_acknowledged &&
|
|
!prop->enemy)
|
|
{
|
|
if (prop->player)
|
|
{
|
|
short v0;
|
|
|
|
player_prop_found = TRUE;
|
|
unit_get_aiming_vector(prop->unit_index, &aiming_vector);
|
|
v0 = actor_perception_aiming_vector_test_blockage(
|
|
&prop->body_position,
|
|
&aiming_vector,
|
|
&actor->input.position.body_position,
|
|
NULL);
|
|
|
|
if (blockage_type <= v0)
|
|
{
|
|
blockage_type = v0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (player_prop_found)
|
|
{
|
|
switch (blockage_type)
|
|
{
|
|
case 0:
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "not-occluding-player", global_real_argb_green);
|
|
break;
|
|
case 1:
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "occluding-player", global_real_argb_blue);
|
|
break;
|
|
case 2:
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "blocking-player", global_real_argb_red);
|
|
break;
|
|
default:
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "aiming occlusion error", global_real_argb_pink);
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/* Vector avoidance */
|
|
|
|
if (ai_debug.render_vector_avoidance &&
|
|
actor_debug_info->field_19C != NONE &&
|
|
actor_debug_info->field_19C + TICKS_PER_SECOND>game_time_get())
|
|
{
|
|
short i;
|
|
short j;
|
|
|
|
/* Avoidance rays */
|
|
|
|
if (ai_debug.render_vector_avoidance_rays)
|
|
{
|
|
short ray_index;
|
|
for (ray_index = 0; ray_index<ACTOR_MAXIMUM_AVOIDANCE_RAYS; ++ray_index)
|
|
{
|
|
real_argb_color const *color = global_real_argb_white;
|
|
|
|
switch (actor_debug_info->avoidance_type[ray_index])
|
|
{
|
|
case _actor_vector_avoidance_obstructed_object:
|
|
color = global_real_argb_magenta;
|
|
break;
|
|
case _actor_vector_avoidance_obstructed_structure:
|
|
color = global_real_argb_red;
|
|
break;
|
|
}
|
|
|
|
if (actor_debug_info->avoidance_type[ray_index]>0)
|
|
{
|
|
real_point3d point0;
|
|
real_point3d point1;
|
|
|
|
point_from_line3d(
|
|
&actor_debug_info->ray_origin[ray_index],
|
|
&actor_debug_info->ray_direction[ray_index],
|
|
actor_debug_info->collision_t[ray_index],
|
|
&point0);
|
|
point_from_line3d(
|
|
&actor_debug_info->ray_origin[ray_index],
|
|
&actor_debug_info->ray_direction[ray_index],
|
|
1.f,
|
|
&point1);
|
|
render_debug_line(TRUE, &actor_debug_info->ray_origin[ray_index], &point0, global_real_argb_white);
|
|
render_debug_point(TRUE, &point0, 0.2f, color);
|
|
render_debug_line(TRUE, &point0, &point1, color);
|
|
|
|
if (ai_debug.render_vector_avoidance_sense_t)
|
|
{
|
|
point_from_line3d(&point0, global_up3d, 0.25f, &point0);
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
&point0,
|
|
csprintf(temporary, "%.3f", actor_debug_info->collision_t[ray_index]),
|
|
color);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
render_debug_vector(
|
|
TRUE,
|
|
&actor_debug_info->ray_origin[ray_index],
|
|
&actor_debug_info->ray_direction[ray_index],
|
|
1.f,
|
|
global_real_argb_white);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Avoidance rays */
|
|
|
|
for (i = 0; i < 8; ++i)
|
|
{
|
|
for (j = 0; j < 2; ++j)
|
|
{
|
|
if (ai_debug.render_vector_avoidance_rays)
|
|
{
|
|
real_argb_color const *color = global_real_argb_white;
|
|
|
|
switch (actor_debug_info->field_62F8[i][j])
|
|
{
|
|
case 1:
|
|
color = global_real_argb_aqua;
|
|
break;
|
|
case 2:
|
|
color = global_real_argb_yellow;
|
|
break;
|
|
}
|
|
|
|
if (actor_debug_info->field_62F8[i][j]>0)
|
|
{
|
|
real_point3d point0;
|
|
real_point3d point1;
|
|
|
|
point_from_line3d(
|
|
&actor_debug_info->field_6358[i][j],
|
|
&actor_debug_info->field_6418[i][j],
|
|
actor_debug_info->avoid_t[i][j],
|
|
&point0);
|
|
point_from_line3d(
|
|
&actor_debug_info->field_6358[i][j],
|
|
&actor_debug_info->field_6418[i][j],
|
|
1.f,
|
|
&point1);
|
|
render_debug_line(TRUE, &actor_debug_info->field_6358[i][j], &point0, global_real_argb_blue);
|
|
render_debug_point(TRUE, &point0, 0.2f, color);
|
|
render_debug_line(TRUE, &point0, &point1, color);
|
|
|
|
if (ai_debug.render_vector_avoidance_avoid_t)
|
|
{
|
|
point_from_line3d(&point0, global_up3d, 0.25f, &point0);
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
&point0,
|
|
csprintf(
|
|
temporary,
|
|
"%.3f",
|
|
actor_debug_info->avoid_t[i][j]),
|
|
global_real_argb_blue);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
render_debug_vector(
|
|
TRUE,
|
|
&actor_debug_info->field_6358[i][j],
|
|
&actor_debug_info->field_6418[i][j],
|
|
1.f,
|
|
color);
|
|
|
|
if (ai_debug.render_vector_avoidance_clear_time)
|
|
{
|
|
real_point3d point;
|
|
|
|
point_from_line3d(&actor_debug_info->field_6358[i][j], &actor_debug_info->field_6418[i][j], 0.1f, &point);
|
|
point_from_line3d(&point, global_up3d, 0.15f, &point);
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
&point,
|
|
csprintf(
|
|
temporary,
|
|
"c%d",
|
|
actor->control.vector_avoidance_clear_times[i][j]),
|
|
global_real_argb_white);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Avoidance weights */
|
|
|
|
if (ai_debug.render_vector_avoidance_weights && j==1)
|
|
{
|
|
real_point3d point;
|
|
|
|
point_from_line3d(
|
|
&actor_debug_info->field_6358[i][j],
|
|
&actor_debug_info->field_6418[i][j],
|
|
0.25f,
|
|
&point);
|
|
point_from_line3d(&point, global_up3d, 0.15f, &point);
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
&point,
|
|
csprintf(
|
|
temporary,
|
|
"%d: w%.2f",
|
|
i,
|
|
actor_debug_info->field_64D8[i]),
|
|
global_real_argb_green);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Avoidance intermediate */
|
|
|
|
if (ai_debug.render_vector_avoidance_intermediate)
|
|
{
|
|
real_vector3d direction;
|
|
|
|
real_argb_color const *color = actor_debug_info->field_6550 ? global_real_argb_magenta : global_real_argb_cyan;
|
|
|
|
render_debug_vector(TRUE, &actor_debug_info->avoidance_data.origin, &actor_debug_info->field_6524, 5.f, global_real_argb_yellow);
|
|
render_debug_vector(TRUE, &actor_debug_info->avoidance_data.origin, &actor_debug_info->field_6530, 1.f, global_real_argb_purple);
|
|
actor_move_get_avoidance_direction(&actor_debug_info->avoidance_data, actor_debug_info->field_6500, &direction);
|
|
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "best %.2f at %d", actor_debug_info->field_64FC, actor_debug_info->field_6500),
|
|
color);
|
|
render_debug_vector(TRUE, &actor_debug_info->avoidance_data.origin, &direction, 2.f, color);
|
|
|
|
actor_move_get_avoidance_direction(&actor_debug_info->avoidance_data, actor_debug_info->field_6504, &direction);
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "curr ~%.2f at %.2f", actor_debug_info->field_6508, actor_debug_info->field_6504),
|
|
global_real_argb_green);
|
|
|
|
render_debug_vector(TRUE, &actor_debug_info->avoidance_data.origin, &direction, 2.0, global_real_argb_green);
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "danger %.2f turncos %.2f", actor_debug_info->sign_no_danger, actor_debug_info->field_6510),
|
|
global_real_argb_yellow);
|
|
|
|
if (actor_debug_info->field_6551)
|
|
{
|
|
actor_move_transform_avoidance_vector(&actor_debug_info->avoidance_data, &actor_debug_info->avoidance_vector, &direction);
|
|
render_debug_vector(TRUE, &actor_debug_info->avoidance_data.origin, &direction, 2.0, global_real_argb_darkgreen);
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(
|
|
temporary,
|
|
"turn angvel %.2f danger %.2f bonus %.2f",
|
|
actor_debug_info->field_6558,
|
|
actor_debug_info->field_6568,
|
|
actor_debug_info->field_6554),
|
|
global_real_argb_darkgreen);
|
|
}
|
|
}
|
|
|
|
/* Avoidance objects */
|
|
|
|
if (ai_debug.render_vector_avoidance_objects)
|
|
{
|
|
short avoidance_object_index;
|
|
|
|
for (avoidance_object_index = 0; avoidance_object_index<actor_debug_info->avoidance_data.avoidance_object_count; ++avoidance_object_index)
|
|
{
|
|
real_vector3d height;
|
|
set_real_vector3d(&height, 0.f, 0.f, actor_debug_info->avoidance_data.avoidance_objects[avoidance_object_index].height);
|
|
render_debug_pill(
|
|
TRUE,
|
|
&actor_debug_info->avoidance_data.avoidance_objects[avoidance_object_index].base,
|
|
&height,
|
|
actor_debug_info->avoidance_data.avoidance_objects[avoidance_object_index].width,
|
|
global_real_argb_aqua);
|
|
}
|
|
}
|
|
|
|
{
|
|
boolean found_player = FALSE;
|
|
|
|
switch (actor_debug_info->field_653C)
|
|
{
|
|
case 0:
|
|
sprintf(temporary, "clear");
|
|
break;
|
|
case 1:
|
|
sprintf(temporary, "sensed at %f, turn %f", actor_debug_info->field_651C, actor_debug_info->field_6520);
|
|
found_player = TRUE;
|
|
break;
|
|
case 2:
|
|
sprintf(temporary, "sign no-danger %f < %f", actor_debug_info->sign_no_danger, 1.3f);
|
|
break;
|
|
case 3:
|
|
sprintf(temporary, "sign too-far cosangle %f < %f", actor_debug_info->sign_too_far_cosangle, 0.5f);
|
|
break;
|
|
case 4:
|
|
sprintf(temporary, "sign rotated %f", actor_debug_info->sign_rotated);
|
|
found_player = TRUE;
|
|
break;
|
|
case 5:
|
|
sprintf(temporary, "sharp new-turn");
|
|
found_player = TRUE;
|
|
break;
|
|
case 6:
|
|
sprintf(temporary, "sharp change-dir");
|
|
found_player = TRUE;
|
|
break;
|
|
case 7:
|
|
sprintf(temporary, "sharp continue");
|
|
found_player = TRUE;
|
|
break;
|
|
default:
|
|
sprintf(temporary, "<error>");
|
|
break;
|
|
}
|
|
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), temporary, global_real_argb_orange);
|
|
|
|
if (found_player)
|
|
{
|
|
render_debug_vector(TRUE, &actor_debug_info->avoidance_data.origin, &actor_debug_info->field_6540, 1.f, global_real_argb_pink);
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), csprintf(temporary, "move emergency %.2f", actor_debug_info->field_654C), global_real_argb_pink);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Activation */
|
|
|
|
if (ai_debug.render_activation)
|
|
{
|
|
{
|
|
char encounterbuf[256];
|
|
real_argb_color const *encounter_color;
|
|
|
|
if (actor->meta.encounter_index!=NONE)
|
|
{
|
|
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->ai_encounters,
|
|
DATUM_INDEX_TO_ABSOLUTE_INDEX(actor->meta.encounter_index),
|
|
struct encounter_definition);
|
|
struct encounter_datum *encounter = encounter_get(actor->meta.encounter_index);
|
|
boolean outside_current_bsp = FALSE;
|
|
|
|
|
|
if (encounter_definition->runtime_structure_bsp_reference_index==NONE)
|
|
{
|
|
encounter_color = global_real_argb_green;
|
|
sprintf(encounterbuf, "%s (no-bsp)", encounter_definition->name);
|
|
}
|
|
else
|
|
{
|
|
outside_current_bsp = encounter_definition->runtime_structure_bsp_reference_index!=global_structure_bsp_index;
|
|
sprintf(encounterbuf, "%s (bsp %d)", encounter_definition->name, encounter_definition->runtime_structure_bsp_reference_index);
|
|
}
|
|
|
|
if (encounter->active)
|
|
{
|
|
encounter_color = global_real_argb_green;
|
|
}
|
|
else if (outside_current_bsp)
|
|
{
|
|
encounter_color = global_real_argb_red;
|
|
}
|
|
else
|
|
{
|
|
encounter_color = global_real_argb_purple;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
encounter_color = global_real_argb_blue;
|
|
sprintf(encounterbuf, "encounterless");
|
|
}
|
|
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), encounterbuf, encounter_color);
|
|
}
|
|
|
|
{
|
|
real_argb_color const *actor_color = actor_activation_debug_color(actor_index);
|
|
long unit_index = actor->meta.unit_index;
|
|
short unit_count = 0;
|
|
struct swarm_datum *swarm = NULL;
|
|
struct observer_result const *camera = observer_get_camera(0);
|
|
unsigned long const *pvs = players_get_combined_pvs();
|
|
|
|
if (actor->meta.swarm && actor->meta.swarm_cache_index!=NONE)
|
|
{
|
|
swarm = swarm_get(actor->meta.swarm_cache_index);
|
|
}
|
|
|
|
while (TRUE)
|
|
{
|
|
if (unit_index!=NONE)
|
|
{
|
|
real_vector3d offset_vector;
|
|
real_point3d base_point;
|
|
short cluster_index;
|
|
|
|
unit_get_head_position(unit_index, &base_point);
|
|
point_from_line3d(&base_point, global_up3d, 0.2f, &base_point);
|
|
|
|
if (camera==NULL)
|
|
{
|
|
offset_vector = *global_forward3d;
|
|
}
|
|
else
|
|
{
|
|
cross_product3d(&camera->forward, global_up3d, &offset_vector);
|
|
if (normalize3d(&offset_vector)==0.f)
|
|
{
|
|
offset_vector = *global_forward3d;
|
|
}
|
|
}
|
|
|
|
{
|
|
struct object_datum const *ultimate_parent = object_get(object_get_ultimate_parent(unit_index));
|
|
cluster_index = ultimate_parent->object.location.cluster_index;
|
|
}
|
|
|
|
if (cluster_index==NONE)
|
|
{
|
|
real_point3d p0;
|
|
real_point3d p1;
|
|
real_point3d p2;
|
|
real_point3d p3;
|
|
|
|
point_from_line3d(&base_point, &offset_vector, 0.1f, &p0);
|
|
point_from_line3d(&base_point, &offset_vector, -0.1f, &p1);
|
|
point_from_line3d(&p0, global_up3d, 0.2f, &p2);
|
|
point_from_line3d(&p1, global_up3d, 0.2f, &p3);
|
|
|
|
render_debug_line(TRUE, &p0, &p2, actor_color);
|
|
render_debug_line(TRUE, &p2, &p3, actor_color);
|
|
render_debug_line(TRUE, &p3, &p1, actor_color);
|
|
render_debug_line(TRUE, &p1, &p0, actor_color);
|
|
}
|
|
else if (BIT_VECTOR_TEST_FLAG(pvs, cluster_index))
|
|
{
|
|
real_point3d p0;
|
|
real_point3d p1;
|
|
real_point3d p2;
|
|
real_point3d p3;
|
|
real_point3d mid_point = base_point;
|
|
|
|
point_from_line3d(&base_point, global_up3d, 0.2f, &p0);
|
|
point_from_line3d(&base_point, global_up3d, 0.1f, &p1);
|
|
point_from_line3d(&p1, &offset_vector, 0.1f, &p3);
|
|
point_from_line3d(&p1, &offset_vector, -0.1f, &p2);
|
|
|
|
render_debug_line(TRUE, &mid_point, &p3, actor_color);
|
|
render_debug_line(TRUE, &p3, &p0, actor_color);
|
|
render_debug_line(TRUE, &p0, &p2, actor_color);
|
|
render_debug_line(TRUE, &p2, &mid_point, actor_color);
|
|
}
|
|
else
|
|
{
|
|
real_point3d p0;
|
|
real_point3d p1;
|
|
real_point3d p2;
|
|
real_point3d p3;
|
|
|
|
point_from_line3d(&base_point, &offset_vector, 0.1f, &p0);
|
|
point_from_line3d(&base_point, &offset_vector, -0.1f, &p1);
|
|
point_from_line3d(&p0, global_up3d, 0.2f, &p2);
|
|
point_from_line3d(&p1, global_up3d, 0.2f, &p3);
|
|
|
|
render_debug_line(TRUE, &p0, &p3, actor_color);
|
|
render_debug_line(TRUE, &p2, &p1, actor_color);
|
|
}
|
|
}
|
|
|
|
if (!swarm || unit_count >= swarm->unit_count)
|
|
{
|
|
break;
|
|
}
|
|
|
|
unit_index = swarm->unit_indices[unit_count++];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Support surfaces */
|
|
|
|
if (ai_debug.render_support_surfaces)
|
|
{
|
|
struct swarm_datum *swarm = NULL;
|
|
long unit_index = NONE;
|
|
short unit_num = 0;
|
|
|
|
if (!actor->meta.swarm || actor->meta.swarm_cache_index==NONE)
|
|
{
|
|
unit_index = actor->meta.unit_index;
|
|
}
|
|
else
|
|
{
|
|
swarm = swarm_get(actor->meta.swarm_cache_index);
|
|
|
|
if (swarm->unit_count>0)
|
|
{
|
|
unit_index = swarm->unit_indices[0];
|
|
}
|
|
}
|
|
|
|
while (unit_index!=NONE)
|
|
{
|
|
struct biped_datum *biped = biped_try_and_get(unit_index);
|
|
|
|
if (!biped || biped->biped.support_surface_index==NONE)
|
|
{
|
|
real_point3d origin;
|
|
|
|
object_get_origin(unit_index, &origin);
|
|
render_debug_sphere(TRUE, &origin, 0.3f, global_real_argb_pink);
|
|
|
|
if (!actor->meta.swarm)
|
|
{
|
|
if (actor->input.pathfinding_surface_index==NONE)
|
|
{
|
|
render_debug_sphere(TRUE, &actor->input.position.body_position, 0.4f, global_real_argb_red);
|
|
}
|
|
else
|
|
{
|
|
render_debug_sphere(TRUE, &actor->input.pathfinding_point, 0.4f, global_real_argb_orange);
|
|
ai_debug_render_surface(global_structure_bsp_get(), actor->input.pathfinding_surface_index, 0.f, global_real_argb_orange);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ai_debug_render_surface(global_structure_bsp_get(), biped->biped.support_surface_index, 0.f, global_real_argb_pink);
|
|
}
|
|
|
|
++unit_num;
|
|
unit_index = NONE;
|
|
|
|
if (swarm && unit_num<swarm->unit_count)
|
|
{
|
|
unit_index = swarm->unit_indices[unit_num];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Vitality */
|
|
|
|
if (ai_debug.render_vitality)
|
|
{
|
|
if (actor->input.body_vitality>0.f)
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), csprintf(temporary, "body %3.2f", actor->input.body_vitality), global_real_argb_red);
|
|
}
|
|
|
|
if (actor->input.shield_vitality>0.f)
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), csprintf(temporary, "shld %3.2f", actor->input.shield_vitality), global_real_argb_blue);
|
|
}
|
|
}
|
|
|
|
/* Damage */
|
|
|
|
if (ai_debug.render_recent_damage)
|
|
{
|
|
if (actor->input.recent_body_damage>0.f)
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), csprintf(temporary, "b/dmg %3.2f", actor->input.recent_body_damage), global_real_argb_yellow);
|
|
}
|
|
|
|
if (actor->input.recent_shield_damage>0.f)
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), csprintf(temporary, "s/dmg %3.2f", actor->input.recent_shield_damage), global_real_argb_green);
|
|
}
|
|
}
|
|
|
|
/* Cover seeking */
|
|
|
|
if (ai_debug.render_active_cover_seeking && actor_debug_info->field_B8)
|
|
{
|
|
const char *strings[8] =
|
|
{
|
|
"wrongaction",
|
|
"visibletarget",
|
|
"repeattimer",
|
|
"visibletimer",
|
|
"shielded",
|
|
"unavailable",
|
|
"success",
|
|
"panic"
|
|
};
|
|
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "%s %d %.2f", strings[actor_debug_info->field_BA], actor_debug_info->field_BC, actor_debug_info->field_C0),
|
|
global_real_argb_yellow);
|
|
}
|
|
|
|
/* Threats */
|
|
|
|
if (ai_debug.render_threats && actor->situation.known_enemies)
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), csprintf(temporary, "enemy %d", actor->situation.known_enemies), global_real_argb_red);
|
|
|
|
actor_debug_print_threat(actor, _actor_threat_visible, "vis %d/%d", global_real_argb_white);
|
|
actor_debug_print_threat(actor, _actor_threat_visible_facing_me, "facing %d/%d", global_real_argb_lightblue);
|
|
actor_debug_print_threat(actor, _actor_threat_visible_aiming_at_me, "aim at %d/%d", global_real_argb_blue);
|
|
actor_debug_print_threat(actor, _actor_threat_shooting, "shoot %d/%d", global_real_argb_yellow);
|
|
actor_debug_print_threat(actor, _actor_threat_shooting_near_me, "s/near %d/%d", global_real_argb_orange);
|
|
actor_debug_print_threat(actor, _actor_threat_shooting_at_me, "s/at me %d/%d", global_real_argb_red);
|
|
actor_debug_print_threat(actor, _actor_threat_extremely_close_to_me, "ex.close %d/%d", global_real_argb_magenta);
|
|
actor_debug_print_threat(actor, _actor_threat_damaging_me, "dmging %d/%d", global_real_argb_magenta);
|
|
}
|
|
|
|
/* Emotions */
|
|
|
|
if (ai_debug.render_emotions)
|
|
{
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "dngr %3.2f/%3.2f", actor->emotions.instantaneous_danger, actor->emotions.perceived_danger),
|
|
global_real_argb_yellow);
|
|
|
|
if (actor->emotions.unopposable_retreat_timer>0)
|
|
{
|
|
real_point3d position;
|
|
struct prop_datum *prop = prop_get(actor->emotions.unopposable_retreat_prop_index);
|
|
|
|
point_from_line3d(&actor->input.position.head_position, global_up3d, 0.05f, &position);
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), csprintf(temporary, "retreat t%d", actor->emotions.unopposable_retreat_timer), global_real_argb_red);
|
|
render_debug_line(TRUE, &position, &prop->head_position, global_real_argb_red);
|
|
}
|
|
|
|
if (TEST_FLAG(actor_definition->flags, _actor_definition_fixed_crouch_facing_bit))
|
|
{
|
|
char const *string;
|
|
|
|
if (actor->control.desire_stationary_facing)
|
|
{
|
|
if (actor->control.fixed_stationary_facing)
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "fixed-facing", global_real_argb_orange);
|
|
render_debug_vector(TRUE, &actor->input.position.head_position, &actor->control.fixed_stationary_facing_vector, 1.5f, global_real_argb_orange);
|
|
}
|
|
else
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "desire-fixed-facing", global_real_argb_orange);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), csprintf(temporary, "nostationary%s", actor->control.moving ? " (moving)" : ""), global_real_argb_orange);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Teams */
|
|
|
|
if (ai_debug.render_teams)
|
|
{
|
|
char const *teams[NUMBER_OF_SOLO_CAMPAIGN_TEAMS] =
|
|
{
|
|
"default",
|
|
"player",
|
|
"human",
|
|
"covenant",
|
|
"flood",
|
|
"sentinel",
|
|
"unused6",
|
|
"unused7",
|
|
"unused8",
|
|
"unused9"
|
|
};
|
|
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), actor->meta.team_index!=NONE ? teams[actor->meta.team_index] : "none", global_real_argb_green);
|
|
}
|
|
|
|
/* Player ratings */
|
|
|
|
if (ai_debug.render_player_ratings && actor->meta.unit_index!=NONE)
|
|
{
|
|
real player_rating = ai_communication_get_player_rating(actor->meta.unit_index, TRUE, NULL, NULL);
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), csprintf(temporary, "%.1f", player_rating), player_rating==0.f ? global_real_argb_blue : global_real_argb_white);
|
|
}
|
|
|
|
/* Audibility */
|
|
|
|
if (ai_debug.render_audibility && actor_debug_info->field_A4)
|
|
{
|
|
real_argb_color const *color;
|
|
char const *aud_type;
|
|
char textstring[512];
|
|
|
|
if (actor_debug_info->field_A6==0)
|
|
{
|
|
color = global_real_argb_red;
|
|
aud_type = "none";
|
|
|
|
}
|
|
else if(actor_debug_info->field_A6==1)
|
|
{
|
|
color = global_real_argb_blue;
|
|
aud_type = "part";
|
|
}
|
|
else
|
|
{
|
|
color = global_real_argb_green;
|
|
aud_type = "full";
|
|
}
|
|
|
|
sprintf(textstring, "aud/%s %.1fp/%.1fd", aud_type, actor_debug_info->field_A8, actor_debug_info->field_AC);
|
|
|
|
if (actor_debug_info->field_B0!=-1.f)
|
|
{
|
|
strcat(textstring, csprintf(temporary, "/%.1fs/%.1ff", actor_debug_info->field_B0, actor_debug_info->field_B4));
|
|
}
|
|
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), textstring, color);
|
|
}
|
|
|
|
/* Props */
|
|
|
|
if (ai_debug.render_props || ai_debug.render_props_web)
|
|
{
|
|
struct prop_datum *prop;
|
|
struct prop_iterator iterator;
|
|
|
|
real_point3d prop_start_point;
|
|
|
|
short dead_count = 0;
|
|
short friend_count = 0;
|
|
short enemy_count = 0;
|
|
short orphan_count = 0;
|
|
short total_count = 0;
|
|
|
|
point_from_line3d(&actor->input.position.head_position, global_up3d, 0.2f, &prop_start_point);
|
|
|
|
prop_iterator_new(&iterator, actor_index);
|
|
|
|
while (prop = prop_iterator_next(&iterator))
|
|
{
|
|
++total_count;
|
|
|
|
if (prop->dead)
|
|
{
|
|
++dead_count;
|
|
}
|
|
else if (
|
|
prop->state>=_prop_state_uninspected_orphan &&
|
|
prop->state<=_prop_state_inspected_orphan)
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_debug.c", 2230, prop->enemy);
|
|
++orphan_count;
|
|
}
|
|
else
|
|
{
|
|
if (prop->enemy)
|
|
{
|
|
++enemy_count;
|
|
}
|
|
else
|
|
{
|
|
++friend_count;
|
|
}
|
|
}
|
|
|
|
if ((render_exclusive || ai_debug.render_props_web) &&
|
|
(prop->enemy || !ai_debug.render_props_no_friends))
|
|
{
|
|
real_argb_color const *color;
|
|
real_point3d origin;
|
|
real_point3d string_point;
|
|
|
|
point_from_line3d(&prop->head_position, global_up3d, 0.2, &string_point);
|
|
|
|
switch (prop->state)
|
|
{
|
|
case 2:
|
|
case 3:
|
|
render_debug_line(TRUE, &prop_start_point, &prop->head_position, global_real_argb_yellow);
|
|
break;
|
|
case 1:
|
|
set_real_point3d(
|
|
&origin,
|
|
((1.f-prop->awareness)*prop_start_point.x) + (prop->awareness*prop->head_position.x),
|
|
((1.f-prop->awareness)*prop_start_point.y) + (prop->awareness*prop->head_position.y),
|
|
((1.f-prop->awareness)*prop_start_point.z) + (prop->awareness*prop->head_position.z)
|
|
);
|
|
render_debug_line(TRUE, &prop_start_point, &origin, global_real_argb_yellow);
|
|
render_debug_line(TRUE, &origin, &prop->head_position, global_real_argb_black);
|
|
break;
|
|
case 0:
|
|
render_debug_line(TRUE, &prop_start_point, &prop->head_position, global_real_argb_black);
|
|
break;
|
|
case 4:
|
|
case 5:
|
|
color = iterator.index==actor->meta.interesting_orphan_index ? global_real_argb_purple : global_real_argb_blue;
|
|
render_debug_line(TRUE, &prop_start_point, &prop->head_position, color);
|
|
|
|
if (!prop->definitely_located)
|
|
{
|
|
real_vector3d hint_vector;
|
|
|
|
real_argb_color const *alt_color = prop->state==_prop_state_uninspected_orphan ? global_real_argb_yellow : global_real_argb_blue;
|
|
|
|
set_real_vector3d(&hint_vector, prop->orphan_hint_vector.i, prop->orphan_hint_vector.j, 0.f);
|
|
render_debug_sphere(TRUE, &prop->head_position, 0.2f, alt_color);
|
|
render_debug_vector(TRUE, &prop->head_position, &hint_vector, 1.f, alt_color);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
point_from_line3d(&prop_start_point, global_up3d, 0.03f, &prop_start_point);
|
|
|
|
if (prop->required_ticks>0 ||
|
|
prop->state>=_prop_state_uninspected_orphan && prop->state<=_prop_state_inspected_orphan)
|
|
{
|
|
if (prop->required_ticks>0)
|
|
{
|
|
sprintf(temporary, "r%d ", prop->required_ticks);
|
|
}
|
|
else
|
|
{
|
|
strcpy(temporary, "");
|
|
}
|
|
|
|
if (prop->state>=_prop_state_uninspected_orphan && prop->state<=_prop_state_inspected_orphan)
|
|
{
|
|
char temp[256];
|
|
strcpy(temp, temporary);
|
|
sprintf(temporary, "%so%d ", temp, prop->orphan_lifespan_ticks);
|
|
}
|
|
|
|
if (prop->state==_prop_state_uninspected_orphan)
|
|
{
|
|
char temp[256];
|
|
strcpy(temp, temporary);
|
|
sprintf(temporary, "%si%d ", temp, prop->orphan_inspection_ticks);
|
|
}
|
|
|
|
render_debug_string_at_point(TRUE, &string_point, temporary, global_real_argb_pink);
|
|
point_from_line3d(&string_point, global_up3d, 0.05f, &string_point);
|
|
}
|
|
|
|
if (ai_debug.render_props_unreachable)
|
|
{
|
|
real_argb_color const *color;
|
|
|
|
if (prop->ignore)
|
|
{
|
|
color = global_real_argb_blue;
|
|
}
|
|
else
|
|
{
|
|
color = prop->preferred_target ? global_real_argb_pink : global_real_argb_red;
|
|
}
|
|
|
|
render_debug_string_at_point(TRUE, &string_point, csprintf(temporary, "%.2f", prop->target_weight), color);
|
|
point_from_line3d(&string_point, global_up3d, 0.05f, &string_point);
|
|
|
|
if (iterator.index==actor->target.target_prop_index)
|
|
{
|
|
render_debug_string_at_point(TRUE, &string_point, "target", global_real_argb_white);
|
|
point_from_line3d(&string_point, global_up3d, 0.05f, &string_point);
|
|
}
|
|
}
|
|
|
|
if (ai_debug.render_props_unopposable && prop->unreachable_ticks>0)
|
|
{
|
|
long time = prop->last_unreachable_time!=NONE ? game_time_get()-prop->last_unreachable_time : NONE;
|
|
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
&string_point,
|
|
csprintf(temporary, "unr %d %d", prop->unreachable_ticks, time),
|
|
global_real_argb_darkgreen);
|
|
point_from_line3d(&string_point, global_up3d, 0.05f, &string_point);
|
|
}
|
|
|
|
if (ai_debug.render_props_target_weight && prop->unopposable_enemy)
|
|
{
|
|
sprintf(
|
|
temporary,
|
|
"unopp c%d(%d) t%d",
|
|
prop->unopposable_casualties_inflicted,
|
|
prop->unopposable_casualty_decay_timer,
|
|
prop->unopposable_trigger_timer);
|
|
if (prop->unopposable_trigger_timer>0)
|
|
{
|
|
char string[256];
|
|
|
|
sprintf(string, "/%d h%d", prop->unopposable_trigger_threshold, prop->unopposable_trigger_hysteresis);
|
|
strcat(temporary, string);
|
|
}
|
|
|
|
render_debug_string_at_point(TRUE, &string_point, temporary, global_real_argb_pink);
|
|
point_from_line3d(&string_point, global_up3d, 0.05f, &string_point);
|
|
render_debug_line(TRUE, &prop_start_point, &prop->head_position, global_real_argb_pink);
|
|
point_from_line3d(&prop_start_point, global_up3d, 0.03f, &prop_start_point);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ai_debug.render_props_target_weight && actor->emotions.unopposable_retreat_timer>0)
|
|
{
|
|
real_point3d p0;
|
|
struct prop_datum const *retreating_prop = prop_get(actor->emotions.unopposable_retreat_prop_index);
|
|
|
|
point_from_line3d(&actor->input.position.head_position, global_up3d, 0.03f, &p0);
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "retreat t%d", actor->emotions.unopposable_retreat_timer),
|
|
global_real_argb_red);
|
|
render_debug_line(TRUE, &p0, &retreating_prop->head_position, global_real_argb_red);
|
|
}
|
|
|
|
if (render_exclusive || ai_debug.render_props_web)
|
|
{
|
|
sprintf(temporary, "d%d o%d e%d f%d", dead_count, orphan_count, enemy_count, friend_count);
|
|
}
|
|
else
|
|
{
|
|
sprintf(temporary, "%d", total_count);
|
|
}
|
|
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), temporary, global_real_argb_green);
|
|
}
|
|
|
|
if (ai_debug.render_secondary_looking && actor->control.secondary_look_type>0)
|
|
{
|
|
char const *direction_specification_type_strings[NUMBER_OF_DIRECTION_SPECIFICATION_TYPES] =
|
|
{
|
|
"move",
|
|
"prop",
|
|
"targ",
|
|
"point",
|
|
"vector",
|
|
"danger",
|
|
NULL
|
|
};
|
|
|
|
char const *secondary_look_type_strings[NUMBER_OF_SECONDARY_LOOK_TYPES] =
|
|
{
|
|
"none",
|
|
"noise",
|
|
"moving",
|
|
"impact",
|
|
"ack",
|
|
"bumped",
|
|
"deton",
|
|
"shoot",
|
|
"comm",
|
|
"comm/d",
|
|
"combat",
|
|
"damage",
|
|
"danger",
|
|
"script"
|
|
};
|
|
|
|
char const *secondary_look_priotity_strings[NUMBER_OF_SECONDARY_LOOK_PRIORITIES] =
|
|
{
|
|
"none",
|
|
"def",
|
|
"i/look",
|
|
"i/aim",
|
|
"aim",
|
|
"turn/a",
|
|
"stop/a",
|
|
"over",
|
|
"over/f"
|
|
};
|
|
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(
|
|
temporary,
|
|
"%s %s %s %d",
|
|
direction_specification_type_strings[actor->control.secondary_look_direction.type],
|
|
secondary_look_type_strings[actor->control.secondary_look_type],
|
|
secondary_look_priotity_strings[actor->control.secondary_look_priority],
|
|
actor->control.secondary_look_timer),
|
|
global_real_argb_magenta);
|
|
|
|
switch (actor->control.secondary_look_direction.type)
|
|
{
|
|
case _direction_specification_prop:
|
|
{
|
|
struct prop_datum *prop = prop_get(actor->control.secondary_look_direction.prop_index);
|
|
|
|
render_debug_line(
|
|
TRUE,
|
|
&actor->input.position.head_position,
|
|
&prop->head_position,
|
|
global_real_argb_magenta);
|
|
break;
|
|
}
|
|
case _direction_specification_target:
|
|
if (actor->target.target_prop_index!=NONE)
|
|
{
|
|
struct prop_datum *prop = prop_get(actor->target.target_prop_index);
|
|
|
|
render_debug_line(
|
|
TRUE,
|
|
&actor->input.position.head_position,
|
|
&prop->head_position,
|
|
global_real_argb_magenta);
|
|
}
|
|
break;
|
|
case _direction_specification_point:
|
|
render_debug_line(
|
|
TRUE,
|
|
&actor->input.position.head_position,
|
|
&actor->control.secondary_look_direction.point,
|
|
global_real_argb_magenta);
|
|
break;
|
|
case _direction_specification_vector:
|
|
{
|
|
real_point3d aim_pos;
|
|
|
|
point_from_line3d(
|
|
&actor->input.position.head_position,
|
|
(real_vector3d *)&actor->control.secondary_look_direction.point,
|
|
1.f,
|
|
&aim_pos);
|
|
render_debug_line(
|
|
TRUE,
|
|
&actor->input.position.head_position,
|
|
&aim_pos,
|
|
global_real_argb_magenta);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Pursuit */
|
|
|
|
if (ai_debug.render_pursuit)
|
|
{
|
|
struct pursuit_location const *location = actor_get_pursuit_location(actor_index);
|
|
|
|
if (location)
|
|
{
|
|
if (location->type==0 && actor->target.target_prop_index!=NONE)
|
|
{
|
|
struct prop_datum *prop = prop_get(actor->target.target_prop_index);
|
|
render_debug_line(TRUE, &actor->input.position.head_position, &prop->body_position, actor_action_debug_color(actor_index));
|
|
}
|
|
else
|
|
{
|
|
if (location->type==1)
|
|
{
|
|
render_debug_line(TRUE, &actor->input.position.head_position, &location->position, actor_action_debug_color(actor_index));
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* Aiming vectors */
|
|
|
|
if (render_exclusive && ai_debug.render_aiming_vectors)
|
|
{
|
|
if (actor->meta.unit_index!=NONE)
|
|
{
|
|
real_point3d p0;
|
|
real_point3d p1;
|
|
real_vector3d forward;
|
|
|
|
point_from_line3d(&actor->input.position.head_position, global_up3d, 0.01f, &p0);
|
|
point_from_line3d(&actor->input.position.head_position, &actor->control.desired_facing_vector, 1.5f, &p1);
|
|
point_from_line3d(&p1, global_up3d, 0.01f, &p1);
|
|
render_debug_line(TRUE, &p0, &p1, global_real_argb_orange);
|
|
|
|
point_from_line3d(&actor->input.position.head_position, &actor->control.desired_aiming_vector, 1.4f, &p1);
|
|
point_from_line3d(&p1, global_up3d, 0.02f, &p1);
|
|
render_debug_line(TRUE, &p0, &p1, global_real_argb_green);
|
|
|
|
point_from_line3d(&actor->input.position.head_position, &actor->control.desired_looking_vector, 1.3f, &p1);
|
|
point_from_line3d(&p1, global_up3d, 0.03f, &p1);
|
|
render_debug_line(TRUE, &p0, &p1, global_real_argb_cyan);
|
|
|
|
point_from_line3d(&actor->input.position.head_position, global_up3d, -0.04f, &p0);
|
|
unit_get_facing_vector(actor->meta.unit_index, &forward);
|
|
|
|
render_debug_vector(TRUE, &p0, &forward, 1.f, global_real_argb_red);
|
|
render_debug_vector(TRUE, &p0, &unit->unit.aiming_vector, 1.f, global_real_argb_darkgreen);
|
|
render_debug_vector(TRUE, &p0, &unit->unit.looking_vector, 1.f, global_real_argb_blue);
|
|
|
|
|
|
if (unit->object.type==_object_type_biped && unit->object.parent_object_index==NONE)
|
|
{
|
|
real_point3d end_point;
|
|
|
|
struct biped_definition *biped_definition = biped_definition_get(unit->definition_index);
|
|
|
|
unit_get_facing_vector(actor->meta.unit_index, &forward);
|
|
|
|
if (TEST_FLAG(biped_definition->biped.flags, _biped_flying_bit))
|
|
{
|
|
real_vector3d left;
|
|
real_vector3d up;
|
|
|
|
biped_build_flying_axes(&forward, &left, &up);
|
|
scale_vector3d(&forward, actor->output.throttle.i, (real_vector3d *)&end_point);
|
|
point_from_line3d(&end_point, &left, actor->output.throttle.j, &end_point);
|
|
point_from_line3d(&end_point, &up, actor->output.throttle.k, &end_point);
|
|
}
|
|
else
|
|
{
|
|
real_vector3d v;
|
|
|
|
set_real_vector3d(&v, -forward.j, forward.i, 0.f);
|
|
scale_vector3d(&forward, actor->output.throttle.i, (real_vector3d *)&end_point);
|
|
point_from_line3d(&end_point, &v, actor->output.throttle.j, &end_point);
|
|
}
|
|
|
|
point_from_line3d(&actor->input.position.body_position, global_up3d, 0.1f, &p0);
|
|
render_debug_vector(TRUE, &p0, (real_vector3d *)&end_point, 1.6f, global_real_argb_pink);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Gun positions */
|
|
|
|
if (ai_debug.render_gun_positions && actor->meta.unit_index!=NONE)
|
|
{
|
|
real_point3d estimated_position;
|
|
|
|
real_vector3d *gun_offset = NULL;
|
|
real_argb_color const *color = global_real_argb_red;
|
|
real_vector3d desired_facing = actor->input.aiming_vector;
|
|
|
|
if (normalize2d((real_vector2d *)&desired_facing)>0.f)
|
|
{
|
|
desired_facing.k = 0.f;
|
|
}
|
|
else
|
|
{
|
|
desired_facing = actor->input.facing_vector;
|
|
}
|
|
|
|
if (actor->control.crouching)
|
|
{
|
|
if (magnitude_squared3d(&actor_variant_definition->ranged_combat.gun_offset_crouch)>_real_epsilon)
|
|
{
|
|
gun_offset = &actor_variant_definition->ranged_combat.gun_offset_crouch;
|
|
color = global_real_argb_magenta;
|
|
}
|
|
else
|
|
{
|
|
if (magnitude_squared3d(&actor_definition->perception.gun_offset_crouch)>_real_epsilon)
|
|
{
|
|
gun_offset = &actor_definition->perception.gun_offset_crouch;
|
|
color = global_real_argb_pink;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (magnitude_squared3d(&actor_variant_definition->ranged_combat.gun_offset_stand)>_real_epsilon)
|
|
{
|
|
gun_offset = &actor_variant_definition->ranged_combat.gun_offset_stand;
|
|
color = global_real_argb_magenta;
|
|
}
|
|
else
|
|
{
|
|
if (magnitude_squared3d(&actor_definition->perception.gun_offset_stand)>_real_epsilon)
|
|
{
|
|
gun_offset = &actor_definition->perception.gun_offset_stand;
|
|
color = global_real_argb_pink;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (gun_offset==NULL)
|
|
{
|
|
estimated_position = actor->input.position.head_position;
|
|
}
|
|
else
|
|
{
|
|
unit_estimate_position(
|
|
actor->meta.unit_index,
|
|
_unit_estimate_gun_position,
|
|
&actor->input.position.body_position,
|
|
&desired_facing,
|
|
gun_offset,
|
|
&estimated_position);
|
|
}
|
|
|
|
render_debug_vector(TRUE, &estimated_position, &actor->input.aiming_vector, 1.f, color);
|
|
}
|
|
|
|
/* Targets */
|
|
|
|
if ((ai_debug.render_targets || ai_debug.render_targets_last_visible) &&
|
|
actor->target.target_type!=0 &&
|
|
actor->target.target_prop_index!=NONE)
|
|
{
|
|
real_point3d actor_target_position;
|
|
real_point3d prop_target_position;
|
|
|
|
struct prop_datum *prop = prop_get(actor->target.target_prop_index);
|
|
real_argb_color const *target_color = global_real_argb_white;
|
|
|
|
switch (actor->target.target_type-1)
|
|
{
|
|
case _actor_target_partial_enemy:
|
|
target_color = global_real_argb_grey;
|
|
break;
|
|
case _actor_target_dead_enemy:
|
|
target_color = global_real_argb_green;
|
|
break;
|
|
case _actor_target_disregarded_orphan:
|
|
target_color = global_real_argb_salmon;
|
|
break;
|
|
case _actor_target_inspected_orphan:
|
|
target_color = global_real_argb_blue;
|
|
break;
|
|
case _actor_target_uninspected_orphan:
|
|
target_color = global_real_argb_lightblue;
|
|
break;
|
|
case _actor_target_definite_orphan:
|
|
target_color = global_real_argb_cyan;
|
|
break;
|
|
case _actor_target_acknowledged_enemy:
|
|
target_color = global_real_argb_purple;
|
|
break;
|
|
case _actor_target_clear_line_of_sight_enemy:
|
|
target_color = global_real_argb_yellow;
|
|
break;
|
|
case _actor_target_potentially_dangerous_enemy:
|
|
target_color = global_real_argb_orange;
|
|
break;
|
|
case _actor_target_visible_enemy:
|
|
target_color = global_real_argb_red;
|
|
break;
|
|
case _actor_target_damaging_enemy:
|
|
target_color = global_real_argb_magenta;
|
|
break;
|
|
default:
|
|
match_vassert("c:\\halo\\SOURCE\\ai\\ai_debug.c", 2653, FALSE, NULL);
|
|
}
|
|
|
|
point_from_line3d(&actor->input.position.head_position, global_down3d, 0.01f, &actor_target_position);
|
|
point_from_line3d(&prop->head_position, global_down3d, 0.01f, &prop_target_position);
|
|
render_debug_line(TRUE, &actor_target_position, &prop_target_position, target_color);
|
|
|
|
if (ai_debug.render_targets_last_visible && prop->last_visible_time!=NONE)
|
|
{
|
|
render_debug_sphere(TRUE, &prop->last_visible_head_position, 0.2f, target_color);
|
|
}
|
|
|
|
if (prop->unreachable_ticks>0 && (!ai_debug.render_props || !ai_debug.render_props_unopposable))
|
|
{
|
|
long time = prop->last_unreachable_time!=NONE ? game_time_get()-prop->last_unreachable_time : NONE;
|
|
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "unr %d %d", prop->unreachable_ticks, time),
|
|
global_real_argb_darkgreen);
|
|
}
|
|
}
|
|
|
|
/* States */
|
|
|
|
if (ai_debug.render_states)
|
|
{
|
|
struct observer_result const *camera = observer_get_camera(0);
|
|
|
|
if (camera)
|
|
{
|
|
real_point3d position;
|
|
|
|
point_from_line3d(&camera->position, &camera->forward, 0.05f, &position);
|
|
render_debug_line(TRUE, &position, &actor->input.position.head_position, actor_action_debug_color(actor_index));
|
|
}
|
|
}
|
|
|
|
/* Current state */
|
|
|
|
if (ai_debug.render_current_state)
|
|
{
|
|
if (actor->meta.encounter_index!=NONE)
|
|
{
|
|
struct encounter_datum *encounter = encounter_get(actor->meta.encounter_index);
|
|
struct encounter_definition *encounter_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->ai_encounters,
|
|
DATUM_INDEX_TO_ABSOLUTE_INDEX(actor->meta.encounter_index),
|
|
struct encounter_definition);
|
|
struct squad_definition *squad_definition = TAG_BLOCK_GET_ELEMENT(
|
|
&encounter_definition->squads,
|
|
actor->meta.squad_index,
|
|
struct squad_definition);
|
|
struct squad_datum *squad = encounter_get_squad(encounter, actor->meta.squad_index);
|
|
|
|
if (squad->delay_timer>0)
|
|
{
|
|
if (squad->delay_timer_started)
|
|
{
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "delaying %d", squad->delay_timer),
|
|
global_real_argb_green);
|
|
}
|
|
else
|
|
{
|
|
if (TEST_FLAG(squad_definition->flags, _squad_delay_forever_bit))
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "delay forever", global_real_argb_green);
|
|
}
|
|
else
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "delay not triggered", global_real_argb_green);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (actor->state.action)
|
|
{
|
|
case _actor_action_flee:
|
|
if (actor->state.action_data.flee.has_approach_point>0)
|
|
{
|
|
render_debug_sphere(TRUE, &actor->state.action_data.flee.approach_point, 0.25f, actor_action_debug_color(actor_index));
|
|
}
|
|
break;
|
|
case _actor_action_fight:
|
|
if (actor->state.action_data.alert.move_position_order>0)
|
|
{
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "change %d", actor->state.action_data.alert.move_position_order),
|
|
actor_action_debug_color(actor_index));
|
|
}
|
|
break;
|
|
case _actor_action_guard:
|
|
if (actor->state.action_data.guard.wait_ticks>0)
|
|
{
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "wait %d", actor->state.action_data.guard.wait_ticks),
|
|
actor_action_debug_color(actor_index));
|
|
}
|
|
|
|
if (actor->state.action_data.guard.look_ticks>0)
|
|
{
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "look %d", actor->state.action_data.guard.look_ticks),
|
|
actor_action_debug_color(actor_index));
|
|
}
|
|
|
|
if (actor->state.action_data.guard.cower)
|
|
{
|
|
if (actor->state.action_data.guard.cower_from_retreat)
|
|
{
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "retreat %d", actor->emotions.unopposable_retreat_timer),
|
|
actor_action_debug_color(actor_index));
|
|
}
|
|
else
|
|
{
|
|
char const *string = actor->state.action_data.guard.cower_panicked ? "panic" : "hide";
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "%s %d", string, actor->state.action_data.guard.cower_ticks),
|
|
actor_action_debug_color(actor_index));
|
|
}
|
|
}
|
|
|
|
if (actor->state.action_data.guard.has_guard_direction)
|
|
{
|
|
render_debug_vector(
|
|
TRUE,
|
|
&actor->input.position.head_position,
|
|
&actor->state.action_data.guard.guard_direction,
|
|
2.5f,
|
|
actor_action_debug_color(actor_index));
|
|
}
|
|
break;
|
|
case _actor_action_uncover:
|
|
case _actor_action_search:
|
|
{
|
|
struct pursuit_location *pursuit_location = actor_get_pursuit_location(actor_index);
|
|
long delay = 0;
|
|
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_debug.c", 2773, pursuit_location != NULL);
|
|
|
|
if (pursuit_location->type==_pursuit_location_target)
|
|
{
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "targ(%d)", actor->firing_positions.pursuit_positions_count),
|
|
actor_action_debug_color(actor_index));
|
|
}
|
|
else if (pursuit_location->type==_pursuit_location_position)
|
|
{
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "pt(%d)", actor->firing_positions.pursuit_positions_count),
|
|
actor_action_debug_color(actor_index));
|
|
}
|
|
else
|
|
{
|
|
render_debug_string_at_point(1, ai_debug_drawstack(), "undirected", actor_action_debug_color(actor_index));
|
|
}
|
|
|
|
if (actor->state.action==_actor_action_uncover)
|
|
{
|
|
delay = actor->state.action_data.uncover.uncover_remaining_time;
|
|
}
|
|
else if (actor->state.action==_actor_action_search)
|
|
{
|
|
if (120-actor->state.action_data.search.search_failure_timer<=actor->state.action_data.search.search_remaining_time)
|
|
{
|
|
delay = 120-actor->state.action_data.search.search_failure_timer;
|
|
}
|
|
else
|
|
{
|
|
delay = actor->state.action_data.search.search_remaining_time;
|
|
}
|
|
}
|
|
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), csprintf(temporary, "delay %d", delay), actor_action_debug_color(actor_index));
|
|
break;
|
|
}
|
|
case _actor_action_vehicle:
|
|
if (actor->state.action_data.vehicle.started_entry)
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "entering", global_real_argb_darkgreen);
|
|
}
|
|
else
|
|
{
|
|
char buffer[256];
|
|
|
|
strcpy(buffer, "");
|
|
|
|
if (actor->state.action_data.vehicle.currently_correct_facing)
|
|
{
|
|
strcat(buffer, "facing-ok ");
|
|
}
|
|
|
|
if (actor->state.action_data.vehicle.currently_within_range)
|
|
{
|
|
strcat(buffer, "range-ok ");
|
|
}
|
|
|
|
if (actor->state.action_data.vehicle.fake_entry_potential_timer>0)
|
|
{
|
|
strcat(buffer, csprintf(temporary, "fake-entry %d ", actor->state.action_data.vehicle.fake_entry_potential_timer));
|
|
}
|
|
|
|
if (actor_path_has_path(actor_index))
|
|
{
|
|
if (actor_path_at_destination(actor_index))
|
|
{
|
|
strcat(buffer, "destination-");
|
|
}
|
|
|
|
strcat(buffer, "moving ");
|
|
}
|
|
|
|
if (actor->state.action_data.vehicle.lock_facing)
|
|
{
|
|
strcat(buffer, "locked ");
|
|
}
|
|
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), buffer, global_real_argb_darkgreen);
|
|
}
|
|
|
|
render_debug_line(
|
|
TRUE,
|
|
&actor->input.position.body_position,
|
|
&actor->state.action_data.vehicle.destination_point,
|
|
global_real_argb_darkgreen);
|
|
render_debug_vector(
|
|
TRUE,
|
|
&actor->state.action_data.vehicle.destination_point,
|
|
&actor->state.action_data.vehicle.destination_facing,
|
|
1.f,
|
|
global_real_argb_yellow);
|
|
break;
|
|
case _actor_action_charge:
|
|
switch (actor->state.action_data.charge.goal)
|
|
{
|
|
case _charge_goal_close_range:
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "charge", global_real_argb_red);
|
|
break;
|
|
case _charge_goal_stalking:
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(
|
|
temporary,
|
|
"stalk%s %s disc%d",
|
|
actor->state.action_data.charge.stalking_catch_target ? " catchtarget" : "",
|
|
actor->state.action_data.charge.stalking_currently_exposed ? " exposed" : "",
|
|
actor->state.action_data.charge.stalking_discovery_timer),
|
|
global_real_argb_blue);
|
|
break;
|
|
case _charge_goal_melee:
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "melee", global_real_argb_red);
|
|
break;
|
|
case _charge_goal_melee_leaping:
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "melee-leap", global_real_argb_red);
|
|
break;
|
|
case _charge_goal_vehicle_strafing:
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "strafe", global_real_argb_red);
|
|
break;
|
|
case _charge_goal_vehicle_ramming:
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "ramming", global_real_argb_red);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (actor->emotions.berserk)
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "berserk", global_real_argb_red);
|
|
}
|
|
break;
|
|
case _actor_action_obey:
|
|
{
|
|
struct ai_command_definition *command = NULL;
|
|
|
|
if (actor->state.action_data.obey.command_list_index!=NONE)
|
|
{
|
|
struct ai_command_list_definition const* command_list = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->ai_command_lists,
|
|
actor->state.action_data.obey.command_list_index,
|
|
struct ai_command_list_definition);
|
|
|
|
if (!actor->meta.swarm)
|
|
{
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(
|
|
temporary,
|
|
"command-list %s: #%d of #%d",
|
|
command_list->name,
|
|
actor->state.action_data.obey.simple_control.current_command_index+1,
|
|
command_list->commands.count),
|
|
global_real_argb_purple);
|
|
|
|
if (actor->state.action_data.obey.simple_control.current_command_index<command_list->commands.count)
|
|
{
|
|
command = TAG_BLOCK_GET_ELEMENT(
|
|
&command_list->commands,
|
|
actor->state.action_data.obey.simple_control.current_command_index,
|
|
struct ai_command_definition);
|
|
}
|
|
|
|
if (TEST_FLAG(actor->state.action_data.obey.simple_control.metadata_flags, _obey_metadata_commands_finished_bit))
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "finished", global_real_argb_purple);
|
|
}
|
|
|
|
if (command)
|
|
{
|
|
boolean v427 = FALSE;
|
|
boolean v426 = FALSE;
|
|
boolean v425 = FALSE;
|
|
boolean v424 = FALSE;
|
|
|
|
switch (command->atom_type)
|
|
{
|
|
case _ai_atom_go_to:
|
|
v427 = TRUE;
|
|
v426 = TRUE;
|
|
break;
|
|
case _ai_atom_go_to_and_face:
|
|
v427 = TRUE;
|
|
v426 = TRUE;
|
|
v425 = TRUE;
|
|
break;
|
|
case _ai_atom_move_direction:
|
|
v426 = TRUE;
|
|
v424 = TRUE;
|
|
break;
|
|
case _ai_atom_look:
|
|
case _ai_atom_shoot:
|
|
case _ai_atom_grenade:
|
|
v426 = TRUE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (v427)
|
|
{
|
|
if (actor->state.action_data.charge.melee_suicide)
|
|
{
|
|
render_debug_sphere(
|
|
TRUE,
|
|
&actor->state.action_data.obey.complex_control.destination_point,
|
|
actor->state.action_data.obey.complex_control.destination_radius_valid ?
|
|
actor->state.action_data.obey.complex_control.destination_radius :
|
|
0.5f,
|
|
global_real_argb_purple);
|
|
}
|
|
}
|
|
|
|
if (v426)
|
|
{
|
|
if (command->point1_index>=0 &&
|
|
command->point1_index<command_list->points.count)
|
|
{
|
|
real_point3d position;
|
|
|
|
struct ai_command_point_definition const *point = TAG_BLOCK_GET_ELEMENT(
|
|
&command_list->points,
|
|
command->point1_index,
|
|
struct ai_command_point_definition);
|
|
|
|
render_debug_line(
|
|
TRUE,
|
|
&actor->input.position.head_position,
|
|
&point->position,
|
|
global_real_argb_purple);
|
|
point_from_line3d(&point->position, global_up3d, 0.1f, &position);
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
&position,
|
|
csprintf(temporary, "%d", command->point1_index),
|
|
global_real_argb_purple);
|
|
}
|
|
else
|
|
{
|
|
if (v424 && command->parameter2>=0.f && command->parameter2<360.f)
|
|
{
|
|
real_vector3d v;
|
|
|
|
vector3d_from_angle(&v, command->parameter2);
|
|
render_debug_vector(TRUE, &actor->input.position.head_position, &v, 1.5f, global_real_argb_purple);
|
|
}
|
|
else
|
|
{
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "error: invalid point 1 specified (%d)", command->point1_index),
|
|
global_real_argb_purple);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (v425)
|
|
{
|
|
if (command->point2_index>=0 &&
|
|
command->point2_index<command_list->points.count)
|
|
{
|
|
real_point3d position;
|
|
|
|
struct ai_command_point_definition const *point = TAG_BLOCK_GET_ELEMENT(
|
|
&command_list->points,
|
|
command->point2_index,
|
|
struct ai_command_point_definition);
|
|
|
|
render_debug_line(
|
|
TRUE,
|
|
&actor->input.position.head_position,
|
|
&point->position,
|
|
global_real_argb_purple);
|
|
point_from_line3d(&point->position, global_up3d, 0.1f, &position);
|
|
render_debug_string_at_point(1, &position, csprintf(temporary, "%d", command->point2_index), global_real_argb_purple);
|
|
|
|
}
|
|
else
|
|
{
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "error: invalid point 2 specified (%d)", command->point2_index),
|
|
global_real_argb_purple);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (command)
|
|
{
|
|
action_obey_describe_command(global_scenario_get(), command, temporary, NUMBEROF(temporary));
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), temporary, global_real_argb_purple);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Shooting */
|
|
|
|
if (ai_debug.render_shooting)
|
|
{
|
|
if (actor_debug_info->firing_decision!=_firing_no_target)
|
|
{
|
|
char const *string = actor_move_animation_busy(actor_index) ? "busy " : "";
|
|
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(
|
|
temporary,
|
|
"%srof %.1f err %.1f dmg %.1f blk %d",
|
|
string,
|
|
actor_debug_info->shooting_rof,
|
|
actor->control.burst_error,
|
|
actor->control.burst_damage_modifier,
|
|
actor->control.blocked_communication_timer),
|
|
global_real_argb_white);
|
|
|
|
|
|
if (actor->emotions.berserk)
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "berserk", global_real_argb_yellow);
|
|
}
|
|
else if (actor->control.firing_at_new_target)
|
|
{
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(
|
|
temporary,
|
|
"newtarget %d",
|
|
(unsigned long)(
|
|
actor_variant_definition->ranged_combat.new_target_pattern_time*TICKS_PER_SECOND -
|
|
(real)actor->control.current_fire_target_timer
|
|
)
|
|
),
|
|
global_real_argb_blue);
|
|
}
|
|
else if (actor->control.firing_while_moving)
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "moving", global_real_argb_green);
|
|
}
|
|
|
|
switch (actor->control.fire_state)
|
|
{
|
|
case _actor_fire_state_none:
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
"none",
|
|
global_real_argb_red);
|
|
break;
|
|
case _actor_fire_state_holding:
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "delay %d", actor->control.fire_state_timer),
|
|
global_real_argb_red);
|
|
break;
|
|
case _actor_fire_state_bursting:
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "burst %d", actor->control.fire_state_timer),
|
|
global_real_argb_red);
|
|
break;
|
|
case _actor_fire_state_pausing:
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "pause %d", actor->control.fire_state_timer),
|
|
global_real_argb_red);
|
|
break;
|
|
case _actor_fire_state_wild:
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "wildfire %d", actor->control.fire_state_timer),
|
|
global_real_argb_red);
|
|
break;
|
|
default:
|
|
match_vassert("c:\\halo\\SOURCE\\ai\\ai_debug.c", 3081, FALSE, NULL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (VALID_INDEX(actor_debug_info->firing_decision, NUMBER_OF_ACTOR_DEBUG_FIRING_DECISIONS))
|
|
{
|
|
char const *firing_decision_names[NUMBER_OF_ACTOR_DEBUG_FIRING_DECISIONS] =
|
|
{
|
|
"firing disabled",
|
|
"animation busy",
|
|
"wrong target",
|
|
"no target",
|
|
"outside active region",
|
|
"not visible",
|
|
"outside range",
|
|
"blocked",
|
|
"first burst align",
|
|
"first burst delay",
|
|
"burst pause align",
|
|
"burst pause",
|
|
"firing wildly",
|
|
"bursting",
|
|
"in midair",
|
|
"not crouching",
|
|
"not standing",
|
|
"not stationary",
|
|
"underwater",
|
|
"minimum range"
|
|
};
|
|
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), firing_decision_names[actor_debug_info->firing_decision], global_real_argb_blue);
|
|
}
|
|
}
|
|
|
|
/* Grenade decisions */
|
|
|
|
if (ai_debug.render_grenade_decisions)
|
|
{
|
|
if (actor_debug_info->grenade_eval_time!=NONE &&
|
|
actor_debug_info->grenade_eval_time+7 >= game_time_get())
|
|
{
|
|
switch (actor_debug_info->grenade_decision)
|
|
{
|
|
case _grenade_vehicle:
|
|
csstrcpy(temporary, "in vehicle");
|
|
break;
|
|
case _grenade_unit_busy:
|
|
csstrcpy(temporary, "unit busy");
|
|
break;
|
|
case _grenade_being_hurt:
|
|
sprintf(temporary, "dmg %.2f", actor_debug_info->grenade_current_damage);
|
|
break;
|
|
case _grenade_no_grenades:
|
|
csstrcpy(temporary, "no grenades");
|
|
break;
|
|
case _grenade_random_failed:
|
|
sprintf(
|
|
temporary,
|
|
"random %.2f > %.2f",
|
|
actor_debug_info->grenade_random_value,
|
|
actor_debug_info->grenade_random_chance);
|
|
break;
|
|
case _grenade_encounter_timeout:
|
|
sprintf(temporary, "encounter time %d", actor_debug_info->grenade_encounter_timeout_ticks);
|
|
break;
|
|
case _grenade_target_failed:
|
|
csstrcpy(temporary, "no target");
|
|
break;
|
|
case _grenade_not_enough_enemies:
|
|
sprintf(
|
|
temporary,
|
|
"not enough enemy %d < %d",
|
|
actor_debug_info->grenade_enemy_count,
|
|
actor_debug_info->grenade_required_enemy_count);
|
|
break;
|
|
case _grenade_collateral_damage:
|
|
csstrcpy(temporary, "collateral dmg");
|
|
break;
|
|
case _grenade_trajectory_failed:
|
|
csstrcpy(temporary, "no trajectory");
|
|
break;
|
|
case _grenade_success:
|
|
csstrcpy(temporary, "success");
|
|
break;
|
|
default:
|
|
csstrcpy(temporary, "<unknown>");
|
|
break;
|
|
}
|
|
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), temporary, global_real_argb_pink);
|
|
}
|
|
|
|
if (actor->control.grenade_trying_to_throw)
|
|
{
|
|
real damage = unit==NULL ? 0.f : unit->object.current_body_damage;
|
|
char const *string = actor->meta.unit_index==NONE || !unit_is_busy(actor->meta.unit_index) ? "not-" : "";
|
|
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "trying: %sbusy dmg %.1f", string, damage),
|
|
global_real_argb_pink);
|
|
}
|
|
}
|
|
|
|
/* Danger zones */
|
|
|
|
if (ai_debug.render_danger_zones && actor->danger_zone.danger_type>0)
|
|
{
|
|
real_argb_color const *color;
|
|
|
|
if (actor->danger_zone.currently_perceived)
|
|
{
|
|
if (actor->danger_zone.acknowledgement_timer>0)
|
|
{
|
|
color = global_real_argb_white;
|
|
}
|
|
else
|
|
{
|
|
if (actor->danger_zone.noticed_danger)
|
|
{
|
|
color = global_real_argb_yellow;
|
|
}
|
|
else
|
|
{
|
|
color = global_real_argb_blue;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
color = global_real_argb_darkgreen;
|
|
}
|
|
|
|
render_debug_line(TRUE, &actor->input.position.head_position, &actor->danger_zone.position, color);
|
|
render_debug_sphere(TRUE, &actor->danger_zone.position, actor->danger_zone.danger_radius, global_real_argb_red);
|
|
render_debug_vector(TRUE, &actor->danger_zone.position, &actor->danger_zone.velocity, 45.f, global_real_argb_red);
|
|
render_debug_sphere(
|
|
TRUE,
|
|
&actor->danger_zone.bounding_sphere_center,
|
|
actor->danger_zone.bounding_sphere_radius,
|
|
global_real_argb_orange);
|
|
|
|
if (actor->danger_zone.danger_type==_actor_unopposable_danger_shooting)
|
|
{
|
|
char const *string = actor->danger_zone.projectile.time_until_explosion==NONE ?
|
|
"NONE" :
|
|
csprintf(temporary, "%d", actor->danger_zone.projectile.time_until_explosion);
|
|
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
&actor->danger_zone.position,
|
|
string,
|
|
color);
|
|
}
|
|
|
|
if (actor->danger_zone.danger_type==_actor_unopposable_danger_visible)
|
|
{
|
|
char const *string = actor->danger_zone.projectile.time_until_explosion==NONE ?
|
|
"NONE" :
|
|
csprintf(temporary, "%d", actor->danger_zone.projectile.time_until_explosion);
|
|
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
&actor->danger_zone.position,
|
|
string,
|
|
color);
|
|
}
|
|
|
|
if (actor_debug_info->danger_avoidance_time!=NONE)
|
|
{
|
|
if (actor_debug_info->danger_avoidance_time+15 >= game_time_get())
|
|
{
|
|
boolean avoiding;
|
|
|
|
if (actor_debug_info->danger_abandoned_path)
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "discarded fp", global_real_argb_magenta);
|
|
}
|
|
|
|
avoiding = TRUE;
|
|
|
|
switch (actor_debug_info->danger_decision)
|
|
{
|
|
case _danger_avoidance_none:
|
|
avoiding = FALSE;
|
|
break;
|
|
case _danger_avoidance_unnoticed:
|
|
strcpy(temporary, "unnoticed");
|
|
break;
|
|
case _danger_avoidance_animation_busy:
|
|
strcpy(temporary, "animation busy");
|
|
break;
|
|
case _danger_avoidance_vehicle:
|
|
strcpy(temporary, "in vehicle");
|
|
break;
|
|
case _danger_avoidance_far_away:
|
|
sprintf(
|
|
temporary,
|
|
"far away (%.1f > %.1f)",
|
|
actor_debug_info->danger_far_dist,
|
|
actor_debug_info->danger_far_radius);
|
|
break;
|
|
case _danger_avoidance_outside_zone:
|
|
sprintf(
|
|
temporary,
|
|
"outside (%.1f > %.1f)",
|
|
actor_debug_info->danger_zone_dist,
|
|
actor_debug_info->danger_zone_radius);
|
|
break;
|
|
case _danger_avoidance_evasion_disallowed:
|
|
strcpy(temporary, "evasion not allowed");
|
|
break;
|
|
case _danger_avoidance_no_safe_direction:
|
|
strcpy(temporary, "no safe direction");
|
|
break;
|
|
case _danger_avoidance_no_desire:
|
|
if (actor_debug_info->danger_intersect_time==REAL_MAX)
|
|
{
|
|
strcpy(temporary, "no desire (no int'n)");
|
|
}
|
|
else
|
|
{
|
|
sprintf(temporary, "no desire (int'n %.1f)", actor_debug_info->danger_intersect_time);
|
|
}
|
|
break;
|
|
case _danger_avoidance_can_avoid:
|
|
strcpy(temporary, "can avoid");
|
|
break;
|
|
case _danger_avoidance_imminent_explosion:
|
|
strcpy(temporary, "imminent explosion");
|
|
break;
|
|
case _danger_avoidance_imminent_impact:
|
|
strcpy(temporary, "imminent impact");
|
|
break;
|
|
case _danger_avoidance_no_animation:
|
|
strcpy(temporary, "no animation");
|
|
break;
|
|
case _danger_avoidance_attached_to_us:
|
|
strcpy(temporary, "attached to us");
|
|
break;
|
|
default:
|
|
strcpy(temporary, "<error>");
|
|
break;
|
|
}
|
|
|
|
if (avoiding)
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), temporary, global_real_argb_magenta);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (actor_debug_info->dive_decision_time!=NONE &&
|
|
actor_debug_info->dive_decision_time+15 >= game_time_get())
|
|
{
|
|
switch (actor_debug_info->dive_decision)
|
|
{
|
|
case _dive_not_attempted:
|
|
csstrcpy(temporary, "not attempted");
|
|
break;
|
|
case _dive_cannot_move:
|
|
csstrcpy(temporary, "cannot move");
|
|
break;
|
|
case _dive_no_animation:
|
|
csstrcpy(temporary, "animation unavailable");
|
|
break;
|
|
case _dive_animation_failure:
|
|
csstrcpy(temporary, "animation failed");
|
|
break;
|
|
case _dive_success:
|
|
csstrcpy(temporary, "success");
|
|
break;
|
|
default:
|
|
csstrcpy(temporary, "<error>");
|
|
break;
|
|
}
|
|
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), temporary, global_real_argb_purple);
|
|
}
|
|
}
|
|
|
|
/* Trigger */
|
|
|
|
if (ai_debug.render_trigger && TEST_FLAG(actor->output.control_flags, _unit_control_weapon_primary_trigger_bit))
|
|
{
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "trigger %.1f", actor->output.analog_primary_trigger),
|
|
global_real_argb_yellow);
|
|
}
|
|
|
|
/* Control */
|
|
|
|
if (ai_debug.render_control && actor->meta.unit_index!=NONE)
|
|
{
|
|
short control_flag_bit;
|
|
|
|
const char *control_flag_names[NUMBER_OF_UNIT_CONTROL_FLAGS] =
|
|
{
|
|
"crouch",
|
|
"jump",
|
|
"user1",
|
|
"user2",
|
|
"light",
|
|
"exactfacing",
|
|
"action",
|
|
"equipment",
|
|
"lookdontturn",
|
|
"forcealert",
|
|
"reload",
|
|
"trigger",
|
|
"trigger2",
|
|
"grenade"
|
|
};
|
|
|
|
short flag_count = NUMBER_OF_UNIT_CONTROL_FLAGS;
|
|
short count = 0;
|
|
|
|
strcpy(temporary, "");
|
|
|
|
for (control_flag_bit = 0; control_flag_bit<NUMBER_OF_UNIT_CONTROL_FLAGS; ++control_flag_bit)
|
|
{
|
|
if (TEST_FLAG(actor->output.control_flags, control_flag_bit))
|
|
{
|
|
if (count > 0)
|
|
{
|
|
strcat(temporary, " ");
|
|
}
|
|
|
|
if (control_flag_bit<NUMBER_OF_UNIT_CONTROL_FLAGS)
|
|
{
|
|
strcat(temporary, control_flag_names[control_flag_bit]);
|
|
}
|
|
else
|
|
{
|
|
char string[88];
|
|
|
|
sprintf(string, "<unknown %d>", control_flag_bit);
|
|
strcat(temporary, string);
|
|
}
|
|
|
|
++count;
|
|
}
|
|
}
|
|
|
|
if (count>0)
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), temporary, global_real_argb_yellow);
|
|
}
|
|
|
|
count = 0;
|
|
|
|
strcpy(temporary, "");
|
|
|
|
for (control_flag_bit = 0; control_flag_bit<NUMBER_OF_UNIT_CONTROL_FLAGS; ++control_flag_bit)
|
|
{
|
|
if (TEST_FLAG(actor->output.persistent_control_flags, control_flag_bit))
|
|
{
|
|
if (count > 0)
|
|
{
|
|
csstrcat(temporary, " ");
|
|
}
|
|
|
|
if (control_flag_bit<NUMBER_OF_UNIT_CONTROL_FLAGS)
|
|
{
|
|
strcat(temporary, control_flag_names[control_flag_bit]);
|
|
}
|
|
else
|
|
{
|
|
char string[88];
|
|
|
|
sprintf(string, "<unknown %d>", control_flag_bit);
|
|
strcat(temporary, string);
|
|
}
|
|
|
|
++count;
|
|
}
|
|
}
|
|
|
|
if (count>0)
|
|
{
|
|
char string[88];
|
|
|
|
sprintf(string, ": persistent %d", actor->output.persistent_control_ticks);
|
|
strcat(temporary, string);
|
|
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), temporary, global_real_argb_orange);
|
|
}
|
|
|
|
{
|
|
const char *movement_type_strings[NUMBER_OF_ACTOR_MOVEMENT_TYPES] =
|
|
{
|
|
"noncombat",
|
|
"asleep",
|
|
"combat",
|
|
"flee",
|
|
NULL
|
|
};
|
|
|
|
const char *aiming_speed_names[NUMBER_OF_UNIT_AIMING_SPEEDS] =
|
|
{
|
|
"alert",
|
|
"casual"
|
|
};
|
|
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(), csprintf(
|
|
temporary,
|
|
"m-%s a-%s",
|
|
movement_type_strings[actor->output.movement_type],
|
|
aiming_speed_names[actor->output.aiming_speed]),
|
|
global_real_argb_magenta);
|
|
}
|
|
|
|
if (TEST_FLAG(actor->output.control_flags, _unit_control_weapon_primary_trigger_bit))
|
|
{
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "analog %.2f", actor->output.analog_primary_trigger),
|
|
global_real_argb_cyan);
|
|
}
|
|
|
|
if (actor->output.animation.impulse!=NONE)
|
|
{
|
|
real_point3d base_point;
|
|
real_vector3d alignment_vector_3d;
|
|
|
|
point_from_line3d(&actor->input.position.body_position, global_up3d, 0.2f, &base_point);
|
|
|
|
alignment_vector_3d = actor->control.current_fire_target_aim_vector;
|
|
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "animation %d", actor->output.animation.impulse),
|
|
global_real_argb_pink);
|
|
render_debug_vector(
|
|
TRUE,
|
|
&base_point,
|
|
&alignment_vector_3d,
|
|
1.f,
|
|
global_real_argb_pink);
|
|
}
|
|
|
|
{
|
|
real_point3d p0;
|
|
real_point3d p1;
|
|
|
|
point_from_line3d(&actor->input.position.head_position, global_up3d, 0.01f, &p0);
|
|
point_from_line3d(&actor->input.position.head_position, &actor->output.facing_vector, 1.3f, &p1);
|
|
point_from_line3d(&p1, global_up3d, 0.01f, &p1);
|
|
|
|
render_debug_line(TRUE, &p0, &p1, global_real_argb_red);
|
|
|
|
point_from_line3d(&actor->input.position.head_position, &actor->output.aiming_vector, 1.2f, &p1);
|
|
point_from_line3d(&p1, global_up3d, 0.02f, &p1);
|
|
|
|
render_debug_line(TRUE, &p0, &p1, global_real_argb_green);
|
|
|
|
point_from_line3d(&actor->input.position.head_position, &actor->output.looking_vector, 1.1f, &p1);
|
|
point_from_line3d(&p1, global_up3d, 0.03f, &p1);
|
|
|
|
render_debug_line(TRUE, &p0, &p1, global_real_argb_blue);
|
|
|
|
if (unit->object.type==_object_type_biped && unit->object.parent_object_index==NONE)
|
|
{
|
|
real_vector3d throttle_vector;
|
|
real_vector3d facing_vector;
|
|
real_vector3d right_facing_vector;
|
|
|
|
struct biped_definition const *biped_definition = biped_definition_get(unit->definition_index);
|
|
|
|
unit_get_facing_vector(actor->meta.unit_index, &facing_vector);
|
|
|
|
if (TEST_FLAG(biped_definition->biped.flags, _biped_flying_bit))
|
|
{
|
|
real_vector3d left_vector;
|
|
real_vector3d up_vector;
|
|
|
|
biped_build_flying_axes(&facing_vector, &left_vector, &up_vector);
|
|
scale_vector3d(&facing_vector, actor->output.throttle.i, &throttle_vector);
|
|
point_from_line3d((real_point3d *)&throttle_vector, &left_vector, actor->output.throttle.j, (real_point3d *)&throttle_vector);
|
|
point_from_line3d((real_point3d *)&throttle_vector, &up_vector, actor->output.throttle.k, (real_point3d *)&throttle_vector);
|
|
}
|
|
else
|
|
{
|
|
set_real_vector3d(&right_facing_vector, -facing_vector.j, facing_vector.i, 0.f);
|
|
scale_vector3d(&facing_vector, actor->output.throttle.i, (real_vector3d *)&throttle_vector);
|
|
point_from_line3d((real_point3d *)&throttle_vector, &right_facing_vector, actor->output.throttle.j, (real_point3d *)&throttle_vector);
|
|
}
|
|
|
|
point_from_line3d(&actor->input.position.body_position, global_up3d, 0.1f, &p0);
|
|
render_debug_vector(TRUE, &p0, &throttle_vector, 1.6f, global_real_argb_purple);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Charge decisions */
|
|
|
|
if (ai_debug.render_charge_decisions &&
|
|
actor_debug_info->charge_last_time!=NONE &&
|
|
actor_debug_info->charge_last_time+TICKS_PER_SECOND >= game_time_get())
|
|
{
|
|
switch (actor_debug_info->charge_decision)
|
|
{
|
|
case _charge_vehicle_success:
|
|
csstrcpy(temporary, "vehicle-success");
|
|
break;
|
|
case _charge_vehicle_not_driver:
|
|
csstrcpy(temporary, "vehicle-notdriver");
|
|
break;
|
|
case _charge_melee_swarm_cant:
|
|
csstrcpy(temporary, "melee-swarmcan't");
|
|
break;
|
|
case _charge_melee_inhibited:
|
|
csstrcpy(temporary, "melee-inhibited");
|
|
break;
|
|
case _charge_melee_notarget:
|
|
csstrcpy(temporary, "melee-notarget");
|
|
break;
|
|
case _charge_melee_no_animation:
|
|
sprintf(temporary, "melee-noanimation (%sairborne)", actor_debug_info->field_198 ? "" : "not-");
|
|
break;
|
|
case _charge_melee_cannot_move:
|
|
sprintf(temporary, "melee-cannotmove (%f)", actor_debug_info->field_194);
|
|
break;
|
|
case _charge_melee_success:
|
|
sprintf(temporary, "melee-success (%sairborne)", actor_debug_info->field_198 ? "not-" : "not-" );
|
|
break;
|
|
case _charge_stalking_success:
|
|
csstrcpy(temporary, "stalking-success");
|
|
break;
|
|
case _charge_close_success:
|
|
csstrcpy(temporary, "close-success");
|
|
break;
|
|
default:
|
|
sprintf(temporary, "<unknown charge-setup decision %d>", actor_debug_info->charge_decision);
|
|
break;
|
|
}
|
|
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), temporary, global_real_argb_red);
|
|
}
|
|
|
|
/* Melee check */
|
|
|
|
if (ai_debug.render_melee_check &&
|
|
actor_debug_info->last_melee_time!=NONE &&
|
|
actor_debug_info->last_melee_time+TICKS_PER_SECOND >= game_time_get())
|
|
{
|
|
render_debug_vector(TRUE, &actor_debug_info->field_108, &actor_debug_info->field_114, 1.5f, global_real_argb_red);
|
|
render_debug_point(TRUE, &actor_debug_info->field_120, 0.2f, global_real_argb_red);
|
|
|
|
if (!actor_debug_info->field_139)
|
|
{
|
|
real_vector3d v;
|
|
real_point3d p0;
|
|
real_point3d p1;
|
|
real_point3d p2;
|
|
|
|
render_debug_line(TRUE, &actor_debug_info->field_120, &actor_debug_info->field_13C, global_real_argb_purple);
|
|
render_debug_sphere(TRUE, &actor_debug_info->field_13C, 0.2f, global_real_argb_purple);
|
|
|
|
point_from_line3d(&actor_debug_info->field_108, &actor_debug_info->field_12C, actor_debug_info->field_14C, &p0);
|
|
render_debug_line(TRUE, &actor_debug_info->field_108, &p0, global_real_argb_green);
|
|
|
|
point_from_line3d(&actor_debug_info->field_108, &actor_debug_info->field_12C, actor_debug_info->field_148, &p1);
|
|
perpendicular3d(&actor_debug_info->field_12C, &v);
|
|
|
|
point_from_line3d(&p1, &v, 0.3f, &p0);
|
|
point_from_line3d(&p1, &v, -0.3f, &p2);
|
|
render_debug_line(TRUE, &p0, &p2, global_real_argb_green);
|
|
}
|
|
|
|
if (actor_debug_info->field_138)
|
|
{
|
|
render_debug_vector(TRUE, &actor_debug_info->field_108, &actor_debug_info->field_12C, 2.f, actor_debug_info->field_139 ? global_real_argb_yellow : global_real_argb_purple);
|
|
}
|
|
else
|
|
{
|
|
render_debug_vector(TRUE, &actor_debug_info->field_108, &actor_debug_info->field_12C, 2.f, global_real_argb_white);
|
|
}
|
|
}
|
|
|
|
/* Vehicle avoidance */
|
|
|
|
if (ai_debug.render_vehicle_avoidance &&
|
|
actor_debug_info->last_vehicle_avoidance_time!=NONE &&
|
|
actor_debug_info->last_vehicle_avoidance_time+TICKS_PER_SECOND >= game_time_get())
|
|
{
|
|
real_point3d p0;
|
|
real_point3d p1;
|
|
real_point3d p2;
|
|
|
|
real_vector3d v0;
|
|
|
|
render_debug_line(TRUE, &actor_debug_info->field_C8, &actor_debug_info->field_E4, global_real_argb_green);
|
|
render_debug_sphere(TRUE, &actor_debug_info->field_D4, actor_debug_info->field_E0, global_real_argb_yellow);
|
|
|
|
p1 = actor_debug_info->field_E4;
|
|
p0 = p1;
|
|
|
|
p0.x = p1.x - 0.2f;
|
|
p1.x = p1.x + 0.2f;
|
|
render_debug_line(TRUE, &p0, &p1, global_real_argb_red);
|
|
|
|
p0.x = p0.x + 0.2f;
|
|
p1.x = p1.x - 0.2f;
|
|
p0.y = p0.y - 0.2f;
|
|
p1.y = p1.y + 0.2f;
|
|
render_debug_line(TRUE, &p0, &p1, global_real_argb_red);
|
|
|
|
p0.y = p0.y + 0.2f;
|
|
p1.y = p1.y - 0.2f;
|
|
p0.z = p0.z - 0.2f;
|
|
p1.z = p1.z + 0.2f;
|
|
render_debug_line(TRUE, &p0, &p1, global_real_argb_red);
|
|
vector_from_points3d(&actor_debug_info->field_C8, &actor_debug_info->field_E4, &v0);
|
|
point_from_line3d(&actor_debug_info->field_C8, &v0, actor_debug_info->field_F0, &p2);
|
|
|
|
p1 = p2;
|
|
p0 = p2;
|
|
|
|
p0.x = p2.x - 0.2f;
|
|
p1.x = p2.x + 0.2f;
|
|
render_debug_line(TRUE, &p0, &p1, global_real_argb_blue);
|
|
|
|
p0.x = p0.x + 0.2f;
|
|
p1.x = p1.x - 0.2f;
|
|
p0.y = p0.y - 0.2f;
|
|
p1.y = p1.y + 0.2f;
|
|
render_debug_line(TRUE, &p0, &p1, global_real_argb_blue);
|
|
|
|
p0.y = p0.y + 0.2f;
|
|
p1.y = p1.y - 0.2f;
|
|
p0.z = p0.z - 0.2f;
|
|
p1.z = p1.z + 0.2f;
|
|
render_debug_line(TRUE, &p0, &p1, global_real_argb_blue);
|
|
|
|
if (actor_debug_info->field_F4)
|
|
{
|
|
real_point3d p3;
|
|
real_point3d p4;
|
|
|
|
render_debug_line(TRUE, &actor_debug_info->field_D4, &actor_debug_info->field_F8, global_real_argb_yellow);
|
|
render_debug_line(TRUE, &actor_debug_info->field_C8, &actor_debug_info->field_F8, global_real_argb_red);
|
|
|
|
p4 = actor_debug_info->field_F8;
|
|
p3 = p4;
|
|
|
|
p3.x = p4.x - 0.2f;
|
|
p4.x = p4.x + 0.2f;
|
|
render_debug_line(TRUE, &p3, &p4, global_real_argb_blue);
|
|
|
|
p3.x = p3.x + 0.2f;
|
|
p4.x = p4.x - 0.2f;
|
|
p3.y = p3.y - 0.2f;
|
|
p4.y = p4.y + 0.2f;
|
|
render_debug_line(TRUE, &p3, &p4, global_real_argb_blue);
|
|
|
|
p3.y = p3.y + 0.2f;
|
|
p4.y = p4.y - 0.2f;
|
|
p3.z = p3.z - 0.2f;
|
|
p4.z = p4.z + 0.2f;
|
|
render_debug_line(TRUE, &p3, &p4, global_real_argb_blue);
|
|
}
|
|
}
|
|
|
|
/* Projectile aiming */
|
|
|
|
if (ai_debug.render_projectile_aiming &&
|
|
actor_debug_info->last_projectile_aiming_time!=NONE &&
|
|
actor_debug_info->last_projectile_aiming_time+15 >= game_time_get())
|
|
{
|
|
real_point3d p0;
|
|
real_point3d p1;
|
|
|
|
p1 = actor_debug_info->field_64;
|
|
p0 = p1;
|
|
|
|
p0.x = p1.x - 0.2f;
|
|
p1.x = p1.x + 0.2f;
|
|
render_debug_line(TRUE, &p0, &p1, global_real_argb_blue);
|
|
|
|
p0.x = p0.x + 0.2f;
|
|
p1.x = p1.x - 0.2f;
|
|
p0.y = p0.y - 0.2f;
|
|
p1.y = p1.y + 0.2f;
|
|
render_debug_line(TRUE, &p0, &p1, global_real_argb_blue);
|
|
|
|
p0.y = p0.y + 0.2f;
|
|
p1.y = p1.y - 0.2f;
|
|
p0.z = p0.z - 0.2f;
|
|
p1.z = p1.z + 0.2f;
|
|
render_debug_line(TRUE, &p0, &p1, global_real_argb_blue);
|
|
|
|
render_debug_vector(
|
|
TRUE,
|
|
&actor_debug_info->field_64,
|
|
&actor_debug_info->field_70,
|
|
2.f,
|
|
actor_debug_info->field_60 ? global_real_argb_blue : global_real_argb_red);
|
|
|
|
if (!actor_debug_info->field_60)
|
|
{
|
|
render_debug_sphere(TRUE, &actor_debug_info->field_7C, 0.2f, global_real_argb_red);
|
|
}
|
|
|
|
if (actor_debug_info->field_88)
|
|
{
|
|
render_debug_vector(
|
|
TRUE,
|
|
&actor->input.position.head_position,
|
|
&actor_debug_info->field_98,
|
|
1.f,
|
|
global_real_argb_white);
|
|
render_debug_vector(
|
|
TRUE,
|
|
&actor_debug_info->field_64,
|
|
&actor_debug_info->field_8C,
|
|
2.f,
|
|
global_real_argb_purple);
|
|
}
|
|
}
|
|
|
|
/* Burst Geometry */
|
|
|
|
if (ai_debug.render_burst_geometry && actor->control.fire_state==_actor_fire_state_bursting)
|
|
{
|
|
real_point3d p0;
|
|
real_point3d p1;
|
|
|
|
render_debug_line(TRUE, &actor->control.burst_initial_position, &actor_debug_info->burst_last_known_position, global_real_argb_green);
|
|
|
|
if (magnitude_squared3d(&actor_debug_info->burst_lead_vector)>_real_epsilon)
|
|
{
|
|
render_debug_vector(
|
|
TRUE,
|
|
&actor_debug_info->burst_tracked_position,
|
|
&actor_debug_info->burst_lead_vector,
|
|
1.f,
|
|
global_real_argb_purple);
|
|
}
|
|
|
|
p1.x = actor->control.desired_aiming_vector.i;
|
|
p1.y = actor->control.desired_aiming_vector.j;
|
|
p1.z = actor->control.desired_aiming_vector.k;
|
|
p0 = p1;
|
|
|
|
p0.x = p1.x - 0.2f;
|
|
p1.x = p1.x + 0.2f;
|
|
render_debug_line(TRUE, &p0, &p1, global_real_argb_blue);
|
|
|
|
p0.x = p0.x + 0.2f;
|
|
p1.x = p1.x - 0.2f;
|
|
p0.y = p0.y - 0.2f;
|
|
p1.y = p1.y + 0.2f;
|
|
render_debug_line(TRUE, &p0, &p1, global_real_argb_blue);
|
|
|
|
p0.y = p0.y + 0.2f;
|
|
p1.y = p1.y - 0.2f;
|
|
p0.z = p0.z - 0.2f;
|
|
p1.z = p1.z + 0.2f;
|
|
render_debug_line(TRUE, &p0, &p1, global_real_argb_blue);
|
|
|
|
render_debug_sphere(TRUE, &actor->control.burst_target, 0.1f, global_real_argb_red);
|
|
render_debug_vector(TRUE, &actor->control.burst_target, &actor->control.burst_adjustment, 5.f, global_real_argb_red);
|
|
}
|
|
|
|
|
|
/* Vision cones */
|
|
|
|
if (render_exclusive && ai_debug.render_vision_cones)
|
|
{
|
|
real max_distance;
|
|
real perception_factor;
|
|
real angle_itr;
|
|
real_point3d last_points[2][2][2];
|
|
|
|
real const angle_step = 0.08726646f;
|
|
|
|
real_argb_color const *const *colors[2] =
|
|
{
|
|
&global_real_argb_red,
|
|
&global_real_argb_blue
|
|
};
|
|
|
|
if (actor_debug_info->vision_last_time!=NONE &&
|
|
actor_debug_info->vision_last_time+15 >= game_time_get())
|
|
{
|
|
max_distance = actor_debug_info->vision_last_maximum_distance;
|
|
perception_factor = actor_debug_info->vision_last_perception_factor;
|
|
}
|
|
else
|
|
{
|
|
max_distance = actor_definition->perception.maximum_vision_distance;
|
|
perception_factor = 1.f;
|
|
}
|
|
|
|
render_debug_vector(
|
|
TRUE,
|
|
&actor->input.position.head_position,
|
|
&actor->input.looking_vector,
|
|
max_distance*perception_factor,
|
|
global_real_argb_yellow);
|
|
|
|
for (angle_itr = 0.f; angle_itr<actor_definition->perception.peripheral_vision_angle+angle_step; angle_itr+=angle_step)
|
|
{
|
|
real_point3d direction_vector[2][2];
|
|
real_point3d current_points[2][2][2];
|
|
real full_distance_reference[2];
|
|
real partial_distance_reference;
|
|
|
|
short side_index;
|
|
short ring_index;
|
|
short height_index;
|
|
|
|
real cosine_vertical_angle[2];
|
|
real sine_vertical_angle[2];
|
|
|
|
real horizontal_angle = actor_definition->perception.peripheral_vision_angle>angle_itr ? actor_definition->perception.peripheral_vision_angle : angle_itr;
|
|
real cosine_horizontal_angle = cosine(horizontal_angle);
|
|
real sine_horizontal_angle = sine(horizontal_angle);
|
|
|
|
cosine_vertical_angle[0] = cosine(DEGREES_TO_RADIANS(50));
|
|
sine_vertical_angle[0] = sine(DEGREES_TO_RADIANS(50));
|
|
cosine_vertical_angle[1] = cosine(DEGREES_TO_RADIANS(45));
|
|
sine_vertical_angle[1] = -sine(DEGREES_TO_RADIANS(45));
|
|
|
|
for (side_index = 0; side_index<2; ++side_index)
|
|
{
|
|
for (ring_index = 0; ring_index<2; ++ring_index)
|
|
{
|
|
real_vector3d headspace_vector;
|
|
headspace_vector.i = cosine_horizontal_angle * sine_vertical_angle[ring_index];
|
|
headspace_vector.j = (((real)(side_index==0 ? 1 : -1)) * sine_horizontal_angle) * sine_vertical_angle[ring_index];
|
|
headspace_vector.k = cosine_vertical_angle[ring_index];
|
|
|
|
direction_vector[side_index][ring_index].x = global_zero_vector3d->i;
|
|
direction_vector[side_index][ring_index].y = global_zero_vector3d->j;
|
|
direction_vector[side_index][ring_index].z = global_zero_vector3d->k;
|
|
|
|
point_from_line3d(&direction_vector[side_index][ring_index], &actor->input.looking_vector, headspace_vector.i, &direction_vector[side_index][ring_index]);
|
|
point_from_line3d(&direction_vector[side_index][ring_index], &actor->input.looking_left_vector, headspace_vector.j, &direction_vector[side_index][ring_index]);
|
|
point_from_line3d(&direction_vector[side_index][ring_index], &actor->input.looking_up_vector, headspace_vector.k, &direction_vector[side_index][ring_index]);
|
|
}
|
|
}
|
|
|
|
actor_get_vision_distances(actor_index, max_distance, perception_factor, horizontal_angle, full_distance_reference, &partial_distance_reference);
|
|
|
|
for (side_index = 0; side_index < 2; ++side_index)
|
|
{
|
|
for (ring_index = 0; ring_index < 2; ++ring_index)
|
|
{
|
|
for (height_index = 0; height_index < 2; ++height_index)
|
|
{
|
|
point_from_line3d(
|
|
&actor->input.position.head_position,
|
|
(real_vector3d *)&direction_vector[ring_index][height_index],
|
|
full_distance_reference[side_index],
|
|
¤t_points[side_index][ring_index][height_index]);
|
|
|
|
if (angle_itr>0.f || ring_index==0)
|
|
{
|
|
render_debug_line(
|
|
TRUE,
|
|
¤t_points[side_index][ring_index][height_index],
|
|
&actor->input.position.head_position,
|
|
*colors[side_index]);
|
|
}
|
|
|
|
if (angle_itr>0.f)
|
|
{
|
|
render_debug_line(
|
|
TRUE,
|
|
&last_points[side_index][ring_index][height_index],
|
|
¤t_points[side_index][ring_index][height_index],
|
|
*colors[side_index]);
|
|
}
|
|
}
|
|
|
|
if (angle_itr>0.f || ring_index==0)
|
|
{
|
|
render_debug_line(TRUE, ¤t_points[side_index][ring_index][0], ¤t_points[side_index][ring_index][1], *colors[side_index]);
|
|
}
|
|
}
|
|
}
|
|
|
|
memcpy(last_points, current_points, sizeof(last_points));
|
|
}
|
|
}
|
|
|
|
/* Detailed state */
|
|
|
|
if (render_exclusive &&ai_debug.render_detailed_state)
|
|
{
|
|
struct prop_datum *prop;
|
|
struct prop_iterator iterator;
|
|
char buffer[1024];
|
|
|
|
short tabs[7] =
|
|
{
|
|
100,
|
|
175,
|
|
250,
|
|
325,
|
|
400,
|
|
475,
|
|
550
|
|
};
|
|
|
|
sprintf(buffer, "|n|n|n|n");
|
|
|
|
if (actor->meta.unit_index!=NONE)
|
|
{
|
|
sprintf(
|
|
&buffer[strlen(buffer)],
|
|
"body|t%3.2f|t[0.0,%3.2f]|nshield|t%3.2f|t[0.0,%3.2f]|n|n",
|
|
object_get_actual_body_vitality(actor->meta.unit_index, FALSE),
|
|
object_get_maximum_body_vitality(actor->meta.unit_index, FALSE),
|
|
object_get_actual_shield_vitality(actor->meta.unit_index, FALSE),
|
|
object_get_maximum_shield_vitality(actor->meta.unit_index, FALSE));
|
|
}
|
|
|
|
sprintf(&buffer[strlen(buffer)], "|ntype|tstate|tvis|taud|tlos|ttarget|tlook|n");
|
|
|
|
prop_iterator_new(&iterator, actor_index);
|
|
|
|
while (prop = prop_iterator_next(&iterator))
|
|
{
|
|
char const *states[NUMBER_OF_PROP_STATES] =
|
|
{
|
|
"-----",
|
|
"->ack",
|
|
"ack->",
|
|
" ack ",
|
|
"u/orph",
|
|
"i/orph"
|
|
};
|
|
|
|
char const *los[NUMBER_OF_AI_LINE_OF_SIGHTS] =
|
|
{
|
|
"clear",
|
|
"occl",
|
|
"f/cvr",
|
|
"to/cvr",
|
|
"obstr"
|
|
};
|
|
|
|
char const *lighting_state_strings[NUMBER_OF_PROP_LIGHTING_STATES] =
|
|
{
|
|
" (dark)",
|
|
" (dim)",
|
|
""
|
|
};
|
|
|
|
char const *perceptions[NUMBER_OF_ACTOR_PERCEPTION_TYPES] =
|
|
{
|
|
"",
|
|
"part",
|
|
"full",
|
|
NULL
|
|
};
|
|
|
|
sprintf(
|
|
&buffer[strlen(buffer)],
|
|
"%s|t%s|t%s%s|t%s|t%s|t%3.2f|t%3.2f|n",
|
|
tag_get_name(object_get(prop->unit_index)->definition_index),
|
|
states[prop->state],
|
|
perceptions[prop->visibility],
|
|
lighting_state_strings[prop->lighting],
|
|
perceptions[prop->audibility],
|
|
los[prop->line_of_sight],
|
|
prop->target_weight,
|
|
prop->look_interest);
|
|
}
|
|
|
|
draw_string_set_tab_stops(tabs, NUMBEROF(tabs));
|
|
draw_string_set_color(global_real_argb_white);
|
|
rasterizer_draw_string(NULL, NULL, NULL, 0, buffer);
|
|
draw_string_set_tab_stops(NULL, 0);
|
|
}
|
|
|
|
/* Paths */
|
|
|
|
if (ai_debug.render_paths && (!ai_debug.render_paths_selected_only || render_exclusive))
|
|
{
|
|
if (ai_debug.render_paths_current && actor_path_has_path(actor_index))
|
|
{
|
|
short step_index;
|
|
short first_index;
|
|
real_argb_color const *color;
|
|
|
|
if (actor->control.path.at_destination)
|
|
{
|
|
color = global_real_argb_yellow;
|
|
}
|
|
else
|
|
{
|
|
color = actor->control.path.path.steps_finish_path ? global_real_argb_pink : global_real_argb_purple;
|
|
}
|
|
|
|
first_index = actor->control.path.path.step_index;
|
|
|
|
render_debug_line_offset(
|
|
TRUE,
|
|
&actor->input.position.body_position,
|
|
&actor->control.path.path.steps[actor->control.path.path.step_index].point,
|
|
color,
|
|
0.1f);
|
|
|
|
for (
|
|
step_index = first_index;
|
|
step_index<actor->control.path.path.step_count;
|
|
++step_index)
|
|
{
|
|
real_point3d position;
|
|
|
|
if (step_index>first_index)
|
|
{
|
|
render_debug_line_offset(
|
|
TRUE,
|
|
// FIXME: whatever is going on here
|
|
(const real_point3d *)(&actor->control.path.path.endpoint.surface_index + 4 * step_index),
|
|
&actor->control.path.path.steps[step_index].point,
|
|
color,
|
|
0.1f);
|
|
}
|
|
|
|
point_from_line3d(&actor->control.path.path.steps[step_index].point, global_up3d, 0.1f, &position);
|
|
render_debug_tick(TRUE, &position, global_up3d, 0.02, color);
|
|
}
|
|
|
|
{
|
|
real_point3d endpoint;
|
|
|
|
point_from_line3d(&actor->control.path.path.endpoint.point, global_up3d, 0.1f, &endpoint);
|
|
render_debug_sphere(TRUE, &endpoint, 0.15f, color);
|
|
}
|
|
|
|
if (actor_path_has_path(actor_index))
|
|
{
|
|
sprintf(
|
|
temporary,
|
|
"following path (%d/%d%s)",
|
|
actor->control.path.path.step_index,
|
|
actor->control.path.path.step_count,
|
|
actor_path_at_destination(actor_index) ? " (at destination)" : "");
|
|
}
|
|
else
|
|
{
|
|
strcpy(temporary, "no current path");
|
|
}
|
|
|
|
if (actor->emotions.ignorant_of_broken_surfaces)
|
|
{
|
|
strcat(temporary, " [ignorant]");
|
|
}
|
|
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), temporary, global_real_argb_orange);
|
|
|
|
if (actor_debug_info->last_path_refresh==NONE || actor_debug_info->last_path_refresh+150 < game_time_get())
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "not refreshing path", global_real_argb_blue);
|
|
}
|
|
else
|
|
{
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(temporary, "path refreshed (%d)", game_time_get()-actor_debug_info->last_path_refresh),
|
|
global_real_argb_blue);
|
|
|
|
if (path && path->valid)
|
|
{
|
|
ai_debug_render_path_storage(path);
|
|
}
|
|
else
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "path debugging not available", global_real_argb_red);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/* Postcombat */
|
|
|
|
if (ai_debug.render_postcombat && actor->external_orders.postcombat_type>0)
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), postcombat_type_strings[actor->external_orders.postcombat_type], global_real_argb_green);
|
|
|
|
if (actor->external_orders.postcombat_prop_index!=NONE)
|
|
{
|
|
struct prop_datum *prop = prop_get(actor->external_orders.postcombat_prop_index);
|
|
|
|
render_debug_line(TRUE, &actor->input.position.head_position, &prop->head_position, global_real_argb_green);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void ai_debug_render_path_storage(
|
|
struct path_debug_storage *path)
|
|
{
|
|
if (path && path->valid && path->last_render_id != ai_debug.last_render_id)
|
|
{
|
|
char const *path_traverse_result_strings[NUMBER_OF_PATH_TRAVERSE_RESULTS] =
|
|
{
|
|
"none",
|
|
"invalid start",
|
|
"not close enough",
|
|
"exhausted search",
|
|
"overflowed nodes",
|
|
"success"
|
|
};
|
|
|
|
char const *path_build_result_strings[NUMBER_OF_PATH_BUILD_RESULTS] =
|
|
{
|
|
"none",
|
|
"no destination",
|
|
"cached node missing",
|
|
"not close enough",
|
|
"obstacles blocked",
|
|
"success"
|
|
};
|
|
|
|
boolean const matching_bsp = path->structure_bsp_index==global_structure_bsp_index_get();
|
|
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_debug.c", 3944, (path->path_traverse_result >= 0) && (path->path_traverse_result < NUMBER_OF_PATH_TRAVERSE_RESULTS));
|
|
match_assert("c:\\halo\\SOURCE\\ai\\ai_debug.c", 3945, (path->path_build_result >= 0) && (path->path_build_result < NUMBER_OF_PATH_BUILD_RESULTS));
|
|
|
|
render_debug_string_at_point(
|
|
TRUE,
|
|
ai_debug_drawstack(),
|
|
csprintf(
|
|
temporary,
|
|
"%s / %s (%d)",
|
|
path_traverse_result_strings[path->path_traverse_result],
|
|
path_build_result_strings[path->path_build_result],
|
|
game_time_get()-path->path_time),
|
|
path->path_traverse_result!=_path_traverse_result_success || path->path_build_result!=_path_build_result_success ?
|
|
global_real_argb_red :
|
|
global_real_argb_green
|
|
);
|
|
|
|
if (ai_debug.render_paths_destination)
|
|
{
|
|
if (path->path_state.destination_valid != 0)
|
|
{
|
|
render_debug_line_offset(
|
|
TRUE,
|
|
&path->path_state.input.start_point,
|
|
&path->path_state.destination.point,
|
|
global_real_argb_pink,
|
|
0.1f);
|
|
render_debug_point(TRUE, &path->path_state.destination.point, 0.3f, global_real_argb_green);
|
|
|
|
if (path->path_state.destination.target_radius > 0.0)
|
|
{
|
|
render_debug_sphere(
|
|
TRUE,
|
|
&path->path_state.destination.point,
|
|
path->path_state.destination.target_radius,
|
|
global_real_argb_green);
|
|
}
|
|
|
|
if (path->path_state.destination.surface_index!=NONE && matching_bsp)
|
|
{
|
|
ai_debug_render_surface(
|
|
path->path_state.structure,
|
|
path->path_state.destination.surface_index,
|
|
0.05f,
|
|
global_real_argb_green);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
render_debug_string_at_point(TRUE, ai_debug_drawstack(), "undirected", global_real_argb_green);
|
|
}
|
|
}
|
|
|
|
if (ai_debug.render_paths_raw)
|
|
{
|
|
// TODO: finish
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|