#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"
#include "Bike.h"
#include "Glass.h"
#include "SpecialFX.h"

uint16 nPlayerInComboMove;
RpClump* flyingClumpTemp;

FightMove tFightMoves[NUM_FIGHTMOVES] =
{
  { NUM_STD_ANIMS,  0.0f,  0.0f,  0.0f,  0.0f,  1.0f, HITLEVEL_NULL, 0, 0 },
  { ANIM_PUNCH_R, 0.2f, 8.f/30.f, 0.0f, 0.3f, 1.0f, HITLEVEL_HIGH, 1, 0 },
  { ANIM_FIGHT_IDLE,  0.0f,  0.0f,  0.0f,  0.0f,  1.0f, HITLEVEL_NULL, 0, 0 },
  { ANIM_FIGHT_SH_F,  0.0f,  0.0f,  0.0f,  0.0f,  1.0f, HITLEVEL_NULL, 0, 0 },
  { ANIM_FIGHT_KNEE, 4.f/30.f, 0.2f, 0.0f, 0.6f, 1.0f, HITLEVEL_LOW, 2, 0 },
  { ANIM_FIGHT_LHOOK, 8.f/30.f, 10.f/30.f, 0.0f, 0.4f, 1.0f, HITLEVEL_HIGH, 3, 0 },
  { ANIM_FIGHT_JAB, 4.f/30.f, 0.2f, 0.0f, 0.7f, 1.0f, HITLEVEL_HIGH, 3, 0 },
  { ANIM_FIGHT_PUNCH, 4.f/30.f, 7.f/30.f, 10.f/30.f, 0.4f, 1.0f, HITLEVEL_HIGH, 1, 0 },
  { ANIM_FIGHT_LONGKICK, 8.f/30.f, 10.f/30.f, 0.0f, 0.5f, 1.0f, HITLEVEL_MEDIUM, 4, 0 },
  { ANIM_FIGHT_ROUNDHOUSE, 8.f/30.f, 10.f/30.f, 0.0f,  0.6f, 1.0f, HITLEVEL_MEDIUM, 4, 0 },
  { ANIM_FIGHT_KICK,  8.f/30.f,  10.f/30.f,  0.0f,  0.5f,  1.0f, HITLEVEL_HIGH, 2, 0 },
  { ANIM_FIGHT_HEAD,  8.f/30.f,  10.f/30.f,  0.0f,  0.5f,  1.0f, HITLEVEL_MEDIUM, 2, 0 },
  { ANIM_FIGHT_BKICK_L,  8.f/30.f,  10.f/30.f,  0.0f,  0.5f,  1.0f, HITLEVEL_LOW, 2, 0 },
  { ANIM_FIGHT_BKICK_L,  8.f/30.f,  10.f/30.f,  0.0f,  0.5f,  1.0f, HITLEVEL_LOW, 2, 0 },
  { ANIM_FIGHT_ELBOW_L,  8.f/30.f,  10.f/30.f,  0.0f,  0.5f,  1.0f, HITLEVEL_MEDIUM, 2, 0 },
  { ANIM_FIGHT_BKICK_R,  8.f/30.f,  10.f/30.f,  0.0f,  0.5f,  1.0f, HITLEVEL_MEDIUM, 2, 0 },
  { ANIM_FIGHT_ELBOW_R,  8.f/30.f,  10.f/30.f,  0.0f,  0.5f,  1.0f, HITLEVEL_HIGH, 2, 0 },
  { ANIM_KICK_FLOOR, 10.f/30.f, 14.f/30.f, 0.0f, 0.4f, 1.0f, HITLEVEL_GROUND, 1, 0 },
  { ANIM_HIT_FRONT,  0.0f,  0.0f,  0.0f,  0.0f,  1.0f, HITLEVEL_NULL, 0, 0 },
  { ANIM_HIT_BACK,  0.0f,  0.0f,  0.0f,  0.0f,  1.0f, HITLEVEL_NULL, 0, 0 },
  { ANIM_HIT_RIGHT,  0.0f,  0.0f,  0.0f,  0.0f,  1.0f, HITLEVEL_NULL, 0, 0 },
  { ANIM_HIT_LEFT,  0.0f,  0.0f,  0.0f,  0.0f,  1.0f, HITLEVEL_NULL, 0, 0 },
  { ANIM_HIT_BODYBLOW,  0.0f,  0.0f,  0.0f,  0.0f,  1.0f, HITLEVEL_NULL, 0, 0 },
  { ANIM_HIT_CHEST,  0.0f,  0.0f,  0.0f,  0.0f,  1.0f, HITLEVEL_NULL, 0, 0 },
  { ANIM_HIT_HEAD,  0.0f,  0.0f,  0.0f,  0.0f,  1.0f, HITLEVEL_NULL, 0, 0 },
  { ANIM_HIT_WALK,  0.0f,  0.0f,  0.0f,  0.0f,  1.0f, HITLEVEL_NULL, 0, 0 },
  { ANIM_FLOOR_HIT,  0.0f,  0.0f,  0.0f,  0.0f,  1.0f, HITLEVEL_NULL, 0, 0 },
  { ANIM_HIT_BEHIND,  0.0f,  0.0f,  0.0f,  0.0f,  1.0f, HITLEVEL_NULL, 0, 0 },
  { ANIM_WEAPON_FIRE, 4.f/30.f, 7.f/30.f, 10.f/30.f, 0.4f, 1.0f, HITLEVEL_HIGH, 1, 0 },
  { ANIM_WEAPON_CROUCHFIRE, 4.f/30.f, 7.f/30.f, 10.f/30.f, 0.4f, 1.0f, HITLEVEL_HIGH, 1, 0 },
  { ANIM_WEAPON_SPECIAL, 4.f / 30.f, 7.f / 30.f, 10.f / 30.f, 0.4f, 1.0f, HITLEVEL_HIGH, 1, 0 },
  { ANIM_FIGHT2_IDLE,  0.0f,  0.0f,  0.0f,  0.0f,  1.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, true);
		SetAimFlag(to);
		SetLookTimer(INT32_MAX);
	}

	CWeaponInfo* curWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType);
	if (m_nPedState == PED_AIM_GUN || (bIsDucking && !IsPlayer()) || m_nWaitState == WAITSTATE_PLAYANIM_DUCK || curWeapon->m_AnimToPlay == ASSOCGRP_STD)
		return;

	if (m_nPedState != PED_ATTACK)
		SetStoredState();

	SetPedState(PED_AIM_GUN);
	bIsPointingGunAt = true;
	SetMoveState(PEDMOVE_STILL);

	CAnimBlendAssociation *aimAssoc;

	if (bCrouchWhenShooting && bIsDucking && GetCrouchFireAnim(curWeapon)) {
		aimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(curWeapon));
	} else {
		aimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_FIRE);
	}

	if (!aimAssoc || aimAssoc->blendDelta < 0.0f) {
		if (bCrouchWhenShooting && bIsDucking && GetCrouchFireAnim(curWeapon)) {
			aimAssoc = CAnimManager::BlendAnimation(GetClump(), curWeapon->m_AnimToPlay, GetCrouchFireAnim(curWeapon), 4.0f);
		} else {
			aimAssoc = CAnimManager::AddAnimation(GetClump(), curWeapon->m_AnimToPlay, ANIM_WEAPON_FIRE);
		}

		aimAssoc->blendAmount = 0.0f;
		aimAssoc->blendDelta = 8.0f;
	}
	if (to && !IsPlayer())
		Say(SOUND_PED_ATTACK);
}

void
CPed::PointGunAt(void)
{
	CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType);
	float animLoopStart = weaponInfo->m_fAnimLoopStart;
	CAnimBlendAssociation *weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_FIRE);
	if (!weaponAssoc || weaponAssoc->blendDelta < 0.0f) {
		if (weaponInfo->IsFlagSet(WEAPONFLAG_CROUCHFIRE)) {
			weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(weaponInfo));
			animLoopStart = weaponInfo->m_fAnim2LoopStart;
		}
	}

	if (weaponAssoc && weaponAssoc->currentTime > animLoopStart * 0.4f) {
		weaponAssoc->SetCurrentTime(animLoopStart);
		weaponAssoc->flags &= ~ASSOC_RUNNING;

		if (bIsDucking)
			m_pedIK.m_flags &= ~CPedIK::AIMS_WITH_ARM;

		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;
	if (m_nPedState == PED_AIM_GUN || m_nPedState == PED_ATTACK) {
		SetPedState(PED_IDLE);
		RestorePreviousState();
	}
	weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType);
	animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_FIRE);
	if (!animAssoc || animAssoc->blendDelta < 0.0f) {
		if (weaponInfo->IsFlagSet(WEAPONFLAG_CROUCHFIRE)) {
			animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(weaponInfo));
		}
	}
	if (animAssoc) {
		animAssoc->flags |= ASSOC_DELETEFADEDOUT;
		animAssoc->blendDelta = -4.0f;
	}
}

void
CPed::SetAttack(CEntity *victim)
{
	CPed *victimPed = nil;
	CWeaponInfo *curWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType);
	CAnimBlendAssociation *animAssoc;

	if (victim && victim->IsPed())
		victimPed = (CPed*)victim;

	if (m_attackTimer > CTimer::GetTimeInMilliseconds() || m_nWaitState == WAITSTATE_SURPRISE || (bIsDucking && !bCrouchWhenShooting))
		return;

	if (curWeapon->IsFlagSet(WEAPONFLAG_RELOAD) &&
		(RpAnimBlendClumpGetAssociation(GetClump(), GetReloadAnim(curWeapon)) || RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchReloadAnim(curWeapon)))) {
		if (!IsPlayer() || m_nPedState != PED_ATTACK || ((CPlayerPed*)this)->m_bHaveTargetSelected)
			bIsAttacking = false;
		else
			bIsAttacking = true;

		return;
	}

	if (GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED || curWeapon->IsFlagSet(WEAPONFLAG_FIGHTMODE) || GetWeapon()->m_eWeaponType == WEAPONTYPE_BRASSKNUCKLE) {
		if (IsPlayer() ||
			(m_nPedState != PED_FIGHT && m_nMoveState != PEDMOVE_NONE && m_nMoveState != PEDMOVE_STILL
				&& !(m_pedStats->m_flags & STAT_SHOPPING_BAGS) && curWeapon->IsFlagSet(WEAPONFLAG_PARTIALATTACK))) {

			if (m_nPedState != PED_ATTACK) {
				SetPedState(PED_ATTACK);
				bIsAttacking = false;

				CAnimBlendAssociation *animAssoc = CAnimManager::BlendAnimation(GetClump(), curWeapon->m_AnimToPlay, ANIM_MELEE_ATTACK_START, 8.0f);
				animAssoc->SetRun();
				if (animAssoc->currentTime == animAssoc->hierarchy->totalLength)
					animAssoc->SetCurrentTime(0.0f);

				animAssoc->SetFinishCallback(FinishedAttackCB, this);
			}
		} else {
			StartFightAttack(CGeneral::GetRandomNumber());
		}
		return;
	}

	if (curWeapon->IsFlagSet(WEAPONFLAG_PARTIALATTACK) &&
		(IsPlayer() && ((CPlayerPed*)this)->m_fMoveSpeed >= 1.0f ||
		m_nMoveState == PEDMOVE_WALK || m_nMoveState == PEDMOVE_RUN)) {

		if (m_nPedState != PED_ATTACK) {
			SetPedState(PED_ATTACK);
			bIsAttacking = false;
			CAnimBlendAssociation* animAssoc = CAnimManager::BlendAnimation(GetClump(), curWeapon->m_AnimToPlay, ANIM_MELEE_ATTACK_START, 8.0f);
			animAssoc->SetRun();
			if (animAssoc->currentTime == animAssoc->hierarchy->totalLength)
				animAssoc->SetCurrentTime(0.0f);

			animAssoc->SetFinishCallback(FinishedAttackCB, this);
		}
		return;
	}

	if (m_pSeekTarget)
		m_pSeekTarget->CleanUpOldReference(&m_pSeekTarget);
	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();
		aimPos += GetUp() * 0.35f;
		CEntity *obstacle = CWorld::TestSphereAgainstWorld(aimPos, 0.2f, nil, true, false, false, true, false, false);
		if (obstacle) {
			if(gaTempSphereColPoints[0].surfaceB != SURFACE_TRANSPARENT_CLOTH && gaTempSphereColPoints[0].surfaceB != SURFACE_METAL_CHAIN_FENCE &&
				gaTempSphereColPoints[0].surfaceB != SURFACE_WOOD_BENCH && gaTempSphereColPoints[0].surfaceB != SURFACE_SCAFFOLD_POLE) {
				if (!IsPlayer()) {
					bObstacleShowedUpDuringKillObjective = true;
					m_shootTimer = 0;
					SetAttackTimer(1500);
					m_shotTime = CTimer::GetTimeInMilliseconds();
				}
				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 if (this == FindPlayerPed() && TheCamera.Cams[0].Using3rdPersonMouseCam()) {
			SetAimFlag(m_fRotationCur);
			((CPlayerPed*)this)->m_fFPSMoveHeading = TheCamera.Find3rdPersonQuickAimPitch();
		} else if (curWeapon->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM)) {
			SetAimFlag(m_fRotationCur);
		}
	}
	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 && (IsPlayer() || !m_carInObjective))
				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 && bIsDucking && curWeapon->IsFlagSet(WEAPONFLAG_CROUCHFIRE)) {
				CAnimBlendAssociation* curMoveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(curWeapon));
				if (curMoveAssoc) {
					if (strcmp(CAnimManager::GetAnimAssociation(curWeapon->m_AnimToPlay, GetCrouchFireAnim(curWeapon))->hierarchy->name, curMoveAssoc->hierarchy->name) != 0) {
						delete curMoveAssoc;
					}
				}
				animAssoc = CAnimManager::BlendAnimation(GetClump(), curWeapon->m_AnimToPlay, GetCrouchFireAnim(curWeapon), 8.0f);
			} else {
				float animDelta = 8.0f;
				if (curWeapon->m_eWeaponFire == WEAPON_FIRE_MELEE)
					animDelta = 1000.0f;

				AnimationId fireAnim;
				if (curWeapon->IsFlagSet(WEAPONFLAG_THROW))
					fireAnim = ANIM_THROWABLE_START_THROW;
				else if (CGame::nastyGame && (curWeapon->IsFlagSet(WEAPONFLAG_GROUND_2ND) || curWeapon->IsFlagSet(WEAPONFLAG_GROUND_3RD))) {
					PedOnGroundState pedOnGround = CheckForPedsOnGroundToAttack(this, nil);
					if (pedOnGround > PED_IN_FRONT_OF_ATTACKER || pedOnGround == NO_PED && bIsStanding && m_pCurSurface && m_pCurSurface->IsVehicle()) {
						fireAnim = GetFireAnimGround(curWeapon, false);
					} else {
						fireAnim = GetFireAnimNotDucking(curWeapon);
					}
				} else {
					fireAnim = GetFireAnimNotDucking(curWeapon);
				}

				CAnimBlendAssociation* curFireAssoc = RpAnimBlendClumpGetAssociation(GetClump(), fireAnim);
				if (curFireAssoc) {
					if (strcmp(CAnimManager::GetAnimAssociation(curWeapon->m_AnimToPlay, fireAnim)->hierarchy->name, curFireAssoc->hierarchy->name) != 0) {
						delete curFireAssoc;
					}
				}
				animAssoc = CAnimManager::BlendAnimation(GetClump(), curWeapon->m_AnimToPlay, fireAnim, 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, true, true);
	SetLookTimer(100);
}

