mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-17 19:00:25 +02:00
* wip objects.c work * objects_disconnect_from_structure_bsp * match more functions * more objects work * objects_update work - 93.42% matching * match matrix4x3_transform_point * more objects work - fix duplicate symbol name at 1870800 * match object_choose_random_change_colors * match object_update * objects_garbage_collection work * match object_set_position * more object work * match objects_update * more object work * match object_select_random_region_permutations_by_variant * objects_in_sphere work 99.90% match * object name list calls * match objects_in_sphere * misc matches * match structure_bsp_definitions object * match objects_in_clusters_by_indices * more object work * match `edit_text_handle_key` * mark edit_text as matching * misc changes - match get_next_character - match data section in objects.c * object_export_function_values work * match object_compute_function_values * match object_get_orientation * added functions and adjust formatting * fix profile macros * formatting changes * more formatting * more formatting --------- Co-authored-by: punpckhdq <121350336+punpckhdq@users.noreply.github.com>
4466 lines
118 KiB
C
4466 lines
118 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 "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 "render/render_debug.h"
|
|
#include "saved games/game_state.h"
|
|
#include "scenario/scenario.h"
|
|
#include "scenario/scenario_definitions.h"
|
|
#include "sound/sound_definitions.h"
|
|
#include "sound/game_sound.h"
|
|
#include "structures/structure_bsp_definitions.h"
|
|
#include "units/units.h"
|
|
|
|
/* ---------- constants */
|
|
|
|
|
|
/* ---------- macros */
|
|
|
|
#define MAXIMUM_DUMPS 1024
|
|
|
|
|
|
// This is dangerous, bungie returns the same value regardless of whether the index is valid
|
|
#define OBJECT_INCOMING_FUNCTION_GET_VALUE(object, index) \
|
|
((index)>=NUMBER_OF_INCOMING_OBJECT_FUNCTIONS+1 ? \
|
|
(object)->object.incoming_function_values[(index)-1] : \
|
|
(object)->object.incoming_function_values[(index)-1])
|
|
|
|
/* ---------- structures */
|
|
|
|
struct object_globals
|
|
{
|
|
boolean initial_placement;
|
|
boolean object_marker_initialized;
|
|
boolean force_garbage_collection;
|
|
short active_garbage_object_count;
|
|
long first_garbage_object_index;
|
|
unsigned long last_active_cluster_bits[16];
|
|
unsigned long active_cluster_bits[16];
|
|
long last_garbage_warn_time;
|
|
short pvs_activation_type;
|
|
union
|
|
{
|
|
long object_index;
|
|
short cluster_index;
|
|
} pvs_activation;
|
|
};
|
|
|
|
struct object_memory_release_function
|
|
{
|
|
void(*init_function)(unsigned char *, short);
|
|
boolean(*function)(char *, unsigned char *, unsigned char *, short);
|
|
};
|
|
|
|
struct dump_datum
|
|
{
|
|
long definition_index;
|
|
short object_type;
|
|
short maximum_size;
|
|
long total_size;
|
|
short count;
|
|
short active_count;
|
|
short garbage_count;
|
|
short dead_count;
|
|
short outside_map_count;
|
|
short at_rest_count;
|
|
};
|
|
|
|
/* ---------- prototypes */
|
|
|
|
static void object_connect_lights(long object_index, boolean disconnect, boolean reconnect);
|
|
static void object_name_list_allocate(void);
|
|
static void object_name_list_free(void);
|
|
static void object_name_list_clear(void);
|
|
static void object_name_list_new(long object_index, short name_index);
|
|
static void object_name_list_delete(long object_index);
|
|
static long object_name_list_lookup(short name_index);
|
|
static long recursive_object_adder(
|
|
long object_index,
|
|
boolean(*add_object_function)(long, void *),
|
|
void *custom_data,
|
|
long object_count,
|
|
long maximum_count,
|
|
long *object_indices);
|
|
static long sort_dumps(struct dump_datum const *dump1, struct dump_datum const *dump2);
|
|
static void object_add_to_dump(long object_index, struct dump_datum *dump);
|
|
static void object_dump_write(FILE *stream, struct dump_datum *dump);
|
|
static short object_find_region_permutations_available_with_variant(struct model_region *region, short variant_number, short *available_indices);
|
|
static boolean object_select_random_region_permutations_by_variant(long object_index, struct model *model, short variant_number);
|
|
static short object_determine_variant_number(long object_index, struct model *model);
|
|
static void object_choose_random_region_permutations(long object_index);
|
|
static void object_add_to_list(long *first_object_reference, long object_index);
|
|
static void object_remove_from_list(long *first_object_reference, long object_index);
|
|
static void attachments_delete(long object_index);
|
|
static void object_create_attachments(long object_index);
|
|
static void object_delete_attachments(long object_index);
|
|
static void object_delete_recursive(long object_index, boolean delete_siblings);
|
|
static void object_compute_function_values(long object_index);
|
|
static void object_compute_change_colors(long object_index);
|
|
|
|
/* ---------- globals */
|
|
|
|
static const struct object_memory_release_function object_memory_release_procs[3] =
|
|
{
|
|
{ NULL, ai_release_inactive_swarms },
|
|
{ ai_find_inactive_encounters, ai_release_inactive_encounters },
|
|
{ NULL, NULL }
|
|
};
|
|
static struct object_globals *object_globals;
|
|
static struct memory_pool *object_memory_pool;
|
|
static long *object_name_list;
|
|
|
|
static struct profile_section objects_update_section= {"objects_update", NONE, TRUE};
|
|
|
|
boolean debug_objects_position_velocity= FALSE;
|
|
boolean debug_objects_bounding_spheres= TRUE;
|
|
boolean debug_objects_collision_models= TRUE;
|
|
|
|
/* ---------- 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)
|
|
{
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\objects\\objects.c",
|
|
1049,
|
|
iterator->cluster_partition==&collideable_object_cluster_partition || iterator->cluster_partition==&noncollideable_object_cluster_partition);
|
|
|
|
return cluster_partition_get_next_cluster(iterator->cluster_partition, &iterator->reference_index);
|
|
}
|
|
|
|
void *object_try_and_get_and_verify_type(
|
|
long object_index,
|
|
unsigned long valid_type_flags)
|
|
{
|
|
struct object_header_datum* header= object_header_try_and_get(object_index);
|
|
void *result= NULL;
|
|
|
|
if (header)
|
|
{
|
|
if (TEST_FLAG(valid_type_flags, header->type))
|
|
{
|
|
result= header->datum;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void *object_get_and_verify_type(
|
|
long object_index,
|
|
unsigned long valid_type_flags)
|
|
{
|
|
struct object_datum *result= object_header_get(object_index)->datum;
|
|
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\objects\\objects.c",
|
|
1690,
|
|
TEST_FLAG(valid_type_flags, result->object.type),
|
|
csprintf(
|
|
temporary,
|
|
"got an object type we didn't expect (expected one of 0x%08x but got #%d).",
|
|
valid_type_flags,
|
|
result->object.type));
|
|
|
|
return result;
|
|
}
|
|
|
|
void object_iterator_new(
|
|
struct object_iterator *iterator,
|
|
unsigned long type_flags,
|
|
byte flags)
|
|
{
|
|
data_verify(object_header_data);
|
|
iterator->signature= OBJECT_ITERATOR_SIGNATURE;
|
|
iterator->type_flags= type_flags;
|
|
iterator->flags= flags;
|
|
iterator->absolute_index= 0;
|
|
iterator->index= NONE;
|
|
|
|
return;
|
|
}
|
|
|
|
void *object_iterator_next(
|
|
struct object_iterator *iterator)
|
|
{
|
|
short abs_index;
|
|
struct object_header_datum *header;
|
|
void *result= NULL;
|
|
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\objects\\objects.c",
|
|
1720,
|
|
iterator->signature==OBJECT_ITERATOR_SIGNATURE,
|
|
"uninitialized iterator passed to object_iterator_next()");
|
|
|
|
data_verify(object_header_data);
|
|
|
|
abs_index= iterator->absolute_index;
|
|
|
|
// The operation to get this header is inlined in the original code?
|
|
header= (struct object_header_datum *)((char*)object_header_data->data + sizeof(struct object_header_datum) * abs_index);
|
|
|
|
while (abs_index<object_header_data->count)
|
|
{
|
|
long index= DATUM_INDEX_NEW(abs_index++, header->identifier);
|
|
if (header->identifier)
|
|
{
|
|
if ((iterator->flags&header->flags)==iterator->flags &&
|
|
TEST_FLAG(iterator->type_flags, header->type))
|
|
{
|
|
iterator->index= index;
|
|
result= header->datum;
|
|
break;
|
|
}
|
|
}
|
|
++header;
|
|
}
|
|
iterator->absolute_index= abs_index;
|
|
|
|
return result;
|
|
}
|
|
|
|
long object_get_ultimate_parent(
|
|
long object_index)
|
|
{
|
|
long result= NONE;
|
|
struct object_datum *object;
|
|
for (;object_index!=NONE; object_index= object->object.parent_object_index)
|
|
{
|
|
result= object_index;
|
|
object= object_get(object_index);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void object_add_scenario_permutation(
|
|
long object_index,
|
|
struct scenario_object_permutation *permutation)
|
|
{
|
|
return;
|
|
}
|
|
|
|
void object_set_object_index_for_name_index(
|
|
short name_index,
|
|
long object_index)
|
|
{
|
|
if (VALID_INDEX(name_index, global_scenario_get()->object_names.count))
|
|
{
|
|
object_name_list[name_index]= object_index;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void objects_fix_for_deleted_object(
|
|
long deleted_object_index)
|
|
{
|
|
struct object_datum *object;
|
|
|
|
struct object_iterator iterator;
|
|
object_iterator_new(&iterator, _object_mask_all, 0);
|
|
|
|
for (object= (struct object_datum *)object_iterator_next(&iterator);
|
|
object;
|
|
object= (struct object_datum *)object_iterator_next(&iterator))
|
|
{
|
|
if (object->object.umbrella_shield_object_index==deleted_object_index)
|
|
{
|
|
object->object.umbrella_shield_object_index= NONE;
|
|
}
|
|
object_type_handle_deleted_object(iterator.index, deleted_object_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void object_set_garbage(
|
|
long object_index,
|
|
boolean garbage)
|
|
{
|
|
struct object_datum *garbage_object;
|
|
struct object_datum* object= object_get(object_index);
|
|
|
|
long garbage_object_index;
|
|
for (garbage_object_index= object_globals->first_garbage_object_index;
|
|
garbage_object_index!=NONE;
|
|
garbage_object_index= garbage_object->object.next_garbage_object_index)
|
|
{
|
|
garbage_object= object_get(garbage_object_index);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\objects\\objects.c",
|
|
1952,
|
|
TEST_FLAG(garbage_object->object.flags, _object_garbage_bit));
|
|
}
|
|
|
|
if (garbage)
|
|
{
|
|
if (!TEST_FLAG(object->object.flags, _object_garbage_bit))
|
|
{
|
|
if (!TEST_FLAG(object->object.flags, _object_cannot_be_garbage_bit))
|
|
{
|
|
object->object.next_garbage_object_index= object_globals->first_garbage_object_index;
|
|
object_globals->first_garbage_object_index= object_index;
|
|
SET_FLAG(object->object.flags, _object_garbage_bit, TRUE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (TEST_FLAG(object->object.flags, _object_garbage_bit))
|
|
{
|
|
long *garbage_object_index;
|
|
for (garbage_object_index= &object_globals->first_garbage_object_index;
|
|
*garbage_object_index!=object_index;
|
|
garbage_object_index= &object_get(*garbage_object_index)->object.next_garbage_object_index)
|
|
{
|
|
}
|
|
|
|
*garbage_object_index= object->object.next_garbage_object_index;
|
|
object->object.next_garbage_object_index= NONE;
|
|
SET_FLAG(object->object.flags, _object_garbage_bit, FALSE);
|
|
}
|
|
}
|
|
|
|
for (garbage_object_index= object_globals->first_garbage_object_index;
|
|
garbage_object_index!=NONE;
|
|
garbage_object_index= garbage_object->object.next_garbage_object_index)
|
|
{
|
|
garbage_object= object_get(garbage_object_index);
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\objects\\objects.c",
|
|
2006,
|
|
TEST_FLAG(garbage_object->object.flags, _object_garbage_bit));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void garbage_collect_now(
|
|
void)
|
|
{
|
|
object_globals->force_garbage_collection= TRUE;
|
|
|
|
return;
|
|
}
|
|
|
|
void objects_information_get(
|
|
struct objects_information *information)
|
|
{
|
|
short i;
|
|
struct object_header_datum *header;
|
|
|
|
memset(information, 0, sizeof(*information));
|
|
|
|
header= (struct object_header_datum *)object_header_data->data;
|
|
for (i= 0; i<object_header_data->count; header++)
|
|
{
|
|
if (header->identifier)
|
|
{
|
|
information->object_count++;
|
|
if (TEST_FLAG(header->flags, _object_header_active_bit))
|
|
{
|
|
information->active_object_count++;
|
|
}
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
information->used_memory= 1.f - (real)memory_pool_get_contiguous_free_size(object_memory_pool) / 1048576.f;
|
|
|
|
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 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;
|
|
}
|
|
|
|
// TODO: match
|
|
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 result= 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 j;
|
|
|
|
short offset= (LONG_BITS * i);
|
|
short size= MIN(offset + LONG_BITS, global_structure_bsp_get()->clusters.count);
|
|
|
|
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))
|
|
{
|
|
result= recursive_object_adder(
|
|
k,
|
|
add_object_function,
|
|
custom_data,
|
|
result,
|
|
maximum_object_count,
|
|
object_indices);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
object_marker_end();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
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 type;
|
|
short object_num;
|
|
|
|
short object_count= 0;
|
|
short overflowed_object_count= 0;
|
|
|
|
memset(dumps, 0, sizeof(dumps));
|
|
memset(dumps_by_type, 0, sizeof(dumps_by_type));
|
|
|
|
for (type= 0; type<NUMBER_OF_OBJECT_TYPES; ++type)
|
|
{
|
|
dumps_by_type[type].object_type= type;
|
|
dumps_by_type[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<object_count; ++object_num)
|
|
{
|
|
if (dumps[object_num].definition_index==object->definition_index)
|
|
{
|
|
index= object_num;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (index==NONE)
|
|
{
|
|
if (object_count<(short)NUMBEROF(dumps))
|
|
{
|
|
index= object_count++;
|
|
dumps[index].object_type= NONE;
|
|
dumps[index].definition_index= object->definition_index;
|
|
}
|
|
else
|
|
{
|
|
++overflowed_object_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,
|
|
object_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 (type= 0; type<NUMBER_OF_OBJECT_TYPES; type++)
|
|
{
|
|
object_dump_write(file, &dumps_by_type[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<object_count; object_num++)
|
|
{
|
|
object_dump_write(file, &dumps[object_num]);
|
|
}
|
|
|
|
fprintf(file, "\n");
|
|
|
|
if (overflowed_object_count>0)
|
|
{
|
|
fprintf(
|
|
file,
|
|
"WARNING: overflowed MAXIMUM_DUMPS (%d), this dump does not include %d objects that would not fit!\n",
|
|
MAXIMUM_DUMPS,
|
|
overflowed_object_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
|
|
{
|
|
object_header_data= data_new("object", MAXIMUM_OBJECTS_PER_MAP*5, 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)
|
|
{
|
|
return (real_matrix4x3 *)object_header_block_get(object_index, &object_get(object_index)->object.node_matrices);
|
|
}
|
|
|
|
char const *object_get_attachment_marker_name(
|
|
long object_index,
|
|
short attachment_index)
|
|
{
|
|
const char *result;
|
|
|
|
struct object_datum *object= object_get(object_index);
|
|
struct object_definition *object_definition= object_definition_get(object->definition_index);
|
|
|
|
if (VALID_INDEX(attachment_index, object_definition->object.attachments.count))
|
|
{
|
|
result= TAG_BLOCK_GET_ELEMENT(
|
|
&object_definition->object.attachments,
|
|
attachment_index,
|
|
struct object_attachment_definition)->marker_name;
|
|
}
|
|
else
|
|
{
|
|
result= NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean object_has_node(
|
|
long object_index,
|
|
short node_index)
|
|
{
|
|
struct object_datum *object= object_get(object_index);
|
|
long model_index= object_definition_get(object->definition_index)->object.model.index;
|
|
boolean result= FALSE;
|
|
|
|
if (model_index==NONE)
|
|
{
|
|
if (!node_index)
|
|
{
|
|
result= TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
struct model *model= model_definition_get(model_index);
|
|
if (VALID_INDEX(node_index, model->nodes.count))
|
|
{
|
|
result= TRUE;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void object_set_automatic_deactivation(
|
|
long object_index,
|
|
boolean automatic_deactivation)
|
|
{
|
|
struct object_header_datum *header= object_header_get(object_index);
|
|
struct object_datum *object= object_get(object_index);
|
|
|
|
SET_FLAG(header->flags, _object_header_automatically_deactivate_bit, automatic_deactivation);
|
|
|
|
if (automatic_deactivation)
|
|
{
|
|
if (object->object.parent_object_index==NONE && object->object.location.cluster_index==NONE)
|
|
{
|
|
object_deactivate(object_index);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!TEST_FLAG(header->flags, _object_header_active_bit))
|
|
{
|
|
object_activate(object_index);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void object_set_visibility(
|
|
long object_index,
|
|
boolean visible_flag)
|
|
{
|
|
struct object_datum *object= object_get(object_index);
|
|
struct object_definition *object_definition= object_definition_get(object->definition_index);
|
|
|
|
if (object_definition->object.model.index!=NONE)
|
|
{
|
|
if (TEST_FLAG(object->object.flags, _object_invisible_bit) && visible_flag)
|
|
{
|
|
object_connect_lights(object_index, FALSE, TRUE);
|
|
}
|
|
else if (!TEST_FLAG(object->object.flags, _object_invisible_bit) && !visible_flag)
|
|
{
|
|
object_connect_lights(object_index, TRUE, FALSE);
|
|
}
|
|
}
|
|
|
|
if (!visible_flag || object_definition->object.model.index!=NONE)
|
|
{
|
|
struct object_header_datum *header= object_header_get(object_index);
|
|
|
|
SET_FLAG(object->object.flags, _object_invisible_bit, !visible_flag);
|
|
SET_FLAG(header->flags, _object_header_visible_bit, visible_flag);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void object_get_velocities(
|
|
long object_index,
|
|
real_vector3d *translational_velocity,
|
|
real_vector3d *angular_velocity)
|
|
{
|
|
struct object_datum *ultimate_parent= object_get(object_index);
|
|
|
|
while (ultimate_parent->object.parent_object_index!=NONE)
|
|
{
|
|
ultimate_parent= object_get(ultimate_parent->object.parent_object_index);
|
|
}
|
|
|
|
if (translational_velocity)
|
|
{
|
|
*translational_velocity= ultimate_parent->object.translational_velocity;
|
|
}
|
|
|
|
if (angular_velocity)
|
|
{
|
|
*angular_velocity= ultimate_parent->object.angular_velocity;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
struct location *object_get_location(
|
|
long object_index,
|
|
struct location *location)
|
|
{
|
|
*location= object_get(object_get_ultimate_parent(object_index))->object.location;
|
|
|
|
return location;
|
|
}
|
|
|
|
void object_start_interpolation(
|
|
long object_index,
|
|
short frame_count)
|
|
{
|
|
struct object_datum *object= object_get(object_index);
|
|
struct object_definition *object_definition= object_definition_get(object->definition_index);
|
|
short node_count= model_definition_get(object_definition->object.model.index)->nodes.count;
|
|
|
|
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1523, !TEST_FLAG(_object_mask_cannot_interpolate, object->object.type));
|
|
|
|
memcpy(
|
|
object_header_block_get(object_index, &object->object.original_node_orientations),
|
|
object_header_block_get(object_index, &object->object.node_orientations),
|
|
sizeof(real_orientation)*node_count);
|
|
if (frame_count>=
|
|
object->object.animation.interpolation_frame_count-
|
|
object->object.animation.interpolation_frame_index
|
|
)
|
|
{
|
|
object->object.animation.interpolation_frame_index= 0;
|
|
object->object.animation.interpolation_frame_count= frame_count;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void object_offset_interpolation(
|
|
long object_index,
|
|
real_vector3d *offset)
|
|
{
|
|
struct object_datum *object= object_get(object_index);
|
|
|
|
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1546, !TEST_FLAG(_object_mask_cannot_interpolate, object->object.type));
|
|
|
|
if (object->object.animation.interpolation_frame_count)
|
|
{
|
|
real_orientation* orientation= (real_orientation *)object_header_block_get(
|
|
object_index,
|
|
&object->object.original_node_orientations);
|
|
orientation->translation.x+= offset->i;
|
|
orientation->translation.y+= offset->j;
|
|
orientation->translation.z+= offset->k;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void object_permute_region(
|
|
long object_index,
|
|
char *permutation_name,
|
|
short desired_region_index,
|
|
boolean active_flag)
|
|
{
|
|
struct object_datum *object= object_get(object_index);
|
|
struct object_definition *object_definition= object_definition_get(object->definition_index);
|
|
|
|
if (object_definition->object.model.index!=NONE)
|
|
{
|
|
short region_index;
|
|
struct model* model= model_definition_get(object_definition->object.model.index);
|
|
|
|
for (region_index= 0; region_index<model->regions.count; region_index++)
|
|
{
|
|
if (desired_region_index==NONE || desired_region_index==region_index)
|
|
{
|
|
short permutation_index;
|
|
struct model_region *region= TAG_BLOCK_GET_ELEMENT(
|
|
&model->regions,
|
|
region_index,
|
|
struct model_region);
|
|
|
|
for (permutation_index= 0; permutation_index<region->permutations.count; permutation_index++)
|
|
{
|
|
struct model_region_permutation *permutation= TAG_BLOCK_GET_ELEMENT(
|
|
®ion->permutations,
|
|
permutation_index,
|
|
struct model_region_permutation);
|
|
|
|
if (!_stricmp(permutation->name, permutation_name))
|
|
{
|
|
object->object.region_permutations[region_index]= active_flag ? permutation_index : 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean object_get_function_value(
|
|
long object_index,
|
|
short function_index,
|
|
real *value_reference)
|
|
{
|
|
boolean result;
|
|
struct object_datum *object= object_get(object_index);
|
|
|
|
|
|
if (function_index==NONE)
|
|
{
|
|
*value_reference= 1.f;
|
|
result= TRUE;
|
|
}
|
|
else
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1654, function_index>=0 && function_index<NUMBER_OF_OUTGOING_OBJECT_FUNCTIONS);
|
|
|
|
*value_reference= object->object.outgoing_function_values[function_index];
|
|
|
|
result= TEST_FLAG(object->object.functions_active_flags, function_index);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
short objects_in_clusters_by_indices(
|
|
unsigned long class_flags,
|
|
short cluster_count,
|
|
short const *cluster_indices,
|
|
short maximum_object_count,
|
|
long *object_indices)
|
|
{
|
|
short cluster_index;
|
|
|
|
short object_count= 0;
|
|
|
|
if (!class_flags)
|
|
{
|
|
class_flags= NONE;
|
|
}
|
|
|
|
object_marker_begin();
|
|
|
|
for (cluster_index= 0; cluster_index<cluster_count; ++cluster_index)
|
|
{
|
|
short cluster_num= cluster_indices[cluster_index];
|
|
if (TEST_FLAG(class_flags, 0))
|
|
{
|
|
long reference;
|
|
long object_index;
|
|
|
|
for (object_index= cluster_get_first_collideable_object(&reference, cluster_num);
|
|
object_index!=NONE;
|
|
object_index= cluster_get_next_collideable_object(&reference))
|
|
{
|
|
if (object_mark_function(object_index))
|
|
{
|
|
if (object_count>=maximum_object_count)
|
|
{
|
|
object_marker_end();
|
|
return object_count;
|
|
}
|
|
object_indices[object_count++]= object_index;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (TEST_FLAG(class_flags, 1))
|
|
{
|
|
long reference;
|
|
long object_index;
|
|
|
|
for (object_index= cluster_get_first_noncollideable_object(&reference, cluster_num);
|
|
object_index!=NONE;
|
|
object_index= cluster_get_next_noncollideable_object(&reference))
|
|
{
|
|
if (object_mark_function(object_index))
|
|
{
|
|
if (object_count>=maximum_object_count)
|
|
{
|
|
object_marker_end();
|
|
return object_count;
|
|
}
|
|
object_indices[object_count++]= object_index;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
object_marker_end();
|
|
|
|
return object_count;
|
|
}
|
|
|
|
long object_index_from_name_index(
|
|
short name_index)
|
|
{
|
|
return name_index>=0 && name_index<MAXIMUM_OBJECT_NAMES_PER_SCENARIO ? object_name_list[name_index] : NONE;
|
|
}
|
|
|
|
void objects_disconnect_from_structure_bsp(
|
|
void)
|
|
{
|
|
struct object_datum *object;
|
|
struct object_iterator iterator;
|
|
|
|
object_iterator_new(&iterator, _object_mask_all, 0);
|
|
while (object= (struct object_datum *)object_iterator_next(&iterator))
|
|
{
|
|
if (TEST_FLAG(object->object.flags, _object_connected_to_map_bit) &&
|
|
object->object.parent_object_index==NONE)
|
|
{
|
|
object_disconnect_from_map(iterator.index);
|
|
|
|
SET_FLAG(object->object.flags, _object_connected_to_map_bit, TRUE);
|
|
}
|
|
|
|
object_type_disconnect_from_structure_bsp(iterator.index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean object_visible_to_any_player(
|
|
long object_index)
|
|
{
|
|
struct object_header_datum *header= object_header_get(object_index);
|
|
struct object_datum *object= object_get(object_index);
|
|
boolean result= 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* combined_pvs= players_get_combined_pvs();
|
|
|
|
for (cluster_index= object_get_first_cluster(&iterator, object_index);
|
|
cluster_index!=NONE && !BIT_VECTOR_TEST_FLAG(combined_pvs, cluster_index);
|
|
cluster_index= object_get_next_cluster(&iterator))
|
|
{
|
|
;
|
|
}
|
|
|
|
if (cluster_index!=NONE)
|
|
{
|
|
long player_index;
|
|
const real bounding_area= 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_point3d position;
|
|
unit_get_head_position(player->unit_index, &position);
|
|
if (distance_squared3d(&position, &object->object.bounding_sphere_center)<bounding_area)
|
|
{
|
|
result= TRUE;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
real_vector3d head_to_bounding_sphere_vector;
|
|
if (
|
|
dot_product3d(&unit_get(player->unit_index)->unit.desired_aiming_vector, &head_to_bounding_sphere_vector)>
|
|
cosine(
|
|
(real)atan2(
|
|
object->object.bounding_sphere_radius,
|
|
normalize3d(
|
|
vector_from_points3d(
|
|
&position,
|
|
&object->object.bounding_sphere_center,
|
|
&head_to_bounding_sphere_vector)
|
|
)
|
|
) + 0.7853982f
|
|
)
|
|
)
|
|
{
|
|
result= TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
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)
|
|
{
|
|
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
|
|
{
|
|
if (!location)
|
|
{
|
|
struct location bounding_sphere_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));
|
|
|
|
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)
|
|
{
|
|
struct object_datum *object= object_get(object_index);
|
|
struct object_definition *object_definition= object_definition_get(object->definition_index);
|
|
|
|
short marker= model_get_marker_by_name(
|
|
object_definition->object.model.index,
|
|
name,
|
|
object->object.region_permutations,
|
|
FALSE,
|
|
NONE,
|
|
object_get_node_matrices(object_index),
|
|
TEST_FLAG(object->object.flags, _object_mirrored_bit),
|
|
markers,
|
|
maximum_marker_count);
|
|
|
|
if (!marker)
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1113, maximum_marker_count>0);
|
|
|
|
markers->node_index= 0;
|
|
matrix4x3_identity(&markers->node_matrix);
|
|
markers->matrix= *object_get_node_matrix(object_index, 0);
|
|
|
|
if (TEST_FLAG(object->object.flags, _object_mirrored_bit))
|
|
{
|
|
negate_vector3d(&markers->matrix.left, &markers->matrix.left);
|
|
}
|
|
|
|
if (name && name[0]=='\0')
|
|
{
|
|
marker= 1;
|
|
}
|
|
}
|
|
|
|
return marker;
|
|
}
|
|
|
|
// TODO: match
|
|
void object_align_marker_to_matrix(
|
|
struct object_datum *object,
|
|
struct object_marker const *child_marker,
|
|
real_matrix4x3 const *destination_matrix)
|
|
{
|
|
real_matrix4x3 marker_transform_matrix;
|
|
real_matrix4x3 final_matrix;
|
|
|
|
real_vector3d vector;
|
|
|
|
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1173, object);
|
|
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1174, child_marker);
|
|
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1175, destination_matrix);
|
|
|
|
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1177, valid_real_matrix4x3(destination_matrix));
|
|
|
|
matrix4x3_from_point_and_vectors(&final_matrix, &object->object.position, &object->object.forward, &object->object.up);
|
|
|
|
matrix4x3_inverse(&final_matrix, &marker_transform_matrix);
|
|
matrix4x3_multiply(&marker_transform_matrix, &child_marker->matrix, &marker_transform_matrix);
|
|
matrix4x3_inverse(&marker_transform_matrix, &marker_transform_matrix);
|
|
matrix4x3_multiply(destination_matrix, &marker_transform_matrix, &final_matrix);
|
|
|
|
object->object.position= final_matrix.position;
|
|
object->object.forward= final_matrix.forward;
|
|
|
|
cross_product3d(&final_matrix.forward, &final_matrix.up, &vector);
|
|
cross_product3d(&vector, &final_matrix.forward, &object->object.up);
|
|
|
|
normalize3d(&object->object.forward);
|
|
normalize3d(&object->object.up);
|
|
|
|
return;
|
|
}
|
|
|
|
void object_detach(
|
|
long child_object_index)
|
|
{
|
|
real_matrix4x3 object_translation_matrix;
|
|
real_matrix4x3 matrix;
|
|
real_matrix4x3 object_rotation_matrix;
|
|
|
|
real_matrix4x3 *node_matrix;
|
|
|
|
struct object_datum *child_object= object_get(child_object_index);
|
|
struct object_datum *parent_object= object_get(child_object->object.parent_object_index);
|
|
|
|
object_disconnect_from_map(child_object_index);
|
|
node_matrix= object_get_node_matrix(child_object->object.parent_object_index, child_object->object.parent_node_index);
|
|
|
|
matrix4x3_translation(&object_translation_matrix, &child_object->object.position);
|
|
matrix4x3_rotation_from_vectors(&object_rotation_matrix, &child_object->object.forward, &child_object->object.up);
|
|
matrix4x3_multiply(node_matrix, &object_translation_matrix, &matrix);
|
|
matrix4x3_multiply(&matrix, &object_rotation_matrix, &matrix);
|
|
matrix4x3_to_point_and_vectors(
|
|
&matrix,
|
|
&child_object->object.position,
|
|
&child_object->object.forward,
|
|
&child_object->object.up);
|
|
|
|
child_object->object.translational_velocity= parent_object->object.translational_velocity;
|
|
child_object->object.angular_velocity= parent_object->object.angular_velocity;
|
|
child_object->object.parent_node_index= NONE;
|
|
child_object->object.parent_object_index= NONE;
|
|
|
|
object_reconnect_to_map(child_object_index, NULL);
|
|
object_activate(child_object_index);
|
|
|
|
return;
|
|
}
|
|
|
|
real_point3d *object_get_origin(
|
|
long object_index,
|
|
real_point3d *origin)
|
|
{
|
|
struct object_datum *object= object_get(object_index);
|
|
|
|
if (object->object.parent_object_index==NONE)
|
|
{
|
|
*origin= object->object.position;
|
|
}
|
|
else
|
|
{
|
|
real_matrix4x3 *matrix= object_get_node_matrix(object->object.parent_object_index, object->object.parent_node_index);
|
|
matrix4x3_transform_point(matrix, &object->object.position, origin);
|
|
}
|
|
|
|
return origin;
|
|
}
|
|
|
|
void object_get_orientation(
|
|
long object_index,
|
|
real_vector3d *forward,
|
|
real_vector3d *up)
|
|
{
|
|
struct object_datum *object= object_get(object_index);
|
|
|
|
if (object->object.parent_object_index==NONE)
|
|
{
|
|
if (forward)
|
|
{
|
|
*forward= object->object.forward;
|
|
}
|
|
if (up)
|
|
{
|
|
*up= object->object.up;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
real_matrix4x3 *node_matrix= object_get_node_matrix(
|
|
object->object.parent_object_index,
|
|
object->object.parent_node_index);
|
|
|
|
if (forward)
|
|
{
|
|
matrix4x3_transform_normal(node_matrix, &object->object.forward, forward);
|
|
}
|
|
if (up)
|
|
{
|
|
matrix4x3_transform_normal(node_matrix, &object->object.up, up);
|
|
}
|
|
}
|
|
|
|
if (forward && up)
|
|
{
|
|
match_assert_valid_real_vector3d_axes2("c:\\halo\\SOURCE\\objects\\objects.c", 1462, forward, up);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
real_matrix4x3 *object_get_world_matrix(
|
|
long object_index,
|
|
real_matrix4x3 *matrix)
|
|
{
|
|
struct object_datum *object= object_get(object_index);
|
|
matrix4x3_from_point_and_vectors(matrix, &object->object.position, &object->object.forward, &object->object.up);
|
|
|
|
if (object->object.parent_object_index!=NONE)
|
|
{
|
|
real_matrix4x3* node_matrix= object_get_node_matrix(object->object.parent_object_index, object->object.parent_node_index);
|
|
matrix4x3_multiply(node_matrix, matrix, matrix);
|
|
}
|
|
|
|
return matrix;
|
|
}
|
|
|
|
void object_inverse_kinematics(
|
|
long hand_object_index,
|
|
char *hand_marker_name,
|
|
long grip_object_index,
|
|
char *grip_marker_name,
|
|
real_matrix4x3 *node_matrices)
|
|
{
|
|
struct object_marker hand_marker;
|
|
struct object_marker grip_marker;
|
|
real_matrix4x3 desired_hand_matrix;
|
|
|
|
struct object_datum *hand_object= object_get(hand_object_index);
|
|
struct object_definition *hand_object_definition= object_definition_get(hand_object->definition_index);
|
|
struct model *hand_model= model_definition_get(hand_object_definition->object.model.index);
|
|
|
|
if (object_get_marker_by_name(hand_object_index, hand_marker_name, &hand_marker, 1) &&
|
|
object_get_marker_by_name(grip_object_index, grip_marker_name, &grip_marker, 1))
|
|
{
|
|
short hand_node_index= hand_marker.node_index;
|
|
short hand_parent_node_index= TAG_BLOCK_GET_ELEMENT(&hand_model->nodes, hand_node_index, struct model_node)->parent_node_index;
|
|
short desired_hand_index= NONE;
|
|
|
|
if (hand_parent_node_index!=NONE)
|
|
{
|
|
desired_hand_index= TAG_BLOCK_GET_ELEMENT(&hand_model->nodes, hand_parent_node_index, struct model_node)->parent_node_index;
|
|
}
|
|
|
|
if (desired_hand_index!=NONE)
|
|
{
|
|
matrix4x3_inverse(&hand_marker.node_matrix, &desired_hand_matrix);
|
|
matrix4x3_multiply(&grip_marker.matrix, &desired_hand_matrix, &desired_hand_matrix);
|
|
inverse_kinematics_adjust_matrices(
|
|
&desired_hand_matrix,
|
|
&node_matrices[desired_hand_index],
|
|
&node_matrices[hand_parent_node_index],
|
|
&node_matrices[hand_node_index]);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
short objects_in_sphere(
|
|
unsigned long class_flags,
|
|
unsigned long type_flags,
|
|
struct location const *location,
|
|
real_point3d const *center,
|
|
real radius,
|
|
long *object_indices,
|
|
short maximum_count)
|
|
{
|
|
long temporary_object_indices[MAXIMUM_OBJECTS_PER_MAP];
|
|
short cluster_indices[MAXIMUM_CLUSTERS_PER_STRUCTURE];
|
|
|
|
short cluster_count;
|
|
short object_count_in_clusters;
|
|
short object_index;
|
|
|
|
short result= 0;
|
|
|
|
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1779, location);
|
|
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1780, center);
|
|
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 1781, object_indices);
|
|
|
|
if (!type_flags)
|
|
{
|
|
type_flags= NONE;
|
|
}
|
|
|
|
cluster_count= structure_clusters_in_sphere(
|
|
location->cluster_index,
|
|
center,
|
|
radius,
|
|
NUMBEROF(cluster_indices),
|
|
cluster_indices);
|
|
object_count_in_clusters= objects_in_clusters_by_indices(
|
|
class_flags,
|
|
cluster_count,
|
|
cluster_indices,
|
|
NUMBEROF(temporary_object_indices),
|
|
temporary_object_indices);
|
|
for (object_index= 0; object_index<object_count_in_clusters && result<maximum_count; ++object_index)
|
|
{
|
|
struct object_datum *object= object_get(temporary_object_indices[object_index]);
|
|
if (TEST_FLAG(type_flags, object->object.type) &&
|
|
point_in_sphere(center, &object->object.bounding_sphere_center, (object->object.bounding_sphere_radius)+radius))
|
|
{
|
|
object_indices[result++]= temporary_object_indices[object_index];
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void objects_reconnect_to_structure_bsp(
|
|
void)
|
|
{
|
|
struct object_datum *object;
|
|
|
|
struct object_iterator iterator;
|
|
|
|
object_iterator_new(&iterator, _object_mask_all, 0);
|
|
|
|
while(object= (struct object_datum *)object_iterator_next(&iterator))
|
|
{
|
|
if (TEST_FLAG(object->object.flags, _object_connected_to_map_bit) &&
|
|
object->object.parent_object_index==NONE)
|
|
{
|
|
struct location scenario_location;
|
|
|
|
SET_FLAG(object->object.flags, _object_connected_to_map_bit, FALSE);
|
|
|
|
object->object.location.cluster_index= NONE;
|
|
object_header_get(iterator.index)->cluster_index= NONE;
|
|
|
|
scenario_location_from_point(&scenario_location, &object->object.bounding_sphere_center);
|
|
if (scenario_location.cluster_index==NONE)
|
|
{
|
|
struct collision_bsp_test_sphere_result result;
|
|
collision_bsp_test_sphere(
|
|
global_collision_bsp_get(),
|
|
0,
|
|
NULL,
|
|
&object->object.bounding_sphere_center,
|
|
object->object.bounding_sphere_radius,
|
|
&result);
|
|
|
|
if (result.leaf_count)
|
|
{
|
|
scenario_location.leaf_index= result.leaf_indices[0];
|
|
if (result.leaf_indices[0] == -1)
|
|
{
|
|
scenario_location.cluster_index= -1;
|
|
}
|
|
else
|
|
{
|
|
scenario_location.cluster_index= TAG_BLOCK_GET_ELEMENT(
|
|
&global_structure_bsp_get()->leaves,
|
|
result.leaf_indices[0] & LONG_MAX,
|
|
struct structure_leaf)->cluster_index;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
scenario_location_from_point(&scenario_location, &object->object.position);
|
|
}
|
|
}
|
|
object_reconnect_to_map(iterator.index, &scenario_location);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void objects_paparazzi(
|
|
void)
|
|
{
|
|
struct object_datum *object;
|
|
struct object_iterator iterator;
|
|
|
|
object_iterator_new(&iterator, _object_mask_all, 0);
|
|
|
|
while (object= (struct object_datum *)object_iterator_next(&iterator))
|
|
{
|
|
if (TEST_FLAG(object->object.flags, _object_movie_star_bit))
|
|
{
|
|
object_delete_initial_recursive(iterator.index, 0);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void object_export_function_values(
|
|
long object_index)
|
|
{
|
|
short i;
|
|
|
|
struct object_datum *object= object_get(object_index);
|
|
struct object_definition *object_definition= object_definition_get(object->definition_index);
|
|
|
|
for (i= 0; i<NUMBEROF(object->object.incoming_function_values); ++i)
|
|
{
|
|
if (object_definition->object.function_modes[i])
|
|
{
|
|
short region_index;
|
|
real_matrix4x3 *node_matrix;
|
|
|
|
real value= 0.f;
|
|
|
|
switch (object_definition->object.function_modes[i])
|
|
{
|
|
case _object_function_recent_body_damage:
|
|
value= object->object.current_body_damage;
|
|
break;
|
|
case _object_function_recent_shield_damage:
|
|
value= object->object.current_shield_damage;
|
|
break;
|
|
case _object_function_body_vitality:
|
|
value= object->object.body_vitality;
|
|
break;
|
|
case _object_function_shield_vitality:
|
|
value= object->object.shield_vitality;
|
|
value= MIN(value, 1.f);
|
|
break;
|
|
|
|
case _object_function_random_constant:
|
|
if (object->object.incoming_function_values[i]==1.f)
|
|
{
|
|
value= real_random();
|
|
}
|
|
break;
|
|
case _object_function_alive:
|
|
value= TEST_FLAG(object->object.damage_flags, _object_dead_bit) ? 0.f : 1.f;
|
|
break;
|
|
case _object_function_compass:
|
|
node_matrix= object_get_node_matrix(object_index, 0);
|
|
if (fabs(node_matrix->forward.k) < 0.995f)
|
|
{
|
|
value= signed_angular_difference(global_scenario_get()->local_north, arctangent(node_matrix->forward.i, node_matrix->forward.j));
|
|
value= (0.15915494f * value) + 0.5f;
|
|
value= PIN(value, 0.f, 1.f);
|
|
}
|
|
else
|
|
{
|
|
value= object->object.incoming_function_values[i];
|
|
}
|
|
break;
|
|
default:
|
|
region_index= object_definition->object.function_modes[i]-_object_function_first_region_damage;
|
|
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 2630, region_index>=0 && region_index<MAXIMUM_REGIONS_PER_OBJECT);
|
|
value= object->object.region_damage[region_index] / 255.f;
|
|
break;
|
|
}
|
|
object->object.incoming_function_values[i]= value;
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
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 node_matrix;
|
|
real_matrix4x3 object_translation_matrix;
|
|
real_matrix4x3 object_rotation_matrix;
|
|
real_matrix4x3 center_matrix;
|
|
real_point3d center;
|
|
real_matrix4x3 offset_matrix;
|
|
real_matrix4x3 parent_node_matrix_no_scale_no_mirror;
|
|
|
|
struct object_datum *object= object_get(object_index);
|
|
struct object_definition *object_definition= object_definition_get(object->definition_index);
|
|
|
|
real_matrix4x3 *object_nodes= (real_matrix4x3 *)object_header_block_get(object_index, &object->object.node_matrices);
|
|
real_orientation *node_orientations=
|
|
TEST_FLAG(_object_mask_cannot_interpolate, object->object.type) ?
|
|
cannot_interpolate_node_orientations_storage :
|
|
(real_orientation *)object_header_block_get(object_index, &object->object.node_orientations);
|
|
|
|
if (object_definition->object.model.index!=NONE)
|
|
{
|
|
struct model *model;
|
|
real_matrix4x3 *object_node_matrix;
|
|
boolean world_relative;
|
|
|
|
object_type_definition_get(object->object.type);
|
|
|
|
model= model_definition_get(object_definition->object.model.index);
|
|
object_node_matrix=
|
|
object->object.parent_object_index!=NONE ?
|
|
object_get_node_matrix(object->object.parent_object_index, object->object.parent_node_index) :
|
|
0;
|
|
|
|
world_relative= FALSE;
|
|
if (object->object.animation.animation_graph_index!=NONE &&
|
|
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->object.animation.state.frame_index;
|
|
}
|
|
else
|
|
{
|
|
frame_index= (object_index + game_time_get()) % animation->frame_count;
|
|
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 2704, frame_index>=0)
|
|
}
|
|
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);
|
|
}
|
|
|
|
if (object_definition->object.animation_graph.index!=NONE)
|
|
{
|
|
struct animation_graph *animation_graph= animation_graph_definition_get(
|
|
object->object.animation.animation_graph_index);
|
|
}
|
|
|
|
// Scale the object orientations
|
|
if (object->object.scale>0.f)
|
|
{
|
|
node_orientations->scale*= object->object.scale;
|
|
node_orientations->translation.x*= object->object.scale;
|
|
node_orientations->translation.y*= object->object.scale;
|
|
node_orientations->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);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
object_nodes[0].scale= 1.f;
|
|
object_nodes[0].forward= object->object.forward;
|
|
object_nodes[0].up= object->object.up;
|
|
cross_product3d(&object_nodes[0].up, &object_nodes[0].forward, &object_nodes[0].left);
|
|
object_nodes[0].position= object->object.position;
|
|
}
|
|
|
|
matrix4x3_transform_point(
|
|
object_nodes,
|
|
&object_definition->object.bounding_offset,
|
|
&object->object.bounding_sphere_center);
|
|
object->object.bounding_sphere_radius= object_definition->object.bounding_radius;
|
|
if (object->object.scale > 0.f)
|
|
{
|
|
object->object.bounding_sphere_radius*= object->object.scale;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void object_postprocess_node_matrices(
|
|
long object_index)
|
|
{
|
|
struct object_datum *object= object_get(object_index);
|
|
struct object_definition *object_definition= object_definition_get(object->definition_index);
|
|
|
|
if (object_definition->object.model.index!=NONE &&
|
|
object_definition->object.animation_graph.index != NONE)
|
|
{
|
|
real_matrix4x3 *matrices= (real_matrix4x3 *)object_header_block_get(
|
|
object_index,
|
|
&object->object.node_matrices);
|
|
object_type_postprocess_node_matrices(object_index, matrices);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void object_choose_random_change_colors(
|
|
long object_index,
|
|
real_rgb_color *placement_change_colors)
|
|
{
|
|
short cc_index;
|
|
|
|
struct object_datum *object= object_get(object_index);
|
|
struct object_definition *object_definition= object_definition_get(object->definition_index);
|
|
|
|
for (cc_index=0; cc_index<NUMBEROF(object->object.base_change_colors); cc_index++)
|
|
{
|
|
object->object.base_change_colors[cc_index]= placement_change_colors[cc_index];
|
|
|
|
if (cc_index<object_definition->object.change_colors.count)
|
|
{
|
|
short permutation_index;
|
|
|
|
struct object_change_color_definition *cc= TAG_BLOCK_GET_ELEMENT(
|
|
&object_definition->object.change_colors,
|
|
cc_index,
|
|
struct object_change_color_definition);
|
|
real weight= fmod(
|
|
fabs(
|
|
object->object.position.x*315.89313f +
|
|
object->object.position.y*587.12946f +
|
|
object->object.position.z*744.12415f +
|
|
(real)cc_index*431.12894f
|
|
),
|
|
1.f);
|
|
|
|
for (permutation_index=0; permutation_index<cc->permutations.count; permutation_index++)
|
|
{
|
|
struct object_change_color_permutation *permutation= TAG_BLOCK_GET_ELEMENT(
|
|
&cc->permutations,
|
|
permutation_index,
|
|
struct object_change_color_permutation);
|
|
if (weight<=permutation->weight)
|
|
{
|
|
rgb_colors_interpolate(
|
|
&object->object.base_change_colors[cc_index],
|
|
1,
|
|
&permutation->color_lower_bound,
|
|
&permutation->color_upper_bound,
|
|
fmod(fabs(object->object.position.y) + cc_index*0.71211f, 1.f));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
object->object.outgoing_change_colors[cc_index].red= PIN(object->object.base_change_colors[cc_index].red, 0.f, 1.f);
|
|
object->object.outgoing_change_colors[cc_index].green= PIN(object->object.base_change_colors[cc_index].green, 0.f, 1.f);
|
|
object->object.outgoing_change_colors[cc_index].blue= PIN(object->object.base_change_colors[cc_index].blue, 0.f, 1.f);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void objects_scripting_detach(
|
|
long parent_object_index,
|
|
long child_object_index)
|
|
{
|
|
if (parent_object_index!=NONE &&
|
|
child_object_index!=NONE &&
|
|
object_get(child_object_index)->object.parent_object_index == parent_object_index)
|
|
{
|
|
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)
|
|
{
|
|
long i;
|
|
|
|
struct object_datum *object= object_get(object_index);
|
|
struct object_definition *object_definition= object_definition_get(object->definition_index);
|
|
|
|
for (i= 0; i<object_definition->object.attachments.count; i++)
|
|
{
|
|
long attachment_index;
|
|
|
|
struct object_attachment_definition* attachment= TAG_BLOCK_GET_ELEMENT(
|
|
&object_definition->object.attachments,
|
|
i,
|
|
struct object_attachment_definition);
|
|
|
|
short attachment_type= 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,
|
|
i,
|
|
attachment->primary_scale_function_reference-1,
|
|
attachment->change_color_reference-1);
|
|
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);
|
|
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, i);
|
|
break;
|
|
case _object_attachment_type_particle_system:
|
|
attachment_index= particle_system_new_attached(attachment->type.index, object_index, i);
|
|
break;
|
|
}
|
|
|
|
object->object.attachment_types[i]= attachment_type;
|
|
object->object.attachment_indices[i]= 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)
|
|
{
|
|
definition_index= game_engine_remap_object_definition(definition_index);
|
|
}
|
|
|
|
if (definition_index!=NONE)
|
|
{
|
|
struct object_definition *object_definition= object_definition_get(definition_index);
|
|
struct object_type_definition *type_definition= object_type_definition_get(object_definition->object.type);
|
|
|
|
object_index= object_header_new(object_header_data, NONE, type_definition->game_datum_size);
|
|
|
|
if (object_index!=NONE)
|
|
{
|
|
short node_count;
|
|
|
|
struct object_header_datum *header= object_header_get(object_index);
|
|
struct object_datum *object= object_get(object_index);
|
|
boolean can_create_object= TRUE;
|
|
|
|
SET_FLAG(header->flags, _object_header_being_created_bit, TRUE);
|
|
SET_FLAG(header->flags, _object_header_automatically_deactivate_bit, TRUE);
|
|
|
|
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;
|
|
|
|
point_from_line3d(
|
|
&object->object.position,
|
|
&object->object.up,
|
|
data->height,
|
|
&object->object.position);
|
|
|
|
SET_FLAG(object->object.flags, _object_mirrored_bit, TEST_FLAG(data->flags, _new_object_mirrored_bit));
|
|
|
|
object->object.location.cluster_index= NONE;
|
|
header->cluster_index= NONE;
|
|
|
|
object->object.magic_number= global_object_marker-1;
|
|
object->object.umbrella_shield_object_index= NONE;
|
|
object->object.first_cluster_reference_index= NONE;
|
|
object->object.animation.state.index= NONE;
|
|
object->object.animation.animation_graph_index= object_definition->object.animation_graph.index;
|
|
object->object.cached_render_state_index= NONE;
|
|
object->object.parent_object_index= NONE;
|
|
object->object.next_object_index= NONE;
|
|
object->object.first_child_object_index= NONE;
|
|
object->object.name_index= NONE;
|
|
object->object.shield_damage_decay_timer= NONE;
|
|
object->object.body_damage_decay_timer= NONE;
|
|
|
|
if (TEST_FLAG(object_definition->object.flags, _object_does_not_cast_shadow_bit))
|
|
{
|
|
SET_FLAG(object->object.flags, _object_shadowless_bit, TRUE);
|
|
}
|
|
|
|
SET_FLAG(object->object.flags, _object_has_collision_model_bit, object_definition->object.collision_model.index!=NONE);
|
|
|
|
object_set_visibility(object_index, object_definition->object.model.index!=NONE);
|
|
object->object.owner_team_index= data->owner_team_index;
|
|
object->object.owner_player_index= data->owner_player_index;
|
|
object->object.owner_object_index= data->owner_object_index;
|
|
object->object.variant_number= data->variant_number;
|
|
object->object.forced_shader_permutation_index= object_definition->object.forced_shader_permutation_index;
|
|
|
|
if (object_definition->object.model.index==NONE)
|
|
{
|
|
node_count=1;
|
|
}
|
|
else
|
|
{
|
|
node_count= model_definition_get(object_definition->object.model.index)->nodes.count;
|
|
}
|
|
|
|
if (object_header_block_allocate(
|
|
object_index,
|
|
offsetof(struct object_datum, object.node_matrices),
|
|
sizeof(struct real_matrix4x3) * node_count))
|
|
{
|
|
if (!TEST_FLAG(_object_mask_cannot_interpolate, object_definition->object.type) &&
|
|
(
|
|
!object_header_block_allocate(
|
|
object_index,
|
|
offsetof(struct object_datum, object.node_orientations),
|
|
sizeof(struct real_orientation) * node_count
|
|
) ||
|
|
!object_header_block_allocate(
|
|
object_index,
|
|
offsetof(struct object_datum, object.original_node_orientations),
|
|
sizeof(struct real_orientation) * node_count
|
|
)
|
|
)
|
|
)
|
|
{
|
|
can_create_object= FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
can_create_object= 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 (can_create_object && 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(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
|
|
{
|
|
can_create_object= FALSE;
|
|
}
|
|
|
|
if (can_create_object)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
object_type_delete(object_index);
|
|
object_header_delete(object_header_data, object_index);
|
|
object_index= NONE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (object_index==NONE && definition_index!=NONE)
|
|
{
|
|
char string[512];
|
|
sprintf(string, "OUT OF OBJECTS: cannot create %s", tag_name_strip_path(tag_get_name(definition_index)));
|
|
console_printf(FALSE, "%s", string);
|
|
error(_error_log, "%s", string);
|
|
}
|
|
|
|
return object_index;
|
|
}
|
|
|
|
|
|
void object_attach_to_node(
|
|
long parent_object_index,
|
|
long child_object_index,
|
|
short parent_node_index)
|
|
{
|
|
long object_index;
|
|
struct object_datum *object;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (valid)
|
|
{
|
|
real_matrix4x3 inverse_node_matrix;
|
|
|
|
struct object_datum *child_object= object_get(child_object_index);
|
|
struct object_datum *parent_object= object_get(parent_object_index);
|
|
boolean connected_to_map= TEST_FLAG(child_object->object.flags, _object_connected_to_map_bit);
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\objects\\objects.c",
|
|
1235,
|
|
object_has_node(parent_object_index, parent_node_index));
|
|
|
|
if (connected_to_map)
|
|
{
|
|
object_disconnect_from_map(child_object_index);
|
|
}
|
|
|
|
matrix4x3_inverse(object_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);
|
|
}
|
|
|
|
object_deactivate(child_object_index);
|
|
|
|
SET_FLAG(object_header_get(child_object_index)->flags, _object_header_do_not_update_bit, TRUE);
|
|
|
|
object_compute_node_matrices(child_object_index);
|
|
}
|
|
else
|
|
{
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\objects\\objects.c",
|
|
1225,
|
|
valid,
|
|
"cannot attach an object to one of its children");
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
boolean object_update(
|
|
long object_index)
|
|
{
|
|
struct object_header_datum *header= object_header_get(object_index);
|
|
struct object_datum *object= object_get(object_index);
|
|
struct object_definition *object_definition= object_definition_get(object->definition_index);
|
|
|
|
boolean result= TRUE;
|
|
if (!TEST_FLAG(header->flags, _object_header_do_not_update_bit))
|
|
{
|
|
if (TEST_FLAG(object->object.flags, _object_garbage_bit))
|
|
{
|
|
++object_globals->active_garbage_object_count;
|
|
}
|
|
|
|
if (object->object.animation.interpolation_frame_count)
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 2508, !TEST_FLAG(_object_mask_cannot_interpolate, object->object.type));
|
|
if (++object->object.animation.interpolation_frame_index>=
|
|
object->object.animation.interpolation_frame_count)
|
|
{
|
|
object->object.animation.interpolation_frame_count= 0;
|
|
}
|
|
}
|
|
|
|
object_type_update(object_index);
|
|
if (object_definition->object.collision_model.index!=NONE)
|
|
{
|
|
object_damage_update(object_index);
|
|
}
|
|
|
|
object_type_export_function_values(object_index);
|
|
|
|
if (!TEST_FLAG(object->object.flags, _object_do_not_recompute_node_matrices_bit))
|
|
{
|
|
object_compute_node_matrices(object_index);
|
|
}
|
|
|
|
object_compute_function_values(object_index);
|
|
object_compute_change_colors(object_index);
|
|
|
|
if (
|
|
TEST_FLAG(object->object.flags, _object_dynamic_lighting_recompute_bit) &&
|
|
(
|
|
!TEST_FLAG(object->object.flags, _object_invisible_bit) ||
|
|
object_definition_get(object->definition_index)->object.model.index==NONE
|
|
)
|
|
)
|
|
{
|
|
object_connect_lights(object_index, TRUE, TRUE);
|
|
}
|
|
|
|
// Update children (if we have any)
|
|
if (object->object.first_child_object_index!=NONE)
|
|
{
|
|
object_update(object->object.first_child_object_index);
|
|
}
|
|
|
|
if (object->object.parent_object_index!=NONE)
|
|
{
|
|
if (object->object.next_object_index!=NONE)
|
|
{
|
|
object_update(object->object.next_object_index);
|
|
}
|
|
}
|
|
|
|
object_postprocess_node_matrices(object_index);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void object_compute_node_matrices_recursive(
|
|
long object_index)
|
|
{
|
|
long child_index;
|
|
struct object_datum *child_object;
|
|
struct object_datum *object= object_get(object_index);
|
|
|
|
object_compute_node_matrices(object_index);
|
|
|
|
for (
|
|
child_index= object->object.first_child_object_index;
|
|
child_index!=NONE;
|
|
child_index= child_object->object.next_object_index)
|
|
{
|
|
child_object= object_get(child_index);
|
|
object_compute_node_matrices_recursive(child_index);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
long object_new_from_scenario(
|
|
struct scenario_object_datum *scenario_object,
|
|
struct tag_block *palette)
|
|
{
|
|
struct object_placement_data placement_data;
|
|
|
|
long result= NONE;
|
|
|
|
if (scenario_object->palette_entry_index!=NONE &&
|
|
(
|
|
!object_globals->initial_placement ||
|
|
!TEST_FLAG(scenario_object->placement_flags, _scenario_object_placement_not_automatic_bit)
|
|
))
|
|
{
|
|
if (scenario_object->name_index==NONE ||
|
|
object_name_list_lookup(scenario_object->name_index)==NONE)
|
|
{
|
|
long definition_index= TAG_BLOCK_GET_ELEMENT(
|
|
palette,
|
|
scenario_object->palette_entry_index,
|
|
struct scenario_object_palette_entry
|
|
)->reference.index;
|
|
|
|
if (definition_index!=NONE)
|
|
{
|
|
object_placement_data_new(&placement_data, definition_index, NONE);
|
|
placement_data.position= scenario_object->position;
|
|
vectors3d_from_euler_angles3d(
|
|
&placement_data.forward,
|
|
&placement_data.up,
|
|
&scenario_object->rotation);
|
|
placement_data.variant_number= scenario_object->variant_number;
|
|
|
|
result= object_new(&placement_data);
|
|
if (result!=NONE)
|
|
{
|
|
object_type_place(result, scenario_object);
|
|
if (scenario_object->name_index!=NONE)
|
|
{
|
|
object_name_list_new(result, scenario_object->name_index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void object_attach_to_marker(
|
|
long parent_object_index,
|
|
char const *parent_marker_name,
|
|
long child_object_index,
|
|
char const *child_marker_name)
|
|
{
|
|
struct object_marker parent_marker;
|
|
struct object_marker child_marker;
|
|
real_matrix4x3 inverted_matrix;
|
|
|
|
struct object_datum *child_object= object_get(child_object_index);
|
|
|
|
object_get_marker_by_name(parent_object_index, parent_marker_name, &parent_marker, 1);
|
|
object_get_marker_by_name(child_object_index, child_marker_name, &child_marker, 1);
|
|
object_disconnect_from_map(child_object_index);
|
|
|
|
if (child_marker_name && *child_marker_name)
|
|
{
|
|
object_align_marker_to_matrix(child_object, &child_marker, &parent_marker.matrix);
|
|
}
|
|
else
|
|
{
|
|
matrix4x3_inverse(&child_marker.node_matrix, &inverted_matrix);
|
|
matrix4x3_transform_point(&inverted_matrix, &parent_marker.matrix.position, &child_object->object.position);
|
|
matrix4x3_transform_normal(&inverted_matrix, &parent_marker.matrix.forward, &child_object->object.forward);
|
|
matrix4x3_transform_normal(&inverted_matrix, &parent_marker.matrix.up, &child_object->object.up);
|
|
}
|
|
|
|
object_reconnect_to_map(child_object_index, NULL);
|
|
object_attach_to_node(parent_object_index, child_object_index, parent_marker.node_index);
|
|
|
|
return;
|
|
}
|
|
|
|
long object_new_by_name(
|
|
short name_index)
|
|
{
|
|
long placement_tag_block_element_size;
|
|
struct scenario *scenario= global_scenario_get();
|
|
|
|
|
|
struct scenario_object_name* name= TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->object_names,
|
|
name_index,
|
|
struct scenario_object_name);
|
|
|
|
struct tag_block* datum= scenario_get_object_type_scenario_datums(
|
|
scenario,
|
|
name->runtime_object_type,
|
|
&placement_tag_block_element_size);
|
|
|
|
struct tag_block *palette= scenario_get_object_type_scenario_palette(
|
|
scenario,
|
|
name->runtime_object_type);
|
|
|
|
struct scenario_object_datum *object= (struct scenario_object_datum *)tag_block_get_element_with_size(
|
|
datum,
|
|
name->runtime_scenario_datum_index,
|
|
placement_tag_block_element_size);
|
|
|
|
return object_new_from_scenario(
|
|
object,
|
|
palette);
|
|
}
|
|
|
|
void objects_scripting_attach(
|
|
long parent_object_index,
|
|
char const *parent_marker_name,
|
|
long child_object_index,
|
|
char const *child_marker_name)
|
|
{
|
|
if (parent_object_index!=NONE &&
|
|
child_object_index !=NONE &&
|
|
object_get(child_object_index)->object.parent_object_index==NONE)
|
|
{
|
|
object_attach_to_marker(parent_object_index, parent_marker_name, child_object_index, child_marker_name);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void object_delete_immediately(
|
|
long object_index)
|
|
{
|
|
object_delete_initial_recursive(object_index, FALSE);
|
|
object_delete_recursive(object_index, FALSE);
|
|
|
|
return;
|
|
}
|
|
|
|
void objects_garbage_collection(
|
|
void)
|
|
{
|
|
long garbage_object_indices[MAXIMUM_OBJECTS_PER_MAP];
|
|
char warningbuf[512];
|
|
unsigned char release_proc_working_memory[4096];
|
|
char released_resultbuf[512];
|
|
|
|
long 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)
|
|
{
|
|
if (MAXIMUM_OBJECTS_PER_MAP-object_header_data->actual_count>GARBAGE_LIMIT_FREE_OBJECTS_TRIGGER)
|
|
{
|
|
if (object_globals->active_garbage_object_count>=GARBAGE_LIMIT_ACTIVE_GARBAGE_TRIGGER)
|
|
{
|
|
garbage_collect_mode= _garbage_collect_active_objects;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
garbage_collect_mode= _garbage_collect_for_space;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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
|
|
{
|
|
object_globals->force_garbage_collection= FALSE;
|
|
}
|
|
}
|
|
|
|
if (garbage_collect_mode!=NONE)
|
|
{
|
|
long garbage_object_index;
|
|
struct object_datum *object;
|
|
|
|
short garbage_object_count= 0;
|
|
boolean should_collect= FALSE;
|
|
|
|
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));
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
while (TRUE)
|
|
{
|
|
boolean garbage_collect;
|
|
long object_index;
|
|
struct object_header_datum *header;
|
|
|
|
switch (garbage_collect_mode)
|
|
{
|
|
case _garbage_collect_everything:
|
|
should_collect= FALSE;
|
|
break;
|
|
case _garbage_collect_active_objects:
|
|
should_collect= object_globals->active_garbage_object_count<=GARBAGE_LIMIT_ACTIVE_GARBAGE_TARGET;
|
|
break;
|
|
case _garbage_collect_for_space:
|
|
should_collect= TRUE;
|
|
break;
|
|
}
|
|
|
|
if (should_collect || !garbage_object_count)
|
|
{
|
|
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 *object= object_get(object_index);
|
|
|
|
if (TEST_FLAG(_object_mask_unit, object->object.type) && !TEST_FLAG(object->object.damage_flags, _object_dead_bit))
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"WARNING: garbage collecting a living unit (%s)",
|
|
ai_debug_describe_actor(NONE, object_index, TRUE, temporary, NUMBEROF(temporary)));
|
|
}
|
|
|
|
|
|
if (TEST_FLAG(header->flags, _object_header_active_bit))
|
|
{
|
|
--object_globals->active_garbage_object_count;
|
|
}
|
|
object_set_garbage(object_index, FALSE);
|
|
object_delete_immediately(object_index);
|
|
}
|
|
}
|
|
|
|
memory_pool_compact(object_memory_pool);
|
|
|
|
if (debug_object_garbage_collection)
|
|
{
|
|
console_printf(
|
|
FALSE,
|
|
"compacted to #%d with 0x%x contiguous bytes free",
|
|
object_header_data->actual_count,
|
|
memory_pool_get_contiguous_free_size(object_memory_pool));
|
|
}
|
|
|
|
if (!should_collect)
|
|
{
|
|
boolean update_time;
|
|
|
|
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= TRUE;
|
|
|
|
if (object_globals->last_garbage_warn_time!=NONE)
|
|
{
|
|
if (object_globals->last_garbage_warn_time+150>=game_time_get())
|
|
{
|
|
garbage_should_warn= FALSE;
|
|
}
|
|
}
|
|
|
|
update_time= FALSE;
|
|
while (TRUE)
|
|
{
|
|
boolean debug_garbage_collection= FALSE;
|
|
boolean status_still_critical= FALSE;
|
|
if (garbage_collect_mode==_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)
|
|
{
|
|
if (free_objects>GARBAGE_LIMIT_FREE_OBJECTS_CRITICAL)
|
|
{
|
|
if (free_size>GARBAGE_LIMIT_FREE_MEMORY_TRIGGER)
|
|
{
|
|
if (free_objects<=GARBAGE_LIMIT_FREE_OBJECTS_TRIGGER)
|
|
{
|
|
debug_garbage_collection= TRUE;
|
|
debug_slots_free= TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
debug_garbage_collection= TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status_still_critical= TRUE;
|
|
debug_garbage_collection= TRUE;
|
|
debug_slots_free= TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status_still_critical= TRUE;
|
|
debug_garbage_collection= TRUE;
|
|
}
|
|
|
|
if (debug_slots_free)
|
|
{
|
|
sprintf(warningbuf, "%d slots free", free_objects);
|
|
}
|
|
else
|
|
{
|
|
sprintf(warningbuf, "%4.2f%% memory free", ((free_size * 100.f) / 2097152.f));
|
|
}
|
|
}
|
|
|
|
if (status_still_critical || garbage_collection_after_first_attempt)
|
|
{
|
|
char tempbuffer[512];
|
|
|
|
const char *status;
|
|
if (garbage_collection_after_first_attempt)
|
|
{
|
|
status= status_still_critical ? "still " : "not ";
|
|
}
|
|
else
|
|
{
|
|
status= "";
|
|
}
|
|
sprintf(tempbuffer, "garbage collection %scritical (%s)", status, warningbuf);
|
|
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)
|
|
{
|
|
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);
|
|
console_printf(FALSE, "%s", tempbuffer);
|
|
error(_error_log, "%s", tempbuffer);
|
|
}
|
|
if (!more_to_release)
|
|
{
|
|
++current_release_procs;
|
|
v0= FALSE;
|
|
}
|
|
}
|
|
|
|
if (!result)
|
|
{
|
|
if (update_time)
|
|
{
|
|
object_globals->last_garbage_warn_time= game_time_get();
|
|
}
|
|
break;
|
|
}
|
|
garbage_collection_after_first_attempt= TRUE;
|
|
memory_pool_compact(object_memory_pool);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
object_globals->force_garbage_collection= FALSE;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void objects_update(
|
|
void)
|
|
{
|
|
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(objects_update_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(objects_update_section);
|
|
|
|
return;
|
|
}
|
|
|
|
void objects_memory_compact(
|
|
void)
|
|
{
|
|
objects_garbage_collection();
|
|
memory_pool_compact(object_memory_pool);
|
|
return;
|
|
}
|
|
|
|
/* ---------- private code */
|
|
|
|
static void object_connect_lights(
|
|
long object_index,
|
|
boolean disconnect,
|
|
boolean reconnect)
|
|
{
|
|
struct object_datum *object= object_get(object_index);
|
|
|
|
if (TEST_FLAG(object->object.flags, _object_has_attached_lights_bit))
|
|
{
|
|
short i;
|
|
struct object_definition *object_definition= object_definition_get(object->definition_index);
|
|
|
|
for (i=0; i<object_definition->object.attachments.count; ++i)
|
|
{
|
|
if (!object->object.attachment_types[i] && object->object.attachment_indices[i]!=NONE)
|
|
{
|
|
if (disconnect)
|
|
{
|
|
light_disconnect_from_map(object->object.attachment_indices[i]);
|
|
}
|
|
if (reconnect)
|
|
{
|
|
light_reconnect_to_map(object->object.attachment_indices[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void object_name_list_allocate(
|
|
void)
|
|
{
|
|
object_name_list= (long*)game_state_malloc("object name list", NULL, MAXIMUM_OBJECT_NAMES_PER_SCENARIO * sizeof(*object_name_list));
|
|
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 4072, object_name_list);
|
|
|
|
return;
|
|
}
|
|
|
|
static void object_name_list_free(
|
|
void)
|
|
{
|
|
// Bungie never free'd this properly
|
|
|
|
return;
|
|
}
|
|
|
|
static void object_name_list_clear(
|
|
void)
|
|
{
|
|
memset(object_name_list, NONE, MAXIMUM_OBJECT_NAMES_PER_SCENARIO * sizeof(*object_name_list));
|
|
|
|
return;
|
|
}
|
|
|
|
static void object_name_list_new(
|
|
long object_index,
|
|
short name_index)
|
|
{
|
|
struct object_datum *object= object_get(object_index);
|
|
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\objects\\objects.c",
|
|
4099,
|
|
name_index>=0 && name_index<MAXIMUM_OBJECT_NAMES_PER_SCENARIO);
|
|
|
|
if (object_name_list[name_index]==NONE)
|
|
{
|
|
object_name_list[name_index]= object_index;
|
|
object->object.name_index= name_index;
|
|
}
|
|
else
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"an object with the name '%s' already exists!",
|
|
TAG_BLOCK_GET_ELEMENT(&global_scenario_get()->object_names, name_index, struct scenario_object_name)->name);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void object_name_list_delete(
|
|
long object_index)
|
|
{
|
|
struct object_datum *object= object_get(object_index);
|
|
|
|
if (object->object.name_index!=NONE)
|
|
{
|
|
short i;
|
|
struct scenario *scenario= global_scenario_get();
|
|
object->object.name_index= NONE;
|
|
|
|
for (i=0; i<scenario->object_names.count; ++i)
|
|
{
|
|
if (object_name_list[i]==object_index)
|
|
{
|
|
object_name_list[i]= NONE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static long object_name_list_lookup(
|
|
short name_index)
|
|
{
|
|
return VALID_INDEX(name_index, MAXIMUM_OBJECT_NAMES_PER_SCENARIO) ? object_name_list[name_index] : NONE;
|
|
}
|
|
|
|
static long recursive_object_adder(
|
|
long object_index,
|
|
boolean(*add_object_function)(long, void *),
|
|
void *custom_data,
|
|
long object_count,
|
|
long maximum_count,
|
|
long *object_indices)
|
|
{
|
|
struct object_datum *object= object_get(object_index);
|
|
|
|
if (object_count<maximum_count)
|
|
{
|
|
if (!add_object_function || add_object_function(object_index, custom_data))
|
|
{
|
|
object_indices[object_count++]= object_index;
|
|
}
|
|
|
|
if (object->object.first_child_object_index!=NONE)
|
|
{
|
|
object_count= recursive_object_adder(
|
|
object->object.first_child_object_index,
|
|
add_object_function,
|
|
custom_data,
|
|
object_count,
|
|
maximum_count,
|
|
object_indices);
|
|
}
|
|
|
|
if (object->object.next_object_index!=NONE)
|
|
{
|
|
object_count= recursive_object_adder(
|
|
object->object.next_object_index,
|
|
add_object_function,
|
|
custom_data,
|
|
object_count,
|
|
maximum_count,
|
|
object_indices);
|
|
}
|
|
}
|
|
return object_count;
|
|
}
|
|
|
|
static long sort_dumps(
|
|
struct dump_datum const *dump1,
|
|
struct dump_datum const *dump2)
|
|
{
|
|
long result;
|
|
if (dump1->total_size < dump2->total_size)
|
|
{
|
|
result= 1;
|
|
}
|
|
else if (dump1->total_size <= dump2->total_size)
|
|
{
|
|
result= 0;
|
|
}
|
|
else
|
|
{
|
|
result= -1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void object_add_to_dump(
|
|
long object_index,
|
|
struct dump_datum *dump)
|
|
{
|
|
long parent_index;
|
|
struct object_datum *parent;
|
|
struct object_header_datum *header= object_header_get(object_index);
|
|
struct object_datum *object= object_get(object_index);
|
|
|
|
|
|
if (header->data_size>dump->maximum_size)
|
|
{
|
|
dump->maximum_size= header->data_size;
|
|
}
|
|
|
|
dump->total_size+= header->data_size;
|
|
dump->count++;
|
|
|
|
if (TEST_FLAG(header->flags, _object_header_active_bit))
|
|
{
|
|
dump->active_count++;
|
|
}
|
|
|
|
if (TEST_FLAG(object->object.flags, _object_garbage_bit))
|
|
{
|
|
dump->garbage_count++;
|
|
}
|
|
|
|
if (TEST_FLAG(object->object.damage_flags, _object_dead_bit))
|
|
{
|
|
dump->dead_count++;
|
|
}
|
|
|
|
if (TEST_FLAG(object->object.flags, _object_at_rest_bit))
|
|
{
|
|
dump->at_rest_count++;
|
|
}
|
|
|
|
parent_index= object_get_ultimate_parent(object_index);
|
|
parent= object_get(parent_index);
|
|
if (TEST_FLAG(parent->object.flags, _object_outside_of_map_bit) || parent->object.location.cluster_index==NONE)
|
|
{
|
|
dump->outside_map_count++;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void object_dump_write(
|
|
FILE *stream,
|
|
struct dump_datum *dump)
|
|
{
|
|
const char *name= "unknown";
|
|
if (dump->definition_index!=NONE)
|
|
{
|
|
name= tag_get_name(dump->definition_index);
|
|
}
|
|
else
|
|
{
|
|
if (dump->object_type!=NONE)
|
|
{
|
|
name= object_type_get_name(dump->object_type);
|
|
}
|
|
}
|
|
|
|
fprintf(
|
|
stream,
|
|
"% 6d (% 6d) [% 7d/% 7d/% 7d/% 7d] % 7d % 7d %s\r\n",
|
|
dump->count,
|
|
dump->active_count,
|
|
dump->garbage_count,
|
|
dump->dead_count,
|
|
dump->outside_map_count,
|
|
dump->at_rest_count,
|
|
dump->maximum_size,
|
|
dump->total_size,
|
|
name);
|
|
|
|
return;
|
|
}
|
|
|
|
static short object_find_region_permutations_available_with_variant(
|
|
struct model_region *region,
|
|
short variant_number,
|
|
short *available_indices)
|
|
{
|
|
short i;
|
|
short result= 0;
|
|
|
|
for (i= 0; i<region->permutations.count; i++)
|
|
{
|
|
struct model_region_permutation* permutation= TAG_BLOCK_GET_ELEMENT(
|
|
®ion->permutations,
|
|
i,
|
|
struct model_region_permutation);
|
|
if (!TEST_FLAG(permutation->flags, _model_region_permutation_cannot_be_chosen_randomly_bit))
|
|
{
|
|
if (permutation->variant_number==variant_number ||
|
|
variant_number==NONE && permutation->variant_number< 100)
|
|
{
|
|
available_indices[result++]= i;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static boolean object_select_random_region_permutations_by_variant(
|
|
long object_index,
|
|
struct model *model,
|
|
short variant_number)
|
|
{
|
|
short available_permutation_indices[32];
|
|
short region_index;
|
|
|
|
struct object_datum *object= object_get(object_index);
|
|
boolean result= TRUE;
|
|
|
|
for (region_index=0; region_index<model->regions.count; region_index++)
|
|
{
|
|
struct model_region *region= TAG_BLOCK_GET_ELEMENT(&model->regions, region_index, struct model_region);
|
|
short permutation_index= object_find_region_permutations_available_with_variant(region, variant_number, available_permutation_indices);
|
|
|
|
if (permutation_index==0 && variant_number!=NONE)
|
|
{
|
|
permutation_index= object_find_region_permutations_available_with_variant(region, 0, available_permutation_indices);
|
|
}
|
|
|
|
if (!permutation_index)
|
|
{
|
|
object->object.region_permutations[region_index]= 0;
|
|
result= FALSE;
|
|
}
|
|
else
|
|
{
|
|
short permutation_num= permutation_index==1 ? 0 : random_range(0, permutation_index);
|
|
object->object.region_permutations[region_index]= available_permutation_indices[permutation_num];
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static short object_determine_variant_number(
|
|
long object_index,
|
|
struct model *model)
|
|
{
|
|
short region_index;
|
|
|
|
struct object_datum *object= object_get(object_index);
|
|
short result= 0;
|
|
|
|
for (region_index=0; region_index<model->regions.count&& !result; region_index++)
|
|
{
|
|
struct model_region* region= TAG_BLOCK_GET_ELEMENT(&model->regions, region_index, struct model_region);
|
|
if (object->object.region_permutations[region_index] < region->permutations.count)
|
|
{
|
|
result= TAG_BLOCK_GET_ELEMENT(
|
|
®ion->permutations,
|
|
object->object.region_permutations[region_index],
|
|
struct model_region_permutation
|
|
)->variant_number;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void object_choose_random_region_permutations(long object_index)
|
|
{
|
|
struct object_datum *object= object_get(object_index);
|
|
struct object_definition *object_definition= object_definition_get(object->definition_index);
|
|
|
|
if (object_definition->object.model.index!=NONE)
|
|
{
|
|
struct model* model= model_definition_get(object_definition->object.model.index);
|
|
if (object->object.variant_number <= 0 ||
|
|
!object_select_random_region_permutations_by_variant(object_index, model, object->object.variant_number))
|
|
{
|
|
object_select_random_region_permutations_by_variant(object_index, model, NONE);
|
|
object->object.variant_number= object_determine_variant_number(object_index, model);
|
|
if (object->object.variant_number > 0)
|
|
{
|
|
object_select_random_region_permutations_by_variant(object_index, model, object->object.variant_number);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void object_add_to_list(
|
|
long *first_object_reference,
|
|
long object_index)
|
|
{
|
|
struct object_datum *object= object_get(object_index);
|
|
object->object.next_object_index= *first_object_reference;
|
|
*first_object_reference= object_index;
|
|
|
|
return;
|
|
}
|
|
|
|
static void object_remove_from_list(
|
|
long *first_object_reference,
|
|
long object_index)
|
|
{
|
|
while (*first_object_reference!=NONE)
|
|
{
|
|
struct object_datum *object= object_get(*first_object_reference);
|
|
|
|
if (*first_object_reference==object_index)
|
|
{
|
|
*first_object_reference= object->object.next_object_index;
|
|
object->object.next_object_index= NONE;
|
|
break;
|
|
}
|
|
|
|
first_object_reference= &object->object.next_object_index;
|
|
|
|
match_assert("c:\\halo\\SOURCE\\objects\\objects.c", 3179, *first_object_reference!=NONE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void attachments_delete(
|
|
long object_index)
|
|
{
|
|
short attachment_index;
|
|
struct object_datum *object= object_get(object_index);
|
|
struct object_definition *object_definition= object_definition_get(object->definition_index);
|
|
|
|
for (attachment_index=0; attachment_index<object_definition->object.attachments.count; ++attachment_index)
|
|
{
|
|
if (object->object.attachment_types[attachment_index]!=NONE &&
|
|
object->object.attachment_indices[attachment_index]!=NONE)
|
|
{
|
|
switch (object->object.attachment_types[attachment_index])
|
|
{
|
|
case _object_attachment_type_light:
|
|
light_delete(object->object.attachment_indices[attachment_index]);
|
|
break;
|
|
case _object_attachment_type_looping_sound:
|
|
game_looping_sound_delete(object->object.attachment_indices[attachment_index]);
|
|
break;
|
|
case _object_attachment_type_effect:
|
|
effect_delete(object->object.attachment_indices[attachment_index]);
|
|
break;
|
|
case _object_attachment_type_contrail:
|
|
object_compute_node_matrices(object_index);
|
|
contrail_owner_collision(object->object.attachment_indices[attachment_index], TRUE, 0.f);
|
|
break;
|
|
case _object_attachment_type_particle_system:
|
|
particle_system_orphan(object->object.attachment_indices[attachment_index]);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void object_create_attachments(
|
|
long object_index)
|
|
{
|
|
struct object_datum *object= object_get(object_index);
|
|
struct object_definition *object_definition= object_definition_get(object->definition_index);
|
|
|
|
widgets_new(object_index);
|
|
attachments_new(object_index);
|
|
|
|
return;
|
|
}
|
|
|
|
static void object_delete_attachments(
|
|
long object_index)
|
|
{
|
|
struct object_datum *object= object_get(object_index);
|
|
struct object_definition *object_definition= object_definition_get(object->definition_index);
|
|
|
|
object_type_definition_get(object->object.type);
|
|
widgets_delete(object_index);
|
|
attachments_delete(object_index);
|
|
|
|
return;
|
|
}
|
|
|
|
static void object_delete_recursive(
|
|
long object_index,
|
|
boolean delete_siblings)
|
|
{
|
|
struct object_datum *object= object_get(object_index);
|
|
struct object_definition *object_definition= object_definition_get(object->definition_index);
|
|
|
|
if (TEST_FLAG(object->object.flags, _object_garbage_bit))
|
|
{
|
|
object_set_garbage(object_index, FALSE);
|
|
}
|
|
|
|
object_deleted_procs_call(object_index);
|
|
if (object->object.first_child_object_index!=NONE)
|
|
{
|
|
object_delete_recursive(object->object.first_child_object_index, TRUE);
|
|
}
|
|
|
|
if (delete_siblings && object->object.next_object_index!=NONE)
|
|
{
|
|
object_delete_recursive(object->object.next_object_index, TRUE);
|
|
}
|
|
|
|
object_deactivate(object_index);
|
|
object_delete_attachments(object_index);
|
|
|
|
if (TEST_FLAG(object->object.flags, _object_connected_to_map_bit))
|
|
{
|
|
object_disconnect_from_map(object_index);
|
|
}
|
|
|
|
object_type_delete(object_index);
|
|
object_header_delete(object_header_data, object_index);
|
|
|
|
return;
|
|
}
|
|
|
|
static void object_compute_function_values(
|
|
long object_index)
|
|
{
|
|
short function_index;
|
|
|
|
struct object_datum *object= object_get(object_index);
|
|
struct object_definition *object_definition= object_definition_get(object->definition_index);
|
|
real huh= (57 * DATUM_INDEX_TO_ABSOLUTE_INDEX(object_index) + game_time_get()) * 0.033333335f;
|
|
|
|
for (function_index= 0; function_index<object_definition->object.functions.count; ++function_index)
|
|
{
|
|
real value;
|
|
real output;
|
|
|
|
struct object_function_definition* function= TAG_BLOCK_GET_ELEMENT(
|
|
&object_definition->object.functions,
|
|
function_index,
|
|
struct object_function_definition);
|
|
boolean function_is_active= TRUE;
|
|
real period= function->runtime_one_over_period;
|
|
|
|
if (function->scale_period_by_function_index)
|
|
{
|
|
real function_value= OBJECT_INCOMING_FUNCTION_GET_VALUE(object, function->scale_period_by_function_index);
|
|
if (function_value>0.f)
|
|
{
|
|
period= period/function_value;
|
|
}
|
|
}
|
|
|
|
value= periodic_function_evaluate(function->function_type, huh*period);
|
|
|
|
if (function->scale_function_by_function_index)
|
|
{
|
|
value*= OBJECT_INCOMING_FUNCTION_GET_VALUE(object, function->scale_function_by_function_index);
|
|
}
|
|
|
|
if (TEST_FLAG(function->flags, _object_function_invert_function_bit))
|
|
{
|
|
value= 1.f-value;
|
|
}
|
|
|
|
if (function->wobble_magnitude!=0.f)
|
|
{
|
|
real val= periodic_function_evaluate(function->wobble_function_type, huh*function->wobble_period);
|
|
value+= (2.f*function->wobble_magnitude * (val-0.5f));
|
|
}
|
|
|
|
if (function->square_wave_threshold!=0.f)
|
|
{
|
|
value= value>function->square_wave_threshold ? 1.f : 0.f ;
|
|
}
|
|
|
|
if (function->step_count>1)
|
|
{
|
|
real floor_value= floor(function->step_count*value);
|
|
value= floor_value*function->runtime_reciprocal_step_count;
|
|
}
|
|
|
|
if (function->runtime_reciprocal_sawtooth_count>0.f)
|
|
{
|
|
value= fmod(value, function->runtime_reciprocal_sawtooth_count);
|
|
}
|
|
|
|
if (function->add_function_index)
|
|
{
|
|
value+= OBJECT_INCOMING_FUNCTION_GET_VALUE(object, function->add_function_index);
|
|
value= MIN(value, 1.f);
|
|
}
|
|
|
|
if (function->scale_result_by_function_index)
|
|
{
|
|
value*= OBJECT_INCOMING_FUNCTION_GET_VALUE(object, function->scale_result_by_function_index);
|
|
}
|
|
|
|
output= transition_function_evaluate(function->map_result_to_transition_function, value);
|
|
|
|
if (function->scale_by>0.f)
|
|
{
|
|
output*= function->scale_by;
|
|
}
|
|
|
|
if (function->bounds_mode==_object_function_scale_to_fit_bounds)
|
|
{
|
|
output*= (function->upper_bound-function->lower_bound);
|
|
output+= function->lower_bound;
|
|
|
|
if (function->lower_bound+_real_epsilon>=output)
|
|
{
|
|
function_is_active= TEST_FLAG(function->flags, _object_function_does_not_deactivate_below_lower_bound_bit);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (function->lower_bound+_real_epsilon>=output)
|
|
{
|
|
output= function->lower_bound;
|
|
function_is_active= TEST_FLAG(function->flags, _object_function_does_not_deactivate_below_lower_bound_bit);
|
|
}
|
|
|
|
if (output>function->upper_bound)
|
|
{
|
|
output= function->upper_bound;
|
|
}
|
|
|
|
if (function->bounds_mode==_object_function_clip_to_bounds_and_normalize)
|
|
{
|
|
output= (output-function->lower_bound)*function->runtime_reciprocal_bounds_range;
|
|
}
|
|
}
|
|
|
|
if (function->turn_off_with_function_index!=NONE &&
|
|
!TEST_FLAG(object->object.functions_active_flags, function->turn_off_with_function_index))
|
|
{
|
|
function_is_active= FALSE;
|
|
}
|
|
|
|
if (TEST_FLAG(function->flags, _object_function_clip_to_bounds_and_normalize))
|
|
{
|
|
output= fmod(output+object->object.outgoing_function_values[function_index], 1.f);
|
|
}
|
|
|
|
object->object.outgoing_function_values[function_index]= output;
|
|
SET_FLAG(object->object.functions_active_flags, function_index, function_is_active);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// TODO: match
|
|
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))
|
|
{
|
|
long cc_index;
|
|
for (cc_index= 0; cc_index<object_definition->object.change_colors.count; cc_index++)
|
|
{
|
|
struct object_change_color_definition *change_color= TAG_BLOCK_GET_ELEMENT(
|
|
&object_definition->object.change_colors,
|
|
cc_index,
|
|
struct object_change_color_definition);
|
|
|
|
if (change_color->scaled_by)
|
|
{
|
|
rgb_colors_interpolate(
|
|
&object->object.outgoing_change_colors[cc_index],
|
|
change_color->scale_flags,
|
|
&change_color->color_lower_bound,
|
|
&change_color->color_upper_bound,
|
|
object->object.incoming_function_values[change_color->scaled_by]);
|
|
}
|
|
|
|
if (change_color->darken_by)
|
|
{
|
|
const real scale= object->object.incoming_function_values[change_color->scaled_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;
|
|
}
|