diff options
-rw-r--r-- | src/FileLoader.cpp | 2 | ||||
-rw-r--r-- | src/General.h | 42 | ||||
-rw-r--r-- | src/Streaming.cpp | 441 | ||||
-rw-r--r-- | src/Streaming.h | 22 | ||||
-rw-r--r-- | src/World.h | 24 | ||||
-rw-r--r-- | src/animation/AnimManager.h | 2 | ||||
-rw-r--r-- | src/audio/DMAudio.cpp | 1 | ||||
-rw-r--r-- | src/audio/DMAudio.h | 7 | ||||
-rw-r--r-- | src/entities/Ped.cpp | 443 | ||||
-rw-r--r-- | src/entities/Ped.h | 33 | ||||
-rw-r--r-- | src/entities/PedIK.cpp | 36 | ||||
-rw-r--r-- | src/entities/PedIK.h | 14 | ||||
-rw-r--r-- | src/patcher.h | 9 | ||||
-rw-r--r-- | src/weapons/Weapon.cpp | 2 | ||||
-rw-r--r-- | src/weapons/Weapon.h | 24 | ||||
-rw-r--r-- | src/weapons/WeaponInfo.cpp | 14 | ||||
-rw-r--r-- | src/weapons/WeaponInfo.h | 45 |
17 files changed, 1120 insertions, 41 deletions
diff --git a/src/FileLoader.cpp b/src/FileLoader.cpp index 1295177d..f50638b4 100644 --- a/src/FileLoader.cpp +++ b/src/FileLoader.cpp @@ -1082,7 +1082,7 @@ CFileLoader::LoadCullZone(const char *line) &minx, &miny, &minz, &maxx, &maxy, &maxz, &flags, &wantedLevelDrop); - CCullZones::AddCullZone(pos, minx, maxx, miny, maxy, minz, maxy, flags, wantedLevelDrop); + CCullZones::AddCullZone(pos, minx, maxx, miny, maxy, minz, maxz, flags, wantedLevelDrop); } // unused diff --git a/src/General.h b/src/General.h index 41bdf5d7..fd78edaa 100644 --- a/src/General.h +++ b/src/General.h @@ -40,6 +40,48 @@ public: } } + static float LimitRadianAngle(float angle) + { + if (angle < -25.0f) + angle = -25.0f; + + if (angle > 25.0f) + angle = 25.0f; + + float result = angle; + + while (result >= PI) { + result -= 2 * PI; + } + + while (result < -PI) { + result += 2 * PI; + } + + return result; + } + + static float GetRadianAngleBetweenPoints(float x1, float y1, float x2, float y2) + { + float x = x2 - x1; + float y = y2 - y1; + + if (y == 0.0f) + y = 0.0001f; + + if (x > 0.0f) { + if (y > 0.0f) + return 2 * PI - atan2(x / y, 1.0f); + else + return -atan2(x / y, 1.0f); + } else { + if (y > 0.0f) + return -(PI + atan2(x / y, 1.0f)); + else + return -atan2(x / y, 1.0f); + } + } + // not too sure about all these... static uint16 GetRandomNumber(void) { return myrand() & 0xFFFF; } diff --git a/src/Streaming.cpp b/src/Streaming.cpp index fa0710ea..1a5800e2 100644 --- a/src/Streaming.cpp +++ b/src/Streaming.cpp @@ -2,11 +2,14 @@ #include "patcher.h" #include "ModelInfo.h" #include "TxdStore.h" +#include "ModelIndices.h" #include "Pools.h" #include "Directory.h" #include "RwHelper.h" +#include "World.h" #include "Entity.h" #include "FileMgr.h" +#include "FileLoader.h" #include "CdStream.h" #include "Streaming.h" @@ -56,11 +59,10 @@ int32 &islandLODcomSub = *(int32*)0x6212D0; int32 &islandLODsubInd = *(int32*)0x6212D4; int32 &islandLODsubCom = *(int32*)0x6212D8; - -WRAPPER void CStreaming::RemoveModel(int32 id) { EAXJMP(0x408830); } -WRAPPER void CStreaming::RequestModel(int32 model, int32 flags) { EAXJMP(0x407EA0); } - WRAPPER void CStreaming::MakeSpaceFor(int32 size) { EAXJMP(0x409B70); } +WRAPPER bool CStreaming::IsTxdUsedByRequestedModels(int32 txdId) { EAXJMP(0x4094C0); } +WRAPPER bool CStreaming::AddToLoadedVehiclesList(int32 modelId) { EAXJMP(0x40B060); } + void CStreaming::Init(void) @@ -279,6 +281,428 @@ CStreaming::LoadCdDirectory(const char *dirname, int n) CFileMgr::CloseFile(fd); } +bool +CStreaming::ConvertBufferToObject(int8 *buf, int32 streamId) +{ + RwMemory mem; + RwStream *stream; + int cdsize; + uint32 startTime, endTime, timeDiff; + CBaseModelInfo *mi; + bool success; + + startTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); + + cdsize = ms_aInfoForModel[streamId].GetCdSize(); + mem.start = (uint8*)buf; + mem.length = cdsize * CDSTREAM_SECTOR_SIZE; + stream = RwStreamOpen(rwSTREAMMEMORY, rwSTREAMREAD, &mem); + + if(streamId < STREAM_OFFSET_TXD){ + // Model + mi = CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL); + + // Txd has to be loaded + if(CTxdStore::GetSlot(mi->GetTxdSlot())->texDict == nil){ + debug("failed to load %s because TXD %s is not in memory\n", mi->GetName(), CTxdStore::GetTxdName(mi->GetTxdSlot())); + RemoveModel(streamId); + RemoveModel(mi->GetTxdSlot() + STREAM_OFFSET_TXD); + // re-request + RequestModel(streamId, ms_aInfoForModel[streamId].m_flags); + RwStreamClose(stream, &mem); + return false; + } + + // Set Txd to use + CTxdStore::AddRef(mi->GetTxdSlot()); + CTxdStore::SetCurrentTxd(mi->GetTxdSlot()); + + if(mi->IsSimple()){ + success = CFileLoader::LoadAtomicFile(stream, streamId - STREAM_OFFSET_MODEL); + }else if(mi->m_type == MITYPE_VEHICLE){ + // load vehicles in two parts + CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL)->AddRef(); + success = CFileLoader::StartLoadClumpFile(stream, streamId - STREAM_OFFSET_MODEL); + if(success) + ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_STARTED; + }else{ + success = CFileLoader::LoadClumpFile(stream, streamId - STREAM_OFFSET_MODEL); + } + UpdateMemoryUsed(); + + // Txd no longer needed unless we only read part of the file + if(ms_aInfoForModel[streamId].m_loadState != STREAMSTATE_STARTED) + CTxdStore::RemoveRefWithoutDelete(mi->GetTxdSlot()); + + if(!success){ + debug("Failed to load %s\n", CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL)->GetName()); + RemoveModel(streamId); + // re-request + RequestModel(streamId, ms_aInfoForModel[streamId].m_flags); + RwStreamClose(stream, &mem); + return false; + } + }else{ + // Txd + assert(streamId < NUMSTREAMINFO); + if((ms_aInfoForModel[streamId].m_flags & STREAMFLAGS_KEEP_IN_MEMORY) == 0 && + !IsTxdUsedByRequestedModels(streamId - STREAM_OFFSET_TXD)){ + RemoveModel(streamId); + RwStreamClose(stream, &mem); + return false; + } + + if(ms_bLoadingBigModel || cdsize > 200){ + success = CTxdStore::StartLoadTxd(streamId - STREAM_OFFSET_TXD, stream); + if(success) + ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_STARTED; + }else + success = CTxdStore::LoadTxd(streamId - STREAM_OFFSET_TXD, stream); + UpdateMemoryUsed(); + + if(!success){ + debug("Failed to load %s.txd\n", CTxdStore::GetTxdName(streamId - STREAM_OFFSET_TXD)); + RemoveModel(streamId); + // re-request + RequestModel(streamId, ms_aInfoForModel[streamId].m_flags); + RwStreamClose(stream, &mem); + return false; + } + } + + RwStreamClose(stream, &mem); + + // We shouldn't even end up here unless load was successful + if(!success){ + RequestModel(streamId, ms_aInfoForModel[streamId].m_flags); + if(streamId < STREAM_OFFSET_TXD) + debug("Failed to load %s.dff\n", mi->GetName()); + else + debug("Failed to load %s.txd\n", CTxdStore::GetTxdName(streamId - STREAM_OFFSET_TXD)); + return false; + } + + if(streamId < STREAM_OFFSET_TXD){ + // Model + // Vehicles and Peds not in loaded list + if(mi->m_type != MITYPE_VEHICLE && mi->m_type != MITYPE_PED){ + CSimpleModelInfo *smi = (CSimpleModelInfo*)mi; + + // Set fading for some objects + if(mi->IsSimple() && !smi->m_isBigBuilding){ + if(ms_aInfoForModel[streamId].m_flags & STREAMFLAGS_NOFADE) + smi->m_alpha = 255; + else + smi->m_alpha = 0; + } + + if((ms_aInfoForModel[streamId].m_flags & STREAMFLAGS_NOT_IN_LIST) == 0) + ms_aInfoForModel[streamId].AddToList(&ms_startLoadedList); + } + }else{ + // Txd + if((ms_aInfoForModel[streamId].m_flags & STREAMFLAGS_NOT_IN_LIST) == 0) + ms_aInfoForModel[streamId].AddToList(&ms_startLoadedList); + } + + // Mark objects as loaded + if(ms_aInfoForModel[streamId].m_loadState != STREAMSTATE_STARTED){ + ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_LOADED; + ms_memoryUsed += ms_aInfoForModel[streamId].GetCdSize() * CDSTREAM_SECTOR_SIZE; + } + + endTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); + timeDiff = endTime - startTime; + if(timeDiff > 5){ + if(streamId < STREAM_OFFSET_TXD) + debug("model %s took %d ms\n", CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL)->GetName(), timeDiff); + else + debug("txd %s took %d ms\n", CTxdStore::GetTxdName(streamId - STREAM_OFFSET_TXD), timeDiff); + } + + return true; +} + + +bool +CStreaming::FinishLoadingLargeFile(int8 *buf, int32 streamId) +{ + RwMemory mem; + RwStream *stream; + uint32 startTime, endTime, timeDiff; + CBaseModelInfo *mi; + bool success; + + startTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); + + if(ms_aInfoForModel[streamId].m_loadState != STREAMSTATE_STARTED){ + if(streamId < STREAM_OFFSET_TXD) + CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL)->RemoveRef(); + return false; + } + + mem.start = (uint8*)buf; + mem.length = ms_aInfoForModel[streamId].GetCdSize() * CDSTREAM_SECTOR_SIZE; + stream = RwStreamOpen(rwSTREAMMEMORY, rwSTREAMREAD, &mem); + + if(streamId < STREAM_OFFSET_TXD){ + // Model + mi = CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL); + CTxdStore::SetCurrentTxd(mi->GetTxdSlot()); + success = CFileLoader::FinishLoadClumpFile(stream, streamId); + if(success) + success = AddToLoadedVehiclesList(streamId); + mi->RemoveRef(); + CTxdStore::RemoveRefWithoutDelete(mi->GetTxdSlot()); + }else{ + // Txd + CTxdStore::AddRef(streamId - STREAM_OFFSET_TXD); + success = CTxdStore::FinishLoadTxd(streamId - STREAM_OFFSET_TXD, stream); + CTxdStore::RemoveRefWithoutDelete(streamId - STREAM_OFFSET_TXD); + } + + RwStreamClose(stream, &mem); + ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_LOADED; + ms_memoryUsed += ms_aInfoForModel[streamId].GetCdSize() * CDSTREAM_SECTOR_SIZE; + + if(!success){ + RemoveModel(streamId); + // re-request + RequestModel(streamId, ms_aInfoForModel[streamId].m_flags); + UpdateMemoryUsed(); + return false; + } + + UpdateMemoryUsed(); + + endTime = CTimer::GetCurrentTimeInCycles() / CTimer::GetCyclesPerMillisecond(); + timeDiff = endTime - startTime; + if(timeDiff > 5){ + if(streamId < STREAM_OFFSET_TXD) + debug("finish model %s took %d ms\n", CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL)->GetName(), timeDiff); + else + debug("finish txd %s took %d ms\n", CTxdStore::GetTxdName(streamId - STREAM_OFFSET_TXD), timeDiff); + } + + return true; +} + +void +CStreaming::RequestModel(int32 id, int32 flags) +{ + CSimpleModelInfo *mi; + + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_INQUEUE){ + // updgrade to priority + if(flags & STREAMFLAGS_PRIORITY && (ms_aInfoForModel[id].m_flags & STREAMFLAGS_PRIORITY) == 0){ + ms_numPriorityRequests++; + ms_aInfoForModel[id].m_flags |= STREAMFLAGS_PRIORITY; + } + }else if(ms_aInfoForModel[id].m_loadState != STREAMSTATE_NOTLOADED){ + flags &= ~STREAMFLAGS_PRIORITY; + } + ms_aInfoForModel[id].m_flags |= flags; + + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_LOADED){ + // Already loaded, only check changed flags + + if(ms_aInfoForModel[id].m_flags & STREAMFLAGS_NOFADE && id < STREAM_OFFSET_TXD){ + mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(id - STREAM_OFFSET_MODEL); + if(mi->IsSimple()) + mi->m_alpha = 255; + } + + // reinsert into list + if(ms_aInfoForModel[id].m_next){ + ms_aInfoForModel[id].RemoveFromList(); + if((ms_aInfoForModel[id].m_flags & STREAMFLAGS_NOT_IN_LIST) == 0) + ms_aInfoForModel[id].AddToList(&ms_startLoadedList); + } + }else if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_NOTLOADED || + ms_aInfoForModel[id].m_loadState == STREAMSTATE_LOADED){ // how can this be true again? + + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_NOTLOADED){ + if(id < STREAM_OFFSET_TXD) + RequestTxd(CModelInfo::GetModelInfo(id - STREAM_OFFSET_MODEL)->GetTxdSlot(), flags); + ms_aInfoForModel[id].AddToList(&ms_startRequestedList); + ms_numModelsRequested++; + if(flags & STREAMFLAGS_PRIORITY) + ms_numPriorityRequests++; + } + + ms_aInfoForModel[id].m_loadState = STREAMSTATE_INQUEUE; + ms_aInfoForModel[id].m_flags = flags; + } +} + +void +CStreaming::RequestSubway(void) +{ + RequestModel(MI_SUBWAY1, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY2, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY3, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY4, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY5, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY6, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY7, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY8, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY9, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY10, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY11, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY12, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY13, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY14, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY15, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY16, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY17, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBWAY18, STREAMFLAGS_NOFADE); + + switch(CGame::currLevel){ + case LEVEL_INDUSTRIAL: + RequestModel(MI_SUBPLATFORM_IND, STREAMFLAGS_NOFADE); + break; + case LEVEL_COMMERCIAL: + if(FindPlayerTrain()->GetPosition().y < -700.0f){ + RequestModel(MI_SUBPLATFORM_COMS, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBPLATFORM_COMS2, STREAMFLAGS_NOFADE); + }else{ + RequestModel(MI_SUBPLATFORM_COMN, STREAMFLAGS_NOFADE); + } + break; + case LEVEL_SUBURBAN: + RequestModel(MI_SUBPLATFORM_SUB, STREAMFLAGS_NOFADE); + RequestModel(MI_SUBPLATFORM_SUB2, STREAMFLAGS_NOFADE); + break; + } +} + +void +CStreaming::RequestBigBuildings(eLevelName level) +{ + int i, n; + CBuilding *b; + + n = CPools::GetBuildingPool()->GetSize(); + for(i = 0; i < n; i++){ + b = CPools::GetBuildingPool()->GetSlot(i); + if(b && b->bIsBIGBuilding && b->m_level == level) + RequestModel(b->GetModelIndex(), STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_PRIORITY); + } + RequestIslands(level); + ms_hasLoadedLODs = false; +} + +void +CStreaming::RequestIslands(eLevelName level) +{ + switch(level){ + case LEVEL_INDUSTRIAL: + RequestModel(islandLODcomInd, STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_PRIORITY); + RequestModel(islandLODsubInd, STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_PRIORITY); + break; + case LEVEL_COMMERCIAL: + RequestModel(islandLODindust, STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_PRIORITY); + RequestModel(islandLODsubCom, STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_PRIORITY); + break; + case LEVEL_SUBURBAN: + RequestModel(islandLODindust, STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_PRIORITY); + RequestModel(islandLODcomSub, STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_PRIORITY); + break; + } +} + +void +CStreaming::RequestSpecialModel(int32 modelId, const char *modelName, int32 flags) +{ + CBaseModelInfo *mi; + int txdId; + char oldName[48]; + uint32 pos, size; + + mi = CModelInfo::GetModelInfo(modelId); + if(strcmp(mi->GetName(), modelName) == 0){ + // Already have the correct name, just request it + RequestModel(modelId, flags); + return; + } + + strcpy(oldName, mi->GetName()); + mi->SetName(modelName); + + // What exactly is going on here? + if(CModelInfo::GetModelInfo(oldName, nil)){ + txdId = CTxdStore::FindTxdSlot(oldName); + if(txdId != -1 && CTxdStore::GetSlot(txdId)->texDict){ + CTxdStore::AddRef(txdId); + RemoveModel(modelId); + CTxdStore::RemoveRefWithoutDelete(txdId); + }else + RemoveModel(modelId); + }else + RemoveModel(modelId); + + ms_pExtraObjectsDir->FindItem(modelName, pos, size); + mi->ClearTexDictionary(); + if(CTxdStore::FindTxdSlot(modelName) == -1) + mi->SetTexDictionary("generic"); + else + mi->SetTexDictionary(modelName); + ms_aInfoForModel[modelId].SetCdPosnAndSize(pos, size); + RequestModel(modelId, flags); +} + +void +CStreaming::RequestSpecialChar(int32 charId, const char *modelName, int32 flags) +{ + RequestSpecialModel(charId + MI_SPECIAL01, modelName, flags); +} + +void +CStreaming::RemoveModel(int32 id) +{ + int i; + + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_NOTLOADED) + return; + + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_LOADED){ + if(id < STREAM_OFFSET_TXD) + CModelInfo::GetModelInfo(id - STREAM_OFFSET_MODEL)->DeleteRwObject(); + else + CTxdStore::RemoveTxd(id - STREAM_OFFSET_TXD); + ms_memoryUsed -= ms_aInfoForModel[id].GetCdSize()*CDSTREAM_SECTOR_SIZE; + } + + if(ms_aInfoForModel[id].m_next){ + // Remove from list, model is neither loaded nor requested + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_INQUEUE){ + ms_numModelsRequested--; + if(ms_aInfoForModel[id].m_flags & STREAMFLAGS_PRIORITY){ + ms_aInfoForModel[id].m_flags &= ~STREAMFLAGS_PRIORITY; + ms_numPriorityRequests--; + } + } + ms_aInfoForModel[id].RemoveFromList(); + }else if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_READING){ + for(i = 0; i < 4; i++){ + if(ms_channel[0].modelIds[i] == id - STREAM_OFFSET_MODEL) + ms_channel[0].modelIds[i] = -1; + if(ms_channel[1].modelIds[i] == id - STREAM_OFFSET_MODEL) + ms_channel[1].modelIds[i] = -1; + } + } + + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_STARTED){ + if(id < STREAM_OFFSET_TXD) + RpClumpGtaCancelStream(); + else + CTxdStore::RemoveTxd(id - STREAM_OFFSET_TXD); + } + + ms_aInfoForModel[id].m_loadState = STREAMSTATE_NOTLOADED; +} + + void CStreaming::ImGonnaUseStreamingMemory(void) { @@ -338,6 +762,15 @@ STARTPATCHES InjectHook(0x406C80, CStreaming::Shutdown, PATCH_JUMP); InjectHook(0x406CC0, (void (*)(void))CStreaming::LoadCdDirectory, PATCH_JUMP); InjectHook(0x406DA0, (void (*)(const char*, int))CStreaming::LoadCdDirectory, PATCH_JUMP); + InjectHook(0x409740, CStreaming::ConvertBufferToObject, PATCH_JUMP); + InjectHook(0x409580, CStreaming::FinishLoadingLargeFile, PATCH_JUMP); + InjectHook(0x407EA0, CStreaming::RequestModel, PATCH_JUMP); + InjectHook(0x407FD0, CStreaming::RequestSubway, PATCH_JUMP); + InjectHook(0x408190, CStreaming::RequestBigBuildings, PATCH_JUMP); + InjectHook(0x408210, CStreaming::RequestIslands, PATCH_JUMP); + InjectHook(0x40A890, CStreaming::RequestSpecialModel, PATCH_JUMP); + InjectHook(0x40ADA0, CStreaming::RequestSpecialChar, PATCH_JUMP); + InjectHook(0x408830, CStreaming::RemoveModel, PATCH_JUMP); InjectHook(0x4063E0, &CStreamingInfo::GetCdPosnAndSize, PATCH_JUMP); InjectHook(0x406410, &CStreamingInfo::SetCdPosnAndSize, PATCH_JUMP); diff --git a/src/Streaming.h b/src/Streaming.h index f31faf9c..ef51f54a 100644 --- a/src/Streaming.h +++ b/src/Streaming.h @@ -13,6 +13,9 @@ enum StreamFlags STREAMFLAGS_DEPENDENCY = 0x04, STREAMFLAGS_PRIORITY = 0x08, STREAMFLAGS_NOFADE = 0x10, + + STREAMFLAGS_NOT_IN_LIST = STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_SCRIPTOWNED, + STREAMFLAGS_KEEP_IN_MEMORY = STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_SCRIPTOWNED|STREAMFLAGS_DEPENDENCY, }; enum StreamLoadState @@ -21,7 +24,7 @@ enum StreamLoadState STREAMSTATE_LOADED = 1, STREAMSTATE_INQUEUE = 2, STREAMSTATE_READING = 3, // what is this? - STREAMSTATE_BIGFILE = 4, + STREAMSTATE_STARTED = 4, // first part read }; enum ChannelState @@ -96,10 +99,21 @@ public: static void Init(void); static void Shutdown(void); static void LoadCdDirectory(void); - static void LoadCdDirectory(const char *dirname, int n); - - static void RemoveModel(int32 id); + static void LoadCdDirectory(const char *dirname, int32 n); + static bool ConvertBufferToObject(int8 *buf, int32 streamId); + static bool FinishLoadingLargeFile(int8 *buf, int32 streamId); static void RequestModel(int32 model, int32 flags); + static void RequestTxd(int32 txd, int32 flags) { RequestModel(txd + STREAM_OFFSET_TXD, flags); } + static void RequestSubway(void); + static void RequestBigBuildings(eLevelName level); + static void RequestIslands(eLevelName level); + static void RequestSpecialModel(int32 modelId, const char *modelName, int32 flags); + static void RequestSpecialChar(int32 charId, const char *modelName, int32 flags); + static void RemoveModel(int32 id); + + static bool IsTxdUsedByRequestedModels(int32 txdId); + static bool AddToLoadedVehiclesList(int32 modelId); + static void MakeSpaceFor(int32 size); static void ImGonnaUseStreamingMemory(void); static void IHaveUsedStreamingMemory(void); diff --git a/src/World.h b/src/World.h index 9a8a0c46..b099583b 100644 --- a/src/World.h +++ b/src/World.h @@ -7,8 +7,20 @@ /* Sectors span from -2000 to 2000 in x and y. * With 100x100 sectors, each is 40x40 units. */ -#define NUMSECTORS_X 100 -#define NUMSECTORS_Y 100 +#define SECTOR_SIZE_X (40.0f) +#define SECTOR_SIZE_Y (40.0f) + +#define NUMSECTORS_X (100) +#define NUMSECTORS_Y (100) + +#define WORLD_SIZE_X (NUMSECTORS_X * SECTOR_SIZE_X) +#define WORLD_SIZE_Y (NUMSECTORS_Y * SECTOR_SIZE_Y) + +#define WORLD_MIN_X (-2000.0f) +#define WORLD_MIN_Y (-2000.0f) + +#define WORLD_MAX_X (WORLD_MIN_X + WORLD_SIZE_X) +#define WORLD_MAX_Y (WORLD_MIN_Y + WORLD_SIZE_Y) enum { @@ -85,12 +97,12 @@ public: static float FindGroundZFor3DCoord(float x, float y, float z, bool *found); static float FindRoofZFor3DCoord(float x, float y, float z, bool *found); - static float GetSectorX(float f) { return ((f + 2000.0f)/40.0f); } - static float GetSectorY(float f) { return ((f + 2000.0f)/40.0f); } + static float GetSectorX(float f) { return ((f - WORLD_MIN_X)/SECTOR_SIZE_X); } + static float GetSectorY(float f) { return ((f - WORLD_MIN_Y)/SECTOR_SIZE_Y); } static int GetSectorIndexX(float f) { return (int)GetSectorX(f); } static int GetSectorIndexY(float f) { return (int)GetSectorY(f); } - static float GetWorldX(int x) { return x*40.0f - 2000.0f; } - static float GetWorldY(int y) { return y*40.0f - 2000.0f; } + static float GetWorldX(int x) { return x*SECTOR_SIZE_X + WORLD_MIN_X; } + static float GetWorldY(int y) { return y*SECTOR_SIZE_Y + WORLD_MIN_Y; } }; class CPed; diff --git a/src/animation/AnimManager.h b/src/animation/AnimManager.h index 1b881f34..3a7c057c 100644 --- a/src/animation/AnimManager.h +++ b/src/animation/AnimManager.h @@ -208,6 +208,8 @@ enum AnimationId ANIM_PHONE_IN, ANIM_PHONE_OUT, ANIM_PHONE_TALK, + + NUM_ANIMS }; class CAnimBlendAssociation; diff --git a/src/audio/DMAudio.cpp b/src/audio/DMAudio.cpp index 7f74fd8a..c320ea94 100644 --- a/src/audio/DMAudio.cpp +++ b/src/audio/DMAudio.cpp @@ -17,3 +17,4 @@ WRAPPER bool cDMAudio::CheckForAnAudioFileOnCD() { EAXJMP(0x57CA70); } WRAPPER void cDMAudio::ChangeMusicMode(uint8 mode) { EAXJMP(0x57CCF0); } WRAPPER void cDMAudio::PlayFrontEndSound(uint32, uint32) { EAXJMP(0x57CC20); } +WRAPPER void cDMAudio::PlayOneShot(int, uint16, float) { EAXJMP(0x57C840); } diff --git a/src/audio/DMAudio.h b/src/audio/DMAudio.h index 9006d248..3ebeaad8 100644 --- a/src/audio/DMAudio.h +++ b/src/audio/DMAudio.h @@ -51,9 +51,9 @@ enum eSound SOUND_WEAPON_BAT_ATTACK = 46, SOUND_WEAPON_SHOT_FIRED = 47, SOUND_WEAPON_RELOAD = 48, - SOUND_31 = 49, - SOUND_32 = 50, - SOUND_33 = 51, + SOUND_WEAPON_AK47_BULLET_ECHO = 49, + SOUND_WEAPON_UZI_BULLET_ECHO = 50, + SOUND_WEAPON_M16_BULLET_ECHO = 51, SOUND_WEAPON_FLAMETHROWER_FIRE = 52, SOUND_WEAPON_SNIPER_SHOT_NO_ZOOM = 53, SOUND_WEAPON_ROCKET_SHOT_NO_ZOOM = 54, @@ -188,5 +188,6 @@ public: bool CheckForAnAudioFileOnCD(void); void ChangeMusicMode(uint8 mode); void PlayFrontEndSound(uint32, uint32); + void PlayOneShot(int, uint16, float); }; extern cDMAudio &DMAudio; diff --git a/src/entities/Ped.cpp b/src/entities/Ped.cpp index 40d81cc8..81534915 100644 --- a/src/entities/Ped.cpp +++ b/src/entities/Ped.cpp @@ -5,8 +5,9 @@ #include "Stats.h" #include "World.h" #include "DMaudio.h" +#include "RpAnimBlend.h" #include "Ped.h" -#include "PedType.h" +#include "PlayerPed.h" #include "General.h" bool &CPed::bNastyLimbsCheat = *(bool*)0x95CD44; @@ -20,6 +21,10 @@ WRAPPER void CPed::KillPedWithCar(CVehicle *veh, float impulse) { EAXJMP(0x4EC43 WRAPPER void CPed::Say(uint16 audio) { EAXJMP(0x4E5A10); } WRAPPER void CPed::SetDie(AnimationId anim, float arg1, float arg2) { EAXJMP(0x4D37D0); } WRAPPER void CPed::SpawnFlyingComponent(int, int8) { EAXJMP(0x4EB060); } +WRAPPER void CPed::RestorePreviousState(void) { EAXJMP(0x4C5E30); } +WRAPPER void CPed::ClearAttack(void) { EAXJMP(0x4E6790); } +WRAPPER void CPed::SelectGunIfArmed(void) { EAXJMP(0x4DD920); } +WRAPPER void CPed::RemoveWeaponModel(int) { EAXJMP(0x4CF980); } static char ObjectiveText[34][28] = { "No Obj", @@ -176,6 +181,97 @@ static char WaitStateText[21][16] = { "Finish Flee", }; +static PedOnGroundState +CheckForPedsOnGroundToAttack(CPlayerPed *player, 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 < player->m_numNearPeds; currentPedId++) { + + currentPed = player->m_nearPeds[currentPedId]; + + CVector posDifference = currentPed->GetPosition() - player->GetPosition(); + distance = posDifference.Magnitude(); + + if (distance < 2.0f) { + angleToFace = CGeneral::GetRadianAngleBetweenPoints( + currentPed->GetPosition().x, currentPed->GetPosition().y, + player->GetPosition().x, player->GetPosition().y); + + angleToFace = CGeneral::LimitRadianAngle(angleToFace); + player->m_fRotationCur = CGeneral::LimitRadianAngle(player->m_fRotationCur); + + angleDiff = fabs(angleToFace - player->m_fRotationCur); + + if (angleDiff > PI) + angleDiff = 2 * PI - angleDiff; + + currentPedState = currentPed->m_nPedState; + + if (currentPedState == PED_FALL || currentPedState == PED_GETUP || currentPedState == PED_DIE || currentPedState == PED_DEAD) { + if (distance < 2.0f && angleDiff < DEGTORAD(65.0f)) { + if (currentPedState == PED_DEAD) { + foundDead = 1; + if (!deadPed) + deadPed = (CPed*)currentPed; + } else if (currentPed->IsPedHeadAbovePos(-0.6f)) { + foundOnTheFloor = 1; + if (!pedOnTheFloor) + pedOnTheFloor = (CPed*)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))) { + + if (angleDiff < DEGTORAD(75.0f)) { + foundBelow = 1; + if (!pedBelow) + pedBelow = (CPed*)currentPed; + } + } else { + foundBelow = 1; + pedBelow = (CPed*)currentPed; + break; + } + } + } + + 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_BELOW_PLAYER; + } else { + currentPed = nil; + stateToReturn = NO_PED; + } + + if (pedOnGround) + * pedOnGround = (CPed*)currentPed; + + return stateToReturn; +} + bool CPed::IsPlayer(void) { @@ -195,7 +291,7 @@ CPed::UseGroundColModel(void) void CPed::AddWeaponModel(int id) { - RpAtomic* atm; + RpAtomic *atm; if (id != -1) { atm = (RpAtomic*)CModelInfo::GetModelInfo(id)->CreateInstance(); @@ -207,14 +303,14 @@ CPed::AddWeaponModel(int id) } void -CPed::AimGun() +CPed::AimGun(void) { RwV3d pos; CVector vector; if (m_pSeekTarget) { if (m_pSeekTarget->m_status == STATUS_PHYSICS) { - m_pSeekTarget->m_pedIK.GetComponentPosition(&pos, 1); + m_pSeekTarget->m_pedIK.GetComponentPosition(&pos, PED_TORSO); vector.x = pos.x; vector.y = pos.y; vector.z = pos.z; @@ -254,7 +350,7 @@ CPed::ApplyHeadShot(eWeaponType weaponType, CVector pos, bool evenOnPlayer) CPed::SetDie(ANIM_KO_SHOT_FRONT1, 4.0f, 0.0f); } - m_ped_flagC20 = 1; + m_ped_flagC20 = true; m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 150; CParticle::AddParticle(PARTICLE_TEST, pos2, @@ -309,7 +405,7 @@ CPed::RemoveBodyPart(PedNode nodeId, int8 unk) nil, 0.0f, 0, 0, 0, 0); } } - m_ped_flagC20 = 1; + m_ped_flagC20 = true; m_bodyPartBleeding = nodeId; } } else { @@ -343,7 +439,7 @@ CPed::SetLookFlag(CPed *target, bool unknown) m_pLookTarget->RegisterReference((CEntity**)&m_pLookTarget); m_fLookDirection = 999999.0f; m_lookTimer = 0; - m_ped_flagA20_look = unknown; + m_ped_flagA20 = unknown; if (m_nPedState != PED_DRIVING) { m_pedIK.m_flags &= ~CPedIK::FLAG_2; } @@ -359,7 +455,7 @@ CPed::SetLookFlag(float direction, bool unknown) m_pLookTarget = nil; m_fLookDirection = direction; m_lookTimer = 0; - m_ped_flagA20_look = unknown; + m_ped_flagA20 = unknown; if (m_nPedState != PED_DRIVING) { m_pedIK.m_flags &= ~CPedIK::FLAG_2; } @@ -444,6 +540,331 @@ CPed::Avoid(void) } } +void +CPed::ClearAimFlag(void) +{ + if (bIsAimingGun) { + bIsAimingGun = false; + bIsRestoringGun = true; + m_pedIK.m_flags &= ~CPedIK:: FLAG_4; + } + + if (CPed::IsPlayer()) + ((CPlayerPed*)this)->m_fFPSMoveHeading = 0.0; +} + +void +CPed::ClearLookFlag(void) { + if (bIsLooking) { + bIsLooking = false; + bIsRestoringLook = true; + m_ped_flagI1 = false; + + m_pedIK.m_flags &= ~CPedIK::FLAG_2; + if (CPed::IsPlayer()) + m_lookTimer = CTimer::GetTimeInMilliseconds() + 2000; + else + m_lookTimer = CTimer::GetTimeInMilliseconds() + 4000; + + if (m_nPedState == PED_LOOK_HEADING || m_nPedState == PED_LOOK_ENTITY) { + CPed::RestorePreviousState(); + CPed::ClearLookFlag(); + } + } +} + +bool +CPed::IsPedHeadAbovePos(float zOffset) +{ + RwMatrix mat; + + CPedIK::GetWorldMatrix(GetNodeFrame(PED_HEAD), &mat); + return zOffset + GetPosition().z >= mat.pos.z; +} + +void +CPed::FinishedAttackCB(CAnimBlendAssociation *attackAssoc, void *arg) +{ + CWeaponInfo *currentWeapon; + CAnimBlendAssociation *newAnim; + CPed *ped = (CPed*)arg; + + if (attackAssoc) { + switch (attackAssoc->animId) { + case ANIM_WEAPON_START_THROW: + if ((!ped->IsPlayer() || ((CPlayerPed*)ped)->field_1376) && ped->IsPlayer()) + { + attackAssoc->blendDelta = -1000.0; + newAnim = CAnimManager::AddAnimation((RpClump*)ped->m_rwObject, ASSOCGRP_STD, ANIM_WEAPON_THROWU); + } else { + attackAssoc->blendDelta = -1000.0; + newAnim = CAnimManager::AddAnimation((RpClump*)ped->m_rwObject, ASSOCGRP_STD, ANIM_WEAPON_THROW); + } + + newAnim->SetFinishCallback(CPed::FinishedAttackCB, ped); + break; + case ANIM_FIGHT_PPUNCH: + attackAssoc->blendDelta = -8.0; + attackAssoc->flags |= ASSOC_DELETEFADEDOUT; + ped->ClearAttack(); + break; + case ANIM_WEAPON_THROW: + case ANIM_WEAPON_THROWU: + if (ped->GetWeapon()->m_nAmmoTotal > 0) { + currentWeapon = CWeaponInfo::GetWeaponInfo(ped->GetWeapon()->m_eWeaponType); + ped->AddWeaponModel(currentWeapon->m_nModelId); + } + break; + default: + if (!ped->m_ped_flagA4) + ped->ClearAttack(); + + break; + } + } else if (!ped->m_ped_flagA4) + ped->ClearAttack(); +} + +void +CPed::Attack(void) +{ + CAnimBlendAssociation *weaponAnimAssoc; + int32 weaponAnim; + float animStart; + RwFrame *f; + eWeaponType ourWeaponType; + float weaponAnimTime; + RwFrame *i; + eWeaponFire ourWeaponFire; + float animEnd; + CWeaponInfo *ourWeapon; + bool lastReloadWasInFuture; + AnimationId reloadAnim; + CAnimBlendAssociation *reloadAnimAssoc; + float delayBetweenAnimAndFire; + CVector firePos; + + ourWeaponType = GetWeapon()->m_eWeaponType; + ourWeapon = CWeaponInfo::GetWeaponInfo(ourWeaponType); + ourWeaponFire = ourWeapon->m_eWeaponFire; + weaponAnimAssoc = RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ourWeapon->m_AnimToPlay); + lastReloadWasInFuture = m_ped_flagA4; + reloadAnimAssoc = 0; + reloadAnim = NUM_ANIMS; + delayBetweenAnimAndFire = ourWeapon->m_fAnimFrameFire; + weaponAnim = ourWeapon->m_AnimToPlay; + + if (weaponAnim == ANIM_WEAPON_HGUN_BODY) + reloadAnim = ANIM_HGUN_RELOAD; + else if (weaponAnim == ANIM_WEAPON_AK_BODY) + reloadAnim = ANIM_AK_RELOAD; + + if (reloadAnim != NUM_ANIMS) + reloadAnimAssoc = RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, reloadAnim); + + if (m_ped_flagE10) + return; + + if (reloadAnimAssoc) { + if (!CPed::IsPlayer() || ((CPlayerPed*)this)->field_1380) + CPed::ClearAttack(); + + return; + } + + // BUG: We currently don't know any situation this cond. could be true. + if (CTimer::GetTimeInMilliseconds() < m_lastHitTime) + lastReloadWasInFuture = true; + + if (!weaponAnimAssoc) { + if (ourWeapon->m_bThrow) { + weaponAnimAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_WEAPON_THROWU); + delayBetweenAnimAndFire = 0.2f; + } else { + weaponAnimAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ourWeapon->m_Anim2ToPlay); + delayBetweenAnimAndFire = ourWeapon->m_fAnim2FrameFire; + } + } + if (weaponAnimAssoc) { + animStart = ourWeapon->m_fAnimLoopStart; + weaponAnimTime = weaponAnimAssoc->currentTime; + if (weaponAnimTime > animStart && weaponAnimTime - weaponAnimAssoc->timeStep <= animStart) { + if (ourWeapon->m_bCanAimWithArm) + m_pedIK.m_flags |= CPedIK::FLAG_4; + else + m_pedIK.m_flags &= ~CPedIK::FLAG_4; + } + if (weaponAnimTime <= delayBetweenAnimAndFire || weaponAnimTime - weaponAnimAssoc->timeStep > delayBetweenAnimAndFire || !weaponAnimAssoc->IsRunning()) { + if (weaponAnimAssoc->speed < 1.0f) + weaponAnimAssoc->speed = 1.0; + + } else { + firePos = ourWeapon->m_vecFireOffset; + if (ourWeaponType == WEAPONTYPE_BASEBALLBAT) { + if (weaponAnimAssoc->animId == ourWeapon->m_Anim2ToPlay) + firePos.z = 0.7f * ourWeapon->m_fRadius - 1.0f; + + firePos = GetMatrix() * firePos; + } else if (ourWeaponType != WEAPONTYPE_UNARMED) { + if (weaponAnimAssoc->animId == ANIM_KICK_FLOOR) + f = GetNodeFrame(PED_FOOTR); + else + f = GetNodeFrame(PED_HANDR); + + while (f) { + RwV3dTransformPoints((RwV3d*)firePos, (RwV3d*)firePos, 1, &f->modelling); + f = RwFrameGetParent(f); + } + } else { + firePos = GetMatrix() * firePos; + } + + GetWeapon()->Fire(this, &firePos); + + if (ourWeaponType == WEAPONTYPE_MOLOTOV || ourWeaponType == WEAPONTYPE_GRENADE) { + RemoveWeaponModel(ourWeapon->m_nModelId); + } + if (!GetWeapon()->m_nAmmoTotal && ourWeaponFire != WEAPON_FIRE_MELEE && FindPlayerPed() != this) { + SelectGunIfArmed(); + } + + if (GetWeapon()->m_eWeaponState != WEAPONSTATE_MELEE_MADECONTACT) { + // If reloading just began, start the animation + if (GetWeapon()->m_eWeaponState == WEAPONSTATE_RELOADING && reloadAnim != NUM_ANIMS && !reloadAnimAssoc) { + CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, reloadAnim, 8.0f); + CPed::ClearLookFlag(); + CPed::ClearAimFlag(); + m_ped_flagA4 = false; + m_ped_flagA8 = false; + m_lastHitTime = CTimer::GetTimeInMilliseconds(); + return; + } + } else { + if (weaponAnimAssoc->animId <= ANIM_WEAPON_BAT_V) { + DMAudio.PlayOneShot(uAudioEntityId, SOUND_WEAPON_BAT_ATTACK, 1.0f); + } else if (weaponAnimAssoc->animId == ANIM_FIGHT_PPUNCH) { + DMAudio.PlayOneShot(uAudioEntityId, SOUND_WEAPON_PUNCH_ATTACK, 0.0f); + } + + weaponAnimAssoc->speed = 0.5; + + // BUG: We currently don't know any situation this cond. could be true. + if (m_ped_flagA4 || CTimer::GetTimeInMilliseconds() < m_lastHitTime) { + weaponAnimAssoc->callbackType = 0; + } + + lastReloadWasInFuture = false; + } + } + + if (ourWeaponType == WEAPONTYPE_SHOTGUN) { + weaponAnimTime = weaponAnimAssoc->currentTime; + if (weaponAnimTime > 1.0f && weaponAnimTime - weaponAnimAssoc->timeStep <= 1.0f && weaponAnimAssoc->IsRunning()) { + for (i = GetNodeFrame(PED_HANDR); i; i = RwFrameGetParent(i)) + RwV3dTransformPoints((RwV3d*)ourWeapon->m_vecFireOffset, (RwV3d*)ourWeapon->m_vecFireOffset, 1, &i->modelling); + + CVector gunshellPos( + ourWeapon->m_vecFireOffset.x - 0.6f * GetForward().x, + ourWeapon->m_vecFireOffset.y - 0.6f * GetForward().y, + ourWeapon->m_vecFireOffset.z - 0.15f * GetUp().z + ); + CVector2D gunshellRot( + GetRight().x, + GetRight().y + ); + + gunshellRot.Normalise(); + CWeapon::AddGunshell(this, gunshellPos, gunshellRot, 0.025f); + } + } + animEnd = ourWeapon->m_fAnimLoopEnd; + if (ourWeaponFire == WEAPON_FIRE_MELEE && weaponAnimAssoc->animId == ourWeapon->m_Anim2ToPlay) + animEnd = 0.56f; + + weaponAnimTime = weaponAnimAssoc->currentTime; + + // End of the attack + if (weaponAnimTime > animEnd || !weaponAnimAssoc->IsRunning() && ourWeaponFire != WEAPON_FIRE_PROJECTILE) { + + if (weaponAnimTime - 2.0f * weaponAnimAssoc->timeStep <= animEnd + // BUG: We currently don't know any situation this cond. could be true. + && (m_ped_flagA4 || CTimer::GetTimeInMilliseconds() < m_lastHitTime) + && GetWeapon()->m_eWeaponState != WEAPONSTATE_RELOADING) { + + weaponAnim = weaponAnimAssoc->animId; + if (ourWeaponFire != WEAPON_FIRE_MELEE || CheckForPedsOnGroundToAttack(((CPlayerPed*)this), 0) < PED_ON_THE_FLOOR) { + if (weaponAnim != ourWeapon->m_Anim2ToPlay || weaponAnim == ANIM_RBLOCK_CSHOOT) { + weaponAnimAssoc->Start(ourWeapon->m_fAnimLoopStart); + } else { + CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, ourWeapon->m_AnimToPlay, 8.0f); + } + } else { + if (weaponAnim == ourWeapon->m_Anim2ToPlay) + weaponAnimAssoc->SetCurrentTime(0.1f); + else + CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, ourWeapon->m_Anim2ToPlay, 8.0f); + } + } else { + CPed::ClearAimFlag(); + + // Echoes of bullets, at the end of the attack. (Bug: doesn't play while reloading) + if (weaponAnimAssoc->currentTime - weaponAnimAssoc->timeStep < ourWeapon->m_fAnimLoopEnd) { + if (ourWeaponType < WEAPONTYPE_SNIPERRIFLE) { + switch (ourWeaponType) { + case WEAPONTYPE_UZI: + DMAudio.PlayOneShot(uAudioEntityId, SOUND_WEAPON_UZI_BULLET_ECHO, 0.0f); + break; + case WEAPONTYPE_AK47: + DMAudio.PlayOneShot(uAudioEntityId, SOUND_WEAPON_AK47_BULLET_ECHO, 0.0f); + break; + case WEAPONTYPE_M16: + DMAudio.PlayOneShot(uAudioEntityId, SOUND_WEAPON_M16_BULLET_ECHO, 0.0f); + break; + default: + break; + } + } + } + + // Fun fact: removing this part leds to reloading flamethrower + if (ourWeaponType == WEAPONTYPE_FLAMETHROWER && weaponAnimAssoc->IsRunning()) { + weaponAnimAssoc->flags |= ASSOC_DELETEFADEDOUT; + weaponAnimAssoc->flags &= ~ASSOC_RUNNING; + weaponAnimAssoc->blendDelta = -4.0; + } + } + } + if (weaponAnimAssoc->currentTime > delayBetweenAnimAndFire) + lastReloadWasInFuture = false; + + m_ped_flagA4 = lastReloadWasInFuture; + return; + } + + if (lastReloadWasInFuture) { + if (ourWeaponFire != WEAPON_FIRE_PROJECTILE || !CPed::IsPlayer() || ((CPlayerPed*)this)->field_1380) { + if (!CGame::nastyGame || ourWeaponFire != WEAPON_FIRE_MELEE || CheckForPedsOnGroundToAttack(((CPlayerPed*)this), 0) < PED_ON_THE_FLOOR) { + weaponAnimAssoc = CAnimManager::BlendAnimation((RpClump*)m_rwObject, ASSOCGRP_STD, ourWeapon->m_AnimToPlay, 8.0f); + } else { + weaponAnimAssoc = CAnimManager::BlendAnimation((RpClump*)m_rwObject, ASSOCGRP_STD, ourWeapon->m_Anim2ToPlay, 8.0f); + } + + weaponAnimAssoc->SetFinishCallback(CPed::FinishedAttackCB, this); + weaponAnimAssoc->flags |= ASSOC_RUNNING; + + if (weaponAnimAssoc->currentTime == weaponAnimAssoc->hierarchy->totalLength) + weaponAnimAssoc->SetCurrentTime(0.0f); + + if (CPed::IsPlayer()) { + ((CPlayerPed*)this)->field_1376 = 0.0f; + ((CPlayerPed*)this)->field_1380 = false; + } + } + } + else + CPed::FinishedAttackCB(0, this); +} + STARTPATCHES InjectHook(0x4CF8F0, &CPed::AddWeaponModel, PATCH_JUMP); InjectHook(0x4C6AA0, &CPed::AimGun, PATCH_JUMP); @@ -454,4 +875,10 @@ STARTPATCHES InjectHook(0x4D12E0, &CPed::SetLookTimer, PATCH_JUMP); InjectHook(0x4C5700, &CPed::OurPedCanSeeThisOne, PATCH_JUMP); InjectHook(0x4D2BB0, &CPed::Avoid, PATCH_JUMP); + InjectHook(0x4C6A50, &CPed::ClearAimFlag, PATCH_JUMP); + InjectHook(0x4C64F0, &CPed::ClearLookFlag, PATCH_JUMP); + InjectHook(0x4E5BD0, &CPed::IsPedHeadAbovePos, PATCH_JUMP); + InjectHook(0x4E68A0, &CPed::FinishedAttackCB, PATCH_JUMP); + InjectHook(0x4E5BD0, &CheckForPedsOnGroundToAttack, PATCH_JUMP); + InjectHook(0x4E6BA0, &CPed::Attack, PATCH_JUMP); ENDPATCHES diff --git a/src/entities/Ped.h b/src/entities/Ped.h index fbfa3f57..45251d46 100644 --- a/src/entities/Ped.h +++ b/src/entities/Ped.h @@ -2,10 +2,13 @@ #include "Physical.h" #include "Weapon.h" -#include "PedIK.h" #include "PedStats.h" +#include "PedType.h" +#include "PedIK.h" #include "AnimManager.h" #include "AnimBlendClumpData.h" +#include "AnimBlendAssociation.h" +#include "WeaponInfo.h" struct CPathNode; @@ -13,6 +16,13 @@ enum { PED_MAX_WEAPONS = 13 }; +enum PedOnGroundState { + NO_PED, + PED_BELOW_PLAYER, + PED_ON_THE_FLOOR, + PED_DEAD_ON_THE_FLOOR +}; + enum PedState { PED_NONE, @@ -98,7 +108,7 @@ public: uint8 m_ped_flagA4 : 1; // stores (CTimer::GetTimeInMilliseconds() < m_lastHitTime) uint8 m_ped_flagA8 : 1; uint8 bIsLooking : 1; - uint8 m_ped_flagA20_look : 1; // probably missing in SA + uint8 m_ped_flagA20 : 1; // "look" method? - probably missing in SA uint8 bIsRestoringLook : 1; uint8 bIsAimingGun : 1; uint8 bIsRestoringGun : 1; @@ -178,7 +188,7 @@ public: CPedIK m_pedIK; uint8 stuff1[8]; uint32 m_nPedStateTimer; - int32 m_nPedState; + PedState m_nPedState; int32 m_nLastPedState; int32 m_nMoveState; int32 m_nStoredActionState; @@ -206,7 +216,7 @@ public: CVector m_vecOffsetFromPhysSurface; CEntity *m_pCurSurface; uint8 stuff3[12]; - CPed* m_pSeekTarget; + CPed *m_pSeekTarget; CVehicle *m_pMyVehicle; bool bInVehicle; uint8 stuff4[23]; @@ -235,7 +245,8 @@ public: uint8 m_bodyPartBleeding; uint8 m_field_4F3; CPed *m_nearPeds[10]; - uint8 stuff11[32]; + uint16 m_numNearPeds; + uint8 stuff11[30]; static void *operator new(size_t); static void operator delete(void*, size_t); @@ -243,7 +254,7 @@ public: bool IsPlayer(void); bool UseGroundColModel(void); void AddWeaponModel(int id); - void AimGun(); + void AimGun(void); void KillPedWithCar(CVehicle *veh, float impulse); void Say(uint16 audio); void SetLookFlag(CPed *target, bool unknown); @@ -255,8 +266,17 @@ public: void SpawnFlyingComponent(int, int8 unknown); bool OurPedCanSeeThisOne(CEntity *target); void Avoid(void); + void Attack(void); + void ClearAimFlag(void); + void ClearLookFlag(void); + void RestorePreviousState(void); + void ClearAttack(void); + bool IsPedHeadAbovePos(float zOffset); + void RemoveWeaponModel(int); + void SelectGunIfArmed(void); static RwObject *SetPedAtomicVisibilityCB(RwObject *object, void *data); static RwFrame *RecurseFrameChildrenVisibilityCB(RwFrame *frame, void *data); + static void FinishedAttackCB(CAnimBlendAssociation *attackAssoc, void *arg); CWeapon *GetWeapon(void) { return &m_weapons[m_currentWeapon]; } RwFrame *GetNodeFrame(int nodeId) { return m_pFrames[nodeId]->frame; } @@ -265,6 +285,7 @@ public: static bool &bPedCheat2; static bool &bPedCheat3; }; + static_assert(offsetof(CPed, m_nPedState) == 0x224, "CPed: error"); static_assert(offsetof(CPed, m_pCurSurface) == 0x2FC, "CPed: error"); static_assert(offsetof(CPed, m_pMyVehicle) == 0x310, "CPed: error"); diff --git a/src/entities/PedIK.cpp b/src/entities/PedIK.cpp index f262fab5..ebc17097 100644 --- a/src/entities/PedIK.cpp +++ b/src/entities/PedIK.cpp @@ -1,7 +1,39 @@ #include "common.h" #include "patcher.h" +#include "PedIK.h" #include "Ped.h" -WRAPPER void CPedIK::GetComponentPosition(RwV3d* pos, int id) { EAXJMP(0x4ED0F0); } WRAPPER bool CPedIK::PointGunInDirection(float phi, float theta) { EAXJMP(0x4ED9B0); } -WRAPPER bool CPedIK::PointGunAtPosition(CVector* position) { EAXJMP(0x4ED920); }
\ No newline at end of file +WRAPPER bool CPedIK::PointGunAtPosition(CVector *position) { EAXJMP(0x4ED920); } + +void +CPedIK::GetComponentPosition(RwV3d *pos, PedNode node) +{ + RwFrame *f; + RwMatrix *mat; + + f = m_ped->GetNodeFrame(node); + mat = &f->modelling; + *pos = mat->pos; + + for (f = RwFrameGetParent(f); f; f = RwFrameGetParent(f)) + RwV3dTransformPoints(pos, pos, 1, &f->modelling); +} + +RwMatrix* +CPedIK::GetWorldMatrix(RwFrame *source, RwMatrix *destination) +{ + RwFrame *i; + + *destination = source->modelling; + + for (i = RwFrameGetParent(source); i; i = RwFrameGetParent(i)) + RwMatrixTransform(destination, &i->modelling, rwCOMBINEPOSTCONCAT); + + return destination; +} + +STARTPATCHES + InjectHook(0x4ED0F0, &CPedIK::GetComponentPosition, PATCH_JUMP); + InjectHook(0x4ED060, &CPedIK::GetWorldMatrix, PATCH_JUMP); +ENDPATCHES
\ No newline at end of file diff --git a/src/entities/PedIK.h b/src/entities/PedIK.h index 266372c4..68e4014a 100644 --- a/src/entities/PedIK.h +++ b/src/entities/PedIK.h @@ -1,5 +1,6 @@ #pragma once #include "common.h" +#include "PedModelInfo.h" struct LimbOrientation { @@ -14,20 +15,21 @@ class CPedIK public: // TODO enum { - FLAG_1, - FLAG_2, - FLAG_4, // aims with arm + FLAG_1 = 1, + FLAG_2 = 2, // related to looking somewhere + FLAG_4 = 4, // aims with arm }; - CPed* m_ped; + CPed *m_ped; LimbOrientation m_headOrient; LimbOrientation m_torsoOrient; LimbOrientation m_upperArmOrient; LimbOrientation m_lowerArmOrient; int32 m_flags; - void GetComponentPosition(RwV3d* pos, int id); bool PointGunInDirection(float phi, float theta); - bool PointGunAtPosition(CVector* position); + bool PointGunAtPosition(CVector *position); + void GetComponentPosition(RwV3d *pos, PedNode node); + static RwMatrix *GetWorldMatrix(RwFrame *source, RwMatrix *destination); }; static_assert(sizeof(CPedIK) == 0x28, "CPedIK: error"); diff --git a/src/patcher.h b/src/patcher.h index 4ac1111b..43618b47 100644 --- a/src/patcher.h +++ b/src/patcher.h @@ -101,6 +101,15 @@ Nop(AT address, unsigned int nCount) VirtualProtect((void*)address, nCount, dwProtect[0], &dwProtect[1]); } +template<typename AT> inline void +ClearCC(AT address, unsigned int nCount) +{ + DWORD dwProtect[2]; + VirtualProtect((void*)address, nCount, PAGE_EXECUTE_READWRITE, &dwProtect[0]); + memset((void*)address, 0xCC, nCount); + VirtualProtect((void*)address, nCount, dwProtect[0], &dwProtect[1]); +} + template<typename AT, typename HT> inline void InjectHook(AT address, HT hook, unsigned int nType=PATCH_NOTHING) { diff --git a/src/weapons/Weapon.cpp b/src/weapons/Weapon.cpp index 22ae595a..fea09245 100644 --- a/src/weapons/Weapon.cpp +++ b/src/weapons/Weapon.cpp @@ -2,3 +2,5 @@ #include "patcher.h" #include "Weapon.h" +WRAPPER bool CWeapon::Fire(CEntity*, CVector*) { EAXJMP(0x55C380); } +WRAPPER void CWeapon::AddGunshell(CEntity*, CVector const&, CVector2D const&, float) { EAXJMP(0x55F770); }
\ No newline at end of file diff --git a/src/weapons/Weapon.h b/src/weapons/Weapon.h index 0fab027b..87134929 100644 --- a/src/weapons/Weapon.h +++ b/src/weapons/Weapon.h @@ -1,4 +1,5 @@ #pragma once +#include "Entity.h" enum eWeaponType { @@ -18,14 +19,35 @@ enum eWeaponType WEAPONTYPE_HELICANNON }; +enum eWeaponFire { + WEAPON_FIRE_MELEE, + WEAPON_FIRE_INSTANT_HIT, + WEAPON_FIRE_PROJECTILE, + WEAPON_FIRE_AREA_EFFECT, + WEAPON_FIRE_USE +}; + +// Taken from MTA SA, seems it's unchanged +enum eWeaponState +{ + WEAPONSTATE_READY, + WEAPONSTATE_FIRING, + WEAPONSTATE_RELOADING, + WEAPONSTATE_OUT_OF_AMMO, + WEAPONSTATE_MELEE_MADECONTACT +}; + class CWeapon { public: eWeaponType m_eWeaponType; - int32 m_eWeaponState; + eWeaponState m_eWeaponState; int32 m_nAmmoInClip; int32 m_nAmmoTotal; int32 m_nTimer; bool m_bAddRotOffset; + + bool Fire(CEntity*, CVector*); + static void AddGunshell(CEntity*, CVector const&, CVector2D const&, float); }; static_assert(sizeof(CWeapon) == 0x18, "CWeapon: error"); diff --git a/src/weapons/WeaponInfo.cpp b/src/weapons/WeaponInfo.cpp new file mode 100644 index 00000000..155425b5 --- /dev/null +++ b/src/weapons/WeaponInfo.cpp @@ -0,0 +1,14 @@ +#include "common.h" +#include "patcher.h" +#include "WeaponInfo.h" + +CWeaponInfo (&CWeaponInfo::ms_apWeaponInfos)[14] = * (CWeaponInfo(*)[14]) * (uintptr*)0x6503EC; + +CWeaponInfo* +CWeaponInfo::GetWeaponInfo(eWeaponType weaponType) { + return &CWeaponInfo::ms_apWeaponInfos[weaponType]; +} + +STARTPATCHES + InjectHook(0x564FD0, &CWeaponInfo::GetWeaponInfo, PATCH_JUMP); +ENDPATCHES
\ No newline at end of file diff --git a/src/weapons/WeaponInfo.h b/src/weapons/WeaponInfo.h new file mode 100644 index 00000000..34790565 --- /dev/null +++ b/src/weapons/WeaponInfo.h @@ -0,0 +1,45 @@ +#pragma once +#include "common.h" +#include "Weapon.h" +#include "AnimManager.h" + +class CWeaponInfo { +public: + eWeaponFire m_eWeaponFire; + float m_fRange; + uint32 m_nFiringRate; + uint32 m_nReload; + uint32 m_nAmountofAmmunition; + uint32 m_nDamage; + float m_fSpeed; + float m_fRadius; + float m_fLifespan; + float m_fSpread; + CVector m_vecFireOffset; + AnimationId m_AnimToPlay; + AnimationId m_Anim2ToPlay; + float m_fAnimLoopStart; + float m_fAnimLoopEnd; + float m_fAnimFrameFire; + float m_fAnim2FrameFire; + int32 m_nModelId; + // flags + uint8 m_bUseGravity : 1; + uint8 m_bSlowsDown : 1; + uint8 m_bDissipates : 1; + uint8 m_bRandSpeed : 1; + uint8 m_bExpands : 1; + uint8 m_bExplodes : 1; + uint8 m_bCanAim : 1; + uint8 m_bCanAimWithArm : 1; + uint8 m_b1stPerson : 1; + uint8 m_bHeavy : 1; + uint8 m_bThrow : 1; + uint8 stuff; + + static CWeaponInfo (&ms_apWeaponInfos)[14]; + + static CWeaponInfo *GetWeaponInfo(eWeaponType weaponType); +}; + +static_assert(sizeof(CWeaponInfo) == 0x54, "CWeaponInfo: error");
\ No newline at end of file |