void
CPed::ClearAttack(void)
{
	if (m_nPedState != PED_ATTACK || (bIsDucking && !IsPlayer()) || m_nWaitState == WAITSTATE_PLAYANIM_DUCK)
		return;

	if (FindPlayerPed() == this && TheCamera.Using1stPersonWeaponMode()) {
		SetPointGunAt(nil);
	} else 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)
		return;

	CWeaponInfo *weapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType);
	CAnimBlendAssociation *weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetPrimaryFireAnim(weapon));

	if (!weaponAssoc) {
		if (GetCrouchFireAnim(weapon))
			weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(weapon));
	}
	if (!weaponAssoc) {
		if(GetFinishingAttackAnim(weapon))
			weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetFinishingAttackAnim(weapon));
	}
	if (!weaponAssoc) {
		if(GetSecondFireAnim(weapon))
			weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetSecondFireAnim(weapon));
	}
	if (!weaponAssoc) {
		if(Get3rdFireAnim(weapon))
			weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), Get3rdFireAnim(weapon));
	}
	if (weaponAssoc) {
		weaponAssoc->blendDelta = -8.0f;
		weaponAssoc->flags &= ~ASSOC_RUNNING;
		weaponAssoc->flags |= ASSOC_DELETEFADEDOUT;
		weaponAssoc->SetDeleteCallback(FinishedAttackCB, this);
	} else {
		ClearAttack();
	}
}

void
CPed::FinishedAttackCB(CAnimBlendAssociation *attackAssoc, void *arg)
{
	CAnimBlendAssociation *newAnim, *reloadAnimAssoc = nil;
	CPed *ped = (CPed*)arg;
	CWeaponInfo *currentWeapon = CWeaponInfo::GetWeaponInfo(ped->GetWeapon()->m_eWeaponType);

	if (ped->m_nPedState != PED_ATTACK) {
		if (ped->bIsDucking && ped->IsPedInControl()) {
			if (GetCrouchReloadAnim(currentWeapon)) {
				reloadAnimAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), GetCrouchReloadAnim(currentWeapon));
			}
			if (GetCrouchFireAnim(currentWeapon) && attackAssoc) {
				if (attackAssoc->animId == GetCrouchFireAnim(currentWeapon) && !reloadAnimAssoc) {
					newAnim = CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_WEAPON_CROUCH, 8.0f);
					newAnim->SetCurrentTime(newAnim->hierarchy->totalLength);
					newAnim->flags &= ~ASSOC_RUNNING;
				}
			}
		}
	} else if (attackAssoc && attackAssoc->animId == ANIM_THROWABLE_START_THROW && currentWeapon->m_AnimToPlay == ASSOCGRP_THROW) {
		if ((!ped->IsPlayer() || ((CPlayerPed*)ped)->m_bHaveTargetSelected) && ped->IsPlayer()) {
			attackAssoc->blendDelta = -1000.0f;
			newAnim = CAnimManager::AddAnimation(ped->GetClump(), currentWeapon->m_AnimToPlay, ANIM_THROWABLE_THROWU);
		} else {
			attackAssoc->blendDelta = -1000.0;
			newAnim = CAnimManager::AddAnimation(ped->GetClump(), currentWeapon->m_AnimToPlay, ANIM_THROWABLE_THROW);
		}
		newAnim->SetFinishCallback(FinishedAttackCB, ped);

	} else if (ped->bIsDucking && ped->bCrouchWhenShooting) {
		if (GetCrouchReloadAnim(currentWeapon)) {
			reloadAnimAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), GetCrouchReloadAnim(currentWeapon));
		}
		if (GetCrouchFireAnim(currentWeapon) && attackAssoc) {
			if (attackAssoc->animId == GetCrouchFireAnim(currentWeapon) && !reloadAnimAssoc) {
				newAnim = CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_WEAPON_CROUCH, 8.0f);
				newAnim->SetCurrentTime(newAnim->hierarchy->totalLength);
				newAnim->flags &= ~ASSOC_RUNNING;
			}
		}

		if (!ped->bIsAttacking)
			ped->ClearAttack();

	} else if (GetSecondFireAnim(currentWeapon) && ped->bIsAttacking && currentWeapon->m_AnimToPlay != ASSOCGRP_THROW) {
		AnimationId groundAnim = GetFireAnimGround(currentWeapon);
		CAnimBlendAssociation *groundAnimAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), groundAnim);
		if (!(groundAnimAssoc && (groundAnimAssoc->blendAmount > 0.95f || groundAnimAssoc->blendDelta > 0.0f))) {
			if (attackAssoc && attackAssoc->animId == ANIM_MELEE_ATTACK) {
				newAnim = CAnimManager::BlendAnimation(
					ped->GetClump(), currentWeapon->m_AnimToPlay, GetSecondFireAnim(currentWeapon), 8.0f);
			} else {
				newAnim = CAnimManager::BlendAnimation(
					ped->GetClump(), currentWeapon->m_AnimToPlay, ANIM_MELEE_ATTACK, 8.0f);
			}
			newAnim->SetFinishCallback(FinishedAttackCB, ped);
		}
	} else {
		if (attackAssoc && attackAssoc->animId == ANIM_MELEE_ATTACK && currentWeapon->m_AnimToPlay == ASSOCGRP_UNARMED) {
			attackAssoc->blendDelta = -8.0f;
			attackAssoc->flags |= ASSOC_DELETEFADEDOUT;
			ped->ClearAttack();
			return;
		}
		if (attackAssoc) {
			if (currentWeapon->m_AnimToPlay == ASSOCGRP_THROW) {
				if ((attackAssoc->animId == ANIM_THROWABLE_THROW || attackAssoc->animId == ANIM_THROWABLE_THROWU) && ped->GetWeapon()->m_nAmmoTotal > 0) {
					ped->RemoveWeaponModel(currentWeapon->m_nModelId);
					ped->AddWeaponModel(currentWeapon->m_nModelId);
				}
			}
		}

		if (!ped->bIsAttacking)
			ped->ClearAttack();
	}
}

