mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-17 17:00:35 +02:00
Integrate coherent B3, P2/K17 prerequisites, completed K1-K16 + S, and the independent profile/effects rulings. Five gains and two explicitly approved B3 debits: net +3 strict functions, +2770 meaningful/+2800 padded bytes (+2774 progress credit). No additional losses; Halo objects 394 to 396 and data unchanged. Clean rebuild, identical 8252-row stable sweep, 68 valid parks, admission/provider checks, 467-unit warning comparison, unchanged fake scan, and 1318 tests passed. Preserve canonical helper recoveries and unrelated work. Full evidence and disclosures in docs/object_matching_logs/astra_five_function_reconciliation_20260928.md.
563 lines
14 KiB
C
563 lines
14 KiB
C
/*
|
|
CAMERA_SCRIPTING.C
|
|
|
|
symbols in this file:
|
|
00073650 0020:
|
|
_scripted_camera_enable (0000)
|
|
00073670 00d0:
|
|
_scripted_camera_set_animation (0000)
|
|
00073740 0040:
|
|
_scripted_camera_set_first_person (0000)
|
|
00073780 0040:
|
|
_scripted_camera_set_dead (0000)
|
|
000737C0 0030:
|
|
_scripted_camera_object_is_first_person_camera (0000)
|
|
000737F0 00e0:
|
|
_scripted_camera_set (0000)
|
|
000738D0 0020:
|
|
_scripted_camera_set_absolute (0000)
|
|
000738F0 00d0:
|
|
_scripted_camera_set_camera_point_relative (0000)
|
|
000739C0 0030:
|
|
_scripted_camera_set_camera_point_absolute (0000)
|
|
000739F0 0010:
|
|
_scripted_camera_next_camera_point (0000)
|
|
00073A00 0010:
|
|
_scripted_camera_object_relative_to (0000)
|
|
00073A10 0020:
|
|
_scripted_camera_time (0000)
|
|
00073A30 0680:
|
|
_scripted_camera_update (0000)
|
|
00256A7C 003d:
|
|
??_C@_0DN@JNLEKBID@cannot?5set?5first?5person?5camera?5o@ (0000)
|
|
00256ABC 0029:
|
|
??_C@_0CJ@GLCFCHKA@c?3?2halo?2SOURCE?2camera?2camera_scr@ (0000)
|
|
002DCB60 0040:
|
|
_camera_script_globals (0000)
|
|
*/
|
|
|
|
/* ---------- headers */
|
|
|
|
#include "camera_scripting.h"
|
|
#include "director.h"
|
|
|
|
#include "cseries/cseries.h"
|
|
#include "cseries/errors.h"
|
|
#include "game/game.h"
|
|
#include "math/real_math.h"
|
|
#include "models/model_animation_definitions.h"
|
|
#include "objects/objects.h"
|
|
#include "scenario/scenario.h"
|
|
#include "scenario/scenario_definitions.h"
|
|
#include "tag_files/tag_groups.h"
|
|
|
|
/* ---------- constants */
|
|
|
|
enum
|
|
{
|
|
_camera_script_mode_point = 0,
|
|
_camera_script_mode_animation,
|
|
_camera_script_mode_first_person,
|
|
_camera_script_mode_dead,
|
|
NUMBER_OF_CAMERA_SCRIPT_MODES,
|
|
};
|
|
|
|
/* ---------- macros */
|
|
|
|
/* ---------- structures */
|
|
|
|
struct camera_control
|
|
{
|
|
long unknown0;
|
|
real seconds_elapsed;
|
|
};
|
|
|
|
struct dead_camera
|
|
{
|
|
real_point3d position;
|
|
real_euler_angles2d facing;
|
|
real distance;
|
|
real field_of_view;
|
|
real timer;
|
|
long player_index;
|
|
long current_player_index;
|
|
long unit_index;
|
|
real switch_timer;
|
|
};
|
|
|
|
struct scripted_camera_command
|
|
{
|
|
long flags;
|
|
real_point3d position;
|
|
real_vector3d offset;
|
|
real depth;
|
|
real field_of_view;
|
|
real_vector3d forward;
|
|
real_vector3d up;
|
|
real_vector3d velocity;
|
|
real timer;
|
|
byte position_flags;
|
|
byte offset_flags;
|
|
byte distance_flags;
|
|
byte field_of_view_flags;
|
|
byte orientation_flags;
|
|
byte pad51[3];
|
|
real position_timer;
|
|
real offset_timer;
|
|
real distance_timer;
|
|
real field_of_view_timer;
|
|
real orientation_timer;
|
|
};
|
|
|
|
struct scripted_camera_globals
|
|
{
|
|
boolean enabled;
|
|
boolean first_update;
|
|
short mode;
|
|
short camera_point_index;
|
|
byte pad06[2];
|
|
real timer;
|
|
real_point3d point;
|
|
real_vector3d forward;
|
|
real_vector3d up;
|
|
real field_of_view;
|
|
long relative_object_index;
|
|
long animation_graph_index;
|
|
short animation_index;
|
|
};
|
|
|
|
typedef char scripted_camera_command_size_assert[
|
|
sizeof(struct scripted_camera_command) == 0x68 ? 1 : -1];
|
|
typedef char scripted_camera_globals_size_assert[
|
|
sizeof(struct scripted_camera_globals) == 0x40 ? 1 : -1];
|
|
|
|
/* ---------- prototypes */
|
|
|
|
void scripted_camera_set(
|
|
short camera_point_index,
|
|
word transition_time,
|
|
long relative_object_index);
|
|
void first_person_camera_fake(
|
|
long unit_index,
|
|
struct scripted_camera_command *result);
|
|
void dead_camera_new(
|
|
struct dead_camera *camera,
|
|
short local_player_index,
|
|
long unit_index);
|
|
void dead_camera_update(
|
|
struct dead_camera *camera,
|
|
struct camera_control const *controls,
|
|
struct scripted_camera_command *result);
|
|
|
|
/* ---------- globals */
|
|
|
|
/* name from the 2003 PC demo PDB and the HCEX PDB (camera_scripting file static); January's 64 bytes are
|
|
* identical to the demo's and it has no public for it (static) */
|
|
static struct scripted_camera_globals camera_script_globals =
|
|
{
|
|
FALSE,
|
|
FALSE,
|
|
NONE,
|
|
NONE,
|
|
{ 0, 0 },
|
|
0.f,
|
|
{ { 0.f, 0.f, 0.f } },
|
|
{ { 0.f, 0.f, 1.f } },
|
|
{ { 0.f, 1.f, 0.f } },
|
|
1.22173047f,
|
|
NONE,
|
|
NONE,
|
|
0
|
|
};
|
|
|
|
/* ---------- public code */
|
|
|
|
void scripted_camera_enable(
|
|
boolean enabled)
|
|
{
|
|
camera_script_globals.enabled = enabled;
|
|
camera_script_globals.first_update = TRUE;
|
|
return;
|
|
}
|
|
|
|
void scripted_camera_set_animation(
|
|
long animation_graph_index,
|
|
char const *animation_name)
|
|
{
|
|
struct animation_graph *animation_graph;
|
|
struct animation *animation;
|
|
short animation_index;
|
|
|
|
if (animation_graph_index != NONE)
|
|
{
|
|
animation_graph = animation_graph_definition_get(animation_graph_index);
|
|
if (animation_graph->nodes.count == 1)
|
|
{
|
|
animation_index = 0;
|
|
while (animation_index < animation_graph->animations.count)
|
|
{
|
|
animation = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
animation_index,
|
|
struct animation);
|
|
if (!_stricmp(animation_name, animation->name))
|
|
{
|
|
camera_script_globals.camera_point_index = NONE;
|
|
camera_script_globals.relative_object_index = NONE;
|
|
camera_script_globals.mode = _camera_script_mode_animation;
|
|
camera_script_globals.first_update = TRUE;
|
|
camera_script_globals.animation_index = animation_index;
|
|
camera_script_globals.animation_graph_index = animation_graph_index;
|
|
camera_script_globals.field_of_view = 1.22173047f;
|
|
camera_script_globals.timer =
|
|
(real)(animation->frame_count / TICKS_PER_SECOND);
|
|
break;
|
|
}
|
|
|
|
animation_index++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void scripted_camera_set_first_person(
|
|
long unit_index)
|
|
{
|
|
if (unit_index != NONE)
|
|
{
|
|
camera_script_globals.mode = _camera_script_mode_first_person;
|
|
camera_script_globals.first_update = TRUE;
|
|
camera_script_globals.relative_object_index = unit_index;
|
|
}
|
|
else
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"cannot set first person camera on a unit that doesn't exist.");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void scripted_camera_set_dead(
|
|
long unit_index)
|
|
{
|
|
if (unit_index != NONE)
|
|
{
|
|
camera_script_globals.mode = _camera_script_mode_dead;
|
|
camera_script_globals.first_update = TRUE;
|
|
camera_script_globals.relative_object_index = unit_index;
|
|
}
|
|
else
|
|
{
|
|
error(
|
|
_error_silent,
|
|
"cannot set first person camera on a unit that doesn't exist.");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
boolean scripted_camera_object_is_first_person_camera(
|
|
long object_index)
|
|
{
|
|
return camera_script_globals.enabled &&
|
|
camera_script_globals.mode == _camera_script_mode_first_person &&
|
|
camera_script_globals.relative_object_index == object_index;
|
|
}
|
|
|
|
void scripted_camera_set(
|
|
short camera_point_index,
|
|
word transition_time,
|
|
long relative_object_index)
|
|
{
|
|
struct scenario_cutscene_camera_point *camera_point;
|
|
long camera_time;
|
|
|
|
camera_point = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->cutscene_camera_points,
|
|
camera_point_index,
|
|
struct scenario_cutscene_camera_point);
|
|
camera_time = (short)transition_time / TICKS_PER_SECOND;
|
|
camera_script_globals.mode = _camera_script_mode_point;
|
|
camera_script_globals.first_update = TRUE;
|
|
camera_script_globals.camera_point_index = camera_point_index;
|
|
camera_script_globals.point = camera_point->position;
|
|
vectors3d_from_euler_angles3d(
|
|
&camera_script_globals.forward,
|
|
&camera_script_globals.up,
|
|
&camera_point->orientation);
|
|
if (camera_point->field_of_view != 0.f)
|
|
camera_script_globals.field_of_view = camera_point->field_of_view;
|
|
else
|
|
camera_script_globals.field_of_view = 1.22173047f;
|
|
camera_script_globals.relative_object_index = relative_object_index;
|
|
camera_script_globals.timer = (real)camera_time;
|
|
|
|
director_update(0.f);
|
|
observer_update(0.0001f);
|
|
return;
|
|
}
|
|
|
|
void scripted_camera_set_absolute(
|
|
short camera_point_index,
|
|
word transition_time)
|
|
{
|
|
scripted_camera_set(camera_point_index, transition_time, NONE);
|
|
return;
|
|
}
|
|
|
|
void scripted_camera_set_camera_point_relative(
|
|
real_point3d const *position,
|
|
real_vector3d const *forward,
|
|
real_vector3d const *up,
|
|
real field_of_view,
|
|
word transition_time,
|
|
long relative_object_index)
|
|
{
|
|
camera_script_globals.mode = _camera_script_mode_point;
|
|
camera_script_globals.camera_point_index = NONE;
|
|
camera_script_globals.point = *position;
|
|
camera_script_globals.forward = *forward;
|
|
camera_script_globals.up = *up;
|
|
if (field_of_view != 0.f)
|
|
camera_script_globals.field_of_view = field_of_view;
|
|
else
|
|
camera_script_globals.field_of_view = 1.22173047f;
|
|
camera_script_globals.timer =
|
|
(real)((short)transition_time / TICKS_PER_SECOND);
|
|
camera_script_globals.relative_object_index = relative_object_index;
|
|
|
|
director_update(0.f);
|
|
observer_update(0.0001f);
|
|
return;
|
|
}
|
|
|
|
void scripted_camera_set_camera_point_absolute(
|
|
real_point3d const *position,
|
|
real_vector3d const *forward,
|
|
real_vector3d const *up,
|
|
real field_of_view,
|
|
word transition_time)
|
|
{
|
|
scripted_camera_set_camera_point_relative(
|
|
position,
|
|
forward,
|
|
up,
|
|
field_of_view,
|
|
transition_time,
|
|
NONE);
|
|
return;
|
|
}
|
|
|
|
short scripted_camera_next_camera_point(
|
|
void)
|
|
{
|
|
return camera_script_globals.camera_point_index;
|
|
}
|
|
|
|
long scripted_camera_object_relative_to(
|
|
void)
|
|
{
|
|
return camera_script_globals.relative_object_index;
|
|
}
|
|
|
|
short scripted_camera_time(
|
|
void)
|
|
{
|
|
return (short)(camera_script_globals.timer * 30.f);
|
|
}
|
|
|
|
void scripted_camera_update(
|
|
struct dead_camera *camera,
|
|
struct camera_control const *controls,
|
|
struct scripted_camera_command *result)
|
|
{
|
|
real_point3d focus_position;
|
|
real speed;
|
|
|
|
focus_position = *global_origin3d;
|
|
speed = game_time_get_speed();
|
|
result->flags = FLAG(3);
|
|
if (game_time_get_paused())
|
|
{
|
|
result->flags |= FLAG(5);
|
|
}
|
|
else
|
|
{
|
|
result->flags &= ~FLAG(5);
|
|
}
|
|
|
|
switch (camera_script_globals.mode)
|
|
{
|
|
case _camera_script_mode_point:
|
|
if (camera_script_globals.relative_object_index != NONE)
|
|
{
|
|
struct object_datum *object;
|
|
|
|
object = object_try_and_get_and_verify_type(
|
|
camera_script_globals.relative_object_index,
|
|
_object_mask_all);
|
|
if (!object)
|
|
{
|
|
break;
|
|
}
|
|
focus_position = object->object.bounding_sphere_center;
|
|
}
|
|
|
|
result->timer = speed != 0.f
|
|
? camera_script_globals.timer / speed
|
|
: 0.f;
|
|
result->field_of_view = camera_script_globals.field_of_view;
|
|
result->forward = camera_script_globals.forward;
|
|
result->up = camera_script_globals.up;
|
|
if (camera_script_globals.relative_object_index != NONE)
|
|
{
|
|
real angle;
|
|
real cosine_value;
|
|
real dot;
|
|
real rotated_offset_i;
|
|
real sine_value;
|
|
real_vector3d offset;
|
|
|
|
angle = arctangent(result->forward.j, result->forward.i);
|
|
dot = dot_product3d(
|
|
(real_vector3d const *)&camera_script_globals.point,
|
|
&result->forward);
|
|
if (dot > 0.f)
|
|
{
|
|
dot = 0.f;
|
|
}
|
|
|
|
result->depth = -dot;
|
|
result->position = focus_position;
|
|
offset.i = camera_script_globals.point.x - dot * result->forward.i;
|
|
offset.j = camera_script_globals.point.y - dot * result->forward.j;
|
|
offset.k = camera_script_globals.point.z - dot * result->forward.k;
|
|
result->position_timer = 0.f;
|
|
result->position_flags = 1;
|
|
sine_value = sine(angle);
|
|
cosine_value = cosine(angle);
|
|
rotated_offset_i = offset.i * cosine_value;
|
|
rotated_offset_i += sine_value * offset.j;
|
|
result->offset.i = rotated_offset_i;
|
|
result->offset.j = sine_value * offset.i - cosine_value * offset.j;
|
|
result->offset.k = offset.k;
|
|
result->flags |= FLAG(0);
|
|
}
|
|
else
|
|
{
|
|
result->position = camera_script_globals.point;
|
|
result->flags |= FLAG(0);
|
|
}
|
|
break;
|
|
|
|
case _camera_script_mode_animation:
|
|
{
|
|
struct animation_graph *animation_graph;
|
|
struct animation *animation;
|
|
short frame;
|
|
long frame_count;
|
|
real_matrix4x3 root_matrix;
|
|
|
|
animation_graph = animation_graph_definition_get(
|
|
camera_script_globals.animation_graph_index);
|
|
animation = TAG_BLOCK_GET_ELEMENT(
|
|
&animation_graph->animations,
|
|
camera_script_globals.animation_index,
|
|
struct animation);
|
|
frame_count = animation->frame_count;
|
|
frame = (short)(frame_count - camera_script_globals.timer * 30.f);
|
|
animation_get_root_matrix(
|
|
NULL,
|
|
animation,
|
|
frame < 0 ? 0 : MIN(frame, frame_count - 1),
|
|
&root_matrix);
|
|
result->forward = root_matrix.forward;
|
|
result->up = root_matrix.up;
|
|
result->field_of_view = DEGREES_TO_RADIANS(70.f);
|
|
result->position = root_matrix.position;
|
|
result->depth = 0.f;
|
|
result->timer = 0.f;
|
|
result->flags |= FLAG(0);
|
|
}
|
|
break;
|
|
|
|
case _camera_script_mode_first_person:
|
|
if (object_try_and_get_and_verify_type(
|
|
camera_script_globals.relative_object_index,
|
|
_object_mask_unit))
|
|
{
|
|
first_person_camera_fake(
|
|
camera_script_globals.relative_object_index,
|
|
result);
|
|
}
|
|
break;
|
|
|
|
case _camera_script_mode_dead:
|
|
if (object_try_and_get_and_verify_type(
|
|
camera_script_globals.relative_object_index,
|
|
_object_mask_unit))
|
|
{
|
|
if (camera_script_globals.first_update)
|
|
{
|
|
dead_camera_new(
|
|
camera,
|
|
(short)controls->unknown0,
|
|
camera_script_globals.relative_object_index);
|
|
}
|
|
dead_camera_update(camera, controls, result);
|
|
}
|
|
break;
|
|
}
|
|
|
|
camera_script_globals.timer = MAX(
|
|
0.f,
|
|
camera_script_globals.timer - speed * controls->seconds_elapsed);
|
|
camera_script_globals.first_update = FALSE;
|
|
|
|
match_vassert(
|
|
"c:\\halo\\SOURCE\\camera\\camera_scripting.c",
|
|
0x172,
|
|
!(result->flags & FLAG(0)) ||
|
|
(valid_real_vector3d_axes2(&result->forward, &result->up) &&
|
|
valid_real(result->position.x) && result->position.x>=-5000.f && result->position.x<=5000.f &&
|
|
valid_real(result->position.y) && result->position.y>=-5000.f && result->position.y<=5000.f &&
|
|
valid_real(result->position.z) && result->position.z>=-5000.f && result->position.z<=5000.f &&
|
|
valid_real(result->offset.i) && result->offset.i>=-5000.f && result->offset.i<=5000.f &&
|
|
valid_real(result->offset.j) && result->offset.j>=-5000.f && result->offset.j<=5000.f &&
|
|
valid_real(result->offset.k) && result->offset.k>=-5000.f && result->offset.k<=5000.f &&
|
|
valid_real_vector3d(&result->velocity) &&
|
|
valid_real(result->depth) && result->depth>=0.f && result->depth<=5000.f &&
|
|
valid_real(result->field_of_view) && result->field_of_view>=0.001f && result->field_of_view<=_pi / 2.f &&
|
|
valid_real(result->timer) && result->timer>=0.f && result->timer<=3600.f),
|
|
csprintf(
|
|
temporary,
|
|
"Invalid camera command.\nF: (%f, %f, %f) U: (%f, %f, %f)\nP: (%f, %f, %f) O: (%f, %f, %f)\nD: %f V: (%f, %f, %f), FOV: %f, T: %f, FL: %ld",
|
|
result->forward.i,
|
|
result->forward.j,
|
|
result->forward.k,
|
|
result->up.i,
|
|
result->up.j,
|
|
result->up.k,
|
|
result->position.x,
|
|
result->position.y,
|
|
result->position.z,
|
|
result->offset.i,
|
|
result->offset.j,
|
|
result->offset.k,
|
|
result->depth,
|
|
result->velocity.i,
|
|
result->velocity.j,
|
|
result->velocity.k,
|
|
result->field_of_view,
|
|
result->timer,
|
|
result->flags));
|
|
|
|
return;
|
|
}
|
|
|
|
/* ---------- private code */
|