#include "common.h" #include "main.h" #include "RpAnimBlend.h" #include "AnimBlendClumpData.h" #include "AnimBlendAssociation.h" #include "Camera.h" #include "CarCtrl.h" #include "Darkel.h" #include "DMAudio.h" #include "FileMgr.h" #include "General.h" #include "Object.h" #include "Pad.h" #include "Particle.h" #include "Ped.h" #include "PlayerPed.h" #include "Stats.h" #include "TempColModels.h" #include "VisibilityPlugins.h" #include "Vehicle.h" #include "Automobile.h" #include "WaterLevel.h" #include "World.h" uint16 nPlayerInComboMove; RpClump *flyingClumpTemp; // This is beta fistfite.dat array. Not used anymore since they're being fetched from fistfite.dat. FightMove tFightMoves[NUM_FIGHTMOVES] = { {ANIM_STD_NUM, 0.0f, 0.0f, 0.0f, 0.0f, HITLEVEL_NULL, 0, 0}, {ANIM_STD_PUNCH, 0.2f, 8.0f / 30.0f, 0.0f, 0.3f, HITLEVEL_HIGH, 1, 0}, {ANIM_STD_FIGHT_IDLE, 0.0f, 0.0f, 0.0f, 0.0f, HITLEVEL_NULL, 0, 0}, {ANIM_STD_FIGHT_SHUFFLE_F, 0.0f, 0.0f, 0.0f, 0.0f, HITLEVEL_NULL, 0, 0}, {ANIM_STD_FIGHT_KNEE, 4.0f / 30.0f, 0.2f, 0.0f, 0.6f, HITLEVEL_LOW, 2, 0}, {ANIM_STD_FIGHT_HEAD, 4.0f / 30.0f, 0.2f, 0.0f, 0.7f, HITLEVEL_HIGH, 3, 0}, {ANIM_STD_FIGHT_PUNCH, 4.0f / 30.0f, 7.0f / 30.0f, 10.0f / 30.0f, 0.4f, HITLEVEL_HIGH, 1, 0}, {ANIM_STD_FIGHT_LHOOK, 8.0f / 30.0f, 10.0f / 30.0f, 0.0f, 0.4f, HITLEVEL_HIGH, 3, 0}, {ANIM_STD_FIGHT_KICK, 8.0f / 30.0f, 10.0f / 30.0f, 0.0f, 0.5, HITLEVEL_MEDIUM, 2, 0}, {ANIM_STD_FIGHT_LONGKICK, 8.0f / 30.0f, 10.0f / 30.0f, 0.0f, 0.5, HITLEVEL_MEDIUM, 4, 0}, {ANIM_STD_FIGHT_ROUNDHOUSE, 8.0f / 30.0f, 10.0f / 30.0f, 0.0f, 0.6f, HITLEVEL_MEDIUM, 4, 0}, {ANIM_STD_FIGHT_BODYBLOW, 5.0f / 30.0f, 7.0f / 30.0f, 0.0f, 0.35f, HITLEVEL_LOW, 2, 0}, {ANIM_STD_KICKGROUND, 10.0f / 30.0f, 14.0f / 30.0f, 0.0f, 0.4f, HITLEVEL_GROUND, 1, 0}, {ANIM_STD_HIT_FRONT, 0.0f, 0.0f, 0.0f, 0.0f, HITLEVEL_NULL, 0, 0}, {ANIM_STD_HIT_BACK, 0.0f, 0.0f, 0.0f, 0.0f, HITLEVEL_NULL, 0, 0}, {ANIM_STD_HIT_RIGHT, 0.0f, 0.0f, 0.0f, 0.0f, HITLEVEL_NULL, 0, 0}, {ANIM_STD_HIT_LEFT, 0.0f, 0.0f, 0.0f, 0.0f, HITLEVEL_NULL, 0, 0}, {ANIM_STD_HIT_BODYBLOW, 0.0f, 0.0f, 0.0f, 0.0f, HITLEVEL_NULL, 0, 0}, {ANIM_STD_HIT_CHEST, 0.0f, 0.0f, 0.0f, 0.0f, HITLEVEL_NULL, 0, 0}, {ANIM_STD_HIT_HEAD, 0.0f, 0.0f, 0.0f, 0.0f, HITLEVEL_NULL, 0, 0}, {ANIM_STD_HIT_WALK, 0.0f, 0.0f, 0.0f, 0.0f, HITLEVEL_NULL, 0, 0}, {ANIM_STD_HIT_FLOOR, 0.0f, 0.0f, 0.0f, 0.0f, HITLEVEL_NULL, 0, 0}, {ANIM_STD_HIT_BEHIND, 0.0f, 0.0f, 0.0f, 0.0f, HITLEVEL_NULL, 0, 0}, {ANIM_STD_FIGHT_2IDLE, 0.0f, 0.0f, 0.0f, 0.0f, HITLEVEL_NULL, 0, 0}, }; static PedOnGroundState CheckForPedsOnGroundToAttack(CPed *attacker, CPed **pedOnGround) { PedOnGroundState stateToReturn; float angleToFace; CPed *currentPed = nil; PedState currentPedState; CPed *pedOnTheFloor = nil; CPed *deadPed = nil; CPed *pedBelow = nil; bool foundDead = false; bool foundOnTheFloor = false; bool foundBelow = false; float angleDiff; float distance; if (!CGame::nastyGame) return NO_PED; for (int currentPedId = 0; currentPedId < attacker->m_numNearPeds; currentPedId++) { currentPed = attacker->m_nearPeds[currentPedId]; CVector posDifference = currentPed->GetPosition() - attacker->GetPosition(); distance = posDifference.Magnitude(); if (distance < 2.0f) { angleToFace = CGeneral::GetRadianAngleBetweenPoints( currentPed->GetPosition().x, currentPed->GetPosition().y, attacker->GetPosition().x, attacker->GetPosition().y); angleToFace = CGeneral::LimitRadianAngle(angleToFace); attacker->m_fRotationCur = CGeneral::LimitRadianAngle(attacker->m_fRotationCur); angleDiff = Abs(angleToFace - attacker->m_fRotationCur); if (angleDiff > PI) angleDiff = 2 * PI - angleDiff; currentPedState = currentPed->m_nPedState; if (currentPed->OnGroundOrGettingUp()) { if (distance < 2.0f && angleDiff < DEGTORAD(65.0f)) { if (currentPedState == PED_DEAD) { foundDead = 1; if (!deadPed) deadPed = currentPed; } else if (!currentPed->IsPedHeadAbovePos(-0.6f)) { foundOnTheFloor = 1; if (!pedOnTheFloor) pedOnTheFloor = currentPed; } } } else if ((distance < 0.8f && angleDiff < DEGTORAD(75.0f)) || (distance < 1.3f && angleDiff < DEGTORAD(55.0f)) || (distance < 1.7f && angleDiff < DEGTORAD(35.0f)) || (distance < 2.0f && angleDiff < DEGTORAD(30.0f))) { // Either this condition or below one was probably returning 4 early in development. See Fight(). foundBelow = 1; pedBelow = currentPed; break; } else { if (angleDiff < DEGTORAD(75.0f)) { foundBelow = 1; if (!pedBelow) pedBelow = currentPed; } } } } if (foundOnTheFloor) { currentPed = pedOnTheFloor; stateToReturn = PED_ON_THE_FLOOR; } else if (foundDead) { currentPed = deadPed; stateToReturn = PED_DEAD_ON_THE_FLOOR; } else if (foundBelow) { currentPed = pedBelow; stateToReturn = PED_IN_FRONT_OF_ATTACKER; } else { currentPed = nil; stateToReturn = NO_PED; } if (pedOnGround) *pedOnGround = currentPed; return stateToReturn; } void CPed::SetPointGunAt(CEntity *to) { if (to) { SetLookFlag(to, true); SetAimFlag(to); #ifdef VC_PED_PORTS SetLookTimer(INT32_MAX); #endif } if (m_nPedState == PED_AIM_GUN || bIsDucking || m_nWaitState == WAITSTATE_PLAYANIM_DUCK) return; if (m_nPedState != PED_ATTACK) SetStoredState(); SetPedState(PED_AIM_GUN); bIsPointingGunAt = true; CWeaponInfo *curWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); SetMoveState(PEDMOVE_NONE); CAnimBlendAssociation *aimAssoc; if (bCrouchWhenShooting) aimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), curWeapon->m_Anim2ToPlay); else aimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), curWeapon->m_AnimToPlay); if (!aimAssoc || aimAssoc->blendDelta < 0.0f) { if (bCrouchWhenShooting) aimAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, curWeapon->m_Anim2ToPlay, 4.0f); else aimAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, curWeapon->m_AnimToPlay); aimAssoc->blendAmount = 0.0f; aimAssoc->blendDelta = 8.0f; } if (to) Say(SOUND_PED_ATTACK); } void CPed::PointGunAt(void) { CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); CAnimBlendAssociation *weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), weaponInfo->m_AnimToPlay); if (!weaponAssoc || weaponAssoc->blendDelta < 0.0f) weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), weaponInfo->m_Anim2ToPlay); if (weaponAssoc && weaponAssoc->currentTime > weaponInfo->m_fAnimLoopStart) { weaponAssoc->SetCurrentTime(weaponInfo->m_fAnimLoopStart); weaponAssoc->flags &= ~ASSOC_RUNNING; if (weaponInfo->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM)) m_pedIK.m_flags |= CPedIK::AIMS_WITH_ARM; else m_pedIK.m_flags &= ~CPedIK::AIMS_WITH_ARM; } } void CPed::ClearPointGunAt(void) { CAnimBlendAssociation *animAssoc; CWeaponInfo *weaponInfo; ClearLookFlag(); ClearAimFlag(); bIsPointingGunAt = false; #ifndef VC_PED_PORTS if (m_nPedState == PED_AIM_GUN) { RestorePreviousState(); #else if (m_nPedState == PED_AIM_GUN || m_nPedState == PED_ATTACK) { SetPedState(PED_IDLE); RestorePreviousState(); } #endif weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), weaponInfo->m_AnimToPlay); if (!animAssoc || animAssoc->blendDelta < 0.0f) { animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), weaponInfo->m_Anim2ToPlay); } if (animAssoc) { animAssoc->flags |= ASSOC_DELETEFADEDOUT; animAssoc->blendDelta = -4.0f; } #ifndef VC_PED_PORTS } #endif } void CPed::SetAttack(CEntity *victim) { CPed *victimPed = nil; if (victim && victim->IsPed()) victimPed = (CPed*)victim; CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_BIGGUN); if (animAssoc) { animAssoc->blendDelta = -1000.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; } if (m_attackTimer > CTimer::GetTimeInMilliseconds() || m_nWaitState == WAITSTATE_SURPRISE) return; if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_HGUN_RELOAD)) { bIsAttacking = false; return; } if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_AK_RELOAD)) { if (!IsPlayer() || m_nPedState != PED_ATTACK || ((CPlayerPed*)this)->m_bHaveTargetSelected) bIsAttacking = false; else bIsAttacking = true; return; } CWeaponInfo *curWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); if (curWeapon->m_eWeaponFire == WEAPON_FIRE_INSTANT_HIT && !IsPlayer()) { if (GetWeapon()->HitsGround(this, nil, victim)) return; } if (GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED) { if (IsPlayer() || (m_nPedState != PED_FIGHT && m_nMoveState != PEDMOVE_NONE && m_nMoveState != PEDMOVE_STILL && !(m_pedStats->m_flags & STAT_SHOPPING_BAGS))) { if (m_nPedState != PED_ATTACK) { SetPedState(PED_ATTACK); bIsAttacking = false; animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, curWeapon->m_AnimToPlay, 8.0f); animAssoc->SetRun(); if (animAssoc->currentTime == animAssoc->hierarchy->totalLength) animAssoc->SetCurrentTime(0.0f); animAssoc->SetFinishCallback(FinishedAttackCB, this); } } else { StartFightAttack(CGeneral::GetRandomNumber() % 256); } return; } m_pSeekTarget = victim; if (m_pSeekTarget) m_pSeekTarget->RegisterReference((CEntity **) &m_pSeekTarget); if (curWeapon->IsFlagSet(WEAPONFLAG_CANAIM)) { CVector aimPos = GetRight() * 0.1f + GetForward() * 0.2f + GetPosition(); CEntity *obstacle = CWorld::TestSphereAgainstWorld(aimPos, 0.2f, nil, true, false, false, true, false, false); if (obstacle) return; m_pLookTarget = victim; if (victim) { m_pLookTarget->RegisterReference((CEntity **) &m_pLookTarget); m_pSeekTarget->RegisterReference((CEntity **) &m_pSeekTarget); } if (m_pLookTarget) { SetAimFlag(m_pLookTarget); } else { SetAimFlag(m_fRotationCur); if (FindPlayerPed() == this && TheCamera.Cams[0].Using3rdPersonMouseCam()) ((CPlayerPed*)this)->m_fFPSMoveHeading = TheCamera.Find3rdPersonQuickAimPitch(); } } if (m_nPedState == PED_ATTACK) { bIsAttacking = true; return; } if (IsPlayer() || !victimPed || victimPed->IsPedInControl()) { if (IsPlayer()) CPad::GetPad(0)->ResetAverageWeapon(); uint8 pointBlankStatus; if ((curWeapon->m_eWeaponFire == WEAPON_FIRE_INSTANT_HIT || GetWeapon()->m_eWeaponType == WEAPONTYPE_FLAMETHROWER) && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_M16_1STPERSON && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_M16_1STPERSON_RUNABOUT && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_SNIPER && TheCamera.PlayerWeaponMode.Mode != CCam::MODE_SNIPER_RUNABOUT && (pointBlankStatus = CheckForPointBlankPeds(victimPed)) != NO_POINT_BLANK_PED) { ClearAimFlag(); // This condition is pointless if (pointBlankStatus == POINT_BLANK_FOR_WANTED_PED || !victimPed) StartFightAttack(200); } else { if (!curWeapon->IsFlagSet(WEAPONFLAG_CANAIM)) m_pSeekTarget = nil; if (m_nPedState != PED_AIM_GUN) SetStoredState(); SetPedState(PED_ATTACK); SetMoveState(PEDMOVE_NONE); if (bCrouchWhenShooting) { animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_RBLOCK_SHOOT, 4.0f); } else { float animDelta = 8.0f; if (curWeapon->m_eWeaponFire == WEAPON_FIRE_MELEE) animDelta = 1000.0f; if (GetWeapon()->m_eWeaponType != WEAPONTYPE_BASEBALLBAT || CheckForPedsOnGroundToAttack(this, nil) < PED_ON_THE_FLOOR) { animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, curWeapon->m_AnimToPlay, animDelta); } else { animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, curWeapon->m_Anim2ToPlay, animDelta); } } animAssoc->SetRun(); if (animAssoc->currentTime == animAssoc->hierarchy->totalLength) animAssoc->SetCurrentTime(0.0f); animAssoc->SetFinishCallback(FinishedAttackCB, this); } return; } if (GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT && victimPed->m_nPedState == PED_GETUP) SetWaitState(WAITSTATE_SURPRISE, nil); SetLookFlag(victim, false); SetLookTimer(100); } void CPed::ClearAttack(void) { if (m_nPedState != PED_ATTACK || bIsDucking || m_nWaitState == WAITSTATE_PLAYANIM_DUCK) return; #ifdef VC_PED_PORTS // VC uses CCamera::Using1stPersonWeaponMode if (FindPlayerPed() == this && (TheCamera.PlayerWeaponMode.Mode == CCam::MODE_SNIPER || TheCamera.PlayerWeaponMode.Mode == CCam::MODE_M16_1STPERSON || TheCamera.PlayerWeaponMode.Mode == CCam::MODE_ROCKETLAUNCHER)) { SetPointGunAt(nil); } else #endif if (bIsPointingGunAt) { if (m_pLookTarget) SetPointGunAt(m_pLookTarget); else ClearPointGunAt(); } else if (m_objective != OBJECTIVE_NONE) { SetIdle(); } else { RestorePreviousState(); } } void CPed::ClearAttackByRemovingAnim(void) { if (m_nPedState != PED_ATTACK || bIsDucking) return; CWeaponInfo *weapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); CAnimBlendAssociation *weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), weapon->m_AnimToPlay); if (!weaponAssoc) { weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), weapon->m_Anim2ToPlay); if (!weaponAssoc && weapon->IsFlagSet(WEAPONFLAG_THROW)) weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_THROW_UNDER); if (!weaponAssoc) { ClearAttack(); return; } } weaponAssoc->blendDelta = -8.0f; weaponAssoc->flags &= ~ASSOC_RUNNING; weaponAssoc->flags |= ASSOC_DELETEFADEDOUT; weaponAssoc->SetDeleteCallback(FinishedAttackCB, this); } void CPed::FinishedAttackCB(CAnimBlendAssociation *attackAssoc, void *arg) { CWeaponInfo *currentWeapon; CAnimBlendAssociation *newAnim; CPed *ped = (CPed*)arg; if (attackAssoc) { switch (attackAssoc->animId) { case ANIM_STD_START_THROW: // what?! if ((!ped->IsPlayer() || ((CPlayerPed*)ped)->m_bHaveTargetSelected) && ped->IsPlayer()) { attackAssoc->blendDelta = -1000.0f; newAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_THROW_UNDER); } else { attackAssoc->blendDelta = -1000.0f; newAnim = CAnimManager::AddAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_STD_WEAPON_THROW); } newAnim->SetFinishCallback(FinishedAttackCB, ped); return; case ANIM_STD_PARTIAL_PUNCH: attackAssoc->blendDelta = -8.0f; attackAssoc->flags |= ASSOC_DELETEFADEDOUT; ped->ClearAttack(); return; case ANIM_STD_WEAPON_THROW: case ANIM_STD_THROW_UNDER: if (ped->GetWeapon()->m_nAmmoTotal > 0) { currentWeapon = CWeaponInfo::GetWeaponInfo(ped->GetWeapon()->m_eWeaponType); ped->AddWeaponModel(currentWeapon->m_nModelId); } break; default: break; } } if (!ped->bIsAttacking) ped->ClearAttack(); } uint8 CPed::CheckForPointBlankPeds(CPed *pedToVerify) { float pbDistance = 1.1f; if (GetWeapon()->IsType2Handed()) pbDistance = 1.6f; for (int i = 0; i < m_numNearPeds; i++) { CPed *nearPed = m_nearPeds[i]; if (!pedToVerify || pedToVerify == nearPed) { CVector diff = nearPed->GetPosition() - GetPosition(); if (diff.Magnitude() < pbDistance) { float neededAngle = CGeneral::GetRadianAngleBetweenPoints( nearPed->GetPosition().x, nearPed->GetPosition().y, GetPosition().x, GetPosition().y); neededAngle = CGeneral::LimitRadianAngle(neededAngle); m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); float neededTurn = Abs(neededAngle - m_fRotationCur); if (neededTurn > PI) neededTurn = 2*PI - neededTurn; if (nearPed->OnGroundOrGettingUp() || nearPed->m_nPedState == PED_DIVE_AWAY) return NO_POINT_BLANK_PED; if (neededTurn < CAN_SEE_ENTITY_ANGLE_THRESHOLD) { if (pedToVerify == nearPed) return POINT_BLANK_FOR_WANTED_PED; else return POINT_BLANK_FOR_SOMEONE_ELSE; } } } } return NO_POINT_BLANK_PED; } void CPed::Attack(void) { CAnimBlendAssociation *weaponAnimAssoc; int32 weaponAnim; float animStart; float weaponAnimTime; float animLoopEnd; CWeaponInfo *ourWeapon; bool attackShouldContinue; AnimationId reloadAnim; CAnimBlendAssociation *reloadAnimAssoc; float delayBetweenAnimAndFire; CVector firePos; ourWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ourWeapon->m_AnimToPlay); attackShouldContinue = bIsAttacking; reloadAnimAssoc = nil; reloadAnim = ANIM_STD_NUM; delayBetweenAnimAndFire = ourWeapon->m_fAnimFrameFire; weaponAnim = ourWeapon->m_AnimToPlay; if (weaponAnim == ANIM_STD_WEAPON_HGUN_BODY) reloadAnim = ANIM_STD_HGUN_RELOAD; else if (weaponAnim == ANIM_STD_WEAPON_AK_BODY) reloadAnim = ANIM_STD_AK_RELOAD; if (reloadAnim != ANIM_STD_NUM) reloadAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), reloadAnim); if (bIsDucking) return; if (reloadAnimAssoc) { if (!IsPlayer() || ((CPlayerPed*)this)->m_bHaveTargetSelected) ClearAttack(); return; } if (CTimer::GetTimeInMilliseconds() < m_shootTimer) attackShouldContinue = true; if (!weaponAnimAssoc) { weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ourWeapon->m_Anim2ToPlay); delayBetweenAnimAndFire = ourWeapon->m_fAnim2FrameFire; // Long throw granade, molotov if (!weaponAnimAssoc && ourWeapon->IsFlagSet(WEAPONFLAG_THROW)) { weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_THROW_UNDER); delayBetweenAnimAndFire = 0.2f; } if (!weaponAnimAssoc) { if (attackShouldContinue) { if (ourWeapon->m_eWeaponFire != WEAPON_FIRE_PROJECTILE || !IsPlayer() || ((CPlayerPed*)this)->m_bHaveTargetSelected) { if (!CGame::nastyGame || ourWeapon->m_eWeaponFire != WEAPON_FIRE_MELEE || CheckForPedsOnGroundToAttack(this, nil) < PED_ON_THE_FLOOR) { weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ourWeapon->m_AnimToPlay, 8.0f); } else { weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ourWeapon->m_Anim2ToPlay, 8.0f); } weaponAnimAssoc->SetFinishCallback(FinishedAttackCB, this); weaponAnimAssoc->SetRun(); if (weaponAnimAssoc->currentTime == weaponAnimAssoc->hierarchy->totalLength) weaponAnimAssoc->SetCurrentTime(0.0f); if (IsPlayer()) { ((CPlayerPed*)this)->m_fAttackButtonCounter = 0.0f; ((CPlayerPed*)this)->m_bHaveTargetSelected = false; } } } else FinishedAttackCB(nil, this); return; } } animStart = ourWeapon->m_fAnimLoopStart; weaponAnimTime = weaponAnimAssoc->currentTime; if (weaponAnimTime > animStart && weaponAnimTime - weaponAnimAssoc->timeStep <= animStart) { if (ourWeapon->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM)) m_pedIK.m_flags |= CPedIK::AIMS_WITH_ARM; else m_pedIK.m_flags &= ~CPedIK::AIMS_WITH_ARM; } if (weaponAnimTime <= delayBetweenAnimAndFire || weaponAnimTime - weaponAnimAssoc->timeStep > delayBetweenAnimAndFire || !weaponAnimAssoc->IsRunning()) { if (weaponAnimAssoc->speed < 1.0f) weaponAnimAssoc->speed = 1.0f; } else { firePos = ourWeapon->m_vecFireOffset; if (GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT) { if (weaponAnimAssoc->animId == ourWeapon->m_Anim2ToPlay) firePos.z = 0.7f * ourWeapon->m_fRadius - 1.0f; firePos = GetMatrix() * firePos; } else if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) { TransformToNode(firePos, weaponAnimAssoc->animId == ANIM_STD_KICKGROUND ? PED_FOOTR : PED_HANDR); } else { firePos = GetMatrix() * firePos; } GetWeapon()->Fire(this, &firePos); if (GetWeapon()->m_eWeaponType == WEAPONTYPE_MOLOTOV || GetWeapon()->m_eWeaponType == WEAPONTYPE_GRENADE) { RemoveWeaponModel(ourWeapon->m_nModelId); } if (!GetWeapon()->m_nAmmoTotal && ourWeapon->m_eWeaponFire != WEAPON_FIRE_MELEE && FindPlayerPed() != this) { SelectGunIfArmed(); } if (GetWeapon()->m_eWeaponState != WEAPONSTATE_MELEE_MADECONTACT) { // If reloading just began, start the animation // Last condition will always return true, even IDA hides it if (GetWeapon()->m_eWeaponState == WEAPONSTATE_RELOADING && reloadAnim != ANIM_STD_NUM /* && !reloadAnimAssoc*/) { CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, reloadAnim, 8.0f); ClearLookFlag(); ClearAimFlag(); bIsAttacking = false; bIsPointingGunAt = false; m_shootTimer = CTimer::GetTimeInMilliseconds(); return; } } else { if (weaponAnimAssoc->animId == ANIM_STD_WEAPON_BAT_V || weaponAnimAssoc->animId == ANIM_STD_WEAPON_BAT_H) { DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_BAT_ATTACK, 1.0f); } else if (weaponAnimAssoc->animId == ANIM_STD_PARTIAL_PUNCH) { DMAudio.PlayOneShot(m_audioEntityId, SOUND_FIGHT_PUNCH_39, 0.0f); } weaponAnimAssoc->speed = 0.5f; if (bIsAttacking || CTimer::GetTimeInMilliseconds() < m_shootTimer) { weaponAnimAssoc->callbackType = 0; } } attackShouldContinue = false; } if (GetWeapon()->m_eWeaponType == WEAPONTYPE_SHOTGUN) { weaponAnimTime = weaponAnimAssoc->currentTime; firePos = ourWeapon->m_vecFireOffset; if (weaponAnimTime > 1.0f && weaponAnimTime - weaponAnimAssoc->timeStep <= 1.0f && weaponAnimAssoc->IsRunning()) { TransformToNode(firePos, PED_HANDR); CVector gunshellPos( firePos.x - 0.6f * GetForward().x, firePos.y - 0.6f * GetForward().y, firePos.z - 0.15f * GetUp().z ); CVector2D gunshellRot( GetRight().x, GetRight().y ); gunshellRot.Normalise(); GetWeapon()->AddGunshell(this, gunshellPos, gunshellRot, 0.025f); } } #ifdef VC_PED_PORTS if (IsPlayer()) { if (CPad::GetPad(0)->GetSprint()) { // animBreakout is a member of WeaponInfo in VC, so it's me that added the below line. float animBreakOut = ((GetWeapon()->m_eWeaponType == WEAPONTYPE_FLAMETHROWER || GetWeapon()->m_eWeaponType == WEAPONTYPE_UZI || GetWeapon()->m_eWeaponType == WEAPONTYPE_SHOTGUN) ? 25 / 30.0f : 99 / 30.0f); if (!attackShouldContinue && weaponAnimAssoc->currentTime > animBreakOut) { weaponAnimAssoc->blendDelta = -4.0f; FinishedAttackCB(nil, this); return; } } } #endif animLoopEnd = ourWeapon->m_fAnimLoopEnd; if (ourWeapon->m_eWeaponFire == WEAPON_FIRE_MELEE && weaponAnimAssoc->animId == ourWeapon->m_Anim2ToPlay) animLoopEnd = 3.4f/6.0f; weaponAnimTime = weaponAnimAssoc->currentTime; // Anim loop end, either start the loop again or finish the attack if (weaponAnimTime > animLoopEnd || !weaponAnimAssoc->IsRunning() && ourWeapon->m_eWeaponFire != WEAPON_FIRE_PROJECTILE) { if (weaponAnimTime - 2.0f * weaponAnimAssoc->timeStep <= animLoopEnd && (bIsAttacking || CTimer::GetTimeInMilliseconds() < m_shootTimer) && GetWeapon()->m_eWeaponState != WEAPONSTATE_RELOADING) { weaponAnim = weaponAnimAssoc->animId; if (ourWeapon->m_eWeaponFire != WEAPON_FIRE_MELEE || CheckForPedsOnGroundToAttack(this, nil) < PED_ON_THE_FLOOR) { if (weaponAnim != ourWeapon->m_Anim2ToPlay || weaponAnim == ANIM_STD_RBLOCK_SHOOT) { weaponAnimAssoc->Start(ourWeapon->m_fAnimLoopStart); } else { CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ourWeapon->m_AnimToPlay, 8.0f); } } else { if (weaponAnim == ourWeapon->m_Anim2ToPlay) weaponAnimAssoc->SetCurrentTime(0.1f); else CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ourWeapon->m_Anim2ToPlay, 8.0f); } #ifdef VC_PED_PORTS } else if (IsPlayer() && m_pPointGunAt && bIsAimingGun && GetWeapon()->m_eWeaponState != WEAPONSTATE_RELOADING) { weaponAnimAssoc->SetCurrentTime(ourWeapon->m_fAnimLoopEnd); weaponAnimAssoc->flags &= ~ASSOC_RUNNING; SetPointGunAt(m_pPointGunAt); #endif } else { ClearAimFlag(); // Echoes of bullets, at the end of the attack. (Bug: doesn't play while reloading) if (weaponAnimAssoc->currentTime - weaponAnimAssoc->timeStep <= ourWeapon->m_fAnimLoopEnd) { switch (GetWeapon()->m_eWeaponType) { case WEAPONTYPE_UZI: DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_UZI_BULLET_ECHO, 0.0f); break; case WEAPONTYPE_AK47: DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_AK47_BULLET_ECHO, 0.0f); break; case WEAPONTYPE_M16: DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_M16_BULLET_ECHO, 0.0f); break; default: break; } } // Fun fact: removing this part leds to reloading flamethrower if (GetWeapon()->m_eWeaponType == WEAPONTYPE_FLAMETHROWER && weaponAnimAssoc->IsRunning()) { weaponAnimAssoc->flags |= ASSOC_DELETEFADEDOUT; weaponAnimAssoc->flags &= ~ASSOC_RUNNING; weaponAnimAssoc->blendDelta = -4.0f; } } } if (weaponAnimAssoc->currentTime > delayBetweenAnimAndFire) attackShouldContinue = false; bIsAttacking = attackShouldContinue; } void CPed::StartFightAttack(uint8 buttonPressure) { if (!IsPedInControl() || m_attackTimer > CTimer::GetTimeInMilliseconds()) return; if (m_nPedState == PED_FIGHT) { m_fightButtonPressure = buttonPressure; return; } if (m_nPedState != PED_AIM_GUN) SetStoredState(); if (m_nWaitState != WAITSTATE_FALSE) { m_nWaitState = WAITSTATE_FALSE; RestoreHeadingRate(); } SetPedState(PED_FIGHT); m_fightButtonPressure = 0; RpAnimBlendClumpRemoveAssociations(GetClump(), ASSOC_REPEAT); CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_STARTWALK); if (animAssoc) { animAssoc->flags |= ASSOC_DELETEFADEDOUT; animAssoc->blendDelta = -1000.0f; } animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNSTOP1); if (!animAssoc) animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNSTOP2); if (animAssoc) { animAssoc->flags |= ASSOC_DELETEFADEDOUT; animAssoc->blendDelta = -1000.0f; RestoreHeadingRate(); } SetMoveState(PEDMOVE_NONE); m_nStoredMoveState = PEDMOVE_NONE; CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FIGHT_IDLE)->blendAmount = 1.0f; CPed *pedOnGround = nil; if (IsPlayer() && CheckForPedsOnGroundToAttack(this, &pedOnGround) > PED_IN_FRONT_OF_ATTACKER) { m_curFightMove = FIGHTMOVE_GROUNDKICK; } else if (m_pedStats->m_flags & STAT_SHOPPING_BAGS) { m_curFightMove = FIGHTMOVE_ROUNDHOUSE; } else { m_curFightMove = FIGHTMOVE_STDPUNCH; } if (pedOnGround && IsPlayer()) { m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints( pedOnGround->GetPosition().x, pedOnGround->GetPosition().y, GetPosition().x, GetPosition().y); m_fRotationDest = CGeneral::LimitRadianAngle(m_fRotationDest); m_fRotationCur = m_fRotationDest; m_lookTimer = 0; SetLookFlag(pedOnGround, true); SetLookTimer(1500); } animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[m_curFightMove].animId, 4.0f); animAssoc->SetFinishCallback(FinishFightMoveCB, this); m_fightState = FIGHTSTATE_NO_MOVE; m_takeAStepAfterAttack = false; bIsAttacking = true; if (IsPlayer()) nPlayerInComboMove = 0; } void CPed::StartFightDefend(uint8 direction, uint8 hitLevel, uint8 unk) { if (m_nPedState == PED_DEAD) { if (CGame::nastyGame) { if (hitLevel == HITLEVEL_GROUND) { CAnimBlendAssociation *floorHitAssoc; if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL)) { floorHitAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR_FRONT, 8.0f); } else { floorHitAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[FIGHTMOVE_HITONFLOOR].animId, 8.0f); } if (floorHitAssoc) { floorHitAssoc->SetCurrentTime(0.0f); floorHitAssoc->SetRun(); floorHitAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; } } if (CGame::nastyGame) { CVector headPos = GetNodePosition(PED_HEAD); for(int i = 0; i < 4; ++i) { CVector bloodDir(0.0f, 0.0f, 0.1f); CVector bloodPos = headPos - 0.2f * GetForward(); CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, bloodDir, nil, 0.0f, 0, 0, 0, 0); } } } } else if (m_nPedState == PED_FALL) { if (hitLevel == HITLEVEL_GROUND && !IsPedHeadAbovePos(-0.3f)) { CAnimBlendAssociation *floorHitAssoc = RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL) ? CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR_FRONT, 8.0f) : CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HIT_FLOOR, 8.0f); if (floorHitAssoc) { floorHitAssoc->flags &= ~ASSOC_FADEOUTWHENDONE; floorHitAssoc->flags |= ASSOC_DELETEFADEDOUT; } } } else if (IsPedInControl()) { if ((IsPlayer() && m_nPedState != PED_FIGHT && ((CPlayerPed*)this)->m_fMoveSpeed > 1.0f) || (!IsPlayer() && m_objective == OBJECTIVE_FLEE_CHAR_ON_FOOT_TILL_SAFE)) { #ifndef VC_PED_PORTS if (hitLevel != HITLEVEL_HIGH && hitLevel != HITLEVEL_LOW || (IsPlayer() || CGeneral::GetRandomNumber() & 3) && CGeneral::GetRandomNumber() & 7) { if (IsPlayer() || CGeneral::GetRandomNumber() & 3) { #else if (hitLevel != HITLEVEL_HIGH && hitLevel != HITLEVEL_LOW || (IsPlayer() || CGeneral::GetRandomNumber() & 1) && CGeneral::GetRandomNumber() & 7) { if (IsPlayer() || CGeneral::GetRandomNumber() & 1) { #endif AnimationId shotAnim; switch (direction) { case 1: shotAnim = ANIM_STD_HITBYGUN_LEFT; break; case 2: shotAnim = ANIM_STD_HITBYGUN_BACK; break; case 3: shotAnim = ANIM_STD_HITBYGUN_RIGHT; break; default: shotAnim = ANIM_STD_HITBYGUN_FRONT; break; } CAnimBlendAssociation *shotAssoc = RpAnimBlendClumpGetAssociation(GetClump(), shotAnim); if (!shotAssoc || shotAssoc->blendDelta < 0.0f) shotAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, shotAnim, 8.0f); shotAssoc->SetCurrentTime(0.0f); shotAssoc->SetRun(); shotAssoc->flags |= ASSOC_FADEOUTWHENDONE; } else { int time = CGeneral::GetRandomNumberInRange(1000, 3000); SetWaitState(WAITSTATE_PLAYANIM_DUCK, &time); } } else { #ifndef VC_PED_PORTS switch (direction) { case 1: SetFall(500, ANIM_STD_HIGHIMPACT_LEFT, false); break; case 2: SetFall(500, ANIM_STD_HIGHIMPACT_BACK, false); break; case 3: SetFall(500, ANIM_STD_HIGHIMPACT_RIGHT, false); break; default: SetFall(500, ANIM_STD_KO_SHOT_STOMACH, false); break; } #else bool fall = true; AnimationId hitAnim; switch (direction) { case 1: hitAnim = ANIM_STD_HIGHIMPACT_LEFT; break; case 2: if (CGeneral::GetRandomNumber() & 1) { fall = false; hitAnim = ANIM_STD_HIT_BACK; } else { hitAnim = ANIM_STD_HIGHIMPACT_BACK; } break; case 3: hitAnim = ANIM_STD_HIGHIMPACT_RIGHT; break; default: if (hitLevel == HITLEVEL_LOW) { hitAnim = ANIM_STD_KO_SHOT_STOMACH; } else if (CGeneral::GetRandomNumber() & 1) { fall = false; hitAnim = ANIM_STD_HIT_WALK; } else if (CGeneral::GetRandomNumber() & 1) { fall = false; hitAnim = ANIM_STD_HIT_HEAD; } else { hitAnim = ANIM_STD_KO_SHOT_FACE; } break; } if (fall) { SetFall(500, hitAnim, false); } else { CAnimBlendAssociation *hitAssoc = RpAnimBlendClumpGetAssociation(GetClump(), hitAnim); if (!hitAssoc || hitAssoc->blendDelta < 0.0f) hitAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, hitAnim, 8.0f); hitAssoc->SetCurrentTime(0.0f); hitAssoc->SetRun(); hitAssoc->flags |= ASSOC_FADEOUTWHENDONE; } #endif } Say(SOUND_PED_DEFEND); } else { Say(SOUND_PED_DEFEND); switch (hitLevel) { case HITLEVEL_GROUND: m_curFightMove = FIGHTMOVE_HITONFLOOR; break; case HITLEVEL_LOW: #ifndef VC_PED_PORTS if (direction == 2) { SetFall(1000, ANIM_STD_HIGHIMPACT_BACK, false); return; } #else if (direction == 2 && (!IsPlayer() || ((CGeneral::GetRandomNumber() & 1) && m_fHealth < 30.0f))) { SetFall(1000, ANIM_STD_HIGHIMPACT_BACK, false); return; } else if (direction != 2 && !IsPlayer() && (CGeneral::GetRandomNumber() & 1) && m_fHealth < 30.0f) { SetFall(1000, ANIM_STD_KO_SHOT_STOMACH, false); return; } #endif m_curFightMove = FIGHTMOVE_HITBODY; break; case HITLEVEL_HIGH: switch (direction) { case 1: m_curFightMove = FIGHTMOVE_HITLEFT; break; case 2: m_curFightMove = FIGHTMOVE_HITBACK; break; case 3: m_curFightMove = FIGHTMOVE_HITRIGHT; break; default: if (unk <= 5) m_curFightMove = FIGHTMOVE_HITHEAD; else m_curFightMove = FIGHTMOVE_HITBIGSTEP; break; } break; default: switch (direction) { case 1: m_curFightMove = FIGHTMOVE_HITLEFT; break; case 2: m_curFightMove = FIGHTMOVE_HITBACK; break; case 3: m_curFightMove = FIGHTMOVE_HITRIGHT; break; default: if (unk <= 5) m_curFightMove = FIGHTMOVE_HITCHEST; else m_curFightMove = FIGHTMOVE_HITBIGSTEP; break; } break; } if (m_nPedState == PED_GETUP && !IsPedHeadAbovePos(0.0f)) m_curFightMove = FIGHTMOVE_HITONFLOOR; if (m_nPedState == PED_FIGHT) { CAnimBlendAssociation *moveAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[m_curFightMove].animId, 8.0f); moveAssoc->SetCurrentTime(0.0f); moveAssoc->SetFinishCallback(FinishFightMoveCB, this); if (IsPlayer()) moveAssoc->speed = 1.3f; m_takeAStepAfterAttack = 0; m_fightButtonPressure = 0; } else if (IsPlayer() && m_currentWeapon != WEAPONTYPE_UNARMED) { CAnimBlendAssociation *moveAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[m_curFightMove].animId, 4.0f); moveAssoc->SetCurrentTime(0.0f); moveAssoc->speed = 1.3f; } else { if (m_nPedState != PED_AIM_GUN && m_nPedState != PED_ATTACK) SetStoredState(); if (m_nWaitState != WAITSTATE_FALSE) { m_nWaitState = WAITSTATE_FALSE; RestoreHeadingRate(); } SetPedState(PED_FIGHT); m_fightButtonPressure = 0; RpAnimBlendClumpRemoveAssociations(GetClump(), ASSOC_REPEAT); CAnimBlendAssociation *walkStartAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_STARTWALK); if (walkStartAssoc) { walkStartAssoc->flags |= ASSOC_DELETEFADEDOUT; walkStartAssoc->blendDelta = -1000.0f; } CAnimBlendAssociation *walkStopAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNSTOP1); if (!walkStopAssoc) walkStopAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_RUNSTOP2); if (walkStopAssoc) { walkStopAssoc->flags |= ASSOC_DELETEFADEDOUT; walkStopAssoc->blendDelta = -1000.0f; RestoreHeadingRate(); } SetMoveState(PEDMOVE_NONE); m_nStoredMoveState = PEDMOVE_NONE; CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FIGHT_IDLE)->blendAmount = 1.0f; CAnimBlendAssociation *moveAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[m_curFightMove].animId, 8.0f); moveAssoc->SetFinishCallback(FinishFightMoveCB, this); m_fightState = FIGHTSTATE_NO_MOVE; m_takeAStepAfterAttack = false; bIsAttacking = true; } } } } void CPed::Fight(void) { CAnimBlendAssociation *currentAssoc, *animAssoc; bool hasShoppingBags, punchOnly, canKick, canKneeHead, canRoundhouse; float angleToFace, nextAngle; bool goForward = false; int nextFightMove; switch (m_curFightMove) { case FIGHTMOVE_NULL: return; case FIGHTMOVE_IDLE2NORM: m_curFightMove = FIGHTMOVE_NULL; RestorePreviousState(); // FIX: Uninitialized currentAssoc = nil; break; case FIGHTMOVE_IDLE: currentAssoc = nil; break; default: currentAssoc = RpAnimBlendClumpGetAssociation(GetClump(), tFightMoves[m_curFightMove].animId); break; } if (!bIsAttacking && IsPlayer()) { if (currentAssoc) { currentAssoc->blendDelta = -1000.0f; currentAssoc->flags |= ASSOC_DELETEFADEDOUT; currentAssoc->flags &= ~ASSOC_RUNNING; } if (m_takeAStepAfterAttack) EndFight(ENDFIGHT_WITH_A_STEP); else EndFight(ENDFIGHT_FAST); } else if (currentAssoc && m_fightState > FIGHTSTATE_MOVE_FINISHED) { float animTime = currentAssoc->currentTime; FightMove &curMove = tFightMoves[m_curFightMove]; if (curMove.hitLevel != HITLEVEL_NULL && animTime > curMove.startFireTime && animTime <= curMove.endFireTime && m_fightState >= FIGHTSTATE_NO_MOVE) { CVector touchingNodePos(0.0f, 0.0f, 0.0f); switch (m_curFightMove) { case FIGHTMOVE_STDPUNCH: case FIGHTMOVE_PUNCHHOOK: case FIGHTMOVE_BODYBLOW: TransformToNode(touchingNodePos, PED_HANDR); break; case FIGHTMOVE_IDLE: case FIGHTMOVE_SHUFFLE_F: break; case FIGHTMOVE_KNEE: TransformToNode(touchingNodePos, PED_LOWERLEGR); break; case FIGHTMOVE_HEADBUTT: TransformToNode(touchingNodePos, PED_HEAD); break; case FIGHTMOVE_PUNCHJAB: TransformToNode(touchingNodePos, PED_HANDL); break; case FIGHTMOVE_KICK: case FIGHTMOVE_LONGKICK: case FIGHTMOVE_ROUNDHOUSE: case FIGHTMOVE_GROUNDKICK: TransformToNode(touchingNodePos, PED_FOOTR); break; } if (m_curFightMove == FIGHTMOVE_PUNCHJAB) { touchingNodePos += 0.1f * GetForward(); } else if (m_curFightMove == FIGHTMOVE_PUNCHHOOK) { touchingNodePos += 0.22f * GetForward(); } FightStrike(touchingNodePos); m_fightButtonPressure = 0; return; } if (curMove.hitLevel != HITLEVEL_NULL) { if (animTime > curMove.endFireTime) { if (IsPlayer()) currentAssoc->speed = 1.0f; else currentAssoc->speed = 0.8f; } if (IsPlayer() && !nPlayerInComboMove) { if (curMove.comboFollowOnTime > 0.0f && m_fightButtonPressure != 0 && animTime > curMove.comboFollowOnTime) { // Notice that it increases fight move index, because we're in combo! animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[++m_curFightMove].animId, 8.0f); animAssoc->SetFinishCallback(FinishFightMoveCB, this); animAssoc->SetCurrentTime(0.1f * animAssoc->hierarchy->totalLength); m_fightButtonPressure = 0; nPlayerInComboMove = 1; } } } else { if (curMove.startFireTime > 0.0f && m_curFightMove != FIGHTMOVE_SHUFFLE_F && animTime > curMove.startFireTime) { if (IsPlayer()) currentAssoc->speed = 1.3f; else currentAssoc->speed = 0.8f; } } } else if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) { EndFight(ENDFIGHT_FAST); } else if (m_fightButtonPressure != 0) { bool canAffectMultiplePeople = true; nextAngle = m_fRotationCur; bool kickGround = false; float angleForGroundKick = 0.0f; CPed *pedOnGround = nil; Say(SOUND_PED_ATTACK); if (IsPlayer()) { canRoundhouse = false; punchOnly = false; canKick = true; nextFightMove = (m_fightButtonPressure > 190 ? FIGHTMOVE_HEADBUTT : FIGHTMOVE_KNEE); hasShoppingBags = false; canKneeHead = true; nPlayerInComboMove = 0; } else { nextFightMove = (m_fightButtonPressure > 120 ? FIGHTMOVE_HEADBUTT : FIGHTMOVE_KNEE); uint16 pedFeatures = m_pedStats->m_flags; punchOnly = pedFeatures & STAT_PUNCH_ONLY; canRoundhouse = pedFeatures & STAT_CAN_ROUNDHOUSE; canKneeHead = pedFeatures & STAT_CAN_KNEE_HEAD; canKick = pedFeatures & STAT_CAN_KICK; hasShoppingBags = pedFeatures & STAT_SHOPPING_BAGS; } // Attack isn't scripted, find the victim if (IsPlayer() || !m_pedInObjective) { for (int i = 0; i < m_numNearPeds; i++) { CPed *nearPed = m_nearPeds[i]; float nearPedDist = (nearPed->GetPosition() - GetPosition()).Magnitude(); if (nearPedDist < 3.0f) { float angleToFace = CGeneral::GetRadianAngleBetweenPoints( nearPed->GetPosition().x, nearPed->GetPosition().y, GetPosition().x, GetPosition().y); nextAngle = CGeneral::LimitRadianAngle(angleToFace); m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur); float neededTurn = Abs(nextAngle - m_fRotationCur); if (neededTurn > PI) neededTurn = TWOPI - neededTurn; if (!nearPed->OnGroundOrGettingUp()) { if (nearPedDist < 0.8f && neededTurn < DEGTORAD(75.0f) && canKneeHead) { canAffectMultiplePeople = false; } else if (nearPedDist >= 1.3f || neededTurn >= DEGTORAD(55.0f) || hasShoppingBags) { if (nearPedDist < 1.7f && neededTurn < DEGTORAD(35.0f) && (canKick || hasShoppingBags)) { nextFightMove = FIGHTMOVE_KICK; if (hasShoppingBags) { nextFightMove = FIGHTMOVE_ROUNDHOUSE; } else if (canRoundhouse && CGeneral::GetRandomNumber() & 1) { nextFightMove = FIGHTMOVE_ROUNDHOUSE; } canAffectMultiplePeople = false; } else if (nearPedDist < 2.0f && neededTurn < DEGTORAD(30.0f) && canKick) { canAffectMultiplePeople = false; nextFightMove = FIGHTMOVE_LONGKICK; } else if (neededTurn < DEGTORAD(30.0f)) { goForward = true; } } else { nextFightMove += 2; // Makes it 6 or 7 if (punchOnly) nextFightMove = FIGHTMOVE_PUNCHJAB; canAffectMultiplePeople = false; } } else if (!CGame::nastyGame || nearPedDist >= 1.3f || neededTurn >= DEGTORAD(55.0f) || punchOnly) { if (nearPedDist > 0.8f && nearPedDist < 3.0f && neededTurn < DEGTORAD(30.0f)) { goForward = true; } } else if (nearPed->m_nPedState != PED_DEAD || pedOnGround) { if (!nearPed->IsPedHeadAbovePos(-0.3f)) { canAffectMultiplePeople = false; nextFightMove = FIGHTMOVE_GROUNDKICK; } } else { pedOnGround = nearPed; kickGround = true; angleForGroundKick = nextAngle; } } if (!canAffectMultiplePeople) { m_fRotationDest = nextAngle; if (IsPlayer()) { m_fRotationCur = m_fRotationDest; m_lookTimer = 0; SetLookFlag(nearPed, true); SetLookTimer(1500); } break; } } } else { // Because we're in a scripted fight with some particular ped. canAffectMultiplePeople = false; float fightingPedDist = (m_pedInObjective->GetPosition() - GetPosition()).Magnitude(); if (hasShoppingBags) { if (fightingPedDist >= 1.7f) nextFightMove = FIGHTMOVE_SHUFFLE_F; else nextFightMove = FIGHTMOVE_ROUNDHOUSE; } else if (punchOnly) { if (fightingPedDist >= 1.3f) nextFightMove = FIGHTMOVE_SHUFFLE_F; else nextFightMove = FIGHTMOVE_PUNCHJAB; } else if (fightingPedDist >= 3.0f) { nextFightMove = FIGHTMOVE_STDPUNCH; } else { angleToFace = CGeneral::GetRadianAngleBetweenPoints( m_pedInObjective->GetPosition().x, m_pedInObjective->GetPosition().y, GetPosition().x, GetPosition().y); nextAngle = CGeneral::LimitRadianAngle(angleToFace); m_fRotationDest = nextAngle; m_fRotationCur = m_fRotationDest; if (!m_pedInObjective->OnGroundOrGettingUp()) { if (fightingPedDist >= 0.8f || !canKneeHead) { if (fightingPedDist >= 1.3f) { if (fightingPedDist < 1.7f && canKick) { nextFightMove = FIGHTMOVE_KICK; if (canRoundhouse && CGeneral::GetRandomNumber() & 1) nextFightMove = FIGHTMOVE_ROUNDHOUSE; } else if (fightingPedDist < 2.0f && canKick) { nextFightMove += 5; // Makes it 9 or 10 } else { nextFightMove = FIGHTMOVE_SHUFFLE_F; } } else { nextFightMove += 2; // Makes it 6 or 7 } } } else if (!CGame::nastyGame || fightingPedDist >= 1.3f || m_pedInObjective->IsPlayer() || m_pedInObjective->m_nPedState != PED_DEAD && m_pedInObjective->IsPedHeadAbovePos(-0.3f)) { nextFightMove = FIGHTMOVE_IDLE; } else { nextFightMove = FIGHTMOVE_GROUNDKICK; } } } if (canAffectMultiplePeople) { if (kickGround && IsPlayer()) { m_fRotationDest = angleForGroundKick; nextFightMove = FIGHTMOVE_GROUNDKICK; m_fRotationCur = m_fRotationDest; m_lookTimer = 0; SetLookFlag(pedOnGround, true); SetLookTimer(1500); } else if (goForward) { nextFightMove = FIGHTMOVE_SHUFFLE_F; } else { nextFightMove = FIGHTMOVE_STDPUNCH; } } if (nextFightMove != FIGHTMOVE_IDLE) { m_curFightMove = nextFightMove; animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[m_curFightMove].animId, 4.0f); animAssoc->SetFinishCallback(FinishFightMoveCB, this); if (m_fightState == FIGHTSTATE_MOVE_FINISHED && animAssoc->currentTime != 0.0f) { animAssoc->SetCurrentTime(0.0f); animAssoc->SetRun(); } m_fightButtonPressure = 0; } m_fightState = FIGHTSTATE_NO_MOVE; } else if (m_takeAStepAfterAttack && m_curFightMove != FIGHTMOVE_SHUFFLE_F #ifndef FIX_BUGS && CheckForPedsOnGroundToAttack(this, nil) == 4) { #else && CheckForPedsOnGroundToAttack(this, nil) == PED_IN_FRONT_OF_ATTACKER) { #endif m_curFightMove = FIGHTMOVE_SHUFFLE_F; animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), tFightMoves[m_curFightMove].animId); if (animAssoc) { animAssoc->SetCurrentTime(0.0f); animAssoc->blendDelta = 4.0f; animAssoc->SetRun(); } else { animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[m_curFightMove].animId, 32.0f); } animAssoc->SetFinishCallback(FinishFightMoveCB, this); m_fightState = FIGHTSTATE_NO_MOVE; m_fightButtonPressure = 0; m_takeAStepAfterAttack = false; } else if (m_takeAStepAfterAttack) { EndFight(ENDFIGHT_FAST); } else if (m_curFightMove == FIGHTMOVE_IDLE) { if (CTimer::GetTimeInMilliseconds() > m_nWaitTimer) { EndFight(ENDFIGHT_NORMAL); } } else { m_curFightMove = FIGHTMOVE_IDLE; if (IsPlayer()) m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 500; else m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 2000; } } void CPed::EndFight(uint8 endType) { if (m_nPedState != PED_FIGHT) return; m_curFightMove = FIGHTMOVE_NULL; RestorePreviousState(); CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_FIGHT_IDLE); if (animAssoc) animAssoc->flags |= ASSOC_DELETEFADEDOUT; switch (endType) { case ENDFIGHT_NORMAL: CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 8.0f); CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FIGHT_2IDLE, 8.0f); break; case ENDFIGHT_WITH_A_STEP: CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 1.0f); CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_STARTWALK, 8.0f); break; case ENDFIGHT_FAST: CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 8.0f); CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_FIGHT_2IDLE, 8.0f)->speed = 2.0f; break; default: break; } m_nWaitTimer = 0; } void CPed::PlayHitSound(CPed *hitTo) { // That was very complicated to reverse for me... // First index is our fight move ID (from 1 to 12, total 12), second is the one of we fight with (from 13 to 22, total 10). enum { S33 = SOUND_FIGHT_PUNCH_33, S34 = SOUND_FIGHT_KICK_34, S35 = SOUND_FIGHT_HEADBUTT_35, S36 = SOUND_FIGHT_PUNCH_36, S37 = SOUND_FIGHT_PUNCH_37, S38 = SOUND_FIGHT_CLOSE_PUNCH_38, S39 = SOUND_FIGHT_PUNCH_39, S40 = SOUND_FIGHT_PUNCH_OR_KICK_BELOW_40 , S41 = SOUND_FIGHT_PUNCH_41, S42 = SOUND_FIGHT_PUNCH_FROM_BEHIND_42, S43 = SOUND_FIGHT_KNEE_OR_KICK_43, S44 = SOUND_FIGHT_KICK_44, NO_SND = SOUND_NO_SOUND }; uint16 hitSoundsByFightMoves[12][10] = { {S39,S42,S43,S43,S39,S39,S39,S39,S39,S42}, {NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND}, {NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND,NO_SND}, {S39,S39,S39,S39,S33,S43,S39,S39,S39,S39}, {S39,S39,S39,S39,S35,S39,S38,S38,S39,S39}, {S39,S39,S39,S39,S33,S39,S41,S36,S39,S39}, {S39,S39,S39,S39,S37,S40,S38,S38,S39,S39}, {S39,S39,S39,S39,S34,S43,S44,S37,S39,S39}, {S39,S39,S39,S39,S34,S43,S44,S37,S39,S39}, {S39,S39,S39,S39,S34,S43,S44,S37,S39,S40}, {S39,S39,S39,S39,S33,S39,S41,S37,S39,S40}, {S39,S39,S39,S39,S39,S39,S39,S39,S33,S33} }; // This is why first dimension is between FightMove 1 and 12. if (m_curFightMove == FIGHTMOVE_NULL || m_curFightMove >= FIGHTMOVE_HITFRONT) return; uint16 soundId; // And this is why second dimension is between 13 and 22. if (hitTo->m_curFightMove > FIGHTMOVE_GROUNDKICK && hitTo->m_curFightMove < FIGHTMOVE_IDLE2NORM) { soundId = hitSoundsByFightMoves[m_curFightMove - FIGHTMOVE_STDPUNCH][hitTo->m_curFightMove - FIGHTMOVE_HITFRONT]; } else { if (hitTo->m_nPedState == PED_DEAD || hitTo->UseGroundColModel()) { soundId = hitSoundsByFightMoves[m_curFightMove - FIGHTMOVE_STDPUNCH][FIGHTMOVE_HITONFLOOR - FIGHTMOVE_HITFRONT]; } else { soundId = hitSoundsByFightMoves[m_curFightMove - FIGHTMOVE_STDPUNCH][FIGHTMOVE_HITFRONT - FIGHTMOVE_HITFRONT]; } } if (soundId != NO_SND) DMAudio.PlayOneShot(m_audioEntityId, soundId, 0.0f); } bool CPed::FightStrike(CVector &touchedNodePos) { CColModel *ourCol; CVector attackDistance; ePedPieceTypes closestPedPiece = PEDPIECE_TORSO; float maxDistanceToBeBeaten; CPed *nearPed; int state = m_fightState; bool pedFound = false; if (state == FIGHTSTATE_JUST_ATTACKED) return false; // Pointless code if (state > FIGHTSTATE_NO_MOVE) attackDistance = touchedNodePos - m_vecHitLastPos; for (int i = 0; i < m_numNearPeds; i++) { nearPed = m_nearPeds[i]; if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) maxDistanceToBeBeaten = nearPed->GetBoundRadius() + tFightMoves[m_curFightMove].strikeRadius + 0.1f; else maxDistanceToBeBeaten = nearPed->GetBoundRadius() + tFightMoves[m_curFightMove].strikeRadius; if (nearPed->bUsesCollision || nearPed->m_nPedState == PED_DEAD) { CVector nearPedCentre; nearPed->GetBoundCentre(nearPedCentre); CVector potentialAttackDistance = nearPedCentre - touchedNodePos; // He can beat us if (sq(maxDistanceToBeBeaten) > potentialAttackDistance.MagnitudeSqr()) { #ifdef PED_SKIN // Have to animate a skinned clump because the initial col model is useless if(IsClumpSkinned(GetClump())) ourCol = ((CPedModelInfo *)CModelInfo::GetModelInfo(GetModelIndex()))->AnimatePedColModelSkinned(GetClump()); else #endif if (nearPed->OnGround() || !nearPed->IsPedHeadAbovePos(-0.3f)) { ourCol = &CTempColModels::ms_colModelPedGroundHit; } else { #ifdef ANIMATE_PED_COL_MODEL ourCol = CPedModelInfo::AnimatePedColModel(((CPedModelInfo *)CModelInfo::GetModelInfo(GetModelIndex()))->GetHitColModel(), RpClumpGetFrame(GetClump())); #else ourCol = ((CPedModelInfo*)CModelInfo::GetModelInfo(m_modelIndex))->GetHitColModel(); #endif } for (int j = 0; j < ourCol->numSpheres; j++) { attackDistance = nearPed->GetPosition() + ourCol->spheres[j].center; attackDistance -= touchedNodePos; CColSphere *ourPieces = ourCol->spheres; float maxDistanceToBeat = ourPieces[j].radius + tFightMoves[m_curFightMove].strikeRadius; // We can beat him too if (sq(maxDistanceToBeat) > attackDistance.MagnitudeSqr()) { pedFound = true; closestPedPiece = (ePedPieceTypes) ourPieces[j].piece; break; } } } } if (pedFound) break; } if (pedFound) { if (nearPed->IsPlayer() && nearPed->m_nPedState == PED_GETUP) return false; float oldVictimHealth = nearPed->m_fHealth; CVector bloodPos = 0.5f * attackDistance + touchedNodePos; int damageMult = tFightMoves[m_curFightMove].damage * ((CGeneral::GetRandomNumber() & 1) + 2) + 1; CVector2D diff (GetPosition() - nearPed->GetPosition()); int direction = nearPed->GetLocalDirection(diff); if (IsPlayer()) { if (((CPlayerPed*)this)->m_bAdrenalineActive) damageMult = 20; } else { damageMult *= m_pedStats->m_attackStrength; } // Change direction if we used kick. if (m_curFightMove == FIGHTMOVE_KICK) { if (CGeneral::GetRandomNumber() & 1) { direction++; if (direction > 3) direction -= 4; } } nearPed->ReactToAttack(this); // Mostly unused. if > 5, ANIM_HIT_WALK will be run, that's it. int unk2; if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED && !nearPed->IsPlayer()) unk2 = 101; else unk2 = damageMult; nearPed->StartFightDefend(direction, tFightMoves[m_curFightMove].hitLevel, unk2); PlayHitSound(nearPed); m_fightState = FIGHTSTATE_JUST_ATTACKED; RpAnimBlendClumpGetAssociation(GetClump(), tFightMoves[m_curFightMove].animId)->speed = 0.6f; if (!nearPed->DyingOrDead()) { nearPed->InflictDamage(this, WEAPONTYPE_UNARMED, damageMult * 3.0f, closestPedPiece, direction); } if (CGame::nastyGame && tFightMoves[m_curFightMove].hitLevel > HITLEVEL_MEDIUM && nearPed->m_nPedState == PED_DIE && nearPed->GetIsOnScreen()) { // Just for blood particle. We will restore it below. attackDistance /= (10.0f * attackDistance.Magnitude()); for(int i=0; i<4; i++) { CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, attackDistance, nil, 0.0f, 0, 0, 0, 0); } } if (!nearPed->OnGround()) { float curVictimHealth = nearPed->m_fHealth; if (curVictimHealth > 0.0f && (curVictimHealth < 40.0f && oldVictimHealth > 40.0f && !nearPed->IsPlayer() || nearPed->m_fHealth < 20.0f && oldVictimHealth > 20.0f || GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED && IsPlayer() || nearPed->m_pedStats->m_flags & STAT_ONE_HIT_KNOCKDOWN)) { nearPed->SetFall(0, (AnimationId)(direction + ANIM_STD_HIGHIMPACT_FRONT), 0); if (nearPed->m_nPedState == PED_FALL) nearPed->bIsStanding = false; } } if (nearPed->m_nPedState == PED_DIE || !nearPed->bIsStanding) { attackDistance = nearPed->GetPosition() - GetPosition(); attackDistance.Normalise(); attackDistance.z = 1.0f; nearPed->bIsStanding = false; float moveMult; if (m_curFightMove == FIGHTMOVE_GROUNDKICK) { moveMult = Min(damageMult * 0.6f, 4.0f); } else { if (nearPed->m_nPedState != PED_DIE || damageMult >= 20) { moveMult = damageMult; } else { moveMult = Min(damageMult * 2.0f, 14.0f); } } nearPed->ApplyMoveForce(moveMult * 0.6f * attackDistance); } CEventList::RegisterEvent(nearPed->m_nPedType == PEDTYPE_COP ? EVENT_ASSAULT_POLICE : EVENT_ASSAULT, EVENT_ENTITY_PED, nearPed, this, 2000); } if (m_fightState == FIGHTSTATE_NO_MOVE) m_fightState = FIGHTSTATE_1; m_vecHitLastPos = touchedNodePos; return false; } void CPed::FinishFightMoveCB(CAnimBlendAssociation *animAssoc, void *arg) { CPed *ped = (CPed*)arg; if (tFightMoves[ped->m_curFightMove].animId == animAssoc->animId) { ped->m_fightState = FIGHTSTATE_MOVE_FINISHED; animAssoc->blendDelta = -1000.0f; } } void CPed::LoadFightData(void) { float startFireTime, endFireTime, comboFollowOnTime, strikeRadius; int damage, flags; char line[256], moveName[32], animName[32], hitLevel; int moveId = 0; CAnimBlendAssociation *animAssoc; size_t bp, buflen; int lp, linelen; buflen = CFileMgr::LoadFile("DATA\\fistfite.dat", work_buff, sizeof(work_buff), "r"); for (bp = 0; bp < buflen; ) { // read file line by line for (linelen = 0; work_buff[bp] != '\n' && bp < buflen; bp++) { line[linelen++] = work_buff[bp]; } bp++; line[linelen] = '\0'; // skip white space for (lp = 0; line[lp] <= ' ' && line[lp] != '\0'; lp++); if (line[lp] == '\0' || line[lp] == '#') continue; sscanf( &line[lp], "%s %f %f %f %f %c %s %d %d", moveName, &startFireTime, &endFireTime, &comboFollowOnTime, &strikeRadius, &hitLevel, animName, &damage, &flags); if (strncmp(moveName, "ENDWEAPONDATA", 13) == 0) return; tFightMoves[moveId].startFireTime = startFireTime / 30.0f; tFightMoves[moveId].endFireTime = endFireTime / 30.0f; tFightMoves[moveId].comboFollowOnTime = comboFollowOnTime / 30.0f; tFightMoves[moveId].strikeRadius = strikeRadius; tFightMoves[moveId].damage = damage; tFightMoves[moveId].flags = flags; switch (hitLevel) { case 'G': tFightMoves[moveId].hitLevel = HITLEVEL_GROUND; break; case 'H': tFightMoves[moveId].hitLevel = HITLEVEL_HIGH; break; case 'L': tFightMoves[moveId].hitLevel = HITLEVEL_LOW; break; case 'M': tFightMoves[moveId].hitLevel = HITLEVEL_MEDIUM; break; case 'N': tFightMoves[moveId].hitLevel = HITLEVEL_NULL; break; default: break; } if (strcmp(animName, "null") != 0) { animAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, animName); tFightMoves[moveId].animId = (AnimationId)animAssoc->animId; } else { tFightMoves[moveId].animId = ANIM_STD_WALK; } moveId++; } } void CPed::SetInvestigateEvent(eEventType event, CVector2D pos, float distanceToCountDone, uint16 time, float angle) { if (!IsPedInControl() || CharCreatedBy == MISSION_CHAR) return; SetStoredState(); bFindNewNodeAfterStateRestore = false; SetPedState(PED_INVESTIGATE); m_chatTimer = CTimer::GetTimeInMilliseconds() + time; m_eventType = event; m_eventOrThreat = pos; m_distanceToCountSeekDone = distanceToCountDone; m_fAngleToEvent = angle; if (m_eventType >= EVENT_ICECREAM) m_lookTimer = 0; else CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_HANDSCOWER, 4.0f); } void CPed::InvestigateEvent(void) { CAnimBlendAssociation *animAssoc; AnimationId animToPlay; AssocGroupId animGroup; if (m_nWaitState == WAITSTATE_TURN180) return; if (CTimer::GetTimeInMilliseconds() > m_chatTimer) { if (m_chatTimer) { if (m_eventType < EVENT_ASSAULT_NASTYWEAPON) SetWaitState(WAITSTATE_TURN180, nil); m_chatTimer = 0; } else { ClearInvestigateEvent(); } return; } CVector2D vecDist = m_eventOrThreat - GetPosition(); float distSqr = vecDist.MagnitudeSqr(); if (sq(m_distanceToCountSeekDone) >= distSqr) { m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(vecDist.x, vecDist.y, 0.0f, 0.0f); SetMoveState(PEDMOVE_STILL); switch (m_eventType) { case EVENT_DEAD_PED: case EVENT_HIT_AND_RUN: case EVENT_HIT_AND_RUN_COP: if (CTimer::GetTimeInMilliseconds() > m_lookTimer) { animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROADCROSS); if (animAssoc) { animAssoc->blendDelta = -8.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; if (m_pEventEntity) SetLookFlag(m_pEventEntity, true); SetLookTimer(CGeneral::GetRandomNumberInRange(1500, 4000)); } else if (CGeneral::GetRandomNumber() & 3) { ClearLookFlag(); CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ROADCROSS, 4.0f); SetLookTimer(CGeneral::GetRandomNumberInRange(1000, 2500)); Say(SOUND_PED_CHAT_EVENT); } else { ClearInvestigateEvent(); } } break; case EVENT_FIRE: case EVENT_EXPLOSION: if (bHasACamera && CTimer::GetTimeInMilliseconds() > m_lookTimer) { animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_CAM); if (!animAssoc) animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE); if (animAssoc && animAssoc->animId == ANIM_STD_IDLE_CAM) { CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_STD_IDLE, 4.0f); SetLookTimer(CGeneral::GetRandomNumberInRange(1000, 2500)); } else if (CGeneral::GetRandomNumber() & 3) { CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_IDLE_CAM, 4.0f); SetLookTimer(CGeneral::GetRandomNumberInRange(2500, 5000)); Say(SOUND_PED_CHAT_EVENT); } else { m_chatTimer = 0; } } else if (CTimer::GetTimeInMilliseconds() > m_lookTimer) { animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE); if (!animAssoc) animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_HBHB); if (!animAssoc) animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_XPRESS_SCRATCH); if (animAssoc && animAssoc->animId == ANIM_STD_IDLE) { if (CGeneral::GetRandomNumber() & 1) animToPlay = ANIM_STD_IDLE_HBHB; else animToPlay = ANIM_STD_XPRESS_SCRATCH; CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, animToPlay, 4.0f); SetLookTimer(CGeneral::GetRandomNumberInRange(1500, 4000)); } else if (animAssoc && animAssoc->animId == ANIM_STD_IDLE_HBHB) { animAssoc->blendDelta = -8.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; if (CGeneral::GetRandomNumber() & 1) { animToPlay = ANIM_STD_IDLE; animGroup = m_animGroup; } else { animToPlay = ANIM_STD_XPRESS_SCRATCH; animGroup = ASSOCGRP_STD; } CAnimManager::BlendAnimation(GetClump(), animGroup, animToPlay, 4.0f); SetLookTimer(CGeneral::GetRandomNumberInRange(1000, 2500)); } else { if (CGeneral::GetRandomNumber() & 1) { animToPlay = ANIM_STD_IDLE; animGroup = m_animGroup; } else { animToPlay = ANIM_STD_IDLE_HBHB; animGroup = ASSOCGRP_STD; } CAnimManager::BlendAnimation(GetClump(), animGroup, animToPlay, 4.0f); SetLookTimer(CGeneral::GetRandomNumberInRange(1000, 2500)); } Say(SOUND_PED_CHAT_EVENT); } break; case EVENT_ICECREAM: case EVENT_SHOPSTALL: m_fRotationDest = m_fAngleToEvent; if (CTimer::GetTimeInMilliseconds() > m_lookTimer) { if (m_lookTimer) { animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROADCROSS); if (animAssoc) { animAssoc->blendDelta = -8.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; if (m_eventType == EVENT_ICECREAM) animToPlay = ANIM_STD_CHAT; else animToPlay = ANIM_STD_XPRESS_SCRATCH; CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, animToPlay,4.0f); SetLookTimer(CGeneral::GetRandomNumberInRange(2000, 5000)); } else { animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CHAT); if (animAssoc) { animAssoc->blendDelta = -8.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; ClearInvestigateEvent(); } else { animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_XPRESS_SCRATCH); if (animAssoc) { animAssoc->blendDelta = -8.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; } ClearInvestigateEvent(); } } } else { CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_ROADCROSS, 4.0f); SetLookTimer(CGeneral::GetRandomNumberInRange(1000, 2500)); } } break; default: return; } } else { m_vecSeekPos.x = m_eventOrThreat.x; m_vecSeekPos.y = m_eventOrThreat.y; m_vecSeekPos.z = GetPosition().z; Seek(); if (m_eventType < EVENT_ICECREAM) { if (sq(5.0f + m_distanceToCountSeekDone) < distSqr) { SetMoveState(PEDMOVE_RUN); return; } } if (m_eventType <= EVENT_EXPLOSION || m_eventType >= EVENT_SHOPSTALL) { SetMoveState(PEDMOVE_WALK); return; } if (distSqr > sq(1.2f)) { SetMoveState(PEDMOVE_WALK); return; } for (int i = 0; i < m_numNearPeds; i++) { if ((m_eventOrThreat - m_nearPeds[i]->GetPosition()).MagnitudeSqr() < sq(0.4f)) { SetMoveState(PEDMOVE_STILL); return; } } SetMoveState(PEDMOVE_WALK); } } void CPed::ClearInvestigateEvent(void) { CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_ROADCROSS); if (!animAssoc) animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_XPRESS_SCRATCH); if (!animAssoc) animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_IDLE_HBHB); if (!animAssoc) animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_STD_CHAT); if (animAssoc) { animAssoc->blendDelta = -8.0f; animAssoc->flags |= ASSOC_DELETEFADEDOUT; } if (m_eventType > EVENT_EXPLOSION) m_chatTimer = CTimer::GetTimeInMilliseconds() + 15000; bGonnaInvestigateEvent = false; m_pEventEntity = nil; ClearLookFlag(); RestorePreviousState(); if(m_nMoveState == PEDMOVE_NONE || m_nMoveState == PEDMOVE_STILL) SetMoveState(PEDMOVE_WALK); } bool CPed::InflictDamage(CEntity *damagedBy, eWeaponType method, float damage, ePedPieceTypes pedPiece, uint8 direction) { CPlayerPed *player = FindPlayerPed(); float dieDelta = 4.0f; float dieSpeed = 0.0f; AnimationId dieAnim = ANIM_STD_KO_FRONT; bool headShot = false; bool willLinger = false; int random; if (player == this) { if (!player->m_bCanBeDamaged) return false; player->AnnoyPlayerPed(false); } if (DyingOrDead()) return false; if (!bUsesCollision && method != WEAPONTYPE_DROWNING) return false; if (bOnlyDamagedByPlayer && damagedBy != player && damagedBy != FindPlayerVehicle() && method != WEAPONTYPE_DROWNING && method != WEAPONTYPE_EXPLOSION) return false; float healthImpact; if (IsPlayer()) healthImpact = damage * 0.33f; else healthImpact = damage * m_pedStats->m_defendWeakness; bool detectDieAnim = true; if (m_nPedState == PED_FALL || m_nPedState == PED_GETUP) { if (!IsPedHeadAbovePos(-0.3f)) { if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL)) dieAnim = ANIM_STD_HIT_FLOOR_FRONT; else dieAnim = ANIM_STD_HIT_FLOOR; dieDelta *= 2.0f; dieSpeed = 0.5f; detectDieAnim = false; } else if (m_nPedState == PED_FALL) { dieAnim = ANIM_STD_NUM; detectDieAnim = false; } } if (detectDieAnim) { switch (method) { case WEAPONTYPE_UNARMED: if (bMeleeProof) return false; if (m_nPedState == PED_FALL) { if (IsPedHeadAbovePos(-0.3f)) { dieAnim = ANIM_STD_NUM; } else { if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL)) dieAnim = ANIM_STD_HIT_FLOOR_FRONT; else dieAnim = ANIM_STD_HIT_FLOOR; dieDelta = dieDelta * 2.0f; dieSpeed = 0.5f; } } else { switch (direction) { case 0: dieAnim = ANIM_STD_HIGHIMPACT_FRONT; break; case 1: dieAnim = ANIM_STD_HIGHIMPACT_LEFT; break; case 2: dieAnim = ANIM_STD_HIGHIMPACT_BACK; break; case 3: dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; break; default: break; } } break; case WEAPONTYPE_BASEBALLBAT: if (bMeleeProof) return false; if (m_nPedState == PED_FALL) { if (IsPedHeadAbovePos(-0.3f)) { dieAnim = ANIM_STD_NUM; } else { if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL)) dieAnim = ANIM_STD_HIT_FLOOR_FRONT; else dieAnim = ANIM_STD_HIT_FLOOR; dieDelta = dieDelta * 2.0f; dieSpeed = 0.5f; } } else { switch (direction) { case 0: dieAnim = ANIM_STD_HIGHIMPACT_FRONT; break; case 1: dieAnim = ANIM_STD_HIGHIMPACT_LEFT; break; case 2: dieAnim = ANIM_STD_HIGHIMPACT_BACK; break; case 3: dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; break; default: break; } } break; case WEAPONTYPE_COLT45: case WEAPONTYPE_UZI: case WEAPONTYPE_SHOTGUN: case WEAPONTYPE_AK47: case WEAPONTYPE_M16: case WEAPONTYPE_SNIPERRIFLE: if (bBulletProof) return false; bool dontRemoveLimb; if (IsPlayer() || bNoCriticalHits) dontRemoveLimb = true; else { switch (method) { case WEAPONTYPE_SNIPERRIFLE: dontRemoveLimb = false; break; case WEAPONTYPE_M16: dontRemoveLimb = false; break; case WEAPONTYPE_SHOTGUN: dontRemoveLimb = CGeneral::GetRandomNumber() & 7; break; default: dontRemoveLimb = CGeneral::GetRandomNumber() & 15; break; } } if (dontRemoveLimb) { if (method == WEAPONTYPE_SHOTGUN) { switch (direction) { case 0: dieAnim = ANIM_STD_HIGHIMPACT_FRONT; break; case 1: dieAnim = ANIM_STD_HIGHIMPACT_LEFT; break; case 2: dieAnim = ANIM_STD_HIGHIMPACT_BACK; break; case 3: dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; break; default: break; } } else dieAnim = ANIM_STD_KO_FRONT; willLinger = false; } else { switch (pedPiece) { case PEDPIECE_TORSO: willLinger = false; dieAnim = ANIM_STD_KO_FRONT; break; case PEDPIECE_MID: willLinger = false; dieAnim = ANIM_STD_KO_SHOT_STOMACH; break; case PEDPIECE_LEFTARM: dieAnim = ANIM_STD_KO_SHOT_ARM_L; RemoveBodyPart(PED_UPPERARML, direction); willLinger = true; break; case PEDPIECE_RIGHTARM: dieAnim = ANIM_STD_KO_SHOT_ARM_R; RemoveBodyPart(PED_UPPERARMR, direction); willLinger = true; break; case PEDPIECE_LEFTLEG: dieAnim = ANIM_STD_KO_SHOT_LEG_L; RemoveBodyPart(PED_UPPERLEGL, direction); willLinger = true; break; case PEDPIECE_RIGHTLEG: dieAnim = ANIM_STD_KO_SHOT_LEG_R; RemoveBodyPart(PED_UPPERLEGR, direction); willLinger = true; break; case PEDPIECE_HEAD: dieAnim = ANIM_STD_KO_SHOT_FACE; RemoveBodyPart(PED_HEAD, direction); headShot = true; willLinger = true; break; default: break; } } break; case WEAPONTYPE_ROCKETLAUNCHER: case WEAPONTYPE_GRENADE: case WEAPONTYPE_EXPLOSION: if (bExplosionProof) return false; if (CGame::nastyGame && !IsPlayer() && !bInVehicle && 1.0f + healthImpact > m_fArmour + m_fHealth) { random = CGeneral::GetRandomNumber(); if (random & 1) RemoveBodyPart(PED_UPPERARML, direction); if (random & 2) RemoveBodyPart(PED_UPPERLEGR, direction); if (random & 4) RemoveBodyPart(PED_HEAD, direction); if (random & 8) RemoveBodyPart(PED_UPPERARMR, direction); if (random & 0x10) RemoveBodyPart(PED_UPPERLEGL, direction); if (bBodyPartJustCameOff) willLinger = true; } // fall through case WEAPONTYPE_MOLOTOV: if (bExplosionProof) return false; switch (direction) { case 0: dieAnim = ANIM_STD_HIGHIMPACT_FRONT; break; case 1: dieAnim = ANIM_STD_HIGHIMPACT_LEFT; break; case 2: dieAnim = ANIM_STD_HIGHIMPACT_BACK; break; case 3: dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; break; default: break; } break; case WEAPONTYPE_FLAMETHROWER: if (bFireProof) return false; dieAnim = ANIM_STD_KO_FRONT; break; case WEAPONTYPE_RAMMEDBYCAR: case WEAPONTYPE_RUNOVERBYCAR: if (bCollisionProof) return false; random = CGeneral::GetRandomNumber() & 3; switch (random) { case 0: if ((pedPiece != PEDPIECE_LEFTARM || random <= 1) && (pedPiece != PEDPIECE_MID || random != 1)) { if (pedPiece == PEDPIECE_RIGHTARM && random > 1 || pedPiece == PEDPIECE_MID && random == 2) dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; else dieAnim = ANIM_STD_HIGHIMPACT_FRONT; } else dieAnim = ANIM_STD_HIGHIMPACT_LEFT; break; case 1: if (m_nPedState == PED_DIVE_AWAY) dieAnim = ANIM_STD_SPINFORWARD_LEFT; else dieAnim = ANIM_STD_HIGHIMPACT_LEFT; break; case 2: if ((pedPiece != PEDPIECE_LEFTARM || random <= 1) && (pedPiece != PEDPIECE_MID || random != 1)) { if ((pedPiece != PEDPIECE_RIGHTARM || random <= 1) && (pedPiece != PEDPIECE_MID || random != 2)) { dieAnim = ANIM_STD_HIGHIMPACT_BACK; } else { dieAnim = ANIM_STD_SPINFORWARD_RIGHT; } } else dieAnim = ANIM_STD_SPINFORWARD_LEFT; break; case 3: if (m_nPedState == PED_DIVE_AWAY) dieAnim = ANIM_STD_SPINFORWARD_RIGHT; else dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; break; default: break; } if (damagedBy) { CVehicle *vehicle = (CVehicle*)damagedBy; if (method == WEAPONTYPE_RAMMEDBYCAR) { float vehSpeed = vehicle->m_vecMoveSpeed.Magnitude(); dieDelta = 8.0f * vehSpeed + 4.0f; } else { float vehSpeed = vehicle->m_vecMoveSpeed.Magnitude(); dieDelta = 12.0f * vehSpeed + 4.0f; dieSpeed = 16.0f * vehSpeed + 1.0f; } } break; case WEAPONTYPE_DROWNING: dieAnim = ANIM_STD_DROWN; break; case WEAPONTYPE_FALL: if (bCollisionProof) return false; switch (direction) { case 0: dieAnim = ANIM_STD_HIGHIMPACT_FRONT; break; case 1: dieAnim = ANIM_STD_HIGHIMPACT_LEFT; break; case 2: dieAnim = ANIM_STD_HIGHIMPACT_BACK; break; case 3: dieAnim = ANIM_STD_HIGHIMPACT_RIGHT; break; default: break; } break; default: break; } } if (m_fArmour != 0.0f && method != WEAPONTYPE_DROWNING) { if (player == this) CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastArmourLoss = CTimer::GetTimeInMilliseconds(); if (healthImpact < m_fArmour) { m_fArmour = m_fArmour - healthImpact; healthImpact = 0.0f; } else { healthImpact = healthImpact - m_fArmour; m_fArmour = 0.0f; } } if (healthImpact != 0.0f) { if (player == this) CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss = CTimer::GetTimeInMilliseconds(); m_lastWepDam = method; } if (m_fHealth - healthImpact >= 1.0f && !willLinger) { m_fHealth -= healthImpact; return false; } if (bInVehicle) { if (method != WEAPONTYPE_DROWNING) { #ifdef VC_PED_PORTS if (m_pMyVehicle) { if (m_pMyVehicle->IsCar() && m_pMyVehicle->pDriver == this) { if (m_pMyVehicle->GetStatus() == STATUS_SIMPLE) { m_pMyVehicle->SetStatus(STATUS_PHYSICS); CCarCtrl::SwitchVehicleToRealPhysics(m_pMyVehicle); } m_pMyVehicle->AutoPilot.m_nCarMission = MISSION_NONE; m_pMyVehicle->AutoPilot.m_nCruiseSpeed = 0; m_pMyVehicle->AutoPilot.m_nTempAction = TEMPACT_HANDBRAKESTRAIGHT; m_pMyVehicle->AutoPilot.m_nTimeTempAction = CTimer::GetTimeInMilliseconds() + 2000; } if (m_pMyVehicle->CanPedExitCar()) { SetObjective(OBJECTIVE_LEAVE_CAR_AND_DIE, m_pMyVehicle); } else { m_fHealth = 0.0f; if (m_pMyVehicle && m_pMyVehicle->pDriver == this) { SetRadioStation(); m_pMyVehicle->SetStatus(STATUS_ABANDONED); } SetDie(dieAnim, dieDelta, dieSpeed); /* if (damagedBy == FindPlayerPed() && damagedBy != this) { // PlayerInfo stuff } */ } for (int i = 0; i < ARRAY_SIZE(m_pMyVehicle->pPassengers); i++) { CPed* passenger = m_pMyVehicle->pPassengers[i]; if (passenger && passenger != this && damagedBy) passenger->ReactToAttack(damagedBy); } CPed *driverOfVeh = m_pMyVehicle->pDriver; if (driverOfVeh && driverOfVeh != this && damagedBy) driverOfVeh->ReactToAttack(damagedBy); if (damagedBy == FindPlayerPed() || damagedBy && damagedBy == FindPlayerVehicle()) { CDarkel::RegisterKillByPlayer(this, method, headShot); m_threatEntity = FindPlayerPed(); } else { CDarkel::RegisterKillNotByPlayer(this, method); } } #endif m_fHealth = 1.0f; return false; } m_fHealth = 0.0f; if (player == this) m_pMyVehicle->SetStatus(STATUS_PLAYER_DISABLED); SetDie(ANIM_STD_NUM, 4.0f, 0.0f); return true; } else { m_fHealth = 0.0f; SetDie(dieAnim, dieDelta, dieSpeed); if (damagedBy == player || damagedBy && damagedBy == FindPlayerVehicle()) { // There are PlayerInfo stuff here in VC CDarkel::RegisterKillByPlayer(this, method, headShot); m_threatEntity = player; } else { CDarkel::RegisterKillNotByPlayer(this, method); } if (method == WEAPONTYPE_DROWNING) bIsInTheAir = false; return true; } } static RwObject* SetPedAtomicVisibilityCB(RwObject* object, void* data) { if (data == nil) RpAtomicSetFlags((RpAtomic*)object, 0); return object; } static RwFrame* RecurseFrameChildrenVisibilityCB(RwFrame* frame, void* data) { RwFrameForAllObjects(frame, SetPedAtomicVisibilityCB, data); RwFrameForAllChildren(frame, RecurseFrameChildrenVisibilityCB, nil); return frame; } static RwObject* CloneAtomicToFrameCB(RwObject *frame, void *data) { RpAtomic *newAtomic = RpAtomicClone((RpAtomic*)frame); RpAtomicSetFrame(newAtomic, (RwFrame*)data); RpClumpAddAtomic(flyingClumpTemp, newAtomic); CVisibilityPlugins::SetAtomicRenderCallback(newAtomic, nil); return frame; } static RwFrame* RecurseFrameChildrenToCloneCB(RwFrame *frame, void *data) { RwFrame *newFrame = RwFrameCreate(); RwFrameAddChild((RwFrame*)data, newFrame); RwFrameTransform(newFrame, RwFrameGetMatrix(frame), rwCOMBINEREPLACE); RwFrameForAllObjects(frame, CloneAtomicToFrameCB, newFrame); RwFrameForAllChildren(frame, RecurseFrameChildrenToCloneCB, newFrame); return newFrame; } void CPed::RemoveBodyPart(PedNode nodeId, int8 direction) { RwFrame *frame; CVector pos; frame = m_pFrames[nodeId]->frame; if (frame) { if (CGame::nastyGame) { #ifdef PED_SKIN if(!IsClumpSkinned(GetClump())) #endif { #ifdef DEBUGMENU if (bPopHeadsOnHeadshot || nodeId != PED_HEAD) #else if (nodeId != PED_HEAD) #endif SpawnFlyingComponent(nodeId, direction); RecurseFrameChildrenVisibilityCB(frame, nil); } pos.x = 0.0f; pos.y = 0.0f; pos.z = 0.0f; TransformToNode(pos, PED_HEAD); if (CEntity::GetIsOnScreen()) { CParticle::AddParticle(PARTICLE_TEST, pos, CVector(0.0f, 0.0f, 0.0f), nil, 0.1f, 0, 0, 0, 0); for (int i = 0; i < 16; i++) { CParticle::AddParticle(PARTICLE_BLOOD_SMALL, pos, CVector(0.0f, 0.0f, 0.03f), nil, 0.0f, 0, 0, 0, 0); } } bBodyPartJustCameOff = true; m_bodyPartBleeding = nodeId; } } else { printf("Trying to remove ped component"); } } CObject* CPed::SpawnFlyingComponent(int pedNode, int8 direction) { if (CObject::nNoTempObjects >= NUMTEMPOBJECTS) return nil; #ifdef PED_SKIN assert(!IsClumpSkinned(GetClump())); #endif CObject *obj = new CObject(); if (!obj) return nil; RwFrame *frame = RwFrameCreate(); RpClump *clump = RpClumpCreate(); RpClumpSetFrame(clump, frame); RwMatrix *matrix = RwFrameGetLTM(m_pFrames[pedNode]->frame); *RwFrameGetMatrix(frame) = *matrix; flyingClumpTemp = clump; RwFrameForAllObjects(m_pFrames[pedNode]->frame, CloneAtomicToFrameCB, frame); RwFrameForAllChildren(m_pFrames[pedNode]->frame, RecurseFrameChildrenToCloneCB, frame); flyingClumpTemp = nil; switch (pedNode) { case PED_HEAD: // So popping head would have wheel collision. They disabled it anyway obj->SetModelIndexNoCreate(MI_CAR_WHEEL); break; case PED_UPPERARML: case PED_UPPERARMR: obj->SetModelIndexNoCreate(MI_BODYPARTB); obj->SetCenterOfMass(0.25f, 0.0f, 0.0f); break; case PED_UPPERLEGL: case PED_UPPERLEGR: obj->SetModelIndexNoCreate(MI_BODYPARTA); obj->SetCenterOfMass(0.4f, 0.0f, 0.0f); break; default: break; } obj->RefModelInfo(GetModelIndex()); obj->AttachToRwObject((RwObject*)clump); obj->m_fMass = 15.0f; obj->m_fTurnMass = 5.0f; obj->m_fAirResistance = 0.99f; obj->m_fElasticity = 0.03f; obj->m_fBuoyancy = m_fMass*GRAVITY/0.75f; obj->ObjectCreatedBy = TEMP_OBJECT; obj->SetIsStatic(false); obj->bIsPickup = false; obj->m_nSpecialCollisionResponseCases = COLLRESPONSE_SMALLBOX; // life time - the more objects the are, the shorter this one will live CObject::nNoTempObjects++; if (CObject::nNoTempObjects > 20) obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 12000; else if (CObject::nNoTempObjects > 10) obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 30000; else obj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 60000; CVector localForcePos, forceDir; if (direction == 2) { obj->m_vecMoveSpeed = 0.03f * GetForward(); obj->m_vecMoveSpeed.z = (CGeneral::GetRandomNumber() & 0x3F) * 0.001f; obj->m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); localForcePos = CVector(0.0f, 0.0f, 0.0f); forceDir = GetForward(); } else { obj->m_vecMoveSpeed = -0.03f * GetForward(); obj->m_vecMoveSpeed.z = (CGeneral::GetRandomNumber() & 0x3F) * 0.001f; obj->m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); localForcePos = CVector(0.0f, 0.0f, 0.0f); forceDir = -GetForward(); } obj->ApplyTurnForce(forceDir, localForcePos); CWorld::Add(obj); return obj; } void CPed::ApplyHeadShot(eWeaponType weaponType, CVector pos, bool evenOnPlayer) { CVector pos2 = CVector( pos.x, pos.y, pos.z + 0.1f ); if (!IsPlayer() || evenOnPlayer) { ++CStats::HeadsPopped; // BUG: This condition will always return true. Even fixing it won't work, because these states are unused. // if (m_nPedState != PED_PASSENGER || m_nPedState != PED_TAXI_PASSENGER) { SetDie(ANIM_STD_KO_FRONT, 4.0f, 0.0f); // } bBodyPartJustCameOff = true; m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 150; CParticle::AddParticle(PARTICLE_TEST, pos2, CVector(0.0f, 0.0f, 0.0f), nil, 0.2f, 0, 0, 0, 0); if (CEntity::GetIsOnScreen()) { for(int i=0; i < 32; i++) { CParticle::AddParticle(PARTICLE_BLOOD_SMALL, pos2, CVector(0.0f, 0.0f, 0.03f), nil, 0.0f, 0, 0, 0, 0); } for (int i = 0; i < 16; i++) { CParticle::AddParticle(PARTICLE_DEBRIS2, pos2, CVector(0.0f, 0.0f, 0.01f), nil, 0.0f, 0, 0, 0, 0); } } } } uint8 CPed::DoesLOSBulletHitPed(CColPoint &colPoint) { #ifdef FIX_BUGS return 1; #else uint8 retVal = 2; float headZ = GetNodePosition(PED_HEAD).z; if (m_nPedState == PED_FALL) retVal = 1; float colZ = colPoint.point.z; if (colZ < headZ) retVal = 1; if (headZ + 0.2f <= colZ) retVal = 0; return retVal; #endif } bool CPed::IsPedHeadAbovePos(float zOffset) { return zOffset + GetPosition().z < GetNodePosition(PED_HEAD).z; } bool CPed::PlacePedOnDryLand(void) { float waterLevel = 0.0f; CEntity *foundEnt = nil; CColPoint foundCol; float foundColZ; CWaterLevel::GetWaterLevelNoWaves(GetPosition().x, GetPosition().y, GetPosition().z, &waterLevel); CVector potentialGround = GetPosition(); potentialGround.z = waterLevel; if (!CWorld::TestSphereAgainstWorld(potentialGround, 5.0f, nil, true, false, false, false, false, false)) return false; CVector potentialGroundDist = gaTempSphereColPoints[0].point - GetPosition(); potentialGroundDist.z = 0.0f; potentialGroundDist.Normalise(); CVector posToCheck = 0.5f * potentialGroundDist + gaTempSphereColPoints[0].point; posToCheck.z = 3.0f + waterLevel; if (CWorld::ProcessVerticalLine(posToCheck, waterLevel - 1.0f, foundCol, foundEnt, true, true, false, true, false, false, nil)) { foundColZ = foundCol.point.z; if (foundColZ >= waterLevel) { posToCheck.z = 0.8f + foundColZ; SetPosition(posToCheck); bIsStanding = true; bWasStanding = true; return true; } } posToCheck = 5.0f * potentialGroundDist + GetPosition(); posToCheck.z = 3.0f + waterLevel; if (!CWorld::ProcessVerticalLine(posToCheck, waterLevel - 1.0f, foundCol, foundEnt, true, true, false, true, false, false, nil)) return false; foundColZ = foundCol.point.z; if (foundColZ < waterLevel) return false; posToCheck.z = 0.8f + foundColZ; SetPosition(posToCheck); bIsStanding = true; bWasStanding = true; return true; } void CPed::CollideWithPed(CPed *collideWith) { CAnimBlendAssociation *animAssoc; AnimationId animToPlay; bool weAreMissionChar = CharCreatedBy == MISSION_CHAR; bool heIsMissionChar = collideWith->CharCreatedBy == MISSION_CHAR; CVector posDiff = collideWith->GetPosition() - GetPosition(); int waitTime = 0; if (weAreMissionChar || !collideWith->IsPlayer() || collideWith->m_nPedState != PED_MAKE_CALL) { bool weDontLookToHim = DotProduct(posDiff, GetForward()) > 0.0f; bool heLooksToUs = DotProduct(posDiff, collideWith->GetForward()) < 0.0f; if (m_nMoveState != PEDMOVE_NONE && m_nMoveState != PEDMOVE_STILL) { if ((!IsPlayer() || ((CPlayerPed*)this)->m_fMoveSpeed <= 1.8f) && (IsPlayer() || heIsMissionChar && weAreMissionChar || m_nMoveState != PEDMOVE_RUN && m_nMoveState != PEDMOVE_SPRINT #ifdef VC_PED_PORTS || m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION && m_pedInObjective == collideWith || collideWith->m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION && collideWith->m_pedInObjective == this #endif )) { if (m_objective != OBJECTIVE_FOLLOW_CHAR_IN_FORMATION && m_objective != OBJECTIVE_GOTO_CHAR_ON_FOOT) { if (CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) { if (heIsMissionChar || !weAreMissionChar && collideWith->m_nMoveState != PEDMOVE_STILL) { if (weAreMissionChar && (m_nPedState == PED_SEEK_POS || m_nPedState == PED_SEEK_ENTITY)) { if (collideWith->m_nMoveState != PEDMOVE_STILL && (!collideWith->IsPlayer() || collideWith->IsPlayer() && CPad::GetPad(0)->ArePlayerControlsDisabled())) { float seekPosDist = (GetPosition() - m_vecSeekPos).MagnitudeSqr2D(); float heAndSeekPosDist = (collideWith->GetPosition() - m_vecSeekPos).MagnitudeSqr2D(); if (seekPosDist <= heAndSeekPosDist) { waitTime = 1000; collideWith->SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &waitTime); collideWith->m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + waitTime; } else { waitTime = 500; SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &waitTime); m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + waitTime; } } else if (collideWith->m_nMoveState == PEDMOVE_STILL) { SetDirectionToWalkAroundObject(collideWith); } } else { if (weAreMissionChar || m_pedStats->m_fear <= 100 - collideWith->m_pedStats->m_temper || (collideWith->IsPlayer() || collideWith->m_nMoveState == PEDMOVE_NONE || collideWith->m_nMoveState == PEDMOVE_STILL) && (!collideWith->IsPlayer() || ((CPlayerPed*)collideWith)->m_fMoveSpeed <= 1.0f)) { SetDirectionToWalkAroundObject(collideWith); if (!weAreMissionChar) Say(SOUND_PED_CHAT); } else { SetEvasiveStep(collideWith, 2); } } } else { if (m_pedStats->m_temper <= m_pedStats->m_fear || GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED || weAreMissionChar || collideWith->m_nPedType == PEDTYPE_CIVFEMALE || collideWith->m_nPedType == m_nPedType || collideWith->GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) { SetDirectionToWalkAroundObject(collideWith); Say(SOUND_PED_CHAT); } else { TurnBody(); SetAttack(collideWith); #ifdef VC_PED_PORTS m_fRotationCur = 0.3f + m_fRotationCur; m_fRotationDest = m_fRotationCur; #endif } m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(250, 450); } } } else { #ifdef VC_PED_PORTS if (m_pedInObjective && (collideWith == m_pedInObjective || collideWith->m_pedInObjective == m_pedInObjective) && CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) { #else if (m_pedInObjective && collideWith == m_pedInObjective && CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) { #endif if (heLooksToUs) { SetEvasiveStep(collideWith, 1); m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 3000; } } else if (weDontLookToHim && IsPedInControl()) { if (m_pedStats != collideWith->m_pedStats) { if (collideWith->m_pedStats->m_fear <= 100 - m_pedStats->m_temper #ifdef VC_PED_PORTS || collideWith->IsPlayer() || CTimer::GetTimeInMilliseconds() < m_nPedStateTimer #endif ) { if (collideWith->IsPlayer()) { // He's on our right side if (DotProduct(posDiff,GetRight()) <= 0.0f) m_fRotationCur -= m_headingRate; else m_fRotationCur += m_headingRate; } else { // He's on our right side if (DotProduct(posDiff, collideWith->GetRight()) <= 0.0f) collideWith->m_fRotationCur -= collideWith->m_headingRate; else collideWith->m_fRotationCur += collideWith->m_headingRate; } } else { SetLookFlag(collideWith, false); TurnBody(); animAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_STD_PARTIAL_PUNCH, 8.0f); animAssoc->flags |= ASSOC_FADEOUTWHENDONE; #ifdef VC_PED_PORTS m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 2000; #endif if (!heIsMissionChar) { CVector2D posDiff2D(posDiff); int direction = collideWith->GetLocalDirection(posDiff2D); collideWith->StartFightDefend(direction, HITLEVEL_HIGH, 5); } } } } } } else if (collideWith->m_pedStats->m_defendWeakness <= 1.5f || heIsMissionChar #ifdef VC_PED_PORTS || m_pedStats->m_defendWeakness <= collideWith->m_pedStats->m_defendWeakness #endif ) { // He looks us and we're not at his right side if (heLooksToUs && DotProduct(posDiff,collideWith->GetRight()) > 0.0f) { CVector moveForce = GetRight(); moveForce.z += 0.1f; ApplyMoveForce(moveForce); if (collideWith->m_nMoveState != PEDMOVE_RUN && collideWith->m_nMoveState != PEDMOVE_SPRINT) animToPlay = ANIM_STD_HIT_LEFT; else animToPlay = ANIM_STD_HITBYGUN_LEFT; } else if (heLooksToUs) { CVector moveForce = GetRight() * -1.0f; moveForce.z += 0.1f; ApplyMoveForce(moveForce); if (collideWith->m_nMoveState != PEDMOVE_RUN && collideWith->m_nMoveState != PEDMOVE_SPRINT) animToPlay = ANIM_STD_HIT_RIGHT; else animToPlay = ANIM_STD_HITBYGUN_RIGHT; } else { if (collideWith->m_nMoveState != PEDMOVE_RUN && collideWith->m_nMoveState != PEDMOVE_SPRINT) animToPlay = ANIM_STD_HIT_BACK; else animToPlay = ANIM_STD_HITBYGUN_BACK; } if (collideWith->IsPedInControl() && CTimer::GetTimeInMilliseconds() > collideWith->m_nPedStateTimer) { animAssoc = CAnimManager::BlendAnimation(collideWith->GetClump(), ASSOCGRP_STD, animToPlay, 8.0f); animAssoc->flags |= ASSOC_FADEOUTWHENDONE; collideWith->m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 1000; if (m_nPedState == PED_ATTACK) DMAudio.PlayOneShot(m_audioEntityId, SOUND_FIGHT_PUNCH_39, 0.0f); } } else { // We're at his right side if (DotProduct(posDiff, collideWith->GetRight()) <= 0.0f) { CVector moveForce = GetRight() * -1.0f; moveForce.z += 0.1f; ApplyMoveForce(moveForce); if (heLooksToUs) animToPlay = ANIM_STD_HIGHIMPACT_RIGHT; else animToPlay = ANIM_STD_SPINFORWARD_RIGHT; } else { CVector moveForce = GetRight(); moveForce.z += 0.1f; ApplyMoveForce(moveForce); if (heLooksToUs) animToPlay = ANIM_STD_HIGHIMPACT_LEFT; else animToPlay = ANIM_STD_SPINFORWARD_LEFT; } if (m_nPedState == PED_ATTACK && collideWith->IsPedInControl()) DMAudio.PlayOneShot(m_audioEntityId, SOUND_FIGHT_PUNCH_39, 0.0f); collideWith->SetFall(3000, animToPlay, 0); } } else { if (!IsPedInControl()) return; if (collideWith->m_nMoveState == PEDMOVE_NONE || collideWith->m_nMoveState == PEDMOVE_STILL) return; if (m_nPedType != collideWith->m_nPedType || m_nPedType == PEDTYPE_CIVMALE || m_nPedType == PEDTYPE_CIVFEMALE) { if (!weAreMissionChar && heLooksToUs && m_pedStats->m_fear > 100 - collideWith->m_pedStats->m_temper) { if (CGeneral::GetRandomNumber() & 1 && CTimer::GetTimeInMilliseconds() < m_nPedStateTimer){ SetEvasiveStep(collideWith, 2); m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 3000; } else if (collideWith->m_nMoveState > PEDMOVE_WALK) { waitTime = 2000; SetWaitState(WAITSTATE_PLAYANIM_DUCK, &waitTime); } } } else if (heLooksToUs && collideWith->m_nPedState != PED_STEP_AWAY && m_nPedState != PED_STEP_AWAY && CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) { SetEvasiveStep(collideWith, 1); m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 3000; } } if (IsPlayer()) { SetLookFlag(collideWith, true); SetLookTimer(800); } } else { bool isRunning = m_nMoveState == PEDMOVE_RUN || m_nMoveState == PEDMOVE_SPRINT; SetFindPathAndFlee(collideWith, 5000, !isRunning); } } void CPed::KillPedWithCar(CVehicle *car, float impulse) { CVehicleModelInfo *vehModel; CColModel *vehColModel; uint8 damageDir; PedNode nodeToDamage; eWeaponType killMethod; if (m_nPedState == PED_FALL || m_nPedState == PED_DIE) { if (!this->m_pCollidingEntity || car->GetStatus() == STATUS_PLAYER) this->m_pCollidingEntity = car; return; } if (m_nPedState == PED_DEAD) return; if (m_pCurSurface) { if (m_pCurSurface->IsVehicle() && (((CVehicle*)m_pCurSurface)->m_vehType == VEHICLE_TYPE_BOAT || IsPlayer())) return; } CVector distVec = GetPosition() - car->GetPosition(); if ((impulse > 12.0f || car->GetModelIndex() == MI_TRAIN) && !IsPlayer()) { nodeToDamage = PED_TORSO; killMethod = WEAPONTYPE_RAMMEDBYCAR; uint8 randVal = CGeneral::GetRandomNumber() & 3; if (car == FindPlayerVehicle()) { float carSpeed = car->m_vecMoveSpeed.Magnitude(); uint8 shakeFreq; if (100.0f * carSpeed * 2000.0f / car->m_fMass + 80.0f <= 250.0f) { shakeFreq = 100.0f * carSpeed * 2000.0f / car->m_fMass + 80.0f; } else { shakeFreq = 250.0f; } CPad::GetPad(0)->StartShake(40000 / shakeFreq, shakeFreq); } bIsStanding = false; damageDir = GetLocalDirection(-m_vecMoveSpeed); vehModel = (CVehicleModelInfo *)CModelInfo::GetModelInfo(car->GetModelIndex()); vehColModel = vehModel->GetColModel(); float carRightAndDistDotProd = DotProduct(distVec, car->GetRight()); if (car->GetModelIndex() == MI_TRAIN) { killMethod = WEAPONTYPE_RUNOVERBYCAR; nodeToDamage = PED_HEAD; m_vecMoveSpeed = 0.9f * car->m_vecMoveSpeed; m_vecMoveSpeed.z = 0.0f; if (damageDir == 1 || damageDir == 3) damageDir = 2; if (CGame::nastyGame) DMAudio.PlayOneShot(m_audioEntityId, SOUND_SPLATTER, 0.0f); // Car doesn't look to us } else if (DotProduct(car->m_vecMoveSpeed, car->GetForward()) >= 0.0f){ if (0.99f * vehColModel->boundingBox.max.x < Abs(carRightAndDistDotProd)) { // We're at the right of the car if (carRightAndDistDotProd <= 0.0f) nodeToDamage = PED_UPPERARML; else nodeToDamage = PED_UPPERARMR; if (Abs(DotProduct(distVec, car->GetForward())) < 0.85f * vehColModel->boundingBox.max.y) { killMethod = WEAPONTYPE_RUNOVERBYCAR; m_vecMoveSpeed = 0.9f * car->m_vecMoveSpeed; m_vecMoveSpeed.z = 0.0f; if (damageDir == 1 || damageDir == 3) damageDir = 2; if (CGame::nastyGame) DMAudio.PlayOneShot(m_audioEntityId, SOUND_SPLATTER, 0.0f); } } else { float carFrontAndDistDotProd = DotProduct(distVec, car->GetForward()); // carFrontAndDistDotProd <= 0.0 car looks to us if ((carFrontAndDistDotProd <= 0.1 || randVal == 1) && randVal != 0) { killMethod = WEAPONTYPE_RUNOVERBYCAR; nodeToDamage = PED_HEAD; m_vecMoveSpeed = 0.9f * car->m_vecMoveSpeed; m_vecMoveSpeed.z = 0.0f; if (damageDir == 1 || damageDir == 3) damageDir = 2; if (CGame::nastyGame) DMAudio.PlayOneShot(m_audioEntityId, SOUND_SPLATTER, 0.0f); } else { nodeToDamage = PED_MID; float vehColMaxY = vehColModel->boundingBox.max.y; float vehColMinY = vehColModel->boundingBox.min.y; float vehColMaxZ = vehColModel->boundingBox.max.z; float carFrontZ = car->GetForward().z; float carHighestZ, carLength; if (carFrontZ < -0.2f) { // Highest point of car's back carHighestZ = (car->GetMatrix() * CVector(0.0f, vehColMinY, vehColMaxZ)).z; carLength = vehColMaxY - vehColMinY; } else if (carFrontZ > 0.1f) { // Highest point of car's front carHighestZ = (car->GetMatrix() * CVector(0.0f, vehColMaxY, vehColMaxZ)).z; float highestZDist = carHighestZ - GetPosition().z; if (highestZDist > 0.0f) { GetMatrix().GetPosition().z += 0.5f * highestZDist; carHighestZ += highestZDist * 0.25f; } carLength = vehColMaxY; } else { // Highest point of car's front carHighestZ = (car->GetMatrix() * CVector(0.0f, vehColMaxY, vehColMaxZ)).z; carLength = vehColMaxY; } float pedJumpSpeedToReachHighestZ = (carHighestZ - GetPosition().z) / (carLength / car->m_vecMoveSpeed.Magnitude()); // TODO: What are we doing down here? float unknown = ((CGeneral::GetRandomNumber() % 256) * 0.002 + 1.5) * pedJumpSpeedToReachHighestZ; // After this point, distVec isn't distVec anymore. distVec = car->m_vecMoveSpeed; distVec.Normalise(); distVec *= 0.2 * unknown; if (damageDir != 1 && damageDir != 3) distVec.z += unknown; else distVec.z += 1.5f * unknown; m_vecMoveSpeed = distVec; damageDir += 2; if (damageDir > 3) damageDir = damageDir - 4; if (car->m_vehType == VEHICLE_TYPE_CAR) { CObject *bonnet = ((CAutomobile*)car)->RemoveBonnetInPedCollision(); if (bonnet) { if (CGeneral::GetRandomNumber() & 1) { bonnet->m_vecMoveSpeed += Multiply3x3(car->GetMatrix(), CVector(0.1f, 0.0f, 0.5f)); } else { bonnet->m_vecMoveSpeed += Multiply3x3(car->GetMatrix(), CVector(-0.1f, 0.0f, 0.5f)); } CVector forceDir = car->GetUp() * 10.0f; bonnet->ApplyTurnForce(forceDir, car->GetForward()); } } } } } if (car->pDriver) { CEventList::RegisterEvent((m_nPedType == PEDTYPE_COP ? EVENT_HIT_AND_RUN_COP : EVENT_HIT_AND_RUN), EVENT_ENTITY_PED, this, car->pDriver, 1000); } ePedPieceTypes pieceToDamage; switch (nodeToDamage) { case PED_HEAD: pieceToDamage = PEDPIECE_HEAD; break; case PED_UPPERARML: pieceToDamage = PEDPIECE_LEFTARM; break; case PED_UPPERARMR: pieceToDamage = PEDPIECE_RIGHTARM; break; default: pieceToDamage = PEDPIECE_MID; break; } InflictDamage(car, killMethod, 1000.0f, pieceToDamage, damageDir); if (DyingOrDead() && bIsPedDieAnimPlaying && !m_pCollidingEntity) { m_pCollidingEntity = car; } if (nodeToDamage == PED_MID) bKnockedUpIntoAir = true; else bKnockedUpIntoAir = false; distVec.Normalise(); #ifdef VC_PED_PORTS distVec *= Min(car->m_fMass / 1400.0f, 1.0f); #endif car->ApplyMoveForce(distVec * -100.0f); Say(SOUND_PED_DEFEND); } else if (m_vecDamageNormal.z < -0.8f && impulse > 3.0f || impulse > 6.0f && (!IsPlayer() || impulse > 10.0f)) { bIsStanding = false; uint8 fallDirection = GetLocalDirection(-car->m_vecMoveSpeed); float damage; if (IsPlayer() && car->GetModelIndex() == MI_TRAIN) damage = 150.0f; else damage = 30.0f; InflictDamage(car, WEAPONTYPE_RAMMEDBYCAR, damage, PEDPIECE_TORSO, fallDirection); SetFall(1000, (AnimationId)(fallDirection + ANIM_STD_HIGHIMPACT_FRONT), true); if (OnGround() && !m_pCollidingEntity && (!IsPlayer() || bHasHitWall || car->GetModelIndex() == MI_TRAIN || m_vecDamageNormal.z < -0.8f)) { m_pCollidingEntity = car; } bKnockedUpIntoAir = false; if (car->GetModelIndex() != MI_TRAIN && !bHasHitWall) { m_vecMoveSpeed = car->m_vecMoveSpeed * 0.75f; } m_vecMoveSpeed.z = 0.0f; distVec.Normalise(); #ifdef VC_PED_PORTS distVec *= Min(car->m_fMass / 1400.0f, 1.0f); #endif car->ApplyMoveForce(distVec * -60.0f); Say(SOUND_PED_DEFEND); } #ifdef VC_PED_PORTS // Killing gang members with car wasn't triggering a fight, until now... Taken from VC. if (IsGangMember()) { CPed *driver = car->pDriver; if (driver && driver->IsPlayer() #ifdef FIX_BUGS && (CharCreatedBy != MISSION_CHAR || bRespondsToThreats) && (!m_leader || m_leader != driver) #endif ) { RegisterThreatWithGangPeds(driver); } } #endif }