void
CPed::FinishedReloadCB(CAnimBlendAssociation *reloadAssoc, void *arg)
{
	CPed *ped = (CPed*)arg;
	CWeaponInfo *weapon = CWeaponInfo::GetWeaponInfo(ped->GetWeapon()->m_eWeaponType);
	
	if (ped->DyingOrDead())
		return;

	if (ped->bIsDucking && ped->bCrouchWhenShooting) {
		CAnimBlendAssociation *crouchFireAssoc = nil;
		if (weapon->IsFlagSet(WEAPONFLAG_CROUCHFIRE)) {
			crouchFireAssoc = RpAnimBlendClumpGetAssociation(ped->GetClump(), GetCrouchFireAnim(weapon));
		}
		if (weapon->IsFlagSet(WEAPONFLAG_RELOAD) && reloadAssoc) {
			if (reloadAssoc->animId == GetCrouchReloadAnim(weapon) && !crouchFireAssoc) {
				CAnimBlendAssociation *crouchAssoc = CAnimManager::BlendAnimation(ped->GetClump(), ASSOCGRP_STD, ANIM_WEAPON_CROUCH, 8.0f);
				crouchAssoc->SetCurrentTime(crouchAssoc->hierarchy->totalLength);
				crouchAssoc->flags &= ~ASSOC_RUNNING;
			}
		}
	} else if (weapon->IsFlagSet(WEAPONFLAG_RELOAD_LOOP2START) && ped->bIsAttacking) {
		CAnimBlendAssociation *fireAssoc =
			CAnimManager::BlendAnimation(ped->GetClump(), weapon->m_AnimToPlay, GetPrimaryFireAnim(weapon), 8.0f);
		fireAssoc->SetFinishCallback(FinishedAttackCB, ped);
		fireAssoc->SetRun();
		if (fireAssoc->currentTime == reloadAssoc->hierarchy->totalLength)
			fireAssoc->SetCurrentTime(Max(weapon->m_fAnimLoopStart - 0.04f, 0.0f));
		else if (fireAssoc->currentTime < weapon->m_fAnimLoopStart)
			fireAssoc->SetCurrentTime(Max(weapon->m_fAnimLoopStart - 0.04f, 0.0f));
	}
}

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.MagnitudeSqr() < SQR(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;

				PedState nearPedState = nearPed->m_nPedState;

				if (nearPedState == PED_FALL || nearPedState == PED_GETUP || nearPedState == PED_DIE || nearPedState == PED_DEAD || nearPedState == 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 weaponAnimTime;
	float animLoopEnd;
	CWeaponInfo *ourWeapon;
	bool attackShouldContinue;
	CAnimBlendAssociation *reloadAnimAssoc;
	CAnimBlendAssociation *throwAssoc;
	float delayBetweenAnimAndFire;
	float animLoopStart;
	CVector firePos;

	ourWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType);
	weaponAnimAssoc = nil;
	attackShouldContinue = !!bIsAttacking;
	reloadAnimAssoc = nil;
	throwAssoc = nil;
	animLoopStart = ourWeapon->m_fAnimLoopStart;
	animLoopEnd = ourWeapon->m_fAnimLoopEnd;
	delayBetweenAnimAndFire = ourWeapon->m_fAnimFrameFire;
	weaponAnim = ourWeapon->m_AnimToPlay;

	if (bIsDucking) {
		if(GetCrouchFireAnim(ourWeapon) && bCrouchWhenShooting) {
			weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchFireAnim(ourWeapon));
			if (weaponAnimAssoc) {
				animLoopStart = ourWeapon->m_fAnim2LoopStart;
				animLoopEnd = ourWeapon->m_fAnim2LoopEnd;
				delayBetweenAnimAndFire = ourWeapon->m_fAnim2FrameFire;
			}
		}
	} else if (m_nPedType == PEDTYPE_COP && Get3rdFireAnim(ourWeapon)){
		weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), Get3rdFireAnim(ourWeapon));
		if (weaponAnimAssoc) {
			animLoopStart = 11.f/30.f;
			animLoopEnd = 19.f/30.f;
			delayBetweenAnimAndFire = 14.f/30.f;
		}
	} else {
		weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetPrimaryFireAnim(ourWeapon));
	}

	if (GetReloadAnim(ourWeapon)) {
		reloadAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetReloadAnim(ourWeapon));
	}

	if (GetCrouchReloadAnim(ourWeapon) && !reloadAnimAssoc)  {
		reloadAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetCrouchReloadAnim(ourWeapon));
	}

	if ( reloadAnimAssoc && reloadAnimAssoc->IsRunning() ) {
		if (!IsPlayer() || ((CPlayerPed*)this)->m_bHaveTargetSelected)
			ClearAttack();
		return;
	}

	if ( reloadAnimAssoc ) {
		reloadAnimAssoc->flags |= ASSOC_DELETEFADEDOUT;
		if ( reloadAnimAssoc->blendDelta >= 0.0f )
			reloadAnimAssoc->blendDelta = -8.0f;
	}

	if (GetThrowAnim(ourWeapon))  {
		throwAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetThrowAnim(ourWeapon));
	}

	if ( CTimer::GetTimeInMilliseconds() < m_shootTimer )
		attackShouldContinue = true;

	bool meleeAttackStarted = false;
	if ( !weaponAnimAssoc ) {
		if (GetMeleeStartAnim(ourWeapon)) {
			weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetMeleeStartAnim(ourWeapon));
			if ( weaponAnimAssoc ) {
				if ( IsPlayer() )
					meleeAttackStarted = true;

				switch ( ourWeapon->m_AnimToPlay ) {
					case ASSOCGRP_UNARMED:
					case ASSOCGRP_SCREWDRIVER:
					case ASSOCGRP_KNIFE:
					case ASSOCGRP_BASEBALLBAT:
					case ASSOCGRP_GOLFCLUB:
					case ASSOCGRP_CHAINSAW:
						delayBetweenAnimAndFire = 0.2f;
						animLoopStart = 0.1f;
						break;
					default:
						break;
				}
				animLoopEnd = 99.9f;
			}
		}
	}
	if (!weaponAnimAssoc) {
		if (GetSecondFireAnim(ourWeapon)) {
			weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetSecondFireAnim(ourWeapon));
			if (weaponAnimAssoc) {
				animLoopStart = ourWeapon->m_fAnim2LoopStart;
				animLoopEnd = ourWeapon->m_fAnim2LoopEnd;
				delayBetweenAnimAndFire = ourWeapon->m_fAnim2FrameFire;
			}
		}
	}
	if (!weaponAnimAssoc) {
		weaponAnimAssoc = RpAnimBlendClumpGetAssociation(GetClump(), GetFireAnimGround(ourWeapon));
		if (weaponAnimAssoc) {
			animLoopStart = ourWeapon->m_fAnim2LoopStart;
			animLoopEnd = ourWeapon->m_fAnim2LoopEnd;
			delayBetweenAnimAndFire = ourWeapon->m_fAnim2FrameFire;
		}
	}

	if (!weaponAnimAssoc) {
		if (!throwAssoc) {
			if (attackShouldContinue) {
				if (ourWeapon->m_eWeaponFire != WEAPON_FIRE_PROJECTILE || !IsPlayer() || ((CPlayerPed*)this)->m_bHaveTargetSelected) {
					if (bCrouchWhenShooting && bIsDucking && GetCrouchFireAnim(ourWeapon)) {
						weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, GetCrouchFireAnim(ourWeapon), 8.0f);

					} else if(GetSecondFireAnim(ourWeapon) && CGeneral::GetRandomNumber() & 1){
						weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, GetSecondFireAnim(ourWeapon), 8.0f);

					} else if(!CGame::nastyGame || ourWeapon->m_eWeaponFire != WEAPON_FIRE_MELEE ||
					          !GetFireAnimGround(ourWeapon, false) || 
						 CheckForPedsOnGroundToAttack(this, nil) < PED_ON_THE_FLOOR) {

						weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, GetFireAnimNotDucking(ourWeapon), 8.0f);

					} else {
						weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, GetFireAnimGround(ourWeapon, false), 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;
	}

	if (meleeAttackStarted && IsPlayer()) {
		if (((CPlayerPed*)this)->m_bHaveTargetSelected || ((CPlayerPed*)this)->m_fMoveSpeed < 0.5f) {
			weaponAnimAssoc->SetRun();
		} else {
			if (weaponAnimAssoc->currentTime > animLoopStart && weaponAnimAssoc->currentTime - weaponAnimAssoc->timeStep <= animLoopStart)
				weaponAnimAssoc->flags &= ~ASSOC_RUNNING;
		}
	}

	float animStart = animLoopStart * 0.4f;
	weaponAnimTime = weaponAnimAssoc->currentTime;
	if (weaponAnimTime > animStart && weaponAnimTime - weaponAnimAssoc->timeStep <= animStart) {
		if (!bIsDucking && !GetFireAnimNotDucking(ourWeapon) && ourWeapon->IsFlagSet(WEAPONFLAG_CANAIM_WITHARM))
			m_pedIK.m_flags |= CPedIK::AIMS_WITH_ARM;
		else
			m_pedIK.m_flags &= ~CPedIK::AIMS_WITH_ARM;
	}

	if (GetWeapon()->m_eWeaponType != WEAPONTYPE_CHAINSAW
		|| !meleeAttackStarted && delayBetweenAnimAndFire - 0.5f >= weaponAnimAssoc->currentTime
		|| weaponAnimAssoc->currentTime - weaponAnimAssoc->timeStep > delayBetweenAnimAndFire) {

		if (GetWeapon()->m_eWeaponType == WEAPONTYPE_CHAINSAW) {
			DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_CHAINSAW_ATTACK, 0.0f);
		} else if (weaponAnimTime <= delayBetweenAnimAndFire || weaponAnimTime - weaponAnimAssoc->timeStep > delayBetweenAnimAndFire || !weaponAnimAssoc->IsRunning()) {
			if (weaponAnimAssoc->speed < 1.0f)
				weaponAnimAssoc->speed = 1.0f;

		} else {
			firePos = ourWeapon->m_vecFireOffset;

			if(ourWeapon->m_AnimToPlay != ASSOCGRP_BASEBALLBAT && ourWeapon->m_AnimToPlay != ASSOCGRP_GOLFCLUB) {
				if (ourWeapon->m_eWeaponFire != WEAPON_FIRE_MELEE) {
					TransformToNode(firePos, (weaponAnimAssoc->animId == ANIM_MELEE_ATTACK_2ND && ourWeapon->m_AnimToPlay == ASSOCGRP_UNARMED) ? PED_FOOTR : PED_HANDR);
				} else {
					firePos = GetMatrix() * firePos;
				}
			} else {
				if (weaponAnimAssoc->animId == ANIM_MELEE_ATTACK_2ND)
					firePos.z = 0.7f * ourWeapon->m_fRadius - 1.0f;

				firePos = GetMatrix() * firePos;
			}
			
			GetWeapon()->Fire(this, &firePos);

			if (GetWeapon()->m_eWeaponType == WEAPONTYPE_MOLOTOV || GetWeapon()->m_eWeaponType == WEAPONTYPE_GRENADE || GetWeapon()->m_eWeaponType == WEAPONTYPE_DETONATOR_GRENADE ||
				GetWeapon()->m_eWeaponType == WEAPONTYPE_TEARGAS) {
				RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_nModelId);
			}
			if (GetWeapon()->m_nAmmoTotal == 0 && ourWeapon->m_eWeaponFire != WEAPON_FIRE_MELEE && FindPlayerPed() != this) {
				SelectGunIfArmed();
			}

			if (GetWeapon()->m_eWeaponState == WEAPONSTATE_MELEE_MADECONTACT) {
				int damagerType = ENTITY_TYPE_NOTHING;
				if (m_pDamageEntity && (m_fDamageImpulse == 0.0f || !m_pDamageEntity->IsBuilding())) {
					damagerType = m_pDamageEntity->GetType();
				}
				switch (ourWeapon->m_AnimToPlay) {
					case ASSOCGRP_UNARMED:
						if (weaponAnimAssoc->animId == ANIM_MELEE_ATTACK || weaponAnimAssoc->animId == ANIM_MELEE_ATTACK_START) 
							DMAudio.PlayOneShot(m_audioEntityId, SOUND_FIGHT_46, (damagerType | (GetWeapon()->m_eWeaponType << 8)));
						break;
					case ASSOCGRP_KNIFE:
					case ASSOCGRP_BASEBALLBAT:
					case ASSOCGRP_GOLFCLUB:
					case ASSOCGRP_CHAINSAW:
						DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_BAT_ATTACK, (damagerType | (GetWeapon()->m_eWeaponType << 8)));
						break;
					default:
						break;
				}

				if (bIsAttacking || CTimer::GetTimeInMilliseconds() < m_shootTimer) {
					weaponAnimAssoc->callbackType = 0;
				}
			}

			attackShouldContinue = false;
		}
	} else {
		CVector firePos = ourWeapon->m_vecFireOffset;

		if (weaponAnimAssoc->animId == ANIM_MELEE_ATTACK_2ND)
			firePos.z = 0.7f * ourWeapon->m_fRadius - 1.0f;

		firePos = GetMatrix() * firePos;
		GetWeapon()->Fire(this, &firePos);
		if (GetWeapon()->m_eWeaponState == WEAPONSTATE_MELEE_MADECONTACT) {
			int damagerType = ENTITY_TYPE_PED;
			if (m_pDamageEntity)
				damagerType = m_pDamageEntity->GetType();

			DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_CHAINSAW_MADECONTACT, (float)damagerType);
			if (IsPlayer()) {
				CPad::GetPad(0)->StartShake(240, 180);
			}
		} else {
			DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_CHAINSAW_IDLE, 0.0f);
			if (IsPlayer()) {
				CPad::GetPad(0)->StartShake(240, 90);
			}
		}
		attackShouldContinue = false;
	}

	if (ourWeapon->m_eWeaponFire == WEAPON_FIRE_INSTANT_HIT && ourWeapon->m_AnimToPlay == ASSOCGRP_SHOTGUN) {
		weaponAnimTime = weaponAnimAssoc->currentTime;

		if (weaponAnimTime > 1.0f && weaponAnimTime - weaponAnimAssoc->timeStep <= 1.0f && weaponAnimAssoc->IsRunning()) {
			firePos = ourWeapon->m_vecFireOffset;
			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);
		}
	}

	if (IsPlayer()) {
		if (GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT || GetWeapon()->m_eWeaponType == WEAPONTYPE_GOLFCLUB || GetWeapon()->m_eWeaponType == WEAPONTYPE_KATANA) {
			float loopEndWithDelay = animLoopEnd;
			if (loopEndWithDelay >= 98.0f)
				loopEndWithDelay = (14.0f / 30.0f) + delayBetweenAnimAndFire;
			if (weaponAnimAssoc->flags & ASSOC_RUNNING) {
				if (weaponAnimAssoc->currentTime >= animLoopStart && weaponAnimAssoc->currentTime <= loopEndWithDelay)
					CSpecialFX::AddWeaponStreak(GetWeapon()->m_eWeaponType);
			}
		}
	}

	// Anim breakout on running
	if (IsPlayer()) {
		if (CPad::GetPad(0)->GetSprint()) {
			if (!attackShouldContinue && weaponAnimAssoc->currentTime > ourWeapon->m_fAnimBreakout) {
				weaponAnimAssoc->blendDelta = -4.0f;
				FinishedAttackCB(nil, this);
				return;
			}
		}
	}

	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 (GetWeapon()->m_eWeaponState == WEAPONSTATE_RELOADING) {
			if (GetReloadAnim(ourWeapon) && !reloadAnimAssoc) {
				if (!CWorld::Players[CWorld::PlayerInFocus].m_bFastReload) {
					CAnimBlendAssociation *newReloadAssoc = CAnimManager::BlendAnimation(
						GetClump(), ourWeapon->m_AnimToPlay,
						bIsDucking && GetCrouchReloadAnim(ourWeapon) ? GetCrouchReloadAnim(ourWeapon) : GetReloadAnim(ourWeapon),
						8.0f);
					newReloadAssoc->SetFinishCallback(FinishedReloadCB, this);
				}
				ClearLookFlag();
				ClearAimFlag();
				bIsAttacking = false;
				bIsPointingGunAt = false;
				m_shootTimer = CTimer::GetTimeInMilliseconds();
				DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_AK47_BULLET_ECHO, GetWeapon()->m_eWeaponType);
				return;
			}
		}
		if (weaponAnimTime - 2.0f * weaponAnimAssoc->timeStep <= animLoopEnd
			&& (bIsAttacking || CTimer::GetTimeInMilliseconds() < m_shootTimer)
			&& (GetWeapon()->m_eWeaponState != WEAPONSTATE_RELOADING
				|| GetWeapon()->m_eWeaponType == WEAPONTYPE_MINIGUN)) {

			PedOnGroundState pedOnGroundState;
			if (ourWeapon->m_eWeaponFire == WEAPON_FIRE_MELEE &&
				(CGame::nastyGame && ((pedOnGroundState = CheckForPedsOnGroundToAttack(this, nil)) > PED_IN_FRONT_OF_ATTACKER)
				|| GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT && pedOnGroundState == NO_PED && bIsStanding && m_pCurSurface && m_pCurSurface->IsVehicle())) {

				AnimationId fireAnim = GetFireAnimGround(ourWeapon, false);
				if (weaponAnimAssoc->animId == fireAnim)
					weaponAnimAssoc->SetCurrentTime(0.1f);
				else {
					if (GetFireAnimGround(ourWeapon, false)) {
						weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, fireAnim, 8.0f);
					} else {
						weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_KICK_FLOOR, 8.0f);
					}
				}
				weaponAnimAssoc->SetFinishCallback(FinishedAttackCB, this);
			} else if (GetSecondFireAnim(ourWeapon)) {
				if (weaponAnimAssoc->animId == GetSecondFireAnim(ourWeapon)) {
					weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, ANIM_WEAPON_FIRE, 8.0f);
				} else {
					weaponAnimAssoc = CAnimManager::BlendAnimation(GetClump(), ourWeapon->m_AnimToPlay, GetSecondFireAnim(ourWeapon), 8.0f);
				}
				weaponAnimAssoc->SetFinishCallback(FinishedAttackCB, this);
			} else {
				weaponAnimAssoc->SetCurrentTime(animLoopStart);
				weaponAnimAssoc->SetRun();
			}
		} else if (IsPlayer() && m_pPointGunAt && bIsAimingGun && GetWeapon()->m_eWeaponState != WEAPONSTATE_RELOADING) {
			weaponAnimAssoc->SetCurrentTime(animLoopEnd);
			weaponAnimAssoc->flags &= ~ASSOC_RUNNING;
			SetPointGunAt(m_pPointGunAt);
		} else {
			ClearAimFlag();

			// Echoes of bullets, at the end of the attack. (Bug: doesn't play while reloading)
			if (weaponAnimAssoc->currentTime - weaponAnimAssoc->timeStep < animLoopEnd) 
				DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_AK47_BULLET_ECHO, GetWeapon()->m_eWeaponType);

			// 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() && buttonPressure != 0))
		return;

	if (m_nPedState == PED_FIGHT) {
		m_fightButtonPressure = buttonPressure;
		return;
	}

	if (m_nPedState != PED_AIM_GUN)
		SetStoredState();

	if (m_nWaitState != WAITSTATE_FALSE) {
		ClearWaitState();
		RestoreHeadingRate();
	}

	CAnimBlendAssociation* animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_RUN_STOP);
	if (!animAssoc)
		animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_RUN_STOP_R);

	if (animAssoc) {
		RestoreHeadingRate();
	}
	SetMoveState(PEDMOVE_NONE);
	m_nStoredMoveState = PEDMOVE_NONE;
	bool fightWithWeapon = false;
	CAnimBlendAssociation *fightIdleAssoc;

	CWeaponInfo* weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType);
	if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) {
		if (GetFightIdleWithMeleeAnim(weaponInfo)) {
			fightIdleAssoc = CAnimManager::BlendAnimation(GetClump(), weaponInfo->m_AnimToPlay, GetFightIdleWithMeleeAnim(weaponInfo), 1000.0f);
			fightWithWeapon = true;
		} else {
			fightIdleAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_FIGHT_IDLE, 1000.0f);
		}
	} else {
		fightIdleAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_FIGHT_IDLE, 1000.0f);
	}
	m_lastFightMove = FIGHTMOVE_IDLE;
	m_curFightMove = IsPlayer() ? ChooseAttackPlayer(buttonPressure, fightWithWeapon) : ChooseAttackAI(buttonPressure, fightWithWeapon);

	SetPedState(PED_FIGHT);
	m_fightButtonPressure = 0;

	if (m_curFightMove > FIGHTMOVE_NULL && m_curFightMove != FIGHTMOVE_IDLE) {
		animAssoc = CAnimManager::BlendAnimation(GetClump(), m_curFightMove < FIGHTMOVE_MELEE1 ? ASSOCGRP_STD : weaponInfo->m_AnimToPlay,
			tFightMoves[m_curFightMove].animId, 8.0f);

		if (weaponInfo->m_AnimToPlay == ASSOCGRP_KNIFE && m_curFightMove >= FIGHTMOVE_MELEE1) {
			switch (GetWeapon()->m_eWeaponType) {
				case WEAPONTYPE_SCREWDRIVER:
				case WEAPONTYPE_KNIFE:
					animAssoc->speed = 1.05f;
					break;
				case WEAPONTYPE_GOLFCLUB:
				case WEAPONTYPE_NIGHTSTICK:
				case WEAPONTYPE_BASEBALLBAT:
				case WEAPONTYPE_HAMMER:
				case WEAPONTYPE_KATANA:
					animAssoc->speed = 0.8f;
					break;
				case WEAPONTYPE_CLEAVER:
				case WEAPONTYPE_MACHETE:
					animAssoc->speed = 0.9f;
					break;
			}
		} else {
			if (m_curFightMove == FIGHTMOVE_BACKKICK)
				animAssoc->speed = 1.15f;
			else
				animAssoc->speed = 0.8f;
		}
		if (IsPlayer())
			animAssoc->SetCurrentTime(0.08f);

		animAssoc->SetFinishCallback(FinishFightMoveCB, this);
	} else {
		m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 2000;
	}

	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_FLOOR_HIT_F, 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_FLOOR_HIT_F, 8.0f) :
				CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_FLOOR_HIT, 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)) {

			if (hitLevel != HITLEVEL_HIGH && hitLevel != HITLEVEL_LOW || (IsPlayer() || CGeneral::GetRandomNumber() & 1) && CGeneral::GetRandomNumber() & 7) {
				if (IsPlayer() || CGeneral::GetRandomNumber() & 1) {
					AnimationId shotAnim;
					switch (direction) {
						case 1:
							shotAnim = ANIM_SHOT_LEFT_PARTIAL;
							break;
						case 2:
							shotAnim = ANIM_SHOT_BACK_PARTIAL;
							break;
						case 3:
							shotAnim = ANIM_SHOT_RIGHT_PARTIAL;
							break;
						default:
							shotAnim = ANIM_SHOT_FRONT_PARTIAL;
							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 {
				bool fall = true;
				AnimationId hitAnim;
				switch (direction) {
					case 1:
						hitAnim = ANIM_KO_SPIN_R;
						break;
					case 2:
						if (CGeneral::GetRandomNumber() & 1) {
							fall = false;
							hitAnim = ANIM_HIT_BACK;
						} else	{
							hitAnim = ANIM_KO_SKID_BACK;
						}
						break;
					case 3:
						hitAnim = ANIM_KO_SPIN_L;
						break;
					default:
						if (hitLevel == HITLEVEL_LOW) {
							hitAnim = ANIM_KO_SHOT_STOM;
						} else if (CGeneral::GetRandomNumber() & 1) {
							fall = false;
							hitAnim = ANIM_HIT_WALK;
						} else if (CGeneral::GetRandomNumber() & 1) {
							fall = false;
							hitAnim = ANIM_HIT_HEAD;
						} else {
							hitAnim = ANIM_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;
				}
			}
			Say(SOUND_PED_DEFEND);
		} else {
			Say(SOUND_PED_DEFEND);
			switch (hitLevel) {
				case HITLEVEL_GROUND:
					m_curFightMove = FIGHTMOVE_HITONFLOOR;
					break;
				case HITLEVEL_LOW:
					if (direction == 2 && (!IsPlayer() || ((CGeneral::GetRandomNumber() & 1) && m_fHealth < 30.0f))) {
						SetFall(1000, ANIM_KO_SKID_BACK, false);
						Say(SOUND_PED_DEFEND);
						return;
					} else if (direction != 2 && !IsPlayer() && (CGeneral::GetRandomNumber() & 1) && m_fHealth < 30.0f) {
						SetFall(1000, ANIM_KO_SHOT_STOM, false);
						Say(SOUND_PED_DEFEND);
						return;
					}	
					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.2f;

				m_takeAStepAfterAttack = 0;
				m_fightButtonPressure = 0;

			} else if (IsPlayer() && GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED && GetWeapon()->m_eWeaponType != WEAPONTYPE_BRASSKNUCKLE &&
					!CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->IsFlagSet(WEAPONFLAG_FIGHTMODE)) {
				CAnimBlendAssociation *moveAssoc = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, tFightMoves[m_curFightMove].animId, 4.0f);
				moveAssoc->SetCurrentTime(0.0f);
				moveAssoc->speed = 1.2f;

			} else {
				if (m_nPedState != PED_AIM_GUN && m_nPedState != PED_ATTACK)
					SetStoredState();

				if (m_nWaitState != WAITSTATE_FALSE) {
					ClearWaitState();
					RestoreHeadingRate();
				}
				SetPedState(PED_FIGHT);
				m_fightButtonPressure = 0;
				m_lastFightMove = FIGHTMOVE_IDLE;
				RpAnimBlendClumpRemoveAssociations(GetClump(), ASSOC_REPEAT);
				CAnimBlendAssociation *walkStartAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WALK_START);
				if (walkStartAssoc) {
					walkStartAssoc->flags |= ASSOC_DELETEFADEDOUT;
					walkStartAssoc->blendDelta = -1000.0f;
				}
				CAnimBlendAssociation *walkStopAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_RUN_STOP);
				if (!walkStopAssoc)
					walkStopAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_RUN_STOP_R);
				if (walkStopAssoc) {
					walkStopAssoc->flags |= ASSOC_DELETEFADEDOUT;
					walkStopAssoc->blendDelta = -1000.0f;
					RestoreHeadingRate();
				}
				SetMoveState(PEDMOVE_NONE);
				m_nStoredMoveState = PEDMOVE_NONE;
				CAnimBlendAssociation *fightIdleAssoc;

				if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) {
					CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType);
					if (GetFightIdleWithMeleeAnim(weaponInfo)) {
						fightIdleAssoc = CAnimManager::AddAnimation(GetClump(), weaponInfo->m_AnimToPlay, GetFightIdleWithMeleeAnim(weaponInfo));
					} else {
						fightIdleAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_FIGHT_IDLE);
					}
				} else {
					fightIdleAssoc = CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_FIGHT_IDLE);
				}
				fightIdleAssoc->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;
			}

			if (m_pedInObjective && m_pedInObjective->IsPlayer() && !IsPlayer())
				((CPlayerPed*)m_pedInObjective)->RemovePedFromMeleeList(this);
		}
	}
}

