summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authoraap <aap@papnet.eu>2020-07-29 09:34:28 +0200
committeraap <aap@papnet.eu>2020-07-29 09:34:28 +0200
commitfb4de46626b00079e169da562a0267126c1c1340 (patch)
tree30afeb6ed4245493dc7b8c87a52eb8b7c84e8c49 /src
parentCCollision done and fixes (diff)
parentMerge pull request #667 from erorcun/miami (diff)
downloadre3-fb4de46626b00079e169da562a0267126c1c1340.tar
re3-fb4de46626b00079e169da562a0267126c1c1340.tar.gz
re3-fb4de46626b00079e169da562a0267126c1c1340.tar.bz2
re3-fb4de46626b00079e169da562a0267126c1c1340.tar.lz
re3-fb4de46626b00079e169da562a0267126c1c1340.tar.xz
re3-fb4de46626b00079e169da562a0267126c1c1340.tar.zst
re3-fb4de46626b00079e169da562a0267126c1c1340.zip
Diffstat (limited to 'src')
-rw-r--r--src/audio/oal/stream.cpp12
-rw-r--r--src/core/CdStreamPosix.cpp16
-rw-r--r--src/core/FileMgr.cpp37
-rw-r--r--src/core/Game.cpp9
-rw-r--r--src/core/Ropes.cpp12
-rw-r--r--src/core/main.cpp2
-rw-r--r--src/fakerw/fake.cpp65
-rw-r--r--src/peds/CopPed.cpp339
-rw-r--r--src/peds/CopPed.h7
-rw-r--r--src/peds/PedPlacement.cpp8
-rw-r--r--src/peds/PedPlacement.h2
-rw-r--r--src/skel/crossplatform.cpp129
-rw-r--r--src/skel/crossplatform.h6
-rw-r--r--src/vehicles/Heli.cpp4
14 files changed, 403 insertions, 245 deletions
diff --git a/src/audio/oal/stream.cpp b/src/audio/oal/stream.cpp
index 40ec57fa..b689e929 100644
--- a/src/audio/oal/stream.cpp
+++ b/src/audio/oal/stream.cpp
@@ -255,15 +255,11 @@ CStream::CStream(char *filename, ALuint &source, ALuint (&buffers)[NUM_STREAMBUF
{
// Be case-insensitive on linux (from https://github.com/OneSadCookie/fcaseopen/)
#if !defined(_WIN32)
- FILE *test = fopen(filename, "r");
- if (!test) {
- char *r = (char*)alloca(strlen(filename) + 2);
- if (casepath(filename, r))
- {
- strcpy(m_aFilename, r);
- }
+ char *real = casepath(filename);
+ if (real) {
+ strcpy(m_aFilename, real);
+ free(real);
} else {
- fclose(test);
#else
{
#endif
diff --git a/src/core/CdStreamPosix.cpp b/src/core/CdStreamPosix.cpp
index 4d6bcdab..45fd9832 100644
--- a/src/core/CdStreamPosix.cpp
+++ b/src/core/CdStreamPosix.cpp
@@ -189,10 +189,11 @@ GetGTA3ImgSize(void)
realpath(gImgNames[0], path);
if (stat(path, &statbuf) == -1) {
// Try case-insensitivity
- char *r = (char*)alloca(strlen(gImgNames[0]) + 2);
- if (casepath(gImgNames[0], r))
+ char* real = casepath(gImgNames[0], false);
+ if (real)
{
- realpath(r, path);
+ realpath(real, path);
+ free(real);
if (stat(path, &statbuf) != -1)
goto ok;
}
@@ -210,7 +211,6 @@ CdStreamShutdown(void)
{
// Destroying semaphores and free(gpReadInfo) will be done at threads
#ifndef ONE_THREAD_PER_CHANNEL
- free(gChannelRequestQ.items);
gCdStreamThreadStatus = 2;
sem_post(&gCdStreamSema);
#endif
@@ -442,6 +442,7 @@ void *CdStreamThread(void *param)
sem_destroy(&gpReadInfo[i].pDoneSemaphore);
}
sem_destroy(&gCdStreamSema);
+ free(gChannelRequestQ.items);
#else
sem_destroy(&gpReadInfo[channel].pStartSemaphore);
sem_destroy(&gpReadInfo[channel].pDoneSemaphore);
@@ -460,10 +461,11 @@ CdStreamAddImage(char const *path)
// Fix case sensitivity and backslashes.
if (gImgFiles[gNumImages] == -1) {
- char *r = (char*)alloca(strlen(path) + 2);
- if (casepath(path, r))
+ char* real = casepath(path, false);
+ if (real)
{
- gImgFiles[gNumImages] = open(r, _gdwCdStreamFlags);
+ gImgFiles[gNumImages] = open(real, _gdwCdStreamFlags);
+ free(real);
}
}
diff --git a/src/core/FileMgr.cpp b/src/core/FileMgr.cpp
index cdcb80f0..4477a190 100644
--- a/src/core/FileMgr.cpp
+++ b/src/core/FileMgr.cpp
@@ -4,6 +4,7 @@
#include <direct.h>
#endif
#include "common.h"
+#include "crossplatform.h"
#include "FileMgr.h"
@@ -31,19 +32,16 @@ static myFILE myfiles[NUMFILES];
#include <dirent.h>
#include <errno.h>
#include <unistd.h>
-#include "crossplatform.h"
#define _getcwd getcwd
// Case-insensitivity on linux (from https://github.com/OneSadCookie/fcaseopen)
void mychdir(char const *path)
{
- char *r = (char*)alloca(strlen(path) + 2);
- if (casepath(path, r))
- {
+ char* r = casepath(path, false);
+ if (r) {
chdir(r);
- }
- else
- {
+ free(r);
+ } else {
errno = ENOENT;
}
}
@@ -73,30 +71,7 @@ found:
*p++ = 'b';
*p = '\0';
-#if !defined(_WIN32)
- char *newPath = strdup(filename);
- // Normally casepath() fixes backslashes, but if the mode is sth other than r/rb it will create new file with backslashes on linux, so fix backslashes here
- char *nextBs;
- while(nextBs = strstr(newPath, "\\")){
- *nextBs = '/';
- }
-#else
- const char *newPath = filename;
-#endif
-
- myfiles[fd].file = fopen(newPath, realmode);
-// Be case-insensitive on linux (from https://github.com/OneSadCookie/fcaseopen/)
-#if !defined(_WIN32)
- if (!myfiles[fd].file) {
- char *r = (char*)alloca(strlen(newPath) + 2);
- if (casepath(newPath, r))
- {
- myfiles[fd].file = fopen(r, realmode);
- }
- }
-
- free(newPath);
-#endif
+ myfiles[fd].file = fcaseopen(filename, realmode);
if(myfiles[fd].file == nil)
return 0;
return fd;
diff --git a/src/core/Game.cpp b/src/core/Game.cpp
index 072bc7be..39bbd364 100644
--- a/src/core/Game.cpp
+++ b/src/core/Game.cpp
@@ -88,6 +88,7 @@
#include "Zones.h"
#include "Occlusion.h"
#include "debugmenu.h"
+#include "Ropes.h"
eLevelName CGame::currLevel;
int32 CGame::currArea;
@@ -403,9 +404,11 @@ bool CGame::Initialise(const char* datFile)
CRubbish::Init();
CClouds::Init();
CSpecialFX::Init();
+ CRopes::Init();
CWaterCannons::Init();
CBridge::Init();
CGarages::Init();
+ LoadingScreen("Loading the Game", "Position dynamic objects", nil);
LoadingScreen("Loading the Game", "Initialise vehicle paths", nil);
CTrain::InitTrains();
CPlane::InitPlanes();
@@ -416,6 +419,7 @@ bool CGame::Initialise(const char* datFile)
if ( !TheMemoryCard.m_bWantToLoad )
{
#endif
+ LoadingScreen("Loading the Game", "Start script", nil);
CTheScripts::StartTestScript();
CTheScripts::Process();
TheCamera.Process();
@@ -426,6 +430,9 @@ bool CGame::Initialise(const char* datFile)
CCollision::ms_collisionInMemory = currLevel;
for (int i = 0; i < MAX_PADS; i++)
CPad::GetPad(i)->Clear(true);
+ // TODO(Miami)
+ // DMAudio.SetStartingTrackPositions(1);
+ DMAudio.ChangeMusicMode(MUSICMODE_GAME);
return true;
}
@@ -546,6 +553,7 @@ void CGame::ReInitGameObjectVariables(void)
CRemote::Init();
#endif
CSpecialFX::Init();
+ CRopes::Init();
CWaterCannons::Init();
CParticle::ReloadConfig();
@@ -718,6 +726,7 @@ void CGame::Process(void)
CGarages::Update();
CRubbish::Update();
CSpecialFX::Update();
+ CRopes::Update();
CTimeCycle::Update();
if (CReplay::ShouldStandardCameraBeProcessed())
TheCamera.Process();
diff --git a/src/core/Ropes.cpp b/src/core/Ropes.cpp
index 4c57f190..dbae9ee3 100644
--- a/src/core/Ropes.cpp
+++ b/src/core/Ropes.cpp
@@ -31,14 +31,14 @@ CRope::Update(void)
for(i = 1; i < ARRAY_SIZE(m_pos); i++){
CVector prevPos = m_pos[i];
m_pos[i] += m_speed[i]*step*CTimer::GetTimeStep();
- m_pos[0].z -= 0.05f*CTimer::GetTimeStep();
+ m_pos[i].z -= 0.05f*CTimer::GetTimeStep();
CVector dist = m_pos[i] - m_pos[i-1];
- m_pos[i] = m_pos[i-1] + dist/dist.Magnitude()*0.625f;
+ m_pos[i] = m_pos[i-1] + (0.625f/dist.Magnitude())*dist;
m_speed[i] = (m_pos[i] - prevPos)/CTimer::GetTimeStep();
}
if(!m_bWasRegistered && m_pos[0].z < 0.0f)
m_bActive = false;
- m_bWasRegistered = true;
+ m_bWasRegistered = false;
}
void
@@ -60,7 +60,11 @@ CRope::Render(void)
RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
if(RwIm3DTransform(TempBufferRenderVertices, ARRAY_SIZE(m_pos), nil, rwIM3D_VERTEXXYZ|rwIM3D_VERTEXRGBA)){
+#ifdef FIX_BUGS
RwIm3DRenderIndexedPrimitive(rwPRIMTYPELINELIST, RopeIndices, 2*(ARRAY_SIZE(m_pos)-1));
+#else
+ RwIm3DRenderIndexedPrimitive(rwPRIMTYPEPOLYLINE, RopeIndices, 2*(ARRAY_SIZE(m_pos)-1));
+#endif
RwIm3DEnd();
}
}
@@ -159,7 +163,7 @@ CRopes::CreateRopeWithSwatComingDown(CVector pos)
if(!CStreaming::HasModelLoaded(MI_SWAT) || !RegisterRope(ropeId+100, pos, true))
return false;
- CCopPed *swat = (CCopPed*)CPopulation::AddPed(PEDTYPE_COP, COP_ARMY, pos);
+ CCopPed *swat = (CCopPed*)CPopulation::AddPed(PEDTYPE_COP, COP_HELI_SWAT, pos);
swat->bUsesCollision = false;
swat->m_pRopeEntity = (CEntity*)1;
swat->m_nRopeID = 100 + ropeId;
diff --git a/src/core/main.cpp b/src/core/main.cpp
index 0ea95c6e..d4ef8c4b 100644
--- a/src/core/main.cpp
+++ b/src/core/main.cpp
@@ -62,6 +62,7 @@
#include "SceneEdit.h"
#include "debugmenu.h"
#include "Occlusion.h"
+#include "Ropes.h"
GlobalScene Scene;
@@ -873,6 +874,7 @@ RenderEffects(void)
CGlass::Render();
CWaterCannons::Render();
CSpecialFX::Render();
+ CRopes::Render();
CShadows::RenderStaticShadows();
CShadows::RenderStoredShadows();
CSkidmarks::Render();
diff --git a/src/fakerw/fake.cpp b/src/fakerw/fake.cpp
index 0bc69abe..7139292e 100644
--- a/src/fakerw/fake.cpp
+++ b/src/fakerw/fake.cpp
@@ -376,23 +376,19 @@ RwStream *RwStreamOpen(RwStreamType type, RwStreamAccessType accessType, const v
file = rwNewT(StreamFile, 1, 0);
memcpy(file, &fakefile, sizeof(StreamFile));
#ifndef _WIN32
- // Be case-insensitive and fix backslashes (from https://github.com/OneSadCookie/fcaseopen/)
- FILE* first = fopen((char*)pData, "r");
- char *r;
- if (!first) {
- r = (char*)alloca(strlen((char*)pData) + 2);
- // Use default path(and pass error handling to librw) if we can't find any match
- if (!casepath((char*)pData, r))
- r = (char*)pData;
+ char *r = casepath((char*)pData);
+ if (r) {
+ if (file->open((char*)r, mode)) {
+ free(r);
+ return file;
+ }
+ free(r);
} else
- fclose(first);
-
- if(file->open((char*)r, mode))
- return file;
-#else
- if(file->open((char*)pData, mode))
- return file;
#endif
+ {
+ if (file->open((char*)pData, mode))
+ return file;
+ }
rwFree(file);
return nil;
}
@@ -856,12 +852,41 @@ RpSkin *RpSkinGeometryGetSkin( RpGeometry *geometry ) { return Skin::get(geometr
RpAtomic *RpSkinAtomicSetHAnimHierarchy( RpAtomic *atomic, RpHAnimHierarchy *hierarchy ) { Skin::setHierarchy(atomic, hierarchy); return atomic; }
RpHAnimHierarchy *RpSkinAtomicGetHAnimHierarchy( const RpAtomic *atomic ) { return Skin::getHierarchy(atomic); }
+RwImage *
+RtBMPImageWrite(RwImage *image, const RwChar *imageName)
+{
+#ifndef _WIN32
+ char *r = casepath(imageName);
+ if (r) {
+ rw::writeBMP(image, r);
+ free(r);
+ } else {
+ rw::writeBMP(image, imageName);
+ }
+
+#else
+ rw::writeBMP(image, imageName);
+#endif
+ return image;
+}
+RwImage *
+RtBMPImageRead(const RwChar *imageName)
+{
+#ifndef _WIN32
+ RwImage *image;
+ char *r = casepath(imageName);
+ if (r) {
+ image = rw::readBMP(r);
+ free(r);
+ } else {
+ image = rw::readBMP(imageName);
+ }
+ return image;
-
-
-
-RwImage *RtBMPImageWrite(RwImage * image, const RwChar * imageName) { rw::writeBMP(image, imageName); return image; }
-RwImage *RtBMPImageRead(const RwChar * imageName) { return rw::readBMP(imageName); }
+#else
+ return rw::readBMP(imageName);
+#endif
+}
#include "rtquat.h"
diff --git a/src/peds/CopPed.cpp b/src/peds/CopPed.cpp
index dd122477..6c9eb276 100644
--- a/src/peds/CopPed.cpp
+++ b/src/peds/CopPed.cpp
@@ -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,18 +80,21 @@ 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
m_bThrowsSpikeTrap = false;
- field_5FF = 0;
m_pRopeEntity = nil;
m_fAbseilPos = 0.0f;
- m_bBeatingSuspect = false;
- m_pPointGunAt = nil;
+ m_nHassleTimer = 0;
+ field_61C = 0;
field_624 = 0;
+ if (m_pPointGunAt)
+ m_pPointGunAt->CleanUpOldReference((CEntity**)&m_pPointGunAt);
+ m_pPointGunAt = nil;
}
CCopPed::~CCopPed()
@@ -96,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;
@@ -128,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;
@@ -148,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)
{
@@ -190,6 +190,7 @@ CCopPed::ClearPursuit(void)
bNotAllowedToDuck = false;
bKindaStayInSamePlace = false;
m_bStopAndShootDisabledZone = false;
+ field_601 = false;
m_bZoneDisabled = false;
ClearObjective();
if (IsPedInControl()) {
@@ -207,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;
@@ -236,6 +241,7 @@ CCopPed::SetPursuit(bool ignoreCopLimit)
}
}
+// --MIAMI: Done
void
CCopPed::ArrestPlayer(void)
{
@@ -301,6 +307,7 @@ CCopPed::ScanForCrimes(void)
}
}
+// --MIAMI: Done
void
CCopPed::CopAI(void)
{
@@ -318,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;
@@ -349,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;
@@ -391,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;
@@ -406,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) {
@@ -435,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);
}
@@ -478,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);
@@ -487,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);
@@ -494,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);
}
@@ -524,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) {
@@ -547,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 {
@@ -563,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;
@@ -606,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();
@@ -671,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);
@@ -718,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);
-
- // 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_pPointGunAt)
+ Say(SOUND_PED_COP_UNK_129);
+
+ 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;
}
- } else {
- FlagToDestroyWhenNextProcessed();
- dontShoot = true;
- }
- if (!dontShoot) {
- bStopAndShoot = true;
- bKindaStayInSamePlace = true;
- bIsPointingGunAt = true;
- SetAttack(m_pedInObjective);
+ 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 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;
+ }
+} \ No newline at end of file
diff --git a/src/peds/CopPed.h b/src/peds/CopPed.h
index e48984ff..edec145e 100644
--- a/src/peds/CopPed.h
+++ b/src/peds/CopPed.h
@@ -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,7 +19,6 @@ 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
@@ -28,6 +28,8 @@ public:
bool m_bThrowsSpikeTrap;
CEntity *m_pRopeEntity; // CHeli or 1
uintptr m_nRopeID;
+ uint32 m_nHassleTimer;
+ uint32 field_61C;
int32 field_624;
int8 field_628;
@@ -41,6 +43,7 @@ public:
void ArrestPlayer(void);
void ScanForCrimes(void);
void CopAI(void);
+ void ProcessHeliSwat(void);
};
#ifndef PED_SKIN
diff --git a/src/peds/PedPlacement.cpp b/src/peds/PedPlacement.cpp
index 6011ce44..8012650a 100644
--- a/src/peds/PedPlacement.cpp
+++ b/src/peds/PedPlacement.cpp
@@ -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*
diff --git a/src/peds/PedPlacement.h b/src/peds/PedPlacement.h
index 5b8354d4..d1b0cd16 100644
--- a/src/peds/PedPlacement.h
+++ b/src/peds/PedPlacement.h
@@ -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);
}; \ No newline at end of file
diff --git a/src/skel/crossplatform.cpp b/src/skel/crossplatform.cpp
index 40f4f053..6188992d 100644
--- a/src/skel/crossplatform.cpp
+++ b/src/skel/crossplatform.cpp
@@ -87,7 +87,7 @@ void FileTimeToSystemTime(time_t* writeTime, SYSTEMTIME* out) {
// Funcs/features from Windows that we need on other platforms
#ifndef _WIN32
char *strupr(char *s) {
- char* tmp = s;
+ char* tmp = s;
for (;*tmp;++tmp) {
*tmp = toupper((unsigned char) *tmp);
@@ -96,7 +96,7 @@ char *strupr(char *s) {
return s;
}
char *strlwr(char *s) {
- char* tmp = s;
+ char* tmp = s;
for (;*tmp;++tmp) {
*tmp = tolower((unsigned char) *tmp);
@@ -116,86 +116,117 @@ char *trim(char *s) {
return s;
}
+FILE* _fcaseopen(char const* filename, char const* mode)
+{
+ FILE* result;
+ char* real = casepath(filename);
+ if (!real)
+ result = fopen(filename, mode);
+ else {
+ result = fopen(real, mode);
+ free(real);
+ }
+ return result;
+}
+
// Case-insensitivity on linux (from https://github.com/OneSadCookie/fcaseopen)
-// r must have strlen(path) + 2 bytes
-int casepath(char const *path, char *r)
+// Returned string should freed manually (if exists)
+char* casepath(char const* path, bool checkPathFirst)
{
+ if (checkPathFirst && access(path, F_OK) != -1) {
+ // File path is correct
+ return nil;
+ }
+
size_t l = strlen(path);
- char *p = (char*)alloca(l + 1);
+ char* p = (char*)alloca(l + 1);
+ char* out = (char*)malloc(l + 3); // for extra ./
strcpy(p, path);
- // my addon: change \'s with /
- char *nextBs;
- while(nextBs = strstr(p, "\\")){
- *nextBs = '/';
- }
-
- // my addon: linux doesn't handle filenames with spaces at the end nicely
- p = trim(p);
+ // my addon: linux doesn't handle filenames with spaces at the end nicely
+ p = trim(p);
size_t rl = 0;
-
- DIR *d;
- if (p[0] == '/')
+
+ DIR* d;
+ if (p[0] == '/' || p[0] == '\\')
{
d = opendir("/");
- p = p + 1;
}
else
{
d = opendir(".");
- r[0] = '.';
- r[1] = 0;
+ out[0] = '.';
+ out[1] = 0;
rl = 1;
}
-
- int last = 0;
- char *c = strsep(&p, "/");
- while (c)
+
+ bool cantProceed = false; // just convert slashes in what's left in string, not case sensitivity
+ bool mayBeTrailingSlash = false;
+ char* c;
+ while (c = strsep(&p, "/\\"))
{
- if (!d)
+ // May be trailing slash(allow), slash at the start(avoid), or multiple slashes(avoid)
+ if (*c == '\0')
{
- return 0;
+ mayBeTrailingSlash = true;
+ continue;
+ } else {
+ mayBeTrailingSlash = false;
}
-
- if (last)
+
+ out[rl] = '/';
+ rl += 1;
+ out[rl] = 0;
+
+ if (cantProceed)
{
- closedir(d);
- return 0;
+ strcpy(out + rl, c);
+ rl += strlen(c);
+ continue;
}
-
- r[rl] = '/';
- rl += 1;
- r[rl] = 0;
-
- struct dirent *e = readdir(d);
- while (e)
+
+ struct dirent* e;
+ while (e = readdir(d))
{
if (strcasecmp(c, e->d_name) == 0)
{
- strcpy(r + rl, e->d_name);
- rl += strlen(e->d_name);
+ strcpy(out + rl, e->d_name);
+ int reportedLen = (int)strlen(e->d_name);
+ rl += reportedLen;
+ assert(reportedLen == strlen(c) && "casepath: This is not good at all");
closedir(d);
- d = opendir(r);
-
+ d = opendir(out);
+
+ // Either it wasn't a folder, or permission error, I/O error etc.
+ if (!d) {
+ cantProceed = true;
+ }
+
break;
}
-
- e = readdir(d);
}
-
+
if (!e)
{
- strcpy(r + rl, c);
+ printf("casepath couldn't find dir/file \"%s\", full path was %s\n", c, path);
+ // No match, add original name and continue converting further slashes.
+ strcpy(out + rl, c);
rl += strlen(c);
- last = 1;
+ cantProceed = true;
}
-
- c = strsep(&p, "/");
}
-
+
if (d) closedir(d);
- return 1;
+ if (mayBeTrailingSlash) {
+ out[rl] = '/'; rl += 1;
+ out[rl] = '\0';
+ }
+
+ if (rl > l + 2) {
+ printf("\n\ncasepath: Corrected path length is longer then original+2:\n\tOriginal: %s (%d chars)\n\tCorrected: %s (%d chars)\n\n", path, l, out, rl);
+ }
+ return out;
}
#endif
diff --git a/src/skel/crossplatform.h b/src/skel/crossplatform.h
index 268eedff..69600385 100644
--- a/src/skel/crossplatform.h
+++ b/src/skel/crossplatform.h
@@ -23,7 +23,7 @@ enum eWinVersion
#include "win.h"
#endif
extern DWORD _dwOperatingSystemVersion;
-
+#define fcaseopen fopen
#else
char *strupr(char *str);
char *strlwr(char *str);
@@ -43,7 +43,9 @@ enum {
};
extern long _dwOperatingSystemVersion;
-int casepath(char const *path, char *r);
+char *casepath(char const *path, bool checkPathFirst = true);
+FILE *_fcaseopen(char const *filename, char const *mode);
+#define fcaseopen _fcaseopen
#endif
#ifdef RW_GL3
diff --git a/src/vehicles/Heli.cpp b/src/vehicles/Heli.cpp
index 408fb8f9..00bb0023 100644
--- a/src/vehicles/Heli.cpp
+++ b/src/vehicles/Heli.cpp
@@ -731,12 +731,12 @@ CHeli::SendDownSwat(void)
float groundZ = CWorld::FindGroundZFor3DCoord(pos.x, pos.y, pos.z, nil);
if(Abs(FindPlayerCoors().z - groundZ) < 2.5f && CRopes::RegisterRope((uintptr)this + m_numSwat-1, pos, false)){
- CCopPed *swat = (CCopPed*)CPopulation::AddPed(PEDTYPE_COP, COP_ARMY, pos);
+ CCopPed *swat = (CCopPed*)CPopulation::AddPed(PEDTYPE_COP, COP_HELI_SWAT, pos);
swat->bUsesCollision = false;
swat->m_pRopeEntity = this;
RegisterReference(&swat->m_pRopeEntity);
m_numSwat--;
- swat->m_nRopeID = m_numSwat;
+ swat->m_nRopeID = (uintptr)this + m_numSwat;
m_aSwatState[m_numSwat] = 255;
CAnimManager::BlendAnimation(swat->GetClump(), ASSOCGRP_STD, ANIM_ABSEIL, 4.0f);
return true;