//**************************************************************************** //* MODULE: Gel/Components //* FILENAME: PedLogicComponent.cpp //* OWNER: Brad Bulkley //* CREATION DATE: 1/9/03 //**************************************************************************** // start autoduck documentation // @DOC pedlogiccomponent // @module pedlogiccomponent | None // @subindex Scripting Database // @index script | pedlogiccomponent #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Obj { #define vDEFAULT_RANGE (10.0f) #define vACTION_WEIGHT_RESOLUTION (1000) /******************************************************************/ /* */ /* */ /******************************************************************/ CBaseComponent* CPedLogicComponent::s_create() { return static_cast< CBaseComponent* >( new CPedLogicComponent ); } /******************************************************************/ /* */ /* */ /******************************************************************/ CPedLogicComponent::CPedLogicComponent() : CBaseComponent() { SetType( CRC_PEDLOGIC ); // reset whiskers for ( int i = 0; i < vNUM_WHISKERS; i++ ) m_whiskers[i] = 0.0; ResetBias(); m_turn_frames = 0; m_max_turn_frames = 0; // Script::GetInteger( CRCD( 0x793535f5, "ped_turn_frames" ), Script::ASSERT ); mp_path_object_tracker = NULL; m_col_dist_above = Script::GetFloat( CRCD( 0x379d93d2, "ped_skater_stick_dist_above" ), Script::ASSERT ); m_col_dist_below = Script::GetFloat( CRCD( 0x8715b7b2, "ped_skater_stick_dist_below" ), Script::ASSERT ); m_flags = 0; m_original_max_vel = 0.0f; } /******************************************************************/ /* */ /* */ /******************************************************************/ CPedLogicComponent::~CPedLogicComponent() { } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::InitFromStructure( Script::CStruct* pParams ) { // ** Add code to parse the structure, and initialize the component // m_state = CRCD( 0x23db4aea, "Idle" );s pParams->GetInteger( CRCD( 0xe50d6573, "nodeIndex" ), &m_node_from, Script::ASSERT ); // printf("m_node_from = %i\n", m_node_from); SkateScript::GetPosition( m_node_from, &m_wp_from ); m_normal_lerp = 0.0f; m_display_normal = m_last_display_normal = m_current_normal = GetObject()->m_matrix[Y]; pParams->GetFloat( CRCD( 0x367c4a1b, "StickToGroundDistAbove" ), &m_col_dist_above ); pParams->GetFloat( CRCD( 0x86f46e7b, "StickToGroundDistBelow" ), &m_col_dist_below ); m_current_display_matrix = GetObject()->GetDisplayMatrix(); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::Update() { m_time = Tmr::FrameLength(); // BB - Switch on the state and call the appropriate update function. // This step could be removed by having the logic for each state in // this function, but this will make it easier to split the component // if need be. Plus it just looks cleaner. switch ( m_state ) { case CRCC( 0x69b05e2c, "generic" ): GenericPedUpdate(); break; case CRCC( 0xa85af587, "generic_skater" ): GenericSkaterUpdate(); break; case CRCC(0x6367167b,"lip_trick"): UpdateLipDisplayMatrix(); break; case CRCC( 0x82b45a67, "generic_standing" ): // this is currently handled in script break; default: // Dbg_MsgAssert( 0, ( "PedLogicComponent has unknown state %s", Script::FindChecksumName( m_state ) ) ); break; } // Gfx::AddDebugArrow( GetObject()->GetDisplayMatrix()[X] + GetObject()->m_pos, GetObject()->GetDisplayMatrix()[X] * 48 + GetObject()->m_pos, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 1 ); // Gfx::AddDebugArrow( GetObject()->GetDisplayMatrix()[Y] + GetObject()->m_pos, GetObject()->GetDisplayMatrix()[Y] * 48 + GetObject()->m_pos, MAKE_RGB( 0, 128, 0 ), MAKE_RGB( 0, 0, 128 ), 1 ); // Gfx::AddDebugArrow( GetObject()->GetDisplayMatrix()[Z] + GetObject()->m_pos, GetObject()->GetDisplayMatrix()[Z] * 48 + GetObject()->m_pos, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 0, 0, 128 ), 1 ); } /******************************************************************/ /* */ /* */ /******************************************************************/ CBaseComponent::EMemberFunctionResult CPedLogicComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript ) { switch ( Checksum ) { // @script | Ped_SetLogicState | set the current state of the ped // @uparm name | state - eg, generic, generic_skater, etc. case CRCC( 0x3685b765, "Ped_SetLogicState" ): pParams->GetChecksum( NONAME, &m_state, Script::ASSERT ); break; // @script | Ped_InitPath | initializes the ped on his linked path and starts movement case CRCC( 0xe9906815, "Ped_InitPath" ): { Script::CStruct* pNodeData = SkateScript::GetNode( m_node_from ); // m_node_to = SkateScript::GetLink( pNodeData, Mth::Rnd( SkateScript::GetNumLinks( pNodeData ) ) ); #ifdef __NOPT_ASSERT__ int numLinks = SkateScript::GetNumLinks( m_node_from ); Dbg_MsgAssert( numLinks, ( "Ped_InitPath called on %s, but path node %i has no links!", Script::FindChecksumName( GetObject()->GetID() ), m_node_from ) ); #endif m_node_to = SkateScript::GetLink( pNodeData, 0 ); SkateScript::GetPosition( m_node_to, &m_wp_to ); SelectNextWaypoint(); m_flags |= PEDLOGIC_MOVING_ON_PATH; mp_path_object_tracker = Obj::CPathMan::Instance()->TrackPed( GetObject(), m_node_to ); Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() ); Dbg_Assert( pMotionComp ); pMotionComp->m_movingobj_status |= MOVINGOBJ_STATUS_ON_PATH; // turn the ped to face the right direction Mth::Vector heading = ( m_wp_to - m_wp_from ).Normalize(); Mth::Matrix display_matrix = GetObject()->GetDisplayMatrix(); display_matrix[Z] = heading; display_matrix.OrthoNormalizeAbout( Z ); GetObject()->SetDisplayMatrix( display_matrix ); m_current_display_matrix = display_matrix; Mth::Vector current_pos = GetObject()->m_pos; for ( int i = 0; i < vPOS_HISTORY_SIZE; i++ ) m_pos_history[i] = current_pos; break; } // @script | Ped_StartMoving | start moving on path case CRCC( 0xd711542, "Ped_StartMoving" ): { if ( m_flags & PEDLOGIC_STOPPED ) { Script::CScript* pScript = Script::SpawnScript( CRCD( 0xbed339d4, "ped_skater_start_moving" ) ); pScript->mpObject = GetObject(); m_flags &= ~PEDLOGIC_STOPPED; } m_flags |= PEDLOGIC_MOVING_ON_PATH; break; } // @script | Ped_StopMoving | stop moving on path case CRCC( 0xcc85adfe, "Ped_StopMoving" ): m_flags &= ~PEDLOGIC_MOVING_ON_PATH; break; // @script | Ped_IsMoving | returns true if the ped is moving on the path case CRCC(0x68d298b0,"Ped_IsMoving"): return m_flags & PEDLOGIC_MOVING_ON_PATH ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE; break; // @script | Ped_SetStickToGroundDist | Set the distances the ped uses to check for ground // when sticking. This check is not performed straight up and down, but at whatever // angle the ped is currently standing (his Y vector). // @parmopt float | StickToGroundDistAbove | | distance to check above the ped // @parmopt float | StickToGroundDistBelow | | distance to check below the ped case CRCC( 0x2cc086dd, "Ped_SetStickToGroundDist" ): pParams->GetFloat( "distAbove", &m_col_dist_above ); pParams->GetFloat( "distBelow", &m_col_dist_below ); printf("StickToGround distances set to %f (above), %f (below)\n", m_col_dist_above, m_col_dist_below); break; // @script | Ped_SetIsGrinding | used for manually setting the grind state on and off // @uparm 0 | 0 for off, anything else for on case CRCC( 0xc643e8f5, "Ped_SetIsGrinding" ): { int is_grinding = 0; pParams->GetInteger( NONAME, &is_grinding, Script::ASSERT ); if ( is_grinding != 0 ) m_flags |= PEDLOGIC_GRINDING; else m_flags &= ~PEDLOGIC_GRINDING; break; } // @script | Ped_GetCurrentVelocity | returns variable named velocity... // @flag max | get the maximum velocity, rather than the currnt velocity. // This is useful in case the ped is currently accelerating or decelerating // to achieve maximum velocity. case CRCC( 0x13713760, "Ped_GetCurrentVelocity" ): { Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() ); Dbg_Assert( pMotionComp ); pScript->GetParams()->AddFloat( CRCD( 0x41272956, "velocity" ), pMotionComp->m_vel_z ); break; } // @script | Ped_StoreMaxVelocity | stores the current max velocity, so you can // change it and restore it later case CRCC( 0xf5627596, "Ped_StoreMaxVelocity" ): { Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() ); Dbg_Assert( pMotionComp ); m_original_max_vel = pMotionComp->m_max_vel; break; } // @script | Ped_GetOriginalMaxVelocity | returns original_max_velocity stored when // Ped_StoreMaxVelocity was called case CRCC( 0xe575efa9, "Ped_GetOriginalMaxVelocity" ): { pScript->GetParams()->AddFloat( "original_max_velocity", m_original_max_vel ); break; } // @script | Ped_Bail | case CRCC( 0xee24a410, "Ped_Bail" ): Bail(); break; case CRCC( 0xc076d74, "Ped_SetIsBailing" ): { int v; pParams->GetInteger( NONAME, &v, Script::ASSERT ); if ( pParams->ContainsFlag( CRCD( 0x255ed86f, "grind" ) ) ) { if ( v == 0 ) m_flags &= ~PEDLOGIC_GRIND_BAILING; else m_flags |= PEDLOGIC_GRIND_BAILING; } else { if ( v == 0 ) m_flags &= ~PEDLOGIC_BAILING; else m_flags |= PEDLOGIC_BAILING; } break; } case CRCC( 0xda7be9e2, "Ped_InitVertTrick" ): { // get the height and gravity float height; pParams->GetFloat( CRCD( 0xab21af0, "height" ), &height, Script::ASSERT ); float gravity; pParams->GetFloat( CRCD( 0xa5e2da58, "gravity" ), &gravity, Script::ASSERT ); Dbg_MsgAssert( gravity < 0.0f, ( "Ped_InitVertTrick given positive number for gravity" ) ); Mth::Vector pt = m_wp_to - GetObject()->m_pos; // adjust height to compensate for difference between ped height // and height of waypoint // height += pt[Y]; // pt[Y] = 0.0f; // figure the time - the gravity is always expressed as negative // acceleration, but since we're figuring the time it will take to // fall from the top at velocity=0, we use positive // Multiply by 2 to get time up and down // d = vi*t + (a*t^2)/2 float time = 2 * sqrtf( 2 * -height / gravity ); // compute the inital y velocity, which equals the final y velocity when // falling back down (vf^2 = vi^2 + 2ad) float vi = sqrtf( 2 * gravity * -height ); Dbg_Assert( vi > 0 ); // find the horizontal distance we need to travel float distance = pt.Length() / 2; // bump it up a bit so we don't miss landing distance *= Script::GetFloat( CRCD( 0x2b5bedba, "ped_skater_vert_jump_slop_distance" ), Script::ASSERT ); // horizontal velocity Mth::Vector vh = ( distance / time ) * pt.Normalize(); // vertical velocity Mth::Vector vy = vi * Mth::Vector(0, 1, 0); // total speed and heading // float jump_speed = ( ( vh + vy ) * time ).Length(); float jump_speed = ( vh + vy ).Length(); Mth::Vector heading = ( vh + vy ).Normalize(); pScript->GetParams()->AddVector( CRCD( 0xfd4bc03e, "heading" ), heading ); pScript->GetParams()->AddFloat( CRCD( 0x1c4c5690, "jumpSpeed" ), jump_speed ); pScript->GetParams()->AddFloat( CRCD( 0x66ced87b, "jumpTime" ), time ); break; } // @script | Ped_HitWaypoint | force the ped to register a hit on the // to waypoint, regardless of his position. Don't call this unless // you know exactly why you're doing it. case CRCC( 0x1affffb3, "Ped_HitWaypoint" ): HitWaypoint(); break; // @script | Ped_SetIsSkater | // @uparm 1 | 0 if not skater, anything else for skater case CRCC( 0x5c794c57, "Ped_SetIsSkater" ): { int is_skater; pParams->GetInteger( NONAME, &is_skater, Script::ASSERT ); if ( is_skater == 0 ) m_flags &= ~PEDLOGIC_IS_SKATER; else m_flags |= PEDLOGIC_IS_SKATER; break; } // @script | Ped_NearVertNode | true if the next node or the to node // is vert case CRCC( 0x5bb23915, "Ped_NearVertNode" ): return ( ( ( m_flags & PEDLOGIC_TO_NODE_IS_VERT ) || ( m_flags & PEDLOGIC_NEXT_NODE_IS_VERT ) || ( m_flags & PEDLOGIC_FROM_NODE_IS_VERT ) ) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE ); break; case CRCC( 0x56d9f55f, "Ped_PlayJumpSound" ): { //Dbg_MsgAssert( m_state == CRCD(0xa85af587,"generic_skater"), ( "%s\nPed_PlayJumpSound called on a non-skater ped %s",pScript->GetScriptInfo(),Script::FindChecksumName(GetObject()->GetID()) ) ); Obj::CSkaterSoundComponent *pSoundComponent = GetSkaterSoundComponentFromObject( GetObject() ); if ( pSoundComponent ) pSoundComponent->PlayJumpSound(m_speed_fraction); break; } case CRCC( 0x749de914, "Ped_PlayLandSound" ): { Dbg_MsgAssert( m_state == CRCD(0xa85af587,"generic_skater"), ( "Ped_PlayLandSound called on a non-ped skater" ) ); Obj::CSkaterSoundComponent *pSoundComponent = GetSkaterSoundComponentFromObject( GetObject() ); if ( pSoundComponent ) pSoundComponent->PlayLandSound(m_speed_fraction); break; } case CRCC( 0x58a588b5, "Ped_GetCurrentNodeNames" ): { pScript->GetParams()->AddChecksum( CRCD( 0xd14cd5bc, "node_to" ), m_node_to ); pScript->GetParams()->AddChecksum( CRCD( 0x8141d319, "node_from" ), m_node_from ); pScript->GetParams()->AddChecksum( CRCD( 0x3c746255, "node_next" ), m_node_next ); break; } default: return CBaseComponent::MF_NOT_EXECUTED; } return CBaseComponent::MF_TRUE; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::GetDebugInfo(Script::CStruct *p_info) { #ifdef __DEBUG_CODE__ Dbg_MsgAssert(p_info,("NULL p_info sent to C......Component::GetDebugInfo")); p_info->AddChecksum( "m_state", m_state ); Script::CArray* pTempArray = new Script::CArray(); pTempArray->SetSizeAndType( vNUM_WHISKERS, ESYMBOLTYPE_FLOAT ); for ( int i = 0; i < vNUM_WHISKERS; i++ ) pTempArray->SetFloat( i, m_whiskers[i] ); p_info->AddArray( "m_whiskers", pTempArray ); for ( int i = 0; i < vNUM_WHISKERS; i++ ) pTempArray->SetFloat( i, m_bias[i] ); p_info->AddArray( "m_bias", pTempArray ); Script::CleanUpArray( pTempArray ); delete pTempArray; p_info->AddInteger( "moving_on_path", m_flags & PEDLOGIC_MOVING_ON_PATH ); p_info->AddInteger( "m_node_from", m_node_from ); p_info->AddInteger( "m_node_to", m_node_to ); p_info->AddInteger( "m_node_next", m_node_next ); p_info->AddVector( "m_wp_from", m_wp_from ); p_info->AddVector( "m_wp_to", m_wp_to ); p_info->AddVector( "m_wp_next", m_wp_next ); p_info->AddInteger( "m_turn_frames", m_turn_frames ); p_info->AddInteger( "m_max_turn_frames", m_max_turn_frames ); p_info->AddInteger( "m_is_grinding", m_flags & PEDLOGIC_GRINDING ); p_info->AddFloat( "m_col_dist_above", m_col_dist_above ); p_info->AddFloat( "m_col_dist_below", m_col_dist_below ); CBaseComponent::GetDebugInfo(p_info); #endif } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::GenericPedUpdate() { if ( !( m_flags & PEDLOGIC_MOVING_ON_PATH ) ) return; Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() ); Dbg_Assert( pMotionComp ); // Obj::CPathObjectTracker* p_path_object_tracker = pMotionComp->mp_path_object_tracker; bool is_avoiding = CheckForOtherPeds(); bool is_turning = UpdateTargetBias(); // update bias based on path following if ( !is_turning && !is_avoiding ) { UpdatePathBias(); } // Gfx::AddDebugLine( m_wp_from, m_wp_to, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 1 ); // Gfx::AddDebugLine( GetObject()->m_pos, m_wp_to, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 1 ); // move and decay pMotionComp->DoPathPhysics(); UpdatePosition(); DecayWhiskerBiases(); pMotionComp->StickToGround(); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::GenericSkaterUpdate() { Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() ); Dbg_Assert( pMotionComp ); pMotionComp->DoPathPhysics(); float distance = m_time * pMotionComp->m_vel_z; bool is_jumping = pMotionComp->m_movingobj_status & MOVINGOBJ_STATUS_JUMPING ? true : false; // Gfx::AddDebugLine( m_wp_from, m_wp_to, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 1 ); // Gfx::AddDebugLine( GetObject()->m_pos, m_wp_to, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 1 ); // Gfx::AddDebugLine( m_pos_history[vPOS_HISTORY_SIZE - 2] + Mth::Vector( 0, 5, 1 ), m_pos_history[vPOS_HISTORY_SIZE - 1] + Mth::Vector( 0, 5, 1 ), MAKE_RGB( 0, 128, 0 ), MAKE_RGB( 0, 128, 0 ) ); // Gfx::AddDebugArrow( GetObject()->GetDisplayMatrix()[Z] + GetObject()->m_pos, GetObject()->GetDisplayMatrix()[Z] * 24 + GetObject()->m_pos, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 1 ); // check for skater if we're grinding if ( m_flags & PEDLOGIC_GRINDING ) { Mdl::Skate* skate_mod = Mdl::Skate::Instance(); Dbg_Assert( skate_mod ); Obj::CSkater* pSkater = skate_mod->GetLocalSkater(); if ( pSkater ) { float d = Mth::DistanceSqr( GetObject()->m_pos, pSkater->GetPos() ); // printf("skater dist square = %f\n", d ); if ( d < Script::GetFloat( CRCD( 0xbc2e678a, "ped_skater_min_square_distance_to_skater" ), Script::ASSERT ) ) { GrindBail(); return; } } } // see if we've hit the waypoint if ( ( m_flags & PEDLOGIC_MOVING_ON_PATH ) || is_jumping ) { Mth::Vector pt; if ( ( m_flags & PEDLOGIC_TO_NODE_IS_VERT ) || is_jumping ) { pt = ( GetObject()->m_pos - m_wp_to ); } else { Mth::Vector wp_to_no_y = m_wp_to; wp_to_no_y[Y] = 0.0f; Mth::Vector ped_pos_no_y = GetObject()->m_pos; ped_pos_no_y[Y] = 0.0f; pt = ped_pos_no_y - wp_to_no_y; } float distance_to_target_square = pt.LengthSqr(); int min_dist_square = Script::GetInteger( CRCD( 0xfb5d106f, "ped_skater_min_square_distance_to_waypoint" ), Script::ASSERT ); if ( !( m_flags & PEDLOGIC_JUMPING_TO_NODE ) ) { if ( distance_to_target_square < Mth::Sqr( distance ) ) HitWaypoint(); else if ( distance_to_target_square < min_dist_square ) HitWaypoint(); } if ( m_flags & PEDLOGIC_JUMP_AT_TO_NODE ) { int dist_to_crouch = Script::GetInteger( CRCD( 0x52ec432f, "ped_skater_min_square_distance_to_crouch_for_jump" ), Script::ASSERT ); if ( distance_to_target_square < dist_to_crouch ) { // clear flag! m_flags &= ~PEDLOGIC_JUMP_AT_TO_NODE; Script::CScript* pScript = Script::SpawnScript( CRCD( 0x9c7ab5f5, "ped_skater_crouch_for_jump" ) ); pScript->mpObject = GetObject(); } } } // move if ( ( m_flags & PEDLOGIC_MOVING_ON_PATH ) && distance ) { Mth::Vector original_pos = GetObject()->m_pos; bool already_adjusted_heading = false; if ( ShouldUseBiases() ) { // use biases to update position bool is_turning = UpdateTargetBias(); if ( !is_turning ) { UpdatePathBias(); } UpdatePosition(); // refresh the display matrix if we're jumping, as // AdjustHeading and AdjustNormal won't be called. if ( is_jumping ) { RefreshDisplayMatrix(); } } else { // figure the new position Mth::Vector new_pos = original_pos + ( distance * ( m_wp_to - GetObject()->m_pos ).Normalize() ); UpdatePathBias(); UpdatePosition(); GetObject()->m_pos = new_pos; AdjustHeading( original_pos ); already_adjusted_heading = true; } // stick to ground if we're not grinding or jumping if ( !( m_flags & PEDLOGIC_GRINDING ) && !( m_flags & PEDLOGIC_GRIND_BAILING ) && !is_jumping ) { StickToGround(); // update z vector if ( !already_adjusted_heading ) AdjustHeading( original_pos ); AdjustNormal(); } } else // if ( !( pMotionComp->m_movingobj_status & MOVINGOBJ_STATUS_JUMPING ) ) { RotatePosHistory(); if ( m_flags & PEDLOGIC_TO_NODE_IS_VERT ) { // AdjustHeading(); } RefreshDisplayMatrix(); if ( m_flags & PEDLOGIC_DOING_VERT_ROTATION || m_flags & PEDLOGIC_DOING_SPINE ) { DoRotations(); } } // update states for looping sounds if ( is_jumping ) { UpdateSkaterSoundStates( AIR ); } else if ( m_flags & PEDLOGIC_GRINDING ) { UpdateSkaterSoundStates( RAIL ); } else if ( m_flags & PEDLOGIC_MOVING_ON_PATH ) { UpdateSkaterSoundStates( GROUND ); } else if ( m_flags & PEDLOGIC_DOING_LIP_TRICK ) { UpdateSkaterSoundStates( LIP ); } if ( m_flags & PEDLOGIC_MOVING_ON_PATH ) m_speed_fraction = ( pMotionComp->m_vel_z / pMotionComp->m_max_vel ); else m_speed_fraction = 0.0f; Obj::CSkaterLoopingSoundComponent* pLoopingSoundComp = GetSkaterLoopingSoundComponentFromObject( GetObject() ); if ( pLoopingSoundComp ) { // reset bailing flag pLoopingSoundComp->SetIsBailing( m_flags & PEDLOGIC_GRIND_BAILING || m_flags & PEDLOGIC_BAILING ); pLoopingSoundComp->SetSpeedFraction( m_speed_fraction ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPedLogicComponent::CheckForOtherPeds() { bool is_avoiding = false; if ( mp_path_object_tracker ) { const CSmtPtr* pp_object_list = mp_path_object_tracker->GetObjectList(); Mth::Matrix display_matrix = GetObject()->GetDisplayMatrix(); float range = Script::GetFloat( CRCD( 0x7a78f471, "ped_avoid_ped_range" ) ); float avoid_ped_bias = Script::GetFloat( CRCD( 0xd1e6177b, "ped_avoid_ped_bias" ) ); Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() ); Dbg_Assert( pMotionComp ); for ( int i = 0; i < CPathObjectTracker::MAX_OBJECTS_PER_PATH; ++i ) { if ( pp_object_list[i] ) { CCompositeObject* p_ob=pp_object_list[i].Convert(); if ( p_ob != GetObject() ) { // ignore peds that are too far above or below float max_y_dist = Script::GetFloat( CRCD(0x21b13ffc,"ped_max_y_distance_to_ignore"), Script::ASSERT ); if ( Mth::Abs( GetObject()->GetPos()[Y] - p_ob->GetPos()[Y] ) > max_y_dist ) { continue; } // ignore peds that are facing the opposite direction and walking away float z_dot = Mth::DotProduct( display_matrix[Z], p_ob->GetDisplayMatrix()[Z] ); if ( z_dot <= 0.0f && Mth::DotProduct( display_matrix[Z], p_ob->GetPos() - GetObject()->GetPos() ) <= 0.0f ) { // printf("walking away from each other\n"); continue; } // ignore peds that are facing the same direction and going faster than us Obj::CMotionComponent* pObMotionComp = GetMotionComponentFromObject( p_ob ); Dbg_Assert( pObMotionComp ); if ( z_dot > 0.0f && pObMotionComp->m_max_vel >= pMotionComp->m_max_vel ) { // printf("he's going faster than me\n"); continue; } float d = Mth::Distance( GetObject()->GetPos(), p_ob->GetPos() ); if ( d <= range ) { // adjust whisker // find the heading float theta = acosf( z_dot / ( display_matrix[Z].Length() * p_ob->GetDisplayMatrix()[Z].Length() ) ); // printf("theta = %f\n", theta); // reduce to less than 2pi. if ( theta > Mth::PI * 2 ) theta -= Mth::PI * 2 * (int)( theta / ( Mth::PI * 2 ) ); // if they're coming straight at each other, we'll have to throw in some // sideways bias - we don't want them to stop moving or turn around // As they start to pass each other, the offset will naturally start // to create its own bias if ( fabs( theta - Mth::PI ) < Script::GetFloat( CRCD( 0xd2e22cec, "ped_head_on_range" ), Script::ASSERT ) ) { // printf("he's coming right for me!\n"); // left by default theta = Mth::PI / 2; // see if we should push them right // if ( theta > Mth::PI ) if ( ( Mth::CrossProduct( display_matrix[Z], p_ob->GetDisplayMatrix()[Z] ) )[Y] < 0 ) theta *= -1; } is_avoiding = true; AddWhiskerBias( theta, avoid_ped_bias, d, range ); } } } } } return is_avoiding; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::UpdatePathBias() { Mth::Vector wp_from_no_y = m_wp_from; wp_from_no_y[Y] = 0.0f; Mth::Vector wp_to_no_y = m_wp_to; wp_to_no_y[Y] = 0.0f; Mth::Vector ped_pos_no_y = GetObject()->m_pos; ped_pos_no_y[Y] = 0.0f; float max_dist_to_path = Script::GetFloat( CRCD( 0xa876aa2c, "ped_max_distance_to_path" ), Script::ASSERT ); Mth::Vector ft = wp_to_no_y - wp_from_no_y; Mth::Vector fp = ped_pos_no_y - wp_from_no_y; // need to store this vector so we can check the sign of y... // if it's negative, we're on one side of the path, positive for the other Mth::Vector ft_cross_fp = Mth::CrossProduct( ft, fp ); // get the direction we want to push the ped Mth::Vector closest_pos_on_path = ( ( Mth::DotProduct( fp, ft ) / ft.LengthSqr() ) * ft ) + m_wp_from; closest_pos_on_path[Y] = 0.0f; Mth::Vector pos_to_path = closest_pos_on_path - ped_pos_no_y; // Gfx::AddDebugArrow( GetObject()->m_pos, closest_pos_on_path, MAKE_RGB( 0, 128, 0 ), MAKE_RGB( 0, 128, 0 ), 1 ); // Mth::Matrix ob_matrix = GetObject()->m_matrix; Mth::Matrix display_matrix = GetObject()->GetDisplayMatrix(); float angle_to_path = Mth::GetAngle( display_matrix[Z], pos_to_path ); if ( angle_to_path > 360.0f ) angle_to_path -= 360.0f * (int)( angle_to_path / 360.0f ); if ( Mth::CrossProduct( display_matrix[Z], pos_to_path )[Y] > 0 ) angle_to_path = 360.0f - angle_to_path; // float path_length = Mth::Distance( m_wp_from, m_wp_to ); float path_length = Mth::Distance( wp_from_no_y, wp_to_no_y ); float current_distance = ft_cross_fp.Length() / path_length; // the bias should get stronger as we get farther away, approaching 1 as we // approach the max distance from the path float path_bias; if ( current_distance > max_dist_to_path ) path_bias = 1; else path_bias = 1 - ( ( max_dist_to_path - current_distance ) / max_dist_to_path ); // reset the bias to get back on the path if ( current_distance > Script::GetFloat( CRCD( 0x81085734, "ped_min_distance_to_path" ) ) ) AdjustBias( Mth::DegToRad( angle_to_path ), path_bias ); } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPedLogicComponent::UpdateTargetBias() { bool is_turning = false; Mth::Vector wp_to_no_y = m_wp_to; wp_to_no_y[Y] = 0.0f; Mth::Vector ped_pos_no_y = GetObject()->m_pos; ped_pos_no_y[Y] = 0.0f; Mth::Vector pt = ped_pos_no_y - wp_to_no_y; float distance_to_target_square = pt.LengthSqr(); float target_bias = Script::GetFloat( CRCD(0x57342672,"ped_target_node_bias"), Script::ASSERT ); ResetBias(); if ( m_node_next == m_node_from ) { float min_square_distance_to_dead_end = Script::GetFloat( CRCD( 0x65b46f32, "ped_walker_min_square_distance_to_dead_end" ), Script::ASSERT ); if ( distance_to_target_square <= min_square_distance_to_dead_end ) { HitWaypoint(); } } else { int fade_bias_max_distance_square; if ( m_flags & PEDLOGIC_IS_SKATER ) fade_bias_max_distance_square = Script::GetInteger( CRCD( 0x354beda1, "ped_skater_fade_target_bias_max_distance_square" ), Script::ASSERT ); else fade_bias_max_distance_square = Script::GetInteger( CRCD( 0x5b21ab0e, "ped_fade_target_bias_max_distance_square" ), Script::ASSERT ); if ( m_max_turn_frames && m_turn_frames >= m_max_turn_frames ) { HitWaypoint(); } else if ( m_node_next != -1 && distance_to_target_square < fade_bias_max_distance_square ) { // do we need to reset m_max_turn_frames? if ( !m_max_turn_frames ) { // figure how long it will take to get there if we don't turn Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() ); Dbg_Assert( pMotionComp ); Mth::Vector wp_from_no_y = m_wp_from; wp_from_no_y[Y] = 0.0f; Mth::Vector wp_to_no_y = m_wp_to; wp_to_no_y[Y] = 0.0f; Mth::Vector wp_next_no_y = m_wp_next; wp_next_no_y[Y] = 0.0f; Mth::Vector ft = wp_to_no_y - wp_from_no_y; Mth::Vector tn = wp_next_no_y - wp_to_no_y; float angle_between_waypoints = Mth::DegToRad( Mth::GetAngle( ft, tn ) ); float distance_to_waypoint_switch = sqrtf( ( 2 * distance_to_target_square ) * ( 1 + cosf( angle_between_waypoints ) ) ); int frames = (int)( ( sqrtf( distance_to_waypoint_switch ) / pMotionComp->m_max_vel ) * 60.0f ); // printf("frames = %i\n", frames); m_max_turn_frames = frames; if ( m_max_turn_frames <= 0 ) { m_max_turn_frames = 1; } // int frames = (int)( ( sqrtf( distance_to_target_square ) / pMotionComp->m_max_vel ) * 60.0f ); // m_max_turn_frames = frames / 2; } // add bias for new waypoint and adjust current target bias is_turning = true; // Guard against potential division by zero here. float target_bias_turn_percent; if( m_max_turn_frames == 0 ) target_bias_turn_percent = 0.0f; else target_bias_turn_percent = 0.5f * ( 1 - (float)( m_max_turn_frames - m_turn_frames ) / (float)m_max_turn_frames ); m_turn_frames++; AddTargetBias( m_wp_next, target_bias * target_bias_turn_percent ); target_bias -= target_bias * target_bias_turn_percent; } } AddTargetBias( m_wp_to, target_bias ); return is_turning; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::UpdatePosition() { Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() ); Dbg_Assert( pMotionComp ); float x_bias = GetTotalXBias(); float z_bias = GetTotalZBias(); // printf("x_bias = %f, z_bias = %f\n", x_bias, z_bias ); Mth::Vector last_pos = GetObject()->m_pos; RotatePosHistory(); float vel = pMotionComp->m_vel_z; float distance_to_travel = vel * m_time; float x_distance = distance_to_travel * x_bias; float z_distance = distance_to_travel * z_bias; /* float total_bias = x_bias + z_bias; if ( !total_bias ) return; float vel = pMotionComp->m_vel_z; float distance_to_travel = vel * m_time; float x_distance = distance_to_travel * ( ( total_bias - x_bias ) / total_bias ); if ( x_bias < 0 ) x_distance *= -1; float z_distance = distance_to_travel * ( ( total_bias - z_bias ) / total_bias ); if ( z_bias < 0 ) z_distance *= -1; */ // printf("x_dist = %f, z_dist = %f\n", x_distance, z_distance); // Mth::Matrix mat0 = GetObject()->m_matrix; Mth::Matrix mat0 = GetObject()->GetDisplayMatrix(); Mth::Vector z_vect = mat0[Z] * z_distance; Mth::Vector x_vect = mat0[X] * x_distance; // GetObject()->GetDisplayMatrix()[Z] = new_heading; // GetObject()->GetDisplayMatrix().OrthoNormalizeAbout( Z ); GetObject()->m_pos += x_vect; GetObject()->m_pos += z_vect; // skaters do their own headings if ( last_pos != GetObject()->m_pos && !( m_flags & PEDLOGIC_IS_SKATER ) ) AdjustHeading( last_pos ); /* float angle_to_new_heading = atan2( x_distance, z_distance ); if ( fabs( angle_to_new_heading ) > Script::GetFloat( "ped_max_angle_to_heading", Script::ASSERT ) ) { GetObject()->GetDisplayMatrix().Rotate( mat0[Y], angle_to_new_heading ); // mat0.Rotate( mat0[Y], angle_to_new_heading ); // GetObject()->m_matrix.Rotate( mat0[Y], angle_to_new_heading ); // printf( "angle_to_new_heading = %f\n", Mth::RadToDeg( angle_to_new_heading ) ); } */ // GetObject()->SetDisplayMatrix( mat0 ); // GetObject()->m_matrix = mat0; // GetObject()->m_pos += adjustment; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::RotatePosHistory() { for ( int i = 0; i < vPOS_HISTORY_SIZE - 1; i++ ) { m_pos_history[i] = m_pos_history[i + 1]; } m_pos_history[vPOS_HISTORY_SIZE - 1] = GetObject()->m_pos; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::AddTargetBias( Mth::Vector target, float target_bias ) { Mth::Matrix display_matrix = GetObject()->GetDisplayMatrix(); Mth::Vector ped_pos_no_y = GetObject()->m_pos; ped_pos_no_y[Y] = 0.0f; target[Y] = 0.0f; // Mth::Vector pt = GetObject()->m_pos - target; Mth::Vector pt = ped_pos_no_y - target; float angle_to_target = Mth::GetAngle( display_matrix[Z], pt ); angle_to_target += 180.0f; if ( angle_to_target > 360.0f ) angle_to_target -= 360.0f * (int)( angle_to_target / 360.0f ); if ( ( Mth::CrossProduct( display_matrix[Z], pt ) )[Y] > 0 ) angle_to_target = 360.0f - angle_to_target; // printf("angle_to_target = %f\n", angle_to_target ); // add bias to get to the target - the target_bias is constant AdjustBias( Mth::DegToRad( angle_to_target ), target_bias ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::AddWhiskerBias( float angle, float amount, float distance, float range ) { // for now, linearly decrease the bias amount within the range amount *= ( range - distance ) / range; float segment_arc = Mth::DegToRad( 360 / vNUM_WHISKERS ); for ( int i = 0; i < vNUM_WHISKERS; i++ ) { float angle_between = ( segment_arc * i ) - angle; // make sure this is in the right quandrant if ( fabs ( angle_between ) >= Mth::PI / 2 && fabs( angle_between ) <= 3 * Mth::PI / 2 ) continue; // float adjusted_amount = fabs( amount * sinf( angle_between ) ); float adjusted_amount = amount * cosf( angle_between ); // printf("adding %f to m_whiskers[%i]\n", adjusted_amount, i ); // add this bias to the whisker m_whiskers[i] += adjusted_amount; // cap absolute value to 1 if ( m_whiskers[i] > 1 ) m_whiskers[i] = 1; if ( m_whiskers[i] < -1 ) m_whiskers[i] = -1; } /* float segment_arc = Mth::DegToRad( 360 / vNUM_WHISKERS ); for ( int i = 0; i < vNUM_WHISKERS; i++ ) { float angle_between = fabs( ( segment_arc * i ) - angle ); // make sure this is less than 180 from the angle we're looking at if ( angle_between >= Mth::PI ) continue; // don't bother with maxed bias if ( m_whiskers[i] == 1 ) continue; // add this bias to the whisker m_whiskers[i] += fabs( amount * cos( angle_between ) ); // cap to 1 if ( m_whiskers[i] > 1 ) m_whiskers[i] = 1; } */ } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::AdjustBias( float angle, float amount ) { // printf("AdjustBias( %f, %f )\n", angle, amount ); // translate the bias to the whisker(s) // makes sure the angle isn't greater than 360 // printf("angle = %f\n", Mth::RadToDeg( angle ) ); float segment_arc = Mth::DegToRad( 360 / vNUM_WHISKERS ); for ( int i = 0; i < vNUM_WHISKERS; i++ ) { float angle_between = ( segment_arc * i ) - angle; // make sure this is in the right quandrant if ( fabs ( angle_between ) >= Mth::PI / 2 && fabs( angle_between ) <= 3 * Mth::PI / 2 ) continue; // float adjusted_amount = fabs( amount * sinf( angle_between ) ); float adjusted_amount = amount * cosf( angle_between ); // printf("adding %f to m_bias[%i]\n", adjusted_amount, i ); // add this bias to the whisker m_bias[i] += adjusted_amount; // cap absolute value to 1 if ( m_bias[i] > 1 ) m_bias[i] = 1; if ( m_bias[i] < -1 ) m_bias[i] = -1; } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::DecayWhiskerBiases() { // multiply each whisker by the decay factor float decay_factor = Script::GetFloat( CRCD( 0x98cd0c8c, "ped_whisker_decay_factor" ), Script::ASSERT ); float min_bias = Script::GetFloat( CRCD( 0x4aba9798, "ped_min_bias" ), Script::ASSERT ); for ( int i = 0; i < vNUM_WHISKERS; i++ ) { m_whiskers[i] *= decay_factor; if ( m_whiskers[i] < min_bias ) m_whiskers[i] = 0; } } /******************************************************************/ /* */ /* */ /******************************************************************/ float CPedLogicComponent::GetTotalXBias() { float total = 0.0f; float segment_arc = Mth::DegToRad( 360 / vNUM_WHISKERS ); for ( int i = 0; i < vNUM_WHISKERS; i++ ) { // float angle = Mth::PI - ( segment_arc * i ); float angle = segment_arc * i; // phase shift 180 // angle += Mth::PI; float whisker_total = m_whiskers[i] + m_bias[i]; // float whisker_total = m_whiskers[i]; // float whisker_total = m_bias[i]; if ( whisker_total > 1 ) whisker_total = 1; total += -sinf( angle ) * whisker_total; if ( total >= 1 ) return 1; if ( total <= -1 ) return -1; } if ( fabs( total ) < Script::GetFloat( CRCD( 0x4aba9798, "ped_min_bias" ), Script::ASSERT ) ) total = 0; // remember that we're 180 degrees out of phase return total; } /******************************************************************/ /* */ /* */ /******************************************************************/ float CPedLogicComponent::GetTotalZBias() { float total = 0.0f; float segment_arc = Mth::DegToRad( 360 / vNUM_WHISKERS ); for ( int i = 0; i < vNUM_WHISKERS; i++ ) { float angle = segment_arc * i; // phase shift 180 // angle += Mth::PI; float whisker_total = m_whiskers[i] + m_bias[i]; // float whisker_total = m_whiskers[i]; // float whisker_total = m_bias[i]; if ( whisker_total > 1 ) whisker_total = 1; if ( whisker_total < -1 ) whisker_total = -1; // printf("whisker_total[%i] = %f, angle = %f\n", i, whisker_total, angle); // 90 degree phase shift // total += cosf( angle ) * whisker_total; total += cosf( angle ) * whisker_total; if ( total >= 1) return 1; if ( total <= -1 ) return -1; } if ( fabs( total ) < Script::GetFloat( CRCD( 0x4aba9798, "ped_min_bias" ), Script::ASSERT ) ) total = 0; // return total; return total; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::ResetBias() { for ( int i = 0; i < vNUM_WHISKERS; i++ ) m_bias[i] = 0; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::HitWaypoint() { // printf( "CPedLogicComponent::HitWaypoint\n" ); int hit_node = m_node_to; int old_from_node = m_node_from; m_flags &= ~PEDLOGIC_FROM_NODE_IS_VERT; m_flags &= ~PEDLOGIC_NEXT_NODE_IS_VERT; if ( m_flags & PEDLOGIC_TO_NODE_IS_VERT ) m_flags |= PEDLOGIC_FROM_NODE_IS_VERT; m_flags &= ~PEDLOGIC_JUMPING_TO_NODE; m_node_from = m_node_to; m_node_to = m_node_next; m_wp_from = m_wp_to; m_wp_to = m_wp_next; // handle dead ends for ped walkers if ( !( m_flags & PEDLOGIC_IS_SKATER ) && old_from_node == m_node_to ) { Script::CScript* pDeadEndScript = Script::SpawnScript( CRCD( 0xa80f965e, "ped_walker_hit_dead_end" ) ); pDeadEndScript->mpObject = GetObject(); } // update m_to_node_is_vert for distance checks m_flags &= ~PEDLOGIC_TO_NODE_IS_VERT; // m_to_node_is_vert = false; Script::CStruct* pToNodeData = SkateScript::GetNode( m_node_to ); m_flags &= ~PEDLOGIC_TO_NODE_IS_LIP; uint32 to_node_type; if ( pToNodeData->GetChecksum( CRCD( 0x17ea37fb, "PedType" ), &to_node_type, Script::NO_ASSERT ) && to_node_type == CRCD( 0x54166acd, "skate" ) ) { uint32 to_node_action; pToNodeData->GetChecksum( CRCD( 0x963dc198, "SkateAction" ), &to_node_action, Script::ASSERT ); if ( WaypointIsVert( m_node_to ) ) { m_flags |= PEDLOGIC_TO_NODE_IS_VERT; // m_to_node_is_vert = true; } // check for a jump next - we'll need to crouch if we're not // already doing some action if ( to_node_action == CRCD( 0x7d9d0008, "vert_grab" ) || to_node_action == CRCD( 0x584cf9e9, "jump" ) ) { // make sure we're not coming from an action Script::CStruct* pFromNodeData = SkateScript::GetNode( m_node_from ); uint32 from_node_action; if ( pFromNodeData->GetChecksum( CRCD( 0x963dc198, "SkateAction" ), &from_node_action, Script::ASSERT ) ) { if ( from_node_action != CRCD( 0x255ed86f, "grind" ) && !( m_flags & PEDLOGIC_GRINDING ) && !( m_flags & PEDLOGIC_FROM_NODE_IS_VERT ) ) { m_flags |= PEDLOGIC_JUMP_AT_TO_NODE; } } } if ( to_node_action == CRCD( 0x554dbd64, "vert_lip" ) ) { m_flags |= PEDLOGIC_TO_NODE_IS_LIP; } } SelectNextWaypoint(); if ( WaypointIsVert( m_node_next ) ) m_flags |= PEDLOGIC_NEXT_NODE_IS_VERT; // we don't want to do any actions if we just came from a vert_grab node Script::CStruct* pOldFromNodeData = SkateScript::GetNode( old_from_node ); uint32 old_skate_action; Script::CStruct* pNodeData = SkateScript::GetNode( hit_node ); Dbg_Assert( pNodeData ); DoGenericNodeActions( pNodeData ); if ( !( pOldFromNodeData->GetChecksum( CRCD( 0x963dc198, "skateAction" ), &old_skate_action ) && old_skate_action == CRCD( 0x7d9d0008, "Vert_Grab" ) ) ) { uint32 waypoint_type; if ( pNodeData->GetChecksum( CRCD( 0x17ea37fb, "PedType" ), &waypoint_type, Script::NO_ASSERT ) ) { switch ( waypoint_type ) { case CRCC( 0x726e85aa, "walk" ): DoWalkActions( pNodeData ); break; case CRCC( 0x54166acd, "skate" ): DoSkateActions( pNodeData ); break; default: Dbg_MsgAssert( 0, ( "Waypoint has bad PedType %s", Script::FindChecksumName( waypoint_type ) ) ); } } } m_turn_frames = m_max_turn_frames = 0; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPedLogicComponent::WaypointIsVert( int waypoint ) { Script::CStruct* pNodeData = SkateScript::GetNode( waypoint ); uint32 node_action; if ( pNodeData->GetChecksum( CRCD( 0x963dc198, "SkateAction" ), &node_action, Script::NO_ASSERT ) ) { if ( node_action == CRCD( 0x7d9d0008, "vert_grab" ) || node_action == CRCD( 0x554dbd64, "vert_lip" ) || node_action == CRCD( 0xe6dfaec7, "vert_grind" ) || node_action == CRCD( 0xda0723da, "vert_land" ) || node_action == CRCD( 0xe8f91257, "vert_flip" ) || node_action == CRCD( 0xd5b4f014, "vert_jump" ) ) { return true; } } return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::DoWalkActions( Script::CStruct* pNodeData ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::DoGenericNodeActions( Script::CStruct* pNodeData ) { Dbg_Assert( pNodeData ); // check for special things we should do at this node if ( pNodeData->ContainsFlag( CRCD( 0x74e4bba5, "AdjustSpeed" ) ) ) { if ( ShouldExecuteAction( pNodeData, CRCD( 0x922c5b59, "AdjustSpeedWeight" ) ) ) { float percent; pNodeData->GetFloat( CRCD( 0xa04d16ec, "AdjustSpeedPercent" ), &percent, Script::ASSERT ); AdjustSpeed( percent ); } } if ( pNodeData->ContainsFlag( CRCD( 0x55d98ed7, "RunScript" ) ) ) { if ( ShouldExecuteAction( pNodeData, CRCD( 0x86c9ccce, "RunScriptWeight" ) ) ) { uint32 script_name; pNodeData->GetChecksum( CRCD( 0x64b4cd9d, "RunScriptName" ), &script_name, Script::ASSERT ); Script::CScript* pScript = Script::SpawnScript( script_name ); pScript->mpObject = GetObject(); } } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::DoSkateActions( Script::CStruct* pNodeData ) { // we don't want to do any actions if we're bailing if ( ( m_flags & PEDLOGIC_BAILING ) || ( m_flags & PEDLOGIC_GRIND_BAILING ) ) return; Dbg_Assert( pNodeData ); uint32 skate_action; if ( pNodeData->GetChecksum( CRCD( 0x963dc198, "SkateAction" ), &skate_action, Script::NO_ASSERT ) ) { switch ( skate_action ) { case CRCC( 0xec1cd520, "continue" ): break; case CRCC( 0x255ed86f, "grind" ): case CRCC( 0xe6dfaec7, "vert_grind" ): DoGrind( pNodeData ); break; case CRCC( 0xc870bce5, "grind_off" ): DoGrindOff( pNodeData ); break; case CRCC(0xe41199af,"grind_bail"): // ignore these nodes...we must have fallen here from a grind break; case CRCC(0xba1d1e94,"flip_trick"): DoFlipTrick( pNodeData ); break; case CRCC( 0xe8f91257, "vert_flip" ): DoFlipTrick( pNodeData, true ); break; case CRCC( 0x12797a1b, "grab_trick" ): DoGrab( pNodeData ); break; case CRCC( 0x7d9d0008, "vert_grab" ): DoGrab( pNodeData, true ); break; case CRCC( 0x554dbd64, "vert_lip" ): DoLipTrick( pNodeData ); break; case CRCC( 0xda0723da, "vert_land" ): break; case CRCC( 0x584cf9e9, "jump" ): DoJump( pNodeData ); break; case CRCC( 0xd5b4f014, "vert_jump" ): DoJump( pNodeData, true ); break; case CRCC( 0x6d3144bf, "Roll_off" ): DoRollOff( pNodeData ); break; case CRCC( 0xef24413b, "manual" ): DoManual( pNodeData ); break; case CRCC( 0x9403a0ed, "manual_down" ): DoManualDown( pNodeData ); break; case CRCC( 0x46a9e949, "stop" ): Stop( pNodeData ); break; default: Dbg_MsgAssert( 0, ( "DoSkateActions found unknown action %s\n", Script::FindChecksumName( skate_action ) ) ); break; } } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::SelectNextWaypoint() { // are we on a grind bail path? bool on_grind_bail_path = false; Script::CStruct* pNode = SkateScript::GetNode( m_node_from ); uint32 skate_action; if ( pNode->GetChecksum( CRCD( 0x963dc198, "SkateAction" ), &skate_action ) && skate_action == CRCD( 0xe41199af, "Grind_Bail" ) ) { on_grind_bail_path = true; } int waypoint; int numLinks; numLinks = SkateScript::GetNumLinks( m_node_to ); if ( numLinks == 0 ) { // turn around waypoint = m_node_from; } else if ( numLinks == 1 ) // shortcut if there's only one link { waypoint = SkateScript::GetLink( m_node_to, 0 ); // special case for jumping straight up in a pipe...land where you started if ( waypoint == m_node_from ) { Script::CStruct* pToNodeData = SkateScript::GetNode( m_node_to ); uint32 skate_action; if ( pToNodeData->GetChecksum( CRCD( 0x963dc198, "skateAction" ), &skate_action ) && skate_action == CRCD( 0x7d9d0008, "Vert_Grab" ) ) { waypoint = m_node_to; } } } else { // sort the links by priority Mth::Vector to_from_vector = m_wp_to - m_wp_from; int valid_high_priority_choices[20]; int valid_low_priority_choices[20]; Dbg_MsgAssert( numLinks < 20, ( "Too many links (max is 20) from node %i\n", m_node_to ) ); for ( int i = 0; i < numLinks; i++ ) { valid_high_priority_choices[i] = i; valid_low_priority_choices[i] = i; } int num_valid_low_choices = 0; int num_valid_high_choices = 0; int min_inner_angle = Script::GetInteger( CRCD( 0xfe65d822, "ped_min_inner_path_angle" ), Script::ASSERT ); // store the max angle, so we can return the best choice if we don't find any good ones float max_angle = 0.0f; int max_angle_index = 0; for ( int i = 0; i < numLinks; i++ ) { int test_node = SkateScript::GetLink( m_node_to, i ); if ( test_node != m_node_from ) { Script::CStruct* pNodeData = SkateScript::GetNode( test_node ); // ignore nodes that aren't PedAI waypoints uint32 class_type = 0; uint32 waypoint_type = 0; pNodeData->GetChecksum( CRCD( 0x12b4e660, "class" ), &class_type ); pNodeData->GetChecksum( CRCD( 0x7321a8d6, "Type" ), &waypoint_type ); if ( class_type != CRCD( 0x4c23a77e, "Waypoint" ) || waypoint_type != CRCD( 0xcba10ffa, "PedAI" ) ) continue; // ignore Grind_Bail nodes unless we're already on a grind_bail path if ( !on_grind_bail_path ) { uint32 skate_action; if ( pNodeData->GetChecksum( CRCD( 0x963dc198, "SkateAction" ), &skate_action ) && skate_action == CRCD( 0xe41199af, "Grind_Bail" ) ) { continue; } } Mth::Vector test_node_position; SkateScript::GetPosition( test_node, &test_node_position ); Mth::Vector to_test_vector = m_wp_to - test_node_position; float angle = Mth::GetAngle( to_from_vector, to_test_vector ); if ( angle > 360.0f ) angle -= 360.0f * (int)( angle / 360.0f ); // update max angle if ( angle > max_angle ) { max_angle = angle; max_angle_index = i; } if ( angle >= min_inner_angle ) { // sort nodes by priority uint32 priority; pNodeData->GetChecksum( CRCD( 0x9d5923d8, "priority" ), &priority, Script::ASSERT ); switch ( priority ) { case CRCC( 0xde7a971b, "normal" ): valid_high_priority_choices[num_valid_high_choices] = i; num_valid_high_choices++; break; case CRCC(0x6d77875e,"low"): valid_low_priority_choices[num_valid_low_choices] = i; num_valid_low_choices++; break; default: Dbg_MsgAssert( 0, ( "Ped node has invalid priority" ) ); break; } } } } // if there weren't any good choices, pick the best one if ( !( num_valid_high_choices || num_valid_low_choices ) ) { waypoint = SkateScript::GetLink( m_node_to, max_angle_index ); } else { bool find_low_priority = false; // decide if we should pick from a low priority or high priority node if ( num_valid_high_choices && num_valid_low_choices ) { float low_priority_probability = Script::GetFloat( "ped_low_priority_waypoint_probability", Script::ASSERT ); Dbg_MsgAssert( low_priority_probability < 1, ( "ped_low_priority_waypoint_probability is %f", low_priority_probability ) ); if ( Mth::Rnd( 100 ) <= ( low_priority_probability * 100 ) ) find_low_priority = true; } else if ( num_valid_low_choices ) find_low_priority = true; // select random index from valid choices if ( find_low_priority ) waypoint = SkateScript::GetLink( m_node_to, valid_low_priority_choices[Mth::Rnd( num_valid_low_choices )] ); else waypoint = SkateScript::GetLink( m_node_to, valid_high_priority_choices[Mth::Rnd( num_valid_high_choices )] ); } } // make sure we got something if ( waypoint == -1 ) { Dbg_MsgAssert( 0, ( "SelectNextWaypoint couldn't find a valid waypoint" ) ); } else { m_node_next = waypoint; SkateScript::GetPosition( m_node_next, &m_wp_next ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPedLogicComponent::ShouldExecuteAction( Script::CStruct* pNodeData, uint32 weight_name ) { float weight; pNodeData->GetFloat( weight_name, &weight, Script::ASSERT ); Dbg_MsgAssert( weight > 0 && weight <= 1, ( "Waypoint %s has a bad action weight of %f\n", Script::FindChecksumName( GetObject()->GetID() ), weight ) ); if ( weight > 0.99f ) return true; return ( Mth::Rnd( vACTION_WEIGHT_RESOLUTION ) < ( weight * vACTION_WEIGHT_RESOLUTION ) ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::AdjustSpeed( float percent ) { Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() ); pMotionComp->m_max_vel *= percent; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::AdjustNormal() { Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() ); Dbg_Assert( pMotionComp ); if ( pMotionComp ) { Obj::STriangle last_triangle = pMotionComp->m_last_triangle; Mth::Vector v1 = last_triangle.mpVertices[1] - last_triangle.mpVertices[0]; Mth::Vector v2 = last_triangle.mpVertices[2] - last_triangle.mpVertices[0]; Mth::Vector normal = ( Mth::CrossProduct( v1, v2 ) ).Normalize(); if ( normal[Y] < 0.0f ) normal = -normal; if ( normal != m_current_normal ) { m_current_normal = normal; // remember this, for detecting if it changes m_last_display_normal = m_display_normal; // remember start position for lerping m_normal_lerp = 1.0f; // set lerp counter } // if m_normal_lerp is 0.0, then we don't need to do anything, as // we should already be there if ( m_normal_lerp != 0.0f ) // if lerping { // If the last display normal is the same as the current normal // then we can't interpolate between them if ( m_last_display_normal == m_current_normal ) { m_normal_lerp = 0.0f; } else { // adjust lerp at constant speed from 1.0 to 0.0, accounting for framerate // m_normal_lerp -= NORMAL_LERP_SPEED * (mp_physics->m_time * 60.0f); m_normal_lerp -= 0.1f * m_time * 60.0f; // if gone all the way, then clear lerping values // and set m_display_normal to be the current face normal if (m_normal_lerp <= 0.0f) { m_normal_lerp = 0.0f; m_display_normal = m_current_normal; } else { // Still between old and current normal, so // calculate intermediate normal m_display_normal = Mth::Lerp( m_current_normal, m_last_display_normal, m_normal_lerp ); m_display_normal.Normalize(); // Must be normalized... } } } // Now update the orientation matrix. // We need our up (Y) vector to be this vector // if it changes, rotate the X and Z vectors to match Mth::Matrix display_matrix = GetObject()->GetDisplayMatrix(); if ( display_matrix[Y] != m_display_normal ) { display_matrix[Y] = m_display_normal; display_matrix.OrthoNormalizeAbout( Y ); GetObject()->SetDisplayMatrix( display_matrix ); m_current_display_matrix = display_matrix; } } } /******************************************************************/ /* */ /* */ /******************************************************************/ // testing out positional history void CPedLogicComponent::AdjustHeading() { AdjustHeading( m_pos_history[0] ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::AdjustHeading( Mth::Vector original_pos ) { Mth::Matrix mat0 = GetObject()->GetDisplayMatrix(); Mth::Vector new_pos = GetObject()->m_pos; if ( m_flags & PEDLOGIC_TO_NODE_IS_LIP ) { original_pos = m_wp_from; new_pos = m_wp_to; } // Gfx::AddDebugLine( original_pos, new_pos, MAKE_RGB( 0, 128, 0 ), MAKE_RGB( 0, 128, 0 ) ); // change z vector mat0[Z] = ( new_pos - original_pos ).Normalize(); // get new X and Y vectors mat0[X] = Mth::CrossProduct( mat0[Y], mat0[Z] ); mat0[Y] = Mth::CrossProduct( mat0[Z], mat0[X] ); GetObject()->SetDisplayMatrix( mat0 ); m_current_display_matrix = mat0; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::RefreshDisplayMatrix() { GetObject()->SetDisplayMatrix( m_current_display_matrix ); } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPedLogicComponent::do_collision_check( Mth::Vector *p_v0, Mth::Vector *p_v1, Mth::Vector *p_collisionPoint, Obj::STriangle *p_lastTriangle ) { // First, see if there is a collision with the last triangle, and if there is, use that. CFeeler feeler; if ( feeler.GetCollision( *p_v0, *p_v1, false ) ) { Nx::CCollObj* p_col_obj=feeler.GetSector(); Dbg_MsgAssert( p_col_obj->IsTriangleCollision(), ( "Not triangle collision !!!" ) ); Nx::CCollObjTriData* p_tri_data=p_col_obj->GetGeometry(); Dbg_MsgAssert( p_tri_data, ( "NULL p_tri_data" ) ); int face_index=feeler.GetFaceIndex(); p_lastTriangle->mpVertices[0]=p_col_obj->GetVertexPos(p_tri_data->GetFaceVertIndex(face_index, 0)); p_lastTriangle->mpVertices[1]=p_col_obj->GetVertexPos(p_tri_data->GetFaceVertIndex(face_index, 1)); p_lastTriangle->mpVertices[2]=p_col_obj->GetVertexPos(p_tri_data->GetFaceVertIndex(face_index, 2)); // if there is a collision, then snap to it *p_collisionPoint = feeler.GetPoint(); UpdateSkaterSoundTerrain( feeler.GetTerrain() ); return true; } return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::StickToGround() { // stick to ground modified from the motion component...uses // the ped's normal instead of the world's Y Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() ); Dbg_Assert( pMotionComp ); Mth::Vector v0, v1; // make sure our X vector is correct (got Z from the nav matrix...) GetObject()->m_matrix[X] = Mth::CrossProduct( GetObject()->m_matrix[Y], GetObject()->m_matrix[Z] ); GetObject()->m_matrix[X].Normalize(); Mth::Matrix display_matrix = GetObject()->GetDisplayMatrix(); v0 = GetObject()->m_pos + ( display_matrix[Y] * m_col_dist_above ); v1 = GetObject()->m_pos + ( -display_matrix[Y] * m_col_dist_below ); Mth::Vector collision_point; if ( do_collision_check( &v0, &v1, &collision_point, &( pMotionComp->m_last_triangle ) ) ) { GetObject()->m_pos = collision_point; // GetObject()->GetDisplayMatrix()[Mth::POS] = collision_point; } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::UpdateSkaterSoundStates( EStateType state ) { Dbg_MsgAssert( m_state == CRCD( 0xa85af587, "generic_skater" ), ( "CPedLogicComponent::UpdateSkaterSoundStates called on a non-skater ped" ) ); Obj::CSkaterLoopingSoundComponent *pLoopingSoundComponent = GetSkaterLoopingSoundComponentFromObject( GetObject() ); if ( pLoopingSoundComponent ) pLoopingSoundComponent->SetState( state ); Obj::CSkaterSoundComponent *pSoundComponent = GetSkaterSoundComponentFromObject( GetObject() ); if ( pSoundComponent ) pSoundComponent->SetState( state ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::UpdateSkaterSoundTerrain( ETerrainType terrain ) { Dbg_MsgAssert( m_state == CRCD(0xa85af587,"generic_skater"), ( "CPedLogicComponent::UpdateSkaterSoundTerrain called on non-skater ped" ) ); // set the terrain type for looping sound Obj::CSkaterLoopingSoundComponent* pLoopingSoundComp = GetSkaterLoopingSoundComponentFromObject( GetObject() ); if ( pLoopingSoundComp ) pLoopingSoundComp->SetTerrain( terrain ); Obj::CSkaterSoundComponent *pSoundComponent = GetSkaterSoundComponentFromObject( GetObject() ); if ( pSoundComponent ) { pSoundComponent->SetTerrain( terrain ); pSoundComponent->SetLastTerrain( terrain ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ bool CPedLogicComponent::ShouldUseBiases() { return ( m_flags & PEDLOGIC_IS_SKATER && !( m_flags & PEDLOGIC_GRINDING ) && !( m_flags & PEDLOGIC_GRIND_BAILING ) && !( m_flags & PEDLOGIC_TO_NODE_IS_VERT ) && !( m_flags & PEDLOGIC_FROM_NODE_IS_VERT ) && !( m_flags & PEDLOGIC_JUMPING_TO_NODE ) ); } /******************************************************************/ /* */ /* Skate Actions! */ /* */ /******************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ Script::CStruct* CPedLogicComponent::GetSkateActionParams( Script::CStruct* pNodeData ) { Script::CStruct* pScriptParams = new Script::CStruct(); // give higher priority to ped skater data - should this be reversed? pScriptParams->AppendStructure( pNodeData ); pScriptParams->AppendStructure( GetObject()->GetTags() ); return pScriptParams; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::DoGrind( Script::CStruct* pNodeData ) { // printf("CPedLogicComponent::DoGrind\n"); StopSkateActions(); // m_is_grinding = true; m_flags |= PEDLOGIC_GRINDING; // set the terrain uint32 terrain_checksum; // TODO: Assert if terrain missing if ( pNodeData->GetChecksum( CRCD( 0x54cf8532, "TerrainType" ), &terrain_checksum, Script::NO_ASSERT ) ) { UpdateSkaterSoundTerrain( Env::CTerrainManager::sGetTerrainFromChecksum( terrain_checksum ) ); } Script::CStruct* pScriptParams = GetSkateActionParams( pNodeData ); Script::CScript* pScript = Script::SpawnScript( CRCD( 0xb7fca430, "ped_skater_grind" ), pScriptParams ); pScript->mpObject = GetObject(); delete pScriptParams; } /******************************************************************/ /* */ /* */ /******************************************************************/ /* void CPedLogicComponent::GrindUpdate() { m_grind_wobble_frames--; if ( m_grind_wobble_frames <= 0 ) { printf("setting the wobble target to %f\n", m_grind_wobble_target); // set new wobble target Obj::CAnimationComponent* pAnimComp = GetAnimationComponentFromObject( GetObject() ); Dbg_Assert( pAnimComp ); pAnimComp->SetWobbleTarget( m_grind_wobble_target, false ); m_grind_wobble_target = -m_grind_wobble_target; m_grind_wobble_frames = Script::GetInteger( "ped_skater_grind_wobble_frames", Script::ASSERT ); } } */ /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::DoGrindOff( Script::CStruct* pNodeData ) { if ( m_flags & PEDLOGIC_GRINDING ) { // m_is_grinding = false; m_flags &= ~PEDLOGIC_GRINDING; Script::CScript* pScript = Script::SpawnScript( CRCD( 0x84c51e26, "ped_skater_grind_off" ) ); pScript->mpObject = GetObject(); // delete pScriptParams; } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::DoJump( Script::CStruct* pNodeData, bool is_vert ) { StopSkateActions(); // call Obj_Jump on the object Script::CStruct* pScriptParams; Script::CScript* pScript; if ( is_vert ) { m_flags &= ~PEDLOGIC_MOVING_ON_PATH; pScriptParams = GetJumpParams( pNodeData, is_vert ); pScript = Script::SpawnScript( CRCD( 0x990152d7, "ped_skater_vert_jump" ), pScriptParams ); } else { if ( pNodeData->ContainsFlag( CRCD( 0xb02567ae, "JumpToNextNode" ) ) ) { m_flags &= ~PEDLOGIC_MOVING_ON_PATH; pScriptParams = GetJumpParams( pNodeData, is_vert ); m_flags |= PEDLOGIC_JUMPING_TO_NODE; } else { pScriptParams = GetSkateActionParams( pNodeData ); } pScript = Script::SpawnScript( CRCD( 0x28be3d25, "ped_skater_jump" ), pScriptParams ); } pScript->mpObject = GetObject(); delete pScriptParams; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::DoLipTrick( Script::CStruct* pNodeData ) { StopSkateActions(); // stop moving on path m_flags &= ~PEDLOGIC_MOVING_ON_PATH; UpdateLipDisplayMatrix(); Script::CStruct* pScriptParams = GetSkateActionParams( pNodeData ); Script::CScript* pScript = Script::SpawnScript( CRCD( 0x2fd2b4b8, "ped_skater_lip_trick" ), pScriptParams ); pScript->mpObject = GetObject(); delete pScriptParams; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::UpdateLipDisplayMatrix() { // set the display matrix Mth::Matrix mat0 = GetObject()->GetDisplayMatrix(); // change z vector mat0[Z] = Mth::Vector(0, 1, 0); mat0[Y] = m_last_display_normal.Normalize(); // get new X and Y vectors mat0[X] = Mth::CrossProduct( mat0[Y], mat0[Z] ).Normalize(); mat0[Y] = Mth::CrossProduct( mat0[Z], mat0[X] ).Normalize(); GetObject()->SetDisplayMatrix( mat0 ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::DoManual( Script::CStruct* pNodeData ) { StopSkateActions(); Script::CStruct* pScriptParams = GetSkateActionParams( pNodeData ); Script::CScript* pScript = Script::SpawnScript( CRCD( 0x1462af22, "ped_skater_manual" ), pScriptParams ); pScript->mpObject = GetObject(); delete pScriptParams; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::DoManualDown( Script::CStruct* pNodeData ) { StopSkateActions(); Script::CStruct* pScriptParams = GetSkateActionParams( pNodeData ); Script::CScript* pScript = Script::SpawnScript( CRCD( 0x4c0caa11, "ped_skater_manual_down" ), pScriptParams ); pScript->mpObject = GetObject(); delete pScriptParams; } /******************************************************************/ /* */ /* */ /******************************************************************/ Script::CStruct* CPedLogicComponent::GetJumpParams( Script::CStruct* pNodeData, bool is_vert ) { StopSkateActions(); // stop moving on path m_flags &= ~PEDLOGIC_DOING_VERT_ROTATION; m_flags &= ~PEDLOGIC_DOING_SPINE; // reset angles m_spine_current_angle = 0.0f; m_spine_start_angle = 0.0f; m_rot_current_angle = 0.0f; Script::CStruct* pScriptParams = GetSkateActionParams( pNodeData ); // figure heading and time // get the height and gravity float height; pNodeData->GetFloat( CRCD( 0x838da447, "jumpHeight" ), &height, Script::ASSERT ); float gravity = Script::GetFloat( CRCD( 0xe224a5d3, "ped_skater_jump_gravity" ), Script::ASSERT ); float time_total; if ( is_vert || pNodeData->ContainsFlag( CRCD( 0xb02567ae, "JumpToNextNode" ) ) ) { Mth::Vector pt = m_wp_to - GetObject()->m_pos; pt[Y] = 0.0f; if ( GetObject()->m_pos[Y] + height < m_wp_to[Y] ) { height = m_wp_to[Y] - GetObject()->m_pos[Y] + Script::GetFloat( CRCD( 0x23f14b13, "ped_skater_jump_to_next_node_height_slop" ), Script::ASSERT ); // Script::PrintContents( pNodeData ); // Dbg_MsgAssert( 0, ( "JumpToNextNode selected but jumpHeight isn't enough to reach target node." ) ); } // adjust height to compensate for difference between ped height // and height of waypoint // height += pt[Y]; // pt[Y] = 0.0f; // figure the time to rise // d = vi*t + (a*t^2)/2 Dbg_Assert( gravity < 0 ); float time_to_rise = sqrtf( 2 * -height / gravity ); // figure the time to fall float time_to_fall = sqrtf( 2 * Mth::Abs( ( GetObject()->m_pos[Y] + height - m_wp_to[Y] ) / gravity ) ); // compute the inital y velocity float vi = ( height / time_to_rise ) - ( 0.5f * gravity * time_to_rise ); Dbg_Assert( vi > 0 ); // find the horizontal distance we need to travel float distance = pt.Length(); // horizontal velocity time_total = time_to_rise + time_to_fall; Mth::Vector vh = ( distance / time_total ) * pt.Normalize(); // vertical velocity Mth::Vector vy = vi * Mth::Vector(0, 1, 0); // total speed and heading float jump_speed = ( vh + vy ).Length(); Mth::Vector heading = ( vh + vy ).Normalize(); pScriptParams->AddVector( CRCD( 0xfd4bc03e, "heading" ), heading ); pScriptParams->AddFloat( CRCD( 0x1c4c5690, "jumpSpeed" ), jump_speed ); pScriptParams->AddFloat( CRCD( 0x66ced87b, "jumpTime" ), time_total ); } else { // just use the jump height float jump_speed = sqrtf( 2 * gravity * -height ); pScriptParams->AddFloat( CRCD(0x1c4c5690,"jumpSpeed"), jump_speed ); time_total = 2 * sqrtf( 2 * -height * gravity ); } // setup any rotation int rot_angle = 0; float rot_float; if ( pNodeData->ContainsFlag( CRCD( 0xb4077854, "RandomSpin" ) ) ) { // figure the spin angle based on the time we've got float min_180_time = Script::GetFloat( CRCD( 0x8b381ab1, "ped_skater_min_180_spin_time" ), Script::ASSERT ); int mult = (int)( time_total / min_180_time ); // cap the rotation to 720 degrees if ( mult > 4 ) mult = 4; rot_angle = 180 * Mth::Rnd( mult + 1 ); } else if ( pNodeData->GetFloat( CRCD( 0x96fb50d9, "SpinAngle" ), &rot_float, Script::NO_ASSERT ) ) { // cast to int but make sure it doesn't get rounded down rot_angle = (int)( rot_float + 0.01f ); } rot_angle = abs( rot_angle ); if ( rot_angle > 0 ) { // figure fs or bs uint32 spin_direction; pNodeData->GetChecksum( CRCD( 0xef0b71a0, "SpinDirection" ), &spin_direction, Script::ASSERT ); Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() ); bool is_flipped = pAnimationComponent->IsFlipped(); switch ( spin_direction ) { case CRCC( 0x20e1c4a3, "BS" ): { if ( is_flipped ) { rot_angle *= -1; } break; } case CRCC( 0x448d01a7, "FS" ): { if ( !is_flipped ) { rot_angle *= -1; } break; } case CRCC( 0xe7390a8b, "Rand" ): { // randomly change direction int toggle = Mth::Rnd( 2 ); if ( toggle == 1 ) { rot_angle *= -1; } break; } default: Dbg_MsgAssert( 0, ( "Unknown SpinDirection" ) ); break; } m_rot_angle = rot_angle; m_flags |= PEDLOGIC_DOING_VERT_ROTATION; } bool is_spine = pNodeData->ContainsFlag( CRCD( 0x4aa0cff, "SpineTransfer" ) ); if ( rot_angle % 360 == 0 ) { if ( !is_spine ) pScriptParams->AddChecksum( NONAME, CRCD( 0x8868e76a, "should_flip" ) ); } else if ( is_spine ) { pScriptParams->AddChecksum( NONAME, CRCD( 0x8868e76a, "should_flip" ) ); } // setup any spine trasnfer if ( is_spine ) { m_flags |= PEDLOGIC_DOING_SPINE; // initialize spine angle to current angle between ped and vert Mth::Vector temp_y(0, 1, 0); float spine_start = Mth::GetAngle( m_current_display_matrix[Z], temp_y ); m_spine_start_angle = spine_start + Script::GetFloat( CRCD(0x95e91d23,"ped_skater_spine_rotation_slop") ); } if ( m_flags & PEDLOGIC_DOING_SPINE || m_flags & PEDLOGIC_DOING_VERT_ROTATION ) { m_rot_start_matrix = m_current_display_matrix; m_rot_total_time = time_total * Script::GetFloat( CRCD( 0xc1dfc97b, "ped_skater_vert_rotation_time_slop" ), Script::ASSERT ); m_rot_current_time = 0.0f; } // check if we're jumping to the next node if ( pNodeData->ContainsFlag( CRCD( 0xb02567ae, "JumpToNextNode" ) ) ) { pScriptParams->AddFloat( CRCD( 0xa1810c27, "land_height" ), m_wp_to[Y] ); } return pScriptParams; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::DoGrab( Script::CStruct* pNodeData, bool is_vert ) { Script::CStruct* pScriptParams; if ( is_vert ) { // Obj::CAnimationComponent* pAnimComp = GetAnimationComponentFromObject( GetObject() ); // printf("flipped = %i\n", pAnimComp->IsFlipped() ); m_flags &= ~PEDLOGIC_MOVING_ON_PATH; pScriptParams = GetJumpParams( pNodeData, is_vert ); pScriptParams->AddChecksum( NONAME, CRCD( 0x1615618, "is_vert" ) ); } else { pScriptParams = GetSkateActionParams( pNodeData ); // see if we're already jumping Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() ); if ( pMotionComp->m_movingobj_status & MOVINGOBJ_STATUS_JUMPING ) pScriptParams->AddInteger( CRCD( 0x65b5788d, "is_jumping" ), 1 ); else { // figure out how long before we start to fall float jumpSpeed = Script::GetFloat( CRCD( 0xf1ff758a, "ped_skater_jump_speed" ), Script::ASSERT ); float jumpGravity = Script::GetFloat( CRCD( 0xe224a5d3, "ped_skater_jump_gravity" ), Script::ASSERT ); float time = 2 * sqrtf( Mth::Sqr( jumpSpeed ) / Mth::Sqr( jumpGravity ) ); pScriptParams->AddInteger( CRCD( 0x66ced87b, "jumpTime" ), time ); pScriptParams->AddInteger( CRCD( 0x65b5788d, "is_jumping" ), 0 ); } } Script::CScript* pScript = Script::SpawnScript( CRCD( 0x10585cb3, "ped_skater_grab_trick" ), pScriptParams ); pScript->mpObject = GetObject(); delete pScriptParams; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::DoRotations() { m_rot_current_time += m_time; float percent = m_time / m_rot_total_time; if ( percent > 1.0f ) percent = 1.0f; float spine_target_angle = 180.0f - m_spine_start_angle; if ( m_flags & PEDLOGIC_DOING_VERT_ROTATION ) m_rot_current_angle += percent * (float)m_rot_angle; if ( m_flags & PEDLOGIC_DOING_SPINE ) m_spine_current_angle += percent * spine_target_angle; if ( m_rot_current_time >= m_rot_total_time || percent >= 1.0f ) { if ( m_flags & PEDLOGIC_DOING_VERT_ROTATION ) m_rot_current_angle = (float)m_rot_angle; if ( m_flags & PEDLOGIC_DOING_SPINE ) m_spine_current_angle = spine_target_angle; m_flags &= ~PEDLOGIC_DOING_VERT_ROTATION; m_flags &= ~PEDLOGIC_DOING_SPINE; } // cap spine angle if ( m_flags & PEDLOGIC_DOING_SPINE && m_spine_current_angle > spine_target_angle ) { m_spine_current_angle = spine_target_angle; } Mth::Matrix temp = m_rot_start_matrix; temp.RotateLocal( Mth::Vector( Mth::DegToRad( m_spine_current_angle ), Mth::DegToRad( m_rot_current_angle ), 0 ) ); GetObject()->SetDisplayMatrix( temp ); m_current_display_matrix = temp; m_display_normal = m_last_display_normal = m_current_normal = temp[Y]; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::Stop( Script::CStruct* pNodeData ) { StopSkateActions(); m_flags |= PEDLOGIC_STOPPED; Script::CStruct* pScriptParams = GetSkateActionParams( pNodeData ); Script::CScript* pScript = Script::SpawnScript( CRCD( 0x365b2d85, "ped_skater_stop" ), pScriptParams ); pScript->mpObject = GetObject(); delete pScriptParams; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::StopSkateActions() { // BB - I moved this from script because it was too damn slow! Script::CArray* p_scripts_to_stop = Script::GetArray( CRCD( 0x2ecd9f57, "ped_skater_action_scripts" ), Script::ASSERT ); int array_size = p_scripts_to_stop->GetSize(); Obj::CObject* p_object = (Obj::CObject*)GetObject(); for ( int i = 0; i < array_size; i++ ) { uint32 script_to_stop = p_scripts_to_stop->GetChecksum( i ); Script::StopScriptsUsingThisObject_Proper( p_object, script_to_stop ); } // m_is_grinding = false; m_flags &= ~PEDLOGIC_GRINDING; m_flags &= ~PEDLOGIC_DOING_VERT_ROTATION; m_flags &= ~PEDLOGIC_DOING_SPINE; } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::Bail() { // kill any running bail scripts Script::StopScriptsUsingThisObject_Proper( GetObject(), CRCD(0xe630bf07,"ped_skater_grind_bail") ); Script::StopScriptsUsingThisObject_Proper( GetObject(), CRCD(0x78131e4f,"ped_skater_generic_bail") ); // restore the max current velocity before calling this script again! if ( m_original_max_vel > 0 ) { Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() ); Dbg_Assert( pMotionComp ); pMotionComp->m_max_vel = m_original_max_vel; } Script::CScript* pScript; if ( m_flags & PEDLOGIC_GRINDING ) { pScript = Script::SpawnScript( CRCD(0xe630bf07,"ped_skater_grind_bail") ); } else { pScript = Script::SpawnScript( CRCD(0x78131e4f,"ped_skater_generic_bail") ); } StopSkateActions(); pScript->mpObject = GetObject(); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::GrindBail() { if ( m_flags & PEDLOGIC_GRINDING ) { m_flags &= ~PEDLOGIC_GRINDING; m_flags |= PEDLOGIC_GRIND_BAILING; // select a new next node int num_links = SkateScript::GetNumLinks( m_node_to ); // if there's only one link, we have to keep going...shitty if ( num_links > 1 ) { // find a grind_bail node! for ( int i = 0; i < num_links; i++ ) { int test_node = SkateScript::GetLink( m_node_to, i ); Script::CStruct* pNodeData = SkateScript::GetNode( test_node ); uint32 skate_action; if ( pNodeData->GetChecksum( CRCD( 0x963dc198, "skateAction" ), &skate_action ) && skate_action == CRCD( 0xe41199af, "grind_bail" ) ) { m_node_next = test_node; SkateScript::GetPosition( m_node_next, &m_wp_next ); break; } } } // run script! Script::CScript* pScript = Script::SpawnScript( CRCD( 0xe630bf07, "ped_skater_grind_bail" ) ); pScript->mpObject = GetObject(); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::DoRollOff( Script::CStruct* pNodeData ) { Script::CScript* pScript = Script::SpawnScript( CRCD( 0xb183d99e, "ped_skater_roll_off" ) ); pScript->mpObject = GetObject(); } /******************************************************************/ /* */ /* */ /******************************************************************/ void CPedLogicComponent::DoFlipTrick( Script::CStruct* pNodeData, bool is_vert ) { StopSkateActions(); // jump if we need to Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() ); Dbg_Assert( pMotionComp ); Script::CStruct* pScriptParams = GetSkateActionParams( pNodeData ); Script::CStruct *pJumpParams = GetJumpParams( pNodeData, is_vert ); pScriptParams->AppendStructure( pJumpParams ); delete pJumpParams; if ( is_vert ) { m_flags &= ~PEDLOGIC_MOVING_ON_PATH; pScriptParams->AddChecksum( NONAME, CRCD( 0x1615618, "is_vert" ) ); } // check jumping state if ( pMotionComp->m_movingobj_status & MOVINGOBJ_STATUS_JUMPING ) pScriptParams->AddInteger( CRCD( 0x65b5788d, "is_jumping" ), 1 ); else pScriptParams->AddInteger( CRCD( 0x65b5788d, "is_jumping" ), 0 ); if ( pNodeData->ContainsFlag( CRCD( 0xb02567ae, "JumpToNextNode" ) ) ) { m_flags &= ~PEDLOGIC_MOVING_ON_PATH; m_flags |= PEDLOGIC_JUMPING_TO_NODE; } Script::CScript *pScript = Script::SpawnScript( CRCD( 0xb83c383c, "ped_skater_flip_trick" ), pScriptParams ); pScript->mpObject = GetObject(); delete pScriptParams; } /******************************************************************/ /* */ /* */ /******************************************************************/ }