void
CPed::Fight(void)
{
	CAnimBlendAssociation *currentAssoc, *animAssoc;
	bool fightWithWeapon = false;

	eWeaponType weapon = GetWeapon()->m_eWeaponType;
	CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(weapon);

	if (weaponInfo->IsFlagSet(WEAPONFLAG_FIGHTMODE) && weapon != WEAPONTYPE_UNARMED) {
		fightWithWeapon = true;
		tFightMoves[FIGHTMOVE_MELEE1].startFireTime = weaponInfo->m_fAnimFrameFire;
		tFightMoves[FIGHTMOVE_MELEE1].endFireTime = weaponInfo->m_fAnimLoopEnd;
		tFightMoves[FIGHTMOVE_MELEE2].startFireTime = weaponInfo->m_fAnim2FrameFire;
		tFightMoves[FIGHTMOVE_MELEE2].endFireTime = weaponInfo->m_fAnim2LoopEnd;
		tFightMoves[FIGHTMOVE_MELEE3].startFireTime = weaponInfo->m_fAnim2FrameFire;
		tFightMoves[FIGHTMOVE_MELEE3].endFireTime = weaponInfo->m_fAnim2LoopEnd;
	}

	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 (m_curFightMove == FIGHTMOVE_SHUFFLE_F && !currentAssoc)
		currentAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_FIGHT_SH_BACK);

	if (IsPlayer() && currentAssoc && weapon == WEAPONTYPE_KATANA) {
		if (m_curFightMove == FIGHTMOVE_MELEE1 || m_curFightMove == FIGHTMOVE_MELEE2) {
			static float streakDelay = 0.2f;

			if (tFightMoves[m_curFightMove].startFireTime - streakDelay < currentAssoc->currentTime &&
				streakDelay + tFightMoves[m_curFightMove].endFireTime > currentAssoc->currentTime) {
				CSpecialFX::AddWeaponStreak(GetWeapon()->m_eWeaponType);
			}
		}
	}

	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) {

			if (animTime > curMove.startFireTime && animTime - currentAssoc->timeStep < curMove.startFireTime &&
				(IsPlayer() || weapon != WEAPONTYPE_UNARMED)) {

				DMAudio.PlayOneShot(m_audioEntityId, SOUND_MELEE_ATTACK_START, weapon << 8);
			}

			CVector touchingNodePos(0.0f, 0.0f, 0.0f);

			switch (m_curFightMove) {
				case FIGHTMOVE_KNEE:
					TransformToNode(touchingNodePos, PED_LOWERLEGR);
					break;
				case FIGHTMOVE_PUNCHHOOK:
				case FIGHTMOVE_PUNCHJAB:
					TransformToNode(touchingNodePos, PED_HANDL);
					break;
				case FIGHTMOVE_LONGKICK:
				case FIGHTMOVE_ROUNDHOUSE:
				case FIGHTMOVE_FWDLEFT:
				case FIGHTMOVE_BACKRIGHT:
				case FIGHTMOVE_GROUNDKICK:
					TransformToNode(touchingNodePos, PED_FOOTR);
					break;
				case FIGHTMOVE_FWDRIGHT:
					TransformToNode(touchingNodePos, PED_HEAD);
					break;
				case FIGHTMOVE_BACKKICK:
				case FIGHTMOVE_BACKFLIP:
					TransformToNode(touchingNodePos, PED_FOOTL);
					break;
				case FIGHTMOVE_BACKLEFT:
					TransformToNode(touchingNodePos, PED_UPPERARML);
					break;
				default:
					TransformToNode(touchingNodePos, PED_HANDR);
					break;
			}

			FightStrike(touchingNodePos, fightWithWeapon);
			m_fightButtonPressure = 0;
			return;
		}

		if (curMove.hitLevel != HITLEVEL_NULL) {
			if (animTime > curMove.endFireTime && weaponInfo->m_AnimToPlay != ASSOCGRP_KNIFE) {
				if (IsPlayer())
					currentAssoc->speed = 1.0f;
				else
					currentAssoc->speed = 0.8f;
			}

			if (IsPlayer() && !nPlayerInComboMove && !fightWithWeapon) {
				if (curMove.comboFollowOnTime > 0.0f && m_fightButtonPressure != 0 && animTime > curMove.comboFollowOnTime) {

					m_lastFightMove = m_curFightMove;
					// 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);
					animAssoc->speed = 0.8f;
					m_fightButtonPressure = 0;
					nPlayerInComboMove = 1;
				}
			}
		}

	} else if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED && GetWeapon()->m_eWeaponType != WEAPONTYPE_BRASSKNUCKLE && !fightWithWeapon) {
		EndFight(ENDFIGHT_FAST);

	} else if (m_fightButtonPressure != 0) {
		if (!IsPlayer())
			Say(SOUND_PED_ATTACK);

		if (m_curFightMove != FIGHTMOVE_IDLE)
			m_lastFightMove = m_curFightMove;

		m_curFightMove = IsPlayer() ? ChooseAttackPlayer(m_fightButtonPressure, fightWithWeapon) : ChooseAttackAI(m_fightButtonPressure, fightWithWeapon);

		if (m_curFightMove != FIGHTMOVE_IDLE) {

			animAssoc = CAnimManager::BlendAnimation(GetClump(), m_curFightMove < FIGHTMOVE_MELEE1 ? ASSOCGRP_STD : weaponInfo->m_AnimToPlay,
				tFightMoves[m_curFightMove].animId, 8.0f);

			if (weaponInfo->m_AnimToPlay != ASSOCGRP_KNIFE || m_curFightMove < FIGHTMOVE_MELEE1) {
				if (m_curFightMove == FIGHTMOVE_BACKKICK)
					animAssoc->speed = 1.15f;
				else
					animAssoc->speed = 0.8f;
			} else {
				switch (GetWeapon()->m_eWeaponType) {
					case WEAPONTYPE_SCREWDRIVER:
					case WEAPONTYPE_KNIFE:
						animAssoc->speed = 1.05f;
						break;
					case WEAPONTYPE_GOLFCLUB:
					case WEAPONTYPE_NIGHTSTICK:
					case WEAPONTYPE_BASEBALLBAT:
					case WEAPONTYPE_HAMMER:
					case WEAPONTYPE_KATANA:
						animAssoc->speed = 0.8f;
						break;
					case WEAPONTYPE_CLEAVER:
					case WEAPONTYPE_MACHETE:
						animAssoc->speed = 0.9f;
						break;
				}
			}

			if (m_fightState == FIGHTSTATE_MOVE_FINISHED && animAssoc->currentTime != 0.0f) {
				animAssoc->SetRun();
				if (!IsPlayer())
					animAssoc->SetCurrentTime(0.0f);
			}
			if (IsPlayer())
				animAssoc->SetCurrentTime(0.08f);

			animAssoc->SetFinishCallback(FinishFightMoveCB, this);
			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_lastFightMove = m_curFightMove;
		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_lastFightMove = m_curFightMove;
		m_curFightMove = FIGHTMOVE_IDLE;
		if (IsPlayer())
			m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 500;
		else
			m_nWaitTimer = CTimer::GetTimeInMilliseconds() + 2000;
	}
}

int32
CPed::ChooseAttackAI(uint8 buttonPressure, bool fightWithWeapon)
{
	eWeaponType weapon = GetWeapon()->m_eWeaponType;
	CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(weapon);
	if (!fightWithWeapon && weapon != WEAPONTYPE_UNARMED && weapon != WEAPONTYPE_BRASSKNUCKLE) {
		return FIGHTMOVE_PUNCH;
	}

	if (!m_pedInObjective)
		return FIGHTMOVE_IDLE;
	if (buttonPressure == 0)
		return FIGHTMOVE_IDLE;

	uint16 pedFeatures = m_pedStats->m_flags;
	bool punchOnly = !!(pedFeatures & STAT_PUNCH_ONLY);
	bool canRoundhouse = !!(pedFeatures & STAT_CAN_ROUNDHOUSE);
	bool canKneeHead = !!(pedFeatures & STAT_CAN_KNEE_HEAD);
	bool canKick = !!(pedFeatures & STAT_CAN_KICK);
	bool hasShoppingBags = !!(pedFeatures & STAT_SHOPPING_BAGS);

	CVector distVec(m_pedInObjective->GetPosition() - GetPosition());
	float dist = distVec.Magnitude();
	m_fRotationDest = CGeneral::LimitRadianAngle(distVec.Heading());
	m_fRotationCur = m_fRotationDest;

	if (fightWithWeapon) {
		if (m_pedInObjective->OnGroundOrGettingUp()) {
			if (CGame::nastyGame && dist < 1.2f && !m_pedInObjective->IsPlayer()
				&& (m_pedInObjective->m_nPedState == PED_DEAD || !m_pedInObjective->IsPedHeadAbovePos(-0.3f))) {
				if (weaponInfo->IsFlagSet(WEAPONFLAG_GROUND_2ND))
					return FIGHTMOVE_MELEE2;
				if (weaponInfo->IsFlagSet(WEAPONFLAG_GROUND_3RD))
					return FIGHTMOVE_MELEE3;

				return FIGHTMOVE_GROUNDKICK;
			} else {
				return FIGHTMOVE_IDLE;
			}
		}
		if (dist < 2.f) {
			if (m_curFightMove == FIGHTMOVE_MELEE1) {
				if (GetSecondFireAnim(weaponInfo))
					return FIGHTMOVE_MELEE2;
			}
			if (m_curFightMove == FIGHTMOVE_MELEE2) {
				if (GetFinishingAttackAnim(weaponInfo))
					return FIGHTMOVE_MELEE3;
			}
			return FIGHTMOVE_MELEE1;
		}
		return FIGHTMOVE_SHUFFLE_F;
	}
	if (!hasShoppingBags) {
		if (punchOnly) {
			if (dist < 1.4f)
				return FIGHTMOVE_PUNCH;
		} else {
			if (m_pedInObjective->OnGroundOrGettingUp()) {
				if (CGame::nastyGame && dist < 1.2f && !m_pedInObjective->IsPlayer()
					&& (m_pedInObjective->m_nPedState == PED_DEAD || !m_pedInObjective->IsPedHeadAbovePos(-0.3f))) {

					return FIGHTMOVE_GROUNDKICK;
				} else {
					return FIGHTMOVE_IDLE;
				}
			}
			if (dist < 0.95f && canKneeHead)
				return FIGHTMOVE_KNEE;
			if (dist < 1.4f)
				return FIGHTMOVE_PUNCH;
			if (dist < 2.f && canKick) {
				int nextMove = FIGHTMOVE_LONGKICK;
				if (canRoundhouse && CGeneral::GetRandomNumber() & 1)
					nextMove = FIGHTMOVE_ROUNDHOUSE;
				return nextMove;
			}
		}
		return FIGHTMOVE_SHUFFLE_F;
	}
	if (dist < 2.f)
		return FIGHTMOVE_ROUNDHOUSE;
	else
		return FIGHTMOVE_SHUFFLE_F;
}

