mirror of
https://github.com/thug1src/thug.git
synced 2025-01-22 05:43:47 +00:00
1451 lines
51 KiB
C++
1451 lines
51 KiB
C++
//****************************************************************************
|
|
//* MODULE: Gel/Components
|
|
//* FILENAME: WalkHangUtil.cpp
|
|
//* OWNER: Dan
|
|
//* CREATION DATE: 4/2/3
|
|
//****************************************************************************
|
|
|
|
#include <gel/components/walkcomponent.h>
|
|
#include <gel/components/inputcomponent.h>
|
|
#include <gel/components/animationcomponent.h>
|
|
#include <gel/components/walkcameracomponent.h>
|
|
#include <gel/components/modelcomponent.h>
|
|
#include <gel/components/triggercomponent.h>
|
|
#include <gel/components/movablecontactcomponent.h>
|
|
#include <gel/components/railmanagercomponent.h>
|
|
|
|
#include <gel/object/compositeobject.h>
|
|
#include <gel/object/compositeobjectmanager.h>
|
|
#include <gel/scripting/checksum.h>
|
|
#include <gel/scripting/script.h>
|
|
#include <gel/scripting/struct.h>
|
|
#include <gel/scripting/symboltable.h>
|
|
#include <gel/collision/collcache.h>
|
|
|
|
#include <sk/objects/rail.h>
|
|
#include <sk/engine/feeler.h>
|
|
#include <sk/modules/skate/skate.h>
|
|
#include <sk/components/skatercorephysicscomponent.h>
|
|
|
|
#include <gfx/nxmodel.h>
|
|
|
|
/*
|
|
* Contains those CWalkComponent member functions which are concerned with hanging.
|
|
*/
|
|
|
|
namespace Obj
|
|
{
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
/*
|
|
bool CWalkComponent::maybe_hop_to_hang ( )
|
|
{
|
|
SHangRailData rail_data;
|
|
|
|
Mth::Vector search_start_pos = m_pos;
|
|
search_start_pos[Y] += s_get_param(CRCD(0x2c942693, "lowest_hang_height"));
|
|
|
|
// clean vectors; facing is a directional vector
|
|
search_start_pos[W] = 1.0f;
|
|
m_facing[W] = 0.0f;
|
|
|
|
bool rail_found = false;
|
|
|
|
float hop_to_hang_reach = s_get_param(CRCD(0x8b785f26, "max_hop_to_hang_height")) - s_get_param(CRCD(0x2c942693, "lowest_hang_height"));
|
|
float max_hop_to_hang_forward_reach = s_get_param(CRCD(0x3b4ca389, "max_hop_to_hang_forward_reach"));
|
|
float max_hop_to_hang_backward_reach = s_get_param(CRCD(0xae806bee, "max_hop_to_hang_backward_reach"));
|
|
|
|
// check level for appropriate rails
|
|
if (rail_found = Mdl::Skate::Instance()->GetRailManager()->CheckForHopToHangRail(
|
|
search_start_pos,
|
|
hop_to_hang_reach,
|
|
m_facing,
|
|
max_hop_to_hang_forward_reach,
|
|
max_hop_to_hang_backward_reach,
|
|
this,
|
|
rail_data,
|
|
&mp_rail_node
|
|
))
|
|
{
|
|
mp_rail_manager = Mdl::Skate::Instance()->GetRailManager();
|
|
mp_movable_contact_component->LoseAnyContact();
|
|
}
|
|
else
|
|
{
|
|
float max_hop_to_movable_hang_vel_sqr = s_get_param(CRCD(0x511cb4, "max_hop_to_movable_hang_vel"));
|
|
max_hop_to_movable_hang_vel_sqr *= max_hop_to_movable_hang_vel_sqr;
|
|
|
|
// check moving objects for appropriate rails
|
|
for (CRailManagerComponent* p_rail_manager_component = static_cast< CRailManagerComponent* >(CCompositeObjectManager::Instance()->GetFirstComponentByType(CRC_RAILMANAGER));
|
|
p_rail_manager_component && !rail_found;
|
|
p_rail_manager_component = static_cast< CRailManagerComponent* >(p_rail_manager_component->GetNextSameType()))
|
|
{
|
|
// only hop to moving hangs below a threshold velocity
|
|
if (p_rail_manager_component->GetObject()->GetVel().LengthSqr() > max_hop_to_movable_hang_vel_sqr) continue;
|
|
|
|
Mth::Matrix obj_matrix_inv = p_rail_manager_component->UpdateRailManager();
|
|
obj_matrix_inv.Invert();
|
|
|
|
// transform into the frame of the moving object
|
|
Mth::Vector obj_frame_search_start_pos = obj_matrix_inv.Transform(search_start_pos);
|
|
Mth::Vector obj_frame_facing = obj_matrix_inv.Transform(m_facing);
|
|
|
|
if (rail_found = p_rail_manager_component->GetRailManager()->CheckForHopToHangRail(
|
|
obj_frame_search_start_pos,
|
|
hop_to_hang_reach,
|
|
obj_frame_facing,
|
|
max_hop_to_hang_forward_reach,
|
|
max_hop_to_hang_backward_reach,
|
|
this,
|
|
rail_data,
|
|
&mp_rail_node
|
|
))
|
|
{
|
|
mp_rail_manager = p_rail_manager_component->GetRailManager();
|
|
mp_movable_contact_component->ObtainContact(p_rail_manager_component->GetObject());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!rail_found) return false;
|
|
|
|
// we've found a rail to hop up to
|
|
m_goal_hop_pos = rail_data.hang_point;
|
|
m_goal_hop_facing = rail_data.hang_facing;
|
|
m_along_rail_factor = rail_data.along_rail_factor;
|
|
m_vertical_hang_offset = rail_data.vertical_hang_offset;
|
|
m_horizontal_hang_offset = rail_data.horizontal_hang_offset;
|
|
m_initial_hang_animation = rail_data.initial_animation;
|
|
|
|
// calculate the vertical velocity required to jump to the hang point
|
|
#ifdef __NOPT_ASSERT__
|
|
if (m_pos[Y] >= m_goal_hop_pos[Y])
|
|
{
|
|
DUMPV(m_pos);
|
|
DUMPV(m_goal_hop_pos);
|
|
}
|
|
Dbg_Assert(m_pos[Y] < m_goal_hop_pos[Y]);
|
|
#endif
|
|
|
|
float hop_target_vert_vel = s_get_param(CRCD(0x7aa618cc, "hop_target_vert_vel"));
|
|
float gravity = s_get_param(CRCD(0xa5e2da58, "gravity"));
|
|
|
|
// ready state for the hop to hang
|
|
|
|
// calculate the required hop velocity
|
|
m_vertical_vel = sqrtf(Mth::Sqr(hop_target_vert_vel) + 2.0f * gravity * (m_goal_hop_pos[Y] - m_pos[Y]));
|
|
|
|
// calculate the required hop duration
|
|
float duration = (m_vertical_vel + hop_target_vert_vel) / gravity;
|
|
|
|
// not neccesarly exactly in the check_direction of the facing
|
|
m_horizontal_vel = (1.0f / duration) * (m_goal_hop_pos - m_pos);
|
|
|
|
// trip the ground's jump triggers
|
|
if (m_last_ground_feeler_valid)
|
|
{
|
|
mp_trigger_component->CheckFeelerForTrigger(TRIGGER_JUMP_OFF, m_last_ground_feeler);
|
|
if (should_bail_from_frame()) return true;
|
|
}
|
|
|
|
set_state(WALKING_HOP);
|
|
|
|
m_frame_event = CRCD(0xf41aba21, "Hop");
|
|
|
|
return true;
|
|
}
|
|
*/
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CWalkComponent::maybe_drop_to_hang ( )
|
|
{
|
|
// check to see if we're in a bail
|
|
if (mp_core_physics_component->GetFlag(IS_BAILING)) return false;
|
|
|
|
// only drop to hang when moving slowly
|
|
if (m_horizontal_vel.LengthSqr() > Mth::Sqr(s_get_param(CRCD(0x1d1e6af, "drop_to_hang_speed_factor")) * get_run_speed())) return false;
|
|
|
|
// clean vector
|
|
m_frame_start_pos[W] = 1.0f;
|
|
m_pos[W] = 1.0f;
|
|
|
|
SHangRailData rail_data;
|
|
|
|
bool rail_found = false;
|
|
|
|
// check level for appropriate rails
|
|
mp_rail_manager = Mdl::Skate::Instance()->GetRailManager();
|
|
if (rail_found = mp_rail_manager->CheckForHangRail(
|
|
m_frame_start_pos,
|
|
m_pos,
|
|
-m_facing,
|
|
this,
|
|
rail_data,
|
|
Script::GetFloat(CRCD(0xa8276303, "Drop_To_Climb_Max_Snap"))
|
|
))
|
|
{
|
|
mp_movable_contact_component->LoseAnyContact();
|
|
}
|
|
else
|
|
{
|
|
// check moving objects for appropriate rails
|
|
for (CRailManagerComponent* p_rail_manager_component = static_cast< CRailManagerComponent* >(CCompositeObjectManager::Instance()->GetFirstComponentByType(CRC_RAILMANAGER));
|
|
p_rail_manager_component && !rail_found;
|
|
p_rail_manager_component = static_cast< CRailManagerComponent* >(p_rail_manager_component->GetNextSameType()))
|
|
{
|
|
Mth::Matrix obj_matrix_inv = p_rail_manager_component->UpdateRailManager();
|
|
obj_matrix_inv.Invert();
|
|
|
|
// transform into the frame of the moving object
|
|
Mth::Vector obj_frame_start_pos = obj_matrix_inv.Transform(m_frame_start_pos);
|
|
Mth::Vector obj_pos = obj_matrix_inv.Transform(m_pos);
|
|
|
|
mp_rail_manager = p_rail_manager_component->GetRailManager();
|
|
if (rail_found = mp_rail_manager->CheckForHangRail(
|
|
obj_frame_start_pos,
|
|
obj_pos,
|
|
-m_facing,
|
|
this,
|
|
rail_data,
|
|
Script::GetFloat(CRCD(0xa8276303, "Drop_To_Climb_Max_Snap"))
|
|
))
|
|
{
|
|
mp_movable_contact_component->ObtainContact(p_rail_manager_component->GetObject());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!rail_found) return false;
|
|
|
|
// we've found a rail to drop down to
|
|
|
|
mp_rail_start = rail_data.p_rail_node;
|
|
mp_rail_end = mp_rail_start->GetNextLink();
|
|
Dbg_Assert(mp_rail_end);
|
|
|
|
// zero velocities
|
|
m_vertical_vel = 0.0f;
|
|
m_horizontal_vel.Set();
|
|
|
|
m_along_rail_factor = rail_data.along_rail_factor;
|
|
m_vertical_hang_offset = rail_data.vertical_hang_offset;
|
|
m_horizontal_hang_offset = rail_data.horizontal_hang_offset;
|
|
|
|
// setup an anim wait state
|
|
|
|
m_anim_wait_initial_pos = m_pos;
|
|
m_anim_wait_goal_pos = rail_data.hang_point;
|
|
|
|
mp_anim_wait_complete_callback = &Obj::CWalkComponent::drop_to_hang_complete;
|
|
|
|
calculate_anim_wait_facing_drift_parameters(rail_data.hang_facing, s_get_param(CRCD(0xfdc8aec0, "drop_to_hang_rotate_factor")));
|
|
|
|
m_anim_wait_camera_mode = AWC_GOAL;
|
|
|
|
m_critical_point_offset = s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * rail_data.hang_facing;
|
|
|
|
// setup the false wall incase the player jumps out of the animation wait
|
|
m_false_wall.normal = rail_data.hang_facing;
|
|
m_false_wall.cancel_height = m_pos[Y];
|
|
|
|
m_drift_initial_display_offset = m_display_offset;
|
|
m_drift_goal_display_offset = get_hang_display_offset();
|
|
|
|
set_state(WALKING_ANIMWAIT);
|
|
|
|
m_frame_event = CRCD(0xeb71d98e, "DropToHang");
|
|
|
|
return true;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CWalkComponent::drop_to_hang_complete ( )
|
|
{
|
|
m_critical_point_offset = s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * m_facing;
|
|
m_critical_point_offset[Y] = get_hang_critical_point_vert_offset();
|
|
|
|
set_state(WALKING_HANG);
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CWalkComponent::maybe_grab_to_hang ( Mth::Vector start_pos, Mth::Vector end_pos )
|
|
{
|
|
// only grab to hang when moving down
|
|
if (m_vertical_vel > 50.0f) return false;
|
|
|
|
start_pos[Y] += s_get_param(CRCD(0xa8c13e74, "hang_vert_origin_offset"));
|
|
end_pos[Y] += s_get_param(CRCD(0xa8c13e74, "hang_vert_origin_offset"));
|
|
|
|
// clean vectors
|
|
start_pos[W] = 1.0f;
|
|
end_pos[W] = 1.0f;
|
|
|
|
SHangRailData rail_data;
|
|
|
|
bool rail_found = false;
|
|
|
|
// check level for appropriate rails
|
|
mp_rail_manager = Mdl::Skate::Instance()->GetRailManager();
|
|
if (rail_found = mp_rail_manager->CheckForHangRail(
|
|
start_pos,
|
|
end_pos,
|
|
m_facing,
|
|
this,
|
|
rail_data,
|
|
Script::GetFloat(CRCD(0x30ce7f2c, "Climb_Max_Snap"))
|
|
))
|
|
{
|
|
mp_movable_contact_component->LoseAnyContact();
|
|
}
|
|
else
|
|
{
|
|
// check moving objects for appropriate rails
|
|
for (CRailManagerComponent* p_rail_manager_component = static_cast< CRailManagerComponent* >(CCompositeObjectManager::Instance()->GetFirstComponentByType(CRC_RAILMANAGER));
|
|
p_rail_manager_component && !rail_found;
|
|
p_rail_manager_component = static_cast< CRailManagerComponent* >(p_rail_manager_component->GetNextSameType()))
|
|
{
|
|
Mth::Matrix obj_matrix_inv = p_rail_manager_component->UpdateRailManager();
|
|
obj_matrix_inv.Invert();
|
|
|
|
// transform into the frame of the moving object
|
|
Mth::Vector obj_frame_start_pos = obj_matrix_inv.Transform(start_pos);
|
|
Mth::Vector obj_frame_end_pos = obj_matrix_inv.Transform(end_pos);
|
|
|
|
mp_rail_manager = p_rail_manager_component->GetRailManager();
|
|
if (rail_found = mp_rail_manager->CheckForHangRail(
|
|
obj_frame_start_pos,
|
|
obj_frame_end_pos,
|
|
m_facing,
|
|
this,
|
|
rail_data,
|
|
Script::GetFloat(CRCD(0x30ce7f2c, "Climb_Max_Snap"))
|
|
))
|
|
{
|
|
mp_movable_contact_component->ObtainContact(p_rail_manager_component->GetObject());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!rail_found) return false;
|
|
|
|
// we've found a rail to hang onto
|
|
|
|
mp_rail_start = rail_data.p_rail_node;
|
|
mp_rail_end = mp_rail_start->GetNextLink();
|
|
Dbg_Assert(mp_rail_end);
|
|
|
|
m_pos = rail_data.hang_point;
|
|
DUMP_WPOSITION
|
|
m_facing = rail_data.hang_facing;
|
|
m_along_rail_factor = rail_data.along_rail_factor;
|
|
m_vertical_hang_offset = rail_data.vertical_hang_offset;
|
|
m_horizontal_hang_offset = rail_data.horizontal_hang_offset;
|
|
m_initial_hang_animation = rail_data.initial_animation;
|
|
|
|
m_critical_point_offset = s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * m_facing;
|
|
m_critical_point_offset[Y] = get_hang_critical_point_vert_offset();
|
|
|
|
m_horizontal_vel.Set();
|
|
m_vertical_vel = 0.0f;
|
|
|
|
m_display_offset = get_hang_display_offset();
|
|
|
|
set_state(WALKING_HANG);
|
|
|
|
m_frame_event = CRCD(0x4194ecca, "Hang");
|
|
|
|
mp_core_physics_component->SetFlagTrue(SNAPPED);
|
|
|
|
return true;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CWalkComponent::FilterHangRail ( const Mth::Vector& pos_a, const Mth::Vector& pos_b, const Mth::Vector& preliminary_hang_point, const Mth::Vector& grab_from_point, float along_rail_factor, SHangRailData& rail_data, bool drop_to_hang )
|
|
{
|
|
// check this rail for potential hang use
|
|
|
|
float hang_vert_origin_offset = s_get_param(CRCD(0xa8c13e74, "hang_vert_origin_offset"));
|
|
|
|
// never snap upward to a hang rail
|
|
if (m_state == WALKING_AIR && preliminary_hang_point[Y] > grab_from_point[Y]) return false;
|
|
|
|
// never snap too far vertically to a hang rail
|
|
if (m_state == WALKING_AIR && Mth::Abs(preliminary_hang_point[Y] - grab_from_point[Y]) > 12.0f) return false;
|
|
|
|
// is the rail too steep
|
|
Mth::Vector rail = pos_b - pos_a;
|
|
Mth::Vector rail_direction = rail;
|
|
rail_direction.Normalize();
|
|
if (Mth::Abs(rail_direction[Y]) > sinf(Mth::DegToRad(s_get_param(CRCD(0x98724ebc, "hang_max_rail_ascent"))))) return false;
|
|
|
|
// calculate the facing we'd have while hanging
|
|
|
|
rail_data.hang_facing.Set(-(rail[Z]), 0.0f, rail[X]);
|
|
rail_data.hang_facing.Normalize();
|
|
if (!drop_to_hang)
|
|
{
|
|
// normally, we attempt retain our current facing
|
|
if (Mth::DotProduct(rail_data.hang_facing, m_facing) < 0.0f)
|
|
{
|
|
rail_data.hang_facing.Negate();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// unless dropping to a hang
|
|
if (Mth::DotProduct(rail_data.hang_facing, m_facing) > 0.0f)
|
|
{
|
|
rail_data.hang_facing.Negate();
|
|
}
|
|
}
|
|
|
|
bool rail_points_left = rail_data.hang_facing[Z] * rail_direction[X] - rail_data.hang_facing[X] * rail_direction[Z] > 0.0f;
|
|
if ((rail_data.p_rail_node->GetFlag(NO_HANG_WITH_LEFT_ALONG_RAIL) && rail_points_left)
|
|
|| (rail_data.p_rail_node->GetFlag(NO_HANG_WITH_RIGHT_ALONG_RAIL) && !rail_points_left))
|
|
{
|
|
rail_data.hang_facing.Negate();
|
|
rail_points_left = !rail_points_left;
|
|
}
|
|
|
|
// investigate the hang point to see if its a good hang
|
|
|
|
CFeeler feeler;
|
|
Nx::CCollCache* p_coll_cache = Nx::CCollCacheManager::sCreateCollCache();
|
|
Mth::CBBox bbox(
|
|
preliminary_hang_point - Mth::Vector(24.0f, 24.0f, 24.0f),
|
|
preliminary_hang_point + Mth::Vector(24.0f, 12.0f + hang_vert_origin_offset, 24.0f)
|
|
);
|
|
p_coll_cache->Update(bbox);
|
|
feeler.SetCache(p_coll_cache);
|
|
|
|
rail_data.along_rail_factor = along_rail_factor;
|
|
|
|
if (!investigate_hang_rail_vicinity(feeler, preliminary_hang_point, rail_data))
|
|
{
|
|
// if we're attempting to grab a rail from the air, try both possible facings
|
|
if (m_state == WALKING_AIR || !drop_to_hang)
|
|
{
|
|
// see if we're allowed to try the other facing
|
|
if ((rail_data.p_rail_node->GetFlag(NO_HANG_WITH_LEFT_ALONG_RAIL) && !rail_points_left)
|
|
|| (rail_data.p_rail_node->GetFlag(NO_HANG_WITH_RIGHT_ALONG_RAIL) && rail_points_left))
|
|
{
|
|
Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
|
|
return false;
|
|
}
|
|
|
|
rail_data.hang_facing.Negate();
|
|
if (!investigate_hang_rail_vicinity(feeler, preliminary_hang_point, rail_data))
|
|
{
|
|
Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
rail_data.vertical_hang_offset = rail_data.hang_point[Y] - preliminary_hang_point[Y];
|
|
rail_data.horizontal_hang_offset = Mth::DotProduct(rail_data.hang_facing, rail_data.hang_point - preliminary_hang_point);
|
|
|
|
// determine which sort of initial hang animation would be used
|
|
feeler.m_start = rail_data.hang_point + s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * rail_data.hang_facing;
|
|
feeler.m_start[Y] += s_get_param(CRCD(0xdbb3d53e, "hang_init_anim_feeler_height"));
|
|
feeler.m_end = feeler.m_start;
|
|
feeler.m_end += s_get_param(CRCD(0x2f83ae83, "hang_init_anim_feeler_length")) * rail_data.hang_facing;
|
|
if (feeler.GetCollision())
|
|
{
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(255, 0, 0, 0);
|
|
}
|
|
#endif
|
|
rail_data.initial_animation = CRCD(0xec0a1009, "WALL");
|
|
}
|
|
else
|
|
{
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(0, 0, 255, 0);
|
|
}
|
|
#endif
|
|
rail_data.initial_animation = CRCD(0x1ee948a5, "SWING");
|
|
}
|
|
|
|
Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
|
|
return true;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CWalkComponent::investigate_hang_rail_vicinity ( CFeeler& feeler, const Mth::Vector& preliminary_hang_point, SHangRailData& rail_data )
|
|
{
|
|
/* turning off geo investigation for hang point adjustment
|
|
// run a feeler up/down at the preliminary hang point to find the true top of the ledge
|
|
feeler.SetLine(preliminary_hang_point, preliminary_hang_point);
|
|
feeler.m_start[Y] += s_get_param(CRCD(0x1bc501ab, "ledge_top_feeler_up"));
|
|
feeler.m_end[Y] -= s_get_param(CRCD(0xf5f39a8, "ledge_top_feeler_down"));
|
|
if (feeler.GetCollision())
|
|
{
|
|
// found a ledge top
|
|
rail_data.hang_point = feeler.GetPoint();
|
|
}
|
|
else
|
|
{
|
|
// use the prelimiary hang point
|
|
rail_data.hang_point = preliminary_hang_point;
|
|
}
|
|
|
|
// run a feeler in an inch below the hang point along the hang facing to find the front of the ledge
|
|
feeler.m_start = rail_data.hang_point - s_get_param(CRCD(0x95fd5dc5, "ledge_front_feeler_back")) * rail_data.hang_facing;
|
|
feeler.m_end = rail_data.hang_point + s_get_param(CRCD(0xccb4f5d2, "ledge_front_feeler_forward")) * rail_data.hang_facing;
|
|
feeler.m_start[Y] -= 1.0f;
|
|
feeler.m_end[Y] -= 1.0f;
|
|
if (feeler.GetCollision())
|
|
{
|
|
// found a ledge front
|
|
rail_data.hang_point = feeler.GetPoint();
|
|
rail_data.hang_point[Y] += 1.0f;
|
|
}
|
|
// hang a touch outside of it
|
|
rail_data.hang_point -= 2.0f * rail_data.hang_facing;
|
|
*/
|
|
|
|
rail_data.hang_point = preliminary_hang_point;
|
|
|
|
Mth::Vector left(-rail_data.hang_facing[Z], 0.0f, rail_data.hang_facing[X]);
|
|
|
|
Mth::Vector rail = mp_rail_manager->GetPos(rail_data.p_rail_node) - mp_rail_manager->GetPos(rail_data.p_rail_node->GetNextLink());
|
|
|
|
// now that we have the hang position, run two feelers up and down the hang location to insure there is room
|
|
if (!vertical_hang_obstruction_check(feeler, rail_data, left) || !vertical_hang_obstruction_check(feeler, rail_data, -left)) return false;
|
|
|
|
// adjust the hang point for the origin of the hanging skater
|
|
rail_data.hang_point[Y] -= s_get_param(CRCD(0xa8c13e74, "hang_vert_origin_offset"));
|
|
rail_data.hang_point += s_get_param(CRCD(0x262a9467, "hang_horiz_origin_offset")) * rail_data.hang_facing;
|
|
|
|
// use our move feelers for a look around
|
|
if (check_environment_for_hang_movement_collisions(rail_data.hang_facing, rail_data.along_rail_factor, rail_data.hang_point, true, left, rail, rail_data.p_rail_node, rail_data.p_rail_node->GetNextLink())) return false;
|
|
if (check_environment_for_hang_movement_collisions(rail_data.hang_facing, rail_data.along_rail_factor, rail_data.hang_point, false, left, rail, rail_data.p_rail_node, rail_data.p_rail_node->GetNextLink())) return false;
|
|
|
|
// adjust the feeler target check point based on hanging's critical point offset
|
|
Mth::Vector critical_point = rail_data.hang_point + s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * rail_data.hang_facing;
|
|
critical_point[Y] += get_hang_critical_point_vert_offset();
|
|
|
|
// check for obstructions to our hang point; it is critical that our critical point remains on the happy side of collision geometry
|
|
feeler.m_start = m_pos;
|
|
feeler.m_end = critical_point;
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(255, 0, 255, 0);
|
|
}
|
|
#endif
|
|
if (feeler.GetCollision())
|
|
{
|
|
if (m_state == WALKING_GROUND)
|
|
{
|
|
// we found an obstruction taking a direct route; if we'll be hopping to the rail, try an up-and-over route
|
|
|
|
feeler.m_end = m_pos;
|
|
feeler.m_end[Y] += s_get_param(CRCD(0xa85ec070, "hop_obstruction_feeler_up"));
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(255, 0, 255, 0);
|
|
}
|
|
#endif
|
|
if (feeler.GetCollision())
|
|
{
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
Gfx::AddDebugStar(feeler.GetPoint(), 12.0f, MAKE_RGB(0, 255, 255), 0);
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
feeler.m_start = feeler.m_end;
|
|
feeler.m_end = critical_point;
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(255, 0, 255, 0);
|
|
}
|
|
#endif
|
|
if (feeler.GetCollision())
|
|
{
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
Gfx::AddDebugStar(feeler.GetPoint(), 12.0f, MAKE_RGB(0, 255, 255), 0);
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CWalkComponent::vertical_hang_obstruction_check ( CFeeler& feeler, SHangRailData& rail_data, const Mth::Vector& check_offset_direction )
|
|
{
|
|
feeler.m_start = rail_data.hang_point + s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * rail_data.hang_facing
|
|
+ s_get_param(CRCD(0xafbf7687, "hang_obstruction_feeler_side")) * check_offset_direction;
|
|
feeler.m_end = feeler.m_start;
|
|
feeler.m_start[Y] += -s_get_param(CRCD(0xa8c13e74, "hang_vert_origin_offset")) + get_hang_critical_point_vert_offset();
|
|
feeler.m_end[Y] += s_get_param(CRCD(0x8f35f29f, "hang_obstruction_feeler_up"));
|
|
if (feeler.GetCollision())
|
|
{
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(255, 0, 50, 0);
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
Mth::Swap(feeler.m_start, feeler.m_end);
|
|
if (feeler.GetCollision())
|
|
{
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(0, 255, 50, 0);
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(200, 200, 50, 0);
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
/*
|
|
void CWalkComponent::go_hop_state ( )
|
|
{
|
|
// During the hop to hang state, the player has no control. We simply move the goal position.
|
|
|
|
if (mp_movable_contact_component->UpdateContact(m_pos))
|
|
{
|
|
m_pos += mp_movable_contact_component->GetContact()->GetMovement();
|
|
|
|
if (mp_movable_contact_component->GetContact()->IsRotated())
|
|
{
|
|
m_facing = mp_movable_contact_component->GetContact()->GetRotation().Rotate(m_facing);
|
|
m_goal_hop_facing = mp_movable_contact_component->GetContact()->GetRotation().Rotate(m_goal_hop_facing);
|
|
if (m_facing[Y] != 0.0f)
|
|
{
|
|
m_facing[Y] = 0.0f;
|
|
m_facing.Normalize();
|
|
}
|
|
if (m_goal_hop_facing[Y] != 0.0f)
|
|
{
|
|
m_goal_hop_facing[Y] = 0.0f;
|
|
m_goal_hop_facing.Normalize();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_vertical_vel > -s_get_param(CRCD(0x7aa618cc, "hop_target_vert_vel")))
|
|
{
|
|
m_pos += m_horizontal_vel * m_frame_length;
|
|
m_pos[Y] += m_vertical_vel * m_frame_length + 0.5f * -s_get_param(CRCD(0xa5e2da58, "gravity")) * Mth::Sqr(m_frame_length);
|
|
|
|
m_vertical_vel += -s_get_param(CRCD(0xa5e2da58, "gravity")) * m_frame_length;
|
|
|
|
// BUG: we really want more control over this; this can cause pops; perhaps hold the hop duration and use the hop fraction complete
|
|
m_facing = Mth::Lerp(m_facing, m_goal_hop_facing, 30.0f * Tmr::FrameLength());
|
|
|
|
m_frame_event = CRCD(0xf41aba21, "Hop");
|
|
}
|
|
else
|
|
{
|
|
m_horizontal_vel.Set();
|
|
m_vertical_vel = 0.0f;
|
|
|
|
m_pos = m_goal_hop_pos;
|
|
|
|
m_facing = m_goal_hop_facing;
|
|
|
|
set_state(WALKING_HANG);
|
|
|
|
m_frame_event = CRCD(0x4194ecca, "Hang");
|
|
}
|
|
}
|
|
*/
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CWalkComponent::go_hang_state ( )
|
|
{
|
|
// Mth::Vector rail = (mp_rail_manager->GetPos(mp_rail_start) - mp_rail_manager->GetPos(mp_rail_end)).Normalize();
|
|
// DUMPF(Mth::RadToDeg(asin(Mth::Abs(rail[Y]))));
|
|
|
|
if (mp_movable_contact_component->UpdateContact(m_pos))
|
|
{
|
|
CRailManagerComponent* p_rail_manager_component = GetRailManagerComponentFromObject(mp_movable_contact_component->GetContact()->GetObject());
|
|
Dbg_Assert(p_rail_manager_component);
|
|
|
|
p_rail_manager_component->UpdateRailManager();
|
|
|
|
// calculate our new position
|
|
set_pos_from_hang_rail_state();
|
|
}
|
|
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0x1a5eab7, "rail_highlights")))
|
|
{
|
|
Gfx::AddDebugLine(mp_rail_manager->GetPos(mp_rail_start), mp_rail_manager->GetPos(mp_rail_end), MAKE_RGB(Mth::Rnd(256), Mth::Rnd(256), Mth::Rnd(256)), 0, 1);
|
|
Gfx::AddDebugLine(mp_rail_manager->GetPos(mp_rail_start) + Mth::Vector(1.0f, 0.0f, 0.0f), mp_rail_manager->GetPos(mp_rail_end) + Mth::Vector(1.0f, 0.0f, 0.0f), MAKE_RGB(Mth::Rnd(256), Mth::Rnd(256), Mth::Rnd(256)), 0, 1);
|
|
Gfx::AddDebugLine(mp_rail_manager->GetPos(mp_rail_start) + Mth::Vector(0.0f, 1.0f, 0.0f), mp_rail_manager->GetPos(mp_rail_end) + Mth::Vector(0.0f, 1.0f, 0.0f), MAKE_RGB(Mth::Rnd(256), Mth::Rnd(256), Mth::Rnd(256)), 0, 1);
|
|
Gfx::AddDebugLine(mp_rail_manager->GetPos(mp_rail_start) + Mth::Vector(0.0f, 0.0f, 1.0f), mp_rail_manager->GetPos(mp_rail_end) + Mth::Vector(0.0f, 0.0f, 1.0f), MAKE_RGB(Mth::Rnd(256), Mth::Rnd(256), Mth::Rnd(256)), 0, 1);
|
|
}
|
|
#endif
|
|
|
|
// only respond to full up/down controls and only after a frame delay (to allow the debounce a frame to kick in)
|
|
if (m_control_pegged)
|
|
{
|
|
// up; IsLeftAnalogUpPressed allows for analog debouncing
|
|
if (mp_input_component->GetControlPad().IsLeftAnalogUpPressed())
|
|
{
|
|
// potential standing position after pull up
|
|
Mth::Vector stand_pos = m_pos + s_get_param(CRCD(0x21dfbe77, "pull_up_offset_forward")) * m_facing;
|
|
stand_pos[Y] += s_get_param(CRCD(0x52fa7ca6, "pull_up_offset_up"));
|
|
if (maybe_pull_up_from_hang(stand_pos)) return;
|
|
|
|
// try standing a little closer
|
|
stand_pos = m_pos + 0.5f * s_get_param(CRCD(0x21dfbe77, "pull_up_offset_forward")) * m_facing;
|
|
stand_pos[Y] += s_get_param(CRCD(0x52fa7ca6, "pull_up_offset_up"));
|
|
if (maybe_pull_up_from_hang(stand_pos)) return;
|
|
}
|
|
// down; IsLeftAnalogDownPressed allows for analog debouncing
|
|
else if (mp_input_component->GetControlPad().IsLeftAnalogDownPressed())
|
|
{
|
|
// drop
|
|
set_state(WALKING_AIR);
|
|
m_primary_air_direction = m_facing;
|
|
leave_movable_contact_for_air(m_horizontal_vel, m_vertical_vel);
|
|
m_frame_event = CRCD(0x439f4704, "Air");
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// left or right
|
|
float control_vel;
|
|
if (Mth::Abs((Mth::PI / 2.0f) - Mth::Abs(mp_input_component->GetControlPad().m_leftAngle)) < cosf(Mth::DegToRad(s_get_param(CRCD(0x1b877928, "hang_vert_control_tolerance")))))
|
|
{
|
|
control_vel = s_get_param(CRCD(0xd77ee881, "hang_move_speed")) * m_control_magnitude
|
|
* (mp_input_component->GetControlPad().m_leftAngle > 0.0f ? 1.0f : -1.0f);
|
|
}
|
|
else
|
|
{
|
|
control_vel = 0.0f;
|
|
}
|
|
m_hang_move_vel = Mth::Lerp(m_hang_move_vel, control_vel, s_get_param(CRCD(0xc506a97c, "hang_move_lerp_rate")) * m_frame_length);
|
|
|
|
if (Mth::Abs(m_hang_move_vel) > s_get_param(CRCD(0x228b5376, "hang_move_cutoff")))
|
|
{
|
|
m_anim_effective_speed = Mth::Abs(m_hang_move_vel);
|
|
|
|
hang_movement(m_hang_move_vel * m_frame_length);
|
|
|
|
// calculate our new position
|
|
set_pos_from_hang_rail_state();
|
|
}
|
|
else
|
|
{
|
|
m_frame_event = CRCD(0x4194ecca, "Hang");
|
|
}
|
|
|
|
/* this doesn't make sense with the new higher critical point
|
|
// run a feeler between our old and new position to insure that our position stays safe
|
|
CFeeler feeler;
|
|
feeler.m_start = m_frame_start_pos + s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * m_facing;
|
|
feeler.m_end = m_pos + s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * m_facing;
|
|
if (feeler.GetCollision())
|
|
{
|
|
// our feet have hit something; often this means the rail has dropped to ground level; drop from our old position
|
|
m_pos = m_frame_start_pos;
|
|
set_state(WALKING_AIR);
|
|
m_primary_air_direction = m_facing;
|
|
leave_movable_contact_for_air(m_horizontal_vel, m_vertical_vel);
|
|
m_frame_event = CRCD(0x439f4704, "Air");
|
|
return;
|
|
}
|
|
*/
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CWalkComponent::hang_movement ( float movement )
|
|
{
|
|
// cache current critical point location and entire state
|
|
Mth::Vector initial_critical_point = m_pos + m_critical_point_offset;
|
|
Mth::Vector initial_pos = m_pos;
|
|
float initial_along_rail_factor = m_along_rail_factor;
|
|
const CRailNode* p_initial_rail_start = mp_rail_start;
|
|
const CRailNode* p_initial_rail_end = mp_rail_end;
|
|
Mth::Vector initial_critical_point_offset = m_critical_point_offset;
|
|
Mth::Vector initial_facing = m_facing;
|
|
|
|
// allow left/right movement along rail
|
|
|
|
Mth::Vector perp_facing(-m_facing[Z], 0.0f, m_facing[X]);
|
|
|
|
// the offset from rail start to rail end
|
|
Mth::Vector rail = mp_rail_manager->GetPos(mp_rail_start) - mp_rail_manager->GetPos(mp_rail_end);
|
|
|
|
// move left or right
|
|
bool left = movement > 0.0f;
|
|
|
|
// is rail fractional position parameter ascends or descends with the movement
|
|
bool with_rail;
|
|
if (Mth::DotProduct(perp_facing, rail) < 0.0f)
|
|
{
|
|
with_rail = left;
|
|
}
|
|
else
|
|
{
|
|
with_rail = !left;
|
|
}
|
|
|
|
// check for collisions
|
|
if (check_environment_for_hang_movement_collisions(m_facing, m_along_rail_factor, m_pos, left, perp_facing, rail, mp_rail_start, mp_rail_end))
|
|
{
|
|
m_hang_move_vel = 0.0f;
|
|
m_frame_event = CRCD(0x4194ecca, "Hang");
|
|
return;
|
|
}
|
|
|
|
// the movement in terms of the fraction of the rail
|
|
float delta_factor = Mth::Abs(movement) / rail.Length();
|
|
|
|
// move along the rail
|
|
m_along_rail_factor += with_rail ? delta_factor : -delta_factor;
|
|
|
|
// if we're moving off the rail
|
|
if (m_along_rail_factor < 0.0f || m_along_rail_factor > 1.0f)
|
|
{
|
|
bool found_rail = find_next_rail(m_along_rail_factor, mp_rail_start, mp_rail_end, mp_rail_start, mp_rail_end);
|
|
|
|
// there's no connecting rail
|
|
if (!found_rail)
|
|
{
|
|
// clamp the movement
|
|
m_hang_move_vel = 0.0f;
|
|
m_along_rail_factor = Mth::Clamp(m_along_rail_factor, 0.0f, 1.0f);
|
|
m_frame_event = CRCD(0x4194ecca, "Hang");
|
|
}
|
|
else
|
|
{
|
|
// move onto the neighboring rail
|
|
float remaining_movement = Mth::Abs(m_along_rail_factor - Mth::Clamp(m_along_rail_factor, 0.0f, 1.0f)) * rail.Length();
|
|
move_onto_neighboring_hang_rail(remaining_movement, m_along_rail_factor < 0.0f, with_rail ? rail : -rail);
|
|
m_frame_event = left ? CRCD(0x2d9815c3, "HangMoveLeft") : CRCD(0x279b1f0b, "HangMoveRight");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_frame_event = left ? CRCD(0x2d9815c3, "HangMoveLeft") : CRCD(0x279b1f0b, "HangMoveRight");
|
|
}
|
|
|
|
// check to see if the critical point has gone through geo
|
|
|
|
Mth::Vector critical_point = m_pos + m_critical_point_offset;
|
|
CFeeler feeler;
|
|
feeler.m_start = initial_critical_point;
|
|
feeler.m_end = critical_point;
|
|
if (feeler.GetCollision())
|
|
{
|
|
// restore entire to state to initial state
|
|
m_pos = initial_pos;
|
|
DUMP_WPOSITION
|
|
m_along_rail_factor = initial_along_rail_factor;
|
|
mp_rail_start = p_initial_rail_start;
|
|
mp_rail_end = p_initial_rail_end;
|
|
m_critical_point_offset = initial_critical_point_offset;
|
|
m_facing = initial_facing;
|
|
|
|
m_hang_move_vel = 0.0f;
|
|
m_frame_event = CRCD(0x4194ecca, "Hang");
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CWalkComponent::check_environment_for_hang_movement_collisions ( const Mth::Vector& facing, float along_rail_factor, const Mth::Vector& pos, bool left, const Mth::Vector& perp_facing, Mth::Vector rail, const CRailNode* p_rail_start, const CRailNode* p_rail_end )
|
|
{
|
|
// check environment in the appropriate check_direction for collisions which would prevent hanging movement
|
|
// OPTIMIZATION: use a CCollCache here
|
|
|
|
bool with_rail;
|
|
if (Mth::DotProduct(perp_facing, rail) < 0.0f)
|
|
{
|
|
with_rail = left;
|
|
}
|
|
else
|
|
{
|
|
with_rail = !left;
|
|
}
|
|
|
|
// check_direction to the side to check for geo
|
|
Mth::Vector check_direction = left ? perp_facing : -perp_facing;
|
|
|
|
// adjusted facing due to adjusted check_direction
|
|
Mth::Vector check_direction_facing = facing;
|
|
|
|
// see if we're near the end of the rail; if so, we may want to angle our feelers out (so we can make inside turns while hanging)
|
|
float distance_to_end_of_rail = rail.Length() * (with_rail ? 1.0f - along_rail_factor : along_rail_factor);
|
|
if (distance_to_end_of_rail < s_get_param(CRCD(0x3344a6d0, "hang_move_collision_side_length")) + 1.0f)
|
|
{
|
|
// pretend we're past the rail when calling find_next_rail
|
|
along_rail_factor += with_rail ? 2.0f : -2.0f;
|
|
|
|
const CRailNode* p_next_start;
|
|
const CRailNode* p_next_end;
|
|
if (find_next_rail(along_rail_factor, p_rail_start, p_rail_end, p_next_start, p_next_end))
|
|
{
|
|
Mth::Vector new_check_direction = (along_rail_factor > 0.5f ? -1.0f : 1.0f)
|
|
* (mp_rail_manager->GetPos(p_next_start) - mp_rail_manager->GetPos(p_next_end));
|
|
new_check_direction[Y] = 0.0f;
|
|
new_check_direction.Normalize();
|
|
|
|
// only use adjusted feelers for inside turns
|
|
float dot = Mth::DotProduct(facing, new_check_direction);
|
|
if (dot < 0.0f && dot > -0.87f)
|
|
{
|
|
check_direction = new_check_direction;
|
|
if (left)
|
|
{
|
|
check_direction_facing[X] = check_direction[Z];
|
|
check_direction_facing[Z] = -check_direction[X];
|
|
}
|
|
else
|
|
{
|
|
check_direction_facing[X] = -check_direction[Z];
|
|
check_direction_facing[Z] = check_direction[X];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CFeeler feeler;
|
|
feeler.m_start = pos;
|
|
feeler.m_start[Y] += s_get_param(CRCD(0x84ff931c, "hang_move_collision_up"));
|
|
feeler.m_start -= s_get_param(CRCD(0xcbeb3d89, "hang_move_collision_back")) * facing;
|
|
|
|
// feel to the side
|
|
feeler.m_end = feeler.m_start;
|
|
feeler.m_end += s_get_param(CRCD(0x3344a6d0, "hang_move_collision_side_length")) * check_direction;
|
|
if (feeler.GetCollision())
|
|
{
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(255, 0, 0, 1);
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(0, 0, 255, 1);
|
|
}
|
|
#endif
|
|
|
|
// feel up and to the side
|
|
feeler.m_end = feeler.m_start;
|
|
feeler.m_end[Y] += s_get_param(CRCD(0xc774dd6d, "hang_move_collision_side_height"));
|
|
feeler.m_end += s_get_param(CRCD(0x3344a6d0, "hang_move_collision_side_length")) * check_direction;
|
|
if (feeler.GetCollision())
|
|
{
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(255, 0, 0, 1);
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(0, 0, 255, 1);
|
|
}
|
|
#endif
|
|
|
|
// feel half up and to the side
|
|
feeler.m_end = feeler.m_start;
|
|
feeler.m_end[Y] += 0.5f * s_get_param(CRCD(0xc774dd6d, "hang_move_collision_side_height"));
|
|
feeler.m_end += s_get_param(CRCD(0x3344a6d0, "hang_move_collision_side_length")) * check_direction;
|
|
if (feeler.GetCollision())
|
|
{
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(255, 0, 0, 1);
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(0, 0, 255, 1);
|
|
}
|
|
#endif
|
|
|
|
// feel down (to critical point) and to the side and back to the critical point
|
|
feeler.m_end = feeler.m_start;
|
|
feeler.m_end[Y] += -s_get_param(CRCD(0x84ff931c, "hang_move_collision_up")) + get_hang_critical_point_vert_offset() - 1.0f;
|
|
feeler.m_end += s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * check_direction_facing;
|
|
feeler.m_end += s_get_param(CRCD(0x3344a6d0, "hang_move_collision_side_length")) * check_direction;
|
|
// add a touch along the rail so we don't get stuck with both directions feelers in the ground
|
|
feeler.m_end += with_rail ? -rail.Normalize() : rail.Normalize();
|
|
if (feeler.GetCollision())
|
|
{
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(255, 0, 0, 1);
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(0, 0, 255, 1);
|
|
}
|
|
#endif
|
|
|
|
// feel half down and to the side and back to the critical point
|
|
feeler.m_end = feeler.m_start;
|
|
feeler.m_end[Y] -= 0.5f * s_get_param(CRCD(0xc774dd6d, "hang_move_collision_side_height"));
|
|
feeler.m_end += s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * check_direction_facing;
|
|
feeler.m_end += s_get_param(CRCD(0x3344a6d0, "hang_move_collision_side_length")) * check_direction;
|
|
if (feeler.GetCollision())
|
|
{
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(255, 0, 0, 1);
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(0, 0, 255, 1);
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CWalkComponent::move_onto_neighboring_hang_rail ( float movement, bool descending, const Mth::Vector& old_rail )
|
|
{
|
|
Mth::Vector next_rail = mp_rail_manager->GetPos(mp_rail_start) - mp_rail_manager->GetPos(mp_rail_end);
|
|
float next_rail_length = next_rail.Length();
|
|
|
|
// if the next rail is shorter than our movement, just ditch the extra movement
|
|
movement = Mth::ClampMax(movement, next_rail_length);
|
|
|
|
// we've found the appropriate neighbor rail to move on to, so set up our state
|
|
|
|
// calculate our position along the rail
|
|
m_along_rail_factor = movement / next_rail_length;
|
|
if (descending)
|
|
{
|
|
m_along_rail_factor = 1.0f - m_along_rail_factor;
|
|
}
|
|
|
|
// calculate our next facing
|
|
Mth::Vector next_facing(-next_rail[Z], 0.0f, next_rail[X]);
|
|
next_facing.Normalize();
|
|
|
|
// check its check_direction
|
|
if (Mth::CrossProduct(old_rail, m_facing)[Y] * Mth::CrossProduct(descending ? -next_rail : next_rail, next_facing)[Y] < 0.0f)
|
|
{
|
|
next_facing.Negate();
|
|
}
|
|
|
|
if (Mth::DotProduct(next_facing, m_facing) < cosf(Mth::DegToRad(60.0f)))
|
|
{
|
|
// move the camera behind us
|
|
mp_camera_component->FlushRequest();
|
|
}
|
|
m_facing = next_facing;
|
|
|
|
m_critical_point_offset = s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * m_facing;
|
|
m_critical_point_offset[Y] = get_hang_critical_point_vert_offset();
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CWalkComponent::set_pos_from_hang_rail_state ( )
|
|
{
|
|
m_pos = Mth::Lerp(mp_rail_manager->GetPos(mp_rail_start), mp_rail_manager->GetPos(mp_rail_end), m_along_rail_factor);
|
|
m_pos[Y] += m_vertical_hang_offset;
|
|
m_pos += m_horizontal_hang_offset * m_facing;
|
|
DUMP_WPOSITION
|
|
|
|
if (mp_movable_contact_component->HaveContact() && mp_movable_contact_component->GetContact()->IsRotated())
|
|
{
|
|
// recalculate facing
|
|
Mth::Vector new_facing = mp_rail_manager->GetPos(mp_rail_start) - mp_rail_manager->GetPos(mp_rail_end);
|
|
new_facing[Y] = 0.0f;
|
|
new_facing.Normalize();
|
|
new_facing.Set(-new_facing[Z], 0.0f, new_facing[X]);
|
|
if (Mth::DotProduct(new_facing, m_facing) > 0.0f)
|
|
{
|
|
m_facing = new_facing;
|
|
}
|
|
else
|
|
{
|
|
m_facing = -new_facing;
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CWalkComponent::find_next_rail ( float& along_rail_factor, const CRailNode* p_current_start, const CRailNode* p_current_end, const CRailNode*& p_new_start, const CRailNode*& p_new_end )
|
|
{
|
|
// find the neighboring rail, if there is one
|
|
|
|
Dbg_Assert(along_rail_factor < 0.0f || along_rail_factor > 1.0f);
|
|
|
|
const CRailNode* next_rail_node = NULL;
|
|
if (along_rail_factor < 0.0f)
|
|
{
|
|
// look for a connection
|
|
if (p_current_start->GetPrevLink() && p_current_start->GetPrevLink()->IsActive() && !p_current_start->GetPrevLink()->GetFlag(NO_CLIMBING)
|
|
&& Rail_ValidInEditor(mp_rail_manager->GetPos(p_current_start->GetPrevLink()), mp_rail_manager->GetPos(p_current_start)))
|
|
{
|
|
p_new_end = p_current_start;
|
|
p_new_start = p_current_start->GetPrevLink();
|
|
// MESSAGE("FOUND CONNECTING RAIL");
|
|
return true;
|
|
}
|
|
|
|
// look for another node who's position corresponds to the current start node
|
|
if (!mp_rail_manager->CheckForCoincidentRailNode(p_current_start, nBit(NO_CLIMBING) | nBit(LADDER), &next_rail_node))
|
|
{
|
|
// MESSAGE("NO COINCIDENT RAIL");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// look for a connection
|
|
if (p_current_end->GetNextLink() && p_current_end->IsActive() && !p_current_end->GetFlag(NO_CLIMBING)
|
|
&& Rail_ValidInEditor(mp_rail_manager->GetPos(p_current_end), mp_rail_manager->GetPos(p_current_end->GetNextLink())))
|
|
{
|
|
p_new_start = p_current_end;
|
|
p_new_end = p_current_end->GetNextLink();
|
|
// MESSAGE("FOUND CONNECTING RAIL");
|
|
return true;
|
|
}
|
|
|
|
// look for another node who's position corresponds a given node's position
|
|
if (!mp_rail_manager->CheckForCoincidentRailNode(p_current_end, nBit(NO_CLIMBING) | nBit(LADDER), &next_rail_node))
|
|
{
|
|
// MESSAGE("NO COINCIDENT RAIL");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Dbg_Assert(next_rail_node && next_rail_node->GetNextLink());
|
|
|
|
bool rails_are_flush = mp_rail_manager->RailNodesAreCoincident(next_rail_node, p_current_end)
|
|
|| mp_rail_manager->RailNodesAreCoincident(next_rail_node->GetNextLink(), p_current_start);
|
|
|
|
Mth::Vector old_rail_dir = (mp_rail_manager->GetPos(p_current_end) - mp_rail_manager->GetPos(p_current_start)).Normalize();
|
|
Mth::Vector new_rail_dir = (mp_rail_manager->GetPos(next_rail_node->GetNextLink()) - mp_rail_manager->GetPos(next_rail_node)).Normalize();
|
|
if (!rails_are_flush)
|
|
{
|
|
new_rail_dir *= -1.0f;
|
|
}
|
|
if (Mth::DotProduct(old_rail_dir, new_rail_dir) < -0.866f)
|
|
{
|
|
MESSAGE("BAD ANGLE ON COINCIDENT RAIL");
|
|
return false;
|
|
}
|
|
|
|
if (!rails_are_flush)
|
|
{
|
|
along_rail_factor = 1.0f - along_rail_factor;
|
|
}
|
|
p_new_start = next_rail_node;
|
|
p_new_end = next_rail_node->GetNextLink();
|
|
MESSAGE("FOUND COINCIDENT RAIL");
|
|
return true;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
bool CWalkComponent::maybe_pull_up_from_hang ( Mth::Vector& proposed_stand_pos )
|
|
{
|
|
// check to see if there's a good place for us to stand if we pull up; if so, do so
|
|
|
|
Mth::Vector stand_pos;
|
|
|
|
CFeeler potential_ground_feeler;
|
|
bool ground = determine_stand_pos(proposed_stand_pos, stand_pos, potential_ground_feeler);
|
|
|
|
if (!ground)
|
|
{
|
|
Mth::Vector initial_stand_pos = stand_pos;
|
|
Mth::Vector left(-m_facing[Z], 0.0f, m_facing[X]);
|
|
|
|
// if we couldn't find the ground, we may be at the end of a rail which hangs over the geo; look for stand positions to the left and right
|
|
const float SIDEWAYS_SEARCH_DISTANCE = 12.0f;
|
|
proposed_stand_pos += SIDEWAYS_SEARCH_DISTANCE * left;
|
|
ground = determine_stand_pos(proposed_stand_pos, stand_pos, potential_ground_feeler);
|
|
if (!ground)
|
|
{
|
|
proposed_stand_pos -= (2.0f * SIDEWAYS_SEARCH_DISTANCE) * left;
|
|
ground = determine_stand_pos(proposed_stand_pos, stand_pos, potential_ground_feeler);
|
|
if (!ground)
|
|
{
|
|
stand_pos = initial_stand_pos;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check for walls around the standing position
|
|
CFeeler feeler;
|
|
float push_feeler_length = 0.5f * s_get_param(CRCD(0xa20c43b7, "push_feeler_length"));
|
|
float feeler_height = s_get_param(CRCD(0x6da7f696, "feeler_height"));
|
|
for (int n = 0; n < vNUM_FEELERS; n++)
|
|
{
|
|
float angle = n * (2.0f * Mth::PI / vNUM_FEELERS);
|
|
|
|
float cos_angle = cosf(angle);
|
|
float sin_angle = sinf(angle);
|
|
|
|
Mth::Vector end_offset;
|
|
end_offset[X] = cos_angle * m_facing[X] - sin_angle * m_facing[Z];
|
|
end_offset[Y] = 0.0f;
|
|
end_offset[Z] = sin_angle * m_facing[X] + cos_angle * m_facing[Z];
|
|
end_offset[W] = 1.0f;
|
|
end_offset *= push_feeler_length;
|
|
|
|
feeler.m_start = stand_pos;
|
|
feeler.m_start[Y] += feeler_height;
|
|
feeler.m_end = stand_pos + end_offset;
|
|
feeler.m_end[Y] += feeler_height;
|
|
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(0, 255, 255, 0);
|
|
}
|
|
#endif
|
|
if (feeler.GetCollision()) return false;
|
|
}
|
|
|
|
// check line from hang point to standing position for obstructions
|
|
feeler.m_start = m_pos;
|
|
feeler.m_start += -6.0f * m_facing;
|
|
feeler.m_start[Y] += s_get_param(CRCD(0xa8c13e74, "hang_vert_origin_offset")) + s_get_param(CRCD(0x9b2818cf, "pull_up_obstruction_height"));
|
|
feeler.m_end = stand_pos;
|
|
feeler.m_end[Y] += 1.0f;
|
|
if (feeler.GetCollision())
|
|
{
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(0, 100, 100, 0);
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
#ifdef __USER_DAN__
|
|
if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
|
|
{
|
|
feeler.DebugLine(100, 0, 100, 0);
|
|
}
|
|
#endif
|
|
|
|
// pull up looks ok, so let's do it
|
|
|
|
if (ground)
|
|
{
|
|
mp_anim_wait_complete_callback = &Obj::CWalkComponent::pull_up_from_hang_to_ground_complete;
|
|
|
|
m_last_ground_feeler = potential_ground_feeler;
|
|
m_last_ground_feeler_valid = true;
|
|
m_ground_normal = potential_ground_feeler.GetNormal();
|
|
}
|
|
else
|
|
{
|
|
mp_anim_wait_complete_callback = &Obj::CWalkComponent::pull_up_from_hang_to_air_complete;
|
|
}
|
|
|
|
m_anim_wait_initial_pos = m_pos;
|
|
m_anim_wait_goal_pos = stand_pos;
|
|
|
|
calculate_anim_wait_facing_drift_parameters(m_facing);
|
|
|
|
m_anim_wait_camera_mode = AWC_GOAL;
|
|
|
|
m_drift_initial_display_offset = m_display_offset;
|
|
m_drift_goal_display_offset = 0.0f;
|
|
|
|
m_critical_point_offset = -(6.0f + s_get_param(CRCD(0x21dfbe77, "pull_up_offset_forward"))) * m_facing;
|
|
m_critical_point_offset[Y] = 1.0f;
|
|
|
|
// setup the false wall incase the player jumps out of the animation wait
|
|
m_false_wall.normal = m_facing;
|
|
m_false_wall.cancel_height = m_pos[Y] - m_vertical_hang_offset;
|
|
|
|
set_state(WALKING_ANIMWAIT);
|
|
|
|
m_frame_event = CRCD(0x9ef9f8f1, "PullUpFromHang");
|
|
|
|
return true;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CWalkComponent::pull_up_from_hang_to_ground_complete ( )
|
|
{
|
|
set_state(WALKING_GROUND);
|
|
|
|
if (m_last_ground_feeler_valid)
|
|
{
|
|
mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_ONTO, m_last_ground_feeler);
|
|
|
|
// check for a moving contact
|
|
mp_movable_contact_component->CheckForMovableContact(m_last_ground_feeler);
|
|
}
|
|
|
|
m_critical_point_offset.Set();
|
|
|
|
m_display_offset = m_drift_goal_display_offset;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
void CWalkComponent::pull_up_from_hang_to_air_complete ( )
|
|
{
|
|
set_state(WALKING_AIR);
|
|
|
|
m_primary_air_direction = m_facing;
|
|
leave_movable_contact_for_air(m_horizontal_vel, m_vertical_vel);
|
|
|
|
m_display_offset = m_drift_goal_display_offset;
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
float CWalkComponent::get_hang_display_offset ( )
|
|
{
|
|
if (mp_model_component->GetModel()->IsScalingEnabled())
|
|
{
|
|
return (1.0f - mp_model_component->GetModel()->GetScale()[Y]) * s_get_param(CRCD(0xa8c13e74, "hang_vert_origin_offset"));
|
|
}
|
|
else
|
|
{
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
/******************************************************************/
|
|
/* */
|
|
/* */
|
|
/******************************************************************/
|
|
|
|
float CWalkComponent::get_hang_critical_point_vert_offset ( )
|
|
{
|
|
return s_get_param(CRCD(0xde86132e, "hang_critical_point_vert_offset"))
|
|
- s_get_param(CRCD(0x7201ad48, "max_cas_scaling")) * s_get_param(CRCD(0xa8c13e74, "hang_vert_origin_offset"));
|
|
}
|
|
|
|
}
|