Files
Cesarus85--ringfall-mjolnir-vr/source/objects/objects.c
T
xshxdex98 7ba780ef59 Garbage collection's notices are chatter: on screen only with console_log = "all"
With many enemies (co-op's extra enemies) object memory runs low and the
garbage collector prints "garbage collection critical" and "removing
objects" at every pass, filling the screen for the host and every client.
They are the game's own chatter, so they show as game.console_log says, as
the sound and texture caches' already do; the log keeps every line.
2026-10-05 18:55:53 -05:00

5009 lines
140 KiB
C

/*
OBJECTS.C
*/
/* ---------- headers */
#include "cseries.h"
#include "objects.h"
#include "damage.h"
#include "light_definitions.h"
#include "object_lights.h"
#include "object_types.h"
#include "ai/ai_debug.h"
#include "bitmaps/bitmaps.h"
#include "cache/predicted_resources.h"
#include "cseries/errors.h"
#include "cseries/profile.h"
#include "devices/device_definitions.h"
#include "devices/devices.h"
#include "editor/editor_stubs.h"
#include "effects/contrail_definitions.h"
#include "effects/contrails.h"
#include "effects/effect_definitions.h"
#include "effects/effects.h"
#include "effects/particle_system_definitions.h"
#include "effects/particle_systems.h"
#include "game/game.h"
#include "game/game_engine.h"
#include "game/players.h"
#include "items/weapons.h"
#include "main/console.h"
#include "interface/terminal.h"
#include "math/periodic_functions.h"
#include "memory/memory_pool.h"
#include "models/model_animation_definitions.h"
#include "models/model_definitions.h"
#include "models/models.h"
#include "objects/widgets/widgets.h"
#include "physics/collision_bsp.h"
#include "physics/collision_model_definitions.h"
#include "physics/collision_models.h"
#include "physics/collision_usage.h"
#include "physics/collisions.h"
#include "physics/physics.h"
#include "physics/physics_definitions.h"
#include "render/render_debug.h"
#include "saved games/game_state.h"
#include "scenario/scenario.h"
#include "scenario/scenario_definitions.h"
#include "sound/sound_definitions.h"
#include "sound/game_sound.h"
#include "structures/structure_bsp_definitions.h"
#include "units/units.h"
/* ---------- constants */
/* ---------- macros */
#define MAXIMUM_DUMPS 1024
// This is dangerous, bungie returns the same value regardless of whether the index is valid
#define OBJECT_INCOMING_FUNCTION_GET_VALUE(object, index) \
((index)>=NUMBER_OF_INCOMING_OBJECT_FUNCTIONS+1 ? \
(object)->object.incoming_function_values[(index)-1] : \
(object)->object.incoming_function_values[(index)-1])
#define OBJECT_FRAME_INDEX_GET(object_index) ((object_index) + game_time_get())
/* ---------- structures */
struct object_globals
{
boolean initial_placement;
boolean object_marker_initialized;
boolean force_garbage_collection;
short active_garbage_object_count;
long first_garbage_object_index;
unsigned long last_active_cluster_bits[16];
unsigned long active_cluster_bits[16];
long last_garbage_warn_time;
short pvs_activation_type;
union
{
long object_index;
short cluster_index;
} pvs_activation;
};
struct object_memory_release_function
{
void(*init_function)(unsigned char *, short);
boolean(*function)(char *, unsigned char *, unsigned char *, short);
};
struct dump_datum
{
long definition_index;
short object_type;
short maximum_size;
long total_size;
short count;
short active_count;
short garbage_count;
short dead_count;
short outside_map_count;
short at_rest_count;
};
/* ---------- prototypes */
/* port/linux/game/network_objects.c's: a client of the distributed netcode
makes the host's objects at the host's datum indices, and deletes them only
on the host's word */
long network_objects_new_object_index(void);
boolean network_objects_creating_host_object(void);
boolean network_objects_may_delete(long object_index);
/* port/linux/game/network_coop.c's: the scripts' attaching, on co-op's clients */
void network_coop_note_attach(long parent_index, char const *parent_marker_name, long child_index,
char const *child_marker_name);
void network_coop_note_detach(long parent_index, long child_index);
static void object_connect_lights(long object_index, boolean disconnect, boolean reconnect);
static void object_name_list_allocate(void);
static void object_name_list_free(void);
static void object_name_list_clear(void);
static void object_name_list_new(long object_index, short name_index);
static void object_name_list_delete(long object_index);
static long object_name_list_lookup(short name_index);
static long recursive_object_adder(
long object_index,
boolean(*add_object_function)(long, void *),
void *custom_data,
long object_count,
long maximum_count,
long *object_indices);
static long sort_dumps(struct dump_datum const *dump1, struct dump_datum const *dump2);
static void object_add_to_dump(long object_index, struct dump_datum *dump);
static void object_dump_write(FILE *stream, struct dump_datum *dump);
static short object_find_region_permutations_available_with_variant(struct model_region *region, short variant_number, short *available_indices);
static boolean object_select_random_region_permutations_by_variant(long object_index, struct model *model, short variant_number);
static short object_determine_variant_number(long object_index, struct model *model);
static void object_choose_random_region_permutations(long object_index);
static void object_add_to_list(long *first_object_reference, long object_index);
static void object_remove_from_list(long *first_object_reference, long object_index);
static void attachments_delete(long object_index);
static void object_create_attachments(long object_index);
static void object_delete_attachments(long object_index);
static void object_delete_recursive(long object_index, boolean delete_siblings);
static void object_compute_function_values(long object_index);
static void object_compute_change_colors(long object_index);
/* ---------- globals */
static const struct object_memory_release_function object_memory_release_procs[3] =
{
{ NULL, ai_release_inactive_swarms },
{ ai_find_inactive_encounters, ai_release_inactive_encounters },
{ NULL, NULL }
};
static long *object_name_list = NULL;
static struct memory_pool *object_memory_pool = NULL;
static struct object_globals *object_globals = NULL;
boolean debug_objects_names;
boolean debug_objects_pathfinding_spheres;
boolean debug_objects_physics;
boolean debug_objects_root_node;
long global_object_marker;
struct cluster_partition noncollideable_object_cluster_partition;
struct cluster_partition collideable_object_cluster_partition;
boolean debug_object_garbage_collection;
struct data_array *object_header_data;
boolean debug_objects_position_velocity = FALSE;
/* ---------- public code */
long cluster_get_first_noncollideable_object(
long *reference_index,
short cluster_index)
{
return cluster_partition_get_first_datum(
&noncollideable_object_cluster_partition,
reference_index,
cluster_index);
}
long cluster_get_next_noncollideable_object(
long *reference_index)
{
return cluster_partition_get_next_datum(
&noncollideable_object_cluster_partition,
reference_index);
}
long cluster_get_first_collideable_object(
long *reference_index,
short cluster_index)
{
return cluster_partition_get_first_datum(
&collideable_object_cluster_partition,
reference_index,
cluster_index);
}
long cluster_get_next_collideable_object(
long *reference_index)
{
return cluster_partition_get_next_datum(
&collideable_object_cluster_partition,
reference_index);
}
short object_get_next_cluster(
struct object_cluster_iterator *iterator,
long object_index)
{
match_assert(
"c:\\halo\\SOURCE\\objects\\objects.c",
1049,
iterator->cluster_partition==&collideable_object_cluster_partition || iterator->cluster_partition==&noncollideable_object_cluster_partition);
return cluster_partition_get_next_cluster(iterator->cluster_partition, &iterator->reference_index);
}
void *object_try_and_get_and_verify_type(
long object_index,
unsigned long valid_type_flags)
{
struct object_header_datum* header = object_header_try_and_get(object_index);
void *result = NULL;
if (header)
{
if (TEST_FLAG(valid_type_flags, header->type))
{
result = header->datum;
}
}
return result;
}
void *object_get_and_verify_type(
long object_index,
unsigned long valid_type_flags)
{
struct object_datum *result = object_header_get(object_index)->datum;
match_vassert(
"c:\\halo\\SOURCE\\objects\\objects.c",
1690,
TEST_FLAG(valid_type_flags, result->object.type),
csprintf(
temporary,
"got an object type we didn't expect (expected one of 0x%08x but got #%d).",
valid_type_flags,
result->object.type));
return result;
}
void object_iterator_new(
struct object_iterator *iterator,
unsigned long type_flags,
byte flags)
{
data_verify(object_header_data);
iterator->signature = OBJECT_ITERATOR_SIGNATURE;
iterator->type_flags = type_flags;
iterator->flags = flags;
iterator->absolute_index = 0;
iterator->index = NONE;
return;
}
void *object_iterator_next(
struct object_iterator *iterator)
{
short abs_index;
struct object_header_datum *header;
void *result = NULL;
match_vassert(
"c:\\halo\\SOURCE\\objects\\objects.c",
1720,
iterator->signature==OBJECT_ITERATOR_SIGNATURE,
"uninitialized iterator passed to object_iterator_next()");
data_verify(object_header_data);
abs_index = iterator->absolute_index;
// The operation to get this header is inlined in the original code?
header = (struct object_header_datum *)((char*)object_header_data->data + sizeof(struct object_header_datum) * abs_index);
while (abs_index<object_header_data->count)
{
long index = DATUM_INDEX_NEW(abs_index++, header->identifier);
if (header->identifier)
{
if ((iterator->flags&header->flags)==iterator->flags &&
TEST_FLAG(iterator->type_flags, header->type))
{
iterator->index = index;
result = header->datum;
break;
}
}
++header;
}
iterator->absolute_index = abs_index;
return result;
}
long object_get_ultimate_parent(
long object_index)
{
long result = NONE;
struct object_datum *object;
for (;object_index!=NONE; object_index = object->object.parent_object_index)
{
result = object_index;
object = object_get(object_index);
}
return result;
}
void object_add_scenario_permutation(
long object_index,
struct scenario_object_permutation *permutation)
{
return;
}
void object_set_object_index_for_name_index(
short name_index,
long object_index)
{
if (VALID_INDEX(name_index, global_scenario_get()->object_names.count))
{
object_name_list[name_index] = object_index;
}
return;
}
void objects_fix_for_deleted_object(
long deleted_object_index)
{
struct object_datum *object;
struct object_iterator iterator;
object_iterator_new(&iterator, _object_mask_all, 0);
for (object = (struct object_datum *)object_iterator_next(&iterator);
object;
object = (struct object_datum *)object_iterator_next(&iterator))
{
if (object->object.umbrella_shield_object_index==deleted_object_index)
{
object->object.umbrella_shield_object_index = NONE;
}
object_type_handle_deleted_object(iterator.index, deleted_object_index);
}
return;
}
void object_set_garbage(
long object_index,
boolean garbage)
{
struct object_datum *garbage_object;
struct object_datum* object = object_get(object_index);
long garbage_object_index;
for (garbage_object_index = object_globals->first_garbage_object_index;
garbage_object_index!=NONE;
garbage_object_index = garbage_object->object.next_garbage_object_index)
{
garbage_object = object_get(garbage_object_index);
match_assert(
"c:\\halo\\SOURCE\\objects\\objects.c",
1952,
TEST_FLAG(garbage_object->object.flags, _object_garbage_bit));
}
if (garbage)
{
if (!TEST_FLAG(object->object.flags, _object_garbage_bit))
{
if (!TEST_FLAG(object->object.flags, _object_cannot_be_garbage_bit))
{
object->object.next_garbage_object_index = object_globals->first_garbage_object_index;
object_globals->first_garbage_object_index = object_index;
SET_FLAG(object->object.flags, _object_garbage_bit, TRUE);
}
}
}
else
{
if (TEST_FLAG(object->object.flags, _object_garbage_bit))
{
long *garbage_object_index;
for (garbage_object_index = &object_globals->first_garbage_object_index;
*garbage_object_index!=object_index;
garbage_object_index = &object_get(*garbage_object_index)->object.next_garbage_object_index)
{
}
*garbage_object_index = object->object.next_garbage_object_index;
object->object.next_garbage_object_index = NONE;
SET_FLAG(object->object.flags, _object_garbage_bit, FALSE);
}
}
for (garbage_object_index = object_globals->first_garbage_object_index;
garbage_object_index!=NONE;
garbage_object_index = garbage_object->object.next_garbage_object_index)
{
garbage_object = object_get(garbage_object_index);
match_assert(
"c:\\halo\\SOURCE\\objects\\objects.c",
2006,
TEST_FLAG(garbage_object->object.flags, _object_garbage_bit));
}
return;
}
void garbage_collect_now(
void)
{
object_globals->force_garbage_collection = TRUE;
return;
}
void objects_information_get(
struct objects_information *information)
{
short i;
struct object_header_datum *header;
memset(information, 0, sizeof(*information));
header = (struct object_header_datum *)object_header_data->data;
for (i = 0; i<object_header_data->count; header++)
{
if (header->identifier)
{
information->object_count++;
if (TEST_FLAG(header->flags, _object_header_active_bit))
{
information->active_object_count++;
}
}
i++;
}
information->used_memory = 1.f - (real)memory_pool_get_contiguous_free_size(object_memory_pool) / (real)OBJECT_MEMORY_POOL_SIZE;
return;
}
void object_pvs_set_object(
long object_index)
{
if (object_index == NONE)
{
object_globals->pvs_activation_type = _pvs_activation_normal;
}
else
{
object_globals->pvs_activation_type = _pvs_activation_object;
object_globals->pvs_activation.object_index = object_index;
}
return;
}
void object_pvs_set_camera_point(
short camera_point_index)
{
if (camera_point_index == NONE)
{
object_globals->pvs_activation_type = _pvs_activation_normal;
}
else
{
struct location location;
struct scenario_cutscene_camera_point *point = (struct scenario_cutscene_camera_point *)TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->cutscene_camera_points,
camera_point_index,
struct scenario_cutscene_camera_point);
scenario_location_from_point(&location, &point->position);
if (location.cluster_index==NONE)
{
error(
_error_silent,
"object_pvs_set_camera_point: camera point %s is outside the map",
point->name);
object_globals->pvs_activation_type = FALSE;
}
else
{
object_globals->pvs_activation_type = _pvs_activation_cluster;
object_globals->pvs_activation.cluster_index = location.cluster_index;
}
}
return;
}
void objects_port_set_activating_cluster(
short cluster_index)
{
if (cluster_index == NONE || cluster_index >= global_structure_bsp_get()->clusters.count)
{
object_globals->pvs_activation_type = _pvs_activation_normal;
return;
}
object_globals->pvs_activation_type = _pvs_activation_cluster;
object_globals->pvs_activation.cluster_index = cluster_index;
}
void object_pvs_clear(void)
{
object_globals->pvs_activation_type = _pvs_activation_normal;
return;
}
short objects_get_activating_cluster_index(
void)
{
struct object_header_datum *header;
short result = NONE;
switch (object_globals->pvs_activation_type)
{
case _pvs_activation_cluster:
result = object_globals->pvs_activation.cluster_index;
break;
case _pvs_activation_object:
{
long index = object_globals->pvs_activation.object_index;
header = object_header_try_and_get(index);
if (header && FLAG(header->type) && header->datum)
{
struct object_datum *parent_object = object_get(object_get_ultimate_parent(object_globals->pvs_activation.object_index));
if (TEST_FLAG(parent_object->object.flags, _object_connected_to_map_bit))
{
if (parent_object->object.location.cluster_index!=NONE)
{
match_assert(
"c:\\halo\\SOURCE\\objects\\objects.c",
2279,
parent_object->object.location.cluster_index>=0 &&
parent_object->object.location.cluster_index<global_structure_bsp_get()->clusters.count);
result = parent_object->object.location.cluster_index;
}
}
}
else
{
object_globals->pvs_activation_type = _pvs_activation_normal;
}
break;
}
default:
break;
}
return result;
}
void object_definition_predict(
long definition_index)
{
if (definition_index!=NONE)
{
struct object_definition *object_definition = object_definition_get(definition_index);
predicted_resources_precache(&object_definition->object.predicted_resources);
}
return;
}
void object_predict(
long object_index)
{
while (object_index!=NONE)
{
struct object_datum *object = object_get(object_index);
object_definition_predict(object->definition_index);
object_predict(object->object.first_child_object_index);
object_index = object->object.next_object_index;
}
return;
}
void object_beautify(
long object_index,
boolean beautiful)
{
if (object_index!=NONE)
{
SET_FLAG(object_get(object_index)->object.flags, _object_movie_star_bit, beautiful);
}
return;
}
void *object_header_block_get(
long object_index,
struct object_header_block_reference *reference)
{
struct object_header_datum *object_header = object_header_get(object_index);
struct object_datum *object = object_get(object_index);
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 2443, reference->offset>0);
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 2444, reference->offset+reference->size<=object_header->data_size);
return (byte *)object + reference->offset;
}
static long object_header_new(
struct data_array *data,
long index,
short size)
{
index = (index==NONE ? datum_new(data) : datum_new_at_index(data, index));
if (index!=NONE)
{
struct object_header_datum *header = (struct object_header_datum *)datum_get(data, index);
if (memory_pool_block_allocate(object_memory_pool, (void**)&header->datum, size))
{
header->data_size = size;
memset(header->datum, 0, size);
}
else
{
datum_delete(data, index);
index = NONE;
}
}
return index;
}
static void object_header_delete(
struct data_array *data,
long object_index)
{
struct object_header_datum *object_header = (struct object_header_datum *)datum_get(data, object_index);
if (object_header->datum)
{
memory_pool_block_free(object_memory_pool, (void**)&object_header->datum);
}
datum_delete(data, object_index);
object_header->datum = NULL;
object_header->flags = 0;
return;
}
boolean object_header_block_allocate(
long object_index,
short block_reference_offset,
short size)
{
boolean result;
struct object_header_datum *object_header = object_header_get(object_index);
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 2459, size>=0);
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 2460, object_header->data_size+size<=SHORT_MAX);
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 2462, block_reference_offset>=0);
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 2463, block_reference_offset+sizeof(struct object_header_block_reference)<=object_header->data_size);
if (memory_pool_block_reallocate(
object_memory_pool,
(void **)&object_header->datum,
size + object_header->data_size))
{
struct object_header_block_reference *block;
short original_size = object_header->data_size;
object_header->data_size+= size;
block = (struct object_header_block_reference *)((byte *)object_get(object_index) + block_reference_offset);
block->offset = original_size;
block->size = size;
memset(((byte *)object_header->datum) + block->offset, 0, size);
result = TRUE;
}
else
{
result = FALSE;
}
return result;
}
void object_scripting_set_collideable(
long object_index,
boolean collideable)
{
if (object_index!=NONE)
{
struct object_datum *object = object_get(object_index);
SET_FLAG(object->object.flags, _object_no_collisions_bit, !collideable);
}
return;
}
void object_marker_begin(
void)
{
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 3503, !object_globals->object_marker_initialized);
++global_object_marker;
object_globals->object_marker_initialized = TRUE;
return;
}
void object_marker_end(
void)
{
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 3514, object_globals->object_marker_initialized);
object_globals->object_marker_initialized = FALSE;
return;
}
boolean object_unmarked_function(
long object_index)
{
struct object_datum *object = object_get(object_index);
boolean result;
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 3526, object_globals->object_marker_initialized);
result = object->object.magic_number!=global_object_marker;
return result;
}
boolean object_mark_function(
long object_index)
{
boolean result;
struct object_datum *object = object_get(object_index);
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 3543, object_globals->object_marker_initialized);
if (object->object.magic_number!=global_object_marker)
{
object->object.magic_number = global_object_marker;
result = TRUE;
}
else
{
result = FALSE;
}
return result;
}
void objects_place(
void)
{
object_globals->initial_placement = TRUE;
object_types_place_all(global_scenario_get());
object_globals->initial_placement = FALSE;
return;
}
long find_objects_from_point_vector(
real_point3d const *position,
real_vector3d const *direction,
boolean (*add_object_function)(long, void *),
void *custom_data,
long maximum_object_count,
long *object_indices)
{
long state = 0;
if (scenario_leaf_index_from_point(position)!=NONE)
{
short cluster_index = TAG_BLOCK_GET_ELEMENT(
&global_structure_bsp_get()->leaves,
scenario_leaf_index_from_point(position) & LONG_MAX,
struct structure_leaf)->cluster_index;
if (cluster_index!=NONE)
{
unsigned long *cluster_pvs;
short i;
short bit_vector_size;
object_marker_begin();
cluster_pvs = structure_bsp_get_cluster_pvs(global_structure_bsp_get(), cluster_index);
bit_vector_size = BIT_VECTOR_SIZE_IN_LONGS(global_structure_bsp_get()->clusters.count);
for (i = 0; i<bit_vector_size; ++i)
{
if (cluster_pvs[i])
{
short offset = (short)(i << 5);
short size = MIN(offset + LONG_BITS, global_structure_bsp_get()->clusters.count);
short j;
for (j = offset; j<size; ++j)
{
if (BIT_VECTOR_TEST_FLAG(cluster_pvs, j))
{
long reference;
long k;
for (k = cluster_get_first_collideable_object(&reference, j);
k!=NONE;
k = cluster_get_next_collideable_object(&reference))
{
if (object_mark_function(k))
{
state = recursive_object_adder(
k,
add_object_function,
custom_data,
state,
maximum_object_count,
object_indices);
}
}
}
}
}
}
object_marker_end();
}
}
return state;
}
void objects_dump_memory(
void)
{
struct dump_datum dumps[MAXIMUM_DUMPS];
struct dump_datum dumps_by_type[NUMBER_OF_OBJECT_TYPES];
struct object_iterator iterator;
struct object_datum *object;
struct object_header_datum *header;
FILE *file;
short object_type;
short object_num;
short dump_count = 0;
short overflowed_count = 0;
memset(dumps, 0, sizeof(dumps));
memset(dumps_by_type, 0, sizeof(dumps_by_type));
for (object_type = 0; object_type<NUMBER_OF_OBJECT_TYPES; ++object_type)
{
dumps_by_type[object_type].object_type = object_type;
dumps_by_type[object_type].definition_index = NONE;
}
object_iterator_new(&iterator, _object_mask_all, 0);
while (object = (struct object_datum *)object_iterator_next(&iterator))
{
short index = NONE;
for (object_num = 0; object_num<dump_count; ++object_num)
{
if (dumps[object_num].definition_index==object->definition_index)
{
index = object_num;
break;
}
}
if (index==NONE)
{
if (dump_count<(short)NUMBEROF(dumps))
{
index = dump_count++;
dumps[index].object_type = NONE;
dumps[index].definition_index = object->definition_index;
}
else
{
++overflowed_count;
}
}
header = object_header_get(iterator.index);
if (index!=NONE)
{
object_add_to_dump(iterator.index, &dumps[index]);
}
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 4905, (header->type >= 0) && (header->type < NUMBER_OF_OBJECT_TYPES));
object_add_to_dump(iterator.index, &dumps_by_type[header->type]);
}
qsort(
dumps,
dump_count,
sizeof(struct dump_datum),
(int(__cdecl *)(const void *, const void *))sort_dumps);
qsort(
dumps_by_type,
NUMBEROF(dumps_by_type),
sizeof(struct dump_datum),
(int(__cdecl *)(const void *, const void *))sort_dumps);
file = fopen("d:\\object_memory.txt", "a+b");
if (file)
{
{
struct objects_information information;
objects_information_get(&information);
fprintf(
file,
"#%d objects (#%d active) using %3.2f%% of available memory\n\n",
information.object_count,
information.active_object_count,
100.f * information.used_memory);
}
fprintf(file, "OBJECTS BY TYPE\n");
fprintf(file, "number (active) [garbage/ dead/outside/at-rest] maxsize totsize\n");
for (object_type = 0; object_type<NUMBER_OF_OBJECT_TYPES; object_type++)
{
object_dump_write(file, &dumps_by_type[object_type]);
}
fprintf(file, "\n");
fprintf(file, "OBJECTS BY DEFINITION\n");
fprintf(file, "number (active) [garbage/ dead/outside/at-rest] maxsize totsize\n");
for (object_num = 0; object_num<dump_count; object_num++)
{
object_dump_write(file, &dumps[object_num]);
}
fprintf(file, "\n");
if (overflowed_count>0)
{
fprintf(
file,
"WARNING: overflowed MAXIMUM_DUMPS (%d), this dump does not include %d objects that would not fit!\n",
MAXIMUM_DUMPS,
overflowed_count);
}
fprintf(file, "\n");
fclose(file);
}
return;
}
void objects_initialize(
void)
{
damage_initialize();
widgets_initialize();
object_types_initialize();
lights_initialize();
if (!game_in_editor())
{
object_header_data = game_state_data_new("object", MAXIMUM_OBJECTS_PER_MAP, sizeof(struct object_header_datum));
object_memory_pool = game_state_memory_pool_new("objects", OBJECT_MEMORY_POOL_SIZE);
}
else
{
/* five times the native builds' object limit would not fit a data
array's short count */
object_header_data = data_new("object", SHORT_MAX, sizeof(struct object_header_datum));
object_memory_pool = memory_pool_new("objects", OBJECT_MEMORY_POOL_SIZE*5);
}
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 216, object_header_data && object_memory_pool);
object_globals = (struct object_globals *)game_state_malloc("object globals", NULL, sizeof(*object_globals));
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 219, object_globals);
object_name_list_allocate();
cluster_partition_new(&collideable_object_cluster_partition, "collideable object");
cluster_partition_new(&noncollideable_object_cluster_partition, "noncollideable object");
return;
}
void objects_initialize_for_new_map(
void)
{
damage_initialize_for_new_map();
widgets_initialize_for_new_map();
object_types_initialize_for_new_map();
lights_initialize_for_new_map();
data_make_valid(object_header_data);
object_name_list_clear();
cluster_partition_make_valid(&collideable_object_cluster_partition);
cluster_partition_make_valid(&noncollideable_object_cluster_partition);
memset(object_globals->last_active_cluster_bits, 0, sizeof(object_globals->last_active_cluster_bits));
memset(object_globals->active_cluster_bits, 0, sizeof(object_globals->active_cluster_bits));
object_globals->pvs_activation_type = 0;
object_globals->object_marker_initialized = FALSE;
global_object_marker = 0;
object_globals->first_garbage_object_index = NONE;
object_globals->active_garbage_object_count = 0;
object_globals->last_garbage_warn_time = 0;
return;
}
void objects_dispose_from_old_map(
void)
{
damage_dispose_from_old_map();
widgets_dispose_from_old_map();
object_types_dispose_from_old_map();
lights_dispose_from_old_map();
if (object_header_data->valid)
{
long i;
for (i = data_next_index(object_header_data, NONE);
i!=NONE;
i = data_next_index(object_header_data, i))
{
struct data_array *data = object_header_data;
struct object_header_datum *header = object_header_get(i);
if (header->datum)
{
memory_pool_block_free(object_memory_pool, (void**)&header->datum);
}
datum_delete(data, i);
header->datum = NULL;
header->flags = 0;
}
data_make_invalid(object_header_data);
}
cluster_partition_make_invalid(&collideable_object_cluster_partition);
cluster_partition_make_invalid(&noncollideable_object_cluster_partition);
return;
}
void objects_dispose(
void)
{
widgets_dispose();
object_types_dispose();
lights_dispose();
if (!game_in_editor())
{
if (object_header_data)
{
object_header_data = 0;
}
}
else
{
data_dispose(object_header_data);
}
if (object_memory_pool)
{
object_memory_pool = NULL;
}
object_name_list_free();
cluster_partition_delete(&collideable_object_cluster_partition);
cluster_partition_delete(&noncollideable_object_cluster_partition);
return;
}
void object_activate(
long object_index)
{
struct object_header_datum *header = object_header_get(object_index);
struct object_datum *object = object_get(object_index);
if (!TEST_FLAG(header->flags, _object_header_active_bit) &&
!TEST_FLAG(object->object.flags, _object_cannot_be_activated_bit) &&
object->object.parent_object_index==NONE)
{
SET_FLAG(header->flags, _object_header_active_bit, TRUE);
}
return;
}
void object_deactivate(
long object_index)
{
struct object_header_datum *header = object_header_get(object_index);
struct object_datum *object = object_get(object_index);
if (TEST_FLAG(header->flags, _object_header_active_bit))
{
SET_FLAG(header->flags, _object_header_active_bit, FALSE);
}
return;
}
void object_reset(
long object_index)
{
struct object_datum *object = object_get(object_index);
object->object.translational_velocity = *global_zero_vector3d;
object->object.angular_velocity = *global_zero_vector3d;
SET_FLAG(object->object.flags, _object_at_rest_bit, FALSE);
object_type_reset(object_index);
return;
}
void object_placement_data_new(
struct object_placement_data *data,
long definition_index,
long owner_object_index)
{
long i;
struct object_header_datum *header;
memset(data, 0, sizeof(*data));
data->definition_index = definition_index;
data->flags = 0;
data->forward = *global_forward3d;
data->up = *global_up3d;
data->variant_number = 0;
header = object_header_try_and_get(owner_object_index);
if (header && FLAG(header->type) && header->datum)
{
struct object_datum *object = header->datum;
data->owner_object_index = owner_object_index;
data->owner_player_index = object->object.owner_player_index;
data->owner_team_index = object->object.owner_team_index;
}
else
{
data->owner_object_index = NONE;
data->owner_player_index = NONE;
data->owner_team_index = NONE;
}
i = 0;
do
{
data->change_colors[i] = *global_real_rgb_white;
i++;
}
while (i<NUMBEROF(data->change_colors));
return;
}
void object_disconnect_from_map(
long object_index)
{
struct object_header_datum *header = object_header_get(object_index);
struct object_datum *object = header->datum;
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 957, DATUM_INDEX_TO_IDENTIFIER(object_index));
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 958, TEST_FLAG(object->object.flags, _object_connected_to_map_bit));
if (object->object.parent_object_index!=NONE)
{
object_remove_from_list(&object_get(object->object.parent_object_index)->object.first_child_object_index, object_index);
}
else
{
cluster_partition_disconnect(
(TEST_FLAG(object_get(object_index)->object.flags, _object_has_collision_model_bit) ?
&collideable_object_cluster_partition :
&noncollideable_object_cluster_partition
),
object_index,
&object->object.first_cluster_reference_index);
if (TEST_FLAG(header->flags, _object_header_automatically_deactivate_bit))
{
object_deactivate(object_index);
}
}
SET_FLAG(object->object.flags, _object_connected_to_map_bit, FALSE);
SET_FLAG(header->flags, _object_header_connected_to_map_bit, FALSE);
return;
}
short object_get_first_cluster(
struct object_cluster_iterator *iterator,
long object_index)
{
long ultimate_parent_index = object_get_ultimate_parent(object_index);
iterator->cluster_partition = TEST_FLAG(object_get(ultimate_parent_index)->object.flags, _object_has_collision_model_bit) ?
&collideable_object_cluster_partition :
&noncollideable_object_cluster_partition;
return cluster_partition_get_first_cluster(
iterator->cluster_partition,
&iterator->reference_index,
object_get(ultimate_parent_index)->object.first_cluster_reference_index);
}
real_matrix4x3 *object_get_node_matrices(
long object_index)
{
/* while a frame is drawn, the pose between the last two ticks
(port/linux/game/render_interpolation.c) */
real_matrix4x3 *interpolated = render_interpolation_object_node_matrices(object_index);
if (interpolated)
return interpolated;
return (real_matrix4x3 *)object_header_block_get(object_index, &object_get(object_index)->object.node_matrices);
}
char const *object_get_attachment_marker_name(
long object_index,
short attachment_index)
{
const char *result;
struct object_datum *object = object_get(object_index);
struct object_definition *object_definition = object_definition_get(object->definition_index);
if (VALID_INDEX(attachment_index, object_definition->object.attachments.count))
{
result = TAG_BLOCK_GET_ELEMENT(
&object_definition->object.attachments,
attachment_index,
struct object_attachment_definition)->marker_name;
}
else
{
result = NULL;
}
return result;
}
boolean object_has_node(
long object_index,
short node_index)
{
struct object_datum *object = object_get(object_index);
long model_index = object_definition_get(object->definition_index)->object.model.index;
boolean result = FALSE;
if (model_index==NONE)
{
if (!node_index)
{
result = TRUE;
}
}
else
{
struct model *model = model_definition_get(model_index);
if (VALID_INDEX(node_index, model->nodes.count))
{
result = TRUE;
}
}
return result;
}
void object_set_automatic_deactivation(
long object_index,
boolean automatic_deactivation)
{
struct object_header_datum *header = object_header_get(object_index);
struct object_datum *object = object_get(object_index);
SET_FLAG(header->flags, _object_header_automatically_deactivate_bit, automatic_deactivation);
if (automatic_deactivation)
{
if (object->object.parent_object_index==NONE && object->object.location.cluster_index==NONE)
{
object_deactivate(object_index);
}
}
else
{
if (!TEST_FLAG(header->flags, _object_header_active_bit))
{
object_activate(object_index);
}
}
return;
}
void object_set_visibility(
long object_index,
boolean visible_flag)
{
struct object_datum *object = object_get(object_index);
struct object_definition *object_definition = object_definition_get(object->definition_index);
if (object_definition->object.model.index!=NONE)
{
if (TEST_FLAG(object->object.flags, _object_invisible_bit) && visible_flag)
{
object_connect_lights(object_index, FALSE, TRUE);
}
else if (!TEST_FLAG(object->object.flags, _object_invisible_bit) && !visible_flag)
{
object_connect_lights(object_index, TRUE, FALSE);
}
}
if (!visible_flag || object_definition->object.model.index!=NONE)
{
struct object_header_datum *header = object_header_get(object_index);
SET_FLAG(object->object.flags, _object_invisible_bit, !visible_flag);
SET_FLAG(header->flags, _object_header_visible_bit, visible_flag);
}
return;
}
void object_get_velocities(
long object_index,
real_vector3d *translational_velocity,
real_vector3d *angular_velocity)
{
struct object_datum *ultimate_parent = object_get(object_index);
while (ultimate_parent->object.parent_object_index!=NONE)
{
ultimate_parent = object_get(ultimate_parent->object.parent_object_index);
}
if (translational_velocity)
{
*translational_velocity = ultimate_parent->object.translational_velocity;
}
if (angular_velocity)
{
*angular_velocity = ultimate_parent->object.angular_velocity;
}
return;
}
struct location *object_get_location(
long object_index,
struct location *location)
{
*location = object_get(object_get_ultimate_parent(object_index))->object.location;
return location;
}
void object_start_interpolation(
long object_index,
short frame_count)
{
struct object_datum *object = object_get(object_index);
struct object_definition *object_definition = object_definition_get(object->definition_index);
short node_count = model_definition_get(object_definition->object.model.index)->nodes.count;
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1523, !TEST_FLAG(_object_mask_cannot_interpolate, object->object.type));
memcpy(
object_header_block_get(object_index, &object->object.original_node_orientations),
object_header_block_get(object_index, &object->object.node_orientations),
sizeof(real_orientation)*node_count);
if (frame_count>=
object->object.animation.interpolation_frame_count-
object->object.animation.interpolation_frame_index
)
{
object->object.animation.interpolation_frame_index = 0;
object->object.animation.interpolation_frame_count = frame_count;
}
return;
}
void object_offset_interpolation(
long object_index,
real_vector3d *offset)
{
struct object_datum *object = object_get(object_index);
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1546, !TEST_FLAG(_object_mask_cannot_interpolate, object->object.type));
if (object->object.animation.interpolation_frame_count)
{
real_orientation* orientation = (real_orientation *)object_header_block_get(
object_index,
&object->object.original_node_orientations);
orientation->translation.x+= offset->i;
orientation->translation.y+= offset->j;
orientation->translation.z+= offset->k;
}
return;
}
void object_permute_region(
long object_index,
char *permutation_name,
short desired_region_index,
boolean active_flag)
{
struct object_datum *object = object_get(object_index);
struct object_definition *object_definition = object_definition_get(object->definition_index);
if (object_definition->object.model.index!=NONE)
{
short region_index;
struct model* model = model_definition_get(object_definition->object.model.index);
for (region_index = 0; region_index<model->regions.count; region_index++)
{
if (desired_region_index==NONE || desired_region_index==region_index)
{
short permutation_index;
struct model_region *region = TAG_BLOCK_GET_ELEMENT(
&model->regions,
region_index,
struct model_region);
for (permutation_index = 0; permutation_index<region->permutations.count; permutation_index++)
{
struct model_region_permutation *permutation = TAG_BLOCK_GET_ELEMENT(
&region->permutations,
permutation_index,
struct model_region_permutation);
if (!_stricmp(permutation->name, permutation_name))
{
object->object.region_permutations[region_index] = active_flag ? permutation_index : 0;
break;
}
}
}
}
}
return;
}
boolean object_get_function_value(
long object_index,
short function_index,
real *value_reference)
{
boolean result;
struct object_datum *object = object_get(object_index);
if (function_index==NONE)
{
*value_reference = 1.f;
result = TRUE;
}
else
{
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1654, function_index>=0 && function_index<NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS);
*value_reference = object->object.outgoing_function_values[function_index];
result = TEST_FLAG(object->object.functions_active_flags, function_index);
}
return result;
}
short objects_in_clusters_by_indices(
unsigned long class_flags,
short cluster_count,
short const *cluster_indices,
short maximum_object_count,
long *object_indices)
{
short cluster_index;
short object_count = 0;
if (!class_flags)
{
class_flags = NONE;
}
object_marker_begin();
for (cluster_index = 0; cluster_index<cluster_count; ++cluster_index)
{
short cluster_num = cluster_indices[cluster_index];
if (TEST_FLAG(class_flags, 0))
{
long reference;
long object_index;
for (object_index = cluster_get_first_collideable_object(&reference, cluster_num);
object_index!=NONE;
object_index = cluster_get_next_collideable_object(&reference))
{
if (object_mark_function(object_index))
{
if (object_count>=maximum_object_count)
{
object_marker_end();
return object_count;
}
object_indices[object_count++] = object_index;
}
}
}
if (TEST_FLAG(class_flags, 1))
{
long reference;
long object_index;
for (object_index = cluster_get_first_noncollideable_object(&reference, cluster_num);
object_index!=NONE;
object_index = cluster_get_next_noncollideable_object(&reference))
{
if (object_mark_function(object_index))
{
if (object_count>=maximum_object_count)
{
object_marker_end();
return object_count;
}
object_indices[object_count++] = object_index;
}
}
}
}
object_marker_end();
return object_count;
}
long object_index_from_name_index(
short name_index)
{
return name_index>=0 && name_index<MAXIMUM_OBJECT_NAMES_PER_SCENARIO ? object_name_list[name_index] : NONE;
}
void objects_disconnect_from_structure_bsp(
void)
{
struct object_datum *object;
struct object_iterator iterator;
object_iterator_new(&iterator, _object_mask_all, 0);
while (object = (struct object_datum *)object_iterator_next(&iterator))
{
if (TEST_FLAG(object->object.flags, _object_connected_to_map_bit) &&
object->object.parent_object_index==NONE)
{
object_disconnect_from_map(iterator.index);
SET_FLAG(object->object.flags, _object_connected_to_map_bit, TRUE);
}
object_type_disconnect_from_structure_bsp(iterator.index);
}
return;
}
boolean object_visible_to_any_player(
long object_index)
{
struct object_header_datum *header = object_header_get(object_index);
struct object_datum *object = object_get(object_index);
boolean visible = FALSE;
if (TEST_FLAG(header->flags, _object_header_active_bit) &&
TEST_FLAG(object->object.flags, _object_connected_to_map_bit) &&
!TEST_FLAG(object->object.flags, _object_outside_of_map_bit))
{
short cluster_index;
struct object_cluster_iterator iterator;
const unsigned long* pvs = players_get_combined_pvs();
for (cluster_index = object_get_first_cluster(&iterator, object_index);
cluster_index!=NONE && !BIT_VECTOR_TEST_FLAG(pvs, cluster_index);
cluster_index = object_get_next_cluster(&iterator, object_index))
{
;
}
if (cluster_index!=NONE)
{
long player_index;
const real radius_squared = object->object.bounding_sphere_radius*object->object.bounding_sphere_radius;
for (player_index = data_next_index(player_data, NONE);
player_index!=NONE;
player_index = data_next_index(player_data, player_index))
{
struct player_datum *player = player_get(player_index);
if (player->unit_index!=NONE)
{
real player_distance_squared;
real_point3d player_position;
unit_get_head_position(player->unit_index, &player_position);
player_distance_squared = distance_squared3d(&player_position, &object->object.bounding_sphere_center);
if (player_distance_squared<radius_squared)
{
visible = TRUE;
break;
}
else
{
real_vector3d eye_to_point;
real const sloppy_maximum_field_of_view = DEGREES_TO_RADIANS(45);
struct unit_datum const *unit = unit_get(player->unit_index);
real distance = normalize3d(vector_from_points3d(&player_position, &object->object.bounding_sphere_center, &eye_to_point));
if (dot_product3d(&unit->unit.desired_aiming_vector, &eye_to_point) > cosine(arctangent(object->object.bounding_sphere_radius, distance) + sloppy_maximum_field_of_view))
{
visible = TRUE;
break;
}
}
}
}
}
}
return visible;
}
void object_pvs_activate(
long object_index)
{
object_pvs_set_object(object_index);
return;
}
void objects_scripting_set_scale(
long object_index,
real scale,
short interpolation_frame_count)
{
if (object_index!=NONE)
{
struct object_datum *object = object_get(object_index);
object->object.scale = scale;
if (!TEST_FLAG(_object_mask_cannot_interpolate, object->object.type))
{
object_start_interpolation(object_index, interpolation_frame_count);
}
}
return;
}
static void object_delete_initial_recursive(
long object_index,
boolean delete_siblings)
{
struct object_header_datum *header = object_header_get(object_index);
struct object_datum *object = object_get(object_index);
if (game_engine_running() && object->object.type == _object_type_weapon)
{
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 829, !(weapon_is_flag(object_index)));
}
if (object->object.first_child_object_index!=NONE)
{
object_delete_initial_recursive(object->object.first_child_object_index, TRUE);
}
if (delete_siblings)
{
if (object->object.next_object_index!=NONE)
{
object_delete_initial_recursive(object->object.next_object_index, TRUE);
}
}
SET_FLAG(header->flags, _object_header_being_deleted_bit, TRUE);
object_set_visibility(object_index, FALSE);
object_name_list_delete(object_index);
return;
}
void object_delete(
long object_index)
{
if (!network_objects_may_delete(object_index))
return;
object_delete_initial_recursive(object_index, FALSE);
return;
}
void object_reconnect_to_map(
long object_index,
struct location const *location)
{
struct object_header_datum *header = object_header_get(object_index);
struct object_datum *object = header->datum;
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 879, DATUM_INDEX_TO_IDENTIFIER(object_index));
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 880, !TEST_FLAG(object->object.flags, _object_connected_to_map_bit));
if (object->object.parent_object_index!=NONE)
{
struct object_datum *parent_object = object_get(object->object.parent_object_index);
object_add_to_list(&parent_object->object.first_child_object_index, object_index);
SET_FLAG(header->flags, _object_header_child_bit, TRUE);
object->object.location.cluster_index =NONE;
}
else
{
/* location may point here after the if below: declared inside it,
the location would be read after its lifetime ended, which an
optimising compiler is free to break (release builds crashed placing
objects at level start, reading a stack slot reused meanwhile) */
struct location bounding_sphere_location;
if (!location)
{
scenario_location_from_point(&bounding_sphere_location, &object->object.bounding_sphere_center);
location = &bounding_sphere_location;
if (bounding_sphere_location.cluster_index==NONE)
{
scenario_location_from_point(&bounding_sphere_location, &object->object.position);
}
}
if (location->cluster_index!=NONE)
{
object->object.location = *location;
header->cluster_index = object->object.location.cluster_index;
SET_FLAG(object->object.flags, _object_outside_of_map_bit, FALSE);
}
else
{
SET_FLAG(object->object.flags, _object_outside_of_map_bit, TRUE);
}
SET_FLAG(header->flags, _object_header_child_bit, FALSE);
cluster_partition_reconnect(
(TEST_FLAG(object_get(object_index)->object.flags, _object_has_collision_model_bit) ?
&collideable_object_cluster_partition :
&noncollideable_object_cluster_partition
),
object_index,
&object->object.first_cluster_reference_index,
&object->object.bounding_sphere_center,
object->object.bounding_sphere_radius,
&object->object.location);
if (TEST_FLAG(header->flags, _object_header_automatically_deactivate_bit))
{
if (header->cluster_index==NONE || !BIT_VECTOR_TEST_FLAG(players_get_combined_pvs(), header->cluster_index))
{
if (TEST_FLAG(object->object.flags, _object_deleted_when_deactivated_bit))
{
object_delete(object_index);
}
}
else
{
object_activate(object_index);
}
}
}
SET_FLAG(object->object.flags, _object_connected_to_map_bit, TRUE);
SET_FLAG(header->flags, _object_header_connected_to_map_bit, TRUE);
return;
}
real_matrix4x3 *object_get_node_matrix(
long object_index,
short node_index)
{
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1060, object_has_node(object_index, node_index));
{
real_matrix4x3 *interpolated = render_interpolation_object_node_matrices(object_index);
if (interpolated)
return &interpolated[node_index];
}
return &((real_matrix4x3 *)object_header_block_get(object_index, &object_get(object_index)->object.node_matrices))[node_index];
}
short object_get_marker_by_name(
long object_index,
char const *name,
struct object_marker *markers,
short maximum_marker_count)
{
short marker;
struct object_datum const *object = object_get(object_index);
struct object_definition const *object_definition = object_definition_get(object->definition_index);
/* port: the matrices the renderer draws with, so a marker an effect, a
particle system or a contrail hangs off moves as the object does, between
the ticks too (port/linux/game/render_interpolation.c). While a tick runs,
which is when the game asks for markers, this is the same array. */
real_matrix4x3 const *matrices = object_get_node_matrices(object_index);
marker = model_get_marker_by_name(
object_definition->object.model.index,
name,
object->object.region_permutations,
FALSE,
NONE,
matrices,
TEST_FLAG(object->object.flags, _object_mirrored_bit),
markers,
maximum_marker_count);
if (marker==0)
{
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1113, maximum_marker_count>0);
markers[marker].node_index = 0;
matrix4x3_identity(&markers->node_matrix);
markers[marker].matrix = *object_get_node_matrix(object_index, 0);
if (TEST_FLAG(object->object.flags, _object_mirrored_bit))
{
negate_vector3d(&markers[0].matrix.left, &markers[0].matrix.left);
}
if (name && name[0]=='\0')
{
marker = 1;
}
}
return marker;
}
void object_align_marker_to_matrix(
struct object_datum *object,
struct object_marker const *child_marker,
real_matrix4x3 const *destination_matrix)
{
real_matrix4x3 marker_transform_matrix;
real_matrix4x3 final_matrix;
real_vector3d vector;
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1173, object);
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1174, child_marker);
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1175, destination_matrix);
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1177, valid_real_matrix4x3(destination_matrix));
matrix4x3_from_point_and_vectors(&final_matrix, &object->object.position, &object->object.forward, &object->object.up);
matrix4x3_inverse(&final_matrix, &marker_transform_matrix);
matrix4x3_multiply(&marker_transform_matrix, &child_marker->matrix, &marker_transform_matrix);
matrix4x3_inverse(&marker_transform_matrix, &marker_transform_matrix);
matrix4x3_multiply(destination_matrix, &marker_transform_matrix, &final_matrix);
object->object.position = final_matrix.position;
object->object.forward = final_matrix.forward;
cross_product3d(&final_matrix.forward, &final_matrix.up, &vector);
cross_product3d(&vector, &final_matrix.forward, &object->object.up);
normalize3d(&object->object.forward);
normalize3d(&object->object.up);
return;
}
void object_detach(
long child_object_index)
{
real_matrix4x3 object_translation_matrix;
real_matrix4x3 matrix;
real_matrix4x3 object_rotation_matrix;
real_matrix4x3 *node_matrix;
struct object_datum *child_object = object_get(child_object_index);
struct object_datum *parent_object = object_get(child_object->object.parent_object_index);
object_disconnect_from_map(child_object_index);
node_matrix = object_get_node_matrix(child_object->object.parent_object_index, child_object->object.parent_node_index);
matrix4x3_translation(&object_translation_matrix, &child_object->object.position);
matrix4x3_rotation_from_vectors(&object_rotation_matrix, &child_object->object.forward, &child_object->object.up);
matrix4x3_multiply(node_matrix, &object_translation_matrix, &matrix);
matrix4x3_multiply(&matrix, &object_rotation_matrix, &matrix);
matrix4x3_to_point_and_vectors(
&matrix,
&child_object->object.position,
&child_object->object.forward,
&child_object->object.up);
child_object->object.translational_velocity = parent_object->object.translational_velocity;
child_object->object.angular_velocity = parent_object->object.angular_velocity;
child_object->object.parent_node_index = NONE;
child_object->object.parent_object_index = NONE;
object_reconnect_to_map(child_object_index, NULL);
object_activate(child_object_index);
return;
}
real_point3d *object_get_origin(
long object_index,
real_point3d *origin)
{
struct object_datum *object = object_get(object_index);
if (object->object.parent_object_index==NONE)
{
*origin = object->object.position;
}
else
{
real_matrix4x3 *matrix = object_get_node_matrix(object->object.parent_object_index, object->object.parent_node_index);
matrix4x3_transform_point(matrix, &object->object.position, origin);
}
return origin;
}
void object_get_orientation(
long object_index,
real_vector3d *forward,
real_vector3d *up)
{
struct object_datum *object = object_get(object_index);
if (object->object.parent_object_index==NONE)
{
if (forward)
{
*forward = object->object.forward;
}
if (up)
{
*up = object->object.up;
}
}
else
{
real_matrix4x3 *node_matrix = object_get_node_matrix(
object->object.parent_object_index,
object->object.parent_node_index);
if (forward)
{
matrix4x3_transform_normal(node_matrix, &object->object.forward, forward);
}
if (up)
{
matrix4x3_transform_normal(node_matrix, &object->object.up, up);
}
}
if (forward && up)
{
match_assert_valid_real_vector3d_axes2("c:\\halo\\SOURCE\\objects\\objects.c", 1462, forward, up);
}
return;
}
real_matrix4x3 *object_get_world_matrix(
long object_index,
real_matrix4x3 *matrix)
{
struct object_datum *object = object_get(object_index);
matrix4x3_from_point_and_vectors(matrix, &object->object.position, &object->object.forward, &object->object.up);
if (object->object.parent_object_index!=NONE)
{
real_matrix4x3* node_matrix = object_get_node_matrix(object->object.parent_object_index, object->object.parent_node_index);
matrix4x3_multiply(node_matrix, matrix, matrix);
}
return matrix;
}
void object_inverse_kinematics(
long hand_object_index,
char *hand_marker_name,
long grip_object_index,
char *grip_marker_name,
real_matrix4x3 *node_matrices)
{
struct object_marker hand_marker;
struct object_marker grip_marker;
real_matrix4x3 desired_hand_matrix;
struct object_datum *hand_object = object_get(hand_object_index);
struct object_definition *hand_object_definition = object_definition_get(hand_object->definition_index);
struct model *hand_model = model_definition_get(hand_object_definition->object.model.index);
if (object_get_marker_by_name(hand_object_index, hand_marker_name, &hand_marker, 1) &&
object_get_marker_by_name(grip_object_index, grip_marker_name, &grip_marker, 1))
{
short hand_node_index = hand_marker.node_index;
short hand_parent_node_index = TAG_BLOCK_GET_ELEMENT(&hand_model->nodes, hand_node_index, struct model_node)->parent_node_index;
short desired_hand_index = NONE;
if (hand_parent_node_index!=NONE)
{
desired_hand_index = TAG_BLOCK_GET_ELEMENT(&hand_model->nodes, hand_parent_node_index, struct model_node)->parent_node_index;
}
if (desired_hand_index!=NONE)
{
matrix4x3_inverse(&hand_marker.node_matrix, &desired_hand_matrix);
matrix4x3_multiply(&grip_marker.matrix, &desired_hand_matrix, &desired_hand_matrix);
inverse_kinematics_adjust_matrices(
&desired_hand_matrix,
&node_matrices[desired_hand_index],
&node_matrices[hand_parent_node_index],
&node_matrices[hand_node_index]);
}
}
return;
}
short objects_in_sphere(
unsigned long class_flags,
unsigned long type_flags,
struct location const *location,
real_point3d const *center,
real radius,
long *object_indices,
short maximum_count)
{
long temporary_object_indices[MAXIMUM_OBJECTS_PER_MAP];
short cluster_indices[MAXIMUM_CLUSTERS_PER_STRUCTURE];
short cluster_count;
short object_count_in_clusters;
short object_index;
short result = 0;
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1779, location);
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1780, center);
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1781, object_indices);
if (!type_flags)
{
type_flags = NONE;
}
cluster_count = structure_clusters_in_sphere(
location->cluster_index,
center,
radius,
NUMBEROF(cluster_indices),
cluster_indices);
object_count_in_clusters = objects_in_clusters_by_indices(
class_flags,
cluster_count,
cluster_indices,
NUMBEROF(temporary_object_indices),
temporary_object_indices);
for (object_index = 0; object_index<object_count_in_clusters && result<maximum_count; ++object_index)
{
struct object_datum *object = object_get(temporary_object_indices[object_index]);
if (TEST_FLAG(type_flags, object->object.type) &&
point_in_sphere(center, &object->object.bounding_sphere_center, (object->object.bounding_sphere_radius)+radius))
{
object_indices[result++] = temporary_object_indices[object_index];
}
}
return result;
}
void objects_reconnect_to_structure_bsp(
void)
{
struct object_datum *object;
struct object_iterator iterator;
object_iterator_new(&iterator, _object_mask_all, 0);
while(object = (struct object_datum *)object_iterator_next(&iterator))
{
if (TEST_FLAG(object->object.flags, _object_connected_to_map_bit) &&
object->object.parent_object_index==NONE)
{
struct location scenario_location;
SET_FLAG(object->object.flags, _object_connected_to_map_bit, FALSE);
object->object.location.cluster_index = NONE;
object_header_get(iterator.index)->cluster_index = NONE;
scenario_location_from_point(&scenario_location, &object->object.bounding_sphere_center);
if (scenario_location.cluster_index==NONE)
{
struct collision_bsp_test_sphere_result result;
collision_bsp_test_sphere(
global_collision_bsp_get(),
0,
NULL,
&object->object.bounding_sphere_center,
object->object.bounding_sphere_radius,
&result);
if (result.leaf_count)
{
scenario_location.leaf_index = result.leaf_indices[0];
if (result.leaf_indices[0] == -1)
{
scenario_location.cluster_index = -1;
}
else
{
scenario_location.cluster_index = TAG_BLOCK_GET_ELEMENT(
&global_structure_bsp_get()->leaves,
result.leaf_indices[0] & LONG_MAX,
struct structure_leaf)->cluster_index;
}
}
else
{
scenario_location_from_point(&scenario_location, &object->object.position);
}
}
object_reconnect_to_map(iterator.index, &scenario_location);
}
}
return;
}
void objects_paparazzi(
void)
{
struct object_datum *object;
struct object_iterator iterator;
object_iterator_new(&iterator, _object_mask_all, 0);
while (object = (struct object_datum *)object_iterator_next(&iterator))
{
if (TEST_FLAG(object->object.flags, _object_movie_star_bit))
{
object_delete_initial_recursive(iterator.index, 0);
}
}
return;
}
void object_export_function_values(
long object_index)
{
short i;
struct object_datum *object = object_get(object_index);
struct object_definition *object_definition = object_definition_get(object->definition_index);
for (i = 0; i<NUMBEROF(object->object.incoming_function_values); ++i)
{
if (object_definition->object.function_modes[i])
{
short region_index;
real_matrix4x3 *node_matrix;
real value = 0.f;
switch (object_definition->object.function_modes[i])
{
case _object_function_recent_body_damage:
value = object->object.current_body_damage;
break;
case _object_function_recent_shield_damage:
value = object->object.current_shield_damage;
break;
case _object_function_body_vitality:
value = object->object.body_vitality;
break;
case _object_function_shield_vitality:
value = object->object.shield_vitality;
value = MIN(value, 1.f);
break;
case _object_function_random_constant:
if (object->object.incoming_function_values[i]==1.f)
{
value = real_random();
}
break;
case _object_function_alive:
value = TEST_FLAG(object->object.damage_flags, _object_dead_bit) ? 0.f : 1.f;
break;
case _object_function_compass:
node_matrix = object_get_node_matrix(object_index, 0);
if (fabs(node_matrix->forward.k) < 0.995f)
{
value = signed_angular_difference(global_scenario_get()->local_north, arctangent(node_matrix->forward.i, node_matrix->forward.j));
value = (0.15915494f * value) + 0.5f;
value = PIN(value, 0.f, 1.f);
}
else
{
value = object->object.incoming_function_values[i];
}
break;
default:
region_index = object_definition->object.function_modes[i]-_object_function_first_region_damage;
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 2630, region_index>=0 && region_index<MAXIMUM_REGIONS_PER_OBJECT);
value = object->object.region_damage[region_index] / 255.f;
break;
}
object->object.incoming_function_values[i] = value;
}
}
return;
}
// These local variants preserve the original XDK 3911 operand provenance for
// this large function. They are semantically identical to the shared matrix
// validation macros, but the typed aliases reproduce the January codegen.
#define ocnm_root_matrix_internal(file, line, matrix, string) \
match_vassert(file, line, valid_real((*matrix).scale), csprintf(temporary, "%s had a bad scale %f", string, (*matrix).scale)); \
match_vassert(file, line, valid_real_normal3d(&(*matrix).forward), csprintf(temporary, "%s had a bad forward (%f,%f,%f)", string, (*matrix).forward.i, (*matrix).forward.j, (*matrix).forward.k)); \
match_vassert(file, line, valid_real_normal3d(&(*matrix).left), csprintf(temporary, "%s had a bad left (%f,%f,%f)", string, (*matrix).left.i, (*matrix).left.j, (*matrix).left.k)); \
match_vassert(file, line, valid_real_normal3d(&(*matrix).up), csprintf(temporary, "%s had a bad up (%f,%f,%f)", string, (*matrix).up.i, (*matrix).up.j, (*matrix).up.k)); \
match_vassert(file, line, valid_real_point3d(&(*matrix).position), csprintf(temporary, "%s had a bad position (%f,%f,%f)", string, (*matrix).position.x, (*matrix).position.y, (*matrix).position.z)); \
match_vassert(file, line, \
valid_realcmp(dot_product3d(&(*matrix).forward, &(*matrix).left), 0.f), \
csprintf(temporary, "%s had a forward (%f,%f,%f) not perpendicular to left (%f,%f,%f)", string, (*matrix).forward.i, (*matrix).forward.j, (*matrix).forward.k, (*matrix).left.i, (*matrix).left.j, (*matrix).left.k)); \
match_vassert(file, line, \
valid_realcmp(dot_product3d(ocnm_left, &(*matrix).up), 0.f), \
csprintf(temporary, "%s had a up (%f,%f,%f) not perpendicular to left (%f,%f,%f)", string, (*matrix).up.i, (*matrix).up.j, (*matrix).up.k, (*matrix).left.i, (*matrix).left.j, (*matrix).left.k)); \
match_vassert(file, line, \
valid_realcmp(dot_product3d(ocnm_forward, ocnm_up), 0.f), \
csprintf(temporary, "%s had a forward (%f,%f,%f) not perpendicular to up (%f,%f,%f)", string, (*matrix).forward.i, (*matrix).forward.j, (*matrix).forward.k, (*matrix).up.i, (*matrix).up.j, (*matrix).up.k)); \
match_vassert(file, line, valid_real_matrix4x3(matrix), csprintf(temporary, "%s: assert_valid_real_matrix4x3", string));
#define ocnm_root_matrix_custom(file, line, matrix, custom_string) \
if (!valid_real_matrix4x3(matrix)) \
{ \
char const *string = (custom_string); \
real_vector3d const *ocnm_forward = &(*matrix).forward; \
real_vector3d const *ocnm_left = &(*matrix).left; \
real_vector3d const *ocnm_up = &(*matrix).up; \
ocnm_root_matrix_internal(file, line, matrix, string); \
}
#define ocnm_parent_matrix_internal(file, line, matrix, string) \
match_vassert(file, line, valid_real((*matrix).scale), csprintf(temporary, "%s had a bad scale %f", string, (*matrix).scale)); \
match_vassert(file, line, valid_real_normal3d(&(*matrix).forward), csprintf(temporary, "%s had a bad forward (%f,%f,%f)", string, (*matrix).forward.i, (*matrix).forward.j, (*matrix).forward.k)); \
match_vassert(file, line, valid_real_normal3d(&(*matrix).left), csprintf(temporary, "%s had a bad left (%f,%f,%f)", string, (*matrix).left.i, (*matrix).left.j, (*matrix).left.k)); \
match_vassert(file, line, valid_real_normal3d(&(*matrix).up), csprintf(temporary, "%s had a bad up (%f,%f,%f)", string, (*matrix).up.i, (*matrix).up.j, (*matrix).up.k)); \
match_vassert(file, line, valid_real_point3d(&(*matrix).position), csprintf(temporary, "%s had a bad position (%f,%f,%f)", string, (*matrix).position.x, (*matrix).position.y, (*matrix).position.z)); \
match_vassert(file, line, \
valid_realcmp(dot_product3d(&(*matrix).forward, &(*matrix).left), 0.f), \
csprintf(temporary, "%s had a forward (%f,%f,%f) not perpendicular to left (%f,%f,%f)", string, (*matrix).forward.i, (*matrix).forward.j, (*matrix).forward.k, (*matrix).left.i, (*matrix).left.j, (*matrix).left.k)); \
match_vassert(file, line, \
valid_realcmp(dot_product3d(ocnm_up, ocnm_left), 0.f), \
csprintf(temporary, "%s had a up (%f,%f,%f) not perpendicular to left (%f,%f,%f)", string, (*matrix).up.i, (*matrix).up.j, (*matrix).up.k, (*matrix).left.i, (*matrix).left.j, (*matrix).left.k)); \
match_vassert(file, line, \
valid_realcmp(dot_product3d(&(*matrix).forward, &(*matrix).up), 0.f), \
csprintf(temporary, "%s had a forward (%f,%f,%f) not perpendicular to up (%f,%f,%f)", string, (*matrix).forward.i, (*matrix).forward.j, (*matrix).forward.k, (*matrix).up.i, (*matrix).up.j, (*matrix).up.k)); \
match_vassert(file, line, valid_real_matrix4x3(matrix), csprintf(temporary, "%s: assert_valid_real_matrix4x3", string));
#define ocnm_parent_matrix_custom(file, line, matrix, custom_string) \
if (!valid_real_matrix4x3(matrix)) \
{ \
char const *string = (custom_string); \
real_vector3d const *ocnm_left = &(*matrix).left; \
real_vector3d const *ocnm_up = &(*matrix).up; \
ocnm_parent_matrix_internal(file, line, matrix, string); \
}
void object_compute_node_matrices(
long object_index)
{
real_orientation cannot_interpolate_node_orientations_storage[MAXIMUM_NODES_PER_MODEL];
short node_stack[MAXIMUM_NODES_PER_MODEL];
real_matrix4x3 center_matrix;
real_matrix4x3 offset_matrix;
real_matrix4x3 node_matrix;
real_matrix4x3 object_translation_matrix;
real_matrix4x3 object_rotation_matrix;
real_point3d center;
real_matrix4x3 parent_node_matrix_no_scale_no_mirror;
struct object_datum *object = object_get(object_index);
struct object_definition *object_definition = object_definition_get(object->definition_index);
real_matrix4x3 *object_nodes = (real_matrix4x3 *)object_header_block_get(object_index, &object->object.node_matrices);
real_orientation *node_orientations =
TEST_FLAG(_object_mask_cannot_interpolate, object->object.type) ?
cannot_interpolate_node_orientations_storage :
(real_orientation *)object_header_block_get(object_index, &object->object.node_orientations);
if (object_definition->object.model.index!=NONE)
{
real_matrix4x3 *object_node_matrix;
boolean world_relative;
short node_count;
short node_index;
long unknown_var;
struct object_type_definition *object_type_definition = object_type_definition_get(object->object.type);
struct model *model = model_definition_get(object_definition->object.model.index);
if (object->object.parent_object_index==NONE)
{
object_node_matrix = NULL;
}
else
{
object_node_matrix = object_get_node_matrix(object->object.parent_object_index, object->object.parent_node_index);
}
world_relative = FALSE;
if (object->object.animation.animation_graph_index!=NONE)
{
if (object->object.animation.state.index!=NONE)
{
short frame_index;
struct animation_graph *animation_graph = animation_graph_definition_get(
object->object.animation.animation_graph_index);
struct animation *animation = TAG_BLOCK_GET_ELEMENT(
&animation_graph->animations,
object->object.animation.state.index,
struct animation);
if (TEST_FLAG(object->object.flags, _object_animates_automatically_bit) && animation->frame_count>0)
{
frame_index = OBJECT_FRAME_INDEX_GET(object_index) % (unsigned long)animation->frame_count;
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 2704, frame_index>=0)
}
else
{
frame_index = object->object.animation.state.frame_index;
}
animation_get_node_orientations(model, animation, frame_index, node_orientations);
world_relative = TEST_FLAG(animation->flags, _animation_world_relative_bit);
}
else
{
model_get_node_orientations(model, node_orientations);
}
}
else
{
model_get_node_orientations(model, node_orientations);
}
if (object_definition->object.animation_graph.index!=NONE)
{
short overlay_index;
struct animation_graph *animation_graph= animation_graph_definition_get(object_definition->object.animation_graph.index);
for (overlay_index = 0; overlay_index<animation_graph->object_overlays.count; ++overlay_index)
{
struct animation_graph_object_overlay* overlay = TAG_BLOCK_GET_ELEMENT(
&animation_graph->object_overlays,
overlay_index,
struct animation_graph_object_overlay);
if (overlay->animation_index!=NONE &&
overlay->function_index<object_definition->object.functions.count)
{
struct object_function_definition* function = TAG_BLOCK_GET_ELEMENT(
&object_definition->object.functions,
overlay->function_index,
struct object_function_definition);
struct animation *animation = TAG_BLOCK_GET_ELEMENT(
&animation_graph->animations,
overlay->animation_index,
struct animation);
real value= object->object.outgoing_function_values[overlay->function_index];
if (overlay->mode==_object_overlay_mode_frame)
{
real frame_index;
if (TEST_FLAG(function->flags, _object_function_additive_bit))
{
frame_index = animation->frame_count*value;
}
else
{
frame_index = (animation->frame_count-1) * value;
}
overlay_animation_apply_continuous(animation, frame_index, node_orientations);
}
else if (overlay->mode==_object_overlay_mode_scale)
{
short frame_index = OBJECT_FRAME_INDEX_GET(object_index) % (unsigned long)animation->frame_count;
overlay_animation_apply_scaled(
animation,
frame_index,
value,
node_orientations);
}
}
}
}
// Scale the object orientations
if (object->object.scale>0.f)
{
node_orientations[0].scale*= object->object.scale;
node_orientations[0].translation.x*= object->object.scale;
node_orientations[0].translation.y*= object->object.scale;
node_orientations[0].translation.z*= object->object.scale;
}
if (object_definition->object.animation_graph.index!=NONE)
{
object_type_preprocess_node_orientations(object_index, node_orientations);
}
if (object->object.animation.interpolation_frame_count>0)
{
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 2777, !TEST_FLAG(_object_mask_cannot_interpolate, object->object.type));
interpolate_node_orientations(
model->nodes.count,
(real_orientation *)object_header_block_get(
object_index,
&object->object.original_node_orientations
),
node_orientations,
object->object.animation.interpolation_frame_index,
object->object.animation.interpolation_frame_count);
}
if (!world_relative)
{
match_vassert(
"c:\\halo\\SOURCE\\objects\\objects.c",
2786,
valid_real_point3d(&object->object.position),
csprintf(
temporary,
"%s had a bad position before compute_node_matrices (%f,%f,%f)",
tag_get_name(object->definition_index),
object->object.position.x,
object->object.position.y,
object->object.position.z));
{
real_vector3d const *validated_forward = &object->object.forward;
match_vassert(
"c:\\halo\\SOURCE\\objects\\objects.c",
2787,
valid_real_vector3d_axes2(validated_forward, &object->object.up),
csprintf(
temporary,
"%s had a bad forward and up before compute_node_matrices (%f,%f,%f)x(%f,%f,%f)",
tag_get_name(object->definition_index),
object->object.forward.i,
object->object.forward.j,
object->object.forward.k,
object->object.up.i,
object->object.up.j,
object->object.up.k));
}
}
node_index = 0;
node_count = 1;
unknown_var = 0;
node_stack[0] = 0;
while (TRUE)
{
short node_stack_index = node_stack[node_index++];
short next_sibling_node_index;
struct model_node *node = TAG_BLOCK_GET_ELEMENT(&model->nodes, node_stack_index, struct model_node);
if (node_stack_index==0)
{
matrix4x3_from_orientation(&node_matrix, node_orientations);
if (!world_relative)
{
matrix4x3_translation(&object_translation_matrix, &object->object.position);
matrix4x3_rotation_from_vectors(&object_rotation_matrix, &object->object.forward, &object->object.up);
if (TEST_FLAG(object->object.flags, _object_mirrored_bit))
{
object_rotation_matrix.left.i = -object_rotation_matrix.left.i;
object_rotation_matrix.left.j = -object_rotation_matrix.left.j;
object_rotation_matrix.left.k = -object_rotation_matrix.left.k;
}
if (object_definition->object.physics.index!=NONE)
{
struct physics_definition *physics_definition= physics_definition_get(object_definition->object.physics.index);
set_real_point3d(&center, -physics_definition->center_of_mass.x, -physics_definition->center_of_mass.y, -physics_definition->center_of_mass.z);
matrix4x3_translation(&center_matrix, &center);
matrix4x3_multiply(&object_rotation_matrix, &center_matrix, &object_rotation_matrix);
}
matrix4x3_translation(&offset_matrix, &object_definition->object.origin_offset);
matrix4x3_multiply(&object_rotation_matrix, &offset_matrix, &object_rotation_matrix);
if (object_node_matrix)
{
real_matrix4x3 const *validated_parent_node_matrix;
if (object_node_matrix->scale!=1.f)
{
real scaled_z;
object_translation_matrix.position.x *= object_node_matrix->scale;
object_translation_matrix.position.y *= object_node_matrix->scale;
scaled_z = object_translation_matrix.position.z * object_node_matrix->scale;
parent_node_matrix_no_scale_no_mirror= *object_node_matrix;
object_translation_matrix.position.z = scaled_z;
object_node_matrix= &parent_node_matrix_no_scale_no_mirror;
parent_node_matrix_no_scale_no_mirror.scale= 1.f;
}
if (TEST_FLAG(object_get(object->object.parent_object_index)->object.flags, _object_mirrored_bit))
{
if (object_node_matrix!=&parent_node_matrix_no_scale_no_mirror)
{
parent_node_matrix_no_scale_no_mirror = *object_node_matrix;
object_node_matrix = &parent_node_matrix_no_scale_no_mirror;
}
negate_vector3d(&object_node_matrix->left, &object_node_matrix->left);
}
validated_parent_node_matrix = object_node_matrix;
ocnm_parent_matrix_custom(
"c:\\halo\\SOURCE\\objects\\objects.c",
2871,
validated_parent_node_matrix,
csprintf(
temporary,
"%s as parent node of %s",
tag_get_name(object_get(object->object.parent_object_index)->definition_index),
tag_get_name(object->definition_index)));
matrix4x3_multiply(object_node_matrix, &object_translation_matrix, &object_nodes[node_stack_index]);
matrix4x3_multiply(&object_nodes[node_stack_index], &object_rotation_matrix, &object_nodes[node_stack_index]);
matrix4x3_multiply(&object_nodes[node_stack_index], &node_matrix, &object_nodes[node_stack_index]);
}
else
{
matrix4x3_multiply(&object_translation_matrix, &object_rotation_matrix, &object_nodes[node_stack_index]);
matrix4x3_multiply(&object_nodes[node_stack_index], &node_matrix, &object_nodes[node_stack_index]);
}
}
else
{
object_nodes[node_stack_index] = node_matrix;
}
if (!valid_real_matrix4x3(&object_nodes[node_stack_index]))
{
real_matrix4x3 const *validated_root_node_matrix;
error(_error_silent, "object_compute_node_matrices FAILURE on root node of %s", tag_get_name(object->definition_index));
error(
_error_silent,
" object: pos %f %f %f, fwd %f %f %f, up %f %f %f",
object->object.position.x,
object->object.position.y,
object->object.position.z,
object->object.forward.i,
object->object.forward.j,
object->object.forward.k,
object->object.up.i,
object->object.up.j,
object->object.up.k);
if (object_definition->object.physics.index!=NONE)
{
struct physics_definition *physics_definition= physics_definition_get(object_definition->object.physics.index);
error(
_error_silent,
" center-of-mass translation %f %f %f",
-physics_definition->center_of_mass.x,
-physics_definition->center_of_mass.y,
-physics_definition->center_of_mass.z);
}
error(
_error_silent,
" origin-offset %f %f %f",
object_definition->object.origin_offset.x,
object_definition->object.origin_offset.y,
object_definition->object.origin_offset.z);
if (object_node_matrix)
{
error(
_error_silent,
" parent-node matrix fwd %f %f %f",
object_node_matrix->forward.i,
object_node_matrix->forward.j,
object_node_matrix->forward.k);
error(
_error_silent,
" left %f %f %f",
object_node_matrix->left.i,
object_node_matrix->left.j,
object_node_matrix->left.k);
error(
_error_silent,
" up %f %f %f",
object_node_matrix->up.i,
object_node_matrix->up.j,
object_node_matrix->up.k);
error(
_error_silent,
" posn %f %f %f",
object_node_matrix->position.x,
object_node_matrix->position.y,
object_node_matrix->position.z);
error(_error_silent, " scale (jason's ugly secret) %f", object_node_matrix->scale);
}
else
{
error(_error_silent, " no parent node");
}
error(_error_silent, "");
error(
_error_silent,
"computed matrix fwd %f %f %f",
object_nodes[node_stack_index].forward.i,
object_nodes[node_stack_index].forward.j,
object_nodes[node_stack_index].forward.k);
error(
_error_silent,
" left %f %f %f",
object_nodes[node_stack_index].left.i,
object_nodes[node_stack_index].left.j,
object_nodes[node_stack_index].left.k);
error(
_error_silent,
" up %f %f %f",
object_nodes[node_stack_index].up.i,
object_nodes[node_stack_index].up.j,
object_nodes[node_stack_index].up.k);
error(_error_silent, " posn %f %f %f",
object_nodes[node_stack_index].position.x,
object_nodes[node_stack_index].position.y,
object_nodes[node_stack_index].position.z);
error(_error_silent, " scale %f", object_nodes[node_stack_index].scale);
validated_root_node_matrix = &object_nodes[node_stack_index];
ocnm_root_matrix_custom(
"c:\\halo\\SOURCE\\objects\\objects.c",
2921,
validated_root_node_matrix,
"object_compute_node_matrices root node matrix");
}
}
else
{
real_orientation *orientation = &node_orientations[node_stack_index];
matrix4x3_from_orientation(&object_nodes[node_stack_index], orientation);
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 2929, node->parent_node_index!=NONE);
matrix4x3_multiply(
&object_nodes[node->parent_node_index],
&object_nodes[node_stack_index],
&object_nodes[node_stack_index]);
}
if (node_stack_index==0)
{
real_matrix4x3 *final_matrix = &object_nodes[node_stack_index];
if (!valid_real_matrix4x3(final_matrix))
{
char *name2 = tag_get_name(object->definition_index);
char *string = name2;
match_assert_valid_real_matrix4x3_internal("c:\\halo\\SOURCE\\objects\\objects.c", 2935, final_matrix, string);
}
}
next_sibling_node_index = node->next_sibling_node_index;
if (next_sibling_node_index!=NONE)
{
node_stack[node_count++] = next_sibling_node_index;
}
if (node->first_child_node_index!=NONE)
{
node_stack[node_count++] = node->first_child_node_index;
}
if (node_index==node_count)
{
break;
}
}
}
else
{
object_nodes[0].scale = 1.f;
object_nodes[0].forward = object->object.forward;
object_nodes[0].up = object->object.up;
cross_product3d(&object_nodes[0].up, &object_nodes[0].forward, &object_nodes[0].left);
object_nodes[0].position = object->object.position;
}
matrix4x3_transform_point(object_nodes, &object_definition->object.bounding_offset, &object->object.bounding_sphere_center);
object->object.bounding_sphere_radius = object_definition->object.bounding_radius;
if (object->object.scale > 0.f)
{
object->object.bounding_sphere_radius *= object->object.scale;
}
return;
}
#undef ocnm_root_matrix_custom
#undef ocnm_root_matrix_internal
#undef ocnm_parent_matrix_custom
#undef ocnm_parent_matrix_internal
static void object_postprocess_node_matrices(
long object_index)
{
struct object_datum *object = object_get(object_index);
struct object_definition *object_definition = object_definition_get(object->definition_index);
if (object_definition->object.model.index!=NONE &&
object_definition->object.animation_graph.index != NONE)
{
real_matrix4x3 *matrices = (real_matrix4x3 *)object_header_block_get(
object_index,
&object->object.node_matrices);
object_type_postprocess_node_matrices(object_index, matrices);
}
return;
}
static void object_choose_random_change_colors(
long object_index,
real_rgb_color *placement_change_colors)
{
short cc_index;
struct object_datum *object = object_get(object_index);
struct object_definition *object_definition = object_definition_get(object->definition_index);
for (cc_index =0; cc_index<NUMBEROF(object->object.base_change_colors); cc_index++)
{
object->object.base_change_colors[cc_index] = placement_change_colors[cc_index];
if (cc_index<object_definition->object.change_colors.count)
{
short permutation_index;
struct object_change_color_definition *cc = TAG_BLOCK_GET_ELEMENT(
&object_definition->object.change_colors,
cc_index,
struct object_change_color_definition);
real weight = fmod(
fabs(
object->object.position.x*315.89313f +
object->object.position.y*587.12946f +
object->object.position.z*744.12415f +
(real)cc_index*431.12894f
),
1.f);
for (permutation_index =0; permutation_index<cc->permutations.count; permutation_index++)
{
struct object_change_color_permutation *permutation = TAG_BLOCK_GET_ELEMENT(
&cc->permutations,
permutation_index,
struct object_change_color_permutation);
if (weight<=permutation->weight)
{
rgb_colors_interpolate(
&object->object.base_change_colors[cc_index],
1,
&permutation->color_lower_bound,
&permutation->color_upper_bound,
fmod(fabs(object->object.position.y) + cc_index*0.71211f, 1.f));
break;
}
}
}
object->object.outgoing_change_colors[cc_index].red = PIN(object->object.base_change_colors[cc_index].red, 0.f, 1.f);
object->object.outgoing_change_colors[cc_index].green = PIN(object->object.base_change_colors[cc_index].green, 0.f, 1.f);
object->object.outgoing_change_colors[cc_index].blue = PIN(object->object.base_change_colors[cc_index].blue, 0.f, 1.f);
}
return;
}
void objects_scripting_detach(
long parent_object_index,
long child_object_index)
{
if (parent_object_index!=NONE &&
child_object_index!=NONE &&
object_get(child_object_index)->object.parent_object_index == parent_object_index)
{
network_coop_note_detach(parent_object_index, child_object_index);
object_detach(child_object_index);
}
return;
}
void object_render_debug(
long object_index)
{
struct object_datum *object = object_get(object_index);
struct object_definition *object_definition = object_definition_get(object->definition_index);
if (debug_objects_root_node)
{
render_debug_matrix(TRUE, object_get_node_matrix(object_index, 0), 0.3f);
}
if (debug_objects_names)
{
if (object->object.name_index!=NONE)
{
real_point3d origin;
struct scenario_object_name* object_name = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->object_names,
object->object.name_index,
struct scenario_object_name);
object_get_origin(object_index, &origin);
render_debug_string_at_point(FALSE, &origin, object_name->name, global_real_argb_purple);
}
}
if (debug_objects_position_velocity)
{
real_vector3d velocity;
real_matrix4x3 world_matrix;
char* model_name = strrchr(object_definition->object.model.name, '\\');
object_get_world_matrix(object_index, &world_matrix);
object_get_velocities(object_index, &velocity, NULL);
model_name = model_name ? &model_name[1] : object_definition->object.model.name;
render_debug_string_at_point(0, &world_matrix.position, model_name, global_real_argb_orange);
render_debug_matrix(TRUE, &world_matrix, object->object.bounding_sphere_radius);
render_debug_vector(TRUE, &world_matrix.position, &velocity, 1.f, global_real_argb_yellow);
}
if (debug_objects_bounding_spheres &&
(object_definition->object.model.index!=NONE ||object_definition->object.collision_model.index!=NONE))
{
const real_argb_color *draw_color = global_real_argb_black;
// Bounding sphere is colored differently based on a couple factors:
// Red: Invalid bounding sphere
// Yellow: Active object
// Black: Inactive object
if (object->object.bounding_sphere_radius<=0.f)
{
draw_color = global_real_argb_red;
}
else
{
if (TEST_FLAG(object_header_get(object_index)->flags, _object_header_active_bit))
{
draw_color = global_real_argb_yellow;
}
}
render_debug_sphere(
TRUE,
&object->object.bounding_sphere_center,
object->object.bounding_sphere_radius>0.f ? object->object.bounding_sphere_radius : 1.f,
draw_color);
}
if (debug_objects_collision_models)
{
struct collision_model_instance collision_model_instance;
if (collision_model_instance_new(&collision_model_instance, object_index))
{
render_debug_collision_model(&collision_model_instance);
}
}
if (debug_objects_physics)
{
struct physics_instance physics_instance;
if (physics_instance_new(&physics_instance, object_index))
{
render_debug_physics(&physics_instance);
}
}
if (debug_objects_pathfinding_spheres)
{
if (!TEST_FLAG(object->object.flags, _object_invisible_bit) &&
!TEST_FLAG(object->object.damage_flags, _object_dead_bit))
{
if (
object->object.type != _object_type_machine ||
TEST_FLAG(((struct machine_definition *)object_definition)->machine.flags, _machine_is_pathfinding_obstacle_bit) &&
(
!TEST_FLAG(
((struct machine_definition *)object_definition)->machine.flags,
_machine_is_not_pathfinding_obstacle_when_open_bit
) ||
((struct machine_datum *)object)->device.position!=1.f)
)
{
if (object_definition->object.collision_model.index!=NONE)
{
real_matrix4x3 world_matrix;
short sphere_index;
struct collision_model* collision_model = collision_model_definition_get(
object_definition->object.collision_model.index);
object_get_world_matrix(object_index, &world_matrix);
// Render all pathfinding spheres
for (sphere_index = 0; sphere_index<collision_model->pathfinding_spheres.count; ++sphere_index)
{
real_point3d origin;
struct pathfinding_sphere *pathfinding_sphere = TAG_BLOCK_GET_ELEMENT(
&collision_model->pathfinding_spheres,
sphere_index,
struct pathfinding_sphere);
if (pathfinding_sphere->node_index!=NONE)
{
real_matrix4x3 *node_matrix = object_get_node_matrix(
object_index,
pathfinding_sphere->node_index);
matrix4x3_transform_point(
node_matrix,
&pathfinding_sphere->center,
&origin);
}
else
{
matrix4x3_transform_point(
&world_matrix,
&pathfinding_sphere->center,
&origin);
}
render_debug_sphere(
TRUE,
&origin,
pathfinding_sphere->radius,
global_real_argb_blue);
}
if (collision_model->pathfinding_box.x0<collision_model->pathfinding_box.x1 &&
collision_model->pathfinding_box.y0<collision_model->pathfinding_box.y1 &&
collision_model->pathfinding_box.z0<collision_model->pathfinding_box.z1)
{
real_point3d line_points[NUMBER_OF_VERTICES_PER_LINE][NUMBER_OF_VERTICES_PER_LINE][NUMBER_OF_VERTICES_PER_LINE];
short i;
short j;
short k;
for (i = 0; i<NUMBER_OF_VERTICES_PER_LINE; ++i)
{
for (j = 0; j<NUMBER_OF_VERTICES_PER_LINE; ++j)
{
for (k = 0; k<NUMBER_OF_VERTICES_PER_LINE; ++k)
{
line_points[i][j][k].x = collision_model->pathfinding_box.n[i + 0]; // x0 and x1
line_points[i][j][k].y = collision_model->pathfinding_box.n[j + 2]; // y0 and y1
line_points[i][j][k].z = collision_model->pathfinding_box.n[k + 4]; // z0 and z1
matrix4x3_transform_point(
&world_matrix,
&line_points[i][j][k],
&line_points[i][j][k]);
}
}
}
for (i = 0; i < NUMBER_OF_VERTICES_PER_LINE; i++)
{
for (j = 0; j < NUMBER_OF_VERTICES_PER_LINE; j++)
{
for (k = 0; k < NUMBER_OF_VERTICES_PER_LINE; k++)
{
if (i==0)
{
render_debug_line(
TRUE,
&line_points[0][j][k],
&line_points[1][j][k],
global_real_argb_blue);
}
if (j==0)
{
render_debug_line(
TRUE,
&line_points[i][0][k],
&line_points[i][1][k],
global_real_argb_blue);
}
if (k==0)
{
render_debug_line(
TRUE,
&line_points[i][j][0],
&line_points[i][j][1],
global_real_argb_blue);
}
}
}
}
}
}
}
}
}
return;
}
static void attachments_new(
long object_index)
{
short attachment_num;
struct object_datum *object = object_get(object_index);
struct object_definition *object_definition = object_definition_get(object->definition_index);
for (attachment_num = 0; attachment_num<object_definition->object.attachments.count; attachment_num++)
{
struct object_attachment_definition* attachment = TAG_BLOCK_GET_ELEMENT(
&object_definition->object.attachments,
attachment_num,
struct object_attachment_definition);
short attachment_type = NONE;
long attachment_index = NONE;
if (attachment->type.index!=NONE)
{
switch (attachment->type.group_tag)
{
case LIGHT_DEFINITION_TAG:
attachment_type = _object_attachment_type_light;
break;
case LOOPING_SOUND_DEFINITION_TAG:
attachment_type = _object_attachment_type_looping_sound;
break;
case EFFECT_DEFINITION_TAG:
attachment_type = _object_attachment_type_effect;
break;
case CONTRAIL_DEFINITION_TAG:
attachment_type = _object_attachment_type_contrail;
break;
case PARTICLE_SYSTEM_DEFINITION_TAG:
attachment_type = _object_attachment_type_particle_system;
break;
}
}
switch (attachment_type)
{
case _object_attachment_type_light:
attachment_index = light_new(
attachment->type.index,
object_index,
attachment_num,
attachment->primary_scale_function_reference-1,
attachment->change_color_reference-1);
if (attachment_index!=NONE)
{
SET_FLAG(object->object.flags, _object_has_attached_lights_bit, TRUE);
}
break;
case _object_attachment_type_looping_sound:
attachment_index = game_looping_sound_new(
object_index,
attachment->type.index,
attachment->marker_name,
attachment->primary_scale_function_reference-1);
if (attachment_index!=NONE)
{
SET_FLAG(object->object.flags, _object_has_attached_looping_sounds_bit, TRUE);
}
break;
case _object_attachment_type_effect:
attachment_index = effect_new_looping(
attachment->type.index,
object_index,
attachment->primary_scale_function_reference-1,
attachment->secondary_scale_function_reference-1,
attachment->change_color_reference-1);
break;
case _object_attachment_type_contrail:
attachment_index = contrail_new(attachment->type.index, object_index, attachment_num);
break;
case _object_attachment_type_particle_system:
attachment_index = particle_system_new_attached(attachment->type.index, object_index, attachment_num);
break;
}
object->object.attachment_types[attachment_num] = attachment_type;
object->object.attachment_indices[attachment_num] = attachment_index;
}
return;
}
void object_set_position(
long object_index,
real_point3d const *position,
real_vector3d const *forward,
real_vector3d const *up)
{
struct object_datum *object = object_get(object_index);
object_disconnect_from_map(object_index);
if (position)
{
object->object.position = *position;
}
if (forward)
{
object->object.forward = *forward;
if (up)
{
object->object.up = *up;
}
else
{
real_vector3d right = {forward->j, -forward->i, 0.f};
if (normalize3d(&right)==0.f)
{
right.i = 1.f;
right.k = 0.f;
right.j = 0.f;
}
cross_product3d(&right, forward, &object->object.up);
}
}
object_compute_node_matrices(object_index);
object_reconnect_to_map(object_index, NULL);
return;
}
void object_translate(
long object_index,
real_point3d const *new_position,
struct location const *new_location)
{
struct object_datum *object = object_get(object_index);
match_assert_valid_real_point3d(
"c:\\halo\\SOURCE\\objects\\objects.c",
562,
new_position);
object_disconnect_from_map(object_index);
object->object.position = *new_position;
object_reconnect_to_map(object_index, new_location);
return;
}
long object_new(
struct object_placement_data *data)
{
long object_index = NONE;
long definition_index = data->definition_index;
match_assert_valid_real_point3d("c:\\halo\\SOURCE\\objects\\objects.c", 618, &data->position)
match_assert_valid_real_vector3d_axes2("c:\\halo\\SOURCE\\objects\\objects.c", 619, &data->forward, &data->up);
match_assert_valid_real_vector3d("c:\\halo\\SOURCE\\objects\\objects.c", 620, &data->angular_velocity);
match_assert_valid_real_vector3d("c:\\halo\\SOURCE\\objects\\objects.c", 621, &data->translational_velocity);
if (game_engine_running() && definition_index!=NONE
/* (the host's object, which the host made as the game type has it) */
&& !network_objects_creating_host_object()
)
{
definition_index = game_engine_remap_object_definition(definition_index);
}
if (definition_index!=NONE)
{
struct object_definition *object_definition = object_definition_get(definition_index);
struct object_type_definition *type_definition = object_type_definition_get(object_definition->object.type);
object_index = object_header_new(object_header_data, network_objects_new_object_index(),
type_definition->game_datum_size);
if (object_index!=NONE)
{
short node_count;
struct object_header_datum *object_header = object_header_get(object_index);
struct object_datum *object = object_get(object_index);
boolean success = TRUE;
SET_FLAG(object_header->flags, _object_header_being_created_bit, TRUE);
SET_FLAG(object_header->flags, _object_header_automatically_deactivate_bit, TRUE);
object_header->type = object_definition->object.type;
object->definition_index = definition_index;
object->object.type = object_definition->object.type;
object_type_adjust_placement(object_index, data);
object->object.position = data->position;
object->object.forward = data->forward;
object->object.up = data->up;
object->object.translational_velocity = data->translational_velocity;
object->object.angular_velocity = data->angular_velocity;
// Preserve the January inline schedule without emitting a point_from_line3d COMDAT.
{
real_point3d const *p = &object->object.position;
real_vector3d const *v = &object->object.up;
real t = data->height;
real_point3d *result = &object->object.position;
real height = t;
result->x = (v->i*height) + p->x;
result->y = (v->j*height) + p->y;
result->z = (v->k*height) + p->z;
}
SET_FLAG(object->object.flags, _object_mirrored_bit, TEST_FLAG(data->flags, _new_object_mirrored_bit));
object->object.location.cluster_index = NONE;
object_header->cluster_index = NONE;
object->object.magic_number = global_object_marker-1;
object->object.umbrella_shield_object_index = NONE;
object->object.first_cluster_reference_index = NONE;
object->object.animation.state.index = NONE;
object->object.animation.animation_graph_index = object_definition->object.animation_graph.index;
object->object.cached_render_state_index = NONE;
object->object.parent_object_index = NONE;
object->object.next_object_index = NONE;
object->object.first_child_object_index = NONE;
object->object.name_index = NONE;
object->object.shield_damage_decay_timer = NONE;
object->object.body_damage_decay_timer = NONE;
if (TEST_FLAG(object_definition->object.flags, _object_does_not_cast_shadow_bit))
{
SET_FLAG(object->object.flags, _object_shadowless_bit, TRUE);
}
SET_FLAG(object->object.flags, _object_has_collision_model_bit, object_definition->object.collision_model.index!=NONE);
object_set_visibility(object_index, object_definition->object.model.index!=NONE);
object->object.owner_team_index = data->owner_team_index;
object->object.owner_player_index = data->owner_player_index;
object->object.owner_object_index = data->owner_object_index;
object->object.variant_number = data->variant_number;
object->object.forced_shader_permutation_index = object_definition->object.forced_shader_permutation_index;
if (object_definition->object.model.index==NONE)
{
node_count =1;
}
else
{
node_count = model_definition_get(object_definition->object.model.index)->nodes.count;
}
if (object_header_block_allocate(
object_index,
offsetof(struct object_datum, object.node_matrices),
sizeof(struct real_matrix4x3) * node_count))
{
if (!TEST_FLAG(_object_mask_cannot_interpolate, object_definition->object.type) &&
(
!object_header_block_allocate(
object_index,
offsetof(struct object_datum, object.node_orientations),
sizeof(struct real_orientation) * node_count
) ||
!object_header_block_allocate(
object_index,
offsetof(struct object_datum, object.original_node_orientations),
sizeof(struct real_orientation) * node_count
)
)
)
{
success = FALSE;
}
}
else
{
success = FALSE;
}
// Get the object, we do this so we can verify that we're able to retrieve it in the future
object = object_get(object_index);
if (success && object_type_new(object_index))
{
boolean original_deleted_when_deactivated = TEST_FLAG(object->object.flags, _object_deleted_when_deactivated_bit);
if (original_deleted_when_deactivated &&
TEST_FLAG(data->flags, _new_object_never_automatically_delete_bit))
{
SET_FLAG(object->object.flags, _object_deleted_when_deactivated_bit, FALSE);
}
object_choose_random_change_colors(object_index, data->change_colors);
object_choose_random_region_permutations(object_index);
object_initialize_vitality(object_index, NULL, NULL);
object_compute_node_matrices(object_index);
object_reconnect_to_map(object_index, NULL);
object_postprocess_node_matrices(object_index);
object_type_export_function_values(object_index);
object_compute_function_values(object_index);
object_compute_change_colors(object_index);
object_create_attachments(object_index);
// Restore value
SET_FLAG(object->object.flags, _object_deleted_when_deactivated_bit, original_deleted_when_deactivated);
if (!TEST_FLAG(object_header->flags, _object_header_active_bit) &&
TEST_FLAG(object->object.flags, _object_deleted_when_deactivated_bit))
{
if (TEST_FLAG(data->flags, _new_object_never_automatically_delete_bit))
{
if (object->object.location.cluster_index!=NONE)
{
object_delete(object_index);
}
}
else
{
object_delete(object_index);
}
}
}
else
{
success = FALSE;
}
if (!success)
{
object_type_delete(object_index);
object_header_delete(object_header_data, object_index);
object_index = NONE;
}
else
{
if (object_definition->object.creation_effect.index!=NONE)
{
effect_new_from_object(
object_definition->object.creation_effect.index,
object_index,
object_index,
NONE,
0.f,
0.f,
NULL,
NULL);
}
}
}
}
if (object_index==NONE && definition_index!=NONE)
{
char string[512];
sprintf(string, "OUT OF OBJECTS: cannot create %s", tag_name_strip_path(tag_get_name(definition_index)));
console_printf(FALSE, "%s", string);
error(_error_log, "%s", string);
}
return object_index;
}
void object_attach_to_node(
long parent_object_index,
long child_object_index,
short parent_node_index)
{
long object_index;
struct object_datum *object;
void *(*get_object)(
long,
unsigned long) = object_get_and_verify_type;
boolean (*has_node)(
long,
short) = object_has_node;
real_matrix4x3 *(*get_node_matrix)(
long,
short) = object_get_node_matrix;
boolean valid = TRUE;
for (
object_index = parent_object_index;
object_index!=NONE;
object_index = object->object.parent_object_index)
{
object = object_get(object_index);
if (object_index==child_object_index)
{
valid = FALSE;
break;
}
}
match_vassert(
"c:\\halo\\SOURCE\\objects\\objects.c",
1225,
valid,
"cannot attach an object to one of its children");
if (valid)
{
real_matrix4x3 inverse_node_matrix;
struct object_datum *child_object = (struct object_datum *)get_object(child_object_index, _object_mask_all);
struct object_datum *parent_object = (struct object_datum *)get_object(parent_object_index, _object_mask_all);
boolean connected_to_map = TEST_FLAG(child_object->object.flags, _object_connected_to_map_bit);
match_vassert(
"c:\\halo\\SOURCE\\objects\\objects.c",
1235,
has_node(parent_object_index, parent_node_index),
"object_has_node(parent_object_index, parent_node_index)");
if (connected_to_map)
{
object_disconnect_from_map(child_object_index);
}
matrix4x3_inverse(get_node_matrix(parent_object_index, parent_node_index), &inverse_node_matrix);
matrix4x3_transform_point(&inverse_node_matrix, &child_object->object.position, &child_object->object.position);
matrix4x3_transform_normal(&inverse_node_matrix, &child_object->object.forward, &child_object->object.forward);
matrix4x3_transform_normal(&inverse_node_matrix, &child_object->object.up, &child_object->object.up);
child_object->object.parent_object_index = parent_object_index;
child_object->object.parent_node_index = parent_node_index;
if (connected_to_map)
{
object_reconnect_to_map(child_object_index, NULL);
}
{
struct object_header_datum *header = object_header_get(child_object_index);
struct object_datum *deactivated_object = (struct object_datum *)get_object(child_object_index, _object_mask_all);
if (TEST_FLAG(header->flags, _object_header_active_bit))
{
SET_FLAG(header->flags, _object_header_active_bit, FALSE);
}
}
{
struct object_header_datum *header = object_header_get(child_object_index);
SET_FLAG(header->flags, _object_header_do_not_update_bit, TRUE);
}
object_compute_node_matrices(child_object_index);
}
return;
}
boolean object_force_inside_bsp(
long object_index,
real_point3d const *known_good_point)
{
struct collision_result collision;
struct object_datum *object = object_get(object_index);
boolean result = FALSE;
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 2365, global_current_collision_user_depth < MAXIMUM_COLLISION_USER_STACK_DEPTH);
global_current_collision_users[global_current_collision_user_depth++] = 19;
if (collision_test_line(_collision_test_for_projectiles_flags, known_good_point, &object->object.position, NONE, &collision) ||
object->object.location.cluster_index==NONE)
{
if (collision.location.cluster_index!=NONE)
{
object_translate(object_index, &collision.point, &collision.location);
object_compute_node_matrices(object_index);
result = TRUE;
}
}
else
{
result = TRUE;
}
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 2387, global_current_collision_user_depth > 1);
--global_current_collision_user_depth;
return result;
}
static boolean object_update(
long object_index)
{
struct object_header_datum *header = object_header_get(object_index);
struct object_datum *object = object_get(object_index);
struct object_definition *object_definition = object_definition_get(object->definition_index);
boolean result = TRUE;
if (!TEST_FLAG(header->flags, _object_header_do_not_update_bit))
{
if (TEST_FLAG(object->object.flags, _object_garbage_bit))
{
++object_globals->active_garbage_object_count;
}
if (object->object.animation.interpolation_frame_count)
{
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 2508, !TEST_FLAG(_object_mask_cannot_interpolate, object->object.type));
if (++object->object.animation.interpolation_frame_index>=
object->object.animation.interpolation_frame_count)
{
object->object.animation.interpolation_frame_count = 0;
}
}
object_type_update(object_index);
if (object_definition->object.collision_model.index!=NONE)
{
object_damage_update(object_index);
}
object_type_export_function_values(object_index);
if (!TEST_FLAG(object->object.flags, _object_do_not_recompute_node_matrices_bit))
{
object_compute_node_matrices(object_index);
}
object_compute_function_values(object_index);
object_compute_change_colors(object_index);
if (
TEST_FLAG(object->object.flags, _object_dynamic_lighting_recompute_bit) &&
(
!TEST_FLAG(object->object.flags, _object_invisible_bit) ||
object_definition_get(object->definition_index)->object.model.index==NONE
)
)
{
object_connect_lights(object_index, TRUE, TRUE);
}
// Update children (if we have any)
if (object->object.first_child_object_index!=NONE)
{
object_update(object->object.first_child_object_index);
}
if (object->object.parent_object_index!=NONE)
{
if (object->object.next_object_index!=NONE)
{
object_update(object->object.next_object_index);
}
}
object_postprocess_node_matrices(object_index);
}
return result;
}
void object_compute_node_matrices_recursive(
long object_index)
{
long child_index;
struct object_datum *child_object;
struct object_datum *object = object_get(object_index);
object_compute_node_matrices(object_index);
for (
child_index = object->object.first_child_object_index;
child_index!=NONE;
child_index = child_object->object.next_object_index)
{
child_object = object_get(child_index);
object_compute_node_matrices_recursive(child_index);
}
return;
}
long object_new_from_scenario(
struct scenario_object_datum *scenario_object,
struct tag_block *palette)
{
struct object_placement_data placement_data;
long result = NONE;
if (scenario_object->palette_entry_index!=NONE &&
(
!object_globals->initial_placement ||
!TEST_FLAG(scenario_object->placement_flags, _scenario_object_placement_not_automatic_bit)
))
{
if (scenario_object->name_index==NONE ||
object_name_list_lookup(scenario_object->name_index)==NONE)
{
long definition_index = TAG_BLOCK_GET_ELEMENT(
palette,
scenario_object->palette_entry_index,
struct scenario_object_palette_entry
)->reference.index;
if (definition_index!=NONE)
{
object_placement_data_new(&placement_data, definition_index, NONE);
placement_data.position = scenario_object->position;
vectors3d_from_euler_angles3d(
&placement_data.forward,
&placement_data.up,
&scenario_object->rotation);
placement_data.variant_number = scenario_object->variant_number;
result = object_new(&placement_data);
if (result!=NONE)
{
object_type_place(result, scenario_object);
if (scenario_object->name_index!=NONE)
{
object_name_list_new(result, scenario_object->name_index);
}
}
}
}
}
return result;
}
void object_attach_to_marker(
long parent_object_index,
char const *parent_marker_name,
long child_object_index,
char const *child_marker_name)
{
struct object_marker parent_marker;
struct object_marker child_marker;
real_matrix4x3 inverted_matrix;
struct object_datum *child_object = object_get(child_object_index);
object_get_marker_by_name(parent_object_index, parent_marker_name, &parent_marker, 1);
object_get_marker_by_name(child_object_index, child_marker_name, &child_marker, 1);
object_disconnect_from_map(child_object_index);
if (child_marker_name && *child_marker_name)
{
object_align_marker_to_matrix(child_object, &child_marker, &parent_marker.matrix);
}
else
{
matrix4x3_inverse(&child_marker.node_matrix, &inverted_matrix);
matrix4x3_transform_point(&inverted_matrix, &parent_marker.matrix.position, &child_object->object.position);
matrix4x3_transform_normal(&inverted_matrix, &parent_marker.matrix.forward, &child_object->object.forward);
matrix4x3_transform_normal(&inverted_matrix, &parent_marker.matrix.up, &child_object->object.up);
}
object_reconnect_to_map(child_object_index, NULL);
object_attach_to_node(parent_object_index, child_object_index, parent_marker.node_index);
return;
}
long object_new_by_name(
short name_index)
{
long placement_tag_block_element_size;
struct scenario *scenario = global_scenario_get();
struct scenario_object_name* name = TAG_BLOCK_GET_ELEMENT(
&scenario->object_names,
name_index,
struct scenario_object_name);
struct tag_block* datum = scenario_get_object_type_scenario_datums(
scenario,
name->runtime_object_type,
&placement_tag_block_element_size);
struct tag_block *palette = scenario_get_object_type_scenario_palette(
scenario,
name->runtime_object_type);
struct scenario_object_datum *object = (struct scenario_object_datum *)tag_block_get_element_with_size(
datum,
name->runtime_scenario_datum_index,
placement_tag_block_element_size);
return object_new_from_scenario(
object,
palette);
}
void objects_scripting_attach(
long parent_object_index,
char const *parent_marker_name,
long child_object_index,
char const *child_marker_name)
{
if (parent_object_index!=NONE &&
child_object_index !=NONE &&
object_get(child_object_index)->object.parent_object_index==NONE)
{
network_coop_note_attach(parent_object_index, parent_marker_name, child_object_index, child_marker_name);
object_attach_to_marker(parent_object_index, parent_marker_name, child_object_index, child_marker_name);
}
return;
}
void object_delete_immediately(
long object_index)
{
if (!network_objects_may_delete(object_index))
return;
object_delete_initial_recursive(object_index, FALSE);
object_delete_recursive(object_index, FALSE);
return;
}
/* the active garbage limits are per 16 players: a session of up to 128
keeps proportionally more garbage around, campaign and smaller games the
Xbox's amount */
static long active_garbage_limit(
long limit_per_16_players)
{
long player_count = player_data ? player_data->actual_count : 0;
return limit_per_16_players * MAX(16, player_count) / 16;
}
void objects_garbage_collection(
void)
{
short garbage_collect_mode = NONE;
if (object_globals->force_garbage_collection)
{
garbage_collect_mode = _garbage_collect_everything;
}
else if (memory_pool_get_contiguous_free_size(object_memory_pool)<=GARBAGE_LIMIT_FREE_MEMORY_CRITICAL)
{
memory_pool_compact(object_memory_pool);
if (memory_pool_get_contiguous_free_size(object_memory_pool)<=GARBAGE_LIMIT_FREE_MEMORY_TRIGGER)
{
garbage_collect_mode = _garbage_collect_for_space;
}
}
else
{
if (MAXIMUM_OBJECTS_PER_MAP-object_header_data->actual_count<=GARBAGE_LIMIT_FREE_OBJECTS_TRIGGER)
{
garbage_collect_mode = _garbage_collect_for_space;
}
else
{
if (object_globals->active_garbage_object_count>=active_garbage_limit(GARBAGE_LIMIT_ACTIVE_GARBAGE_TRIGGER))
{
garbage_collect_mode = _garbage_collect_active_objects;
}
}
}
if (garbage_collect_mode!=NONE)
{
long garbage_collect_mode_wide;
short garbage_object_count = 0;
boolean should_collect = FALSE;
{
long garbage_object_indices[MAXIMUM_OBJECTS_PER_MAP];
if (debug_object_garbage_collection)
{
console_printf(
FALSE,
"#%d objects using 0x%x bytes (0x%x contiguous free)",
object_header_data->actual_count,
OBJECT_MEMORY_POOL_SIZE - memory_pool_get_free_size(object_memory_pool),
memory_pool_get_contiguous_free_size(object_memory_pool));
}
{
long garbage_object_index;
struct object_datum *object;
for (
garbage_object_index = object_globals->first_garbage_object_index;
garbage_object_index!=NONE;
garbage_object_index = object->object.next_garbage_object_index)
{
object = object_get(garbage_object_index);
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 4289, garbage_object_count<MAXIMUM_OBJECTS_PER_MAP);
garbage_object_indices[garbage_object_count++] = garbage_object_index;
}
}
garbage_collect_mode_wide = garbage_collect_mode;
while (TRUE)
{
long object_index;
struct object_header_datum *header;
boolean garbage_collect;
switch (garbage_collect_mode_wide)
{
case _garbage_collect_everything:
should_collect = FALSE;
break;
case _garbage_collect_active_objects:
should_collect = object_globals->active_garbage_object_count<=
active_garbage_limit(GARBAGE_LIMIT_ACTIVE_GARBAGE_TARGET);
break;
case _garbage_collect_for_space:
should_collect =
memory_pool_get_free_size(object_memory_pool)>=GARBAGE_LIMIT_FREE_MEMORY_TRIGGER &&
MAXIMUM_OBJECTS_PER_MAP-object_header_data->count>=GARBAGE_LIMIT_FREE_OBJECTS_TARGET;
break;
default:
match_vassert("c:\\halo\\SOURCE\\objects\\objects.c", 4314, FALSE, NULL);
break;
}
if (should_collect || garbage_object_count==0)
{
break;
}
object_index = garbage_object_indices[--garbage_object_count];
header = object_header_get(object_index);
garbage_collect = TRUE;
if (garbage_collect_mode==_garbage_collect_active_objects)
{
garbage_collect = TEST_FLAG(header->flags, _object_header_active_bit);
}
if (object_visible_to_any_player(object_index))
{
garbage_collect = FALSE;
}
if (garbage_collect)
{
struct object_datum *garbage_object = object_get(object_index);
if (TEST_FLAG(_object_mask_unit, garbage_object->object.type) &&
!TEST_FLAG(garbage_object->object.damage_flags, _object_dead_bit))
{
error(
_error_silent,
"WARNING: garbage collecting a living unit (%s)",
ai_debug_describe_actor(NONE, object_index, TRUE, temporary, NUMBEROF(temporary)));
}
if (TEST_FLAG(header->flags, _object_header_active_bit))
{
--object_globals->active_garbage_object_count;
}
object_set_garbage(object_index, FALSE);
object_delete_immediately(object_index);
}
}
}
memory_pool_compact(object_memory_pool);
if (debug_object_garbage_collection)
{
console_printf(
FALSE,
"compacted to #%d with 0x%x contiguous bytes free",
object_header_data->actual_count,
memory_pool_get_contiguous_free_size(object_memory_pool));
}
if (!should_collect)
{
char warningbuf[512];
unsigned char release_proc_working_memory[4096];
char released_resultbuf[512];
const struct object_memory_release_function *current_release_procs = object_memory_release_procs;
boolean v0 = FALSE;
boolean garbage_collection_after_first_attempt = FALSE;
boolean garbage_should_warn = object_globals->last_garbage_warn_time==NONE || object_globals->last_garbage_warn_time+150<game_time_get();
boolean update_time = FALSE;
while (TRUE)
{
boolean debug_garbage_collection = FALSE;
boolean status_still_critical = FALSE;
switch (garbage_collect_mode_wide)
{
case _garbage_collect_for_space:
{
long free_size = memory_pool_get_contiguous_free_size(object_memory_pool);
long free_objects = MAXIMUM_OBJECTS_PER_MAP-object_header_data->count;
boolean debug_slots_free = FALSE;
if (free_size<=GARBAGE_LIMIT_FREE_MEMORY_CRITICAL/2)
{
status_still_critical = TRUE;
debug_garbage_collection = TRUE;
}
else
{
if (free_objects<=GARBAGE_LIMIT_FREE_OBJECTS_CRITICAL)
{
status_still_critical = TRUE;
debug_garbage_collection = TRUE;
debug_slots_free = TRUE;
}
else
{
if (free_size<=GARBAGE_LIMIT_FREE_MEMORY_CRITICAL)
{
debug_garbage_collection = TRUE;
}
else
{
if (free_objects<=GARBAGE_LIMIT_FREE_OBJECTS_TRIGGER)
{
debug_garbage_collection = TRUE;
debug_slots_free = TRUE;
}
}
}
}
if (debug_slots_free)
{
sprintf(warningbuf, "%d slots free", free_objects);
}
else
{
sprintf(warningbuf, "%4.2f%% memory free", ((free_size * 100.f) / (real)OBJECT_MEMORY_POOL_SIZE));
}
}
break;
}
if (status_still_critical || garbage_collection_after_first_attempt)
{
char tempbuffer[512];
const char *status;
if (garbage_collection_after_first_attempt)
{
status = status_still_critical ? "still " : "not ";
}
else
{
status = "";
}
sprintf(tempbuffer, "garbage collection %scritical (%s)", status, warningbuf);
/* (port: chatter, shown as config.toml's game.console_log says:
many enemies keep it coming, over the whole screen) */
if (terminal_shows(_terminal_message_chatter))
console_printf(FALSE, "%s", tempbuffer);
error(_error_log, "%s", tempbuffer);
update_time = TRUE;
}
else if (debug_garbage_collection && garbage_should_warn)
{
error(_error_silent, "garbage collection warning (%s)", warningbuf);
update_time = TRUE;
}
if (status_still_critical)
{
if (current_release_procs->function)
{
boolean result = FALSE;
while (current_release_procs->function && !result)
{
boolean more_to_release = FALSE;
if (!v0 && current_release_procs->init_function)
{
current_release_procs->init_function(
release_proc_working_memory,
sizeof(release_proc_working_memory));
v0 = TRUE;
}
result = current_release_procs->function(
released_resultbuf,
&more_to_release,
release_proc_working_memory,
sizeof(release_proc_working_memory));
if (result)
{
char tempbuffer[512];
sprintf(tempbuffer, "removing objects: %s", released_resultbuf);
/* (port: chatter, shown as config.toml's game.console_log says) */
if (terminal_shows(_terminal_message_chatter))
console_printf(FALSE, "%s", tempbuffer);
error(_error_log, "%s", tempbuffer);
}
if (!more_to_release)
{
++current_release_procs;
v0 = FALSE;
}
}
if (!result)
{
break;
}
garbage_collection_after_first_attempt = TRUE;
memory_pool_compact(object_memory_pool);
}
else
{
break;
}
}
else
{
break;
}
}
if (update_time)
{
object_globals->last_garbage_warn_time = game_time_get();
}
}
}
object_globals->force_garbage_collection = FALSE;
return;
}
void objects_update(
void)
{
static struct profile_section section = {"objects_update", NONE, TRUE};
unsigned long *last_active_cluster_bits;
unsigned long *active_cluster_bits;
short cluster_count;
short i;
struct object_header_datum *object_header;
boolean dont_update_object = ((game_time_get()&1)!=0) && game_players_are_double_speed();
profile_enter(section);
object_globals->active_garbage_object_count = 0;
last_active_cluster_bits = object_globals->last_active_cluster_bits;
active_cluster_bits = object_globals->active_cluster_bits;
cluster_count = global_structure_bsp_get()->clusters.count;
memcpy(last_active_cluster_bits, active_cluster_bits, BIT_VECTOR_SIZE_IN_BYTES(cluster_count));
memcpy(active_cluster_bits, players_get_combined_pvs(), BIT_VECTOR_SIZE_IN_BYTES(cluster_count));
if (csmemcmp(last_active_cluster_bits, active_cluster_bits, BIT_VECTOR_SIZE_IN_BYTES(cluster_count)))
{
object_header = (struct object_header_datum *)object_header_data->data;
for (i = 0; i<object_header_data->count; ++object_header)
{
if (object_header->identifier &&
TEST_FLAG(object_header->flags, _object_header_automatically_deactivate_bit) &&
TEST_FLAG(object_header->flags, _object_header_connected_to_map_bit))
{
if (TEST_FLAG(object_header->flags, _object_header_active_bit))
{
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 369, object_header->cluster_index!=NONE);
if (!BIT_VECTOR_TEST_FLAG(active_cluster_bits, object_header->cluster_index))
{
if (TEST_FLAG(object_header->datum->object.flags, _object_deleted_when_deactivated_bit))
{
object_delete_initial_recursive(i, FALSE);
}
else
{
object_deactivate(i);
}
}
}
else if (
!TEST_FLAG(object_header->flags, _object_header_child_bit) &&
object_header->cluster_index != NONE &&
BIT_VECTOR_TEST_FLAG(active_cluster_bits, object_header->cluster_index)
)
{
object_activate(i);
}
}
++i;
}
structure_decals_update(
last_active_cluster_bits,
active_cluster_bits,
cluster_count);
}
object_header = (struct object_header_datum *)object_header_data->data;
for (i = 0; i<object_header_data->count; ++object_header)
{
if (object_header->identifier)
{
if (TEST_FLAG(object_header->flags, _object_header_active_bit) &&
!TEST_FLAG(object_header->flags, _object_header_being_created_bit))
{
long object_index = DATUM_INDEX_NEW(i, object_header->identifier);
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 416, object_get(object_index)->object.parent_object_index==NONE);
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 417, object_get(object_index)->object.next_object_index==NONE);
if (!dont_update_object ||
TEST_FLAG(_object_mask_unit, object_header->type) &&
unit_get(object_index)->unit.player_index!=NONE)
{
object_update(object_index);
}
}
}
++i;
}
object_header = (struct object_header_datum *)object_header_data->data;
for (i = 0; i<object_header_data->count; ++object_header)
{
if (object_header->identifier)
{
SET_FLAG(object_header->flags, _object_header_do_not_update_bit, FALSE);
if (TEST_FLAG(object_header->flags, _object_header_being_created_bit))
{
SET_FLAG(
object_header->flags,
_object_header_being_created_bit,
FALSE);
object_update(DATUM_INDEX_NEW(i, object_header->identifier));
}
if (TEST_FLAG(object_header->flags, _object_header_being_deleted_bit))
{
object_delete_recursive(DATUM_INDEX_NEW(i, object_header->identifier), FALSE);
}
}
++i;
}
objects_garbage_collection();
profile_exit(section);
return;
}
boolean debug_objects_bounding_spheres = TRUE;
boolean debug_objects_collision_models = TRUE;
void objects_memory_compact(
void)
{
objects_garbage_collection();
memory_pool_compact(object_memory_pool);
return;
}
/* ---------- private code */
static void object_connect_lights(
long object_index,
boolean disconnect,
boolean reconnect)
{
struct object_datum *object = object_get(object_index);
if (TEST_FLAG(object->object.flags, _object_has_attached_lights_bit))
{
short i;
struct object_definition *object_definition = object_definition_get(object->definition_index);
for (i =0; i<object_definition->object.attachments.count; ++i)
{
if (!object->object.attachment_types[i] && object->object.attachment_indices[i]!=NONE)
{
if (disconnect)
{
light_disconnect_from_map(object->object.attachment_indices[i]);
}
if (reconnect)
{
light_reconnect_to_map(object->object.attachment_indices[i]);
}
}
}
}
return;
}
static void object_name_list_allocate(
void)
{
object_name_list = (long*)game_state_malloc("object name list", NULL, MAXIMUM_OBJECT_NAMES_PER_SCENARIO * sizeof(*object_name_list));
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 4072, object_name_list);
return;
}
static void object_name_list_free(
void)
{
// Bungie never free'd this properly
return;
}
static void object_name_list_clear(
void)
{
memset(object_name_list, NONE, MAXIMUM_OBJECT_NAMES_PER_SCENARIO * sizeof(*object_name_list));
return;
}
static void object_name_list_new(
long object_index,
short name_index)
{
struct object_datum *object = object_get(object_index);
match_assert(
"c:\\halo\\SOURCE\\objects\\objects.c",
4099,
name_index>=0 && name_index<MAXIMUM_OBJECT_NAMES_PER_SCENARIO);
if (object_name_list[name_index]==NONE)
{
object_name_list[name_index] = object_index;
object->object.name_index = name_index;
}
else
{
error(
_error_silent,
"an object with the name '%s' already exists!",
TAG_BLOCK_GET_ELEMENT(&global_scenario_get()->object_names, name_index, struct scenario_object_name)->name);
}
return;
}
static void object_name_list_delete(
long object_index)
{
struct object_datum *object = object_get(object_index);
if (object->object.name_index!=NONE)
{
short i;
struct scenario *scenario = global_scenario_get();
object->object.name_index = NONE;
for (i =0; i<scenario->object_names.count; ++i)
{
if (object_name_list[i]==object_index)
{
object_name_list[i] = NONE;
}
}
}
return;
}
static long object_name_list_lookup(
short name_index)
{
return VALID_INDEX(name_index, MAXIMUM_OBJECT_NAMES_PER_SCENARIO) ? object_name_list[name_index] : NONE;
}
static long recursive_object_adder(
long object_index,
boolean(*add_object_function)(long, void *),
void *custom_data,
long object_count,
long maximum_count,
long *object_indices)
{
struct object_datum *object = object_get(object_index);
if (object_count<maximum_count)
{
if (!add_object_function || add_object_function(object_index, custom_data))
{
object_indices[object_count++] = object_index;
}
if (object->object.first_child_object_index!=NONE)
{
object_count = recursive_object_adder(
object->object.first_child_object_index,
add_object_function,
custom_data,
object_count,
maximum_count,
object_indices);
}
if (object->object.next_object_index!=NONE)
{
object_count = recursive_object_adder(
object->object.next_object_index,
add_object_function,
custom_data,
object_count,
maximum_count,
object_indices);
}
}
return object_count;
}
static long sort_dumps(
struct dump_datum const *dump1,
struct dump_datum const *dump2)
{
long result;
if (dump1->total_size < dump2->total_size)
{
result = 1;
}
else if (dump1->total_size <= dump2->total_size)
{
result = 0;
}
else
{
result = -1;
}
return result;
}
static void object_add_to_dump(
long object_index,
struct dump_datum *dump)
{
long parent_index;
struct object_datum *parent;
struct object_header_datum *header = object_header_get(object_index);
struct object_datum *object = object_get(object_index);
if (header->data_size>dump->maximum_size)
{
dump->maximum_size = header->data_size;
}
dump->total_size+= header->data_size;
dump->count++;
if (TEST_FLAG(header->flags, _object_header_active_bit))
{
dump->active_count++;
}
if (TEST_FLAG(object->object.flags, _object_garbage_bit))
{
dump->garbage_count++;
}
if (TEST_FLAG(object->object.damage_flags, _object_dead_bit))
{
dump->dead_count++;
}
if (TEST_FLAG(object->object.flags, _object_at_rest_bit))
{
dump->at_rest_count++;
}
parent_index = object_get_ultimate_parent(object_index);
parent = object_get(parent_index);
if (TEST_FLAG(parent->object.flags, _object_outside_of_map_bit) || parent->object.location.cluster_index==NONE)
{
dump->outside_map_count++;
}
return;
}
static void object_dump_write(
FILE *stream,
struct dump_datum *dump)
{
const char *name = "unknown";
if (dump->definition_index!=NONE)
{
name = tag_get_name(dump->definition_index);
}
else
{
if (dump->object_type!=NONE)
{
name = object_type_get_name(dump->object_type);
}
}
fprintf(
stream,
"% 6d (% 6d) [% 7d/% 7d/% 7d/% 7d] % 7d % 7d %s\r\n",
dump->count,
dump->active_count,
dump->garbage_count,
dump->dead_count,
dump->outside_map_count,
dump->at_rest_count,
dump->maximum_size,
dump->total_size,
name);
return;
}
static short object_find_region_permutations_available_with_variant(
struct model_region *region,
short variant_number,
short *available_indices)
{
short i;
short result = 0;
for (i = 0; i<region->permutations.count; i++)
{
struct model_region_permutation* permutation = TAG_BLOCK_GET_ELEMENT(
&region->permutations,
i,
struct model_region_permutation);
if (!TEST_FLAG(permutation->flags, _model_region_permutation_cannot_be_chosen_randomly_bit))
{
if (permutation->variant_number==variant_number ||
variant_number==NONE && permutation->variant_number< 100)
{
available_indices[result++] = i;
}
}
}
return result;
}
static boolean object_select_random_region_permutations_by_variant(
long object_index,
struct model *model,
short variant_number)
{
short available_permutation_indices[32];
short region_index;
struct object_datum *object = object_get(object_index);
boolean result = TRUE;
for (region_index =0; region_index<model->regions.count; region_index++)
{
struct model_region *region = TAG_BLOCK_GET_ELEMENT(&model->regions, region_index, struct model_region);
short permutation_index = object_find_region_permutations_available_with_variant(region, variant_number, available_permutation_indices);
if (permutation_index==0 && variant_number!=NONE)
{
permutation_index = object_find_region_permutations_available_with_variant(region, 0, available_permutation_indices);
}
if (!permutation_index)
{
object->object.region_permutations[region_index] = 0;
result = FALSE;
}
else
{
short permutation_num = permutation_index==1 ? 0 : random_range(0, permutation_index);
object->object.region_permutations[region_index] = available_permutation_indices[permutation_num];
}
}
return result;
}
static short object_determine_variant_number(
long object_index,
struct model *model)
{
short region_index;
struct object_datum *object = object_get(object_index);
short result = 0;
for (region_index =0; region_index<model->regions.count&& !result; region_index++)
{
struct model_region* region = TAG_BLOCK_GET_ELEMENT(&model->regions, region_index, struct model_region);
if (object->object.region_permutations[region_index] < region->permutations.count)
{
result = TAG_BLOCK_GET_ELEMENT(
&region->permutations,
object->object.region_permutations[region_index],
struct model_region_permutation
)->variant_number;
}
}
return result;
}
static void object_choose_random_region_permutations(long object_index)
{
struct object_datum *object = object_get(object_index);
struct object_definition *object_definition = object_definition_get(object->definition_index);
if (object_definition->object.model.index!=NONE)
{
struct model* model = model_definition_get(object_definition->object.model.index);
if (object->object.variant_number <= 0 ||
!object_select_random_region_permutations_by_variant(object_index, model, object->object.variant_number))
{
object_select_random_region_permutations_by_variant(object_index, model, NONE);
object->object.variant_number = object_determine_variant_number(object_index, model);
if (object->object.variant_number > 0)
{
object_select_random_region_permutations_by_variant(object_index, model, object->object.variant_number);
}
}
}
return;
}
static void object_add_to_list(
long *first_object_reference,
long object_index)
{
struct object_datum *object = object_get(object_index);
object->object.next_object_index = *first_object_reference;
*first_object_reference = object_index;
return;
}
static void object_remove_from_list(
long *first_object_reference,
long object_index)
{
while (*first_object_reference!=NONE)
{
struct object_datum *object = object_get(*first_object_reference);
if (*first_object_reference==object_index)
{
*first_object_reference = object->object.next_object_index;
object->object.next_object_index = NONE;
break;
}
first_object_reference = &object->object.next_object_index;
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 3179, *first_object_reference!=NONE);
}
return;
}
static void attachments_delete(
long object_index)
{
short attachment_index;
struct object_datum *object = object_get(object_index);
struct object_definition *object_definition = object_definition_get(object->definition_index);
for (attachment_index =0; attachment_index<object_definition->object.attachments.count; ++attachment_index)
{
if (object->object.attachment_types[attachment_index]!=NONE &&
object->object.attachment_indices[attachment_index]!=NONE)
{
switch (object->object.attachment_types[attachment_index])
{
case _object_attachment_type_light:
light_delete(object->object.attachment_indices[attachment_index]);
break;
case _object_attachment_type_looping_sound:
game_looping_sound_delete(object->object.attachment_indices[attachment_index]);
break;
case _object_attachment_type_effect:
effect_delete(object->object.attachment_indices[attachment_index]);
break;
case _object_attachment_type_contrail:
object_compute_node_matrices(object_index);
contrail_owner_collision(object->object.attachment_indices[attachment_index], TRUE, 0.f);
break;
case _object_attachment_type_particle_system:
particle_system_orphan(object->object.attachment_indices[attachment_index]);
break;
default:
break;
}
}
}
return;
}
static void object_create_attachments(
long object_index)
{
struct object_datum *object = object_get(object_index);
struct object_definition *object_definition = object_definition_get(object->definition_index);
widgets_new(object_index);
attachments_new(object_index);
return;
}
static void object_delete_attachments(
long object_index)
{
struct object_datum *object = object_get(object_index);
struct object_definition *object_definition = object_definition_get(object->definition_index);
object_type_definition_get(object->object.type);
widgets_delete(object_index);
attachments_delete(object_index);
return;
}
static void object_delete_recursive(
long object_index,
boolean delete_siblings)
{
struct object_datum *object = object_get(object_index);
struct object_definition *object_definition = object_definition_get(object->definition_index);
if (TEST_FLAG(object->object.flags, _object_garbage_bit))
{
object_set_garbage(object_index, FALSE);
}
object_deleted_procs_call(object_index);
if (object->object.first_child_object_index!=NONE)
{
object_delete_recursive(object->object.first_child_object_index, TRUE);
}
if (delete_siblings && object->object.next_object_index!=NONE)
{
object_delete_recursive(object->object.next_object_index, TRUE);
}
object_deactivate(object_index);
object_delete_attachments(object_index);
if (TEST_FLAG(object->object.flags, _object_connected_to_map_bit))
{
object_disconnect_from_map(object_index);
}
object_type_delete(object_index);
object_header_delete(object_header_data, object_index);
return;
}
static void object_compute_function_values(
long object_index)
{
short function_index;
struct object_datum *object = object_get(object_index);
struct object_definition *object_definition = object_definition_get(object->definition_index);
real huh = (57 * DATUM_INDEX_TO_ABSOLUTE_INDEX(object_index) + game_time_get()) * 0.033333335f;
for (function_index = 0; function_index<object_definition->object.functions.count; ++function_index)
{
real value;
real output;
struct object_function_definition* function = TAG_BLOCK_GET_ELEMENT(
&object_definition->object.functions,
function_index,
struct object_function_definition);
boolean function_is_active = TRUE;
real period = function->runtime_one_over_period;
if (function->scale_period_by_function_index)
{
real function_value = OBJECT_INCOMING_FUNCTION_GET_VALUE(object, function->scale_period_by_function_index);
if (function_value>0.f)
{
period = period/function_value;
}
}
value = periodic_function_evaluate(function->function_type, huh*period);
if (function->scale_function_by_function_index)
{
value*= OBJECT_INCOMING_FUNCTION_GET_VALUE(object, function->scale_function_by_function_index);
}
if (TEST_FLAG(function->flags, _object_function_invert_function_bit))
{
value = 1.f-value;
}
if (function->wobble_magnitude!=0.f)
{
real val = periodic_function_evaluate(function->wobble_function_type, huh*function->wobble_period);
value+= (2.f*function->wobble_magnitude * (val-0.5f));
}
if (function->square_wave_threshold!=0.f)
{
value = value>function->square_wave_threshold ? 1.f : 0.f ;
}
if (function->step_count>1)
{
real floor_value = floor(function->step_count*value);
value = floor_value*function->runtime_reciprocal_step_count;
}
if (function->runtime_reciprocal_sawtooth_count>0.f)
{
value = fmod(value, function->runtime_reciprocal_sawtooth_count);
}
if (function->add_function_index)
{
value+= OBJECT_INCOMING_FUNCTION_GET_VALUE(object, function->add_function_index);
value = MIN(value, 1.f);
}
if (function->scale_result_by_function_index)
{
value*= OBJECT_INCOMING_FUNCTION_GET_VALUE(object, function->scale_result_by_function_index);
}
output = transition_function_evaluate(function->map_result_to_transition_function, value);
if (function->scale_by>0.f)
{
output*= function->scale_by;
}
if (function->bounds_mode==_object_function_scale_to_fit_bounds)
{
output*= (function->upper_bound-function->lower_bound);
output+= function->lower_bound;
if (function->lower_bound+_real_epsilon>=output)
{
function_is_active = TEST_FLAG(function->flags, _object_function_does_not_deactivate_below_lower_bound_bit);
}
}
else
{
if (function->lower_bound+_real_epsilon>=output)
{
output = function->lower_bound;
function_is_active = TEST_FLAG(function->flags, _object_function_does_not_deactivate_below_lower_bound_bit);
}
if (output>function->upper_bound)
{
output = function->upper_bound;
}
if (function->bounds_mode==_object_function_clip_to_bounds_and_normalize)
{
output = (output-function->lower_bound)*function->runtime_reciprocal_bounds_range;
}
}
if (function->turn_off_with_function_index!=NONE &&
!TEST_FLAG(object->object.functions_active_flags, function->turn_off_with_function_index))
{
function_is_active = FALSE;
}
if (TEST_FLAG(function->flags, _object_function_clip_to_bounds_and_normalize))
{
output = fmod(output+object->object.outgoing_function_values[function_index], 1.f);
}
object->object.outgoing_function_values[function_index] = output;
SET_FLAG(object->object.functions_active_flags, function_index, function_is_active);
}
return;
}
static void object_compute_change_colors(
long object_index)
{
struct object_datum *object = object_get(object_index);
struct object_definition *object_definition = object_definition_get(object->definition_index);
if (TEST_FLAG(object_definition->object.runtime_flags, _object_runtime_scaled_change_colors_bit))
{
short cc_index;
for (cc_index = 0; cc_index<object_definition->object.change_colors.count; cc_index++)
{
struct object_change_color_definition *change_color = TAG_BLOCK_GET_ELEMENT(
&object_definition->object.change_colors,
cc_index,
struct object_change_color_definition);
if (change_color->scaled_by)
{
rgb_colors_interpolate(
&object->object.outgoing_change_colors[cc_index],
change_color->scale_flags,
&change_color->color_lower_bound,
&change_color->color_upper_bound,
OBJECT_INCOMING_FUNCTION_GET_VALUE(object, change_color->scaled_by));
}
if (change_color->darken_by)
{
const real scale = OBJECT_INCOMING_FUNCTION_GET_VALUE(object, change_color->darken_by);
object->object.outgoing_change_colors[cc_index].red*= scale;
object->object.outgoing_change_colors[cc_index].green*= scale;
object->object.outgoing_change_colors[cc_index].blue*= scale;
}
object->object.outgoing_change_colors[cc_index].red = PIN(
object->object.outgoing_change_colors[cc_index].red,
0.f,
1.f);
object->object.outgoing_change_colors[cc_index].green = PIN(
object->object.outgoing_change_colors[cc_index].green,
0.f,
1.f);
object->object.outgoing_change_colors[cc_index].blue = PIN(
object->object.outgoing_change_colors[cc_index].blue,
0.f,
1.f);
}
}
return;
}