int32
CPed::ChooseAttackPlayer(uint8 buttonPressure, bool fightWithWeapon)
{
	CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType);
	const float maxAttackDist = 2.7f;
	float weaponAttackDist = 2.0f;
	CPed *victimPed = nil;
	CPed *walkUpTo = nil;
	CPed *groundAttackDeadPed = nil;
	CPed *groundAttackAlivePed = nil;
	if (fightWithWeapon)
		weaponAttackDist = weaponInfo->m_fRange;

	bool willWalkUp = false;
	PedFightMoves choosenMove = FIGHTMOVE_IDLE;
	int numPedsWeCanReach = 0;
	if (m_takeAStepAfterAttack)
		willWalkUp = true;

	float groundAttackDeadAngle, groundAttackAliveAngle, walkAngle, victimAngle, distToVictim;

	for (int i = 0; i < m_numNearPeds; ++i) {
		CPed *nearPed = m_nearPeds[i];
		CVector distVec(nearPed->GetPosition() - GetPosition());
		float dist = distVec.Magnitude();
		if (dist < maxAttackDist) {
			float nearPedAngle = CGeneral::LimitRadianAngle(distVec.Heading());
			m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur);
			float neededTurn = Abs(nearPedAngle - m_fRotationCur);
			if (neededTurn > PI)
				neededTurn = TWOPI - neededTurn;

			if (!nearPed->OnGroundOrGettingUp() && nearPed->m_nWaitState != WAITSTATE_SUN_BATHE_IDLE) {
				if (!willWalkUp || neededTurn <= DEGTORAD(45.0f)) {

					if (neededTurn <= DEGTORAD(30.0f) || nearPed->m_pedInObjective == this
						&& (nearPed->m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT || nearPed->m_objective == OBJECTIVE_KILL_CHAR_ANY_MEANS)) {

						if (dist < weaponAttackDist) {
							if (!victimPed
								|| nearPed->m_attackTimer < victimPed->m_attackTimer && nearPed->m_attackTimer > CTimer::GetTimeInMilliseconds() - 100) {
								victimPed = nearPed;
								victimAngle = nearPedAngle;
								distToVictim = dist;
							}
							++numPedsWeCanReach;
							
						} else {
							if (neededTurn < DEGTORAD(30.0f)) {
								walkUpTo = nearPed;
								walkAngle = nearPedAngle;
							}
						}
					}
				}
			} else if (CGame::nastyGame && dist < 1.2f && neededTurn < DEGTORAD(55.0f)) {
				if (!nearPed->DyingOrDead() || groundAttackDeadPed) {
					if (!nearPed->IsPedHeadAbovePos(-0.3f)) {
						groundAttackAlivePed = nearPed;
						groundAttackAliveAngle = nearPedAngle;
					}
				} else {
					groundAttackDeadPed = nearPed;
					groundAttackDeadAngle = nearPedAngle;
				}
				++numPedsWeCanReach;

			} else if (dist > 1.4f && dist < maxAttackDist && neededTurn < DEGTORAD(30.0f)) {
				if (!walkUpTo) {
					walkUpTo = nearPed;
					walkAngle = nearPedAngle;
				}
#ifdef FIX_BUGS
				if(dist < 2.1f)
#endif
				++numPedsWeCanReach;
			}
		}
	}

	if (victimPed) {
		float adjustedAngleDiff = victimAngle - m_fRotationCur + DEGTORAD(30.0f);
		if (adjustedAngleDiff < 0.0f)
			adjustedAngleDiff += TWOPI;

		int16 dir = Floor(adjustedAngleDiff / DEGTORAD(60.0f));

		// Just focus on who we're fighting with, don't care peds on ground
		if (numPedsWeCanReach < 2 || fightWithWeapon) {
			float angleDiff = Abs(victimAngle - m_fRotationCur);
			if (angleDiff > PI)
				angleDiff = TWOPI - angleDiff;

			if (angleDiff < DEGTORAD(60.0f))
				dir = 0; // forward
		}
		int16 randVal = CGeneral::GetRandomNumber() & 3;
		switch (dir) {
			case 0: // forward
				if (fightWithWeapon) {
					if (distToVictim < 0.95f - 0.2f && m_nPedState == PED_FIGHT) {
						choosenMove = FIGHTMOVE_KNEE;
					} else {
						if (GetWeapon()->m_eWeaponType == WEAPONTYPE_CLEAVER) {
							if (distToVictim < 0.85f * weaponInfo->m_fRange)
								choosenMove = FIGHTMOVE_MELEE1;
							else
								choosenMove = FIGHTMOVE_SHUFFLE_F;
						} else {
							float weaponRange = weaponInfo->m_fRange;
							if (distToVictim < 0.75f * weaponRange && GetWeapon()->m_eWeaponType != WEAPONTYPE_SCREWDRIVER) {
								if (m_lastFightMove == FIGHTMOVE_MELEE1 && GetFinishingAttackAnim(weaponInfo)) {
									choosenMove = FIGHTMOVE_MELEE2;
								} else if (m_lastFightMove == FIGHTMOVE_MELEE2 && GetFinishingAttackAnim(weaponInfo)) {
									choosenMove = FIGHTMOVE_MELEE3;
								} else {
									choosenMove = FIGHTMOVE_MELEE1;
								}
							} else if (distToVictim < weaponRange && GetFinishingAttackAnim(weaponInfo)) {
								choosenMove = FIGHTMOVE_MELEE3;
							} else {
								choosenMove = FIGHTMOVE_SHUFFLE_F;
							}
						}
					}
				} else if (distToVictim < 0.95f && m_nPedState == PED_FIGHT) {
					choosenMove = FIGHTMOVE_KNEE;

				} else if (distToVictim < 1.4f) {
					if (m_curFightMove == FIGHTMOVE_PUNCHJAB) {
						choosenMove = FIGHTMOVE_PUNCH;

					} else if (m_curFightMove != FIGHTMOVE_PUNCH || randVal != 1) {
						if (randVal == 2)
							choosenMove = FIGHTMOVE_PUNCH;
						else
							choosenMove = FIGHTMOVE_PUNCHJAB;
					} else {
						choosenMove = FIGHTMOVE_LONGKICK;
					}
				} else {
					choosenMove = FIGHTMOVE_LONGKICK;
				}
				break;
			case 1:
				choosenMove = FIGHTMOVE_FWDLEFT;
				break;
			case 2:
				choosenMove = FIGHTMOVE_BACKLEFT;
				break;
			case 3:
				choosenMove = FIGHTMOVE_BACKKICK;
				break;
			case 4:
				choosenMove = FIGHTMOVE_BACKRIGHT;
				break;
			default:
				choosenMove = FIGHTMOVE_FWDRIGHT;
				break;
		}

		// forward
		if (dir == 0) {
			m_fRotationDest = CGeneral::LimitRadianAngle(victimAngle);
		} else {
			m_fRotationDest = victimAngle - dir * DEGTORAD(60.0f);
			m_fRotationDest = CGeneral::LimitRadianAngle(m_fRotationDest);
		}
		
		m_fRotationCur = m_fRotationDest;
		Say(SOUND_PED_ATTACK);

	} else if (groundAttackAlivePed || groundAttackDeadPed) {
		if (fightWithWeapon && weaponInfo->IsFlagSet(WEAPONFLAG_GROUND_2ND)) {
			choosenMove = FIGHTMOVE_MELEE2;
		} else if (fightWithWeapon && weaponInfo->IsFlagSet(WEAPONFLAG_GROUND_3RD)) {
			choosenMove = FIGHTMOVE_MELEE3;
		} else {
			choosenMove = FIGHTMOVE_GROUNDKICK;
		}
		if (groundAttackAlivePed)
			m_fRotationDest = groundAttackAliveAngle;
		else
			m_fRotationDest = groundAttackDeadAngle;

		m_fRotationCur = m_fRotationDest;
		m_lookTimer = 0;
		if (groundAttackAlivePed)
			SetLookFlag(groundAttackAlivePed, 1, 0);
		else
			SetLookFlag(groundAttackDeadPed, 1, 0);

		SetLookTimer(1500u);

	} else if (walkUpTo) {
		choosenMove = FIGHTMOVE_SHUFFLE_F;
		m_fRotationCur = m_fRotationDest = walkAngle;
		m_lookTimer = 0;
		SetLookFlag(walkUpTo, true);
		SetLookTimer(1500);

	} else if (fightWithWeapon) {
		// No enemy, fight with space
		if (GetWeapon()->m_eWeaponType == WEAPONTYPE_SCREWDRIVER) {
			choosenMove = FIGHTMOVE_MELEE3;
		} else {
			if (m_lastFightMove == FIGHTMOVE_MELEE1 && GetFinishingAttackAnim(weaponInfo)) {
				choosenMove = FIGHTMOVE_MELEE2;
			} else if (m_lastFightMove == FIGHTMOVE_MELEE2 && GetFinishingAttackAnim(weaponInfo)) {
				choosenMove = FIGHTMOVE_MELEE3;
			} else {
				choosenMove = FIGHTMOVE_MELEE1;
			}
		}
	} else {
		// Max number GetRandomNumberInRange returns is max-1
#ifdef FIX_BUGS
		switch (CGeneral::GetRandomNumberInRange(0,4)) {
#else
		switch (CGeneral::GetRandomNumberInRange(0,3)) {
#endif
			case 0:
				choosenMove = FIGHTMOVE_PUNCHJAB;
				break;
			case 1:
				choosenMove = FIGHTMOVE_PUNCH;
				break;
			case 2:
				choosenMove = FIGHTMOVE_LONGKICK;
				break;
			case 3:
				choosenMove = FIGHTMOVE_KNEE;
				break;
			default:
				break;
		}
	}
	return choosenMove;
}

void
CPed::EndFight(uint8 endType)
{
	if (m_nPedState != PED_FIGHT)
		return;

	m_curFightMove = FIGHTMOVE_NULL;
	RestorePreviousState();
	CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_FIGHT_IDLE);
	if (!animAssoc)
		animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_MELEE_IDLE_FIGHTMODE);

	if (animAssoc)
		animAssoc->flags |= ASSOC_DELETEFADEDOUT;

	switch (endType) {
		case ENDFIGHT_NORMAL:
			CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_IDLE_STANCE, 8.0f);
			CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_FIGHT2_IDLE, 8.0f);
			break;
		case ENDFIGHT_WITH_A_STEP:
			CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_IDLE_STANCE, 1.0f);
			CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_WALK_START, 8.0f);
			break;
		case ENDFIGHT_FAST:
			CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_IDLE_STANCE, 8.0f);
			CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_FIGHT2_IDLE, 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 17, total 17), second is the one of we fight with (from 18 to 27, total 10).
	enum {
		S37 = SOUND_FIGHT_37,
		S38 = SOUND_FIGHT_38,
		S39 = SOUND_FIGHT_39,
		S40 = SOUND_FIGHT_40,
		S41 = SOUND_FIGHT_41,
		S42 = SOUND_FIGHT_42,
		S43 = SOUND_FIGHT_43,
		S44 = SOUND_FIGHT_44,
		S45 = SOUND_FIGHT_45,
		S46 = SOUND_FIGHT_46,
		S47 = SOUND_FIGHT_47,
		S48 = SOUND_FIGHT_48,
		NO_SND = SOUND_NO_SOUND
	};
	const uint16 hitSoundsByFightMoves[17][10] = {
	  { S37, S46, S41, S41, S46, S46, S40, S41, S43, S40 },
	  { 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 },
	  { S46, S46, S46, S46, S37, S47, S37, S38, S43, S38 },
	  { S46, S46, S46, S46, S46, S46, S40, S41, S43, S46 },
	  { S46, S46, S46, S46, S46, S46, S40, S41, S43, S40 },
	  { S46, S46, S46, S46, S46, S46, S40, S41, S43, S40 },
	  { S46, S46, S37, S46, S37, S47, S40, S47, S43, S37 },
	  { S46, S46, S46, S46, S46, S46, S43, S44, S43, S43 },
	  { S37, S46, S46, S46, S38, S47, S40, S38, S43, S46 },
	  { S46, S37, S46, S37, S39, S46, S40, S39, S43, S37 },
	  { S46, S37, S46, S46, S38, S47, S40, S38, S43, S46 },
	  { S37, S37, S46, S46, S38, S47, S48, S38, S43, S37 },
	  { S46, S46, S46, S46, S37, S46, S40, S38, S43, S46 },
	  { S46, S46, S46, S37, S39, S46, S40, S39, S43, S46 },
	  { S37, S46, S46, S46, S37, S46, S40, S37, S43, S46 },
	  { S43, S43, S43, S43, S43, S43, S43, S43, S43, S43 }
	};

	eWeaponType weapon = GetWeapon()->m_eWeaponType;
	CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(weapon);
	if (weaponInfo->m_AnimToPlay == ASSOCGRP_KNIFE) {
		if (m_curFightMove >= FIGHTMOVE_MELEE1) {
			if (m_curFightMove == FIGHTMOVE_MELEE3) {
				DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_BAT_ATTACK, (weapon << 8) | ENTITY_TYPE_PED);
			} else {
				DMAudio.PlayOneShot(m_audioEntityId, SOUND_WEAPON_KNIFE_ATTACK, (weapon << 8) | ENTITY_TYPE_PED);
			}
			return;
		}
	}

	// This is why first dimension is between FightMove 1 and 17.
	if (m_curFightMove <= FIGHTMOVE_NULL || m_curFightMove >= FIGHTMOVE_HITFRONT)
		return;

	uint16 soundId;

	// And this is why second dimension is between 18 and 27.
	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, (weapon << 8) | ENTITY_TYPE_PED);
}

bool
CPed::FightStrike(CVector &touchedNodePos, bool fightWithWeapon)
{
	CColModel *hisCol;
	CVector attackDistance;
	float maxDistanceToBeat;
	CPed *nearPed;
	CVector extendedTouchPoint;

	CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType);
	float radius = tFightMoves[m_curFightMove].strikeRadius;
	if (fightWithWeapon)
		radius = weaponInfo->m_fRadius;

	if (m_fightState == FIGHTSTATE_JUST_ATTACKED)
		return false;

	if (this == FindPlayerPed() && fightWithWeapon && GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED)
		CGlass::BreakGlassPhysically(touchedNodePos, radius);

	for (int i = 0; i < m_numNearPeds; i++) {
		int8 pedFound = 0;
		nearPed = m_nearPeds[i];
		if (!fightWithWeapon && GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED && GetWeapon()->m_eWeaponType != WEAPONTYPE_BRASSKNUCKLE)
			maxDistanceToBeat = nearPed->GetBoundRadius() + radius + 0.1f;
		else
			maxDistanceToBeat = nearPed->GetBoundRadius() + radius;

		if ((nearPed->bUsesCollision || nearPed->m_nPedState == PED_DEAD) && (m_pedInObjective != FindPlayerPed() || nearPed == FindPlayerPed())) {
			CVector nearPedCentre;

			// Have to animate a skinned clump because the initial col model is useless
			hisCol = ((CPedModelInfo*)CModelInfo::GetModelInfo(nearPed->GetModelIndex()))->AnimatePedColModelSkinnedWorld(nearPed->GetClump());

			nearPed->GetBoundCentre(nearPedCentre);
			CVector potentialAttackDistance = nearPedCentre - touchedNodePos;

			// He can beat us
			if (sq(maxDistanceToBeat) > potentialAttackDistance.MagnitudeSqr()) {

				for (int j = 0; j < hisCol->numSpheres; j++) {
					attackDistance = hisCol->spheres[j].center;
					attackDistance -= touchedNodePos;
					CColSphere *hisPieces = hisCol->spheres;
					maxDistanceToBeat = hisPieces[j].radius + radius;

					// We can beat him too
					if (sq(maxDistanceToBeat) > attackDistance.MagnitudeSqr()) {
						FightHitPed(nearPed, touchedNodePos, attackDistance, hisPieces[j].piece);
						pedFound = 1;
						break;
					}
				}
			}
			if (!pedFound && !fightWithWeapon) {
				extendedTouchPoint = touchedNodePos - GetPosition();
				if (DotProduct(touchedNodePos - GetPosition(), nearPed->GetPosition() - GetPosition()) > 0.f) {
					if (m_curFightMove == FIGHTMOVE_GROUNDKICK) {
						extendedTouchPoint += tFightMoves[FIGHTMOVE_GROUNDKICK].extendReachMultiplier * GetForward();
					} else {
						extendedTouchPoint.x *= tFightMoves[m_curFightMove].extendReachMultiplier;
						extendedTouchPoint.y *= tFightMoves[m_curFightMove].extendReachMultiplier;
					}
					pedFound = -1;
					extendedTouchPoint += GetPosition();
				}
			}
			if (pedFound == -1) {
				CVector nearPedCentre = nearPed->GetBoundCentre();
				if (sq(maxDistanceToBeat) > (nearPedCentre - extendedTouchPoint).MagnitudeSqr()) {

					for (int j = 0; j < hisCol->numSpheres; j++) {
						attackDistance = hisCol->spheres[j].center;
						attackDistance -= extendedTouchPoint;
						CColSphere* hisPieces = hisCol->spheres;
						float maxDistanceToBeat2 = hisPieces[j].radius + tFightMoves[m_curFightMove].strikeRadius;

						// We can beat him too
						if (sq(maxDistanceToBeat2) > attackDistance.MagnitudeSqr()) {
							FightHitPed(nearPed, extendedTouchPoint, attackDistance, hisPieces[j].piece);
							break;
						}
					}
				}
			}
		}
	}


	if (m_fightState == FIGHTSTATE_NO_MOVE)
		m_fightState = FIGHTSTATE_1;

	m_vecHitLastPos = touchedNodePos;
	return false;
}

