mirror of
https://github.com/halpz/re3.git
synced 2025-07-03 19:50:45 +00:00
@ -170,9 +170,13 @@ CCivilianPed::CivilianAI(void)
|
||||
}
|
||||
}
|
||||
|
||||
// --MIAMI: Done except comments
|
||||
void
|
||||
CCivilianPed::ProcessControl(void)
|
||||
{
|
||||
if (CharCreatedBy == TODO_CHAR)
|
||||
return;
|
||||
|
||||
CPed::ProcessControl();
|
||||
|
||||
if (bWasPostponed)
|
||||
@ -198,7 +202,8 @@ CCivilianPed::ProcessControl(void)
|
||||
// fall through
|
||||
case PED_SEEK_POS:
|
||||
if (Seek()) {
|
||||
if ((m_objective == OBJECTIVE_GOTO_AREA_ON_FOOT || m_objective == OBJECTIVE_RUN_TO_AREA || IsUseAttractorObjective(m_objective)) && m_pNextPathNode) {
|
||||
if ((m_objective == OBJECTIVE_GOTO_AREA_ON_FOOT || m_objective == OBJECTIVE_RUN_TO_AREA || m_objective == OBJECTIVE_SPRINT_TO_AREA ||
|
||||
IsUseAttractorObjective(m_objective)) && m_pNextPathNode) {
|
||||
m_pNextPathNode = nil;
|
||||
|
||||
} else if (bRunningToPhone) {
|
||||
@ -207,10 +212,10 @@ CCivilianPed::ProcessControl(void)
|
||||
m_phoneId = -1;
|
||||
} else {
|
||||
gPhoneInfo.m_aPhones[m_phoneId].m_nState = PHONE_STATE_REPORTING_CRIME;
|
||||
m_nPedState = PED_FACE_PHONE;
|
||||
SetPedState(PED_FACE_PHONE);
|
||||
}
|
||||
} else if (m_objective != OBJECTIVE_KILL_CHAR_ANY_MEANS && m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT) {
|
||||
if (m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION) {
|
||||
if (m_pedInObjective && m_objective == OBJECTIVE_FOLLOW_CHAR_IN_FORMATION) {
|
||||
if (m_moved.Magnitude() == 0.0f) {
|
||||
if (m_pedInObjective->m_nMoveState == PEDMOVE_STILL)
|
||||
m_fRotationDest = m_pedInObjective->m_fRotationCur;
|
||||
@ -218,7 +223,8 @@ CCivilianPed::ProcessControl(void)
|
||||
} else if (m_objective == OBJECTIVE_GOTO_CHAR_ON_FOOT
|
||||
&& m_pedInObjective && m_pedInObjective->m_nMoveState != PEDMOVE_STILL) {
|
||||
SetMoveState(m_pedInObjective->m_nMoveState);
|
||||
} else if (m_objective == OBJECTIVE_GOTO_AREA_ON_FOOT || m_objective == OBJECTIVE_RUN_TO_AREA || IsUseAttractorObjective(m_objective)) {
|
||||
} else if (m_objective == OBJECTIVE_GOTO_AREA_ON_FOOT || m_objective == OBJECTIVE_RUN_TO_AREA || m_objective == OBJECTIVE_SPRINT_TO_AREA ||
|
||||
IsUseAttractorObjective(m_objective)) {
|
||||
SetIdle();
|
||||
} else {
|
||||
RestorePreviousState();
|
||||
@ -228,7 +234,7 @@ CCivilianPed::ProcessControl(void)
|
||||
break;
|
||||
case PED_FACE_PHONE:
|
||||
if (FacePhone())
|
||||
m_nPedState = PED_MAKE_CALL;
|
||||
SetPedState(PED_MAKE_CALL);
|
||||
break;
|
||||
case PED_MAKE_CALL:
|
||||
if (MakePhonecall())
|
||||
@ -284,6 +290,8 @@ CCivilianPed::ProcessControl(void)
|
||||
GetPosition().x - m_pMyVehicle->GetPosition().x, GetPosition().y - m_pMyVehicle->GetPosition().y, 0.0f);
|
||||
|
||||
DMAudio.PlayOneShot(m_pMyVehicle->m_audioEntityId, SOUND_CAR_JERK, 0.0f);
|
||||
m_pMyVehicle->pDriver->Say(SOUND_PED_PLAYER_BEFORESEX);
|
||||
Say(SOUND_PED_PLAYER_BEFORESEX);
|
||||
|
||||
int playerSexFrequency = CWorld::Players[CWorld::PlayerInFocus].m_nSexFrequency;
|
||||
if (CWorld::Players[CWorld::PlayerInFocus].m_nMoney >= 10 && playerSexFrequency > 250) {
|
||||
@ -300,13 +308,17 @@ CCivilianPed::ProcessControl(void)
|
||||
} else {
|
||||
bWanderPathAfterExitingCar = true;
|
||||
CWorld::Players[CWorld::PlayerInFocus].m_pHooker = nil;
|
||||
ClearLeader();
|
||||
SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle);
|
||||
m_pMyVehicle->pDriver->Say(SOUND_PED_PLAYER_AFTERSEX);
|
||||
}
|
||||
} else {
|
||||
bWanderPathAfterExitingCar = true;
|
||||
CWorld::Players[CWorld::PlayerInFocus].m_pHooker = nil;
|
||||
m_pMyVehicle->pDriver->m_fHealth = CWorld::Players[0].m_nMaxHealth + 25.0f;
|
||||
ClearLeader();
|
||||
SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle);
|
||||
m_pMyVehicle->pDriver->Say(SOUND_PED_PLAYER_AFTERSEX);
|
||||
}
|
||||
} else {
|
||||
CWorld::Players[CWorld::PlayerInFocus].m_nNextSexFrequencyUpdateTime = CTimer::GetTimeInMilliseconds() + 3000;
|
||||
@ -319,6 +331,7 @@ CCivilianPed::ProcessControl(void)
|
||||
} else {
|
||||
bWanderPathAfterExitingCar = true;
|
||||
CWorld::Players[CWorld::PlayerInFocus].m_pHooker = nil;
|
||||
ClearLeader();
|
||||
SetObjective(OBJECTIVE_LEAVE_CAR, m_pMyVehicle);
|
||||
}
|
||||
}
|
||||
@ -340,6 +353,7 @@ CCivilianPed::ProcessControl(void)
|
||||
CivilianAI();
|
||||
|
||||
if (CharCreatedBy == RANDOM_CHAR) {
|
||||
// TODO(Miami): EnterVacantNearbyCars();
|
||||
UseNearbyAttractors();
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include "CarCtrl.h"
|
||||
#include "Renderer.h"
|
||||
#include "Camera.h"
|
||||
#include "PedPlacement.h"
|
||||
#include "Ropes.h"
|
||||
|
||||
CCopPed::CCopPed(eCopType copType, int32 modifier) : CPed(PEDTYPE_COP)
|
||||
{
|
||||
@ -39,12 +41,13 @@ CCopPed::CCopPed(eCopType copType, int32 modifier) : CPed(PEDTYPE_COP)
|
||||
m_wepAccuracy = 76;
|
||||
break;
|
||||
case COP_SWAT:
|
||||
case COP_HELI_SWAT:
|
||||
SetModelIndex(MI_SWAT);
|
||||
GiveDelayedWeapon(WEAPONTYPE_UZI, 1000);
|
||||
SetCurrentWeapon(WEAPONTYPE_UZI);
|
||||
m_fArmour = 50.0f;
|
||||
m_wepSkills = 32; /* TODO: what is this? seems unused */
|
||||
m_wepAccuracy = 64;
|
||||
m_wepAccuracy = 68;
|
||||
break;
|
||||
case COP_ARMY:
|
||||
SetModelIndex(MI_ARMY);
|
||||
@ -77,14 +80,20 @@ CCopPed::CCopPed(eCopType copType, int32 modifier) : CPed(PEDTYPE_COP)
|
||||
field_5FE = 1;
|
||||
m_bIsDisabledCop = false;
|
||||
m_attackTimer = 0;
|
||||
m_bBeatingSuspect = false;
|
||||
m_bStopAndShootDisabledZone = false;
|
||||
field_601 = false;
|
||||
m_bZoneDisabled = false;
|
||||
field_628 = -1;
|
||||
m_nRoadblockNode = -1; // TODO(Miami): this will be nil
|
||||
field_5FF = 0;
|
||||
m_bThrowsSpikeTrap = false;
|
||||
m_pRopeEntity = nil;
|
||||
m_fAbseilPos = 0.0f;
|
||||
m_bBeatingSuspect = false;
|
||||
m_nHassleTimer = 0;
|
||||
field_61C = 0;
|
||||
field_624 = 0;
|
||||
if (m_pPointGunAt)
|
||||
m_pPointGunAt->CleanUpOldReference((CEntity**)&m_pPointGunAt);
|
||||
m_pPointGunAt = nil;
|
||||
}
|
||||
|
||||
@ -93,24 +102,17 @@ CCopPed::~CCopPed()
|
||||
ClearPursuit();
|
||||
}
|
||||
|
||||
// --MIAMI: Done
|
||||
// Parameter should always be CPlayerPed, but it seems they considered making civilians arrestable at some point
|
||||
void
|
||||
CCopPed::SetArrestPlayer(CPed *player)
|
||||
{
|
||||
if (!IsPedInControl() || !player)
|
||||
return;
|
||||
/*
|
||||
switch (m_nCopType) {
|
||||
case COP_FBI:
|
||||
Say(SOUND_PED_ARREST_FBI);
|
||||
break;
|
||||
case COP_SWAT:
|
||||
Say(SOUND_PED_ARREST_SWAT);
|
||||
break;
|
||||
default:
|
||||
Say(SOUND_PED_ARREST_COP);
|
||||
break;
|
||||
} */
|
||||
|
||||
player->Say(SOUND_PED_PLAYER_REACTTOCOP);
|
||||
Say(SOUND_PED_ARREST_COP);
|
||||
|
||||
if (player->EnteringCar()) {
|
||||
if (CTimer::GetTimeInMilliseconds() > m_nPedStateTimer)
|
||||
return;
|
||||
@ -125,14 +127,14 @@ CCopPed::SetArrestPlayer(CPed *player)
|
||||
|
||||
} else if (player->m_nPedState != PED_DIE && player->m_nPedState != PED_DEAD && player->m_nPedState != PED_ARRESTED) {
|
||||
player->m_nLastPedState = player->m_nPedState;
|
||||
player->m_nPedState = PED_ARRESTED;
|
||||
player->SetPedState(PED_ARRESTED);
|
||||
|
||||
FindPlayerPed()->m_bCanBeDamaged = false;
|
||||
((CPlayerPed*)player)->m_pArrestingCop = this;
|
||||
this->RegisterReference((CEntity**) &((CPlayerPed*)player)->m_pArrestingCop);
|
||||
}
|
||||
|
||||
m_nPedState = PED_ARREST_PLAYER;
|
||||
SetPedState(PED_ARREST_PLAYER);
|
||||
SetObjective(OBJECTIVE_NONE);
|
||||
m_prevObjective = OBJECTIVE_NONE;
|
||||
bIsPointingGunAt = false;
|
||||
@ -145,10 +147,11 @@ CCopPed::SetArrestPlayer(CPed *player)
|
||||
player->m_pMyVehicle->bIsHandbrakeOn = true;
|
||||
player->m_pMyVehicle->SetStatus(STATUS_PLAYER_DISABLED);
|
||||
}
|
||||
if (GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED)
|
||||
if (GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED || GetWeapon()->m_eWeaponType == WEAPONTYPE_BRASSKNUCKLE)
|
||||
SetCurrentWeapon(WEAPONTYPE_COLT45);
|
||||
}
|
||||
|
||||
// --MIAMI: Done
|
||||
void
|
||||
CCopPed::ClearPursuit(void)
|
||||
{
|
||||
@ -187,6 +190,7 @@ CCopPed::ClearPursuit(void)
|
||||
bNotAllowedToDuck = false;
|
||||
bKindaStayInSamePlace = false;
|
||||
m_bStopAndShootDisabledZone = false;
|
||||
field_601 = false;
|
||||
m_bZoneDisabled = false;
|
||||
ClearObjective();
|
||||
if (IsPedInControl()) {
|
||||
@ -204,10 +208,14 @@ CCopPed::ClearPursuit(void)
|
||||
}
|
||||
}
|
||||
|
||||
// --MIAMI: Done
|
||||
// TODO: I don't know why they needed that parameter.
|
||||
void
|
||||
CCopPed::SetPursuit(bool ignoreCopLimit)
|
||||
{
|
||||
if (CTimer::GetTimeInMilliseconds() < field_61C)
|
||||
return;
|
||||
|
||||
CWanted *wanted = FindPlayerPed()->m_pWanted;
|
||||
if (m_bIsInPursuit || !IsPedInControl())
|
||||
return;
|
||||
@ -233,6 +241,7 @@ CCopPed::SetPursuit(bool ignoreCopLimit)
|
||||
}
|
||||
}
|
||||
|
||||
// --MIAMI: Done
|
||||
void
|
||||
CCopPed::ArrestPlayer(void)
|
||||
{
|
||||
@ -298,6 +307,7 @@ CCopPed::ScanForCrimes(void)
|
||||
}
|
||||
}
|
||||
|
||||
// --MIAMI: Done
|
||||
void
|
||||
CCopPed::CopAI(void)
|
||||
{
|
||||
@ -315,11 +325,6 @@ CCopPed::CopAI(void)
|
||||
if (bHitSomethingLastFrame) {
|
||||
m_bZoneDisabled = true;
|
||||
m_bIsDisabledCop = true;
|
||||
#ifdef FIX_BUGS
|
||||
m_nRoadblockNode = -1;
|
||||
#else
|
||||
m_nRoadblockNode = 0;
|
||||
#endif
|
||||
bKindaStayInSamePlace = true;
|
||||
bIsRunning = false;
|
||||
bNotAllowedToDuck = false;
|
||||
@ -346,6 +351,27 @@ CCopPed::CopAI(void)
|
||||
}
|
||||
if (wantedLevel > 0) {
|
||||
if (!m_bIsDisabledCop) {
|
||||
// Turn and shoot the player's vehicle, if possible
|
||||
if (!m_bIsInPursuit && !GetWeapon()->IsTypeMelee() && FindPlayerVehicle() && m_fDistanceToTarget < CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_fRange) {
|
||||
if (FindPlayerVehicle()->m_vecMoveSpeed.Magnitude2D() > 0.1f) {
|
||||
CVector2D distToVeh = GetPosition() - FindPlayerVehicle()->GetPosition();
|
||||
distToVeh.Normalise();
|
||||
CVector2D vehSpeed = FindPlayerVehicle()->m_vecMoveSpeed;
|
||||
vehSpeed.Normalise();
|
||||
|
||||
if (DotProduct2D(distToVeh, vehSpeed) > 0.8f) {
|
||||
SetLookFlag(playerOrHisVeh, true);
|
||||
SetMoveState(PEDMOVE_STILL);
|
||||
if (TurnBody()) {
|
||||
SetAttack(FindPlayerVehicle());
|
||||
SetShootTimer(CGeneral::GetRandomNumberInRange(500.0f, 1000.0f));
|
||||
SetAttackTimer(CGeneral::GetRandomNumberInRange(200.0f, 300.0f));
|
||||
}
|
||||
} else if (m_nPedState == PED_ATTACK)
|
||||
RestorePreviousState();
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_bIsInPursuit || wanted->m_CurrentCops > wanted->m_MaxCops) {
|
||||
CCopPed *copFarthestToTarget = nil;
|
||||
float copFarthestToTargetDist = m_fDistanceToTarget;
|
||||
@ -388,11 +414,14 @@ CCopPed::CopAI(void)
|
||||
|
||||
if (wantedLevel > 1 && GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED)
|
||||
SetCurrentWeapon(WEAPONTYPE_COLT45);
|
||||
else if (wantedLevel == 1 && GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED && !FindPlayerPed()->m_pCurrentPhysSurface) {
|
||||
else if (wantedLevel == 1 && GetWeapon()->m_eWeaponType == WEAPONTYPE_UNARMED && !FindPlayerPed()->m_pCurrentPhysSurface) {
|
||||
// i.e. if player is on top of car, cop will still use colt45.
|
||||
SetCurrentWeapon(WEAPONTYPE_UNARMED);
|
||||
SetCurrentWeapon(GetWeaponSlot(WEAPONTYPE_NIGHTSTICK) >= 0 ? WEAPONTYPE_NIGHTSTICK : WEAPONTYPE_UNARMED);
|
||||
}
|
||||
|
||||
if (m_bBeatingSuspect && GetWeapon()->m_eWeaponType == WEAPONTYPE_NIGHTSTICK)
|
||||
Say(SOUND_PED_PULLOUTWEAPON);
|
||||
|
||||
if (FindPlayerVehicle()) {
|
||||
if (m_bBeatingSuspect) {
|
||||
--wanted->m_CopsBeatingSuspect;
|
||||
@ -403,18 +432,18 @@ CCopPed::CopAI(void)
|
||||
}
|
||||
return;
|
||||
}
|
||||
float weaponRange = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType)->m_fRange;
|
||||
SetCurrentWeapon(WEAPONTYPE_COLT45);
|
||||
CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType);
|
||||
float weaponRange = weaponInfo->m_fRange;
|
||||
SetLookFlag(playerOrHisVeh, true);
|
||||
TurnBody();
|
||||
SetCurrentWeapon(WEAPONTYPE_COLT45);
|
||||
if (!bIsDucking) {
|
||||
if (!bIsDucking || bCrouchWhenShooting && GetCrouchFireAnim(weaponInfo)) {
|
||||
if (m_attackTimer >= CTimer::GetTimeInMilliseconds()) {
|
||||
if (m_nPedState != PED_ATTACK && m_nPedState != PED_FIGHT && !m_bZoneDisabled) {
|
||||
CVector targetDist = playerOrHisVeh->GetPosition() - GetPosition();
|
||||
if (m_fDistanceToTarget > 30.0f) {
|
||||
CAnimBlendAssociation* crouchShootAssoc = RpAnimBlendClumpGetAssociation(GetClump(), ANIM_RBLOCK_CSHOOT);
|
||||
if (crouchShootAssoc)
|
||||
crouchShootAssoc->blendDelta = -1000.0f;
|
||||
if (bIsDucking)
|
||||
ClearDuck();
|
||||
|
||||
// Target is coming onto us
|
||||
if (DotProduct(playerOrHisVeh->m_vecMoveSpeed, targetDist) > 0.0f) {
|
||||
@ -432,42 +461,23 @@ CCopPed::CopAI(void)
|
||||
bNotAllowedToDuck = false;
|
||||
bDuckAndCover = false;
|
||||
} else {
|
||||
// VC checks for != nil compared to buggy behaviour of III. I check for != -1 here.
|
||||
#ifdef VC_PED_PORTS
|
||||
// TODO(Miami): Roadblock system is still III
|
||||
float dotProd;
|
||||
if (m_nRoadblockNode != -1) {
|
||||
// TODO(MIAMI): check this, i'm only getting this compile here....
|
||||
CPathNode *roadBlockNode = &ThePaths.m_pathNodes[CRoadBlocks::RoadBlockNodes[m_nRoadblockNode]];
|
||||
dotProd = DotProduct2D(playerOrHisVeh->GetPosition() - roadBlockNode->GetPosition(), GetPosition() - roadBlockNode->GetPosition());
|
||||
} else
|
||||
dotProd = -1.0f;
|
||||
|
||||
if(dotProd >= 0.0f) {
|
||||
#else
|
||||
|
||||
#ifndef FIX_BUGS
|
||||
float copRoadDotProd, targetRoadDotProd;
|
||||
#else
|
||||
float copRoadDotProd = 1.0f, targetRoadDotProd = 1.0f;
|
||||
if (m_nRoadblockNode != -1)
|
||||
#endif
|
||||
{
|
||||
CTreadable* roadBlockRoad = ThePaths.m_mapObjects[CRoadBlocks::RoadBlockObjects[m_nRoadblockNode]];
|
||||
CVector2D roadFwd = roadBlockRoad->GetForward();
|
||||
copRoadDotProd = DotProduct2D(GetPosition() - roadBlockRoad->GetPosition(), roadFwd);
|
||||
targetRoadDotProd = DotProduct2D(playerOrHisVeh->GetPosition() - roadBlockRoad->GetPosition(), roadFwd);
|
||||
}
|
||||
// Roadblock may be towards road's fwd or opposite, so check both
|
||||
if ((copRoadDotProd >= 0.0f || targetRoadDotProd >= 0.0f)
|
||||
&& (copRoadDotProd <= 0.0f || targetRoadDotProd <= 0.0f)) {
|
||||
#endif
|
||||
bIsPointingGunAt = true;
|
||||
} else {
|
||||
if (bIsDucking)
|
||||
ClearDuck();
|
||||
m_bIsDisabledCop = false;
|
||||
bKindaStayInSamePlace = false;
|
||||
bNotAllowedToDuck = false;
|
||||
bCrouchWhenShooting = false;
|
||||
bIsDucking = false;
|
||||
bDuckAndCover = false;
|
||||
SetPursuit(false);
|
||||
}
|
||||
@ -475,7 +485,6 @@ CCopPed::CopAI(void)
|
||||
}
|
||||
} else {
|
||||
if (m_fDistanceToTarget < weaponRange) {
|
||||
CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType);
|
||||
CVector gunPos = weaponInfo->m_vecFireOffset;
|
||||
TransformToNode(gunPos, PED_HANDR);
|
||||
|
||||
@ -484,6 +493,9 @@ CCopPed::CopAI(void)
|
||||
if (!CWorld::ProcessLineOfSight(gunPos, playerOrHisVeh->GetPosition(), foundCol, foundEnt,
|
||||
false, true, false, false, true, false, false)
|
||||
|| foundEnt && foundEnt == playerOrHisVeh) {
|
||||
|
||||
if (m_pPointGunAt)
|
||||
m_pPointGunAt->CleanUpOldReference((CEntity**) &m_pPointGunAt);
|
||||
m_pPointGunAt = playerOrHisVeh;
|
||||
if (playerOrHisVeh)
|
||||
playerOrHisVeh->RegisterReference((CEntity**) &m_pPointGunAt);
|
||||
@ -491,7 +503,7 @@ CCopPed::CopAI(void)
|
||||
SetAttack(playerOrHisVeh);
|
||||
SetShootTimer(CGeneral::GetRandomNumberInRange(500, 1000));
|
||||
}
|
||||
SetAttackTimer(CGeneral::GetRandomNumberInRange(100, 300));
|
||||
SetAttackTimer(CGeneral::GetRandomNumberInRange(200, 300));
|
||||
}
|
||||
SetMoveState(PEDMOVE_STILL);
|
||||
}
|
||||
@ -521,10 +533,8 @@ CCopPed::CopAI(void)
|
||||
ClearObjective();
|
||||
SetWanderPath(CGeneral::GetRandomNumber() & 7);
|
||||
}
|
||||
}
|
||||
#ifdef VC_PED_PORTS
|
||||
else {
|
||||
if (m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT && CharCreatedBy == RANDOM_CHAR) {
|
||||
} else {
|
||||
if (m_objective != OBJECTIVE_KILL_CHAR_ON_FOOT && m_objective != OBJECTIVE_HASSLE_CHAR && CharCreatedBy == RANDOM_CHAR) {
|
||||
for (int i = 0; i < m_numNearPeds; i++) {
|
||||
CPed *nearPed = m_nearPeds[i];
|
||||
if (nearPed->CharCreatedBy == RANDOM_CHAR) {
|
||||
@ -544,12 +554,30 @@ CCopPed::CopAI(void)
|
||||
nearPed->bBeingChasedByPolice = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (nearPed->m_nPedType != PEDTYPE_COP && !nearPed->IsPlayer()
|
||||
&& nearPed->IsPedInControl() && m_nHassleTimer < CTimer::GetTimeInMilliseconds()) {
|
||||
|
||||
if (nearPed->m_objective == OBJECTIVE_NONE && nearPed->m_nPedState == PED_WANDER_PATH
|
||||
&& !nearPed->m_pLookTarget && nearPed->m_lookTimer < CTimer::GetTimeInMilliseconds()) {
|
||||
|
||||
if ((GetPosition() - nearPed->GetPosition()).MagnitudeSqr() < sq(5.0f)) {
|
||||
|
||||
if (CWorld::GetIsLineOfSightClear(GetPosition(), nearPed->GetPosition(),
|
||||
true, false, false, false, false, false, false)) {
|
||||
Say(SOUND_PED_COP_REACTION);
|
||||
SetObjective(OBJECTIVE_HASSLE_CHAR, nearPed);
|
||||
nearPed->SetObjective(OBJECTIVE_WAIT_ON_FOOT_FOR_COP, this);
|
||||
m_nHassleTimer = CTimer::GetTimeInMilliseconds() + 100000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -560,18 +588,35 @@ CCopPed::CopAI(void)
|
||||
bKindaStayInSamePlace = false;
|
||||
bNotAllowedToDuck = false;
|
||||
bCrouchWhenShooting = false;
|
||||
bIsDucking = false;
|
||||
bDuckAndCover = false;
|
||||
if (bIsDucking)
|
||||
ClearDuck();
|
||||
if (m_pMyVehicle)
|
||||
SetObjective(OBJECTIVE_ENTER_CAR_AS_DRIVER, m_pMyVehicle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --MIAMI: Done except commented things
|
||||
void
|
||||
CCopPed::ProcessControl(void)
|
||||
{
|
||||
if (m_nCopType == COP_HELI_SWAT)
|
||||
ProcessHeliSwat();
|
||||
|
||||
CPed::ProcessControl();
|
||||
|
||||
if (m_bThrowsSpikeTrap) {
|
||||
// TODO(Miami)
|
||||
/*
|
||||
if (CGame::currArea != AREA_MALL)
|
||||
ProcessStingerCop();
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(Miami): CStinger::Process
|
||||
|
||||
if (bWasPostponed)
|
||||
return;
|
||||
|
||||
@ -603,25 +648,36 @@ CCopPed::ProcessControl(void)
|
||||
if (IsPedInControl())
|
||||
SetIdle();
|
||||
}
|
||||
/*
|
||||
|
||||
if (m_bIsInPursuit) {
|
||||
if (player->m_nPedState != PED_ARRESTED && !player->DyingOrDead()) {
|
||||
switch (m_nCopType) {
|
||||
case COP_FBI:
|
||||
Say(SOUND_PED_PURSUIT_FBI);
|
||||
break;
|
||||
case COP_SWAT:
|
||||
Say(SOUND_PED_PURSUIT_SWAT);
|
||||
break;
|
||||
case COP_ARMY:
|
||||
Say(SOUND_PED_PURSUIT_ARMY);
|
||||
break;
|
||||
default:
|
||||
Say(SOUND_PED_PURSUIT_COP);
|
||||
break;
|
||||
if (player->m_pWanted->m_CurrentCops == 1) {
|
||||
Say(SOUND_PED_COP_ALONE);
|
||||
} else {
|
||||
int numCopsNear = 0;
|
||||
for (int i = 0; i < player->m_numNearPeds; ++i) {
|
||||
CPed *nearPed = player->m_nearPeds[i];
|
||||
if (nearPed->m_nPedType == PEDTYPE_COP && nearPed->m_nPedState != PED_DEAD)
|
||||
++numCopsNear;
|
||||
}
|
||||
if (numCopsNear <= 3) {
|
||||
Say(SOUND_PED_COP_LITTLECOPSAROUND);
|
||||
if (!player->bInVehicle) {
|
||||
CVector distToPlayer = player->GetPosition() - GetPosition();
|
||||
if (distToPlayer.MagnitudeSqr() < sq(20.0f)) {
|
||||
player->Say(SOUND_PED_PLAYER_FARFROMCOPS);
|
||||
if (player->m_nPedState != PED_ATTACK && player->m_nPedState != PED_AIM_GUN) {
|
||||
player->SetLookFlag(this, false);
|
||||
player->SetLookTimer(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ((CGeneral::GetRandomNumber() % 16) == 1) {
|
||||
Say(SOUND_PED_COP_MANYCOPSAROUND);
|
||||
}
|
||||
}
|
||||
}
|
||||
} */
|
||||
}
|
||||
|
||||
if (IsPedInControl()) {
|
||||
CopAI();
|
||||
@ -668,23 +724,10 @@ CCopPed::ProcessControl(void)
|
||||
RestorePreviousObjective();
|
||||
} else {
|
||||
if (player->m_pMyVehicle && player->m_pMyVehicle->m_nNumGettingIn != 0) {
|
||||
// This is 1.3f when arresting in car without seeking first (in above)
|
||||
#if defined(VC_PED_PORTS) || defined(FIX_BUGS)
|
||||
m_distanceToCountSeekDone = 1.3f;
|
||||
#else
|
||||
m_distanceToCountSeekDone = 2.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (bDuckAndCover) {
|
||||
#if !defined(GTA3_1_1_PATCH) && !defined(VC_PED_PORTS)
|
||||
if (!bNotAllowedToDuck && Seek()) {
|
||||
SetMoveState(PEDMOVE_STILL);
|
||||
SetMoveAnim();
|
||||
SetPointGunAt(m_pedInObjective);
|
||||
}
|
||||
#endif
|
||||
} else if (Seek()) {
|
||||
if (!bDuckAndCover && Seek()) {
|
||||
CVehicle *playerVeh = FindPlayerVehicle();
|
||||
if (!playerVeh && player && player->EnteringCar()) {
|
||||
SetArrestPlayer(player);
|
||||
@ -715,35 +758,100 @@ CCopPed::ProcessControl(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!m_bStopAndShootDisabledZone)
|
||||
return;
|
||||
|
||||
bool dontShoot = false;
|
||||
if (GetIsOnScreen()) {
|
||||
if (((CTimer::GetFrameCounter() + m_randomSeed) & 0x1F) == 17) {
|
||||
CEntity *foundBuilding = nil;
|
||||
CColPoint foundCol;
|
||||
CVector lookPos = GetPosition() + CVector(0.0f, 0.0f, 0.7f);
|
||||
CVector camPos = TheCamera.GetGameCamPosition();
|
||||
CWorld::ProcessLineOfSight(camPos, lookPos, foundCol, foundBuilding,
|
||||
true, false, false, false, false, false, false);
|
||||
if (m_pPointGunAt)
|
||||
Say(SOUND_PED_COP_UNK_129);
|
||||
|
||||
// He's at least 15.0 far, in disabled zone, collided into somewhere (that's why m_bStopAndShootDisabledZone set),
|
||||
// and now has building on front of him. He's stupid, we don't need him.
|
||||
if (foundBuilding) {
|
||||
FlagToDestroyWhenNextProcessed();
|
||||
dontShoot = true;
|
||||
if (m_bStopAndShootDisabledZone) {
|
||||
bool dontShoot = false;
|
||||
if (GetIsOnScreen()) {
|
||||
if (((CTimer::GetFrameCounter() + m_randomSeed) & 0x1F) == 17) {
|
||||
CEntity* foundBuilding = nil;
|
||||
CColPoint foundCol;
|
||||
CVector lookPos = GetPosition() + CVector(0.0f, 0.0f, 0.7f);
|
||||
CVector camPos = TheCamera.GetGameCamPosition();
|
||||
CWorld::ProcessLineOfSight(camPos, lookPos, foundCol, foundBuilding,
|
||||
true, false, false, false, false, false, false);
|
||||
|
||||
// He's at least 15.0 far, in disabled zone, collided into somewhere (that's why m_bStopAndShootDisabledZone set),
|
||||
// and now has building on front of him. He's stupid, we don't need him.
|
||||
if (foundBuilding) {
|
||||
FlagToDestroyWhenNextProcessed();
|
||||
dontShoot = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FlagToDestroyWhenNextProcessed();
|
||||
dontShoot = true;
|
||||
}
|
||||
|
||||
if (!dontShoot) {
|
||||
bStopAndShoot = true;
|
||||
bKindaStayInSamePlace = true;
|
||||
bIsPointingGunAt = true;
|
||||
SetAttack(m_pedInObjective);
|
||||
}
|
||||
}
|
||||
|
||||
if (field_624 >= 2 && m_objective == OBJECTIVE_KILL_CHAR_ON_FOOT) {
|
||||
CVector centre = GetPosition() + CVector(0.f, 0.f, 0.65f);
|
||||
if (CWorld::TestSphereAgainstWorld(centre, 0.35f, this, true, false, false, false, false, false)) {
|
||||
field_624 = 0;
|
||||
m_bStopAndShootDisabledZone = true;
|
||||
ClearPursuit();
|
||||
SetObjective(OBJECTIVE_NONE);
|
||||
SetWanderPath(CGeneral::GetRandomNumberInRange(0,8));
|
||||
field_61C = CTimer::GetTimeInMilliseconds() + 30000;
|
||||
} else {
|
||||
field_624 = 0;
|
||||
if (GetWeapon()->IsTypeMelee()) {
|
||||
// TODO(Miami): enum
|
||||
for (int i = 3; i < 7; i++) {
|
||||
if (HasWeaponSlot(i)) {
|
||||
SetCurrentWeapon(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
SetMoveState(PEDMOVE_STILL);
|
||||
bStopAndShoot = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FlagToDestroyWhenNextProcessed();
|
||||
dontShoot = true;
|
||||
}
|
||||
|
||||
if (!dontShoot) {
|
||||
bStopAndShoot = true;
|
||||
bKindaStayInSamePlace = true;
|
||||
bIsPointingGunAt = true;
|
||||
SetAttack(m_pedInObjective);
|
||||
}
|
||||
} else if (CTimer::GetTimeStep() / 100.f <= m_fDistanceTravelled)
|
||||
field_624 = 0;
|
||||
}
|
||||
|
||||
// --MIAMI: Done
|
||||
void
|
||||
CCopPed::ProcessHeliSwat(void)
|
||||
{
|
||||
CVector bestPos = GetPosition();
|
||||
SetPedState(PED_ABSEIL);
|
||||
CPedPlacement::FindZCoorForPed(&bestPos);
|
||||
if (GetPosition().z - 2.0f >= bestPos.z && m_pRopeEntity) {
|
||||
m_fAbseilPos += 0.003f * CTimer::GetTimeStep();
|
||||
m_vecMoveSpeed.z = -0.03f;
|
||||
m_vecTurnSpeed = CVector(0.f, 0.f, (m_randomSeed % 32) * 0.003f - 0.05f);
|
||||
CPhysical::ApplyTurnSpeed();
|
||||
GetMatrix().Reorthogonalise();
|
||||
CVector posOnRope;
|
||||
|
||||
if (CRopes::FindCoorsAlongRope(m_nRopeID, m_fAbseilPos, &posOnRope)) {
|
||||
SetPosition(posOnRope);
|
||||
} else {
|
||||
bUsesCollision = true;
|
||||
m_vecMoveSpeed = CVector(0.f, 0.f, 0.f);
|
||||
SetPedState(PED_IDLE);
|
||||
m_nCopType = COP_SWAT;
|
||||
SetInTheAir();
|
||||
bKnockedUpIntoAir = true;
|
||||
}
|
||||
Say(SOUND_PED_COP_HELIPILOTPHRASE);
|
||||
} else {
|
||||
bUsesCollision = true;
|
||||
m_vecMoveSpeed = CVector(0.f, 0.f, 0.f);
|
||||
SetPedState(PED_IDLE);
|
||||
m_nCopType = COP_SWAT;
|
||||
SetInTheAir();
|
||||
bKnockedUpIntoAir = true;
|
||||
}
|
||||
}
|
@ -6,7 +6,8 @@ enum eCopType
|
||||
COP_STREET = 0,
|
||||
COP_FBI = 1,
|
||||
COP_SWAT = 2,
|
||||
COP_ARMY = 3,
|
||||
COP_HELI_SWAT = 3,
|
||||
COP_ARMY = 4,
|
||||
COP_MIAMIVICE = 5
|
||||
};
|
||||
|
||||
@ -18,13 +19,18 @@ public:
|
||||
bool m_bIsInPursuit;
|
||||
bool m_bIsDisabledCop;
|
||||
int8 field_5FE;
|
||||
int8 field_5FF;
|
||||
bool m_bBeatingSuspect;
|
||||
bool m_bStopAndShootDisabledZone;
|
||||
bool field_601; // set when police dragging player from car
|
||||
bool m_bZoneDisabled;
|
||||
float m_fAbseilPos;
|
||||
eCopType m_nCopType;
|
||||
bool m_bThrowsSpikeTrap;
|
||||
CEntity *m_pRopeEntity; // CHeli or 1
|
||||
uintptr m_nRopeID;
|
||||
uint32 m_nHassleTimer;
|
||||
uint32 field_61C;
|
||||
int32 field_624;
|
||||
int8 field_628;
|
||||
|
||||
CCopPed(eCopType, int32 modifier = 0);
|
||||
@ -37,6 +43,7 @@ public:
|
||||
void ArrestPlayer(void);
|
||||
void ScanForCrimes(void);
|
||||
void CopAI(void);
|
||||
void ProcessHeliSwat(void);
|
||||
};
|
||||
|
||||
#ifndef PED_SKIN
|
||||
|
2817
src/peds/Ped.cpp
2817
src/peds/Ped.cpp
File diff suppressed because it is too large
Load Diff
@ -30,6 +30,13 @@ struct PedAudioData
|
||||
int m_nMaxRandomDelayTime;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ATTACK_IN_PROGRESS,
|
||||
CANT_ATTACK,
|
||||
WATCH_UNTIL_HE_DISAPPEARS,
|
||||
};
|
||||
|
||||
enum eFormation
|
||||
{
|
||||
FORMATION_UNDEFINED,
|
||||
@ -98,7 +105,6 @@ enum PedFightMoves
|
||||
FIGHTMOVE_PUNCHHOOK,
|
||||
FIGHTMOVE_PUNCHJAB,
|
||||
FIGHTMOVE_PUNCH,
|
||||
FIGHTMOVE_BODYBLOW = FIGHTMOVE_PUNCH,
|
||||
FIGHTMOVE_LONGKICK,
|
||||
FIGHTMOVE_ROUNDHOUSE,
|
||||
// Directionals
|
||||
@ -460,11 +466,9 @@ public:
|
||||
uint32 bIsDrowning : 1;
|
||||
uint32 bDrownsInWater : 1;
|
||||
//uint32 b156_4
|
||||
uint32 b156_8 : 1;
|
||||
uint32 bHeldHostageInCar : 1;
|
||||
uint32 bIsPlayerFriend : 1;
|
||||
#ifdef VC_PED_PORTS
|
||||
uint32 bHeadStuckInCollision : 1;
|
||||
#endif
|
||||
uint32 bDeadPedInFrontOfCar : 1;
|
||||
uint32 bStayInCarOnJack : 1;
|
||||
|
||||
@ -472,22 +476,21 @@ public:
|
||||
uint32 bDoomAim : 1;
|
||||
uint32 bCanBeShotInVehicle : 1;
|
||||
//uint32 b157_8
|
||||
//uint32 b157_10
|
||||
//uint32 b157_20
|
||||
uint32 bMakeFleeScream : 1;
|
||||
uint32 bPushedAlongByCar : 1;
|
||||
uint32 b157_40 : 1;
|
||||
uint32 bIgnoreThreatsBehindObjects : 1;
|
||||
|
||||
uint32 bNeverEverTargetThisPed : 1;
|
||||
uint32 bCrouchWhenScared : 1;
|
||||
uint32 bKnockedOffBike : 1;
|
||||
//uint32 b158_8
|
||||
uint32 b158_8 : 1;
|
||||
uint32 b158_10 : 1;
|
||||
uint32 bBoughtIceCream : 1;
|
||||
//uint32 b158_40
|
||||
uint32 b158_40 : 1;
|
||||
//uint32 b158_80
|
||||
|
||||
// our own flags
|
||||
uint32 m_ped_flagI40 : 1; // bMakePedsRunToPhonesToReportCrimes makes use of this as runover by car indicator
|
||||
uint32 m_ped_flagI80 : 1; // KANGAROO_CHEAT define makes use of this as cheat toggle
|
||||
|
||||
uint8 m_gangFlags;
|
||||
@ -595,8 +598,8 @@ public:
|
||||
uint8 m_wepAccuracy;
|
||||
CEntity *m_pPointGunAt;
|
||||
CVector m_vecHitLastPos;
|
||||
uint32 m_curFightMove;
|
||||
uint32 m_lastFightMove;
|
||||
uint32 m_lastHitState; // TODO(Miami): What's this?
|
||||
uint8 m_fightButtonPressure;
|
||||
FightState m_fightState;
|
||||
bool m_takeAStepAfterAttack;
|
||||
@ -634,8 +637,8 @@ public:
|
||||
uint32 m_threatFlags;
|
||||
uint32 m_threatCheck;
|
||||
uint32 m_lastThreatCheck;
|
||||
uint32 m_sayType;
|
||||
uint32 m_sayTimer;
|
||||
uint32 m_delayedSoundID;
|
||||
uint32 m_delayedSoundTimer;
|
||||
uint32 m_lastSoundStart;
|
||||
uint32 m_soundStart;
|
||||
uint16 m_lastQueuedSound;
|
||||
@ -667,6 +670,7 @@ public:
|
||||
void AimGun(void);
|
||||
void KillPedWithCar(CVehicle *veh, float impulse);
|
||||
void Say(uint16 audio);
|
||||
void Say(uint16 audio, int32 time);
|
||||
void SetLookFlag(CEntity* target, bool keepTryingToLook, bool cancelPrevious = false);
|
||||
void SetLookFlag(float direction, bool keepTryingToLook, bool cancelPrevious = false);
|
||||
void SetLookTimer(int time);
|
||||
@ -761,6 +765,8 @@ public:
|
||||
void SetWaitState(eWaitState, void*);
|
||||
bool FightStrike(CVector&, bool);
|
||||
void FightHitPed(CPed*, CVector&, CVector&, int16);
|
||||
int32 ChooseAttackPlayer(uint8, bool);
|
||||
int32 ChooseAttackAI(uint8, bool);
|
||||
int GetLocalDirection(const CVector2D &);
|
||||
void StartFightDefend(uint8, uint8, uint8);
|
||||
void PlayHitSound(CPed*);
|
||||
@ -948,6 +954,8 @@ public:
|
||||
void ClearWaitState(void);
|
||||
void Undress(const char*);
|
||||
void Dress(void);
|
||||
int32 KillCharOnFootMelee(CVector&, CVector&, CVector&);
|
||||
int32 KillCharOnFootArmed(CVector&, CVector&, CVector&);
|
||||
|
||||
bool HasWeaponSlot(uint8 slot) { return m_weapons[slot].m_eWeaponType != WEAPONTYPE_UNARMED; }
|
||||
CWeapon& GetWeapon(uint8 slot) { return m_weapons[slot]; }
|
||||
@ -964,6 +972,7 @@ public:
|
||||
bool Dying(void) { return m_nPedState == PED_DIE; }
|
||||
bool DyingOrDead(void) { return m_nPedState == PED_DIE || m_nPedState == PED_DEAD; }
|
||||
bool OnGround(void) { return m_nPedState == PED_FALL || m_nPedState == PED_DIE || m_nPedState == PED_DEAD; }
|
||||
bool OnGroundOrGettingUp(void) { return OnGround() || m_nPedState == PED_GETUP; }
|
||||
|
||||
bool Driving(void) { return m_nPedState == PED_DRIVING; }
|
||||
bool InVehicle(void) { return bInVehicle && m_pMyVehicle; } // True when ped is sitting/standing in vehicle, not in enter/exit state.
|
||||
@ -1032,6 +1041,20 @@ public:
|
||||
else
|
||||
return (AnimationId)0;
|
||||
}
|
||||
|
||||
static AnimationId GetFinishingAttackAnim(CWeaponInfo* weapon) {
|
||||
if (!!weapon->m_bFinish3rd)
|
||||
return ANIM_MELEE_ATTACK_FINISH;
|
||||
else
|
||||
return (AnimationId)0;
|
||||
}
|
||||
|
||||
static AnimationId GetSecondFireAnim(CWeaponInfo* weapon) {
|
||||
if (!!weapon->m_bUse2nd)
|
||||
return ANIM_WEAPON_FIRE_2ND; // or ANIM_MELEE_ATTACK_2ND
|
||||
else
|
||||
return (AnimationId)0;
|
||||
}
|
||||
// --
|
||||
|
||||
// My additions, because there were many, many instances of that.
|
||||
|
@ -70,17 +70,17 @@ const C2dEffect* CVehicleToEffect::ChooseEffect(const CVector& pos) const
|
||||
if (!m_pVehicle)
|
||||
return nil;
|
||||
if (DotProduct(pos - m_pVehicle->GetPosition(), m_pVehicle->GetRight()) > 0.0f) {
|
||||
if (DotProduct(pos - m_pVehicle->GetPosition(), m_pVehicle->GetForward()) > 0.0f)
|
||||
return &m_effects[0];
|
||||
else
|
||||
return &m_effects[2];
|
||||
}
|
||||
else {
|
||||
if (DotProduct(pos - m_pVehicle->GetPosition(), m_pVehicle->GetForward()) > 0.0f)
|
||||
return &m_effects[1];
|
||||
else
|
||||
return &m_effects[3];
|
||||
}
|
||||
else {
|
||||
if (DotProduct(pos - m_pVehicle->GetPosition(), m_pVehicle->GetForward()) > 0.0f)
|
||||
return &m_effects[0];
|
||||
else
|
||||
return &m_effects[2];
|
||||
}
|
||||
}
|
||||
|
||||
bool CVehicleToEffect::HasThisEffect(C2dEffect* pEffect) const
|
||||
@ -137,8 +137,8 @@ void CPedAttractorManager::RemoveIceCreamVanEffects(C2dEffect* pEffect)
|
||||
for (std::vector<CVehicleToEffect>::const_iterator assoc = vVehicleToEffect.cbegin(); assoc != vVehicleToEffect.cend();) {
|
||||
if (assoc->GetVehicle() != pVehicle)
|
||||
return;
|
||||
size_t total = 0;
|
||||
for (size_t j = 0; j < NUM_ATTRACTORS_FOR_ICECREAM_VAN; j++) {
|
||||
uint32 total = 0;
|
||||
for (uint32 j = 0; j < NUM_ATTRACTORS_FOR_ICECREAM_VAN; j++) {
|
||||
if (FindAssociatedAttractor(assoc->GetEffect(j), vIceCreamAttractors))
|
||||
total++;
|
||||
}
|
||||
@ -355,13 +355,13 @@ bool CPedAttractor::BroadcastArrival(CPed* pPed)
|
||||
bool CPedAttractor::BroadcastDeparture(CPed* pPed)
|
||||
{
|
||||
int qid = -1;
|
||||
for (size_t i = 0; i < vWaitingQueue.size(); i++){
|
||||
for (uint32 i = 0; i < vWaitingQueue.size(); i++){
|
||||
if (vWaitingQueue[i] == pPed)
|
||||
qid = i;
|
||||
}
|
||||
if (qid < 0)
|
||||
return false;
|
||||
for (size_t i = qid + 1; i < vWaitingQueue.size(); i++) {
|
||||
for (uint32 i = qid + 1; i < vWaitingQueue.size(); i++) {
|
||||
CVector pos;
|
||||
float heading;
|
||||
float time;
|
||||
@ -401,7 +401,7 @@ bool CPedAttractor::BroadcastDeparture(CPed* pPed)
|
||||
bool CPedShelterAttractor::BroadcastDeparture(CPed* pPed)
|
||||
{
|
||||
int qid = -1;
|
||||
for (size_t i = 0; i < vWaitingQueue.size(); i++) {
|
||||
for (uint32 i = 0; i < vWaitingQueue.size(); i++) {
|
||||
if (vWaitingQueue[i] == pPed)
|
||||
qid = i;
|
||||
}
|
||||
|
@ -108,8 +108,8 @@ public:
|
||||
float ComputeDeltaHeading() const;
|
||||
float ComputeDeltaPos() const;
|
||||
void ComputeAttractTime(int32 id, bool, float& time) const;
|
||||
int32 GetNoOfRegisteredPeds() const { return vWaitingQueue.size() + vApproachingQueue.size(); }
|
||||
int32 ComputeFreeSlot() const { return vWaitingQueue.size(); }
|
||||
int32 GetNoOfRegisteredPeds() const { return (int32)(vWaitingQueue.size() + vApproachingQueue.size()); }
|
||||
int32 ComputeFreeSlot() const { return (int32)vWaitingQueue.size(); }
|
||||
bool IsInQueue(CPed*) const;
|
||||
bool RegisterPed(CPed*);
|
||||
bool BroadcastArrival(CPed*);
|
||||
|
@ -155,4 +155,14 @@ CPed::Say(uint16 audio)
|
||||
m_queuedSound = audioToPlay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --MIAMI: Done
|
||||
void
|
||||
CPed::Say(uint16 audio, int32 time)
|
||||
{
|
||||
if (m_delayedSoundID == -1) {
|
||||
m_delayedSoundID = audio;
|
||||
m_delayedSoundTimer = CTimer::GetTimeInMilliseconds() + time;
|
||||
}
|
||||
}
|
@ -7,10 +7,11 @@
|
||||
#include "Sprite.h"
|
||||
#include "Text.h"
|
||||
|
||||
|
||||
// TODO(Miami)
|
||||
static char ObjectiveText[][28] = {
|
||||
"No Obj",
|
||||
"Wait on Foot",
|
||||
"Wait on Foot For Cop",
|
||||
"Flee on Foot Till Safe",
|
||||
"Guard Spot",
|
||||
"Guard Area",
|
||||
@ -21,6 +22,8 @@ static char ObjectiveText[][28] = {
|
||||
"Flee Char on Foot Till Safe",
|
||||
"Flee Char on Foot Always",
|
||||
"GoTo Char on Foot",
|
||||
"GoTo Char on Foot Walking",
|
||||
"Hassle Char",
|
||||
"Follow Char in Formation",
|
||||
"Leave Car",
|
||||
"Enter Car as Passenger",
|
||||
@ -42,10 +45,9 @@ static char ObjectiveText[][28] = {
|
||||
"Catch Train",
|
||||
"Buy IceCream",
|
||||
"Steal Any Car",
|
||||
"Steal Any Mission Car",
|
||||
"Mug Char",
|
||||
#ifdef VC_PED_PORTS
|
||||
"Leave Car and Die"
|
||||
#endif
|
||||
"Leave Car and Die",
|
||||
};
|
||||
|
||||
static char StateText[][18] = {
|
||||
@ -82,8 +84,14 @@ static char StateText[][18] = {
|
||||
"Investigate",
|
||||
"Step away",
|
||||
"On Fire",
|
||||
"Sun Bathe",
|
||||
"Flash",
|
||||
"Jog",
|
||||
"Answer Mobile",
|
||||
"Unknown",
|
||||
"STATES_NO_AI",
|
||||
"Abseil",
|
||||
"Sit",
|
||||
"Jump",
|
||||
"Fall",
|
||||
"GetUp",
|
||||
@ -106,6 +114,7 @@ static char StateText[][18] = {
|
||||
"Exit Car",
|
||||
"Hands Up",
|
||||
"Arrested",
|
||||
"Deploying Stinger"
|
||||
};
|
||||
|
||||
static char PersonalityTypeText[][18] = {
|
||||
@ -283,7 +292,7 @@ CPed::DebugRenderOnePedText(void)
|
||||
CFont::SetJustifyOff();
|
||||
CFont::SetColor(CRGBA(255, 255, 0, 255));
|
||||
CFont::SetBackGroundOnlyTextOn();
|
||||
CFont::SetFontStyle(0);
|
||||
CFont::SetFontStyle(1);
|
||||
AsciiToUnicode(StateText[m_nPedState], gUString);
|
||||
CFont::PrintString(screenCoords.x, screenCoords.y, gUString);
|
||||
AsciiToUnicode(ObjectiveText[m_objective], gUString);
|
||||
|
@ -4,7 +4,8 @@
|
||||
#include "PedPlacement.h"
|
||||
#include "World.h"
|
||||
|
||||
void
|
||||
// --MIAMI: Done
|
||||
bool
|
||||
CPedPlacement::FindZCoorForPed(CVector* pos)
|
||||
{
|
||||
float zForPed;
|
||||
@ -32,8 +33,11 @@ CPedPlacement::FindZCoorForPed(CVector* pos)
|
||||
|
||||
zForPed = Max(foundColZ, foundColZ2);
|
||||
|
||||
if (zForPed > -99.0f)
|
||||
if (zForPed > -99.0f) {
|
||||
pos->z = FEET_OFFSET + zForPed;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CEntity*
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
class CPedPlacement {
|
||||
public:
|
||||
static void FindZCoorForPed(CVector* pos);
|
||||
static bool FindZCoorForPed(CVector* pos);
|
||||
static CEntity* IsPositionClearOfCars(Const CVector*);
|
||||
static bool IsPositionClearForPed(const CVector& pos, float radius = -1.0f, int total = -1, CEntity** entities = nil);
|
||||
};
|
@ -47,7 +47,7 @@ CPedStats::LoadPedStats(void)
|
||||
char *buf;
|
||||
char line[256];
|
||||
char name[32];
|
||||
int bp, buflen;
|
||||
size_t bp, buflen;
|
||||
int lp, linelen;
|
||||
int type;
|
||||
float fleeDist, headingChangeRate, attackStrength, defendWeakness;
|
||||
|
@ -43,7 +43,7 @@ CPedType::LoadPedData(void)
|
||||
char *buf;
|
||||
char line[256];
|
||||
char word[32];
|
||||
int bp, buflen;
|
||||
size_t bp, buflen;
|
||||
int lp, linelen;
|
||||
int type;
|
||||
uint32 flags;
|
||||
|
@ -35,7 +35,7 @@ CPlayerPed::~CPlayerPed()
|
||||
delete m_pWanted;
|
||||
}
|
||||
|
||||
// --MIAMI: Done except commented out things
|
||||
// --MIAMI: Done
|
||||
CPlayerPed::CPlayerPed(void) : CPed(PEDTYPE_PLAYER1)
|
||||
{
|
||||
m_fMoveSpeed = 0.0f;
|
||||
@ -53,10 +53,8 @@ CPlayerPed::CPlayerPed(void) : CPed(PEDTYPE_PLAYER1)
|
||||
m_nSpeedTimer = 0;
|
||||
m_bSpeedTimerFlag = false;
|
||||
|
||||
// TODO(Miami)
|
||||
// if (pPointGunAt)
|
||||
// m_pPointGunAt->CleanUpOldReference(&m_pPointGunAt);
|
||||
|
||||
if (m_pPointGunAt)
|
||||
m_pPointGunAt->CleanUpOldReference(&m_pPointGunAt);
|
||||
m_pPointGunAt = nil;
|
||||
SetPedState(PED_IDLE);
|
||||
#ifndef FIX_BUGS
|
||||
@ -69,13 +67,14 @@ CPlayerPed::CPlayerPed(void) : CPed(PEDTYPE_PLAYER1)
|
||||
m_fAttackButtonCounter = 0.0f;
|
||||
m_bHaveTargetSelected = false;
|
||||
m_bHasLockOnTarget = false;
|
||||
m_bDrunkVisualsWearOff = true;
|
||||
m_bCanBeDamaged = true;
|
||||
m_bDrunkVisualsWearOff = false;
|
||||
m_fWalkAngle = 0.0f;
|
||||
m_fFPSMoveHeading = 0.0f;
|
||||
m_pMinigunTopAtomic = nil;
|
||||
m_fGunSpinSpeed = 0.0;
|
||||
m_fGunSpinAngle = 0.0;
|
||||
m_nPadDownPressedInMilliseconds = 0;
|
||||
m_nTargettableObjects[0] = m_nTargettableObjects[1] = m_nTargettableObjects[2] = m_nTargettableObjects[3] = -1;
|
||||
unused1 = false;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
@ -85,17 +84,16 @@ CPlayerPed::CPlayerPed(void) : CPed(PEDTYPE_PLAYER1)
|
||||
}
|
||||
m_nCheckPlayersIndex = 0;
|
||||
m_nPadUpPressedInMilliseconds = 0;
|
||||
m_nPadDownPressedInMilliseconds = 0;
|
||||
idleAnimBlockIndex = CAnimManager::GetAnimationBlockIndex("playidles");
|
||||
}
|
||||
|
||||
void CPlayerPed::ClearWeaponTarget()
|
||||
// --MIAMI: Done
|
||||
void
|
||||
CPlayerPed::ClearWeaponTarget()
|
||||
{
|
||||
if (m_nPedType == PEDTYPE_PLAYER1) {
|
||||
|
||||
// TODO(Miami)
|
||||
// if (m_pPointGunAt)
|
||||
// m_pPointGunAt->CleanUpOldReference(&m_pPointGunAt);
|
||||
if (m_pPointGunAt)
|
||||
m_pPointGunAt->CleanUpOldReference(&m_pPointGunAt);
|
||||
|
||||
m_pPointGunAt = nil;
|
||||
TheCamera.ClearPlayerWeaponMode();
|
||||
@ -784,6 +782,7 @@ CPlayerPed::PlayerControlM16(CPad *padUsed)
|
||||
GetWeapon()->Update(m_audioEntityId, nil);
|
||||
}
|
||||
|
||||
// --MIAMI: Done
|
||||
void
|
||||
CPlayerPed::PlayerControlFighter(CPad *padUsed)
|
||||
{
|
||||
@ -809,6 +808,7 @@ CPlayerPed::PlayerControlFighter(CPad *padUsed)
|
||||
}
|
||||
}
|
||||
|
||||
// --MIAMI: Done
|
||||
void
|
||||
CPlayerPed::PlayerControl1stPersonRunAround(CPad *padUsed)
|
||||
{
|
||||
@ -1814,6 +1814,7 @@ CPlayerPed::PlayIdleAnimations(CPad *padUsed)
|
||||
}
|
||||
}
|
||||
|
||||
// --MIAMI: Done
|
||||
void
|
||||
CPlayerPed::RemovePedFromMeleeList(CPed *ped)
|
||||
{
|
||||
@ -1826,6 +1827,100 @@ CPlayerPed::RemovePedFromMeleeList(CPed *ped)
|
||||
ped->m_attackTimer = 0;
|
||||
}
|
||||
|
||||
// --MIAMI: Done
|
||||
void
|
||||
CPlayerPed::GetMeleeAttackCoords(CVector& coords, int8 dir, float dist)
|
||||
{
|
||||
coords = GetPosition();
|
||||
switch (dir) {
|
||||
case 0:
|
||||
coords.y += dist;
|
||||
break;
|
||||
case 1:
|
||||
coords.x += Sqrt(3.f / 4.f) * dist;
|
||||
coords.y += 0.5f * dist;
|
||||
break;
|
||||
case 2:
|
||||
coords.x += Sqrt(3.f / 4.f) * dist;
|
||||
coords.y -= 0.5f * dist;
|
||||
break;
|
||||
case 3:
|
||||
coords.y -= dist;
|
||||
break;
|
||||
case 4:
|
||||
coords.x -= Sqrt(3.f / 4.f) * dist;
|
||||
coords.y -= 0.5f * dist;
|
||||
break;
|
||||
case 5:
|
||||
coords.x -= Sqrt(3.f / 4.f) * dist;
|
||||
coords.y += 0.5f * dist;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// --MIAMI: Done
|
||||
int32
|
||||
CPlayerPed::FindMeleeAttackPoint(CPed *victim, CVector &dist, uint32 &endOfAttackOut)
|
||||
{
|
||||
endOfAttackOut = 0;
|
||||
bool thereIsAnEmptySlot = false;
|
||||
int dirToAttack = -1;
|
||||
for (int i = 0; i < ARRAY_SIZE(m_pMeleeList); i++) {
|
||||
CPed* pedAtThisDir = m_pMeleeList[i];
|
||||
if (pedAtThisDir) {
|
||||
if (pedAtThisDir == victim) {
|
||||
dirToAttack = i;
|
||||
} else {
|
||||
if (pedAtThisDir->m_attackTimer > endOfAttackOut)
|
||||
endOfAttackOut = pedAtThisDir->m_attackTimer;
|
||||
}
|
||||
} else {
|
||||
thereIsAnEmptySlot = true;
|
||||
}
|
||||
}
|
||||
|
||||
// We don't have victim ped in our melee list
|
||||
if (dirToAttack == -1 && thereIsAnEmptySlot) {
|
||||
float angle = Atan2(-dist.x, -dist.y);
|
||||
float adjustedAngle = angle + DEGTORAD(30.0f);
|
||||
if (adjustedAngle < 0.f)
|
||||
adjustedAngle += TWOPI;
|
||||
|
||||
int wantedDir = Floor(adjustedAngle / DEGTORAD(60.0f));
|
||||
|
||||
// And we have another ped at the direction of victim ped, so store victim to next empty direction to it's real direction. (Bollocks)
|
||||
if (m_pMeleeList[wantedDir]) {
|
||||
int closestDirToPreferred = -99;
|
||||
int preferredDir = wantedDir;
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(m_pMeleeList); i++) {
|
||||
if (!m_pMeleeList[i]) {
|
||||
if (Abs(i - preferredDir) < Abs(closestDirToPreferred - preferredDir))
|
||||
closestDirToPreferred = i;
|
||||
}
|
||||
}
|
||||
if (closestDirToPreferred > 0)
|
||||
dirToAttack = closestDirToPreferred;
|
||||
} else {
|
||||
|
||||
// Luckily the direction of victim ped is already empty, good
|
||||
dirToAttack = wantedDir;
|
||||
}
|
||||
|
||||
if (dirToAttack != -1) {
|
||||
m_pMeleeList[dirToAttack] = victim;
|
||||
victim->RegisterReference((CEntity**) &m_pMeleeList[dirToAttack]);
|
||||
if (endOfAttackOut > CTimer::GetTimeInMilliseconds())
|
||||
victim->m_attackTimer = endOfAttackOut + CGeneral::GetRandomNumberInRange(1000, 2000);
|
||||
else
|
||||
victim->m_attackTimer = CTimer::GetTimeInMilliseconds() + CGeneral::GetRandomNumberInRange(500, 1000);
|
||||
}
|
||||
}
|
||||
return dirToAttack;
|
||||
}
|
||||
|
||||
#ifdef COMPATIBLE_SAVES
|
||||
#define CopyFromBuf(buf, data) memcpy(&data, buf, sizeof(data)); SkipSaveBuf(buf, sizeof(data));
|
||||
#define CopyToBuf(buf, data) memcpy(buf, &data, sizeof(data)); SkipSaveBuf(buf, sizeof(data));
|
||||
|
@ -34,7 +34,7 @@ public:
|
||||
bool m_bDrunkVisualsWearOff; // TODO(Miami): That may be something else
|
||||
CVector m_vecSafePos[6]; // safe places from the player, for example behind a tree
|
||||
CPed *m_pPedAtSafePos[6];
|
||||
CPlayerPed* m_pMeleeList[6];
|
||||
CPed *m_pMeleeList[6]; // reachable peds at each direction(6)
|
||||
char unused1;
|
||||
int16 m_nCheckPlayersIndex;
|
||||
float m_fWalkAngle; //angle between heading and walking direction
|
||||
@ -85,6 +85,8 @@ public:
|
||||
bool DoesPlayerWantNewWeapon(eWeaponType, bool);
|
||||
void PlayIdleAnimations(CPad*);
|
||||
void RemovePedFromMeleeList(CPed*);
|
||||
void GetMeleeAttackCoords(CVector&, int8, float);
|
||||
int32 FindMeleeAttackPoint(CPed*, CVector&, uint32&);
|
||||
|
||||
static void SetupPlayerPed(int32);
|
||||
static void DeactivatePlayerPed(int32);
|
||||
|
@ -353,7 +353,7 @@ CPopulation::FindCollisionZoneForCoors(CVector *coors, int *safeZoneOut, eLevelN
|
||||
}
|
||||
// Then it's transition area
|
||||
if (*safeZoneOut >= 0)
|
||||
*levelOut = LEVEL_NONE;
|
||||
*levelOut = LEVEL_GENERIC;
|
||||
else
|
||||
*levelOut = CTheZones::GetLevelFromPosition(coors);
|
||||
}
|
||||
@ -559,16 +559,23 @@ CPopulation::AddToPopulation(float minDist, float maxDist, float minDistOffScree
|
||||
CZoneInfo zoneInfo;
|
||||
CPed *gangLeader = nil;
|
||||
bool addCop = false;
|
||||
bool forceAddingCop = false;
|
||||
CPlayerInfo *playerInfo = &CWorld::Players[CWorld::PlayerInFocus];
|
||||
CVector playerCentreOfWorld = FindPlayerCentreOfWorld(CWorld::PlayerInFocus);
|
||||
CTheZones::GetZoneInfoForTimeOfDay(&playerCentreOfWorld, &zoneInfo);
|
||||
CWanted *wantedInfo = playerInfo->m_pPed->m_pWanted;
|
||||
|
||||
if (wantedInfo->m_nWantedLevel > 2) {
|
||||
if (ms_nNumCop < wantedInfo->m_MaxCops && !playerInfo->m_pPed->bInVehicle
|
||||
&& (CCarCtrl::NumLawEnforcerCars >= wantedInfo->m_MaximumLawEnforcerVehicles
|
||||
if (!CGame::IsInInterior() && (CGeneral::GetRandomNumber() % 32 == 0) && FindPlayerVehicle())
|
||||
forceAddingCop = true;
|
||||
|
||||
uint32 maxCops = CGame::IsInInterior() ? wantedInfo->m_MaxCops * 1.6f : wantedInfo->m_MaxCops;
|
||||
if ((ms_nNumCop < maxCops || forceAddingCop) &&
|
||||
(!playerInfo->m_pPed->bInVehicle &&
|
||||
(CCarCtrl::NumLawEnforcerCars >= wantedInfo->m_MaximumLawEnforcerVehicles
|
||||
|| CCarCtrl::NumRandomCars >= playerInfo->m_nTrafficMultiplier * CCarCtrl::CarDensityMultiplier
|
||||
|| CCarCtrl::NumFiretrucksOnDuty + CCarCtrl::NumAmbulancesOnDuty + CCarCtrl::NumParkedCars
|
||||
+ CCarCtrl::NumMissionCars + CCarCtrl::NumLawEnforcerCars + CCarCtrl::NumRandomCars >= CCarCtrl::MaxNumberOfCarsInUse)) {
|
||||
+ CCarCtrl::NumMissionCars + CCarCtrl::NumLawEnforcerCars + CCarCtrl::NumRandomCars >= CCarCtrl::MaxNumberOfCarsInUse) || forceAddingCop)) {
|
||||
addCop = true;
|
||||
minDist = PedCreationDistMultiplier() * MIN_CREATION_DIST;
|
||||
maxDist = PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE);
|
||||
@ -693,6 +700,9 @@ CPopulation::AddToPopulation(float minDist, float maxDist, float minDistOffScree
|
||||
if (!farEnoughToAdd)
|
||||
break;
|
||||
CPed *newPed = AddPed((ePedType)pedTypeToAdd, modelToAdd, generatedCoors);
|
||||
if (forceAddingCop && newPed->m_nPedType == PEDTYPE_COP)
|
||||
((CCopPed*)newPed)->m_bThrowsSpikeTrap = true;
|
||||
|
||||
newPed->SetWanderPath(CGeneral::GetRandomNumberInRange(0, 8));
|
||||
|
||||
if (i != 0) {
|
||||
@ -721,10 +731,11 @@ CPopulation::AddToPopulation(float minDist, float maxDist, float minDistOffScree
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(Miami)
|
||||
CPed*
|
||||
CPopulation::AddPedInCar(CVehicle* car, bool isDriver)
|
||||
{
|
||||
int defaultModel = MI_MALE01;
|
||||
const int defaultModel = MI_MALE01;
|
||||
int miamiViceIndex = 0;
|
||||
bool imSureThatModelIsLoaded = true;
|
||||
CVector coors = FindPlayerCoors();
|
||||
@ -744,10 +755,6 @@ CPopulation::AddPedInCar(CVehicle* car, bool isDriver)
|
||||
preferredModel = 0;
|
||||
pedType = PEDTYPE_EMERGENCY;
|
||||
break;
|
||||
case MI_FBICAR:
|
||||
preferredModel = COP_FBI;
|
||||
pedType = PEDTYPE_COP;
|
||||
break;
|
||||
case MI_POLICE:
|
||||
case MI_PREDATOR:
|
||||
preferredModel = COP_STREET;
|
||||
@ -762,24 +769,24 @@ CPopulation::AddPedInCar(CVehicle* car, bool isDriver)
|
||||
preferredModel = COP_ARMY;
|
||||
pedType = PEDTYPE_COP;
|
||||
break;
|
||||
case MI_VICECHEE: // TODO(MIAMI): figure out new structure of the function
|
||||
preferredModel = COP_MIAMIVICE;
|
||||
case MI_FBIRANCH:
|
||||
preferredModel = COP_FBI;
|
||||
pedType = PEDTYPE_COP;
|
||||
miamiViceIndex = (isDriver ? 2 * CCarCtrl::MiamiViceCycle : 2 * CCarCtrl::MiamiViceCycle + 1);
|
||||
break;
|
||||
case MI_TAXI:
|
||||
case MI_CABBIE:
|
||||
case MI_ZEBRA:
|
||||
case MI_KAUFMAN:
|
||||
if (CGeneral::GetRandomTrueFalse()) {
|
||||
pedType = PEDTYPE_CIVMALE;
|
||||
preferredModel = MI_TAXI_D;
|
||||
default:
|
||||
if (car->GetModelIndex() == MI_TAXI || car->GetModelIndex() == MI_CABBIE || car->GetModelIndex() == MI_ZEBRA || car->GetModelIndex() == MI_KAUFMAN) {
|
||||
if (isDriver) {
|
||||
pedType = PEDTYPE_CIVMALE;
|
||||
preferredModel = MI_TAXI_D;
|
||||
break;
|
||||
}
|
||||
} else if (car->GetModelIndex() == MI_VICECHEE && car->bIsLawEnforcer) {
|
||||
preferredModel = COP_MIAMIVICE;
|
||||
pedType = PEDTYPE_COP;
|
||||
miamiViceIndex = (isDriver ? 2 * CCarCtrl::MiamiViceCycle : 2 * CCarCtrl::MiamiViceCycle + 1);
|
||||
break;
|
||||
}
|
||||
defaultModel = MI_TAXI_D;
|
||||
|
||||
// fall through
|
||||
default:
|
||||
int gangOfPed = 0;
|
||||
imSureThatModelIsLoaded = false;
|
||||
|
||||
@ -798,8 +805,12 @@ CPopulation::AddPedInCar(CVehicle* car, bool isDriver)
|
||||
if (preferredModel == -1)
|
||||
preferredModel = defaultModel;
|
||||
|
||||
if (((CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel))->m_carsCanDrive & (1 << carModelInfo->m_vehicleClass))
|
||||
break;
|
||||
if (((CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel))->GetRwObject()) {
|
||||
if (!car->IsPassenger(preferredModel) && !car->IsDriver(preferredModel)) {
|
||||
if (((CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel))->m_carsCanDrive & (1 << carModelInfo->m_vehicleClass))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i == -1)
|
||||
preferredModel = defaultModel;
|
||||
|
Reference in New Issue
Block a user