Files
MrBruh a58fd479e5 Item resting surface: a port comment, its helpers with the private code (#231)
The helpers' comment is marked as the port's and says plainly what the short holds; their definitions move to the file's private code, and the plane the item is moved onto is indented inside its new surface check.
2026-10-10 15:14:51 +13:00

981 lines
23 KiB
C

/*
ITEMS.C
symbols in this file:
000E60A0 0020:
_object_get_type (0000)
000E60C0 0010:
_items_initialize (0000)
000E60D0 0010:
_items_initialize_for_new_map (0000)
000E60E0 0010:
_items_dispose_from_old_map (0000)
000E60F0 0010:
_items_dispose (0000)
000E6100 0040:
_item_new (0000)
000E6140 0010:
_item_delete (0000)
000E6150 0010:
_verify_item_location (0000)
000E6160 0050:
_dangerous_items_near_player (0000)
000E61B0 00a0:
_item_in_unit_inventory (0000)
000E6250 0090:
_item_get_position_even_if_in_inventory (0000)
000E62E0 0090:
_item_detonate (0000)
000E6370 00c0:
_item_adjust_for_angular_velocity_change (0000)
000E6430 00c0:
_valid_real_vector3d_axes3 (0000)
000E64F0 0060:
_valid_real_matrix4x3 (0000)
000E6550 03b0:
_item_accelerate (0000)
000E6900 0230:
_item_align_to_normal_and_point (0000)
000E6B30 0930:
_item_update (0000)
00278FB0 0004:
_item_maximum_impact_velocity (0000)
00278FB4 000c:
??_C@_0M@HIOGOKBN@item_update?$AA@ (0000)
00278FC0 000d:
??_C@_0N@MIPPJENF@ground?5point?$AA@ (0000)
00278FD0 001d:
??_C@_0BN@FFDGPJIN@c?3?2halo?2SOURCE?2items?2items?4c?$AA@ (0000)
00278FF0 002b:
??_C@_0CL@EEILLIKO@valid_real_matrix4x3?$CI?$CGground_poi@ (0000)
0027901C 0008:
??_C@_07GAHNFPHG@scale?$DO0?$AA@ (0000)
00279024 0004:
__real@3f350481 (0000)
00306498 05f8:
_item_update_section (0000)
*/
/* ---------- headers */
#include "cseries.h"
#include "cseries/profile.h"
#include "effects/material_effects.h"
#include "game/game.h"
#include "effects/effects.h"
#include "game/players.h"
#include "game/game_engine.h"
#include "items/item_definitions.h"
#include "items.h"
#include "physics/breakable_surfaces.h"
#include "physics/collision_bsp_definitions.h"
#include "physics/collision_usage.h"
#include "physics/collisions.h"
#include "physics/physics.h"
#include "scenario/scenario.h"
#include "sound/game_sound.h"
#include "units/units.h"
/* ---------- constants */
enum
{
_item_definition_always_maintains_z_up_bit = 0,
_item_definition_destroyed_by_explosions_bit,
_item_definition_antigravity_bit,
};
enum
{
_collision_surface_breakable_bit = 3,
};
enum
{
ITEM_UPDATE_COLLISION_TEST_FLAGS = 0x1FF3E9,
};
/* ---------- macros */
/* ---------- structures */
/* ---------- prototypes */
/* port: the surface an item rests on, kept in a short of the datum: a
collision surface past 32767 (a large map's) is read back unsigned, NONE
stays NONE, and a surface of 65535 or more (NONE's bits) is not kept */
static long item_rested_surface_index(
struct item_datum const *item);
static struct collision_surface const *item_rested_surface(
struct item_datum const *item);
static void item_set_rested_surface(
struct item_datum *item,
long surface_index);
/* ---------- globals */
static struct profile_section item_update_section = { "item_update", NONE, TRUE };
static real const item_maximum_impact_velocity = 0.1f;
/* ---------- public code */
void items_initialize(
void)
{
return;
}
void items_initialize_for_new_map(
void)
{
return;
}
void items_dispose_from_old_map(
void)
{
return;
}
void items_dispose(
void)
{
return;
}
void item_delete(
long item_index)
{
return;
}
void verify_item_location(
void)
{
return;
}
boolean item_new(
long item_index)
{
struct item_datum *item = item_get(item_index);
item->object.flags |= FLAG(_object_dynamic_lighting_recompute_bit) |
FLAG(_object_static_lighting_recompute_bit);
item->item.last_owned_time = game_time_get();
item->item.ignore_object_index = NONE;
return TRUE;
}
boolean dangerous_items_near_player(
void)
{
struct object_iterator iterator;
struct item_datum *item;
object_iterator_new(&iterator, _object_mask_item, 1);
while (item = (struct item_datum *)object_iterator_next(&iterator))
{
if (item->item.detonation_ticks > 0)
return TRUE;
}
return FALSE;
}
void item_in_unit_inventory(
long item_index,
long owner_unit_index)
{
struct item_datum *item = item_get(item_index);
if (owner_unit_index != NONE)
{
struct unit_datum *unit = unit_get(owner_unit_index);
item->item.flags |= FLAG(_item_attached_to_unit_bit);
if (unit->unit.player_index != NONE)
{
item->item.flags |= FLAG(_item_belongs_to_player_bit);
}
else
{
item->item.flags &= ~FLAG(_item_belongs_to_player_bit);
}
item->object.owner_player_index = unit->unit.player_index;
object_set_garbage(item_index, FALSE);
item->item.flags &=
~(FLAG(_item_on_structure_bit) | FLAG(_item_does_not_accelerate_bit));
item->object.location.leaf_index = NONE;
item->object.location.cluster_index = NONE;
scenario_location_award_bonus(&item->object.location);
return;
}
item->item.flags &=
~(FLAG(_item_attached_to_unit_bit) | FLAG(_item_belongs_to_player_bit));
return;
}
void item_get_position_even_if_in_inventory(
long item_index,
real_point3d *position)
{
struct item_datum *item = item_try_and_get(item_index);
position->x = 0.f;
position->y = 0.f;
position->z = 0.f;
if (!item)
{
return;
}
if (TEST_FLAG(item->item.flags, _item_attached_to_unit_bit))
{
long player_index = item->object.owner_player_index;
struct player_datum *player;
struct unit_datum *unit;
if (player_index == NONE)
{
return;
}
player = player_get(player_index);
if (player->unit_index == NONE)
{
return;
}
unit = unit_get(player->unit_index);
*position = unit->object.bounding_sphere_center;
return;
}
*position = item->object.bounding_sphere_center;
return;
}
void item_detonate(
long item_index)
{
struct item_datum *item = item_get(item_index);
struct item_definition *definition = item_definition_get(item->definition_index);
if (!item->item.detonation_ticks)
{
effect_new_from_object(
definition->item.detonating_effect.index,
item_index,
item_index,
NONE,
0.f,
0.f,
NULL,
NULL);
item->item.detonation_ticks =
(short)(real_random_range(
definition->item.detonation_delay_timer_lower_bound,
definition->item.detonation_delay_timer_upper_bound) * TICKS_PER_SECOND);
}
return;
}
/* ---------- private code */
static long item_rested_surface_index(
struct item_datum const *item)
{
if (item->item.rested_surface_index == NONE)
return NONE;
return (unsigned short)item->item.rested_surface_index;
}
static struct collision_surface const *item_rested_surface(
struct item_datum const *item)
{
struct collision_bsp const *bsp = global_collision_bsp_get();
long surface_index = item_rested_surface_index(item);
if (!bsp || surface_index < 0 || surface_index >= bsp->surfaces.count)
return NULL;
return TAG_BLOCK_GET_ELEMENT(&bsp->surfaces, surface_index, struct collision_surface);
}
static void item_set_rested_surface(
struct item_datum *item,
long surface_index)
{
if (surface_index < 0 || surface_index >= 65535)
item->item.rested_surface_index = NONE;
else
item->item.rested_surface_index = (short)surface_index;
}
static void item_adjust_for_angular_velocity_change(
long item_index)
{
struct item_datum *item = item_get(item_index);
real angular_velocity_magnitude = magnitude3d(&item->object.angular_velocity);
if (angular_velocity_magnitude != 0.f)
{
item->item.flags |= FLAG(_item_has_nonzero_angular_velocity_bit);
if (!TEST_FLAG(item->object.flags, _object_at_rest_bit))
{
scale_vector3d(
&item->object.angular_velocity,
1.f / angular_velocity_magnitude,
&item->item.rotation_axis);
}
item->item.rotation_sine = sine(angular_velocity_magnitude);
item->item.rotation_cosine = cosine(angular_velocity_magnitude);
}
else
{
item->item.flags &= ~FLAG(_item_has_nonzero_angular_velocity_bit);
item->item.rotation_sine = 0.f;
item->item.rotation_cosine = 1.f;
}
return;
}
void item_accelerate(
long item_index,
real_vector3d const *acceleration,
boolean detonates_explosives)
{
struct item_datum *item = item_get(item_index);
struct item_definition *definition = item_definition_get(item->definition_index);
if (TEST_FLAG(item->item.flags, _item_does_not_accelerate_bit))
{
return;
}
match_assert(
"c:\\halo\\SOURCE\\items\\items.c",
536,
global_current_collision_user_depth < MAXIMUM_COLLISION_USER_STACK_DEPTH);
global_current_collision_users[global_current_collision_user_depth++] =
_collision_user_items;
if (item->object.parent_object_index == NONE)
{
if (detonates_explosives &&
!game_engine_running() &&
TEST_FLAG(definition->item.flags, 1))
{
item_detonate(item_index);
}
if (TEST_FLAG(item->item.flags, _item_on_structure_bit))
{
if (!(magnitude_squared3d(acceleration) < 0.0001f))
{
struct object_marker marker;
if (object_get_marker_by_name(item_index, "ground point", &marker, 1))
{
struct collision_bsp *collision_bsp;
struct collision_surface const *surface;
real_point3d new_position;
real_plane3d plane;
real distance_above;
collision_bsp = global_collision_bsp_get();
surface = item_rested_surface(item);
if (surface)
{
bsp3d_get_plane_from_designator(
&collision_bsp->bsp3d,
surface->plane_designator,
&plane);
distance_above =
0.05f - (plane3d_distance_to_point(&plane, &marker.matrix.position));
point_from_line3d(
&marker.matrix.position,
&plane.n,
distance_above,
&new_position);
object_translate(item_index, &new_position, NULL);
}
}
item->object.flags &= ~FLAG(_object_at_rest_bit);
item->item.flags &= ~FLAG(_item_on_structure_bit);
}
}
else
{
item->object.flags &= ~FLAG(_object_at_rest_bit);
}
add_vectors3d(
&item->object.translational_velocity,
acceleration,
&item->object.translational_velocity);
if (item->item.ignore_object_index == NONE &&
TEST_FLAG(item->item.flags, _item_on_structure_bit) &&
magnitude_squared3d(acceleration) < 0.0001f)
{
struct object_marker marker;
real_vector3d rotation_axis;
real_vector3d scaled_rotation_axis;
real rotation_magnitude;
if (object_get_marker_by_name(item_index, "ground point", &marker, 1))
{
rotation_axis = marker.matrix.up;
}
else
{
rotation_axis = *global_up3d;
}
rotation_magnitude = real_random_range(-1.5707964f, 1.5707964f);
scale_vector3d(
&rotation_axis,
rotation_magnitude,
&scaled_rotation_axis);
add_vectors3d(
&item->object.angular_velocity,
&scaled_rotation_axis,
&item->object.angular_velocity);
}
else
{
real acceleration_magnitude = magnitude3d(acceleration);
real_vector3d rotation_axis;
double rotation_magnitude;
if (acceleration_magnitude < 0.0001f)
{
acceleration_magnitude =
real_seed_random(get_global_random_seed_address());
}
cross_product3d(global_up3d, acceleration, &rotation_axis);
if (!(normalize3d(&rotation_axis) > 0.f))
{
seed_random_direction3d(
get_global_random_seed_address(),
&rotation_axis);
}
rotation_magnitude =
real_seed_random(get_global_random_seed_address()) *
acceleration_magnitude * 1.5707964f;
scale_vector3d(&rotation_axis, (real)rotation_magnitude, &rotation_axis);
add_vectors3d(
&item->object.angular_velocity,
&rotation_axis,
&item->object.angular_velocity);
}
item_adjust_for_angular_velocity_change(item_index);
object_set_garbage(item_index, FALSE);
}
match_assert(
"c:\\halo\\SOURCE\\items\\items.c",
651,
global_current_collision_user_depth > 1);
--global_current_collision_user_depth;
return;
}
static void item_align_to_normal_and_point(
long item_index,
real_vector3d const *normal,
real_point3d const *position,
real_point3d *new_position)
{
struct item_datum *item = item_get(item_index);
struct object_marker marker;
real_vector3d new_forward;
real_matrix4x3 ground_point_matrix;
real scale;
match_assert("c:\\halo\\SOURCE\\items\\items.c", 713, normal);
if (!object_get_marker_by_name(item_index, "ground point", &marker, 1))
{
return;
}
if (!position)
{
position = &marker.matrix.position;
}
if (!new_position)
{
new_position = &marker.matrix.position;
}
scale = square_root(
2.f * (dot_product3d(&marker.matrix.up, normal) + 1.f));
if (scale > 0.01)
{
real_quaternion rotation;
real inverse_scale;
match_assert("c:\\halo\\SOURCE\\items\\items.c", 732, scale>0);
cross_product3d(&marker.matrix.up, normal, &rotation.v);
inverse_scale = 1.f / scale;
scale_vector3d(&rotation.v, inverse_scale, &rotation.v);
rotation.w = scale * 0.5f;
quaternion_transform_point(
&rotation,
(real_point3d const *)&marker.matrix.forward,
(real_point3d *)&new_forward);
}
else
{
real_vector3d cross;
cross_product3d(normal, &marker.matrix.forward, &cross);
cross_product3d(&cross, normal, &new_forward);
}
normalize3d(&new_forward);
matrix4x3_from_point_and_vectors(
&ground_point_matrix,
position,
&new_forward,
normal);
match_assert(
"c:\\halo\\SOURCE\\items\\items.c",
755,
valid_real_matrix4x3(&ground_point_matrix));
object_align_marker_to_matrix(
object_get(item_index),
&marker,
&ground_point_matrix);
*new_position = item->object.position;
return;
}
boolean item_update(
long item_index)
{
struct item_datum *item;
struct item_definition *definition;
item = item_get(item_index);
definition = item_definition_get(item->definition_index);
profile_enter(item_update_section);
match_assert(
"c:\\halo\\SOURCE\\items\\items.c",
174,
global_current_collision_user_depth < MAXIMUM_COLLISION_USER_STACK_DEPTH);
global_current_collision_users[global_current_collision_user_depth++] =
_collision_user_items;
if (TEST_FLAG(item->object.flags, _object_connected_to_map_bit) &&
item->object.parent_object_index == NONE)
{
if (TEST_FLAG(
definition->item.flags,
_item_definition_always_maintains_z_up_bit) &&
!(fabs(item->object.up.k - 1.f) < 0.0001f))
{
real_vector3d left;
item->object.up = *global_up3d;
cross_product3d(
&item->object.up,
&item->object.forward,
&left);
cross_product3d(
&left,
&item->object.up,
&item->object.forward);
if (normalize3d(&item->object.forward) == 0.f)
{
item->object.forward = *global_forward3d;
}
}
if (!TEST_FLAG(item->object.flags, _object_at_rest_bit))
{
struct collision_result collision;
real_vector3d new_velocity;
real_point3d new_position;
new_velocity = item->object.translational_velocity;
if (!TEST_FLAG(
definition->item.flags,
_item_definition_antigravity_bit))
{
new_velocity.k -= global_gravity;
}
new_position.x = item->object.position.x + new_velocity.i;
new_position.y = item->object.position.y + new_velocity.j;
new_position.z = item->object.position.z + new_velocity.k;
if (collision_test_line(
ITEM_UPDATE_COLLISION_TEST_FLAGS,
&item->object.position,
&new_position,
item->item.ignore_object_index,
&collision))
{
long impulse_sound_index;
real impact_scale;
point_from_line3d(
&new_position,
&collision.plane.n,
0.05f,
&new_position);
impact_scale = magnitude3d(&new_velocity);
impact_scale /= item_maximum_impact_velocity;
if (impact_scale < 0.f)
{
impact_scale = 0.f;
}
else if (impact_scale > 1.f)
{
impact_scale = 1.f;
}
if (definition->item.material_effects.index != NONE &&
material_effect_visible(&collision.point))
{
material_effect_new(
definition->item.material_effects.index,
8,
collision.material_type,
&collision.point,
&collision.plane.n,
&collision.location,
impact_scale);
}
impulse_sound_index = definition->item.collision_sound.index;
if (impulse_sound_index != NONE)
{
struct sound_location location;
location.position = new_position;
location.forward = collision.plane.n;
location.translational_velocity = *global_zero_vector3d;
location.game_location = item->object.location;
unattached_impulse_sound_new(
impulse_sound_index,
&location,
impact_scale);
}
if ((collision.type == _collision_result_structure ||
(collision.type == _collision_result_object &&
TEST_FLAG(
_object_mask_scenery | _object_mask_device,
object_get_type(collision.object_index)))) &&
collision.plane.n.k > 0.7071f &&
-dot_product3d(&collision.plane.n, &new_velocity) < 0.05f)
{
new_position = collision.point;
item_align_to_normal_and_point(
item_index,
&collision.plane.n,
&collision.point,
&new_position);
new_velocity.i = new_velocity.j = new_velocity.k = 0.f;
scale_vector3d(
&collision.plane.n,
dot_product3d(
&item->object.angular_velocity,
&collision.plane.n),
&item->object.angular_velocity);
if (!game_engine_running() &&
item->object.owner_player_index == NONE)
{
object_set_garbage(item_index, TRUE);
}
SET_FLAG(item->object.flags, _object_at_rest_bit, TRUE);
switch (collision.type)
{
case _collision_result_structure:
SET_FLAG(item->item.flags, _item_on_structure_bit, TRUE);
item_set_rested_surface(item, collision.surface_index);
item->item.bsp_index =
global_structure_bsp_index_get();
break;
case _collision_result_object:
{
real_matrix4x3 const *node_matrix;
SET_FLAG(item->item.flags, _item_on_object_bit, TRUE);
item->item.item_on_rest_object_index =
collision.object_index;
node_matrix = object_get_node_matrix(
collision.object_index,
0);
matrix4x3_inverse_transform_point(
node_matrix,
&collision.point,
&item->item.item_rest_object_offset);
break;
}
default:
match_assert(
"c:\\halo\\SOURCE\\items\\items.c",
283,
!"unreachable");
break;
}
item->item.rotation_axis = collision.plane.n;
item_adjust_for_angular_velocity_change(item_index);
item->item.ignore_object_index = NONE;
}
else
{
real reflection;
reflection = -1.4f * dot_product3d(
&collision.plane.n,
&new_velocity);
if (collision.type != _collision_result_structure)
{
reflection = MIN(1.5f, reflection);
}
new_position = collision.point;
new_velocity.i += collision.plane.n.i * reflection;
new_velocity.j += collision.plane.n.j * reflection;
new_velocity.k += collision.plane.n.k * reflection;
if (collision_test_point(
ITEM_UPDATE_COLLISION_TEST_FLAGS,
&new_position,
item_index))
{
point_from_line3d(
&collision.point,
&collision.plane.n,
0.05f,
&new_position);
}
collision_test_point(
ITEM_UPDATE_COLLISION_TEST_FLAGS,
&new_position,
item_index);
}
}
item->object.translational_velocity = new_velocity;
object_translate(
item_index,
&new_position,
&collision.location);
}
else if (!TEST_FLAG(
definition->item.flags,
_item_definition_antigravity_bit))
{
struct object_marker marker;
object_get_marker_by_name(
item_index,
"ground point",
&marker,
1);
if (TEST_FLAG(item->item.flags, _item_on_structure_bit) &&
item_rested_surface_index(item) != NONE &&
item->item.bsp_index == global_structure_bsp_index_get())
{
struct collision_surface const *surface;
surface = item_rested_surface(item);
if (!surface)
{
SET_FLAG(item->item.flags, _item_on_structure_bit, FALSE);
item->item.rested_surface_index = NONE;
}
else if (TEST_FLAG(surface->flags, _collision_surface_breakable_bit) &&
!breakable_surface_extant(
surface->breakable_surface_index))
{
real_vector3d gravity;
scale_vector3d(
global_down3d,
global_gravity,
&gravity);
SET_FLAG(item->item.flags, _item_on_structure_bit, FALSE);
item->item.rested_surface_index = NONE;
item_accelerate(
item_index,
&gravity,
FALSE);
}
}
else if (TEST_FLAG(item->item.flags, _item_on_object_bit))
{
if (object_try_and_get(item->item.item_on_rest_object_index))
{
real_matrix4x3 const *node_matrix;
real_point3d origin;
node_matrix = object_get_node_matrix(
item->item.item_on_rest_object_index,
0);
matrix4x3_transform_point(
node_matrix,
&item->item.item_rest_object_offset,
&origin);
item_align_to_normal_and_point(
item_index,
&item->item.rotation_axis,
&origin,
NULL);
}
else
{
real_vector3d gravity;
scale_vector3d(
global_down3d,
global_gravity,
&gravity);
SET_FLAG(item->item.flags, _item_on_object_bit, FALSE);
item_accelerate(
item_index,
&gravity,
FALSE);
}
}
item->object.angular_velocity.i *= 0.9f;
item->object.angular_velocity.j *= 0.9f;
item->object.angular_velocity.k *= 0.9f;
item_adjust_for_angular_velocity_change(item_index);
}
if (TEST_FLAG(
item->item.flags,
_item_has_nonzero_angular_velocity_bit))
{
struct object_marker marker;
real_vector3d left;
if (!TEST_FLAG(item->object.flags, _object_at_rest_bit) ||
!object_get_marker_by_name(
item_index,
"ground point",
&marker,
1))
{
rotate_vector_about_axis(
&item->object.forward,
&item->item.rotation_axis,
item->item.rotation_sine,
item->item.rotation_cosine);
rotate_vector_about_axis(
&item->object.up,
&item->item.rotation_axis,
item->item.rotation_sine,
item->item.rotation_cosine);
}
else
{
real_matrix4x3 ground_point_matrix;
ground_point_matrix = marker.matrix;
rotate_vector_about_axis(
&ground_point_matrix.forward,
&item->item.rotation_axis,
item->item.rotation_sine,
item->item.rotation_cosine);
rotate_vector_about_axis(
&ground_point_matrix.up,
&item->item.rotation_axis,
item->item.rotation_sine,
item->item.rotation_cosine);
cross_product3d(
&ground_point_matrix.up,
&ground_point_matrix.forward,
&ground_point_matrix.left);
cross_product3d(
&ground_point_matrix.left,
&ground_point_matrix.up,
&ground_point_matrix.forward);
normalize3d(&ground_point_matrix.forward);
normalize3d(&ground_point_matrix.left);
normalize3d(&ground_point_matrix.up);
object_align_marker_to_matrix(
object_get(item_index),
&marker,
&ground_point_matrix);
}
normalize3d(&item->object.up);
cross_product3d(
&item->object.up,
&item->object.forward,
&left);
cross_product3d(
&left,
&item->object.up,
&item->object.forward);
normalize3d(&item->object.forward);
}
}
if (item->item.detonation_ticks > 0)
{
--item->item.detonation_ticks;
if (item->item.detonation_ticks == 0)
{
effect_new_from_object(
definition->item.detonation_effect.index,
item_index,
item_index,
NONE,
0.f,
0.f,
NULL,
NULL);
object_delete(item_index);
}
}
if (TEST_FLAG(item->item.flags, _item_attached_to_unit_bit))
{
item->item.last_owned_time = game_time_get();
}
match_assert(
"c:\\halo\\SOURCE\\items\\items.c",
468,
global_current_collision_user_depth > 1);
--global_current_collision_user_depth;
profile_exit(item_update_section);
return TRUE;
}