void
CPed::FightHitPed(CPed *victim, CVector &touchPoint, CVector &dir, int16 piece)
{
	if (victim->IsPlayer() && victim->m_nPedState == PED_GETUP)
		return;

	CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType);
	bool fightingWithWeapon = false;
	int damageMult = tFightMoves[m_curFightMove].damage * ((CGeneral::GetRandomNumber() & 1) + 2) + 1;

	if (weaponInfo->IsFlagSet(WEAPONFLAG_FIGHTMODE)) {
		fightingWithWeapon = true;
		if (m_curFightMove >= FIGHTMOVE_MELEE1) {
			damageMult = weaponInfo->m_nDamage;
			if (m_curFightMove == FIGHTMOVE_MELEE3 && GetWeapon()->m_eWeaponType != WEAPONTYPE_SCREWDRIVER)
				damageMult *= 5;
		}
	}

	if (IsPlayer()) {
		if (((CPlayerPed*)this)->m_bAdrenalineActive)
			damageMult = 20;
	} else if (!fightingWithWeapon) {
		damageMult *= m_pedStats->m_attackStrength;
	}

	float oldVictimHealth = victim->m_fHealth;
	CVector bloodPos = 0.5f * dir + touchPoint;
	CVector2D diff(GetPosition() - victim->GetPosition());
	int direction = victim->GetLocalDirection(diff);

	bool brassKnucklePunch = false;
	if (GetWeapon()->m_eWeaponType == WEAPONTYPE_BRASSKNUCKLE) {
		if (m_curFightMove == FIGHTMOVE_PUNCHHOOK || m_curFightMove == FIGHTMOVE_PUNCHJAB || m_curFightMove == FIGHTMOVE_BACKLEFT ||
			m_curFightMove == FIGHTMOVE_STDPUNCH || m_curFightMove == FIGHTMOVE_PUNCH) {
			brassKnucklePunch = true;
			damageMult *= 1.5f;
		}
	}
	victim->ReactToAttack(this);

	// Mostly unused. if > 5, ANIM_HIT_BIGSTEP will be run, that's it.
	int unk2;
	if (GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED && GetWeapon()->m_eWeaponType != WEAPONTYPE_BRASSKNUCKLE &&
		!victim->IsPlayer() && !fightingWithWeapon)
		unk2 = 101;
	else
		unk2 = damageMult;

	victim->StartFightDefend(direction, tFightMoves[m_curFightMove].hitLevel, unk2);
	PlayHitSound(victim);
	m_fightState = FIGHTSTATE_JUST_ATTACKED;
	RpAnimBlendClumpGetAssociation(GetClump(), tFightMoves[m_curFightMove].animId)->speed = 0.6f;

	if (!victim->DyingOrDead()) {
		if(fightingWithWeapon)
			victim->InflictDamage(this, GetWeapon()->m_eWeaponType, damageMult, (ePedPieceTypes)piece, direction);
		else
			victim->InflictDamage(this, WEAPONTYPE_UNARMED, damageMult * 3.0f, (ePedPieceTypes)piece, direction);
	}

	if (CGame::nastyGame && weaponInfo->m_AnimToPlay == ASSOCGRP_KNIFE && m_curFightMove >= FIGHTMOVE_MELEE1
		&& victim->GetIsOnScreen()) {

		static float particleRightLen = 0.05f;
		static float particleUpLen = 0.05f;

		// Just for particles. We will restore it below.
		dir /= (20.0f * dir.Magnitude());
		if (m_curFightMove == FIGHTMOVE_MELEE1) {
			float rightMult = -particleRightLen;
			dir += particleUpLen * GetUp() + rightMult * GetRight();

		} else if (m_curFightMove == FIGHTMOVE_MELEE2) {
			float upMult = 2.0f * particleUpLen;
			dir += particleRightLen * GetRight() + upMult * GetUp();
		}
		CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir, nil, 0.0f, 0, 0, 0, 0);
		CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir, nil, 0.0f, 0, 0, 0, 0);
		if (IsPlayer()) {
			CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir, nil, 0.0f, 0, 0, 0, 0);
			CParticle::AddParticle(PARTICLE_BLOOD_SPURT, bloodPos, dir, nil, 0.0f, 0, 0, 0, 0);
		}
		if (!(CGeneral::GetRandomNumber() & 3)) {
			CParticle::AddParticle(PARTICLE_TEST, bloodPos, dir, nil, 0.0f, 0, 0, 0, 0);
		}
	} else if (CGame::nastyGame && (tFightMoves[m_curFightMove].hitLevel > HITLEVEL_MEDIUM || fightingWithWeapon)
			&& victim->GetIsOnScreen()) {

		// Just for particles. We will restore it below.
		dir /= (10.0f * dir.Magnitude());
		for (int i = 0; i < 4; i++) {
			CParticle::AddParticle(PARTICLE_BLOOD, bloodPos, dir, nil, 0.0f, 0, 0, 0, 0);
		}
	}

	eWeaponType weaponType = GetWeapon()->m_eWeaponType;
	if (!fightingWithWeapon) {
		if (!victim->OnGround()) {
			float curVictimHealth = victim->m_fHealth;
			if (curVictimHealth > 0.0f
				&& (curVictimHealth < 30.0f && oldVictimHealth > 30.0f
					|| weaponType != WEAPONTYPE_UNARMED && weaponType != WEAPONTYPE_BRASSKNUCKLE && IsPlayer()
					|| victim->m_pedStats->m_flags & STAT_ONE_HIT_KNOCKDOWN || brassKnucklePunch)) {

				victim->SetFall(0, (AnimationId)(direction + ANIM_KO_SKID_FRONT), 0);
				if (victim->m_nPedState == PED_FALL)
					victim->bIsStanding = false;
			}
		}
	}

	if (victim->m_nPedState == PED_DIE || !victim->bIsStanding) {
		dir = victim->GetPosition() - GetPosition();
		dir.Normalise();
		dir.z = 1.0f;
		victim->bIsStanding = false;

		float moveMult;
		if (fightingWithWeapon) {
			moveMult = Min(damageMult * 0.02f, 1.0f);
		} else if (m_curFightMove == FIGHTMOVE_GROUNDKICK) {
			moveMult = Min(damageMult * 0.6f, 4.0f);
		} else {
			if (victim->m_nPedState != PED_DIE || damageMult >= 20) {
				moveMult = damageMult;
			} else {
				moveMult = Min(damageMult * 2.0f, 14.0f);
			}
		}

		victim->ApplyMoveForce(moveMult * 0.6 * dir);
	}

	if (weaponType != WEAPONTYPE_KNIFE && weaponType != WEAPONTYPE_MACHETE
		&& weaponType != WEAPONTYPE_KATANA && weaponType != WEAPONTYPE_CHAINSAW) {

		if (victim->m_nPedType == PEDTYPE_COP)
			CEventList::RegisterEvent(EVENT_ASSAULT_POLICE, EVENT_ENTITY_PED, victim, this, 2000);
		else
			CEventList::RegisterEvent(EVENT_ASSAULT, EVENT_ENTITY_PED, victim, this, 2000);
	} else {
		if (victim->m_nPedType == PEDTYPE_COP)
			CEventList::RegisterEvent(EVENT_ASSAULT_NASTYWEAPON_POLICE, EVENT_ENTITY_PED, victim, this, 2000);
		else
			CEventList::RegisterEvent(EVENT_ASSAULT_NASTYWEAPON, EVENT_ENTITY_PED, victim, this, 2000);
	}
}

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, extendReachMultiplier;
	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 %f %c %s %d %d",
			moveName,
			&startFireTime,
			&endFireTime,
			&comboFollowOnTime,
			&strikeRadius,
			&extendReachMultiplier,
			&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].extendReachMultiplier = extendReachMultiplier;
		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, "default") != 0) {
			if (strcmp(animName, "null") != 0) {
				animAssoc = CAnimManager::GetAnimAssociation(ASSOCGRP_STD, animName);
				tFightMoves[moveId].animId = (AnimationId)animAssoc->animId;
			} else {
				tFightMoves[moveId].animId = ANIM_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_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_UNK)
				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_ROAD_CROSS);

					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_ROAD_CROSS, 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_IDLE_CAM);
					if (!animAssoc)
						animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_STANCE);

					if (animAssoc && animAssoc->animId == ANIM_IDLE_CAM) {
						CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_IDLE_STANCE, 4.0f);
						SetLookTimer(CGeneral::GetRandomNumberInRange(1000, 2500));

					} else if (CGeneral::GetRandomNumber() & 3) {
						CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_IDLE_CAM, 4.0f);
						SetLookTimer(CGeneral::GetRandomNumberInRange(2500, 5000));
						if (!CGame::germanGame)
							Say(SOUND_PED_CHAT_EVENT);

					} else {
						m_chatTimer = 0;
					}

				} else if (CTimer::GetTimeInMilliseconds() > m_lookTimer) {
					animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_STANCE);
					if (!animAssoc)
						animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_HBHB);

					if (!animAssoc)
						animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_XPRESS_SCRATCH);

					if (animAssoc && animAssoc->animId == ANIM_IDLE_STANCE) {
						if (CGeneral::GetRandomNumber() & 1)
							animToPlay = ANIM_IDLE_HBHB;
						else
							animToPlay = ANIM_XPRESS_SCRATCH;

						CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, animToPlay, 4.0f);
						SetLookTimer(CGeneral::GetRandomNumberInRange(1500, 4000));

					} else if (animAssoc && animAssoc->animId == ANIM_IDLE_HBHB) {
						animAssoc->blendDelta = -8.0f;
						animAssoc->flags |= ASSOC_DELETEFADEDOUT;
						if (CGeneral::GetRandomNumber() & 1) {
							animToPlay = ANIM_IDLE_STANCE;
							animGroup = m_animGroup;
						} else {
							animToPlay = ANIM_XPRESS_SCRATCH;
							animGroup = ASSOCGRP_STD;
						}

						CAnimManager::BlendAnimation(GetClump(), animGroup, animToPlay, 4.0f);
						SetLookTimer(CGeneral::GetRandomNumberInRange(1000, 2500));

					} else {
						if (CGeneral::GetRandomNumber() & 1) {
							animToPlay = ANIM_IDLE_STANCE;
							animGroup = m_animGroup;
						} else {
							animToPlay = ANIM_IDLE_HBHB;
							animGroup = ASSOCGRP_STD;
						}

						CAnimManager::BlendAnimation(GetClump(), animGroup, animToPlay, 4.0f);
						SetLookTimer(CGeneral::GetRandomNumberInRange(1000, 2500));
					}
					if (!CGame::germanGame)
						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_ROAD_CROSS);

						if (animAssoc) {
							animAssoc->blendDelta = -8.0f;
							animAssoc->flags |= ASSOC_DELETEFADEDOUT;
							if (m_eventType == EVENT_ICECREAM)
								animToPlay = ANIM_IDLE_CHAT;
							else
								animToPlay = ANIM_XPRESS_SCRATCH;
							CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, animToPlay,4.0f);
							SetLookTimer(CGeneral::GetRandomNumberInRange(2000, 5000));

						} else {
							animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_CHAT);
							if (animAssoc) {
								animAssoc->blendDelta = -8.0f;
								animAssoc->flags |= ASSOC_DELETEFADEDOUT;
								ClearInvestigateEvent();
							} else {
								animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_XPRESS_SCRATCH);
								if (animAssoc) {
									animAssoc->blendDelta = -8.0f;
									animAssoc->flags |= ASSOC_DELETEFADEDOUT;
								}
								ClearInvestigateEvent();
							}
						}
					} else {
						CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_ROAD_CROSS, 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;
		}

		bool willStandStill = false;
		for (int i = 0; i < m_numNearPeds; i++) {
			if ((m_eventOrThreat - m_nearPeds[i]->GetPosition()).MagnitudeSqr() < sq(0.4f)) {
				SetMoveState(PEDMOVE_STILL);
				willStandStill = true;
				break;
			}
		}

		if (!willStandStill)
			SetMoveState(PEDMOVE_WALK);
	}
}

