Files
Cesarus85--ringfall-mjolnir-vr/source/objects/objects.c
T
justinfilip af7ad15d3c Keep a respawned vehicle on the name its teleporter uses.
The timer was leaving the named vehicle on the pad and creating another on top of it. The home now follows that named vehicle, and a pad that already has one does not get a second.
2026-10-10 13:25:53 -05:00

5345 lines
153 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"
/* port: object_bounds_cache.c's */
void object_bounds_cache_update(long object_index, real_point3d const *center, real radius);
/* ---------- constants */
/* ---------- macros */
#define MAXIMUM_DUMPS 1024
/* port: the garbage collection (objects_garbage_collection). With many
players spread over a level and many bodies (co-op's extra enemies), every
body was in some player's view, none could go, and the whole collection
(every garbage object's visibility, the memory pool compacted) ran again
every tick on the host. In network co-op only (single player and
multiplayer keep the game's own collection), a player sees garbage no
further off than GARBAGE_VISIBLE_DISTANCE (world units, about 90 metres),
and a collection of active garbage that left too much is tried again only
after GARBAGE_ACTIVE_RETRY_TICKS. Running short of memory or objects still
collects at once. */
#define GARBAGE_VISIBLE_DISTANCE 30.0f
#define GARBAGE_ACTIVE_RETRY_TICKS TICKS_PER_SECOND
// This is dangerous, bungie returns the same value regardless of whether the index is valid
/* port: an index is 1-4 for the incoming functions and 5-8 for the outgoing
ones (the Xbox read both through incoming_function_values, which the
outgoing ones follow; retail uses up to 8). Anything else is a map's bad
index, which reads no function: 0 */
#define OBJECT_INCOMING_FUNCTION_GET_VALUE(object, index) \
((unsigned long)((index)-1)>=NUMBER_OF_INCOMING_OBJECT_FUNCTIONS+NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS ? \
0.f : \
(index)>=NUMBER_OF_INCOMING_OBJECT_FUNCTIONS+1 ? \
(object)->object.outgoing_function_values[(index)-NUMBER_OF_INCOMING_OBJECT_FUNCTIONS-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;
};
/* port: when a collection of active garbage may be tried again (NONE: at
once); not in the game state, so a revert, which takes the clock back, puts
it right (objects_garbage_active_due) */
static long garbage_active_retry_time = NONE;
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);
boolean network_coop_active(void);
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);
static void object_model_nodes_error(long model_index, struct model const *model);
static void object_definition_data_error(long definition_index, char const *problem);
/* ---------- 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)
{
/* port: and one the name list holds (a map's count; retail has up to
448 names) */
if (VALID_INDEX(name_index, MIN(global_scenario_get()->object_names.count, MAXIMUM_OBJECT_NAMES_PER_SCENARIO)))
{
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)
{
/* (port: a co-op host sends it, so any index outside the BSP's clusters is none) */
if (cluster_index < 0 || 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);
/* port: no more regions than the object's region_permutations holds
(a map's count; retail has up to 8) */
for (region_index = 0; region_index<MIN(model->regions.count, MAXIMUM_REGIONS_PER_OBJECT); 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);
/* port: no more permutations than a region has room for (a map's
count, which a short counter would never reach; retail has up
to 12) */
for (permutation_index = 0; permutation_index<MIN(region->permutations.count, MAXIMUM_PERMUTATIONS_PER_MODEL_REGION); 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);
/* port: a function the object can have (a map's index, from an
attachment, a widget or a shader; retail's are NONE or 0-3), or
it reads as off */
if (VALID_INDEX(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);
}
else
{
static boolean function_index_reported = FALSE;
if (!function_index_reported)
{
function_index_reported = TRUE;
error(
_error_silent,
"### ERROR %s asked for function #%d; it reads as off",
tag_get_name(object->definition_index),
function_index);
}
*value_reference = 0.f;
result = FALSE;
}
}
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 ((!network_coop_active() || distance <= GARBAGE_VISIBLE_DISTANCE) &&
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));
/* port: a node the object has no matrix for (an index a map's data
gave) is its first node, which every object has, not whatever lies
past its matrices */
{
struct object_datum *object = object_get(object_index);
if (node_index < 0 ||
node_index >= object->object.node_matrices.size / (short)sizeof(real_matrix4x3))
{
static boolean logged = FALSE;
if (!logged)
{
logged = TRUE;
error(_error_silent, "object %08lx has no node #%d: its first node is used",
(unsigned long)object_index, node_index);
}
node_index = 0;
}
}
{
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);
/* port: a region the object has, or 0 (a map's function mode;
retail's are 0-5, 10, 18 and 19) */
if (VALID_INDEX(region_index, MAXIMUM_REGIONS_PER_OBJECT))
{
value = object->object.region_damage[region_index] / 255.f;
}
else
{
object_definition_data_error(object->definition_index, "function mode; it reads 0");
}
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);
/* port: a model without nodes is placed like no model (a map's count) */
if (object_definition->object.model.index!=NONE &&
model_definition_get(object_definition->object.model.index)->nodes.count>0)
{
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);
/* port: the nodes node_stack and the orientations hold (a map's count) */
short model_node_count = (short)MIN(model->nodes.count, MAXIMUM_NODES_PER_MODEL);
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);
/* port: an animation the graph has (a map's index), and a function
the object has room for (a map's index; NONE read before the
functions; retail's are 0-3) */
if (VALID_INDEX(overlay->animation_index, animation_graph->animations.count) &&
VALID_INDEX(overlay->function_index, MIN(object_definition->object.functions.count, NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS)))
{
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];
/* port: no more nodes than the model has, which is all the
orientations hold (a map's animation; retail's overlays have
the model's count) */
if (animation->node_count>model_node_count)
{
object_definition_data_error(object->definition_index, "overlay animation (more nodes than its model); it is skipped");
}
else 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);
}
/* port: and a frame to pick (a map's frame count of 0 divided
by zero) */
else if (overlay->mode==_object_overlay_mode_scale && animation->frame_count>0)
{
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];
short parent_node_index = node->parent_node_index;
matrix4x3_from_orientation(&object_nodes[node_stack_index], orientation);
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 2929, node->parent_node_index!=NONE);
/* port: a parent the model doesn't have is the root (a map's index) */
if (!VALID_INDEX(parent_node_index, model_node_count))
{
object_model_nodes_error(object_definition->object.model.index, model);
parent_node_index = 0;
}
matrix4x3_multiply(
&object_nodes[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;
/* port: only nodes the model has, and no more than node_stack holds
(a map's links, which could go in a loop) */
if (next_sibling_node_index!=NONE)
{
if (VALID_INDEX(next_sibling_node_index, model_node_count) && node_count<MAXIMUM_NODES_PER_MODEL)
{
node_stack[node_count++] = next_sibling_node_index;
}
else
{
object_model_nodes_error(object_definition->object.model.index, model);
}
}
if (node->first_child_node_index!=NONE)
{
if (VALID_INDEX(node->first_child_node_index, model_node_count) && node_count<MAXIMUM_NODES_PER_MODEL)
{
node_stack[node_count++] = node->first_child_node_index;
}
else
{
object_model_nodes_error(object_definition->object.model.index, model);
}
}
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;
}
/* port: (and in the packed copy collision reads: object_bounds_cache.c) */
object_bounds_cache_update(object_index, &object->object.bounding_sphere_center, object->object.bounding_sphere_radius);
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);
/* port: no more than the short counter reaches (a map's count;
retail has up to 8) */
for (permutation_index =0; permutation_index<MIN(cc->permutations.count, SHORT_MAX); 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);
/* port: no more attachments than the object holds (a map's count; the rest
aren't made) */
short attachment_count = (short)MIN(object_definition->object.attachments.count, MAXIMUM_NUMBER_OF_ATTACHMENTS_PER_OBJECT);
if (object_definition->object.attachments.count>MAXIMUM_NUMBER_OF_ATTACHMENTS_PER_OBJECT &&
model_data_report_once(object_definition))
{
error(
_error_silent,
"### ERROR %s has %ld attachments; only the first %d are made",
tag_get_name(object->definition_index),
object_definition->object.attachments.count,
MAXIMUM_NUMBER_OF_ATTACHMENTS_PER_OBJECT);
}
for (attachment_num = 0; attachment_num<attachment_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);
}
/* port: only an object whose type is its tag's group (a map's type, which
picks the datum's size, the type's functions and how the rest of the tag
is read; retail's all match). The object isn't made */
if (definition_index!=NONE &&
object_definition_index_to_object_type(definition_index)!=object_definition_get(definition_index)->object.type)
{
if (model_data_report_once(object_definition_get(definition_index)))
{
error(
_error_silent,
"### ERROR %s has object type #%d, not its tag's; it isn't made",
tag_get_name(definition_index),
object_definition_get(definition_index)->object.type);
}
definition_index = NONE;
}
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
{
long model_node_count = model_definition_get(object_definition->object.model.index)->nodes.count;
/* port: the root's matrix at least, which the object always has
(a map's model with no nodes is placed like no model). And no
more nodes than a block's size, a short, can hold: past that the
size wrapped and the matrices overran the object (a map's count;
the object isn't made) */
if (model_node_count==0)
{
node_count = 1;
}
else if (model_node_count<0 ||
model_node_count>SHORT_MAX/(long)sizeof(struct real_matrix4x3))
{
if (model_data_report_once(model_definition_get(object_definition->object.model.index)))
{
error(
_error_silent,
"### ERROR model %s has %ld nodes; %s isn't made",
tag_get_name(object_definition->object.model.index),
model_node_count,
tag_get_name(definition_index));
}
node_count = NONE;
}
else
{
node_count = (short)model_node_count;
}
}
if (node_count>0 &&
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];
/* port: snprintf (a map's tag's name may be any length) */
snprintf(string, sizeof(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;
/* port: a palette entry the scenario has (a map's index; retail's are
all NONE or in range) */
if (VALID_INDEX(scenario_object->palette_entry_index, palette->count) &&
(
!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;
struct tag_block *palette;
struct scenario_object_datum *object;
/* port: a name that names no placement (one past the names, or whose
type has no placements or whose placement is not there: a map's) makes
no object, rather than one of whatever its fields read as */
if (!VALID_INDEX(name_index, scenario->object_names.count) ||
!VALID_INDEX(name->runtime_object_type, NUMBER_OF_OBJECT_TYPES) ||
object_type_definition_get(name->runtime_object_type)->placement_tag_block_offset == NONE ||
object_type_definition_get(name->runtime_object_type)->palette_tag_block_offset == NONE)
{
return NONE;
}
datum = scenario_get_object_type_scenario_datums(
scenario,
name->runtime_object_type,
&placement_tag_block_element_size);
if (!VALID_INDEX(name->runtime_scenario_datum_index, datum->count))
return NONE;
palette = scenario_get_object_type_scenario_palette(
scenario,
name->runtime_object_type);
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;
}
/* port: whether a collection of active garbage may be tried
(GARBAGE_ACTIVE_RETRY_TICKS: in network co-op) */
static boolean objects_garbage_active_due(
void)
{
long now = game_time_get();
if (!network_coop_active())
return TRUE;
if (garbage_active_retry_time != NONE && garbage_active_retry_time - now > GARBAGE_ACTIVE_RETRY_TICKS)
garbage_active_retry_time = NONE;
return garbage_active_retry_time == NONE || now >= garbage_active_retry_time;
}
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) &&
objects_garbage_active_due())
{
garbage_collect_mode = _garbage_collect_active_objects;
}
}
}
if (garbage_collect_mode!=NONE)
{
long garbage_collect_mode_wide;
short garbage_object_count = 0;
short collected_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);
collected_count++;
}
}
}
/* port: in network co-op, active garbage left above its target is
tried again later, and nothing collected moves nothing to compact */
if (garbage_collect_mode == _garbage_collect_active_objects)
garbage_active_retry_time = should_collect ? NONE : game_time_get() + GARBAGE_ACTIVE_RETRY_TICKS;
if (!network_coop_active() || collected_count > 0 ||
garbage_collect_mode != _garbage_collect_active_objects)
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;
}
/* (port: in network co-op as often as the warning, not every
tick: a host kept critical by many enemies wrote it to
debug.txt each tick) */
if ((status_still_critical || garbage_collection_after_first_attempt) &&
(garbage_should_warn || !network_coop_active()))
{
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);
/* port: the attachments attachments_new made (a map's count) */
for (i =0; i<MIN(object_definition->object.attachments.count, MAXIMUM_NUMBER_OF_ATTACHMENTS_PER_OBJECT); ++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);
/* port: only a name the scenario has and the name list holds (a map's
index; the lookup gave NONE for any other, so it came here; retail's
are below 448) */
if (!VALID_INDEX(name_index, MIN(global_scenario_get()->object_names.count, MAXIMUM_OBJECT_NAMES_PER_SCENARIO)))
{
static boolean name_index_reported = FALSE;
if (!name_index_reported)
{
name_index_reported = TRUE;
error(
_error_silent,
"### ERROR %s was placed with name #%d, which the scenario doesn't have; it is left unnamed",
tag_get_name(object->definition_index),
name_index);
}
}
else 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;
}
/* the name is free: deleting the vehicle that held it cleared the slot.
A name something else still holds stays with that object. */
void object_claim_scenario_name(
long object_index,
short name_index)
{
if (object_index == NONE ||
!VALID_INDEX(name_index, MIN(global_scenario_get()->object_names.count, MAXIMUM_OBJECT_NAMES_PER_SCENARIO)) ||
object_name_list[name_index] != NONE)
{
return;
}
object_name_list_new(object_index, name_index);
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;
/* port: no more than the name list holds (a map's count, which a short
counter would never reach; retail has up to 448) */
for (i =0; i<MIN(scenario->object_names.count, MAXIMUM_OBJECT_NAMES_PER_SCENARIO); ++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;
/* port: no more permutations than a region has room for, and the caller's
available_indices holds (a map's count) */
for (i = 0; i<MIN(region->permutations.count, MAXIMUM_PERMUTATIONS_PER_MODEL_REGION); 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;
/* port: no more regions than the object's region_permutations holds (a
map's count; past them are the change colors and the node blocks; retail
has up to 8) */
for (region_index =0; region_index<MIN(model->regions.count, MAXIMUM_REGIONS_PER_OBJECT); 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;
/* port: the regions object_select_random_region_permutations_by_variant
chose for (a map's count) */
for (region_index =0; region_index<MIN(model->regions.count, MAXIMUM_REGIONS_PER_OBJECT)&& !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);
/* port: the attachments attachments_new made (a map's count) */
for (attachment_index =0; attachment_index<MIN(object_definition->object.attachments.count, MAXIMUM_NUMBER_OF_ATTACHMENTS_PER_OBJECT); ++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;
}
/* port: a map's object definition with a bad count, index or type, reported
once (most of these are read every tick) */
static void object_definition_data_error(
long definition_index,
char const *problem)
{
if (model_data_report_once(object_definition_get(definition_index)))
{
error(
_error_silent,
"### ERROR %s has a bad %s",
tag_get_name(definition_index),
problem);
}
return;
}
/* port: a map's model with a bad node list, reported once (the matrices are
built every tick) */
static void object_model_nodes_error(
long model_index,
struct model const *model)
{
if (model_data_report_once(model))
{
error(
_error_silent,
"### ERROR model %s has a bad node list; its bad links are skipped",
tag_get_name(model_index));
}
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;
/* port: no more functions than the object holds values for (a map's count;
past them are the attachments and the node blocks; retail has up to 4) */
short function_count = (short)MIN(object_definition->object.functions.count, NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS);
if (object_definition->object.functions.count>NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS)
{
object_definition_data_error(object->definition_index, "function count; only the first 4 are computed");
}
for (function_index = 0; function_index<function_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;
/* port: no more change colors than the object holds (a map's count;
past them are the node blocks; retail has up to 3) */
short change_color_count = (short)MIN(object_definition->object.change_colors.count, NUMBER_OF_OBJECT_CHANGE_COLORS);
if (object_definition->object.change_colors.count>NUMBER_OF_OBJECT_CHANGE_COLORS)
{
object_definition_data_error(object->definition_index, "change color count; only the first 4 are used");
}
for (cc_index = 0; cc_index<change_color_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;
}