void
CPed::ClearInvestigateEvent(void)
{
	CAnimBlendAssociation *animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_ROAD_CROSS);
	if (!animAssoc)
		animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_XPRESS_SCRATCH);
	if (!animAssoc)
		animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_HBHB);
	if (!animAssoc)
		animAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_IDLE_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_KO_SHOT_FRONT1;
	bool headShot = false;
	bool willLinger = false;
	int random;

	if (damagedBy == FindPlayerPed() && damagedBy != this && damage > 3.0f)
		++CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel;

	if (player == this) {
		if (!player->m_bCanBeDamaged)
			return false;

		if (damagedBy && damagedBy->IsPed() && ((CPed*)damagedBy)->m_nPedType == PEDTYPE_GANG7)
			return false;

		if ((method == WEAPONTYPE_FLAMETHROWER || method == WEAPONTYPE_MOLOTOV) && CWorld::Players[CWorld::PlayerInFocus].m_bFireproof)
			return false;

		player->AnnoyPlayerPed(false);
	}

	if (DyingOrDead())
		return false;

	if (method == WEAPONTYPE_DROWNING && !bDrownsInWater)
		return false;

	if (!bUsesCollision && (!bInVehicle || m_nPedState != PED_DRIVING) && 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;

	if (!IsPlayer() &&
		(method == WEAPONTYPE_SCREWDRIVER || method == WEAPONTYPE_KNIFE || (method >= WEAPONTYPE_CLEAVER && method <= WEAPONTYPE_CHAINSAW)))
		m_bleedCounter = 200;

	bool detectDieAnim = true;
	if (m_nPedState == PED_GETUP) {
		if (!IsPedHeadAbovePos(-0.3f)) {
			if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL))
				dieAnim = ANIM_FLOOR_HIT_F;
			else
				dieAnim = ANIM_FLOOR_HIT;
			dieDelta *= 2.0f;
			dieSpeed = 0.5f;
			detectDieAnim = false;
		}
	} else if (m_nPedState == PED_FALL) {
		CAnimBlendAssociation *fallAssoc = RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_PARTIAL);
		if (!fallAssoc || fallAssoc->IsRunning()) {
			if (fallAssoc && fallAssoc->blendDelta >= 0.0f)
				dieAnim = NUM_STD_ANIMS;
			else
				dieAnim = ANIM_KO_SHOT_FRONT1;
		} else {
			if (fallAssoc->flags & ASSOC_FRONTAL)
				dieAnim = ANIM_FLOOR_HIT_F;
			else
				dieAnim = ANIM_FLOOR_HIT;

			dieDelta *= 2.0f;
			dieSpeed = 0.5f;
		}
		detectDieAnim = false;
	}

	if (detectDieAnim) {
		switch (method) {
			case WEAPONTYPE_UNARMED:
			case WEAPONTYPE_BRASSKNUCKLE:
				if (bMeleeProof)
					return false;

				if (m_nPedState == PED_FALL) {
					if (IsPedHeadAbovePos(-0.3f)) {
						dieAnim = NUM_STD_ANIMS;
					} else {
						if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL))
							dieAnim = ANIM_FLOOR_HIT_F;
						else
							dieAnim = ANIM_FLOOR_HIT;
						dieDelta = dieDelta * 2.0f;
						dieSpeed = 0.5f;
					}
				} else {
					switch (direction) {
						case 0:
							dieAnim = ANIM_KO_SKID_FRONT;
							break;
						case 1:
							dieAnim = ANIM_KO_SPIN_R;
							break;
						case 2:
							dieAnim = ANIM_KO_SKID_BACK;
							break;
						case 3:
							dieAnim = ANIM_KO_SPIN_L;
							break;
						default:
							break;
					}
				}
				break;
			case WEAPONTYPE_SCREWDRIVER:
			case WEAPONTYPE_GOLFCLUB:
			case WEAPONTYPE_NIGHTSTICK:
			case WEAPONTYPE_KNIFE:
			case WEAPONTYPE_BASEBALLBAT:
			case WEAPONTYPE_HAMMER:
			case WEAPONTYPE_CLEAVER:
			case WEAPONTYPE_MACHETE:
			case WEAPONTYPE_KATANA:
			case WEAPONTYPE_CHAINSAW:
				if (bMeleeProof)
					return false;

				if (method != WEAPONTYPE_KATANA ||
					damagedBy != FindPlayerPed()
					|| FindPlayerPed()->m_nPedState != PED_FIGHT
					|| FindPlayerPed()->m_curFightMove != FIGHTMOVE_MELEE1 && FindPlayerPed()->m_curFightMove != FIGHTMOVE_MELEE2
					|| CGeneral::GetRandomNumber() & 3) {

					if (m_nPedState == PED_FALL) {
						if (IsPedHeadAbovePos(-0.3f)) {
							dieAnim = NUM_STD_ANIMS;
						} else {
							if (RpAnimBlendClumpGetFirstAssociation(GetClump(), ASSOC_FRONTAL))
								dieAnim = ANIM_FLOOR_HIT_F;
							else
								dieAnim = ANIM_FLOOR_HIT;
							dieDelta = dieDelta * 2.0f;
							dieSpeed = 0.5f;
						}
					} else if (damagedBy != FindPlayerPed() || FindPlayerPed()->m_curFightMove != FIGHTMOVE_MELEE2) {
						if (damagedBy != FindPlayerPed() || FindPlayerPed()->m_curFightMove != FIGHTMOVE_MELEE3) {
							switch (direction) {
								case 0:
									dieAnim = ANIM_KO_SKID_FRONT;
									break;
								case 1:
									dieAnim = ANIM_KO_SPIN_R;
									break;
								case 2:
									dieAnim = ANIM_KO_SKID_BACK;
									break;
								case 3:
									dieAnim = ANIM_KO_SPIN_L;
									break;
								default:
									break;
							}
						} else {
							dieAnim = ANIM_KO_SHOT_STOM;
						}
					} else {
						dieAnim = ANIM_KO_SHOT_FACE;
					}
				} else {
					dieAnim = ANIM_KO_SHOT_FACE;
					RemoveBodyPart(PED_HEAD, direction);
					headShot = true;
					willLinger = true;
				}
				break;
			case WEAPONTYPE_COLT45:
			case WEAPONTYPE_SHOTGUN:
			case WEAPONTYPE_STUBBY_SHOTGUN:
			case WEAPONTYPE_SPAS12_SHOTGUN:
			case WEAPONTYPE_TEC9:
			case WEAPONTYPE_UZI:
			case WEAPONTYPE_SILENCED_INGRAM:
			case WEAPONTYPE_MP5:
			case WEAPONTYPE_M4:
			case WEAPONTYPE_RUGER:
			case WEAPONTYPE_SNIPERRIFLE:
			case WEAPONTYPE_LASERSCOPE:
			case WEAPONTYPE_M60:
			case WEAPONTYPE_MINIGUN:
			case WEAPONTYPE_UZI_DRIVEBY:

				if (bBulletProof)
					return false;

				bool dontRemoveLimb;
				if (IsPlayer() || bNoCriticalHits)
					dontRemoveLimb = true;
				else if (method != WEAPONTYPE_M4 && method != WEAPONTYPE_RUGER && method != WEAPONTYPE_SNIPERRIFLE &&
						method != WEAPONTYPE_LASERSCOPE) {
					if (method == WEAPONTYPE_SHOTGUN)
						dontRemoveLimb = CGeneral::GetRandomNumber() & 7;
					else
						dontRemoveLimb = CGeneral::GetRandomNumber() & 15;
				} else
					dontRemoveLimb = false;

				if (dontRemoveLimb) {
					if (method == WEAPONTYPE_SHOTGUN) {
						switch (direction) {
							case 0:
								dieAnim = ANIM_KO_SKID_FRONT;
								break;
							case 1:
								dieAnim = ANIM_KO_SPIN_R;
								break;
							case 2:
								dieAnim = ANIM_KO_SKID_BACK;
								break;
							case 3:
								dieAnim = ANIM_KO_SPIN_L;
								break;
							default:
								break;
						}
					} else
						dieAnim = ANIM_KO_SHOT_FRONT1;

					willLinger = false;
				} else {
					switch (pedPiece) {
						case PEDPIECE_TORSO:
							willLinger = false;
							dieAnim = ANIM_KO_SHOT_FRONT1;
							break;
						case PEDPIECE_MID:
							willLinger = false;
							dieAnim = ANIM_KO_SHOT_STOM;
							break;
						case PEDPIECE_LEFTARM:
							dieAnim = ANIM_KO_SHOT_ARML;
							RemoveBodyPart(PED_UPPERARML, direction);
							willLinger = true;
							break;
						case PEDPIECE_RIGHTARM:
							dieAnim = ANIM_KO_SHOT_ARMR;
							RemoveBodyPart(PED_UPPERARMR, direction);
							willLinger = true;
							break;
						case PEDPIECE_LEFTLEG:
							dieAnim = ANIM_KO_SHOT_LEGL;
							RemoveBodyPart(PED_UPPERLEGL, direction);
							willLinger = true;
							break;
						case PEDPIECE_RIGHTLEG:
							dieAnim = ANIM_KO_SHOT_LEGR;
							RemoveBodyPart(PED_UPPERLEGR, direction);
							willLinger = true;
							break;
						case PEDPIECE_HEAD:
							dieAnim = ANIM_KO_SHOT_FACE;
							RemoveBodyPart(PED_HEAD, direction);
							headShot = true;
							willLinger = true;
							break;
						default:
							break;
					}
				}
				break;
			case WEAPONTYPE_GRENADE:
			case WEAPONTYPE_ROCKETLAUNCHER:
			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_KO_SKID_FRONT;
						break;
					case 1:
						dieAnim = ANIM_KO_SPIN_R;
						break;
					case 2:
						dieAnim = ANIM_KO_SKID_BACK;
						break;
					case 3:
						dieAnim = ANIM_KO_SPIN_L;
						break;
					default:
						break;
				}
				break;
			case WEAPONTYPE_FLAMETHROWER:
				if (bFireProof)
					return false;

				dieAnim = ANIM_KO_SHOT_FRONT1;
				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_KO_SPIN_L;
							else
								dieAnim = ANIM_KO_SKID_FRONT;
						} else
							dieAnim = ANIM_KO_SPIN_R;

						break;
					case 1:
						if (m_nPedState == PED_DIVE_AWAY)
							dieAnim = ANIM_KD_LEFT;
						else
							dieAnim = ANIM_KO_SPIN_R;
						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_KO_SKID_BACK;
							} else {
								dieAnim = ANIM_KD_RIGHT;
							}
						} else
							dieAnim = ANIM_KD_LEFT;
						break;
					case 3:
						if (m_nPedState == PED_DIVE_AWAY)
							dieAnim = ANIM_KD_RIGHT;
						else
							dieAnim = ANIM_KO_SPIN_L;
						break;
					default:
						break;
				}
				if (damagedBy && pedPiece != PEDPIECE_TORSO) {
					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_DROWN;
				break;
			case WEAPONTYPE_FALL:
				if (bCollisionProof)
					return false;

				switch (direction) {
					case 0:
						dieAnim = ANIM_KO_SKID_FRONT;
						break;
					case 1:
						dieAnim = ANIM_KO_SPIN_R;
						break;
					case 2:
						dieAnim = ANIM_KO_SKID_BACK;
						break;
					case 3:
						dieAnim = ANIM_KO_SPIN_L;
						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;
		m_lastDamEntity = damagedBy;
	}

	if (method == WEAPONTYPE_FALL) {
		if (RpAnimBlendClumpGetAssociation(GetClump(), ANIM_CAR_ROLLOUT_LHS)) {
			if (m_fHealth >= 1.0 && m_fHealth - healthImpact < 5.0f) {
				m_fHealth = Min(m_fHealth, 5.0f);
				return false;
			}
		}
	}

	if (m_fHealth - healthImpact >= 1.0f && !willLinger) {
		m_fHealth -= healthImpact;
		return false;
	}

	if (bInVehicle) {
		if (method != WEAPONTYPE_DROWNING) {
			if (m_pMyVehicle) {
				CVehicle* pVehicle = m_pMyVehicle;
				bool bDone = false;
				if (m_pMyVehicle->IsBike()) {
					m_fHealth = 0.0f;
					((CBike*)m_pMyVehicle)->KnockOffRider(method, direction, this, false);
					bDone = true;
				} else {
					if (m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_CAR) {
						if (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(true)) {
						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) {
							CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel += 10;
							CWorld::Players[CWorld::PlayerInFocus].m_fMediaAttention += 5.f;
						}
					}
				}
				for (int i = 0; i < ARRAY_SIZE(pVehicle->pPassengers); i++) {
					CPed* passenger = pVehicle->pPassengers[i];
					if (passenger && passenger != this && damagedBy)
						passenger->ReactToAttack(damagedBy);
				}

				CPed *driverOfVeh = pVehicle->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);
				}
				if (bDone)
					return true;
			}
			m_fHealth = 1.0f;
			return false;
		}
		m_fHealth = 0.0f;
		if (player == this)
			m_pMyVehicle->SetStatus(STATUS_PLAYER_DISABLED);

		SetDie(NUM_STD_ANIMS, 4.0f, 0.0f);
		return true;
	} else {
		m_fHealth = 0.0f;
		SetDie(dieAnim, dieDelta, dieSpeed);

		if (damagedBy == player || damagedBy && damagedBy == FindPlayerVehicle()) {
			CDarkel::RegisterKillByPlayer(this, method, headShot);
			CWorld::Players[CWorld::PlayerInFocus].m_nHavocLevel += 10;
			CWorld::Players[CWorld::PlayerInFocus].m_fMediaAttention += 5.f;
			m_threatEntity = player;
		} else {
			CDarkel::RegisterKillNotByPlayer(this, method);
		}
		if (method == WEAPONTYPE_DROWNING) {
			bIsInTheAir = false;
			if (FindPlayerPed() == this)
				CStats::TimesDrowned++;
		}

		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) {
			if (CEntity::GetIsOnScreen()) {
				m_pedIK.GetComponentPosition(pos, nodeId);
				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)
{
	// VC doesn't have detachable limbs :shrug:
	return nil;
}

// III leftover and unused
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();
		// }

		bBodyPartJustCameOff = true;
		m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 150;

		RemoveBodyPart(PED_HEAD, 0);
		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);
			}
		}
	}
}

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) {
		if (m_nWaitState == WAITSTATE_SUN_BATHE_IDLE) {
			SetGetUp();
			return;
		}
		if (collideWith->m_nWaitState == WAITSTATE_SUN_BATHE_IDLE) {
			collideWith->SetGetUp();
			return;
		}

		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
					|| m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION && m_pedInObjective == collideWith
					|| collideWith->m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION && collideWith->m_pedInObjective == this		
					)) {

				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 (FindPlayerPed() != m_pedInObjective
									|| m_objective != OBJECTIVE_KILL_CHAR_ANY_MEANS && m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT
									|| collideWith == m_pedInObjective) {

									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 (collideWith->m_nMoveState != PEDMOVE_STILL && GetWeapon()->IsTypeMelee()
									&& collideWith->m_pedInObjective == m_pedInObjective) {

									int colliderIndexAtPlayersKillList = -1;
									int ourIndexAtPlayersKillList = -1;
									for (int i = 0; i < ARRAY_SIZE(((CPlayerPed*)m_pedInObjective)->m_pMeleeList); i++) {
										CPed *pedInKillList = ((CPlayerPed*)m_pedInObjective)->m_pMeleeList[i];
										if (pedInKillList == this) {
											ourIndexAtPlayersKillList = i;
										} else if (pedInKillList == collideWith) {
											colliderIndexAtPlayersKillList = i;
										}
									}
									bool weAreCloserToTargetThenCollider = false;
									if ((GetPosition() - m_vecSeekPos).MagnitudeSqr2D() < (collideWith->GetPosition() - m_vecSeekPos).MagnitudeSqr2D())
										weAreCloserToTargetThenCollider = true;

									if (ourIndexAtPlayersKillList > 0 && !weAreCloserToTargetThenCollider) {
										if (colliderIndexAtPlayersKillList > 0) {
											int time = 300;
											SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &time);
											m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + time;

										} else if (collideWith->m_pedInObjective == FindPlayerPed()) {
											((CPlayerPed*)m_pedInObjective)->RemovePedFromMeleeList(this);
											int time = 500;
											SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &time);
											m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + time;
										}
									} else if (!weAreCloserToTargetThenCollider) {
										int time = 300;
										SetWaitState(WAITSTATE_CROSS_ROAD_LOOK, &time);
										m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + time;
									} 
								} else {
									SetDirectionToWalkAroundObject(collideWith);
								}
							}
						} 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);
								m_fRotationCur = 0.3f + m_fRotationCur;
								m_fRotationDest = m_fRotationCur;
							}
							m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(250, 450);
						}
					}
				} else {
					if (m_pedInObjective && (collideWith == m_pedInObjective || collideWith->m_pedInObjective == m_pedInObjective) && CTimer::GetTimeInMilliseconds() > m_nPedStateTimer) {
						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 || collideWith->IsPlayer()
								|| CTimer::GetTimeInMilliseconds() < m_nPedStateTimer) {

								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_FIGHT_PPUNCH, 8.0f);
								animAssoc->flags |= ASSOC_FADEOUTWHENDONE;
								m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 2000;
								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 ||
				m_pedStats->m_defendWeakness <= collideWith->m_pedStats->m_defendWeakness) {

				// 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_HIT_LEFT;
					else
						animToPlay = ANIM_SHOT_LEFT_PARTIAL;
				} 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_HIT_RIGHT;
					else
						animToPlay = ANIM_SHOT_RIGHT_PARTIAL;
				} else {
					if (collideWith->m_nMoveState != PEDMOVE_RUN && collideWith->m_nMoveState != PEDMOVE_SPRINT)
						animToPlay = ANIM_HIT_BACK;
					else
						animToPlay = ANIM_SHOT_BACK_PARTIAL;
				}

				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_49, 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_KO_SPIN_L;
					else
						animToPlay = ANIM_KD_RIGHT;
				} else {
					CVector moveForce = GetRight();
					moveForce.z += 0.1f;
					ApplyMoveForce(moveForce);
					if (heLooksToUs)
						animToPlay = ANIM_KO_SPIN_R;
					else
						animToPlay = ANIM_KD_LEFT;
				}

				if (m_nPedState == PED_ATTACK && collideWith->IsPedInControl())
					DMAudio.PlayOneShot(m_audioEntityId, SOUND_49, 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 (!m_pCollidingEntity || car->GetStatus() == STATUS_PLAYER)
			m_pCollidingEntity = car;
		return;
	}

	if (m_nPedState == PED_DEAD)
		return;

	if (m_pCurSurface) {
		if (m_pCurSurface->IsVehicle() && (((CVehicle*)m_pCurSurface)->IsBoat()|| 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 really distVec.
					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->IsCar()) {
						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();

		distVec *= Min(car->m_fMass / 1400.0f, 1.0f);
		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_KO_SKID_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();
		distVec *= Min(car->m_fMass / 1400.0f, 1.0f);
		car->ApplyMoveForce(distVec * -60.0f);
		Say(SOUND_PED_DEFEND);
	}

	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);
		}
	}
}

void
CPed::DriveVehicle(void)
{
	if (bOffscreen)
		return;

	CVehicle *veh = m_pMyVehicle;
	if (veh->IsBike()) {
		CBike *bike = (CBike*)veh;
		float blendDelta = 1.0f;
		float targetUDLean = 0.0f;
		CAnimBlendAssociation *leftAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_LEFT);
		CAnimBlendAssociation *rightAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_RIGHT);
		CAnimBlendAssociation *stillAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_STILL);
		CAnimBlendAssociation *fwdAssoc, *backAssoc;
		if (IsPlayer()) {
			fwdAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_FWD);
			backAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_BACK);
		}
		CAnimBlendAssociation *walkbackAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_PUSHES);
		CAnimBlendAssociation *drivebyAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_DRIVEBY_RHS);
		if (!drivebyAssoc)
			drivebyAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_DRIVEBY_LHS);
		if (!drivebyAssoc)
			drivebyAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BIKE_DRIVEBY_FT);

		float velocityFwdDotProd = DotProduct(bike->m_vecMoveSpeed, bike->GetForward());
		if (m_vecTurnSpeed.MagnitudeSqr() > 0.09f) {
			bike->KnockOffRider(WEAPONTYPE_FALL, 2, this, false);
			if (bike->pPassengers[0])
				bike->KnockOffRider(WEAPONTYPE_FALL, 2, bike->pPassengers[0], false);
			return;
		}
		if (!drivebyAssoc && Abs(velocityFwdDotProd) < 0.02f) {
			if (!stillAssoc || stillAssoc->blendAmount < 1.0 && stillAssoc->blendDelta <= 0.0) {
				stillAssoc = CAnimManager::BlendAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_STILL, 2.0f);
			}
		} else {
			if (velocityFwdDotProd >= 0.0f) {
				if (stillAssoc && stillAssoc->blendDelta >= 0.0f)
					stillAssoc->blendDelta = -4.0f;
				if (walkbackAssoc && walkbackAssoc->blendDelta >= 0.0f)
					walkbackAssoc->blendDelta = -4.0f;
			} else {
				float maxReverseSpeed = bike->pHandling->Transmission.fMaxReverseVelocity;
				if (3.5f * maxReverseSpeed > velocityFwdDotProd && (bike->m_nWheelsOnGround || bike->GetUp().z < -0.5f)) {
					bike->KnockOffRider(WEAPONTYPE_FALL, 2, this, false);
					if (bike->pPassengers[0])
						bike->KnockOffRider(WEAPONTYPE_FALL, 2, bike->pPassengers[0], false);
					return;
				}
				if (bike->m_fGasPedal >= 0.0 || velocityFwdDotProd <= maxReverseSpeed * 1.5) {
					if (IsPlayer() && velocityFwdDotProd < maxReverseSpeed * 1.5)
						targetUDLean = -1.0f;

					if (stillAssoc && stillAssoc->blendDelta >= 0.0f)
						stillAssoc->blendDelta = -4.0f;

					if (walkbackAssoc && walkbackAssoc->blendDelta >= 0.0f) {
						walkbackAssoc->blendDelta = -4.0f;
					}
				} else if (!walkbackAssoc || walkbackAssoc->blendAmount < 1.0f && walkbackAssoc->blendDelta <= 0.0f) {
					walkbackAssoc = CAnimManager::BlendAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_PUSHES, 4.0f);
				}
			}
		}
		if (stillAssoc)
			blendDelta -= Min(1.0f, CTimer::GetTimeStepNonClipped() * 0.02f * stillAssoc->blendDelta + stillAssoc->blendAmount);

		if (drivebyAssoc)
			blendDelta -= Min(blendDelta, CTimer::GetTimeStepNonClipped() * 0.02f * drivebyAssoc->blendDelta + drivebyAssoc->blendAmount);

		if (walkbackAssoc)
			blendDelta -= Min(blendDelta, CTimer::GetTimeStepNonClipped() * 0.02f * walkbackAssoc->blendDelta + walkbackAssoc->blendAmount);

		float targetLRLean, timeBlend, neededAngForWheelie, stoppieAng;

		// Smooth the lean amount
		if (targetUDLean == -1.0f) {
			targetLRLean = 0.0f;
			timeBlend = Pow(0.86f, CTimer::GetTimeStep());
		} else {
			targetLRLean = clamp(bike->m_fLeanLRAngle / bike->pBikeHandling->fFullAnimLean, -1.0f, 1.0f);
			timeBlend = Pow(0.86f, CTimer::GetTimeStep());
		}

		bike->m_fPedLeanAmountLR = bike->m_fPedLeanAmountLR * timeBlend + (1.0 - timeBlend) * targetLRLean;

		if (!IsPlayer()) {
			targetUDLean = 0.0f;

		} else if (targetUDLean > -1.0f) {
			targetUDLean = bike->m_fLeanInput;
			bike->bWheelieCam = false;
			neededAngForWheelie = 1.0f;
			if (bike->m_aWheelTimer[0] != 0.0f || bike->m_aWheelTimer[1] != 0.0f || bike->GetForward().z <= 0.0f ||
				(0.0f == bike->m_aWheelTimer[2] && 0.0f == bike->m_aWheelTimer[3])) {

				if (0.0f == bike->m_aWheelTimer[2] && 0.0f == bike->m_aWheelTimer[3] &&
					(bike->GetForward().z < 0.0f && (bike->m_aWheelTimer[0] != 0.0f || bike->m_aWheelTimer[1] != 0.0f))) {

					stoppieAng = bike->pBikeHandling->fStoppieAng;
					if (stoppieAng - bike->GetForward().z > 0.6f * stoppieAng)
						bike->bWheelieCam = true;
				}
			} else {
				float wheelieAng = bike->pBikeHandling->fWheelieAng;
				neededAngForWheelie = wheelieAng - bike->GetForward().z;
				if (neededAngForWheelie < wheelieAng / 2.f)
					bike->bWheelieCam = true;
			}
			if (neededAngForWheelie >= 0.15f) {
				if (bike->m_fBrakePedal <= 0.5f || velocityFwdDotProd <= 0.01f) {
					if (bike->m_fGasPedal > 0.5f && targetUDLean <= 0.0f && 0.3f * bike->pHandling->Transmission.fMaxCruiseVelocity > velocityFwdDotProd) {
						targetUDLean = Min(0.1f, targetUDLean);
					}
				} else {
					targetUDLean = Max(0.1f, targetUDLean);
				}
			} else {
				targetUDLean = Max(0.25f, targetUDLean);
			}
			float targetLRLeanABS = Abs(targetLRLean);
			if (targetLRLeanABS > 0.3f) {
				// Yes, UD
				targetUDLean *= Max(0.0f, 1.0f - (targetLRLeanABS - 0.3f) * 50.f / 13.f);
			}
		}
		if (IsPlayer()) {
			float timeBlend = Pow(0.89f, CTimer::GetTimeStep());
			bike->m_fPedLeanAmountUD = (timeBlend * bike->m_fPedLeanAmountUD) + ((1.0f - timeBlend) * targetUDLean);
		} else {
			bike->m_fPedLeanAmountUD = 0.0f;
		}

		float fwdBackLeanAmount, leftRightLeanAmount;
		if (Abs(bike->m_fPedLeanAmountLR) <= 0.56f && IsPlayer()) {

			if (Abs(bike->m_fPedLeanAmountUD) <= 0.56f) {
				CVector2D smoothedLean(bike->m_fPedLeanAmountLR, bike->m_fPedLeanAmountUD);
				float smoothLeanMag = smoothedLean.Magnitude();
				if (smoothLeanMag <= 0.01f) {
					fwdBackLeanAmount = Abs(smoothedLean.y);
					leftRightLeanAmount = Abs(smoothedLean.x);
				} else {
					fwdBackLeanAmount = Abs(smoothedLean.y / smoothLeanMag);
					leftRightLeanAmount = Abs(smoothedLean.x / smoothLeanMag);
				}
			} else {
				fwdBackLeanAmount = 1.0f;
				leftRightLeanAmount = 0.0f;
			}
		} else {
			fwdBackLeanAmount = 0.0f;
			leftRightLeanAmount = 1.0f;
		}
		float fwdBackBlend = fwdBackLeanAmount * blendDelta;
		float leftRightBlend = leftRightLeanAmount * blendDelta;
		if (IsPlayer()) {
			if (!fwdAssoc)
				fwdAssoc = CAnimManager::AddAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_FWD);
			if (!backAssoc)
				backAssoc = CAnimManager::AddAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_BACK);

			if (bike->m_fPedLeanAmountUD < 0.0f) {
				backAssoc->blendAmount = fwdBackBlend;
				backAssoc->SetCurrentTime(-(bike->m_fPedLeanAmountUD * backAssoc->hierarchy->totalLength));
				backAssoc->flags &= ~ASSOC_RUNNING;
				fwdAssoc->blendAmount = 0.0f;
			} else {
				fwdAssoc->blendAmount = fwdBackBlend;
				fwdAssoc->SetCurrentTime(bike->m_fPedLeanAmountUD* fwdAssoc->hierarchy->totalLength);
				fwdAssoc->flags &= ~ASSOC_RUNNING;
				backAssoc->blendAmount = 0.0f;
			}
		}
		if (!leftAssoc)
			leftAssoc = CAnimManager::AddAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_LEFT);
		if (!rightAssoc)
			rightAssoc = CAnimManager::AddAnimation(GetClump(), bike->m_bikeAnimType, ANIM_BIKE_RIGHT);

		if (bike->m_fPedLeanAmountLR < 0.0f) {
			leftAssoc->blendAmount = leftRightBlend;
			leftAssoc->SetCurrentTime(-(bike->m_fPedLeanAmountLR * leftAssoc->hierarchy->totalLength));
			leftAssoc->flags &= ~ASSOC_RUNNING;
			rightAssoc->blendAmount = 0.0f;
		} else {
			rightAssoc->blendAmount = leftRightBlend;
			rightAssoc->SetCurrentTime(bike->m_fPedLeanAmountLR* rightAssoc->hierarchy->totalLength);
			rightAssoc->flags &= ~ASSOC_RUNNING;
			leftAssoc->blendAmount = 0.0f;
		}
		if (velocityFwdDotProd > 0.3f) {
			RwV3d Xaxis = { 1.0f, 0.0f, 0.0f };
			RwV3d Yaxis = { 0.0f, 1.0f, 0.0f };
			RtQuatRotate(&m_pFrames[PED_HEAD]->hanimFrame->q, &Xaxis, CGeneral::GetRandomNumberInRange(-6.0f * velocityFwdDotProd, 6.0f * velocityFwdDotProd), rwCOMBINEPOSTCONCAT);
			RtQuatRotate(&m_pFrames[PED_HEAD]->hanimFrame->q, &Yaxis, CGeneral::GetRandomNumberInRange(-6.0f * velocityFwdDotProd, 6.0f * velocityFwdDotProd), rwCOMBINEPOSTCONCAT);
			bDontAcceptIKLookAts = true;
		}
		return;
	}

	if (!IsPlayer())
		return;

	float steerAngle = m_pMyVehicle->m_fSteerAngle;
	CAnimBlendAssociation* lDriveAssoc;
	CAnimBlendAssociation* rDriveAssoc;
	CAnimBlendAssociation* lbAssoc;
	CAnimBlendAssociation* sitAssoc;
	if (m_pMyVehicle->IsBoat() && !(m_pMyVehicle->pHandling->Flags & HANDLING_SIT_IN_BOAT)) {
		sitAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_DRIVE_BOAT);

		if (!sitAssoc || sitAssoc->blendAmount < 1.0f) {
			return;
		}

		lDriveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_DRIVE_BOAT_L);
		rDriveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_DRIVE_BOAT_R);
		lbAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_BOAT_LB);
	} else if (m_pMyVehicle->bLowVehicle) {
		sitAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_CAR_LSIT);

		if (!sitAssoc || sitAssoc->blendAmount < 1.0f) {
			return;
		}

		lDriveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_DRIVE_LOW_L);
		lbAssoc = nil;
		rDriveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_DRIVE_LOW_R);
	} else {
		sitAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_CAR_SIT);

		if (!sitAssoc || sitAssoc->blendAmount < 1.0f) {
			return;
		}

		lDriveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_DRIVE_L);
		rDriveAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_DRIVE_R);
		lbAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_CAR_LB);
	}

	if (lbAssoc &&
		TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_1STPERSON
		&& TheCamera.Cams[TheCamera.ActiveCam].DirectionWasLooking == LOOKING_LEFT) {
		lbAssoc->blendDelta = -1000.0f;
	}

	CAnimBlendAssociation* driveByAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_DRIVEBY_L);
	if (!driveByAssoc)
		driveByAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_DRIVEBY_R);
	if (!driveByAssoc)
		driveByAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_DRIVEBY_LOW_L);
	if (!driveByAssoc)
		driveByAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_DRIVEBY_LOW_R);

	if (m_pMyVehicle->bLowVehicle || m_pMyVehicle->m_fGasPedal >= 0.0f || driveByAssoc ||
		m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_HELI || m_pMyVehicle->GetVehicleAppearance() == VEHICLE_APPEARANCE_PLANE) {
		if (steerAngle == 0.0f || driveByAssoc) {
			if (lDriveAssoc)
				lDriveAssoc->blendAmount = 0.0f;
			if (rDriveAssoc)
				rDriveAssoc->blendAmount = 0.0f;

		} else if (steerAngle <= 0.0f) {
			if (lDriveAssoc)
				lDriveAssoc->blendAmount = 0.0f;

			if (rDriveAssoc)
				rDriveAssoc->blendAmount = clamp(steerAngle * -100.0f / 61.0f, 0.0f, 1.0f);
			else if (m_pMyVehicle->IsBoat() && !(m_pMyVehicle->pHandling->Flags & HANDLING_SIT_IN_BOAT))
				CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_DRIVE_BOAT_R);
			else if (m_pMyVehicle->bLowVehicle)
				CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_DRIVE_LOW_R);
			else
				CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_DRIVE_R);

		} else {
			if (rDriveAssoc)
				rDriveAssoc->blendAmount = 0.0f;

			if (lDriveAssoc)
				lDriveAssoc->blendAmount = clamp(steerAngle * 100.0f / 61.0f, 0.0f, 1.0f);
			else if (m_pMyVehicle->IsBoat() && !(m_pMyVehicle->pHandling->Flags & HANDLING_SIT_IN_BOAT))
				CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_DRIVE_BOAT_L);
			else if (m_pMyVehicle->bLowVehicle)
				CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_DRIVE_LOW_L);
			else
				CAnimManager::AddAnimation(GetClump(), ASSOCGRP_STD, ANIM_DRIVE_L);
		}

		if (lbAssoc)
			lbAssoc->blendDelta = -4.0f;
	} else {

		if ((TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_1STPERSON
			|| TheCamera.Cams[TheCamera.ActiveCam].DirectionWasLooking != LOOKING_LEFT)
			&& (!lbAssoc || lbAssoc->blendAmount < 1.0f && lbAssoc->blendDelta <= 0.0f)) {

			if (m_pMyVehicle->IsBoat() && !(m_pMyVehicle->pHandling->Flags & HANDLING_SIT_IN_BOAT))
				CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_BOAT_LB, 4.0f);
			else
				CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_CAR_LB, 4.0f);
		}
	}
}

void
CPed::RemoveWeaponAnims(int unused, float animDelta)
{	
	CAnimBlendAssociation *weaponAssoc;
	//CWeaponInfo::GetWeaponInfo(unused);

	weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_FIRE);
	if (weaponAssoc) {
		weaponAssoc->blendDelta = animDelta;
		weaponAssoc->flags |= ASSOC_DELETEFADEDOUT;
	}
	weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_FIRE_2ND);
	if (weaponAssoc) {
		weaponAssoc->blendDelta = animDelta;
		weaponAssoc->flags |= ASSOC_DELETEFADEDOUT;
	}
	weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_FIRE_3RD);
	if (weaponAssoc) {
		weaponAssoc->blendDelta = animDelta;
		weaponAssoc->flags |= ASSOC_DELETEFADEDOUT;
	}
	weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_RELOAD);
	if (weaponAssoc) {
		weaponAssoc->blendDelta = animDelta;
		weaponAssoc->flags |= ASSOC_DELETEFADEDOUT;
	}
	weaponAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_WEAPON_CROUCHRELOAD);
	if (weaponAssoc) {
		weaponAssoc->flags |= ASSOC_DELETEFADEDOUT;
		if (weaponAssoc->flags & ASSOC_PARTIAL)
			weaponAssoc->blendDelta = animDelta;
		else
			CAnimManager::BlendAnimation(GetClump(), m_animGroup, ANIM_IDLE_STANCE, -animDelta);
	}
}
