diff --git a/README.md b/README.md index 1588a9a..8a8fcf4 100644 --- a/README.md +++ b/README.md @@ -33,4 +33,89 @@ Status values may not be 100% accurate and will fluctuate during the analyzation ## Building -TODO +### Toolchain + +The client library is built with **Microsoft Visual C++ 7.1 (`cl.exe` 13.10.3077)**, the compiler that ships +with Visual Studio .NET 2003. This is not a preference. A matching decompilation only reproduces the original +bytes if the original compiler produces them, and no later MSVC release generates the same code for this +source. Building with a modern toolset may well succeed and will not match. + +Two flags worth knowing before you start: `/GS-` does not exist in VC7.1 (buffer security checks are off by +default, and `BufferSecurityCheck="FALSE"` in the project reflects that), and `__thiscall` is not a usable +keyword in this compiler version. + +### On Windows + +Open `saco.sln` in Visual Studio .NET 2003 and build the `Release` configuration of the `saco` project. The +output is `saco/Release/samp.dll`. The `Debug` configuration exists but produces nothing comparable to the +original binary. + +Other components have their own solutions: `announce/announce.sln`, `arctool2/arctool2.sln`, `bot/bot.sln` +and `server/server.sln`. + +### On Linux + +VC7.1 runs well under Wine. Install Visual Studio .NET 2003 into a Wine prefix, then invoke `cl.exe` +directly. Two things are easy to get wrong: the working directory has to be `saco/`, and `INCLUDE` has to be +set explicitly, because the Wine environment does not inherit the compiler's own registry settings. + +```sh +export WINEPREFIX="$HOME/.wine" +VC='C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7' +export INCLUDE="$VC\\include;$VC\\PlatformSDK\\Include" + +cd saco +wine "$VC\\bin\\cl.exe" /c /Ox /Og /Ob1 /Oi /Ot /Oy /MT /Zp1 /EHsc /GF \ + /D WIN32 /D NDEBUG /D _WINDOWS /D _USRDLL /D SACO_EXPORTS /D SAMPCLI \ + /I "d3d9\\include" /I "." net\\netgame.cpp /Fo"Z:\\tmp\\netgame.obj" +``` + +### Release settings + +These are the settings the `Release` configuration in `saco/saco.vcproj` expands to. They matter because +changing any of them changes the generated code, so a function that matched before may stop matching. + +| Flag | Project property | +| --- | --- | +| `/Ox` | `Optimization="3"` | +| `/Og` | `GlobalOptimizations="TRUE"` | +| `/Ob1` | `InlineFunctionExpansion="1"` | +| `/Oi` | `EnableIntrinsicFunctions="TRUE"` | +| `/Ot` | `FavorSizeOrSpeed="1"` | +| `/Oy` | `OmitFramePointers="TRUE"` | +| `/GF` | `StringPooling="TRUE"` | +| `/EHsc` | `ExceptionHandling="TRUE"` | +| `/MT` | `RuntimeLibrary="0"` | +| `/Zp1` | `StructMemberAlignment="1"` | + +Preprocessor definitions are `WIN32`, `NDEBUG`, `_WINDOWS`, `_USRDLL`, `SACO_EXPORTS` and `SAMPCLI`, and the +only additional include directory is `d3d9\include`. + +Five files carry per-file overrides, and compiling them with the defaults above will not match: + +| File | Override | +| --- | --- | +| `game/entity.cpp` | `/O2` | +| `game/keystuff.cpp` | `/O2` | +| `game/patches.cpp` | `/O2` | +| `httpclient.cpp` | `/O2` | +| `d3d9/dxutil.cpp` | `/Ob2` | + +The link is `/SUBSYSTEM:WINDOWS /MACHINE:X86` against `dxguid.lib`, `d3d9.lib`, `d3dx9.lib`, `comctl32.lib`, +`wsock32.lib`, `winmm.lib` and `bass.lib`, with `d3d9` as an additional library directory. Both +`/OPT:NOREF` and `/OPT:NOICF` are in effect (`OptimizeReferences="0"` and `EnableCOMDATFolding="0"`), so the +linker neither strips unreferenced functions nor folds identical ones. Identical functions therefore appear +separately in the original binary, which is why byte-identical siblings are expected rather than suspicious. + +### Current state of the build + +`samp.dll` does not link yet. `raknet/SocketDataEncryptor.cpp` is referenced by the project but is not +present in the tree, and it is the only such file. Every other translation unit the project lists compiles +cleanly. + +Because of that, progress is measured per function rather than per binary. A function counts as done when the +bytes the compiler emits for it are identical to the bytes at the corresponding address in the original +`samp.dll`, after masking the four-byte slots that COFF marks as relocations. Masking is what makes an +unlinked object file comparable to a linked binary: call targets, global addresses and string addresses are +resolved at link time and cannot agree, while every other byte must. + diff --git a/raknet/SocketDataEncryptor.h b/raknet/SocketDataEncryptor.h new file mode 100644 index 0000000..93840fa --- /dev/null +++ b/raknet/SocketDataEncryptor.h @@ -0,0 +1,8 @@ +#pragma once + +// only SetKey is reconstructed; it is all `CNetGame::CNetGame` needs +class SocketDataEncryptor +{ +public: + static void SetKey(int iPort); +}; diff --git a/saco/audiostream.cpp b/saco/audiostream.cpp index b14956f..73fd3e4 100644 --- a/saco/audiostream.cpp +++ b/saco/audiostream.cpp @@ -1,19 +1,98 @@ -#include "audiostream.h" +#include "main.h" +#include "bass.h" -void CAudioStream::Reset() +extern CGame *pGame; +extern CConfig *pConfig; +extern CFontRender *pDefaultFont; + +// stream state (file scope) +HSTREAM g_hStream = 0; +volatile BYTE g_bPlaying = 0; +BYTE g_bStopRequested = 0; +float g_fPosX = 0.0f; +float g_fPosY = 0.0f; +float g_fPosZ = 0.0f; +float g_fDistance = 0.0f; +BYTE g_bIs3D = 0; +char g_szUrl[257]; +char g_szMeta[257]; +char g_szName[257]; +char g_szUrlInfo[257]; + +int CAudioStream::Reset() { - // TODO: CAudioStream::sub_10066480 10066480 + g_hStream = 0; + BASS_Free(); + if(BASS_Init(-1, 44100, 0, 0, 0)) + { + BASS_SetConfigPtr(BASS_CONFIG_NET_AGENT, "SA-MP/0.3"); + BASS_SetConfig(BASS_CONFIG_GVOL_STREAM, (DWORD)(pGame->m_pGameAudio->GetRadioVolume() * 7000.0f)); + BASS_SetConfig(BASS_CONFIG_NET_PLAYLIST, 1); + BASS_SetConfig(BASS_CONFIG_NET_TIMEOUT, 10000); + + if(!pConfig->IsVariableExist("audioproxyoff")) + pConfig->SetIntVariable("audioproxyoff", 0, 0); + + if(pConfig->GetIntVariable("audioproxyoff")) + BASS_SetConfigPtr(BASS_CONFIG_NET_PROXY, 0); + + BASS_SetEAXParameters(-1, 0, -1.0f, -1.0f); + field_0 = 1; + return 1; + } + return 0; } -void CAudioStream::Stop() +void CAudioStream::Stop(char byteResetRadio) { // TODO: CAudioStream::sub_10066560 10066560 } void CAudioStream::ConstructInfo() { - // TODO: CAudioStream::sub_100665C0 100665C0 + const char *pTags; + + memset(g_szMeta, 0, 257); + + pTags = (const char *)BASS_ChannelGetTags(g_hStream, BASS_TAG_META); + if(pTags) + { + char *p = strstr((char *)pTags, "StreamTitle='"); + if(p) + { + char *pDup = _strdup(p + 13); + *(strchr(pDup, ';') - 1) = 0; + _snprintf(g_szMeta, 256, "%s", pDup); + free(pDup); + } + } + else + { + pTags = (const char *)BASS_ChannelGetTags(g_hStream, BASS_TAG_OGG); + if(pTags) + { + char *pArtist = 0; + char *pTitle = 0; + char *p = (char *)pTags; + + if(*p) + { + do + { + if(!_strnicmp(p, "artist=", 7)) pArtist = p + 7; + if(!_strnicmp(p, "title=", 6)) pTitle = p + 6; + p += strlen(p) + 1; + } + while(*p); + + if(pArtist) + _snprintf(g_szMeta, 256, "%s - %s", pArtist, pTitle); + else if(pTitle) + _snprintf(g_szMeta, 256, "%s", pTitle); + } + } + } } void CAudioStream::SyncProc() @@ -33,10 +112,35 @@ void CAudioStream::Play() void CAudioStream::ControlGameRadio() { - // TODO: CAudioStream::sub_10066A80 10066A80 + if(field_0 && g_bPlaying) + { + pGame->m_pGameAudio->StartRadio(-1); + pGame->m_pGameAudio->StopRadio(); + } } void CAudioStream::DrawInfo() { - // TODO: CAudioStream::sub_10066AB0 10066AB0 + char szString[264]; + + if(field_0 && g_bPlaying) + { + memset(szString, 0, 257); + + if(strlen(g_szMeta) && strlen(g_szName)) + { + RECT rc; + int iTop; + + _snprintf(szString, 256, "%s - %s", g_szMeta, g_szName); + + iTop = pGame->GetScreenHeight() - 20; + rc.left = 15; + rc.top = iTop; + rc.bottom = iTop + 30; + rc.right = pGame->GetScreenWidth(); + + pDefaultFont->RenderText(NULL, szString, rc, DT_NOCLIP, 0x99FFFFFF, FALSE); + } + } } \ No newline at end of file diff --git a/saco/audiostream.h b/saco/audiostream.h index 48e54f0..31f8d10 100644 --- a/saco/audiostream.h +++ b/saco/audiostream.h @@ -12,12 +12,13 @@ public: field_0 = 0; } - void Reset(); - void Stop(); + int Reset(); + void Stop(char byteResetRadio); void ConstructInfo(); void SyncProc(); void Process(); void Play(); + void FUNC_10066960(char *szURL, float fX, float fY, float fZ, float fRadius, char bUsePos); // .text:10066960 void ControlGameRadio(); void DrawInfo(); }; diff --git a/saco/chatwindow.cpp b/saco/chatwindow.cpp index be99534..94d188d 100644 --- a/saco/chatwindow.cpp +++ b/saco/chatwindow.cpp @@ -1,5 +1,17 @@ +#include #include "main.h" +#include "game/util.h" + +extern CConfig *pConfig; +extern CCmdWindow *pCmdWindow; +extern CGame *pGame; + +typedef char AssertEntrySize[sizeof(CHAT_WINDOW_ENTRY) == 252 ? 1 : -1]; +typedef char AssertEntryText[offsetof(CHAT_WINDOW_ENTRY, szText) == 0x20 ? 1 : -1]; +typedef char AssertEntryType[offsetof(CHAT_WINDOW_ENTRY, iType) == 0xF0 ? 1 : -1]; +typedef char AssertChatWindowSize[sizeof(CChatWindow) == 25578 ? 1 : -1]; +typedef char AssertTimestampWidth[offsetof(CChatWindow, field_63E6) == 0x63E6 ? 1 : -1]; //---------------------------------------------------- @@ -26,7 +38,7 @@ CChatWindow::CChatWindow(IDirect3DDevice9 *pD3DDevice, CFontRender *pFontRender, m_dwChatDebugColor = D3DCOLOR_ARGB(255,169,196,228); field_0 = 10; - field_C = 0; + m_bTimestamp = 0; if(szChatLogFile && strlen(szChatLogFile)) { @@ -57,8 +69,66 @@ CChatWindow::CChatWindow(IDirect3DDevice9 *pD3DDevice, CFontRender *pFontRender, void CChatWindow::CreateFonts() { + D3DSURFACE_DESC desc; + HRESULT hr; - // TODO: CChatWindow::CreateFonts .text:100681D0 + field_63B2 = 1; + + if(field_63BE) { + field_63BE->Release(); + field_63BE = NULL; + } + + if(field_63BA) { + field_63BA->Release(); + field_63BA = NULL; + } + + if(field_63B6) { + field_63B6->Release(); + field_63B6 = NULL; + } + + m_pD3DDevice->GetDisplayMode(0,&field_63C2); + + if(pConfig->GetIntVariable("directmode")) { + AddDebugMessage("ChatWindow: Using direct drawing mode."); + FUNC_10067120(); + field_63B2 = 0; + return; + } + + // the render target has to cover the whole screen + if(field_63C2.Width <= 1024) { + hr = D3DXCreateTexture(m_pD3DDevice,1024,512,1,D3DUSAGE_RENDERTARGET, + D3DFMT_A8R8G8B8,D3DPOOL_DEFAULT,&field_63BA); + } else { + hr = D3DXCreateTexture(m_pD3DDevice,2048,1024,1,D3DUSAGE_RENDERTARGET, + D3DFMT_A8R8G8B8,D3DPOOL_DEFAULT,&field_63BA); + } + + if(hr < 0) { + AddDebugMessage("ChatWindow: Can't create a render surface texture. Will use direct mode."); + field_63B2 = 0; + return; + } + + field_63BA->GetSurfaceLevel(0,&field_63BE); + field_63BE->GetDesc(&desc); + + if(D3DXCreateRenderToSurface(m_pD3DDevice,desc.Width,desc.Height,desc.Format, + TRUE,D3DFMT_D16,&field_63B6) < 0) + { + AddDebugMessage("ChatWindow: Can't create a render to surface. Will use direct mode."); + FUNC_10067120(); + field_63B2 = 0; + return; + } + + FUNC_10067120(); + + field_63D6 = 0; + m_bRedraw = 1; } //---------------------------------------------------- @@ -80,9 +150,26 @@ void CChatWindow::ResetDialogControls(CDXUTDialog *pGameUI) //---------------------------------------------------- //---------------------------------------------------- -void CChatWindow::AddDebugMessage(CHAR * szFormat, ...) +int CChatWindow::AddDebugMessage(CHAR * szFormat, ...) { - // TODO: CChatWindow::AddDebugMessage .text:100680F0 + char szBuffer[512]; + va_list va; + va_start(va, szFormat); + memset(szBuffer, 0, sizeof(szBuffer)); + vsprintf(szBuffer, szFormat, va); + + char *p = szBuffer; + if(szBuffer[0]) + { + do + { + if(*p > 0 && *p < 32) + *p = 32; + } + while(*++p); + } + + return sub_10067BE0(8, szBuffer, 0, m_dwChatDebugColor, 0); } // MATCH void CChatWindow::FUNC_10067200() @@ -96,3 +183,460 @@ void CChatWindow::FUNC_10067200() } } + +//---------------------------------------------------- + +void CChatWindow::SetPageSize(int iPageSize) +{ + if(iPageSize >= 10 && iPageSize <= MAX_MESSAGES) { + field_0 = iPageSize; + FUNC_10067200(); + m_bRedraw = 1; + } +} + +//---------------------------------------------------- + +void CChatWindow::ReplaceControlChars(char *szString) +{ + if(*szString) + { + do + { + if(*szString > 0 && *szString < 32) + *szString = 32; + } + while(*++szString); + } +} + +//---------------------------------------------------- + +void CChatWindow::PageUp() +{ + if(field_8 && m_pScrollBar && !pGame->sub_100A0920() && !pCmdWindow->IsImeActive()) { + int iPos = m_pScrollBar->GetTrackPos()-field_0; + if(iPos < 1) iPos = 1; + m_pScrollBar->SetTrackPos(iPos); + } +} + +//---------------------------------------------------- + +void CChatWindow::PageDown() +{ + if(field_8 && m_pScrollBar && !pGame->sub_100A0920() && !pCmdWindow->IsImeActive()) { + int iPos = m_pScrollBar->GetTrackPos(); + if(iPos == 1) + iPos = field_0; + else + iPos += field_0; + if(iPos > MAX_MESSAGES) iPos = MAX_MESSAGES; + m_pScrollBar->SetTrackPos(iPos); + } +} + +//---------------------------------------------------- + +void CChatWindow::FUNC_10067390() +{ + if(m_pScrollBar) + m_pScrollBar->SetTrackPos(MAX_MESSAGES-10); +} + +//---------------------------------------------------- + +void CChatWindow::OnMouseWheel(int nDelta) +{ + if(field_8 && m_pScrollBar) { + int nScrollLines; + SystemParametersInfo(SPI_GETWHEELSCROLLLINES,0,&nScrollLines,0); + m_pScrollBar->Scroll(-(nDelta*nScrollLines)); + } +} + +//---------------------------------------------------- + +void CChatWindow::AddChatMessage(PCHAR szName, DWORD dwColor, PCHAR szText) +{ + char *p = szText; + if(szText[0]) + { + do + { + if(*p > 0 && *p < 32) + *p = 32; + } + while(*++p); + } + + sub_10067BE0(2, szText, szName, m_dwChatTextColor, dwColor); +} + +//---------------------------------------------------- + +void CChatWindow::AddClientMessage(DWORD dwColor, CHAR *szMessage) +{ + if(strlen(szMessage) <= 144) + { + unnamed_100B6100(szMessage,255); + ReplaceControlChars(szMessage); + sub_10067BE0(4, szMessage, 0, (dwColor >> 8)|0xFF000000, 0); + } +} + +//---------------------------------------------------- + +void CChatWindow::AddInfoMessage(CHAR * szFormat, ...) +{ + char szBuffer[512]; + va_list va; + va_start(va, szFormat); + memset(szBuffer, 0, sizeof(szBuffer)); + vsprintf(szBuffer, szFormat, va); + + char *p = szBuffer; + if(szBuffer[0]) + { + do + { + if(*p > 0 && *p < 32) + *p = 32; + } + while(*++p); + } + + sub_10067BE0(4, szBuffer, 0, m_dwChatInfoColor, 0); +} + +//---------------------------------------------------- + +void CChatWindow::ReleaseTextures() +{ + if(field_63BE) { + field_63BE->Release(); + field_63BE = NULL; + } + + if(field_63BA) { + field_63BA->Release(); + field_63BA = NULL; + } + + if(field_63B6) { + field_63B6->Release(); + field_63B6 = NULL; + } +} + +//---------------------------------------------------- + +void CChatWindow::FUNC_10067410() +{ + ReleaseTextures(); +} + +//---------------------------------------------------- +// measures the line height and the width of a timestamp with DT_CALCRECT + +void CChatWindow::FUNC_10067120() +{ + RECT rect; + + m_pFontRender->field_0->DrawTextA(NULL,"Y",-1,&rect,DT_CALCRECT|DT_SINGLELINE,0xFF000000); + field_63E2 = rect.bottom-rect.top; + + m_pFontRender->field_0->DrawTextA(NULL,"[19:58:34]",-1,&rect,DT_CALCRECT|DT_SINGLELINE,0xFF000000); + field_63E6 = rect.right-rect.left; +} + +//---------------------------------------------------- + +void CChatWindow::FUNC_10067E00() +{ + if(m_pScrollBar) + { + if(field_8 && (m_pScrollBar->GetTrackPos() != MAX_MESSAGES-field_0 || pCmdWindow->m_bEnabled)) + m_pScrollBar->SetVisible(true); + else + m_pScrollBar->SetVisible(false); + + if(field_8) + { + if(field_63B2 && field_63D6) + { + field_63AA->Begin(D3DXSPRITE_ALPHABLEND); + field_63AA->Draw(field_63BA,NULL,NULL,NULL,0xFFFFFFFF); + field_63AA->End(); + } + else + { + field_63A6->Begin(D3DXSPRITE_ALPHABLEND); + FUNC_10067940(); + field_63A6->End(); + } + } + } +} + +//---------------------------------------------------- + +void CChatWindow::WriteChatLog(int iType, char *szText, char *szName) +{ + time_t curTime = time(NULL); + + if(field_D) + { + FILE *f = fopen(field_11,"a"); + if(f) + { + // the frame reserves 68 bytes for this, strftime is still capped at 64 + char szTimestamp[68]; + strftime(szTimestamp,64,"[%H:%M:%S]",localtime(&curTime)); + + switch(iType) + { + case 2: + fprintf(f,"%s <%s> %s\r\n",szTimestamp,szName,szText); + break; + case 4: + case 8: + fprintf(f,"%s %s\r\n",szTimestamp,szText); + break; + } + + fclose(f); + } + } +} + +//---------------------------------------------------- +// redraws the chat into the render target texture when it is dirty + +void CChatWindow::FUNC_10067ED0() +{ + if(field_63B2 && m_pD3DDevice && field_63B6 && field_63BA && field_63BE && m_pFontRender->field_0) + { + if(pGame->sub_100A0920()) + { + field_63D6 = 0; + m_bRedraw = 1; + } + else if(field_8) + { + if(m_bRedraw || field_63DE != m_pScrollBar->GetTrackPos()) + { + field_63B6->BeginScene(field_63BE,NULL); + m_pD3DDevice->Clear(0,NULL,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,0,0,0); + field_63A6->Begin(D3DXSPRITE_ALPHABLEND); + m_pD3DDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_BLENDFACTOR); + FUNC_10067940(); + field_63A6->End(); + field_63B6->EndScene(0); + + m_bRedraw = 0; + field_63D6 = 1; + field_63D2 = GetTickCount(); + field_63DE = m_pScrollBar->GetTrackPos(); + } + } + } +} + +//---------------------------------------------------- +// lays out the visible page of chat lines + +void CChatWindow::FUNC_10067940() +{ + int x = 0; + int iEntry; + RECT rect, rectName; + char szTimestamp[72]; + struct tm *pTime; + int iPos; + + memset(szTimestamp,0,64); + + rect.top = 10; + rect.left = 45; + rect.right = 550; + rect.bottom = 110; + + if(m_pScrollBar->GetTrackPos() < 1) + m_pScrollBar->SetTrackPos(1); + + iPos = m_pScrollBar->GetTrackPos(); + if(iPos < 1) + iPos = 1; + + iEntry = iPos; + + while(x != field_0) + { + if(m_bTimestamp) + { + pTime = localtime(&m_ChatWindowEntries[iEntry].tTime); + memset(szTimestamp,0,64); + + if(pTime) + strftime(szTimestamp,64,"[%H:%M:%S]",pTime); + } + + switch(m_ChatWindowEntries[iEntry].iType) + { + case 2: + if(m_bTimestamp) + { + if(strlen(szTimestamp) && m_ChatWindowEntries[iEntry].szName[0]) + { + FUNC_10067470(szTimestamp,rect,m_ChatWindowEntries[iEntry].dwTextColor); + rect.left = field_63E6+50; + } + } + + if(strlen(m_ChatWindowEntries[iEntry].szName)) + { + m_pFontRender->field_0->DrawTextA(NULL,m_ChatWindowEntries[iEntry].szName,-1,&rectName,DT_CALCRECT,0xFF000000); + FUNC_10067470(m_ChatWindowEntries[iEntry].szName,rect,m_ChatWindowEntries[iEntry].dwNameColor); + rect.left += rectName.right-rectName.left+5; + } + + FUNC_10067470(m_ChatWindowEntries[iEntry].szText,rect,m_ChatWindowEntries[iEntry].dwTextColor); + break; + + case 4: + case 8: + if(m_bTimestamp) + { + if(strlen(szTimestamp)) + { + FUNC_10067470(szTimestamp,rect,m_ChatWindowEntries[iEntry].dwTextColor); + rect.left = field_63E6+50; + } + } + + FUNC_10067470(m_ChatWindowEntries[iEntry].szText,rect,m_ChatWindowEntries[iEntry].dwTextColor); + break; + } + + iEntry++; + + rect.top += field_63E2+1; + rect.bottom = rect.top+field_63E2+1; + rect.left = 45; + x++; + } + + field_12E = rect.bottom; +} + +//---------------------------------------------------- +// draws the text with a black outline, thicker over 1280 wide + +void CChatWindow::FUNC_10067470(char *szText, RECT rect, DWORD dwColor) +{ + ID3DXFontHook *pFont; + ID3DXFont *pShadow; + int iLen; + + pShadow = m_pFontRender->field_8; + pFont = m_pFontRender->field_0; + iLen = strlen(szText); + + if(field_8 == 2) + { + char szBuffer[512] = {0}; + int iShadowLen; + + strncpy(szBuffer,szText,511); + RemoveColorEmbedsFromString(szBuffer); + iShadowLen = strlen(szBuffer); + + if(*(int *)0xC17044 > 1280) + { + rect.top -= 2; + rect.bottom -= 2; + + pShadow->DrawTextA(field_63A6,szBuffer,iShadowLen,&rect,DT_NOCLIP|DT_SINGLELINE,0xFF000000); + + rect.top++; + rect.bottom++; + rect.left--; + rect.right--; + + pShadow->DrawTextA(field_63A6,szBuffer,iShadowLen,&rect,DT_NOCLIP|DT_SINGLELINE,0xFF000000); + + rect.left++; + rect.right++; + rect.left++; + rect.right++; + + pShadow->DrawTextA(field_63A6,szBuffer,iShadowLen,&rect,DT_NOCLIP|DT_SINGLELINE,0xFF000000); + + rect.left--; + rect.right--; + rect.top++; + rect.bottom++; + rect.top += 2; + rect.bottom += 2; + + pShadow->DrawTextA(field_63A6,szBuffer,iShadowLen,&rect,DT_NOCLIP|DT_SINGLELINE,0xFF000000); + + rect.top--; + rect.bottom--; + rect.left--; + rect.right--; + + pShadow->DrawTextA(field_63A6,szBuffer,iShadowLen,&rect,DT_NOCLIP|DT_SINGLELINE,0xFF000000); + + rect.left++; + rect.right++; + rect.left++; + rect.right++; + + pShadow->DrawTextA(field_63A6,szBuffer,iShadowLen,&rect,DT_NOCLIP|DT_SINGLELINE,0xFF000000); + + rect.left--; + rect.right--; + rect.top--; + rect.bottom--; + rect.left -= 2; + rect.right -= 2; + + pShadow->DrawTextA(field_63A6,szBuffer,iShadowLen,&rect,DT_NOCLIP|DT_SINGLELINE,0xFF000000); + + rect.left += 2; + rect.right += 2; + rect.left += 2; + rect.right += 2; + + pShadow->DrawTextA(field_63A6,szBuffer,iShadowLen,&rect,DT_NOCLIP|DT_SINGLELINE,0xFF000000); + + rect.left -= 2; + rect.right -= 2; + } + else + { + rect.top--; + + pShadow->DrawTextA(field_63A6,szBuffer,iShadowLen,&rect,DT_NOCLIP|DT_SINGLELINE,0xFF000000); + + rect.top += 2; + + pShadow->DrawTextA(field_63A6,szBuffer,iShadowLen,&rect,DT_NOCLIP|DT_SINGLELINE,0xFF000000); + + rect.top--; + rect.left--; + + pShadow->DrawTextA(field_63A6,szBuffer,iShadowLen,&rect,DT_NOCLIP|DT_SINGLELINE,0xFF000000); + + rect.left += 2; + + pShadow->DrawTextA(field_63A6,szBuffer,iShadowLen,&rect,DT_NOCLIP|DT_SINGLELINE,0xFF000000); + + rect.left--; + } + } + + pFont->DrawTextA(field_63A6,szText,iLen,&rect,DT_NOCLIP|DT_SINGLELINE,dwColor|0xFF000000); +} diff --git a/saco/chatwindow.h b/saco/chatwindow.h index 588fa17..16da6f5 100644 --- a/saco/chatwindow.h +++ b/saco/chatwindow.h @@ -7,17 +7,22 @@ #pragma pack(1) typedef struct _CHAT_WINDOW_ENTRY { - char _gap0[252]; + time_t tTime; + char szName[28]; + char szText[208]; + int iType; + DWORD dwTextColor; + DWORD dwNameColor; } CHAT_WINDOW_ENTRY; class CChatWindow // size: 25578 { -private: +public: int field_0; char _gap4[4]; int field_8; - char field_C; + char m_bTimestamp; int field_D; char field_11[261]; CDXUTDialog *m_pGameUI; @@ -26,7 +31,7 @@ private: DWORD m_dwChatTextColor; DWORD m_dwChatInfoColor; DWORD m_dwChatDebugColor; - char _gap12E[4]; + int field_12E; CHAT_WINDOW_ENTRY m_ChatWindowEntries[MAX_MESSAGES]; CFontRender *m_pFontRender; ID3DXSprite *field_63A6; @@ -35,23 +40,59 @@ private: int field_63B2; ID3DXRenderToSurface *field_63B6; IDirect3DTexture9* field_63BA; - int field_63BE; + IDirect3DSurface9 *field_63BE; D3DDISPLAYMODE field_63C2; DWORD field_63D2; int field_63D6; - int field_63DA; + int m_bRedraw; int field_63DE; int field_63E2; - char _gap63E6[4]; + int field_63E6; - void CreateFonts(); + void CreateFonts(); // .text:100681D0 + + void FUNC_10067120(); // .text:10067120 void FUNC_10067200(); + void FUNC_10067390(); // .text:10067390 + + void OnMouseWheel(int nDelta); // .text:100673C0 + + void PageUp(); // .text:100672D0 + + void PageDown(); // .text:10067330 + + void ReplaceControlChars(char *szString); // .text:10067420 + + void ReleaseTextures(); // .text:100671A0 + + void FUNC_10067410(); // .text:10067410 + + void FUNC_10067470(char *szText, RECT rect, DWORD dwColor); // .text:10067470 + + void FUNC_10067940(); // .text:10067940 + + void FUNC_10067E00(); // .text:10067E00 + + void FUNC_10067ED0(); // .text:10067ED0 + public: - void AddDebugMessage(CHAR *szFormat, ...); + void WriteChatLog(int iType, char *szText, char *szName); // .text:100677D0 + + int sub_10067BE0(int a1, char *a2, char *a3, DWORD a4, DWORD a5); // .text:10067BE0 + + void AddChatMessage(PCHAR szName, DWORD dwColor, PCHAR szText); // .text:10068020 + + int AddDebugMessage(CHAR *szFormat, ...); + + void AddInfoMessage(CHAR *szFormat, ...); // .text:10068070 + + void AddClientMessage(DWORD dwColor, CHAR *szMessage); // .text:10068170 + + void SetPageSize(int iPageSize); // .text:100672A0 void ResetDialogControls(CDXUTDialog *pGameUI); diff --git a/saco/cmdprocs.cpp b/saco/cmdprocs.cpp index bc217bc..f48a3e5 100644 --- a/saco/cmdprocs.cpp +++ b/saco/cmdprocs.cpp @@ -1,15 +1,25 @@ #include "main.h" +#include "game/util.h" extern CChatWindow *pChatWindow; +extern CGame *pGame; extern CCmdWindow *pCmdWindow; extern CDeathWindow *pDeathWindow; extern CNetGame *pNetGame; extern GAME_SETTINGS tSettings; extern CConfig *pConfig; +extern CFontRender *pDefaultFont; extern bool bShowDebugLabels; extern bool bHudScaleFix; +extern bool bCameraTargetDebug; +extern BOOL bToggleObjectLight; + +void ShutdownGame(); // .text:100C3E80 +int VehicleSelect(int a1, int a2, int a3); // .text:1009E0A0 +void sub_100C5430(); // .text:100C5430 +char *sub_100C3AD0(); // .text:100C3AD0 void cmdDefaultCmdProc(PCHAR szCmd) { @@ -36,37 +46,95 @@ void cmdTestDeathWindow(PCHAR szCmd) void cmdCameraTargetDebug(PCHAR szCmd) { - // TODO: cmdCameraTargetDebug .text:100685E0 + bCameraTargetDebug = !bCameraTargetDebug; } void cmdPageSize(PCHAR szCmd) { - // TODO: cmdPageSize .text:100685F0 + int iPageSize; + if(strlen(szCmd) && (iPageSize = atoi(szCmd)) >= 10 && iPageSize <= 20) + { + if(pChatWindow) pChatWindow->SetPageSize(iPageSize); + if(pConfig) pConfig->SetIntVariable("pagesize", iPageSize); + } + else + { + if(pChatWindow) pChatWindow->AddDebugMessage("pagesize [10-20] (lines)"); + } } void cmdFontSize(PCHAR szCmd) { - // TODO: cmdFontSize .text:10068670 + int iSize; + if(strlen(szCmd) && (iSize = atoi(szCmd)) >= -3 && iSize <= 5) + { + if(pConfig) pConfig->SetIntVariable("fontsize", iSize); + if(pDefaultFont) pDefaultFont->CreateFonts(); + if(pChatWindow) + { + pChatWindow->CreateFonts(); + pChatWindow->FUNC_10067200(); + } + if(pDeathWindow) pDeathWindow->CreateFonts(); + sub_100C5430(); + } + else + { + if(pChatWindow) pChatWindow->AddDebugMessage("Valid fontsize: -3 to 5"); + } } void cmdNameTagStatus(PCHAR szCmd) { - // TODO: cmdNameTagStatus .text:10068720 + if(pNetGame) pNetGame->ToggleNameTagStatus(); } void cmdTimestamp(PCHAR szCmd) { - // TODO: cmdTimestamp .text:10068730 + CChatWindow *pChatWindow = ::pChatWindow; + if(pChatWindow) + { + if(pChatWindow->m_bTimestamp) + { + pChatWindow->m_bTimestamp = 0; + pChatWindow->m_bRedraw = 1; + if(pConfig) pConfig->SetIntVariable("timestamp", 0); + } + else + { + pChatWindow->m_bTimestamp = 1; + pChatWindow->m_bRedraw = 1; + if(pConfig) pConfig->SetIntVariable("timestamp", 1); + } + } } void cmdAudioMsg(PCHAR szCmd) { - // TODO: cmdAudioMsg .text:10068790 + if(pConfig->GetIntVariable("audiomsgoff") == 1) + { + pConfig->SetIntVariable("audiomsgoff", 0); + if(pChatWindow) pChatWindow->AddInfoMessage("Audio messages: On"); + } + else + { + pConfig->SetIntVariable("audiomsgoff", 1); + if(pChatWindow) pChatWindow->AddInfoMessage("Audio messages: Off"); + } } void cmdLogUrls(PCHAR szCmd) { - // TODO: cmdLogUrls .text:10068800 + if(pConfig->GetIntVariable("logurls") == 1) + { + pConfig->SetIntVariable("logurls", 0); + if(pChatWindow) pChatWindow->AddInfoMessage("URL messages: Off"); + } + else + { + pConfig->SetIntVariable("logurls", 1); + if(pChatWindow) pChatWindow->AddInfoMessage("URL messages: On"); + } } void cmdHudScaleFix(PCHAR szCmd) @@ -92,67 +160,256 @@ void cmdMem(PCHAR szCmd) void cmdSetFrameLimit(PCHAR szCmd) { - // TODO: cmdSetFrameLimit .text:100688D0 + if(strlen(szCmd)) + { + unsigned int uiLimit = atoi(szCmd); + if(uiLimit >= 20 && uiLimit <= 90) + { + pGame->SetFrameLimit(uiLimit); + if(pConfig) pConfig->SetIntVariable("fpslimit", uiLimit); + pChatWindow->AddInfoMessage("-> Frame Limiter: %u", uiLimit); + } + else + { + pChatWindow->AddInfoMessage("-> Frame Limiter: valid amounts are 20-90", uiLimit); + } + } } void cmdHeadMove(PCHAR szCmd) { - // TODO: cmdSetFrameLimit .text:10068960 + CGame *pGame = ::pGame; + if(pGame) + { + if(pGame->m_bHeadMove) + { + pGame->m_bHeadMove = 0; + if(pConfig) pConfig->SetIntVariable("disableheadmove", 1); + pChatWindow->AddInfoMessage("-> Head movements disabled"); + } + else + { + pGame->m_bHeadMove = 1; + if(pConfig) pConfig->SetIntVariable("disableheadmove", 0); + pChatWindow->AddInfoMessage("-> Head movements enabled"); + } + } } void cmdQuit(PCHAR szCmd) { - // TODO: cmdQuit .text:100689E0 + ShutdownGame(); } void cmdCmpStat(PCHAR szCmd) { - // TODO: cmdCmpStat .text:100689F0 } void cmdSavePos(PCHAR szCmd) { - // TODO: cmdSavePos .text:10068A00 + CPlayerPed *pPlayerPed = pGame->FindPlayerPed(); + + char szString[264]; + sprintf(szString, "%s\\savedpositions.txt", sub_100C3AD0()); + + FILE *f = fopen(szString, "a"); + if(!f) + { + pChatWindow->AddDebugMessage("I can't open the savepositions.txt file for append."); + return; + } + + if(pPlayerPed->IsInVehicle()) + { + VEHICLE_TYPE *pVehicle = pPlayerPed->GetGtaVehicle(); + int iIndex = GamePool_Vehicle_GetIndex(pVehicle); + float fAngle; + ScriptCommand(&get_car_z_angle, iIndex, &fAngle); + fprintf(f, "AddStaticVehicle(%u,%.4f,%.4f,%.4f,%.4f,%u,%u); // %s\n", + pVehicle->entity.nModelIndex, + pVehicle->entity.mat->pos.X, + pVehicle->entity.mat->pos.Y, + pVehicle->entity.mat->pos.Z, + fAngle, + pVehicle->byteColor1, + pVehicle->byteColor2, + szCmd); + fclose(f); + pChatWindow->AddInfoMessage("-> InCar position saved"); + } + else + { + PED_TYPE *pPed = pPlayerPed->m_pPed; + float fAngle; + ScriptCommand(&get_actor_z_angle, pPlayerPed->m_dwGTAId, &fAngle); + fprintf(f, "AddPlayerClass(%u,%.4f,%.4f,%.4f,%.4f,0,0,0,0,0,0); // %s\n", + pPlayerPed->GetModelIndex(), + pPed->entity.mat->pos.X, + pPed->entity.mat->pos.Y, + pPed->entity.mat->pos.Z, + fAngle, + szCmd); + fclose(f); + pChatWindow->AddInfoMessage("-> OnFoot position saved"); + } } void cmdRawSavePos(PCHAR szCmd) { - // TODO: cmdRawSavePos .text:10068B80 + CPlayerPed *pPlayerPed = pGame->FindPlayerPed(); + float fAngle; + FILE *f; + PED_TYPE *pPed; + char szString[264]; + + if(pPlayerPed->IsInVehicle()) + { + f = fopen("rawvehicles.txt", "a"); + if(!f) goto cant_open; + VEHICLE_TYPE *pVehicle = pPlayerPed->GetGtaVehicle(); + int iIndex = GamePool_Vehicle_GetIndex(pVehicle); + ScriptCommand(&get_car_z_angle, iIndex, &fAngle); + fprintf(f, "%u,%.4f,%.4f,%.4f,%.4f,%u,%u ; %s\n", + pVehicle->entity.nModelIndex, + pVehicle->entity.mat->pos.X, + pVehicle->entity.mat->pos.Y, + pVehicle->entity.mat->pos.Z, + fAngle, + pVehicle->byteColor1, + pVehicle->byteColor2, + szCmd); + fclose(f); + pChatWindow->AddDebugMessage("-> InCar pos saved"); + return; + } + + pPed = pPlayerPed->m_pPed; + ScriptCommand(&get_actor_z_angle, pPlayerPed->m_dwGTAId, &fAngle); + sprintf(szString, "%s\\rawpositions.txt", sub_100C3AD0()); + f = fopen(szString, "a"); + if(!f) + { +cant_open: + pChatWindow->AddDebugMessage("I can't open the rawvehicles.txt file for append."); + return; + } + fprintf(f, "%.4f,%.4f,%.4f,%.4f ; %s\n", + pPed->entity.mat->pos.X, + pPed->entity.mat->pos.Y, + pPed->entity.mat->pos.Z, + fAngle, + szCmd); + fclose(f); + pChatWindow->AddDebugMessage("-> OnFoot pos saved"); } void cmdPlayerSkin(PCHAR szCmd) { - // TODO: cmdPlayerSkin .text:10068D00 + if(tSettings.bDebug) + { + if(!strlen(szCmd)) + { + pChatWindow->AddDebugMessage("Usage: player_skin (skin number)."); + } + else + { + int iSkin = atoi(szCmd); + if(pGame->sub_100A0930()) + { + CPlayerPed *pPlayerPed = pGame->FindPlayerPed(); + if(pPlayerPed) + { + pPlayerPed->SetSkin(iSkin); + } + } + } + } } void cmdCreateVehicle(PCHAR szCmd) { - // TODO: cmdCreateVehicle .text:10068D70 + if(tSettings.bDebug) + { + if(!strlen(szCmd)) + { + pChatWindow->AddDebugMessage("Usage: /v (vehicle id)."); + } + else + { + int iModel = atoi(szCmd); + if(pGame->sub_100A0930()) + { + pGame->sub_100A0940(iModel, 2); + pGame->sub_100A0960(); + CPlayerPed *pPlayerPed = pGame->FindPlayerPed(); + if(pPlayerPed) + { + MATRIX4X4 mat; + pPlayerPed->GetMatrix(&mat); + char szName[9] = {0}; + sprintf(szName, "TYPE_%d", iModel); + pGame->sub_100A0250(iModel, mat.pos.X - 5.0f, mat.pos.Y - 5.0f, mat.pos.Z + 1.0f, 0, 0)->Add(); + } + else + { + pChatWindow->AddDebugMessage("I couldn't find the player actor."); + } + } + else + { + pChatWindow->AddDebugMessage("game not loaded."); + } + } + } } void cmdSelectVehicle(PCHAR szCmd) { - // TODO: cmdSelectVehicle .text:10068EB0 + if(tSettings.bDebug) + { + VehicleSelect(0, 0, 10); + } } void cmdSetWeather(PCHAR szCmd) { - // TODO: cmdSetWeather .text:10068ED0 + if(!strlen(szCmd)) + { + pChatWindow->AddDebugMessage("Usage: set_weather (weather number)"); + return; + } + pGame->SetWorldWeather(atoi(szCmd)); } void cmdSetTime(PCHAR szCmd) { - // TODO: cmdSetTime .text:10068F20 + if(!strlen(szCmd)) + { + pChatWindow->AddDebugMessage("Usage: set_time (hour) (minute)"); + return; + } + int iHour = 0, iMinute = 0; + sscanf(szCmd, "%d%d", &iHour, &iMinute); + if(iHour < 0 || iHour > 23 || iMinute < 0 || iMinute > 59) + { + pChatWindow->AddDebugMessage("Invalid Time. Use /set_time (hour 0-23) (minute 0-59)"); + } + else + { + pGame->SetWorldTime(iHour, iMinute); + } } void cmdShowInterior(PCHAR szCmd) { - // TODO: cmdShowInterior .text:10068FD0 + int iInterior; + ScriptCommand(&get_active_interior, &iInterior); + pChatWindow->AddDebugMessage("Current Interior: %u", iInterior); } void cmdToggleObjectLight(PCHAR szCmd) { - // TODO: cmdToggleObjectLight .text:10069000 + bToggleObjectLight = !bToggleObjectLight; } void cmdDebugLabels(PCHAR szCmd) @@ -162,7 +419,18 @@ void cmdDebugLabels(PCHAR szCmd) void cmdRcon(PCHAR szCmd) { - // TODO: cmdRcon .text:10069030 + if(szCmd) + { + if(pNetGame) + { + RakNet::BitStream bsSend; + bsSend.Write((BYTE)201); + int iStrlen = strlen(szCmd); + bsSend.Write(iStrlen); + bsSend.Write(szCmd, iStrlen); + pNetGame->GetRakClient()->Send(&bsSend, HIGH_PRIORITY, RELIABLE, 0); + } + } } //---------------------------------------------------- diff --git a/saco/cmdwindow.cpp b/saco/cmdwindow.cpp index d3c708d..a4c4bc2 100644 --- a/saco/cmdwindow.cpp +++ b/saco/cmdwindow.cpp @@ -3,6 +3,7 @@ extern CGame *pGame; extern CConfig *pConfig; +extern CChatWindow *pChatWindow; //---------------------------------------------------- @@ -70,10 +71,21 @@ void CCmdWindow::Enable() RECT rect; GetClientRect(pGame->GetMainWindowHwnd(), &rect); + int iWidth = (int)(rect.right * 0.6f); + if(iWidth > 800) iWidth = 800; + m_pEditControl->SetEnabled(true); m_pEditControl->SetVisible(true); - // TODO: CCmdWindow::Enable() + m_pEditControl->SetLocation(40, pChatWindow->field_12E); + m_pEditControl->SetSize(iWidth, 14 - (int)(m_pGameUI->GetFont(0)->nHeight * -1.5f)); + m_pGameUI->RequestFocus(m_pEditControl); + m_pEditControl->OnFocusIn(); + + int iField = pChatWindow->field_12E; + CDXUTDialog *pUI = m_pGameUI; + *(int *)((char *)pUI + 0x11E) = iWidth + 100; + *(int *)((char *)pUI + 0x122) = iField + 50; } m_bEnabled = TRUE; diff --git a/saco/cmdwindow.h b/saco/cmdwindow.h index 9fb44b7..3bf34a4 100644 --- a/saco/cmdwindow.h +++ b/saco/cmdwindow.h @@ -35,6 +35,11 @@ public: void GetDialogSize(RECT *pRect); + // 0x14E0 falls inside m_szCmdNames, so the accessor reaches it by a cast + int GetField14E0() { return *(int *)((char *)this + 0x14E0); }; // .text:10002E50, out of line copy + + bool IsImeActive(); // .text:10069810 + void Enable(); void Disable(); diff --git a/saco/d3d9/common/DXUT.cpp b/saco/d3d9/common/DXUT.cpp index 523b830..abfad7d 100644 --- a/saco/d3d9/common/DXUT.cpp +++ b/saco/d3d9/common/DXUT.cpp @@ -1946,7 +1946,9 @@ float DXUTRankDeviceCombo( CD3DEnumDeviceSettingsCombo* pDeviceSettingsCombo, } else { - int nBitDepthDelta = abs( (long) DXUTColorChannelBits(pDeviceSettingsCombo->AdapterFormat) - + // hoisted: retail evaluates the combo side first here (unlike the back buffer block below) + long nAdapterBits = (long) DXUTColorChannelBits(pDeviceSettingsCombo->AdapterFormat); + int nBitDepthDelta = abs( nAdapterBits - (long) DXUTColorChannelBits(pOptimalDeviceSettings->AdapterFormat) ); float fScale = __max(0.9f - (float)nBitDepthDelta*0.2f, 0.0f); fCurRanking += fScale * fAdapterFormatWeight; @@ -2713,11 +2715,11 @@ HRESULT DXUTChangeDevice( DXUTDeviceSettings* pNewDeviceSettings, IDirect3DDevic // If different device windows are used for windowed mode and fullscreen mode, // hide the fullscreen window so that it doesn't obscure the screen. - if( DXUTGetHWNDDeviceFullScreen() != DXUTGetHWNDDeviceWindowed() ) + if( DXUTGetHWNDDeviceWindowed() != DXUTGetHWNDDeviceFullScreen() ) ShowWindow( DXUTGetHWNDDeviceFullScreen(), SW_HIDE ); // If using the same window for windowed and fullscreen mode, reattach menu if one exists - if( DXUTGetHWNDDeviceFullScreen() == DXUTGetHWNDDeviceWindowed() ) + if( DXUTGetHWNDDeviceWindowed() == DXUTGetHWNDDeviceFullScreen() ) { if( GetDXUTState().GetMenu() != NULL ) SetMenu( DXUTGetHWNDDeviceWindowed(), GetDXUTState().GetMenu() ); @@ -2752,7 +2754,7 @@ HRESULT DXUTChangeDevice( DXUTDeviceSettings* pNewDeviceSettings, IDirect3DDevic SetWindowLong( DXUTGetHWNDDeviceFullScreen(), GWL_STYLE, WS_POPUP|WS_SYSMENU ); // If using the same window for windowed and fullscreen mode, save and remove menu - if( DXUTGetHWNDDeviceFullScreen() == DXUTGetHWNDDeviceWindowed() ) + if( DXUTGetHWNDDeviceWindowed() == DXUTGetHWNDDeviceFullScreen() ) { HMENU hMenu = GetMenu( DXUTGetHWNDDeviceFullScreen() ); GetDXUTState().SetMenu( hMenu ); diff --git a/saco/d3d9/common/DXUTSettingsDlg.cpp b/saco/d3d9/common/DXUTSettingsDlg.cpp index c2dd357..f0eb2fc 100644 --- a/saco/d3d9/common/DXUTSettingsDlg.cpp +++ b/saco/d3d9/common/DXUTSettingsDlg.cpp @@ -26,6 +26,7 @@ TCHAR* DXUTVertexProcessingTypeToString(DWORD vpt); //-------------------------------------------------------------------------------------- DXUTDeviceSettings g_DeviceSettings; +// MATCH CD3DSettingsDlg* DXUTGetSettingsDialog() { // Using an accessor function gives control of the construction order @@ -35,6 +36,7 @@ CD3DSettingsDlg* DXUTGetSettingsDialog() //-------------------------------------------------------------------------------------- +// MATCH CD3DSettingsDlg::CD3DSettingsDlg() { m_pStateBlock = NULL; @@ -43,6 +45,7 @@ CD3DSettingsDlg::CD3DSettingsDlg() //-------------------------------------------------------------------------------------- +// MATCH CD3DSettingsDlg::~CD3DSettingsDlg() { OnDestroyDevice(); @@ -137,6 +140,7 @@ void CD3DSettingsDlg::CreateControls() //-------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::OnCreateDevice( IDirect3DDevice9* pd3dDevice ) { if( pd3dDevice == NULL ) @@ -152,6 +156,7 @@ HRESULT CD3DSettingsDlg::OnCreateDevice( IDirect3DDevice9* pd3dDevice ) //-------------------------------------------------------------------------------------- // Changes the UI defaults to the current device settings //-------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::Refresh() { HRESULT hr = S_OK; @@ -217,6 +222,7 @@ HRESULT CD3DSettingsDlg::Refresh() //-------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::OnResetDevice() { const D3DSURFACE_DESC* pDesc = DXUTGetBackBufferSurfaceDesc(); @@ -238,6 +244,7 @@ HRESULT CD3DSettingsDlg::OnResetDevice() //-------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::OnRender( float fElapsedTime ) { IDirect3DDevice9* pd3dDevice = DXUTGetD3DDevice(); @@ -260,6 +267,7 @@ HRESULT CD3DSettingsDlg::OnRender( float fElapsedTime ) //-------------------------------------------------------------------------------------- +// MATCH LRESULT CD3DSettingsDlg::MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { m_Dialog.MsgProc( hWnd, uMsg, wParam, lParam ); @@ -270,6 +278,7 @@ LRESULT CD3DSettingsDlg::MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lP //-------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::OnLostDevice() { SAFE_RELEASE( m_pStateBlock ); @@ -278,6 +287,7 @@ HRESULT CD3DSettingsDlg::OnLostDevice() //-------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::OnDestroyDevice() { m_Dialog.ClearFocus(); @@ -286,6 +296,7 @@ HRESULT CD3DSettingsDlg::OnDestroyDevice() //-------------------------------------------------------------------------------------- +// MATCH void WINAPI CD3DSettingsDlg::StaticOnEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserData ) { @@ -296,6 +307,7 @@ void WINAPI CD3DSettingsDlg::StaticOnEvent( UINT nEvent, int nControlID, //-------------------------------------------------------------------------------------- +// MATCH void CD3DSettingsDlg::OnEvent( UINT nEvent, int nControlID, CDXUTControl* pControl ) { @@ -356,6 +368,7 @@ void CD3DSettingsDlg::OnEvent( UINT nEvent, int nControlID, //------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::SetDeviceSettingsFromUI() { CDXUTComboBox* pComboBox; @@ -405,6 +418,7 @@ HRESULT CD3DSettingsDlg::SetDeviceSettingsFromUI() //------------------------------------------------------------------------------------- +// MATCH CD3DEnumAdapterInfo* CD3DSettingsDlg::GetCurrentAdapterInfo() { CD3DEnumeration* pD3DEnum = DXUTGetEnumeration(); @@ -413,6 +427,7 @@ CD3DEnumAdapterInfo* CD3DSettingsDlg::GetCurrentAdapterInfo() //------------------------------------------------------------------------------------- +// MATCH CD3DEnumDeviceInfo* CD3DSettingsDlg::GetCurrentDeviceInfo() { CD3DEnumeration* pD3DEnum = DXUTGetEnumeration(); @@ -422,6 +437,7 @@ CD3DEnumDeviceInfo* CD3DSettingsDlg::GetCurrentDeviceInfo() //------------------------------------------------------------------------------------- +// MATCH CD3DEnumDeviceSettingsCombo* CD3DSettingsDlg::GetCurrentDeviceSettingsCombo() { CD3DEnumeration* pD3DEnum = DXUTGetEnumeration(); @@ -434,6 +450,7 @@ CD3DEnumDeviceSettingsCombo* CD3DSettingsDlg::GetCurrentDeviceSettingsCombo() //------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::OnAdapterChanged() { HRESULT hr = S_OK; @@ -467,6 +484,7 @@ HRESULT CD3DSettingsDlg::OnAdapterChanged() //------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::OnDeviceTypeChanged() { HRESULT hr = S_OK; @@ -507,6 +525,7 @@ HRESULT CD3DSettingsDlg::OnDeviceTypeChanged() //------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::OnWindowedFullScreenChanged() { HRESULT hr = S_OK; @@ -627,6 +646,7 @@ HRESULT CD3DSettingsDlg::OnWindowedFullScreenChanged() //------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::OnAdapterFormatChanged() { HRESULT hr = S_OK; @@ -707,6 +727,7 @@ HRESULT CD3DSettingsDlg::OnAdapterFormatChanged() //------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::OnResolutionChanged() { HRESULT hr = S_OK; @@ -751,6 +772,7 @@ HRESULT CD3DSettingsDlg::OnResolutionChanged() //------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::OnRefreshRateChanged() { // Set refresh rate @@ -761,6 +783,7 @@ HRESULT CD3DSettingsDlg::OnRefreshRateChanged() //------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::OnBackBufferFormatChanged() { HRESULT hr = S_OK; @@ -863,6 +886,7 @@ HRESULT CD3DSettingsDlg::OnBackBufferFormatChanged() //------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::OnDepthStencilBufferFormatChanged() { HRESULT hr = S_OK; @@ -911,6 +935,7 @@ HRESULT CD3DSettingsDlg::OnDepthStencilBufferFormatChanged() //------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::OnMultisampleTypeChanged() { HRESULT hr = S_OK; @@ -953,6 +978,7 @@ HRESULT CD3DSettingsDlg::OnMultisampleTypeChanged() //------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::OnMultisampleQualityChanged() { g_DeviceSettings.pp.MultiSampleQuality = GetSelectedMultisampleQuality(); @@ -962,6 +988,7 @@ HRESULT CD3DSettingsDlg::OnMultisampleQualityChanged() //------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::OnVertexProcessingChanged() { DWORD dwBehavior = g_DeviceSettings.BehaviorFlags; @@ -985,6 +1012,7 @@ HRESULT CD3DSettingsDlg::OnVertexProcessingChanged() //------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::OnPresentIntervalChanged() { g_DeviceSettings.pp.PresentationInterval = GetSelectedPresentInterval(); @@ -994,6 +1022,7 @@ HRESULT CD3DSettingsDlg::OnPresentIntervalChanged() //------------------------------------------------------------------------------------- +// MATCH HRESULT CD3DSettingsDlg::OnDeviceClipChanged() { if( IsDeviceClip() ) @@ -1006,6 +1035,7 @@ HRESULT CD3DSettingsDlg::OnDeviceClipChanged() //------------------------------------------------------------------------------------- +// MATCH void CD3DSettingsDlg::AddAdapter( const TCHAR* strDescription, UINT iAdapter ) { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_ADAPTER ); @@ -1016,6 +1046,7 @@ void CD3DSettingsDlg::AddAdapter( const TCHAR* strDescription, UINT iAdapter ) //------------------------------------------------------------------------------------- +// MATCH UINT CD3DSettingsDlg::GetSelectedAdapter() { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_ADAPTER ); @@ -1025,6 +1056,7 @@ UINT CD3DSettingsDlg::GetSelectedAdapter() //------------------------------------------------------------------------------------- +// MATCH void CD3DSettingsDlg::AddDeviceType( D3DDEVTYPE devType ) { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_DEVICE_TYPE ); @@ -1035,6 +1067,7 @@ void CD3DSettingsDlg::AddDeviceType( D3DDEVTYPE devType ) //------------------------------------------------------------------------------------- +// MATCH D3DDEVTYPE CD3DSettingsDlg::GetSelectedDeviceType() { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_DEVICE_TYPE ); @@ -1044,6 +1077,7 @@ D3DDEVTYPE CD3DSettingsDlg::GetSelectedDeviceType() //------------------------------------------------------------------------------------- +// MATCH void CD3DSettingsDlg::SetWindowed( bool bWindowed ) { CDXUTRadioButton* pRadioButton = m_Dialog.GetRadioButton( DXUTSETTINGSDLG_WINDOWED ); @@ -1055,6 +1089,7 @@ void CD3DSettingsDlg::SetWindowed( bool bWindowed ) //------------------------------------------------------------------------------------- +// MATCH bool CD3DSettingsDlg::IsWindowed() { CDXUTRadioButton* pRadioButton = m_Dialog.GetRadioButton( DXUTSETTINGSDLG_WINDOWED ); @@ -1063,6 +1098,7 @@ bool CD3DSettingsDlg::IsWindowed() //------------------------------------------------------------------------------------- +// MATCH void CD3DSettingsDlg::AddAdapterFormat( D3DFORMAT format ) { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_ADAPTER_FORMAT ); @@ -1073,6 +1109,7 @@ void CD3DSettingsDlg::AddAdapterFormat( D3DFORMAT format ) //------------------------------------------------------------------------------------- +// MATCH D3DFORMAT CD3DSettingsDlg::GetSelectedAdapterFormat() { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_ADAPTER_FORMAT ); @@ -1082,6 +1119,7 @@ D3DFORMAT CD3DSettingsDlg::GetSelectedAdapterFormat() //------------------------------------------------------------------------------------- +// MATCH void CD3DSettingsDlg::AddResolution( DWORD dwWidth, DWORD dwHeight ) { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_RESOLUTION ); @@ -1097,6 +1135,7 @@ void CD3DSettingsDlg::AddResolution( DWORD dwWidth, DWORD dwHeight ) //------------------------------------------------------------------------------------- +// MATCH void CD3DSettingsDlg::GetSelectedResolution( DWORD* pdwWidth, DWORD* pdwHeight ) { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_RESOLUTION ); @@ -1109,6 +1148,7 @@ void CD3DSettingsDlg::GetSelectedResolution( DWORD* pdwWidth, DWORD* pdwHeight ) //------------------------------------------------------------------------------------- +// MATCH void CD3DSettingsDlg::AddRefreshRate( DWORD dwRate ) { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_REFRESH_RATE ); @@ -1126,6 +1166,7 @@ void CD3DSettingsDlg::AddRefreshRate( DWORD dwRate ) //------------------------------------------------------------------------------------- +// MATCH DWORD CD3DSettingsDlg::GetSelectedRefreshRate() { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_REFRESH_RATE ); @@ -1135,6 +1176,7 @@ DWORD CD3DSettingsDlg::GetSelectedRefreshRate() //------------------------------------------------------------------------------------- +// MATCH void CD3DSettingsDlg::AddBackBufferFormat( D3DFORMAT format ) { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_BACK_BUFFER_FORMAT ); @@ -1145,6 +1187,7 @@ void CD3DSettingsDlg::AddBackBufferFormat( D3DFORMAT format ) //------------------------------------------------------------------------------------- +// MATCH D3DFORMAT CD3DSettingsDlg::GetSelectedBackBufferFormat() { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_BACK_BUFFER_FORMAT ); @@ -1154,6 +1197,7 @@ D3DFORMAT CD3DSettingsDlg::GetSelectedBackBufferFormat() //------------------------------------------------------------------------------------- +// MATCH void CD3DSettingsDlg::AddDepthStencilBufferFormat( D3DFORMAT format ) { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_DEPTH_STENCIL ); @@ -1164,6 +1208,7 @@ void CD3DSettingsDlg::AddDepthStencilBufferFormat( D3DFORMAT format ) //------------------------------------------------------------------------------------- +// MATCH D3DFORMAT CD3DSettingsDlg::GetSelectedDepthStencilBufferFormat() { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_DEPTH_STENCIL ); @@ -1173,6 +1218,7 @@ D3DFORMAT CD3DSettingsDlg::GetSelectedDepthStencilBufferFormat() //------------------------------------------------------------------------------------- +// MATCH void CD3DSettingsDlg::AddMultisampleType( D3DMULTISAMPLE_TYPE type ) { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_MULTISAMPLE_TYPE ); @@ -1183,6 +1229,7 @@ void CD3DSettingsDlg::AddMultisampleType( D3DMULTISAMPLE_TYPE type ) //------------------------------------------------------------------------------------- +// MATCH D3DMULTISAMPLE_TYPE CD3DSettingsDlg::GetSelectedMultisampleType() { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_MULTISAMPLE_TYPE ); @@ -1192,6 +1239,7 @@ D3DMULTISAMPLE_TYPE CD3DSettingsDlg::GetSelectedMultisampleType() //------------------------------------------------------------------------------------- +// MATCH void CD3DSettingsDlg::AddMultisampleQuality( DWORD dwQuality ) { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_MULTISAMPLE_QUALITY ); @@ -1205,6 +1253,7 @@ void CD3DSettingsDlg::AddMultisampleQuality( DWORD dwQuality ) //------------------------------------------------------------------------------------- +// MATCH DWORD CD3DSettingsDlg::GetSelectedMultisampleQuality() { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_MULTISAMPLE_QUALITY ); @@ -1214,6 +1263,7 @@ DWORD CD3DSettingsDlg::GetSelectedMultisampleQuality() //------------------------------------------------------------------------------------- +// MATCH void CD3DSettingsDlg::AddVertexProcessingType( DWORD dwType ) { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_VERTEX_PROCESSING ); @@ -1224,6 +1274,7 @@ void CD3DSettingsDlg::AddVertexProcessingType( DWORD dwType ) //------------------------------------------------------------------------------------- +// MATCH DWORD CD3DSettingsDlg::GetSelectedVertexProcessingType() { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_VERTEX_PROCESSING ); @@ -1244,6 +1295,7 @@ void CD3DSettingsDlg::AddPresentInterval( DWORD dwInterval ) //------------------------------------------------------------------------------------- +// MATCH DWORD CD3DSettingsDlg::GetSelectedPresentInterval() { CDXUTComboBox* pComboBox = m_Dialog.GetComboBox( DXUTSETTINGSDLG_PRESENT_INTERVAL ); @@ -1253,6 +1305,7 @@ DWORD CD3DSettingsDlg::GetSelectedPresentInterval() //------------------------------------------------------------------------------------- +// MATCH void CD3DSettingsDlg::SetDeviceClip( bool bDeviceClip ) { CDXUTCheckBox* pCheckBox = m_Dialog.GetCheckBox( DXUTSETTINGSDLG_DEVICECLIP ); @@ -1261,6 +1314,7 @@ void CD3DSettingsDlg::SetDeviceClip( bool bDeviceClip ) //------------------------------------------------------------------------------------- +// MATCH bool CD3DSettingsDlg::IsDeviceClip() { CDXUTCheckBox* pCheckBox = m_Dialog.GetCheckBox( DXUTSETTINGSDLG_DEVICECLIP ); diff --git a/saco/d3d9/common/DXUTUniBuffer.cpp b/saco/d3d9/common/DXUTUniBuffer.cpp new file mode 100644 index 0000000..4c4090c --- /dev/null +++ b/saco/d3d9/common/DXUTUniBuffer.cpp @@ -0,0 +1,550 @@ +//-------------------------------------------------------------------------------------- +// File: DXUTUniBuffer.cpp +// +// Desc: CUniBuffer, the Uniscribe-backed text buffer behind the edit controls +// +// Copyright (c) Microsoft Corporation. All rights reserved. +//-------------------------------------------------------------------------------------- +#include "dxstdafx.h" +// omits operator delete's throw(), which forces unwind frames into +// every dtor that deletes +#include +#include "DXUTgui.h" + +#define UNISCRIBE_DLLNAME "\\usp10.dll" + +#define GETPROCADDRESS( Module, APIName, Temp ) \ + Temp = GetProcAddress( Module, #APIName ); \ + if( Temp ) \ + *(FARPROC*)&_##APIName = Temp + +#define PLACEHOLDERPROC( APIName ) \ + _##APIName = Dummy_##APIName + + +// DXUT_MAX_EDITBOXLENGTH is the maximum string length allowed in edit boxes, +// including the NULL terminator. +// +// Uniscribe does not support strings having bigger-than-16-bits length. +// This means that the string must be less than 65536 characters long, +// including the NULL terminator. +#define DXUT_MAX_EDITBOXLENGTH 0xFFFF + +// stays in DXUTgui.cpp; retail reaches it across the tu boundary +LPCWSTR MaskText( LPCWSTR wszText ); + + +// Static member initialization +HINSTANCE CUniBuffer::s_hDll = NULL; +HRESULT (WINAPI *CUniBuffer::_ScriptApplyDigitSubstitution)( const SCRIPT_DIGITSUBSTITUTE*, SCRIPT_CONTROL*, SCRIPT_STATE* ) = Dummy_ScriptApplyDigitSubstitution; +HRESULT (WINAPI *CUniBuffer::_ScriptStringAnalyse)( HDC, const void *, int, int, int, DWORD, int, SCRIPT_CONTROL*, SCRIPT_STATE*, const int*, SCRIPT_TABDEF*, const BYTE*, SCRIPT_STRING_ANALYSIS* ) = Dummy_ScriptStringAnalyse; +HRESULT (WINAPI *CUniBuffer::_ScriptStringCPtoX)( SCRIPT_STRING_ANALYSIS, int, BOOL, int* ) = Dummy_ScriptStringCPtoX; +HRESULT (WINAPI *CUniBuffer::_ScriptStringXtoCP)( SCRIPT_STRING_ANALYSIS, int, int*, int* ) = Dummy_ScriptStringXtoCP; +HRESULT (WINAPI *CUniBuffer::_ScriptStringFree)( SCRIPT_STRING_ANALYSIS* ) = Dummy_ScriptStringFree; +const SCRIPT_LOGATTR* (WINAPI *CUniBuffer::_ScriptString_pLogAttr)( SCRIPT_STRING_ANALYSIS ) = Dummy_ScriptString_pLogAttr; +const int* (WINAPI *CUniBuffer::_ScriptString_pcOutChars)( SCRIPT_STRING_ANALYSIS ) = Dummy_ScriptString_pcOutChars; + + +//-------------------------------------------------------------------------------------- +void CUniBuffer::Initialize() +{ + if( s_hDll ) // Only need to do once + return; + + TCHAR wszPath[MAX_PATH+1]; + if( !::GetSystemDirectory( wszPath, MAX_PATH+1 ) ) + return; + + // Verify whether it is safe to concatenate these strings + int len1 = lstrlen(wszPath); + int len2 = lstrlen(UNISCRIBE_DLLNAME); + if (len1 + len2 > MAX_PATH) + { + return; + } + + // We have verified that the concatenated string will fit into wszPath, + // so it is safe to concatenate them. + StringCchCat( wszPath, MAX_PATH, UNISCRIBE_DLLNAME ); + + s_hDll = LoadLibrary( wszPath ); + if( s_hDll ) + { + FARPROC Temp; + GETPROCADDRESS( s_hDll, ScriptApplyDigitSubstitution, Temp ); + GETPROCADDRESS( s_hDll, ScriptStringAnalyse, Temp ); + GETPROCADDRESS( s_hDll, ScriptStringCPtoX, Temp ); + GETPROCADDRESS( s_hDll, ScriptStringXtoCP, Temp ); + GETPROCADDRESS( s_hDll, ScriptStringFree, Temp ); + GETPROCADDRESS( s_hDll, ScriptString_pLogAttr, Temp ); + GETPROCADDRESS( s_hDll, ScriptString_pcOutChars, Temp ); + } +} + + +//-------------------------------------------------------------------------------------- +void CUniBuffer::Uninitialize() +{ + if( s_hDll ) + { + PLACEHOLDERPROC( ScriptApplyDigitSubstitution ); + PLACEHOLDERPROC( ScriptStringAnalyse ); + PLACEHOLDERPROC( ScriptStringCPtoX ); + PLACEHOLDERPROC( ScriptStringXtoCP ); + PLACEHOLDERPROC( ScriptStringFree ); + PLACEHOLDERPROC( ScriptString_pLogAttr ); + PLACEHOLDERPROC( ScriptString_pcOutChars ); + + FreeLibrary( s_hDll ); + s_hDll = NULL; + } +} + + +//-------------------------------------------------------------------------------------- +bool CUniBuffer::SetBufferSize( int nNewSize ) +{ + // If the current size is already the maximum allowed, + // we can't possibly allocate more. + if( m_nBufferSize == DXUT_MAX_EDITBOXLENGTH ) + return false; + + int nAllocateSize = ( nNewSize == -1 || nNewSize < m_nBufferSize * 2 ) ? ( m_nBufferSize ? m_nBufferSize * 2 : 256 ) : nNewSize * 2; + + // Cap the buffer size at the maximum allowed. + if( nAllocateSize > DXUT_MAX_EDITBOXLENGTH ) + nAllocateSize = DXUT_MAX_EDITBOXLENGTH; + + WCHAR *pTempBuffer = new WCHAR[nAllocateSize]; + if( !pTempBuffer ) + return false; + if( m_pwszBuffer ) + { + CopyMemory( pTempBuffer, m_pwszBuffer, m_nBufferSize * sizeof(WCHAR) ); + delete[] m_pwszBuffer; + } + else + { + ZeroMemory( pTempBuffer, sizeof(WCHAR) * nAllocateSize ); + } + + m_pwszBuffer = pTempBuffer; + m_nBufferSize = nAllocateSize; + return true; +} + + +//-------------------------------------------------------------------------------------- +// Uniscribe -- Analyse() analyses the string in the buffer +//-------------------------------------------------------------------------------------- +HRESULT CUniBuffer::Analyse() +{ + if( m_Analysis ) + _ScriptStringFree( &m_Analysis ); + + SCRIPT_CONTROL ScriptControl; // For uniscribe + SCRIPT_STATE ScriptState; // For uniscribe + ZeroMemory( &ScriptControl, sizeof(ScriptControl) ); + ZeroMemory( &ScriptState, sizeof(ScriptState) ); + _ScriptApplyDigitSubstitution ( NULL, &ScriptControl, &ScriptState ); + + if( !m_pFontNode ) + return E_FAIL; + + HRESULT hr; + if( !field_12 ) + { + hr = _ScriptStringAnalyse( m_pFontNode->pFont ? m_pFontNode->pFont->GetDC() : NULL, + m_pwszBuffer, + lstrlenW( m_pwszBuffer ) + 1, // NULL is also analyzed. + lstrlenW( m_pwszBuffer ) * 3 / 2 + 16, + -1, + SSA_BREAK | SSA_GLYPHS | SSA_FALLBACK | SSA_LINK, + 0, + &ScriptControl, + &ScriptState, + NULL, + NULL, + NULL, + &m_Analysis ); + } + else + { + hr = _ScriptStringAnalyse( m_pFontNode->pFont ? m_pFontNode->pFont->GetDC() : NULL, + MaskText( m_pwszBuffer ), + lstrlenW( MaskText( m_pwszBuffer ) ) + 1, // NULL is also analyzed. + lstrlenW( MaskText( m_pwszBuffer ) ) * 3 / 2 + 16, + -1, + SSA_BREAK | SSA_GLYPHS | SSA_FALLBACK | SSA_LINK, + 0, + &ScriptControl, + &ScriptState, + NULL, + NULL, + NULL, + &m_Analysis ); + } + if( SUCCEEDED( hr ) ) + m_bAnalyseRequired = false; // Analysis is up-to-date + return hr; +} + + +//-------------------------------------------------------------------------------------- +CUniBuffer::CUniBuffer( int nInitialSize ) +{ + CUniBuffer::Initialize(); // ensure static vars are properly init'ed first + + m_nBufferSize = 0; + m_pwszBuffer = NULL; + m_bAnalyseRequired = true; + m_Analysis = NULL; + m_pFontNode = NULL; + field_8 = 0; + field_12 = 0; + + if( nInitialSize > 0 ) + SetBufferSize( nInitialSize ); +} + + +//-------------------------------------------------------------------------------------- +CUniBuffer::~CUniBuffer() +{ + delete[] m_pwszBuffer; + if( m_Analysis ) + _ScriptStringFree( &m_Analysis ); +} + + +//-------------------------------------------------------------------------------------- +WCHAR& CUniBuffer::operator[]( int n ) // No param checking +{ + // This version of operator[] is called only + // if we are asking for write access, so + // re-analysis is required. + m_bAnalyseRequired = true; + return m_pwszBuffer[n]; +} + + +//-------------------------------------------------------------------------------------- +void CUniBuffer::Clear() +{ + *m_pwszBuffer = L'\0'; + m_bAnalyseRequired = true; +} + + +//-------------------------------------------------------------------------------------- +// Inserts the char at specified index. +// If nIndex == -1, insert to the end. +//-------------------------------------------------------------------------------------- +bool CUniBuffer::InsertChar( int nIndex, WCHAR tchr ) +{ + assert( nIndex >= 0 ); + + if( nIndex < 0 || nIndex > lstrlenW( m_pwszBuffer ) ) + return false; // invalid index + + // Check for maximum length allowed + if( GetTextSize() + 1 >= DXUT_MAX_EDITBOXLENGTH ) + return false; + + if( lstrlenW( m_pwszBuffer ) + 1 >= m_nBufferSize ) + { + if( !SetBufferSize( -1 ) ) + return false; // out of memory + } + + assert( m_nBufferSize >= 2 ); + + // Shift the characters after the index, start by copying the null terminator + WCHAR* dest = m_pwszBuffer + lstrlenW(m_pwszBuffer)+1; + WCHAR* stop = m_pwszBuffer + nIndex; + WCHAR* src = dest - 1; + + while( dest > stop ) + { + *dest-- = *src--; + } + + // Set new character + m_pwszBuffer[ nIndex ] = tchr; + m_bAnalyseRequired = true; + + return true; +} + +bool CUniBuffer::InsertChar(int nIndex, CHAR tchr) +{ + WCHAR WideCharStr[2]; + ZeroMemory(&WideCharStr[0], sizeof(WideCharStr)); + + if(field_8) + { + CHAR MultiByteStr[3]; + MultiByteStr[0] = field_8; + MultiByteStr[1] = tchr; + MultiByteStr[2] = 0; + MultiByteToWideChar(CP_ACP, 0, MultiByteStr, 2, WideCharStr, 1); + InsertChar(nIndex, WideCharStr[0]); + field_8 = 0; + return true; + } + else if(IsDBCSLeadByteEx(CP_ACP, tchr)) + { + field_8 = tchr; + return false; + } + else + { + MultiByteToWideChar(CP_ACP, 0, &tchr, 1, WideCharStr, 1); + InsertChar(nIndex, WideCharStr[0]); + return true; + } +} + +// overwrite variant of InsertChar; the buffer index assignment is samp's, not a typo of ours +bool CUniBuffer::SetChar(int nIndex, CHAR tchr) +{ + WCHAR WideCharStr[2]; + ZeroMemory(&WideCharStr[0], sizeof(WideCharStr)); + + if(field_8) + { + CHAR MultiByteStr[3]; + MultiByteStr[0] = field_8; + MultiByteStr[1] = tchr; + MultiByteStr[2] = 0; + MultiByteToWideChar(CP_ACP, 0, MultiByteStr, 2, WideCharStr, 1); + InsertChar(nIndex, WideCharStr[0]); + field_8 = 0; + return true; + } + else if(IsDBCSLeadByteEx(CP_ACP, tchr)) + { + field_8 = tchr; + return false; + } + else + { + MultiByteToWideChar(CP_ACP, 0, &tchr, 1, WideCharStr, 1); + this[nIndex] = WideCharStr[0]; + return true; + } +} + + +//-------------------------------------------------------------------------------------- +// Removes the char at specified index. +// If nIndex == -1, remove the last char. +//-------------------------------------------------------------------------------------- +bool CUniBuffer::RemoveChar( int nIndex ) +{ + if( !lstrlenW( m_pwszBuffer ) || nIndex < 0 || nIndex >= lstrlenW( m_pwszBuffer ) ) + return false; // Invalid index + + MoveMemory( m_pwszBuffer + nIndex, m_pwszBuffer + nIndex + 1, sizeof(WCHAR) * ( lstrlenW( m_pwszBuffer ) - nIndex ) ); + m_bAnalyseRequired = true; + return true; +} + + +//-------------------------------------------------------------------------------------- +// Inserts the first nCount characters of the string pStr at specified index. +// If nCount == -1, the entire string is inserted. +// If nIndex == -1, insert to the end. +//-------------------------------------------------------------------------------------- +bool CUniBuffer::InsertString( int nIndex, const WCHAR *pStr, int nCount ) +{ + assert( nIndex >= 0 ); + + if( nIndex > lstrlenW( m_pwszBuffer ) ) + return false; // invalid index + + if( -1 == nCount ) + nCount = lstrlenW( pStr ); + + // Check for maximum length allowed + if( GetTextSize() + nCount >= DXUT_MAX_EDITBOXLENGTH ) + return false; + + if( lstrlenW( m_pwszBuffer ) + nCount >= m_nBufferSize ) + { + if( !SetBufferSize( lstrlenW( m_pwszBuffer ) + nCount + 1 ) ) + return false; // out of memory + } + + MoveMemory( m_pwszBuffer + nIndex + nCount, m_pwszBuffer + nIndex, sizeof(WCHAR) * ( lstrlenW( m_pwszBuffer ) - nIndex + 1 ) ); + CopyMemory( m_pwszBuffer + nIndex, pStr, nCount * sizeof(WCHAR) ); + m_bAnalyseRequired = true; + + return true; +} + + +//-------------------------------------------------------------------------------------- +bool CUniBuffer::SetText( LPCWSTR wszText ) +{ + assert( wszText != NULL ); + + int nRequired = int(lstrlenW( wszText ) + 1); + + // Check for maximum length allowed + if( nRequired >= DXUT_MAX_EDITBOXLENGTH ) + return false; + + while( GetBufferSize() < nRequired ) + if( !SetBufferSize( -1 ) ) + break; + // Check again in case out of memory occurred inside while loop. + if( GetBufferSize() >= nRequired ) + { + StringCchCopyW( m_pwszBuffer, GetBufferSize(), wszText ); + m_bAnalyseRequired = true; + return true; + } + else + return false; +} + + +//-------------------------------------------------------------------------------------- +void ConvertAnsiToWide(LPCSTR wszSrc, LPWSTR wszDest, int nSize) +{ + memset(wszDest, 0, sizeof(WCHAR) * nSize); + + int nCount = MultiByteToWideChar(CP_ACP, 0, wszSrc, strlen(wszSrc), NULL, 0); + if(nCount < nSize) + MultiByteToWideChar(CP_ACP, 0, wszSrc, strlen(wszSrc), wszDest, nCount); +} + + +//-------------------------------------------------------------------------------------- +bool CUniBuffer::SetText( LPCTSTR wszText ) +{ + if(lstrlenA(wszText) > 1024) return false; + + WCHAR szBuffer[2049]; + ConvertAnsiToWide(wszText, szBuffer, 2048); + SetText(szBuffer); + return true; +} + + +//-------------------------------------------------------------------------------------- +HRESULT CUniBuffer::CPtoX( int nCP, BOOL bTrail, int *pX ) +{ + assert( pX ); + *pX = 0; // Default + + HRESULT hr = S_OK; + if( m_bAnalyseRequired ) + hr = Analyse(); + + if( SUCCEEDED( hr ) ) + hr = _ScriptStringCPtoX( m_Analysis, nCP, bTrail, pX ); + + return hr; +} + + +//-------------------------------------------------------------------------------------- +HRESULT CUniBuffer::XtoCP( int nX, int *pCP, int *pnTrail ) +{ + assert( pCP && pnTrail ); + *pCP = 0; *pnTrail = FALSE; // Default + + HRESULT hr = S_OK; + if( m_bAnalyseRequired ) + hr = Analyse(); + + if( SUCCEEDED( hr ) ) + hr = _ScriptStringXtoCP( m_Analysis, nX, pCP, pnTrail ); + + // If the coordinate falls outside the text region, we + // can get character positions that don't exist. We must + // filter them here and convert them to those that do exist. + if( *pCP == -1 && *pnTrail == TRUE ) + { + *pCP = 0; *pnTrail = FALSE; + } else + if( *pCP > lstrlenW( m_pwszBuffer ) && *pnTrail == FALSE ) + { + *pCP = lstrlenW( m_pwszBuffer ); *pnTrail = TRUE; + } + + return hr; +} + + +//-------------------------------------------------------------------------------------- +void CUniBuffer::GetPriorItemPos( int nCP, int *pPrior ) +{ + *pPrior = nCP; // Default is the char itself + + if( m_bAnalyseRequired ) + if( FAILED( Analyse() ) ) + return; + + const SCRIPT_LOGATTR *pLogAttr = _ScriptString_pLogAttr( m_Analysis ); + if( !pLogAttr ) + return; + + if( !_ScriptString_pcOutChars( m_Analysis ) ) + return; + int nInitial = *_ScriptString_pcOutChars( m_Analysis ); + if( nCP - 1 < nInitial ) + nInitial = nCP - 1; + for( int i = nInitial; i > 0; --i ) + if( pLogAttr[i].fWordStop || // Either the fWordStop flag is set + ( !pLogAttr[i].fWhiteSpace && // Or the previous char is whitespace but this isn't. + pLogAttr[i-1].fWhiteSpace ) ) + { + *pPrior = i; + return; + } + // We have reached index 0. 0 is always a break point, so simply return it. + *pPrior = 0; +} + + +//-------------------------------------------------------------------------------------- +void CUniBuffer::GetNextItemPos( int nCP, int *pPrior ) +{ + *pPrior = nCP; // Default is the char itself + + HRESULT hr = S_OK; + if( m_bAnalyseRequired ) + hr = Analyse(); + if( FAILED( hr ) ) + return; + + const SCRIPT_LOGATTR *pLogAttr = _ScriptString_pLogAttr( m_Analysis ); + if( !pLogAttr ) + return; + + if( !_ScriptString_pcOutChars( m_Analysis ) ) + return; + int nInitial = *_ScriptString_pcOutChars( m_Analysis ); + if( nCP + 1 < nInitial ) + nInitial = nCP + 1; + for( int i = nInitial; i < *_ScriptString_pcOutChars( m_Analysis ) - 1; ++i ) + { + if( pLogAttr[i].fWordStop ) // Either the fWordStop flag is set + { + *pPrior = i; + return; + } + else + if( pLogAttr[i].fWhiteSpace && // Or this whitespace but the next char isn't. + !pLogAttr[i+1].fWhiteSpace ) + { + *pPrior = i+1; // The next char is a word stop + return; + } + } + // We have reached the end. It's always a word stop, so simply return it. + *pPrior = *_ScriptString_pcOutChars( m_Analysis ) - 1; +} diff --git a/saco/d3d9/common/DXUTgui.cpp b/saco/d3d9/common/DXUTgui.cpp index d9c4779..33da0f9 100644 --- a/saco/d3d9/common/DXUTgui.cpp +++ b/saco/d3d9/common/DXUTgui.cpp @@ -6,6 +6,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. //-------------------------------------------------------------------------------------- #include "dxstdafx.h" +// omits operator delete's throw(), which forces unwind frames into +// every dtor that deletes +#include #include "DXUTgui.h" #include "DXUTsettingsDlg.h" #undef min // use __min instead @@ -35,8 +38,6 @@ extern CGame *pGame; #define SCROLLBAR_ARROWCLICK_DELAY 0.33 #define SCROLLBAR_ARROWCLICK_REPEAT 0.05 -#define UNISCRIBE_DLLNAME "\\usp10.dll" - #define GETPROCADDRESS( Module, APIName, Temp ) \ Temp = GetProcAddress( Module, #APIName ); \ if( Temp ) \ @@ -467,7 +468,7 @@ HRESULT CDXUTDialog::OnRender( float fElapsedTime ) /* if( m_bMinimized ) StringCchCat( wszOutput, 256, " (Minimized)" );*/ - DrawText( wszOutput, &m_CapElement, &rc, true ); + DrawText( wszOutput, &m_CapElement, &rc, false ); } // If the dialog is minimized, skip rendering @@ -550,6 +551,27 @@ int CDXUTDialogResourceManager::AddFont( LPCTSTR strFaceName, LONG height, LONG } +//-------------------------------------------------------------------------------------- +// retains a cached font entry in place; unreferenced in retail +int CDXUTDialogResourceManager::SetFont( int iFont, LPCTSTR strFaceName, LONG height, LONG weight ) +{ + DXUTFontNode* pFontNode = m_FontCache.GetAt( iFont ); + if( pFontNode ) + { + StringCchCopy( pFontNode->strFace, MAX_PATH, strFaceName ); + pFontNode->nHeight = height; + pFontNode->nWeight = weight; + + if( m_pd3dDevice ) + CreateFont( iFont ); + + return iFont; + } + + return -1; +} + + //-------------------------------------------------------------------------------------- // MATCH HRESULT CDXUTDialog::SetFont( UINT index, LPCTSTR strFaceName, LONG height, LONG weight ) @@ -666,38 +688,6 @@ bool CDXUTDialog::MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) m_bKeyboardInput = true; } - // If caption is enable, check for clicks in the caption area. - if( m_bCaption ) - { - static bool bDrag; - - if( uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONDBLCLK ) - { - POINT mousePoint = { short(LOWORD(lParam)), short(HIWORD(lParam)) }; - - if( mousePoint.x >= m_x && mousePoint.x < m_x + m_width && - mousePoint.y >= m_y && mousePoint.y < m_y + m_nCaptionHeight ) - { - bDrag = true; - SetCapture( DXUTGetHWND() ); - return true; - } - } else - if( uMsg == WM_LBUTTONUP && bDrag ) - { - POINT mousePoint = { short(LOWORD(lParam)), short(HIWORD(lParam)) }; - - if( mousePoint.x >= m_x && mousePoint.x < m_x + m_width && - mousePoint.y >= m_y && mousePoint.y < m_y + m_nCaptionHeight ) - { - ReleaseCapture(); - bDrag = false; - m_bMinimized = !m_bMinimized; - return true; - } - } - } - // If the dialog is minimized, don't send any messages to controls. if( m_bMinimized ) return false; @@ -833,8 +823,6 @@ bool CDXUTDialog::MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) case WM_RBUTTONDBLCLK: case WM_MOUSEWHEEL: { - OutputDebugString("CDXUTDialog::MsgProc(MOUSE)"); - // If not accepting mouse input, return false to indicate the message should still // be handled by the application (usually to move the camera). if( !m_bMouseInput ) @@ -2498,8 +2486,6 @@ bool CDXUTButton::HandleKeyboard( UINT uMsg, WPARAM wParam, LPARAM lParam ) //-------------------------------------------------------------------------------------- bool CDXUTButton::HandleMouse( UINT uMsg, POINT pt, WPARAM wParam, LPARAM lParam ) { - OutputDebugString("CDXUTButton::HandleMouse"); - if( !m_bEnabled || !m_bVisible ) return false; @@ -2513,6 +2499,7 @@ bool CDXUTButton::HandleMouse( UINT uMsg, POINT pt, WPARAM wParam, LPARAM lParam // Pressed while inside the control m_bPressed = true; SetCapture( DXUTGetHWND() ); + m_pDialog->SendEvent( EVENT_BUTTON_PRESSED, true, this ); if( !m_bHasFocus ) m_pDialog->RequestFocus( this ); @@ -4219,6 +4206,9 @@ CDXUTListBox::CDXUTListBox( CDXUTDialog *pDialog ) : m_nBorder = 6; m_nMargin = 5; m_nTextHeight = 0; + field_4D = 0; + for( int i = 0; i < 3; i++ ) + field_51[i] = 0; } @@ -4243,7 +4233,7 @@ void CDXUTListBox::UpdateRects() // Update the scrollbar's rects m_ScrollBar.SetLocation( m_rcBoundingBox.right - m_nSBWidth, m_rcBoundingBox.top ); m_ScrollBar.SetSize( m_nSBWidth, m_height ); - DXUTFontNode* pFontNode = m_pDialog->GetManager()->GetFontNode( m_Elements.GetAt( 0 )->iFont ); + DXUTFontNode* pFontNode = m_pDialog->GetFont( m_Elements.GetAt( 0 )->iFont ); if( pFontNode && pFontNode->nHeight ) { m_ScrollBar.SetPageSize( RectHeight( m_rcText ) / pFontNode->nHeight ); @@ -4256,7 +4246,7 @@ void CDXUTListBox::UpdateRects() //-------------------------------------------------------------------------------------- -HRESULT CDXUTListBox::AddItem( const TCHAR *wszText, void *pData ) +HRESULT CDXUTListBox::AddItem( const TCHAR *wszText, void *pData, D3DCOLOR TextColor ) { DXUTListBoxItem *pNewItem = new DXUTListBoxItem; if( !pNewItem ) @@ -4266,6 +4256,10 @@ HRESULT CDXUTListBox::AddItem( const TCHAR *wszText, void *pData ) pNewItem->pData = pData; SetRect( &pNewItem->rcActive, 0, 0, 0, 0 ); pNewItem->bSelected = false; + pNewItem->TextColor = TextColor; + pNewItem->field_29D = false; + for( int i = 0; i < 3; i++ ) + ZeroMemory( pNewItem->strColumnText[i], 64 ); HRESULT hr = m_Items.Add( pNewItem ); if( FAILED(hr) ) @@ -4282,7 +4276,7 @@ HRESULT CDXUTListBox::AddItem( const TCHAR *wszText, void *pData ) //-------------------------------------------------------------------------------------- -HRESULT CDXUTListBox::InsertItem( int nIndex, const TCHAR *wszText, void *pData ) +HRESULT CDXUTListBox::InsertItem( int nIndex, const TCHAR *wszText, void *pData, D3DCOLOR TextColor ) { DXUTListBoxItem *pNewItem = new DXUTListBoxItem; if( !pNewItem ) @@ -4292,6 +4286,7 @@ HRESULT CDXUTListBox::InsertItem( int nIndex, const TCHAR *wszText, void *pData pNewItem->pData = pData; SetRect( &pNewItem->rcActive, 0, 0, 0, 0 ); pNewItem->bSelected = false; + pNewItem->TextColor = TextColor; HRESULT hr = m_Items.Insert( nIndex, pNewItem ); if( SUCCEEDED( hr ) ) @@ -4321,6 +4316,17 @@ void CDXUTListBox::RemoveItem( int nIndex ) } +//-------------------------------------------------------------------------------------- +void CDXUTListBox::SetItemColumnText( int nIndex, int nColumn, const TCHAR *wszText ) +{ + if( nIndex < 0 || nIndex >= m_Items.GetSize() || nColumn < 0 || nColumn >= field_4D ) + return; + + ZeroMemory( m_Items.GetAt( nIndex )->strColumnText[nColumn], 128 ); + strncpy( m_Items.GetAt( nIndex )->strColumnText[nColumn], wszText, 128 ); +} + + //-------------------------------------------------------------------------------------- void CDXUTListBox::RemoveItemByText( TCHAR *wszText ) { @@ -4558,7 +4564,6 @@ bool CDXUTListBox::HandleMouse( UINT uMsg, POINT pt, WPARAM wParam, LPARAM lPara nClicked < m_ScrollBar.GetTrackPos() + m_ScrollBar.GetPageSize() ) { SetCapture( DXUTGetHWND() ); - m_bDrag = true; // If this is a double click, fire off an event and exit // since the first click would have taken care of the selection @@ -4669,7 +4674,7 @@ bool CDXUTListBox::HandleMouse( UINT uMsg, POINT pt, WPARAM wParam, LPARAM lPara ReleaseCapture(); m_bDrag = false; - if( m_nSelected != -1 ) + if( m_nSelected < (int)m_Items.GetSize() && m_nSelected != -1 ) { // Set all items between m_nSelStart and m_nSelected to // the same state as m_nSelStart @@ -4759,13 +4764,16 @@ void CDXUTListBox::Render( IDirect3DDevice9* pd3dDevice, float fElapsedTime ) if( m_Items.GetSize() > 0 ) { // Find out the height of a single line of text - /*RECT rc = m_rcText; + RECT rc = m_rcText; RECT rcSel = m_rcSelection; - rc.bottom = rc.top + m_pDialog->GetManager()->GetFontNode( pElement->iFont )->nHeight; + rc.bottom = rc.top + m_pDialog->GetFont( pElement->iFont )->nHeight; // Update the line height formation m_nTextHeight = rc.bottom - rc.top; + if( m_ScrollBar.GetTrackPos() < 0 ) + m_ScrollBar.SetTrackPos( 0 ); + static bool bSBInit; if( !bSBInit ) { @@ -4803,17 +4811,44 @@ void CDXUTListBox::Render( IDirect3DDevice9* pd3dDevice, float fElapsedTime ) bSelectedStyle = true; } - if( bSelectedStyle ) + int nLeft; + + if( pItem->field_29D || !bSelectedStyle ) + { + if( pItem->TextColor ) + pElement->FontColor.Current = pItem->TextColor; + + m_pDialog->DrawText( pItem->strText, pElement, &rc ); + if( field_4D > 0 ) + { + nLeft = rc.left; + for( int iColumn = 0; iColumn < field_4D; ++iColumn ) + { + rc.left += field_51[iColumn]; + m_pDialog->DrawText( pItem->strColumnText[iColumn], pElement, &rc ); + } + rc.left = nLeft; + } + } + else { rcSel.top = rc.top; rcSel.bottom = rc.bottom; m_pDialog->DrawSprite( pSelElement, &rcSel ); m_pDialog->DrawText( pItem->strText, pSelElement, &rc ); + if( field_4D > 0 ) + { + nLeft = rc.left; + for( int iColumn = 0; iColumn < field_4D; ++iColumn ) + { + rc.left += field_51[iColumn]; + m_pDialog->DrawText( pItem->strColumnText[iColumn], pSelElement, &rc ); + } + rc.left = nLeft; + } } - else - m_pDialog->DrawText( pItem->strText, pElement, &rc ); OffsetRect( &rc, 0, m_nTextHeight ); - }*/ + } } // Render the scroll bar @@ -4823,14 +4858,6 @@ void CDXUTListBox::Render( IDirect3DDevice9* pd3dDevice, float fElapsedTime ) // Static member initialization -HINSTANCE CUniBuffer::s_hDll = NULL; -HRESULT (WINAPI *CUniBuffer::_ScriptApplyDigitSubstitution)( const SCRIPT_DIGITSUBSTITUTE*, SCRIPT_CONTROL*, SCRIPT_STATE* ) = Dummy_ScriptApplyDigitSubstitution; -HRESULT (WINAPI *CUniBuffer::_ScriptStringAnalyse)( HDC, const void *, int, int, int, DWORD, int, SCRIPT_CONTROL*, SCRIPT_STATE*, const int*, SCRIPT_TABDEF*, const BYTE*, SCRIPT_STRING_ANALYSIS* ) = Dummy_ScriptStringAnalyse; -HRESULT (WINAPI *CUniBuffer::_ScriptStringCPtoX)( SCRIPT_STRING_ANALYSIS, int, BOOL, int* ) = Dummy_ScriptStringCPtoX; -HRESULT (WINAPI *CUniBuffer::_ScriptStringXtoCP)( SCRIPT_STRING_ANALYSIS, int, int*, int* ) = Dummy_ScriptStringXtoCP; -HRESULT (WINAPI *CUniBuffer::_ScriptStringFree)( SCRIPT_STRING_ANALYSIS* ) = Dummy_ScriptStringFree; -const SCRIPT_LOGATTR* (WINAPI *CUniBuffer::_ScriptString_pLogAttr)( SCRIPT_STRING_ANALYSIS ) = Dummy_ScriptString_pLogAttr; -const int* (WINAPI *CUniBuffer::_ScriptString_pcOutChars)( SCRIPT_STRING_ANALYSIS ) = Dummy_ScriptString_pcOutChars; bool CDXUTEditBox::s_bHideCaret; // If true, we don't render the caret. @@ -5029,20 +5056,20 @@ void CDXUTEditBox::CopyToClipboard() { EmptyClipboard(); - HGLOBAL hBlock = GlobalAlloc( GMEM_MOVEABLE, sizeof(TCHAR) * ( m_Buffer.GetTextSize() + 1 ) ); + HGLOBAL hBlock = GlobalAlloc( GMEM_MOVEABLE, sizeof(WCHAR) * ( m_Buffer.GetTextSize() + 1 ) ); if( hBlock ) { - TCHAR *pwszText = (TCHAR*)GlobalLock( hBlock ); + WCHAR *pwszText = (WCHAR*)GlobalLock( hBlock ); if( pwszText ) { int nFirst = __min( m_nCaret, m_nSelStart ); int nLast = __max( m_nCaret, m_nSelStart ); if( nLast - nFirst > 0 ) - CopyMemory( pwszText, m_Buffer.GetBuffer() + nFirst, (nLast - nFirst) * sizeof(TCHAR) ); + CopyMemory( pwszText, m_Buffer.GetBuffer() + nFirst, (nLast - nFirst) * sizeof(WCHAR) ); pwszText[nLast - nFirst] = L'\0'; // Terminate it GlobalUnlock( hBlock ); } - SetClipboardData( CF_OEMTEXT, hBlock ); + SetClipboardData( CF_UNICODETEXT, hBlock ); } CloseClipboard(); // We must not free the object until CloseClipboard is called. @@ -5058,7 +5085,7 @@ void CDXUTEditBox::PasteFromClipboard() if( OpenClipboard( NULL ) ) { - HANDLE handle = GetClipboardData( CF_OEMTEXT ); + HANDLE handle = GetClipboardData( CF_UNICODETEXT ); if( handle ) { // Convert the ANSI string to Unicode, then @@ -5078,6 +5105,26 @@ void CDXUTEditBox::PasteFromClipboard() } +// replaces every character with a bullet so password boxes never show the real text +LPCWSTR MaskText( LPCWSTR wszText ) +{ + static WCHAR wszMasked[256]; + ZeroMemory( wszMasked, sizeof(wszMasked) - 1 ); + + int nLength = wcslen( wszText ); + int i = 0; + if( nLength <= 255 ) + { + for( ; i != nLength; i++ ) + wszMasked[i] = 0x25CF; + + wszMasked[i] = 0; + } + + return wszMasked; +} + + //-------------------------------------------------------------------------------------- bool CDXUTEditBox::HandleKeyboard( UINT uMsg, WPARAM wParam, LPARAM lParam ) { @@ -5286,7 +5333,7 @@ bool CDXUTEditBox::MsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam ) { case WM_CHAR: { - switch( (TCHAR)wParam ) + switch( (WCHAR)wParam ) { // Backspace case VK_BACK: @@ -5317,7 +5364,7 @@ bool CDXUTEditBox::MsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam ) CopyToClipboard(); // If the key is Ctrl-X, delete the selection too. - if( (TCHAR)wParam == 24 ) + if( (WCHAR)wParam == 24 ) { DeleteSelectionText(); m_pDialog->SendEvent( EVENT_EDITBOX_CHANGE, true, this ); @@ -5383,7 +5430,7 @@ bool CDXUTEditBox::MsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam ) if(strlen(GetTextA()) >= 128) return true; - bool bPlaceCaret; + bool bPlaceCaret = false; // If we are in overwrite mode and there is already // a char at the caret's position, simply replace it. @@ -5398,16 +5445,24 @@ bool CDXUTEditBox::MsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam ) } else { - + bPlaceCaret = m_Buffer.SetChar(m_nCaret, (CHAR)wParam); } } else { // Insert the char - if((WCHAR)wParam <= 255) - bPlaceCaret = m_Buffer.InsertChar(m_nCaret, (WCHAR)wParam); + int nCaret = m_nCaret; + if((WCHAR)wParam > 255) + bPlaceCaret = m_Buffer.InsertChar(nCaret, (WCHAR)wParam); else - bPlaceCaret = m_Buffer.InsertChar(m_nCaret, (CHAR)wParam); + bPlaceCaret = m_Buffer.InsertChar(nCaret, (CHAR)wParam); } + + if( bPlaceCaret ) + { + PlaceCaret( m_nCaret + 1 ); + m_nSelStart = m_nCaret; + } + ResetCaretBlink(); m_pDialog->SendEvent( EVENT_EDITBOX_CHANGE, true, this ); } @@ -5491,18 +5546,35 @@ void CDXUTEditBox::Render( IDirect3DDevice9* pd3dDevice, float fElapsedTime ) // Render the text // // Element 0 for text - m_Elements.GetAt( 0 )->FontColor.Current = m_TextColor; - m_pDialog->DrawText( m_Buffer.GetBuffer() + m_nFirstVisible, m_Elements.GetAt( 0 ), &m_rcText ); + if( !field_126 ) + { + m_Elements.GetAt( 0 )->FontColor.Current = m_TextColor; + m_pDialog->DrawText( m_Buffer.GetBuffer() + m_nFirstVisible, m_Elements.GetAt( 0 ), &m_rcText ); - // Render the selected text - if( m_nCaret != m_nSelStart ) - { - int nFirstToRender = __max( m_nFirstVisible, __min( m_nSelStart, m_nCaret ) ); - int nNumChatToRender = __max( m_nSelStart, m_nCaret ) - nFirstToRender; - m_Elements.GetAt( 0 )->FontColor.Current = m_SelTextColor; - m_pDialog->DrawText( m_Buffer.GetBuffer() + nFirstToRender, - m_Elements.GetAt( 0 ), &rcSelection, false, nNumChatToRender ); - } + // Render the selected text + if( m_nCaret != m_nSelStart ) + { + int nFirstToRender = __max( m_nFirstVisible, __min( m_nSelStart, m_nCaret ) ); + int nNumChatToRender = __max( m_nSelStart, m_nCaret ) - nFirstToRender; + m_Elements.GetAt( 0 )->FontColor.Current = m_SelTextColor; + m_pDialog->DrawText( m_Buffer.GetBuffer() + nFirstToRender, + m_Elements.GetAt( 0 ), &rcSelection, false, nNumChatToRender ); + } + } + else + { + m_Elements.GetAt( 0 )->FontColor.Current = m_TextColor; + m_pDialog->DrawText( MaskText( m_Buffer.GetBuffer() + m_nFirstVisible ), m_Elements.GetAt( 0 ), &m_rcText ); + + if( m_nCaret != m_nSelStart ) + { + int nFirstToRender = __max( m_nFirstVisible, __min( m_nSelStart, m_nCaret ) ); + int nNumChatToRender = __max( m_nSelStart, m_nCaret ) - nFirstToRender; + m_Elements.GetAt( 0 )->FontColor.Current = m_SelTextColor; + m_pDialog->DrawText( MaskText( m_Buffer.GetBuffer() + nFirstToRender ), + m_Elements.GetAt( 0 ), &rcSelection, false, nNumChatToRender ); + } + } // /* Blink the caret @@ -5676,7 +5748,7 @@ int CDXUTIMEEditBox::s_nFirstTargetConv; // Index of the first target con CUniBuffer CDXUTIMEEditBox::s_CompString = CUniBuffer( 0 ); BYTE CDXUTIMEEditBox::s_abCompStringAttr[MAX_COMPSTRING_SIZE]; DWORD CDXUTIMEEditBox::s_adwCompStringClause[MAX_COMPSTRING_SIZE]; -TCHAR CDXUTIMEEditBox::s_wszReadingString[32]; +WCHAR CDXUTIMEEditBox::s_wszReadingString[32]; CDXUTIMEEditBox::CCandList CDXUTIMEEditBox::s_CandList; // Data relevant to the candidate list bool CDXUTIMEEditBox::s_bShowReadingWindow; // Indicates whether reading window is visible bool CDXUTIMEEditBox::s_bHorizontalReading; // Indicates whether the reading window is vertical or horizontal @@ -5750,18 +5822,23 @@ HRESULT CDXUTIMEEditBox::StaticOnCreateDevice() //-------------------------------------------------------------------------------------- void CDXUTIMEEditBox::UpdateRects() { - // Temporary adjust m_width so that CDXUTEditBox can compute - // the correct rects for its rendering since we need to make space - // for the indicator button - int nWidth = m_width; - m_width -= m_nIndicatorWidth + m_nBorder * 2; // Make room for the indicator button - CDXUTEditBox::UpdateRects(); - m_width = nWidth; // Restore + if( s_bEnableImeSystem ) + { + // Temporary adjust m_width so that CDXUTEditBox can compute + // the correct rects for its rendering since we need to make space + // for the indicator button + int nWidth = m_width; + m_width -= m_nIndicatorWidth + m_nBorder * 2; // Make room for the indicator button + CDXUTEditBox::UpdateRects(); + m_width = nWidth; // Restore - // Compute the indicator button rectangle - SetRect( &m_rcIndicator, m_rcBoundingBox.right, m_rcBoundingBox.top, m_x + m_width, m_rcBoundingBox.bottom ); + // Compute the indicator button rectangle + SetRect( &m_rcIndicator, m_rcBoundingBox.right, m_rcBoundingBox.top, m_x + m_width, m_rcBoundingBox.bottom ); // InflateRect( &m_rcIndicator, -m_nBorder, -m_nBorder ); - m_rcBoundingBox.right = m_rcBoundingBox.left + m_width; + m_rcBoundingBox.right = m_rcBoundingBox.left + m_width; + } + else + CDXUTEditBox::UpdateRects(); } @@ -5929,7 +6006,7 @@ void CDXUTIMEEditBox::CheckInputLocale() WCHAR wszLang[5]; GetLocaleInfoW( MAKELCID( LOWORD( s_hklCurrent ), SORT_DEFAULT ), LOCALE_SABBREVLANGNAME, wszLang, 5 ); s_wszCurrIndicator[0] = wszLang[0]; - s_wszCurrIndicator[1] = tolower( wszLang[1] ); + s_wszCurrIndicator[1] = towlower( wszLang[1] ); } } @@ -5942,7 +6019,7 @@ void CDXUTIMEEditBox::CheckToggleState() s_bChineseIME = ( GetPrimaryLanguage() == LANG_CHINESE ) && bIme; HIMC hImc; - if( NULL != ( hImc = _ImmGetContext( DXUTGetHWND() ) ) ) + if( NULL != ( hImc = _ImmGetContext( pGame->GetMainWindowHwnd() ) ) ) { if( s_bChineseIME ) { @@ -5954,7 +6031,7 @@ void CDXUTIMEEditBox::CheckToggleState() { s_ImeState = ( bIme && _ImmGetOpenStatus( hImc ) != 0 ) ? IMEUI_STATE_ON : IMEUI_STATE_OFF; } - _ImmReleaseContext( DXUTGetHWND(), hImc ); + _ImmReleaseContext( pGame->GetMainWindowHwnd(), hImc ); } else s_ImeState = IMEUI_STATE_OFF; @@ -6098,6 +6175,25 @@ void CDXUTIMEEditBox::FinalizeString( bool bSend ) bProcessing = false; } +//-------------------------------------------------------------------------------------- +// nothing calls this in retail either +void CDXUTIMEEditBox::PumpMessage() +{ + MSG msg; + + while( PeekMessageW( &msg, NULL, 0, 0, PM_NOREMOVE ) ) + { + if( !GetMessageW( &msg, NULL, 0, 0 ) ) + { + PostQuitMessage( msg.wParam ); + return; + } + + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } +} + //-------------------------------------------------------------------------------------- // Determine whether the reading window should be vertical or horizontal. @@ -6138,7 +6234,210 @@ void CDXUTIMEEditBox::GetReadingWindowOrientation( DWORD dwId ) // Obtain the reading string upon WM_IME_NOTIFY/INM_PRIVATE notification. void CDXUTIMEEditBox::GetPrivateReadingString() { - return; + DWORD dwId = GetImeId(); + + if( !dwId ) + { + s_bShowReadingWindow = false; + return; + } + + HIMC hImc; + if( NULL == ( hImc = _ImmGetContext( pGame->GetMainWindowHwnd() ) ) ) + { + s_bShowReadingWindow = false; + return; + } + + DWORD dwReadingStrLen = 0; + DWORD dwErr = 0; + WCHAR *pwszReadingStringBuffer = NULL; + WCHAR *pwStr = NULL; + bool bUnicodeIme = false; + INPUTCONTEXT *lpIC = NULL; + + if( _GetReadingString ) + { + UINT uMaxUiLen; + BOOL bVertical; + + // ask for the size, then for the string itself + dwReadingStrLen = _GetReadingString( hImc, 0, NULL, (PINT)&dwErr, &bVertical, &uMaxUiLen ); + if( dwReadingStrLen ) + { + pwStr = pwszReadingStringBuffer = (WCHAR *)HeapAlloc( GetProcessHeap(), 0, sizeof(WCHAR) * dwReadingStrLen ); + if( !pwszReadingStringBuffer ) + { + // out of memory + _ImmReleaseContext( pGame->GetMainWindowHwnd(), hImc ); + return; + } + + dwReadingStrLen = _GetReadingString( hImc, dwReadingStrLen, (PCHAR)pwStr, (PINT)&dwErr, &bVertical, &uMaxUiLen ); + } + + s_bHorizontalReading = !bVertical; + bUnicodeIme = true; + } + else + { + // the IME exports nothing, so dig the reading string out of its private data + lpIC = _ImmLockIMC( hImc ); + + LPBYTE p; + switch( dwId ) + { + case IMEID_CHT_VER42: + case IMEID_CHT_VER43: + case IMEID_CHT_VER44: + p = *(LPBYTE *)( (LPBYTE)_ImmLockIMCC( lpIC->hPrivate ) + 24 ); + if( !p ) + break; + dwReadingStrLen = *(DWORD *)( p + 7*4 + 32*4 ); + dwErr = *(DWORD *)( p + 8*4 + 32*4 ); + pwStr = (WCHAR *)( p + 56 ); + bUnicodeIme = true; + break; + + case IMEID_CHT_VER50: + { + p = *(LPBYTE *)( (LPBYTE)_ImmLockIMCC( lpIC->hPrivate ) + 3*4 ); + if( !p ) + break; + LPBYTE q = *(LPBYTE *)( p + 8*4 ); + if( !q ) + break; + dwReadingStrLen = *(DWORD *)( q + 16*4 + 16 ); + dwErr = *(DWORD *)( q + 16*4 + 16 + 4 ); + pwStr = (WCHAR *)( q + 16*4 ); + bUnicodeIme = false; + break; + } + + case IMEID_CHT_VER51: + case IMEID_CHT_VER52: + case IMEID_CHS_VER53: + { + p = *(LPBYTE *)( (LPBYTE)_ImmLockIMCC( lpIC->hPrivate ) + 1*4 ); + if( !p ) + break; + LPBYTE q = *(LPBYTE *)( p + 6*4 ); + if( !q ) + break; + dwReadingStrLen = *(DWORD *)( q + 16*4 + 16*2 ); + dwErr = *(DWORD *)( q + 16*4 + 16*2 + 4 ); + pwStr = (WCHAR *)( q + 16*4 ); + bUnicodeIme = true; + break; + } + + case IMEID_CHS_VER41: + { + int nOffset = ( GetImeId( 1 ) >= 2 ) ? 8 : 7; + + p = *(LPBYTE *)( (LPBYTE)_ImmLockIMCC( lpIC->hPrivate ) + nOffset*4 ); + if( !p ) + break; + dwReadingStrLen = *(DWORD *)( p + 7*4 + 16*2*4 ); + dwErr = *(DWORD *)( p + 8*4 + 16*2*4 ); + dwErr = __min( dwErr, dwReadingStrLen ); + pwStr = (WCHAR *)( p + 6*4 + 16*2 ); + bUnicodeIme = true; + break; + } + + case IMEID_CHS_VER42: + { + OSVERSIONINFOW osi; + osi.dwOSVersionInfoSize = sizeof( OSVERSIONINFOW ); + GetVersionExW( &osi ); + int nTcharSize = ( osi.dwPlatformId == VER_PLATFORM_WIN32_NT ) ? sizeof(WCHAR) : sizeof(char); + + p = *(LPBYTE *)( (LPBYTE)_ImmLockIMCC( lpIC->hPrivate ) + 8*4 ); + if( !p ) + break; + dwReadingStrLen = *(DWORD *)( p + ( nTcharSize + 4 ) * 16 ); + dwErr = *(DWORD *)( p + nTcharSize * 16 + 68 ); + pwStr = (WCHAR *)( p + 16*4 ); + bUnicodeIme = ( osi.dwPlatformId == VER_PLATFORM_WIN32_NT ); + break; + } + } + } + + // Copy the reading string to the candidate list + s_CandList.awszCandidate[0][0] = L'\0'; + s_CandList.awszCandidate[1][0] = L'\0'; + s_CandList.awszCandidate[2][0] = L'\0'; + s_CandList.awszCandidate[3][0] = L'\0'; + s_CandList.dwCount = dwReadingStrLen; + s_CandList.dwSelection = (DWORD)-1; // don't select any candidate + UINT i; + if( bUnicodeIme ) + { + for( i = 0; i < dwReadingStrLen; ++i ) + { + if( dwErr <= i && s_CandList.dwSelection == (DWORD)-1 ) + s_CandList.dwSelection = i; + + s_CandList.awszCandidate[i][0] = pwStr[i]; + s_CandList.awszCandidate[i][1] = L'\0'; + } + s_CandList.awszCandidate[i][0] = L'\0'; + } + else + { + // ansi private data, so widen it one character at a time + LPSTR pszStr = (LPSTR)pwStr; + UINT indexA = 0; + for( i = 0; indexA < dwReadingStrLen; ++i ) + { + if( dwErr <= indexA && s_CandList.dwSelection == (DWORD)-1 ) + s_CandList.dwSelection = i; + + UINT nCodePage = 0; + WCHAR wszCodePage[8]; + if( GetLocaleInfoW( MAKELCID( GetLanguage(), SORT_DEFAULT ), LOCALE_IDEFAULTANSICODEPAGE, wszCodePage, 8 ) ) + nCodePage = wcstol( wszCodePage, NULL, 0 ); + + MultiByteToWideChar( nCodePage, 0, pszStr + indexA, + IsDBCSLeadByteEx( nCodePage, pszStr[indexA] ) ? 2 : 1, + s_CandList.awszCandidate[i], 1 ); + if( IsDBCSLeadByteEx( nCodePage, pszStr[indexA] ) ) + ++indexA; + ++indexA; + } + s_CandList.awszCandidate[i][0] = L'\0'; + s_CandList.dwCount = i; + } + + if( !_GetReadingString ) + { + _ImmUnlockIMCC( lpIC->hPrivate ); + _ImmUnlockIMC( hImc ); + + GetReadingWindowOrientation( dwId ); + } + _ImmReleaseContext( pGame->GetMainWindowHwnd(), hImc ); + + if( pwszReadingStringBuffer ) + HeapFree( GetProcessHeap(), 0, pwszReadingStringBuffer ); + + s_bShowReadingWindow = ( s_CandList.dwCount > 0 ); + if( s_bHorizontalReading ) + { + s_CandList.nReadingError = -1; // Clear error + s_wszReadingString[0] = L'\0'; + for( UINT i = 0; i < s_CandList.dwCount; ++i ) + { + if( s_CandList.dwSelection == i ) + s_CandList.nReadingError = lstrlenW( s_wszReadingString ); + + StringCchCatW( s_wszReadingString, 32, s_CandList.awszCandidate[i] ); + } + } + + s_CandList.dwPageSize = MAX_CANDLIST; } //-------------------------------------------------------------------------------------- @@ -6148,22 +6447,21 @@ void CDXUTIMEEditBox::OnFocusIn() if( s_bEnableImeSystem ) { - _ImmAssociateContext( DXUTGetHWND(), s_hImcDef ); + _ImmAssociateContext( pGame->GetMainWindowHwnd(), s_hImcDef ); CheckToggleState(); - } else - _ImmAssociateContext( DXUTGetHWND(), NULL ); - // - // Set up the IME global state according to the current instance state - // - HIMC hImc; - if( NULL != ( hImc = _ImmGetContext( DXUTGetHWND() ) ) ) - { - if( !s_bEnableImeSystem ) - s_ImeState = IMEUI_STATE_OFF; + // + // Set up the IME global state according to the current instance state + // + HIMC hImc; + if( NULL != ( hImc = _ImmGetContext( pGame->GetMainWindowHwnd() ) ) ) + { + if( !s_bEnableImeSystem ) + s_ImeState = IMEUI_STATE_OFF; - _ImmReleaseContext( DXUTGetHWND(), hImc ); - CheckToggleState(); + _ImmReleaseContext( pGame->GetMainWindowHwnd(), hImc ); + CheckToggleState(); + } } } @@ -6173,9 +6471,12 @@ void CDXUTIMEEditBox::OnFocusOut() { CDXUTEditBox::OnFocusOut(); - FinalizeString( false ); // Don't send the comp string as to match RichEdit behavior + if( s_bEnableImeSystem ) + { + FinalizeString( false ); // Don't send the comp string as to match RichEdit behavior - _ImmAssociateContext( DXUTGetHWND(), NULL ); + _ImmAssociateContext( pGame->GetMainWindowHwnd(), NULL ); + } } @@ -6244,7 +6545,7 @@ bool CDXUTIMEEditBox::StaticMsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam ) default: // unsupported sub-language GetLocaleInfoW( MAKELCID( LOWORD( phKL[i] ), SORT_DEFAULT ), LOCALE_SABBREVLANGNAME, wszDesc, 128 ); Locale.m_wszLangAbb[0] = wszDesc[0]; - Locale.m_wszLangAbb[1] = tolower( wszDesc[1] ); + Locale.m_wszLangAbb[1] = towlower( wszDesc[1] ); Locale.m_wszLangAbb[2] = '\0'; break; } @@ -6364,12 +6665,12 @@ bool CDXUTIMEEditBox::HandleMouse( UINT uMsg, POINT pt, WPARAM wParam, LPARAM lP // Now generate keypress events to move the comp string cursor // to the click point. First, if the candidate window is displayed, // send Esc to close it. - HIMC hImc = _ImmGetContext( DXUTGetHWND() ); + HIMC hImc = _ImmGetContext( pGame->GetMainWindowHwnd() ); if( !hImc ) return true; _ImmNotifyIME( hImc, NI_CLOSECANDIDATE, 0, 0 ); - _ImmReleaseContext( DXUTGetHWND(), hImc ); + _ImmReleaseContext( pGame->GetMainWindowHwnd(), hImc ); switch( GetPrimaryLanguage() ) { @@ -6473,7 +6774,7 @@ bool CDXUTIMEEditBox::HandleMouse( UINT uMsg, POINT pt, WPARAM wParam, LPARAM lP if( nCharHit >= nEntryStart ) { // Haven't found it. - nEntryStart += lstrlenA( s_CandList.awszCandidate[i] ) + 1; // plus space separator + nEntryStart += lstrlenW( s_CandList.awszCandidate[i] ) + 1; // plus space separator } else { // Found it. This entry starts at the right side of the click point, @@ -6536,10 +6837,10 @@ bool CDXUTIMEEditBox::MsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam ) DXUTTRACE( "WM_IME_COMPOSITION\n" ); { LONG lRet; // Returned count in CHARACTERS - TCHAR wszCompStr[MAX_COMPSTRING_SIZE]; + WCHAR wszCompStr[MAX_COMPSTRING_SIZE]; *trapped = true; - if( NULL == ( hImc = _ImmGetContext( DXUTGetHWND() ) ) ) + if( NULL == ( hImc = _ImmGetContext( pGame->GetMainWindowHwnd() ) ) ) { break; } @@ -6568,9 +6869,9 @@ bool CDXUTIMEEditBox::MsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam ) lRet = _ImmGetCompositionStringW( hImc, GCS_RESULTSTR, wszCompStr, sizeof( wszCompStr ) ); if( lRet > 0 ) { - lRet /= sizeof(TCHAR); + lRet /= sizeof(WCHAR); wszCompStr[lRet] = 0; // Force terminate - TruncateCompString( false, (int)strlen( wszCompStr ) ); + TruncateCompString( false, (int)wcslen( wszCompStr ) ); s_CompString.SetText( wszCompStr ); SendCompString(); ResetCompositionString(); @@ -6588,12 +6889,12 @@ bool CDXUTIMEEditBox::MsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam ) lRet = _ImmGetCompositionStringW( hImc, GCS_COMPSTR, wszCompStr, sizeof( wszCompStr ) ); if( lRet > 0 ) { - lRet /= sizeof(TCHAR); // Convert size in byte to size in char + lRet /= sizeof(WCHAR); // Convert size in byte to size in char wszCompStr[lRet] = 0; // Force terminate // // Remove the whole of the string // - TruncateCompString( false, (int)strlen( wszCompStr ) ); + TruncateCompString( false, (int)wcslen( wszCompStr ) ); s_CompString.SetText( wszCompStr ); @@ -6618,7 +6919,7 @@ bool CDXUTIMEEditBox::MsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam ) } s_CandList.dwPageSize = MAX_CANDLIST; // Clear comp string after we are done copying - ZeroMemory( (LPVOID)s_CompString.GetBuffer(), 4 * sizeof(TCHAR) ); + ZeroMemory( (LPVOID)s_CompString.GetBuffer(), 4 * sizeof(WCHAR) ); s_bShowReadingWindow = true; GetReadingWindowOrientation( 0 ); if( s_bHorizontalReading ) @@ -6636,8 +6937,8 @@ bool CDXUTIMEEditBox::MsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam ) for( UINT i = 0; i < s_CandList.dwCount; ++i ) { if( s_CandList.dwSelection == i ) - s_CandList.nReadingError = lstrlen( s_wszReadingString ); - StringCchCat( s_wszReadingString, 32, s_CandList.awszCandidate[i] ); + s_CandList.nReadingError = lstrlenW( s_wszReadingString ); + StringCchCatW( s_wszReadingString, 32, s_CandList.awszCandidate[i] ); } } } @@ -6659,8 +6960,8 @@ bool CDXUTIMEEditBox::MsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam ) int nCount = lstrlenW( s_CompString.GetBuffer() + s_nCompCaret ); // Send left keystrokes for( int i = 0; i < nCount; ++i ) - SendMessage( DXUTGetHWND(), WM_KEYDOWN, VK_LEFT, 0 ); - SendMessage( DXUTGetHWND(), WM_KEYUP, VK_LEFT, 0 ); + SendMessage( pGame->GetMainWindowHwnd(), WM_KEYDOWN, VK_LEFT, 0 ); + SendMessage( pGame->GetMainWindowHwnd(), WM_KEYUP, VK_LEFT, 0 ); } } @@ -6682,7 +6983,7 @@ bool CDXUTIMEEditBox::MsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam ) s_adwCompStringClause[lRet / sizeof(DWORD)] = 0; // Terminate } - _ImmReleaseContext( DXUTGetHWND(), hImc ); + _ImmReleaseContext( pGame->GetMainWindowHwnd(), hImc ); } break; @@ -6714,8 +7015,9 @@ bool CDXUTIMEEditBox::MsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam ) DXUTTRACE( wParam == IMN_CHANGECANDIDATE ? " IMN_CHANGECANDIDATE\n" : " IMN_OPENCANDIDATE\n" ); s_CandList.bShowWindow = true; + dwImeWaitTick = GetTickCount(); *trapped = true; - if( NULL == ( hImc = _ImmGetContext( DXUTGetHWND() ) ) ) + if( NULL == ( hImc = _ImmGetContext( pGame->GetMainWindowHwnd() ) ) ) break; LPCANDIDATELIST lpCandList = NULL; @@ -6757,14 +7059,14 @@ bool CDXUTIMEEditBox::MsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam ) i++, j++ ) { // Initialize the candidate list strings - PCHAR pwsz = s_CandList.awszCandidate[j]; + PWCHAR pwsz = s_CandList.awszCandidate[j]; // For every candidate string entry, // write [index] + Space + [String] if vertical, // write [index] + [String] + Space if horizontal. - *pwsz++ = (TCHAR)( L'0' + ( (j + 1) % 10 ) ); // Index displayed is 1 based + *pwsz++ = (WCHAR)( L'0' + ( (j + 1) % 10 ) ); // Index displayed is 1 based if( s_bVerticalCand ) *pwsz++ = L' '; - TCHAR *pwszNewCand = (PCHAR)( (LPBYTE)lpCandList + lpCandList->dwOffset[i] ); + WCHAR *pwszNewCand = (PWCHAR)( (LPBYTE)lpCandList + lpCandList->dwOffset[i] ); while ( *pwszNewCand ) *pwsz++ = *pwszNewCand++; if( !s_bVerticalCand ) @@ -6778,7 +7080,7 @@ bool CDXUTIMEEditBox::MsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam ) s_CandList.dwCount = lpCandList->dwPageSize; HeapFree( GetProcessHeap(), 0, lpCandList ); - _ImmReleaseContext( DXUTGetHWND(), hImc ); + _ImmReleaseContext( pGame->GetMainWindowHwnd(), hImc ); // Korean and old Chinese IME can't have selection. // User must use the number hotkey or Enter to select @@ -6793,7 +7095,7 @@ bool CDXUTIMEEditBox::MsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam ) // horizontal candidate window. if( !s_bVerticalCand ) { - TCHAR wszCand[256] = ""; + WCHAR wszCand[256] = L""; s_CandList.nFirstSelected = 0; s_CandList.nHoriSelectedLen = 0; @@ -6802,17 +7104,17 @@ bool CDXUTIMEEditBox::MsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam ) if( s_CandList.awszCandidate[i][0] == L'\0' ) break; - TCHAR wszEntry[32]; - StringCchPrintf( wszEntry, 32, "%s ", s_CandList.awszCandidate[i] ); + WCHAR wszEntry[32]; + StringCchPrintfW( wszEntry, 32, L"%s ", s_CandList.awszCandidate[i] ); // If this is the selected entry, mark its char position. if( s_CandList.dwSelection == i ) { - s_CandList.nFirstSelected = lstrlen( wszCand ); - s_CandList.nHoriSelectedLen = lstrlen( wszEntry ) - 1; // Minus space + s_CandList.nFirstSelected = lstrlenW( wszCand ); + s_CandList.nHoriSelectedLen = lstrlenW( wszEntry ) - 1; // Minus space } - StringCchCat( wszCand, 256, wszEntry ); + StringCchCatW( wszCand, 256, wszEntry ); } - wszCand[lstrlen(wszCand) - 1] = L'\0'; // Remove the last space + wszCand[lstrlenW(wszCand) - 1] = L'\0'; // Remove the last space s_CandList.HoriCand.SetText( wszCand ); } } @@ -6823,6 +7125,7 @@ bool CDXUTIMEEditBox::MsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam ) { DXUTTRACE( " IMN_CLOSECANDIDATE\n" ); s_CandList.bShowWindow = false; + dwImeWaitTick = GetTickCount(); if( !s_bShowReadingWindow ) { s_CandList.dwCount = 0; @@ -6987,12 +7290,6 @@ void CDXUTIMEEditBox::RenderCandidateReadingWindow( IDirect3DDevice9* pd3dDevice } // Now that we have the dimension, calculate the location for the candidate window. - // We attempt to fit the window in this order: - // bottom, top, right, left. - - bool bHasPosition = false; - - // Bottom SetRect( &rc, s_ptCompString.x + nXComp, s_ptCompString.y + m_rcText.bottom - m_rcText.top, s_ptCompString.x + nXComp + nWidthRequired, s_ptCompString.y + m_rcText.bottom - m_rcText.top + nHeightRequired ); // if the right edge is cut off, move it left. @@ -7001,52 +7298,6 @@ void CDXUTIMEEditBox::RenderCandidateReadingWindow( IDirect3DDevice9* pd3dDevice rc.left -= rc.right - m_pDialog->GetWidth(); rc.right = m_pDialog->GetWidth(); } - if( rc.bottom <= m_pDialog->GetHeight() ) - bHasPosition = true; - - // Top - if( !bHasPosition ) - { - SetRect( &rc, s_ptCompString.x + nXComp, s_ptCompString.y - nHeightRequired, - s_ptCompString.x + nXComp + nWidthRequired, s_ptCompString.y ); - // if the right edge is cut off, move it left. - if( rc.right > m_pDialog->GetWidth() ) - { - rc.left -= rc.right - m_pDialog->GetWidth(); - rc.right = m_pDialog->GetWidth(); - } - if( rc.top >= 0 ) - bHasPosition = true; - } - - // Right - if( !bHasPosition ) - { - int nXCompTrail; - s_CompString.CPtoX( s_nCompCaret, TRUE, &nXCompTrail ); - SetRect( &rc, s_ptCompString.x + nXCompTrail, 0, - s_ptCompString.x + nXCompTrail + nWidthRequired, nHeightRequired ); - if( rc.right <= m_pDialog->GetWidth() ) - bHasPosition = true; - } - - // Left - if( !bHasPosition ) - { - SetRect( &rc, s_ptCompString.x + nXComp - nWidthRequired, 0, - s_ptCompString.x + nXComp, nHeightRequired ); - if( rc.right >= 0 ) - bHasPosition = true; - } - - if( !bHasPosition ) - { - // The dialog is too small for the candidate window. - // Fall back to render at 0, 0. Some part of the window - // will be cut off. - rc.left = 0; - rc.right = nWidthRequired; - } // If we are rendering the candidate window, save the position // so that mouse clicks are checked properly. @@ -7317,7 +7568,7 @@ void CDXUTIMEEditBox::Render( IDirect3DDevice9* pd3dDevice, float fElapsedTime ) // // Now render the IME elements // - if( m_bHasFocus ) + if( s_bEnableImeSystem ) { // Render the input locale indicator RenderIndicator( pd3dDevice, fElapsedTime ); @@ -7341,461 +7592,6 @@ void CDXUTIMEEditBox::Render( IDirect3DDevice9* pd3dDevice, float fElapsedTime ) } -//-------------------------------------------------------------------------------------- -void CUniBuffer::Initialize() -{ - if( s_hDll ) // Only need to do once - return; - - TCHAR wszPath[MAX_PATH+1]; - if( !::GetSystemDirectory( wszPath, MAX_PATH+1 ) ) - return; - - // Verify whether it is safe to concatenate these strings - int len1 = lstrlen(wszPath); - int len2 = lstrlen(UNISCRIBE_DLLNAME); - if (len1 + len2 > MAX_PATH) - { - return; - } - - // We have verified that the concatenated string will fit into wszPath, - // so it is safe to concatenate them. - StringCchCat( wszPath, MAX_PATH, UNISCRIBE_DLLNAME ); - - s_hDll = LoadLibrary( wszPath ); - if( s_hDll ) - { - FARPROC Temp; - GETPROCADDRESS( s_hDll, ScriptApplyDigitSubstitution, Temp ); - GETPROCADDRESS( s_hDll, ScriptStringAnalyse, Temp ); - GETPROCADDRESS( s_hDll, ScriptStringCPtoX, Temp ); - GETPROCADDRESS( s_hDll, ScriptStringXtoCP, Temp ); - GETPROCADDRESS( s_hDll, ScriptStringFree, Temp ); - GETPROCADDRESS( s_hDll, ScriptString_pLogAttr, Temp ); - GETPROCADDRESS( s_hDll, ScriptString_pcOutChars, Temp ); - } -} - - -//-------------------------------------------------------------------------------------- -void CUniBuffer::Uninitialize() -{ - if( s_hDll ) - { - PLACEHOLDERPROC( ScriptApplyDigitSubstitution ); - PLACEHOLDERPROC( ScriptStringAnalyse ); - PLACEHOLDERPROC( ScriptStringCPtoX ); - PLACEHOLDERPROC( ScriptStringXtoCP ); - PLACEHOLDERPROC( ScriptStringFree ); - PLACEHOLDERPROC( ScriptString_pLogAttr ); - PLACEHOLDERPROC( ScriptString_pcOutChars ); - - FreeLibrary( s_hDll ); - s_hDll = NULL; - } -} - - -//-------------------------------------------------------------------------------------- -bool CUniBuffer::SetBufferSize( int nNewSize ) -{ - // If the current size is already the maximum allowed, - // we can't possibly allocate more. - if( m_nBufferSize == DXUT_MAX_EDITBOXLENGTH ) - return false; - - int nAllocateSize = ( nNewSize == -1 || nNewSize < m_nBufferSize * 2 ) ? ( m_nBufferSize ? m_nBufferSize * 2 : 256 ) : nNewSize * 2; - - // Cap the buffer size at the maximum allowed. - if( nAllocateSize > DXUT_MAX_EDITBOXLENGTH ) - nAllocateSize = DXUT_MAX_EDITBOXLENGTH; - - WCHAR *pTempBuffer = new WCHAR[nAllocateSize]; - if( !pTempBuffer ) - return false; - if( m_pwszBuffer ) - { - CopyMemory( pTempBuffer, m_pwszBuffer, m_nBufferSize * sizeof(WCHAR) ); - delete[] m_pwszBuffer; - } - else - { - ZeroMemory( pTempBuffer, sizeof(WCHAR) * nAllocateSize ); - } - - m_pwszBuffer = pTempBuffer; - m_nBufferSize = nAllocateSize; - return true; -} - - -//-------------------------------------------------------------------------------------- -// Uniscribe -- Analyse() analyses the string in the buffer -//-------------------------------------------------------------------------------------- -HRESULT CUniBuffer::Analyse() -{ - if( m_Analysis ) - _ScriptStringFree( &m_Analysis ); - - SCRIPT_CONTROL ScriptControl; // For uniscribe - SCRIPT_STATE ScriptState; // For uniscribe - ZeroMemory( &ScriptControl, sizeof(ScriptControl) ); - ZeroMemory( &ScriptState, sizeof(ScriptState) ); - _ScriptApplyDigitSubstitution ( NULL, &ScriptControl, &ScriptState ); - - if( !m_pFontNode ) - return E_FAIL; - - HRESULT hr = _ScriptStringAnalyse( m_pFontNode->pFont ? m_pFontNode->pFont->GetDC() : NULL, - m_pwszBuffer, - lstrlenW( m_pwszBuffer ) + 1, // NULL is also analyzed. - lstrlenW( m_pwszBuffer ) * 3 / 2 + 16, - DEFAULT_CHARSET, - SSA_BREAK | SSA_GLYPHS | SSA_FALLBACK | SSA_LINK, - 0, - &ScriptControl, - &ScriptState, - NULL, - NULL, - NULL, - &m_Analysis ); - if( SUCCEEDED( hr ) ) - m_bAnalyseRequired = false; // Analysis is up-to-date - return hr; -} - - -//-------------------------------------------------------------------------------------- -CUniBuffer::CUniBuffer( int nInitialSize ) -{ - CUniBuffer::Initialize(); // ensure static vars are properly init'ed first - - m_nBufferSize = 0; - m_pwszBuffer = NULL; - m_bAnalyseRequired = true; - m_Analysis = NULL; - m_pFontNode = NULL; - field_8 = 0; - field_12 = 0; - - if( nInitialSize > 0 ) - SetBufferSize( nInitialSize ); -} - - -//-------------------------------------------------------------------------------------- -CUniBuffer::~CUniBuffer() -{ - delete[] m_pwszBuffer; - if( m_Analysis ) - _ScriptStringFree( &m_Analysis ); -} - - -//-------------------------------------------------------------------------------------- -WCHAR& CUniBuffer::operator[]( int n ) // No param checking -{ - // This version of operator[] is called only - // if we are asking for write access, so - // re-analysis is required. - m_bAnalyseRequired = true; - return m_pwszBuffer[n]; -} - - -//-------------------------------------------------------------------------------------- -void CUniBuffer::Clear() -{ - *m_pwszBuffer = L'\0'; - m_bAnalyseRequired = true; -} - - -//-------------------------------------------------------------------------------------- -// Inserts the char at specified index. -// If nIndex == -1, insert to the end. -//-------------------------------------------------------------------------------------- -bool CUniBuffer::InsertChar( int nIndex, WCHAR tchr ) -{ - assert( nIndex >= 0 ); - - if( nIndex < 0 || nIndex > lstrlenW( m_pwszBuffer ) ) - return false; // invalid index - - // Check for maximum length allowed - if( GetTextSize() + 1 >= DXUT_MAX_EDITBOXLENGTH ) - return false; - - if( lstrlenW( m_pwszBuffer ) + 1 >= m_nBufferSize ) - { - if( !SetBufferSize( -1 ) ) - return false; // out of memory - } - - assert( m_nBufferSize >= 2 ); - - // Shift the characters after the index, start by copying the null terminator - WCHAR* dest = m_pwszBuffer + lstrlenW(m_pwszBuffer)+1; - WCHAR* stop = m_pwszBuffer + nIndex; - WCHAR* src = dest - 1; - - while( dest > stop ) - { - *dest-- = *src--; - } - - // Set new character - m_pwszBuffer[ nIndex ] = tchr; - m_bAnalyseRequired = true; - - return true; -} - -bool CUniBuffer::InsertChar(int nIndex, CHAR tchr) -{ - WCHAR WideCharStr[2]; - ZeroMemory(&WideCharStr[0], sizeof(WideCharStr)); - - if(field_8) - { - CHAR MultiByteStr[3]; - MultiByteStr[0] = field_8; - MultiByteStr[1] = tchr; - MultiByteStr[2] = 0; - MultiByteToWideChar(CP_ACP, 0, MultiByteStr, 2, WideCharStr, 1); - InsertChar(nIndex, WideCharStr[0]); - field_8 = 0; - return true; - } - else if(IsDBCSLeadByteEx(CP_ACP, tchr)) - { - field_8 = tchr; - return false; - } - else - { - MultiByteToWideChar(CP_ACP, 0, &tchr, 1, WideCharStr, 1); - InsertChar(nIndex, WideCharStr[0]); - return true; - } -} - - -//-------------------------------------------------------------------------------------- -// Removes the char at specified index. -// If nIndex == -1, remove the last char. -//-------------------------------------------------------------------------------------- -bool CUniBuffer::RemoveChar( int nIndex ) -{ - if( !lstrlenW( m_pwszBuffer ) || nIndex < 0 || nIndex >= lstrlenW( m_pwszBuffer ) ) - return false; // Invalid index - - MoveMemory( m_pwszBuffer + nIndex, m_pwszBuffer + nIndex + 1, sizeof(TCHAR) * ( lstrlenW( m_pwszBuffer ) - nIndex ) ); - m_bAnalyseRequired = true; - return true; -} - - -//-------------------------------------------------------------------------------------- -// Inserts the first nCount characters of the string pStr at specified index. -// If nCount == -1, the entire string is inserted. -// If nIndex == -1, insert to the end. -//-------------------------------------------------------------------------------------- -bool CUniBuffer::InsertString( int nIndex, const WCHAR *pStr, int nCount ) -{ - assert( nIndex >= 0 ); - - if( nIndex > lstrlenW( m_pwszBuffer ) ) - return false; // invalid index - - if( -1 == nCount ) - nCount = lstrlenW( pStr ); - - // Check for maximum length allowed - if( GetTextSize() + nCount >= DXUT_MAX_EDITBOXLENGTH ) - return false; - - if( lstrlenW( m_pwszBuffer ) + nCount >= m_nBufferSize ) - { - if( !SetBufferSize( lstrlenW( m_pwszBuffer ) + nCount + 1 ) ) - return false; // out of memory - } - - MoveMemory( m_pwszBuffer + nIndex + nCount, m_pwszBuffer + nIndex, sizeof(TCHAR) * ( lstrlenW( m_pwszBuffer ) - nIndex + 1 ) ); - CopyMemory( m_pwszBuffer + nIndex, pStr, nCount * sizeof(TCHAR) ); - m_bAnalyseRequired = true; - - return true; -} - - -//-------------------------------------------------------------------------------------- -bool CUniBuffer::SetText( LPCWSTR wszText ) -{ - assert( wszText != NULL ); - - int nRequired = int(lstrlenW( wszText ) + 1); - - // Check for maximum length allowed - if( nRequired >= DXUT_MAX_EDITBOXLENGTH ) - return false; - - while( GetBufferSize() < nRequired ) - if( !SetBufferSize( -1 ) ) - break; - // Check again in case out of memory occurred inside while loop. - if( GetBufferSize() >= nRequired ) - { - StringCchCopyW( m_pwszBuffer, GetBufferSize(), wszText ); - m_bAnalyseRequired = true; - return true; - } - else - return false; -} - - -//-------------------------------------------------------------------------------------- -void ConvertAnsiToWide(LPCSTR wszSrc, LPWSTR wszDest, int nSize) -{ - memset(wszDest, 0, sizeof(WCHAR) * nSize); - - int nCount = MultiByteToWideChar(CP_ACP, 0, wszSrc, strlen(wszSrc), NULL, 0); - if(nCount < nSize) - MultiByteToWideChar(CP_ACP, 0, wszSrc, strlen(wszSrc), wszDest, nCount); -} - - -//-------------------------------------------------------------------------------------- -bool CUniBuffer::SetText( LPCTSTR wszText ) -{ - if(lstrlenA(wszText) > 1024) return false; - - WCHAR szBuffer[2048]; - ConvertAnsiToWide(wszText, szBuffer, 2048); - SetText(szBuffer); - return true; -} - - -//-------------------------------------------------------------------------------------- -HRESULT CUniBuffer::CPtoX( int nCP, BOOL bTrail, int *pX ) -{ - assert( pX ); - *pX = 0; // Default - - HRESULT hr = S_OK; - if( m_bAnalyseRequired ) - hr = Analyse(); - - if( SUCCEEDED( hr ) ) - hr = _ScriptStringCPtoX( m_Analysis, nCP, bTrail, pX ); - - return hr; -} - - -//-------------------------------------------------------------------------------------- -HRESULT CUniBuffer::XtoCP( int nX, int *pCP, int *pnTrail ) -{ - assert( pCP && pnTrail ); - *pCP = 0; *pnTrail = FALSE; // Default - - HRESULT hr = S_OK; - if( m_bAnalyseRequired ) - hr = Analyse(); - - if( SUCCEEDED( hr ) ) - hr = _ScriptStringXtoCP( m_Analysis, nX, pCP, pnTrail ); - - // If the coordinate falls outside the text region, we - // can get character positions that don't exist. We must - // filter them here and convert them to those that do exist. - if( *pCP == -1 && *pnTrail == TRUE ) - { - *pCP = 0; *pnTrail = FALSE; - } else - if( *pCP > lstrlenW( m_pwszBuffer ) && *pnTrail == FALSE ) - { - *pCP = lstrlenW( m_pwszBuffer ); *pnTrail = TRUE; - } - - return hr; -} - - -//-------------------------------------------------------------------------------------- -void CUniBuffer::GetPriorItemPos( int nCP, int *pPrior ) -{ - *pPrior = nCP; // Default is the char itself - - if( m_bAnalyseRequired ) - if( FAILED( Analyse() ) ) - return; - - const SCRIPT_LOGATTR *pLogAttr = _ScriptString_pLogAttr( m_Analysis ); - if( !pLogAttr ) - return; - - if( !_ScriptString_pcOutChars( m_Analysis ) ) - return; - int nInitial = *_ScriptString_pcOutChars( m_Analysis ); - if( nCP - 1 < nInitial ) - nInitial = nCP - 1; - for( int i = nInitial; i > 0; --i ) - if( pLogAttr[i].fWordStop || // Either the fWordStop flag is set - ( !pLogAttr[i].fWhiteSpace && // Or the previous char is whitespace but this isn't. - pLogAttr[i-1].fWhiteSpace ) ) - { - *pPrior = i; - return; - } - // We have reached index 0. 0 is always a break point, so simply return it. - *pPrior = 0; -} - - -//-------------------------------------------------------------------------------------- -void CUniBuffer::GetNextItemPos( int nCP, int *pPrior ) -{ - *pPrior = nCP; // Default is the char itself - - HRESULT hr = S_OK; - if( m_bAnalyseRequired ) - hr = Analyse(); - if( FAILED( hr ) ) - return; - - const SCRIPT_LOGATTR *pLogAttr = _ScriptString_pLogAttr( m_Analysis ); - if( !pLogAttr ) - return; - - if( !_ScriptString_pcOutChars( m_Analysis ) ) - return; - int nInitial = *_ScriptString_pcOutChars( m_Analysis ); - if( nCP + 1 < nInitial ) - nInitial = nCP + 1; - for( int i = nInitial; i < *_ScriptString_pcOutChars( m_Analysis ) - 1; ++i ) - { - if( pLogAttr[i].fWordStop ) // Either the fWordStop flag is set - { - *pPrior = i; - return; - } - else - if( pLogAttr[i].fWhiteSpace && // Or this whitespace but the next char isn't. - !pLogAttr[i+1].fWhiteSpace ) - { - *pPrior = i+1; // The next char is a word stop - return; - } - } - // We have reached the end. It's always a word stop, so simply return it. - *pPrior = *_ScriptString_pcOutChars( m_Analysis ) - 1; -} - - //-------------------------------------------------------------------------------------- void CDXUTEditBox::ResetCaretBlink() { @@ -7906,6 +7702,22 @@ void CDXUTIMEEditBox::Uninitialize() } } +//-------------------------------------------------------------------------------------- +// samp asks this before letting its own key handlers run +bool CDXUTIMEEditBox::IsImeActive() +{ + if( s_CandList.bShowWindow ) + return true; + + if( s_bShowReadingWindow ) + return true; + + if( (int)( GetTickCount() - dwImeWaitTick ) < 300 ) + return true; + + return false; +} + //-------------------------------------------------------------------------------------- // MATCH diff --git a/saco/d3d9/common/DXUTgui.h b/saco/d3d9/common/DXUTgui.h index a43a99f..c803498 100644 --- a/saco/d3d9/common/DXUTgui.h +++ b/saco/d3d9/common/DXUTgui.h @@ -17,6 +17,7 @@ // Defines and macros //-------------------------------------------------------------------------------------- #define EVENT_BUTTON_CLICKED 0x0101 +#define EVENT_BUTTON_PRESSED 0x0102 #define EVENT_COMBOBOX_SELECTION_CHANGED 0x0201 #define EVENT_RADIOBUTTON_CHANGED 0x0301 #define EVENT_CHECKBOX_CHANGED 0x0401 @@ -328,6 +329,7 @@ public: int AddFont( LPCTSTR strFaceName, LONG height, LONG weight ); int AddTexture( LPCTSTR strFilename ); + int SetFont( int iFont, LPCTSTR strFaceName, LONG height, LONG weight ); DXUTFontNode* GetFontNode( int iIndex ) { return m_FontCache.GetAt( iIndex ); }; DXUTTextureNode* GetTextureNode( int iIndex ) { return m_TextureCache.GetAt( iIndex ); }; @@ -593,11 +595,15 @@ protected: //----------------------------------------------------------------------------- struct DXUTListBoxItem { - TCHAR strText[256]; + TCHAR strText[257]; + TCHAR strColumnText[3][129]; void* pData; RECT rcActive; bool bSelected; + + D3DCOLOR TextColor; + bool field_29D; }; class CDXUTListBox : public CDXUTControl @@ -619,9 +625,10 @@ public: void SetStyle( DWORD dwStyle ) { m_dwStyle = dwStyle; } void SetScrollBarWidth( int nWidth ) { m_nSBWidth = nWidth; UpdateRects(); } void SetBorder( int nBorder, int nMargin ) { m_nBorder = nBorder; m_nMargin = nMargin; } - HRESULT AddItem( const TCHAR *wszText, void *pData ); - HRESULT InsertItem( int nIndex, const TCHAR *wszText, void *pData ); + HRESULT AddItem( const TCHAR *wszText, void *pData, D3DCOLOR TextColor ); + HRESULT InsertItem( int nIndex, const TCHAR *wszText, void *pData, D3DCOLOR TextColor ); void RemoveItem( int nIndex ); + void SetItemColumnText( int nIndex, int nColumn, const TCHAR *wszText ); void RemoveItemByText( TCHAR *wszText ); void RemoveItemByData( void *pData ); void RemoveAllItems(); @@ -630,18 +637,17 @@ public: int GetSelectedIndex( int nPreviousSelected = -1 ); DXUTListBoxItem *GetSelectedItem( int nPreviousSelected = -1 ) { return GetItem( GetSelectedIndex( nPreviousSelected ) ); } void SelectItem( int nNewIndex ); + CDXUTScrollBar *GetScrollBar() { return &m_ScrollBar; } enum STYLE { MULTISELECTION = 1 }; int field_4D; - int field_51; - int field_55; - int field_59; + int field_51[3]; protected: + CDXUTScrollBar m_ScrollBar; RECT m_rcText; // Text rendering bound RECT m_rcSelection; // Selection box bound - CDXUTScrollBar m_ScrollBar; int m_nSBWidth; int m_nBorder; int m_nMargin; @@ -798,6 +804,7 @@ public: bool InsertChar( int nIndex, WCHAR tchr ); // Inserts the char at specified index. If nIndex == -1, insert to the end. bool InsertChar( int nIndex, CHAR tchr ); + bool SetChar( int nIndex, CHAR tchr ); bool RemoveChar( int nIndex ); // Removes the char at specified index. If nIndex == -1, remove the last char. bool InsertString( int nIndex, const WCHAR *pStr, int nCount = -1 ); // Inserts the first nCount characters of the string pStr at specified index. If nCount == -1, the entire string is inserted. If nIndex == -1, insert to the end. bool SetText( LPCWSTR wszText ); @@ -1005,6 +1012,7 @@ public: virtual void OnFocusOut(); void PumpMessage(); + static bool IsImeActive(); virtual void RenderCandidateReadingWindow( IDirect3DDevice9* pd3dDevice, float fElapsedTime, bool bReading ); virtual void RenderComposition( IDirect3DDevice9* pd3dDevice, float fElapsedTime ); @@ -1033,7 +1041,7 @@ protected: struct CCandList { - TCHAR awszCandidate[MAX_CANDLIST][256]; + WCHAR awszCandidate[MAX_CANDLIST][256]; CUniBuffer HoriCand; // Candidate list string (for horizontal candidate window) int nFirstSelected; // First character position of the selected string in HoriCand int nHoriSelectedLen; // Length of the selected string in HoriCand @@ -1071,7 +1079,7 @@ protected: static CUniBuffer s_CompString; // Buffer to hold the composition string (we fix its length) static BYTE s_abCompStringAttr[MAX_COMPSTRING_SIZE]; static DWORD s_adwCompStringClause[MAX_COMPSTRING_SIZE]; - static TCHAR s_wszReadingString[32];// Used only with horizontal reading window (why?) + static WCHAR s_wszReadingString[32];// Used only with horizontal reading window (why?) static CCandList s_CandList; // Data relevant to the candidate list static bool s_bShowReadingWindow; // Indicates whether reading window is visible static bool s_bHorizontalReading; // Indicates whether the reading window is vertical or horizontal diff --git a/saco/d3dhook/ID3DXFontHook.cpp b/saco/d3dhook/ID3DXFontHook.cpp index 5ce2706..e43a539 100644 --- a/saco/d3dhook/ID3DXFontHook.cpp +++ b/saco/d3dhook/ID3DXFontHook.cpp @@ -2,9 +2,36 @@ #include "ID3DXFontHook.h" #include "../main.h" +int dword_10136EA0; +DWORD dword_10136E90; +int dword_10136EA4; +char *dword_10140AEC; +char *dword_10140AF0; +WCHAR WideCharStr[20000]; +int off_10102BF0; + +void sub_100739A0(char *lpMultiByteStr, WCHAR *lpWideCharStr, int cchWideChar); +void sub_100B60C0(char *lpString); + INT __stdcall ID3DXFontHook::DrawTextA(LPD3DXSPRITE pSprite, LPCSTR pString, INT Count, LPRECT pRect, DWORD Format, D3DCOLOR Color) { - // TODO: ID3DXFontHook::DrawTextA + if(pSprite) + { + dword_10136EA0 = (int)pSprite; + dword_10136E90 = Color; + dword_10136EA4 = 0; + + if(strlen(pString) > 0x186A0) + return 0; + + strcpy(dword_10140AEC, pString); + strcpy(dword_10140AF0, pString); + + sub_100739A0(dword_10140AEC, WideCharStr, 20000); + sub_100B60C0(dword_10140AF0); + + return m_pD3DFont->DrawTextA((LPD3DXSPRITE)&off_10102BF0, dword_10140AF0, strlen(dword_10140AF0), pRect, Format, Color); + } return m_pD3DFont->DrawTextA(pSprite, pString, Count, pRect, Format, Color); } diff --git a/saco/d3dhook/IDirect3DDevice9Hook.cpp b/saco/d3dhook/IDirect3DDevice9Hook.cpp index 158c04b..6a13e5e 100644 --- a/saco/d3dhook/IDirect3DDevice9Hook.cpp +++ b/saco/d3dhook/IDirect3DDevice9Hook.cpp @@ -13,15 +13,360 @@ #include "../main.h" extern IDirect3DDevice9 *pD3DDevice; +extern CNetGame *pNetGame; D3DXMATRIX matView, matProj, matWorld; +int dword_101A25F8; +int dword_101A25FC; +int dword_10140D60; +int dword_1026EBA0; +int dword_101516D8; + +extern CGame *pGame; +extern CChatWindow *pChatWindow; +extern CNewPlayerTags *pNewPlayerTags; +extern GAME_SETTINGS tSettings; + +int dword_1012DE64; +int dword_1026EB4C; +int dword_1026EB50; +int dword_1026EB54; +int dword_1026EB58; +int dword_1026EB5C; +int dword_1026EB60; +int dword_1026EB64; +int dword_1026EB68; +int dword_1026EB6C; +int dword_1026EB70; +int dword_1026EB74; +int dword_1026EB78; +int dword_1026EB84; +int dword_1026EB88; +int dword_1026EB8C; +int dword_1026EC24; +int dword_1026EC2C; +char byte_1026EBB8; +MATRIX4X4 stru_10140CF8; +MATRIX4X4 stru_10140AF8; +VECTOR vec_10140D3C; +VECTOR vec_10140D64; +char byte_10140C78[128]; + +void sub_1009D8B0(); // .text:1009D8B0 +void sub_100C3F50(); // .text:100C3F50 +void sub_100C41B0(); // .text:100C41B0 +int sub_100A4470(); // .text:100A4470 +int sub_100C3E20(int a1, int a2); // .text:100C3E20 +int sub_10062270(std::string *pstrFileName); // .text:10062270 +void sub_10074480(); // .text:10074480 +void sub_10074210(); // .text:10074210 +int sub_100B7E00(); // .text:100B7E00 +void sub_1009E490(); // .text:1009E490 +// samp keeps this gta helper in a data slot, so the call goes through a pointer +float (*dword_10102BF4)(float a1, float a2) = (float (*)(float, float))0x568FE0; + +// dispatch helpers invoked on render-manager globals (thiscall) +class CD3DHookDispatch +{ +public: + void FUNC_10066AB0(); // .text:10066AB0 + void FUNC_1006BC70(); // .text:1006BC70 + void FUNC_1006BC80(); // .text:1006BC80 + void FUNC_1006BC90(void *a1, char *a2, int a3, int a4, int a5); // .text:1006BC90 + void FUNC_1006F0B0(); // .text:1006F0B0 + void FUNC_10060D70(); // .text:10060D70 + void FUNC_10071260(); // .text:10071260 + void FUNC_1006BB30(); // .text:1006BB30 + void FUNC_10066ED0(); // .text:10066ED0 + void FUNC_100696F0(); // .text:100696F0 + void FUNC_1006A2E0(); // .text:1006A2E0 + void FUNC_1006F890(); // .text:1006F890 + void FUNC_10071AC0(); // .text:10071AC0 + void FUNC_1006A910(); // .text:1006A910 + void FUNC_100908E0(float a1); // .text:100908E0 + void FUNC_10073780(); // .text:10073780 + void FUNC_1006DC00(); // .text:1006DC00 + void FUNC_10071410(); // .text:10071410 +}; + +//------------------------------------------- + +// per-frame nametag pass: walks the player pool and draws a tag over every visible remote ped +void sub_10075330() +{ + if(!pNetGame) + return; + if(!pNetGame->GetSettings()->field_38) + return; + + pNewPlayerTags->FUNC_1006CD80(); + + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + pGame->FindPlayerPed()->GetMatrix(&stru_10140CF8); + + int iHighestId = pPlayerPool->field_2F3A; + for(int i = 0; i <= iHighestId; i++) + { + if(pPlayerPool->GetSlotState((PLAYERID)i) != 1) + continue; + CRemotePlayer *pRemotePlayer = pPlayerPool->GetAt((PLAYERID)i); + if(!pRemotePlayer) + continue; + CPlayerPed *pPlayerPed = pRemotePlayer->m_pPlayerPed; + if(!pPlayerPed) + continue; + if(!pRemotePlayer->field_10A) + continue; + if(!pRemotePlayer->field_4) + continue; + + float fDistance = pPlayerPed->FUNC_1009F190(); + if(fDistance <= pNetGame->GetSettings()->field_27) + { + if(pRemotePlayer->field_10A == 19 && pRemotePlayer->field_1E1 && sub_100B7E00()) + { + ((CEntity *)pRemotePlayer->field_1E1)->GetMatrix(&stru_10140AF8); + vec_10140D3C = stru_10140AF8.pos; + } + else + { + if(!pPlayerPed->IsAdded()) + continue; + memset(&vec_10140D3C, 0, sizeof(VECTOR)); + pPlayerPed->GetBonePosition(8, &vec_10140D3C); + } + + vec_10140D64 = vec_10140D3C; + + CAMERA_AIM *pAim = GameGetInternalAim(); + int iLineOfSight = 0; + if(pNetGame->GetSettings()->field_2F) + iLineOfSight = ScriptCommand(&is_line_of_sight_clear, + vec_10140D64.X, vec_10140D64.Y, vec_10140D64.Z, + pAim->pos1x, pAim->pos1y, pAim->pos1z, 1, 0, 0, 1, 0); + if(!pNetGame->GetSettings()->field_2F || iLineOfSight) + { + sprintf(byte_10140C78, "%s (%d)", pPlayerPool->FUNC_100175C0(i), i); + if(!pRemotePlayer->field_10C) + { + pNewPlayerTags->Draw((D3DXVECTOR3 *)&vec_10140D64, byte_10140C78, + pRemotePlayer->GetPlayerColorAsARGB(), pPlayerPed->FUNC_1009F190(), + pNetGame->field_234, pRemotePlayer->FUNC_10016330()); + } + } + } + } + + pNewPlayerTags->FUNC_1006CD90(); +} + +//------------------------------------------- + +// screenshot grab: reached from Present when the F8 handler raises dword_1012DE64 +void sub_100755C0() +{ + dword_1012DE64 = 0; + + std::string strFileName; + int iIndex = sub_10062270(&strFileName); + + IDirect3DSurface9 *pSurface; + pD3DDevice->CreateOffscreenPlainSurface(GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), + D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pSurface, NULL); + + if(SUCCEEDED(pD3DDevice->GetFrontBufferData(0, pSurface))) + { + POINT pt; + pt.x = 0; + pt.y = 0; + ClientToScreen(*(HWND *)0xC97C1C, &pt); + + RECT rc; + GetClientRect(*(HWND *)0xC97C1C, &rc); + rc.left += pt.x; + rc.right += pt.x; + rc.top += pt.y; + rc.bottom += pt.y; + + D3DXSaveSurfaceToFileA(strFileName.c_str(), D3DXIFF_PNG, pSurface, NULL, &rc); + pChatWindow->AddInfoMessage("Screenshot Taken - sa-mp-%03i.png", iIndex); + } + else + { + pChatWindow->AddDebugMessage("Unable to save screenshot."); + } +} + //------------------------------------------- HRESULT __stdcall IDirect3DDevice9Hook::Present(CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion) { - // TODO: IDirect3DDevice9Hook::Present + if(dword_1012DE64) + sub_100755C0(); - return pD3DDevice->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion); + sub_10074480(); + + if(!pGame->sub_100A0920()) + { + if(pNetGame) + { + CVehiclePool *v3 = pNetGame->GetVehiclePool(); + sub_10074210(); + sub_10075330(); + if(dword_1026EB8C) + ((CD3DHookDispatch *)dword_1026EB8C)->FUNC_10066AB0(); + if(byte_1026EBB8) + { + if(dword_1026EB5C) + ((CD3DHookDispatch *)dword_1026EB5C)->FUNC_1006BC70(); + float *v4 = (float *)((char *)v3 + 0x11AD8); + for(unsigned short i = 0; i < MAX_VEHICLES; i++, v4 += 3) + { + if(v3->GetSlotState(i) == 1) + { + CEntity *v7 = (CEntity *)v3->GetAt(i); + if(v7) + { + if(v7->FUNC_1009F0C0() <= 20.0f) + { + MATRIX4X4 a2; + v7->GetMatrix(&a2); + int v35[3]; + v35[0] = *(int *)&a2.pos.X; + v35[1] = *(int *)&a2.pos.Y; + v35[2] = *(int *)&a2.pos.Z; + char string[264]; + sprintf(string, + "[id: %d, type: %d subtype: %d Health: %.1f preloaded: %u]\n" + "Distance: %.2fm\n" + "PassengerSeats: %u\n" + "cPos: %.3f,%.3f,%.3f\n" + "sPos: %.3f,%.3f,%.3f", + i, + v7->GetModelIndex(), + v7->FUNC_100B7390(), + v7->FUNC_100B72A0(), + *((BYTE *)pGame + v7->GetModelIndex() - 0x122), + v7->FUNC_1009F0C0(), + v7->FUNC_100B8340(), + a2.pos.X, a2.pos.Y, a2.pos.Z, v4[-1], v4[0], v4[1]); + ((CD3DHookDispatch *)dword_1026EB5C)->FUNC_1006BC90(v35, string, 0xFF358BD4, 1, 0); + } + } + } + } + if(dword_1026EB5C) + ((CD3DHookDispatch *)dword_1026EB5C)->FUNC_1006BC80(); + } + if(pNetGame) + { + if(*(int *)dword_1026EB4C) + { + pGame->sub_100A1DB0(0); + ((CD3DHookDispatch *)dword_1026EB4C)->FUNC_1006F0B0(); + goto LABEL_77; + } + if(GetAsyncKeyState(0x74) && pNetGame->GetGameState() == 5) + { + pGame->sub_100A1DB0(0); + if(dword_1026EB6C) + ((CD3DHookDispatch *)dword_1026EB6C)->FUNC_10060D70(); + goto LABEL_77; + } + if(pNetGame) + { + if(GetAsyncKeyState(0x79)) + { + pGame->sub_100A1DB0(0); + if(dword_1026EB70) + ((CD3DHookDispatch *)dword_1026EB70)->FUNC_10071260(); + goto LABEL_77; + } + if(pNetGame && GetAsyncKeyState(0x70) && dword_1026EB74) + ((CD3DHookDispatch *)dword_1026EB74)->FUNC_1006BB30(); + } + } + } + + if(pChatWindow->field_8) + pGame->sub_100A1DB0(1); + else + pGame->sub_100A1DB0(0); + if(pGame) + { + if(pGame->FindPlayerPed()) + { + CPlayerPed *PlayerPed = pGame->FindPlayerPed(); + if(PlayerPed->sub_100ADFA0() > 5000) + pGame->sub_100A1DB0(0); + } + } + if(dword_1026EB78) + ((CD3DHookDispatch *)dword_1026EB78)->FUNC_10066ED0(); + if(pNetGame && pNetGame->GetLabelPool()) + pNetGame->GetLabelPool()->FUNC_10001350(); + if(pChatWindow) + pChatWindow->FUNC_10067E00(); + if(dword_1026EB84) + ((CD3DHookDispatch *)dword_1026EB84)->FUNC_100696F0(); + if(dword_1026EB88) + ((CD3DHookDispatch *)dword_1026EB88)->FUNC_1006A2E0(); + if(pNetGame) + { + if(dword_1026EB50) + ((CD3DHookDispatch *)dword_1026EB50)->FUNC_1006F890(); + if(pNetGame) + { + if(dword_1026EB54) + ((CD3DHookDispatch *)dword_1026EB54)->FUNC_10071AC0(); + if(pNetGame && dword_1026EB58) + ((CD3DHookDispatch *)dword_1026EB58)->FUNC_1006A910(); + } + } + if(dword_1026EC24) + ((CD3DHookDispatch *)dword_1026EC24)->FUNC_100908E0(10.0f); + if(dword_1026EC2C) + ((CD3DHookDispatch *)dword_1026EC2C)->FUNC_100908E0(10.0f); + if(dword_1026EB60) + ((CD3DHookDispatch *)dword_1026EB60)->FUNC_10073780(); + if(dword_1026EB64) + ((CD3DHookDispatch *)dword_1026EB64)->FUNC_1006DC00(); + if(dword_1026EB68) + ((CD3DHookDispatch *)dword_1026EB68)->FUNC_10071410(); + } + else + { + if(pGame->sub_100A0920() && pNetGame && *(int *)0xBA6774 && (GetAsyncKeyState(2) & 1) && !*(char *)0xBA8298) + { + float v32, v30; + char *p = (char *)0xBA873D; + int n = 175; + do + { + if(*(WORD *)p == 0x1203 && *(WORD *)p) + { + v32 = *(float *)(p - 29); + v30 = *(float *)(p - 25); + } + p += 40; + } + while(--n); + + RakNet::BitStream bs; + bs.Write(v32); + bs.Write(v30); + bs.Write(dword_10102BF4(v32, v30) + 2.0f); + pNetGame->GetRakClient()->RPC("w", &bs, HIGH_PRIORITY, RELIABLE, 0, 0); + } + } + +LABEL_77: + if(tSettings.bDebug) + sub_1009E490(); + sub_1009D8B0(); + HRESULT hr = pD3DDevice->Present(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion); + sub_1009D8B0(); + return hr; } HRESULT __stdcall IDirect3DDevice9Hook::QueryInterface(REFIID riid, void** ppvObj) @@ -106,9 +451,11 @@ UINT __stdcall IDirect3DDevice9Hook::GetNumberOfSwapChains() HRESULT __stdcall IDirect3DDevice9Hook::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) { - // TODO: IDirect3DDevice9Hook::Reset - - return pD3DDevice->Reset(pPresentationParameters); + sub_100C3F50(); + HRESULT hr = pD3DDevice->Reset(pPresentationParameters); + if(hr >= 0) + sub_100C41B0(); + return hr; } HRESULT __stdcall IDirect3DDevice9Hook::GetBackBuffer(UINT iSwapChain, UINT iBackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface9** ppBackBuffer) @@ -233,9 +580,10 @@ HRESULT __stdcall IDirect3DDevice9Hook::BeginScene() HRESULT __stdcall IDirect3DDevice9Hook::EndScene() { - // TODO: IDirect3DDevice9Hook::EndScene - - return pD3DDevice->EndScene(); + sub_1009D8B0(); + HRESULT hr = pD3DDevice->EndScene(); + sub_1009D8B0(); + return hr; } HRESULT __stdcall IDirect3DDevice9Hook::Clear(DWORD Count, CONST D3DRECT* pRects, DWORD Flags, D3DCOLOR Color, float Z, DWORD Stencil) @@ -363,7 +711,36 @@ HRESULT __stdcall IDirect3DDevice9Hook::GetTexture(DWORD Stage, IDirect3DBaseTex HRESULT __stdcall IDirect3DDevice9Hook::SetTexture(DWORD Stage, IDirect3DBaseTexture9* pTexture) { - // TODO: IDirect3DDevice9Hook::SetTexture + if(dword_101A25F8 && dword_101A25FC) + { + for(dword_10140D60 = 0; dword_10140D60 != 16; dword_10140D60++) + { + if(*(DWORD *)(dword_101A25FC + 4 * dword_10140D60 + 0x110B) == (DWORD)pTexture && + *(DWORD *)(dword_101A25FC + 4 * dword_10140D60 + 0x114B)) + { + return pD3DDevice->SetTexture(Stage, + (IDirect3DBaseTexture9 *)*(DWORD *)(dword_101A25FC + 4 * dword_10140D60 + 0x114B)); + } + } + } + + int v4 = sub_100A4470(); + if(v4 && v4 == (int)pTexture && pNetGame && pNetGame->GetVehiclePool()) + { + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + short v5 = pVehiclePool->FUNC_1001EB90(dword_101516D8); + int v7 = pVehiclePool->FUNC_10001120(v5); + if(v7 && *(DWORD *)(v7 + 0x8F)) + { + sub_100C3E20(9, 2); + return pD3DDevice->SetTexture(Stage, (IDirect3DBaseTexture9 *)*(DWORD *)(v7 + 0x8F)); + } + if(*(DWORD *)(dword_1026EBA0 + 0x20)) + { + sub_100C3E20(9, 2); + return pD3DDevice->SetTexture(Stage, (IDirect3DBaseTexture9 *)*(DWORD *)(dword_1026EBA0 + 0x20)); + } + } return pD3DDevice->SetTexture(Stage, pTexture); } diff --git a/saco/exceptions.cpp b/saco/exceptions.cpp index fb6dd15..9568cd7 100644 --- a/saco/exceptions.cpp +++ b/saco/exceptions.cpp @@ -27,13 +27,38 @@ int dword_10125A58=0; void DumpNetworkStateInformation(PCHAR sz) { CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); - PLAYERID playerId=0; + PLAYERID playerId; + char szBuffer[16384]; sz[0] = '\0'; sprintf(sz,"\r\nState Information: Ped Context: %u\r\n",*pbyteCurrentPlayer); - // TODO: DumpNetworkStateInformation 10060160 + playerId = 0; + do + { + if(playerId < MAX_PLAYERS && pPlayerPool->field_2A[playerId]) + { + CNetPlayer *pNetPlayer = pPlayerPool->m_pPlayers[playerId]; + CRemotePlayer *pRemotePlayer; + + if(pNetPlayer) + pRemotePlayer = pNetPlayer->m_pRemotePlayer; + else + pRemotePlayer = NULL; + + sprintf(szBuffer,"P%u (%u,%u) ",playerId,pRemotePlayer->field_10A,pRemotePlayer->field_1E7); + strcat(sz,szBuffer); + + if(playerId > 0) + { + if(!((playerId + 1) % 4)) + strcat(sz,"\r\n"); + } + } + playerId++; + } + while(playerId != MAX_PLAYERS); } //---------------------------------------------------- diff --git a/saco/game/actorped.h b/saco/game/actorped.h index 2322471..ad2353c 100644 --- a/saco/game/actorped.h +++ b/saco/game/actorped.h @@ -31,6 +31,10 @@ public: void ToggleImmunity(BYTE byteEnable); + void FUNC_1009C550(); // .text:1009C550 + void FUNC_1009C570(float a1); // .text:1009C570 + void FUNC_1009C5D0(int a1); // .text:1009C5D0 + PED_TYPE *m_pPed; DWORD m_dwMarkerID; DWORD m_dwArrow; diff --git a/saco/game/audio.h b/saco/game/audio.h index 646bb7f..4d5f40d 100644 --- a/saco/game/audio.h +++ b/saco/game/audio.h @@ -5,7 +5,7 @@ class CAudio { -private: +public: int field_0; bool field_4; diff --git a/saco/game/camera.h b/saco/game/camera.h index ae0d7d1..607853e 100644 --- a/saco/game/camera.h +++ b/saco/game/camera.h @@ -23,6 +23,7 @@ public: void InterpolateLookAt(VECTOR *vecFrom, VECTOR *vecTo, int iTime, BYTE byteCut); void FUNC_1009D660(CEntity *pEntity); void FUNC_1009D6B0(); + void FUNC_1009D740(); // .text:1009D740 float GetDistanceFromPoint(VECTOR *vecPos); CCamera() { diff --git a/saco/game/common.h b/saco/game/common.h index 6236eb4..666a54a 100644 --- a/saco/game/common.h +++ b/saco/game/common.h @@ -65,7 +65,8 @@ typedef struct _WEAPON_SLOT_TYPE { DWORD dwType; - char _gap4[8]; + DWORD field_4; + DWORD field_8; DWORD dwAmmo; diff --git a/saco/game/entity.h b/saco/game/entity.h index f6f98f3..eb089f5 100644 --- a/saco/game/entity.h +++ b/saco/game/entity.h @@ -26,6 +26,7 @@ public: void GetBoundCentre(PVECTOR Vector); void GetBoundRect(PFRECT Rect); // unused UINT GetModelIndex(); + int SetModelIndex(int iModel); // .text:1009EF50 void TeleportTo(float x, float y, float z); float GetDistanceFromLocalPlayerPed(); float GetDistanceFromCamera(); @@ -49,10 +50,22 @@ public: DWORD GetWorldBoundRadius(); void SetEulerAngles(float fX, float fY, float fZ); + void FUNC_100A7E20(); // .text:100A7E20 + virtual void Add(); virtual void Remove(); BOOL IsAdded(); + float FUNC_1009F0C0(); // .text:1009F0C0 + BOOL FUNC_1009F420(float a1, float a2, float a3, float a4); // .text:1009F420 + float FUNC_1009F190(); // .text:1009F190 + char FUNC_1009FC00(VECTOR *vecStart, VECTOR *vecEnd, char a3); // .text:1009FC00 + char FUNC_1009FCE0(); // .text:1009FCE0 + BYTE FUNC_100B8340(); // .text:100B8340 + float FUNC_100B72A0(); // .text:100B72A0 + int FUNC_100B7390(); // .text:100B7390 + DWORD * FUNC_1009FA60(); // .text:1009FA60 + VECTOR field_4[5]; ENTITY_TYPE *m_pEntity; DWORD m_dwGTAId; diff --git a/saco/game/game.cpp b/saco/game/game.cpp index b7f4164..4ebc116 100644 --- a/saco/game/game.cpp +++ b/saco/game/game.cpp @@ -49,14 +49,18 @@ CGame::CGame() memset(bUsedPlayerSlots, 0, sizeof(bUsedPlayerSlots)); memset(field_6E, 0, sizeof(field_6E)); field_55 = 0; - field_59 = 1; + m_bHeadMove = 1; field_5D = 90; } void CGame::sub_100A0010() { int time = (int)RakNet::GetTime(); - if(unnamed_1015068C) + if(!unnamed_1015068C) + { + unnamed_1015068C = time; + } + else { if((time - unnamed_1015068C) > 30) { @@ -67,13 +71,9 @@ void CGame::sub_100A0010() } *(BYTE*)0xB7356E = unnamed_10150688; } - else - { - unnamed_1015068C = time; - } } -void unnamed_100A0060(float a1) +void __stdcall unnamed_100A0060(float a1) { *(float*)0xB7CB5C = a1; *(float*)0xB7CB58 = a1; @@ -243,9 +243,18 @@ void CGame::ProcessInputDisabling() *(BYTE*)0x53F421 = 0x0F; *(BYTE*)0x53F422 = 0x8C; - DIResetMouse(); - UpdatePads(); - DIResetMouse(); + *(DWORD*)0xB73424 = 0; + *(DWORD*)0xB73428 = 0; + _asm mov edx, 0x541BD0 + _asm call edx + + _asm mov edx, 0x541DD0 + _asm call edx + + *(DWORD*)0xB73424 = 0; + *(DWORD*)0xB73428 = 0; + _asm mov edx, 0x541BD0 + _asm call edx UnFuck(0x6194A0,1); *(BYTE*)0x6194A0 = 0xE9; @@ -253,9 +262,8 @@ void CGame::ProcessInputDisabling() pD3DDevice->ShowCursor(FALSE); field_65--; } else { - if(field_65 <= 0) - return; - field_65 -= 1; + if(field_65 > 0) + field_65--; } } @@ -269,8 +277,13 @@ void CGame::ToggleKeyInputsDisabled(int a2, BOOL a3) memset((PVOID)0x541DF5,0x90,5); // disable call DisableMouseProcess(); - DIResetMouse(); - UpdatePads(); + *(DWORD*)0xB73424 = 0; + *(DWORD*)0xB73428 = 0; + _asm mov edx, 0x541BD0 + _asm call edx + + _asm mov edx, 0x541DD0 + _asm call edx UnFuck(0x6194A0u,1); *(BYTE*)0x6194A0 = 0xC3; @@ -293,8 +306,13 @@ void CGame::ToggleKeyInputsDisabled(int a2, BOOL a3) if(field_61 != 3) { DisableMouseProcess(); - DIResetMouse(); - UpdatePads(); + *(DWORD*)0xB73424 = 0; + *(DWORD*)0xB73428 = 0; + _asm mov edx, 0x541BD0 + _asm call edx + + _asm mov edx, 0x541DD0 + _asm call edx UnFuck(0x6194A0,1); *(BYTE*)0x6194A0 = 0xC3; @@ -308,8 +326,13 @@ void CGame::ToggleKeyInputsDisabled(int a2, BOOL a3) if(field_61 != 4) { DisableMousePositionUpdate(); - DIResetMouse(); - UpdatePads(); + *(DWORD*)0xB73424 = 0; + *(DWORD*)0xB73428 = 0; + _asm mov edx, 0x541BD0 + _asm call edx + + _asm mov edx, 0x541DD0 + _asm call edx UnFuck(0x6194A0,1); *(BYTE*)0x6194A0 = 0xC3; @@ -380,29 +403,55 @@ void CGame::StartGame() -void CGame::sub_100A0110() +CPlayerPed *CGame::sub_100A0110(int a1, float fX, float fY, float fZ, float fRotation, int a6, int a7) { // TODO: CGame::sub_100A0110() .text:100A0110 + return NULL; } -void CGame::sub_100A0210() +int CGame::DeletePlayerPed(int pPlayerPed) { - // TODO: CGame::sub_100A0210() .text:100A0210 + if(pPlayerPed) + { + BYTE bytePlayerNumber = *(BYTE *)(pPlayerPed + 0x2B0); + ((struc_vdelete *)pPlayerPed)->Delete(1); + bUsedPlayerSlots[bytePlayerNumber] = 0; + return 1; + } + return 0; } -void CGame::sub_100A0250() +CVehicle *CGame::sub_100A0250(int a2, float a3, float a4, float a5, float a6, int a7) { - // TODO: CGame::sub_100A0250() .text:100A0250 + BOOL bKeepModelLoaded = 0; + if(*((char *)this + a2 - 290)) + bKeepModelLoaded = 1; + CVehicle *pVehicle = new CVehicle(a2, a3, a4, a5, a6, bKeepModelLoaded, a7); + return pVehicle->m_pVehicle ? pVehicle : 0; } -void CGame::sub_100A02E0() +char szUnk_10113968[8]; + +void sub_100A4170(const char *a1) { - // TODO: CGame::sub_100A02E0() .text:100A02E0 (unused) + memset(szUnk_10113968, 0, sizeof(szUnk_10113968)); + strcpy(szUnk_10113968, a1); } -void CGame::sub_100A0310() +void __stdcall sub_100A02E0(const char *a1) { - // TODO: CGame::sub_100A0310() .text:100A0310 (unused) + if(a1 && *a1 && strlen(a1) <= 8) + sub_100A4170(a1); +} + +int __stdcall sub_100A0310(void *a1) +{ + if(a1) + { + ((struc_vdelete *)a1)->Delete(1); + return 1; + } + return 0; } void CGame::sub_100A0330() @@ -438,6 +487,13 @@ void CGame::sub_100A1C10() // TODO: CGame::sub_100A1C10() .text:100A1C10 } +//----------------------------------------------------------- + +BOOL CGame::sub_100A0920() +{ + return *(PDWORD)0xBA67A4 != 0; +} + @@ -479,8 +535,8 @@ BOOL CGame::SetModelDeletable(int iModelID) { BYTE * pStreamingModelInfo = (BYTE*)(iModelID * 20); - if(IsModelLoaded(iModelID)) - return TRUE; + if(iModelID > 20000 || iModelID < 0) return TRUE; + if(ScriptCommand(&is_model_available,iModelID)) return TRUE; _asm push 2 _asm push iModelID @@ -573,10 +629,10 @@ void CGame::DisplayHud(BOOL bDisp) { if(bDisp) { *(BYTE*)ADDR_ENABLE_HUD = 1; - ToggleRadar(1); + *(PBYTE)0xBAA3FB = 0; } else { *(BYTE*)ADDR_ENABLE_HUD = 0; - ToggleRadar(0); + *(PBYTE)0xBAA3FB = 1; } } @@ -668,7 +724,7 @@ int CGame::IsAnimationLoaded(char *szAnimFile) void CGame::ReleaseAnimation(char *szAnimFile) { - if (IsAnimationLoaded(szAnimFile)) + if (ScriptCommand(&is_animation_loaded,szAnimFile)) ScriptCommand(&release_animation,szAnimFile); } @@ -713,7 +769,7 @@ void CGame::SetCheckpointInformation(VECTOR *pos, VECTOR *extent) memcpy(&m_vecCheckpointPos,pos,sizeof(VECTOR)); memcpy(&m_vecCheckpointExtent,extent,sizeof(VECTOR)); if(m_dwCheckpointMarker) { - DisableMarker(m_dwCheckpointMarker); + ScriptCommand(&disable_marker, m_dwCheckpointMarker); m_dwCheckpointMarker = NULL; DWORD dwMarkerID = 0; @@ -734,7 +790,7 @@ void CGame::SetRaceCheckpointInformation(BYTE byteType, VECTOR *pos, VECTOR *nex m_byteRaceType = byteType; if(m_dwRaceCheckpointMarker) { - DisableMarker(m_dwRaceCheckpointMarker); + ScriptCommand(&disable_marker, m_dwRaceCheckpointMarker); m_dwRaceCheckpointMarker = NULL; DWORD dwMarkerID = 0; @@ -750,7 +806,12 @@ void CGame::SetRaceCheckpointInformation(BYTE byteType, VECTOR *pos, VECTOR *nex void CGame::MakeRaceCheckpoint() { - DisableRaceCheckpoint(); + if (m_dwRaceCheckpointHandle) + { + ScriptCommand(&destroy_racing_checkpoint, m_dwRaceCheckpointHandle); + m_dwRaceCheckpointHandle = NULL; + } + m_bRaceCheckpointsEnabled = false; ScriptCommand(&create_racing_checkpoint, (int)m_byteRaceType, m_vecRaceCheckpointPos.X, m_vecRaceCheckpointPos.Y, m_vecRaceCheckpointPos.Z, @@ -796,7 +857,7 @@ void CGame::UpdateCheckpoints() } } else if(m_dwCheckpointMarker) { - DisableMarker(m_dwCheckpointMarker); + ScriptCommand(&disable_marker, m_dwCheckpointMarker); m_dwCheckpointMarker = NULL; } @@ -816,8 +877,13 @@ void CGame::UpdateCheckpoints() } } else if(m_dwRaceCheckpointMarker) { - DisableMarker(m_dwRaceCheckpointMarker); - DisableRaceCheckpoint(); + ScriptCommand(&disable_marker, m_dwRaceCheckpointMarker); + if (m_dwRaceCheckpointHandle) + { + ScriptCommand(&destroy_racing_checkpoint, m_dwRaceCheckpointHandle); + m_dwRaceCheckpointHandle = NULL; + } + m_bRaceCheckpointsEnabled = false; m_dwRaceCheckpointMarker = NULL; } } @@ -878,7 +944,9 @@ void CGame::UpdateFarClippingPlane() PED_TYPE *pPlayerPed = GamePool_FindPlayerPed(); if(pPlayerPed) { - if(GetActiveInterior() == 0) { + DWORD dwInterior; + ScriptCommand(&get_active_interior,&dwInterior); + if(!(BYTE)dwInterior) { fFarClip = 1250.0f - (pPlayerPed->entity.mat->pos.Z * 2.0f); if(fFarClip < 700.0f) { fFarClip = 700.0f; @@ -904,13 +972,13 @@ void CGame::AddToLocalMoney(int iAmount) void CGame::ResetLocalMoney() { - int iMoney = GetLocalMoney(); + int iMoney = *(int *)0xB7CE50; if(!iMoney) return; if(iMoney < 0) { - AddToLocalMoney(abs(iMoney)); + ScriptCommand(&add_to_player_money,0,abs(iMoney)); } else { - AddToLocalMoney(-(iMoney)); + ScriptCommand(&add_to_player_money,0,-(iMoney)); } } @@ -1025,10 +1093,13 @@ DWORD CGame::CreateWeaponPickup(int iModel, DWORD dwAmmo, float fX, float fY, fl { DWORD hnd; - if(!IsModelLoaded(iModel)) { - RequestModel(iModel); - LoadRequestedModels(); - while(!IsModelLoaded(iModel)) Sleep(5); + // the model wait is spelled out, none of the model helpers are inline here + if(iModel <= 20000 && iModel >= 0 && !ScriptCommand(&is_model_available, iModel)) + { + ScriptCommand(&request_model, iModel); + ScriptCommand(&load_requested_models); + + while(!ScriptCommand(&is_model_available, iModel)) Sleep(5); } ScriptCommand(&create_pickup_with_ammo, iModel, 4, dwAmmo, fX, fY, fZ, &hnd); @@ -1136,12 +1207,16 @@ void CGame::EnableClock(BYTE byteClock) UnFuck(0x859A6C,10); if (byteClock) { - ToggleThePassingOfTime(1); + UnFuck(0x52CF10,1); + *(PBYTE)0x52CF10 = 0x56; // push esi + field_69 = TRUE; memcpy((PVOID)0x859A6C, byteClockData, 10); } else { - ToggleThePassingOfTime(0); + UnFuck(0x52CF10,1); + *(PBYTE)0x52CF10 = 0xC3; // ret + field_69 = FALSE; memset((PVOID)0x859A6C,0,10); } } diff --git a/saco/game/game.h b/saco/game/game.h index d8f85cc..9fd9a59 100644 --- a/saco/game/game.h +++ b/saco/game/game.h @@ -17,7 +17,7 @@ class CGame // size: 322 { -private: +public: CAudio *m_pGameAudio; CCamera *m_pGameCamera; @@ -35,7 +35,7 @@ private: BOOL m_bCheckpointsEnabled; DWORD m_dwCheckpointMarker; int field_55; - int field_59; + int m_bHeadMove; DWORD field_5D; int field_61; int field_65; @@ -56,22 +56,23 @@ public: void InitGame(); BOOL IsGameLoaded(); + void RequestModel(int iModelID, int iLoadingStream = 2); void LoadRequestedModels(); BOOL IsModelLoaded(int iModelID); BOOL SetModelDeletable(int iModelID); - void SetWorldTime(int iHour, int iMinute); + void SetWorldTime(int iHour, int iMinute); // .text:100A0AB0 void GetWorldTime(BYTE* byteHour, BYTE* byteMinute); void ToggleThePassingOfTime(BYTE byteOnOff); - void SetWorldWeather(int iWeatherID); + void SetWorldWeather(int iWeatherID); // .text:100A0B40 int GetWorldWeather(); void DisplayHud(BOOL bDisp); BYTE IsHudEnabled(); void SetFrameLimiterOn(BOOL bLimiter); BOOL IsFrameLimiterEnabled(); void EnableFrameLimiter(); - void SetFrameLimit(DWORD dwLimit); + void SetFrameLimit(DWORD dwLimit); // .text:100A0BB0 void SetMaxStats(); void DisableTrainTraffic(); void RefreshStreamingAt(float x, float y); @@ -79,11 +80,12 @@ public: int IsAnimationLoaded(char *szAnimFile); void ReleaseAnimation(char *szAnimFile); void ToggleRadar(int iToggle); - void DisplayGameText(char *szStr,int iTime,int iSize); + void DisplayGameText(char *szStr,int iTime,int iSize); // .text:100A0CE0 + DWORD FUNC_100A0D90(int a1, float fX, float fY, float fZ, int a5, int a6); // .text:100A0D90 void SetGravity(float fGravity); void EnableClock(BYTE byteClock); void EnableZoneNames(BYTE byteEnable); - void SetWantedLevel(BYTE byteLevel); + void SetWantedLevel(BYTE byteLevel); // .text:100A1420 void SetGameTextCount(WORD wCount); void DrawGangZone(float* fPos, DWORD dwColor); void EnableStuntBonus(bool bEnable); @@ -140,14 +142,23 @@ public: void sub_100A0010(); void sub_100A0090(int a1, int a2); BYTE sub_100A00C0(); - void sub_100A0110(); - void sub_100A0210(); - void sub_100A0250(); - void sub_100A02E0(); - void sub_100A0310(); + CPlayerPed *sub_100A0110(int a1, float fX, float fY, float fZ, float fRotation, int a6, int a7); // .text:100A0110 + int DeletePlayerPed(int pPlayerPed); // .text:100A0210 + CVehicle *sub_100A0250(int a2, float a3, float a4, float a5, float a6, int a7); void sub_100A0330(); + BOOL sub_100A0920(); + BOOL sub_100A0930(); + void sub_100A0940(int a1, int a2); + void sub_100A0960(); + BOOL sub_100A0970(int iModel); // .text:100A0970 + void sub_100A09A0(int iModel, int a2); // .text:100A09A0 + BOOL sub_100A1BC0(); void sub_100A1C10(); + void sub_100A1DB0(int a1); + + BYTE FUNC_100A0EE0(); // .text:100A0EE0 + int FUNC_100A0F90(); // .text:100A0F90 void LoadScene(char* szScene); int GetMemoryUsedForStreaming(); @@ -161,6 +172,14 @@ public: void LoadCullZone(char *szLine); BOOL IsUsingController(); void DisableWeaponLockOnTarget(); + void PlayCrimeReport(int a1, VECTOR *pPos, int a3, int a4, int a5); // .text:100A1790 }; //----------------------------------------------------------- + +struct struc_vdelete { virtual int Delete(int a1); }; + +void sub_100A4170(const char *a1); // .text:100A4170 +void __stdcall sub_100A02E0(const char *a1); // .text:100A02E0 +int __stdcall sub_100A0310(void *a1); // .text:100A0310 +int FUNC_1009D3D0(int iModel, float fX, float fY, float fZ, float fRadius); // .text:1009D3D0 diff --git a/saco/game/hooks.cpp b/saco/game/hooks.cpp index 2314dc7..d184272 100644 --- a/saco/game/hooks.cpp +++ b/saco/game/hooks.cpp @@ -9,9 +9,12 @@ extern int iGtaVersion; extern CNetGame* pNetGame; extern CGame* pGame; extern CChatWindow *pChatWindow; +extern CObjectPool *unnamed_1014FFAC; // 0x1014FFAC, lives in another translation unit extern DWORD dwGraphicsLoop; // Used for the external dll game loop. +int WINAPI exc_filter(unsigned int code, struct _EXCEPTION_POINTERS *ep, char *what); + #define NUDE void _declspec(naked) //----------------------------------------------------------- @@ -31,6 +34,7 @@ BYTE byteSavedCameraMode; DWORD dwCurPlayerActor=0; BYTE *pbyteCameraMode = (BYTE *)0xB6F1A8; BYTE *pbyteCurrentPlayer = (BYTE *)0xB7CD74; +WORD *wCameraMode2 = (WORD *)0xB6F858; PED_TYPE pedCrimeReportTemp; // pay attention! used in 0x100A1790 ; void __thiscall CGame::PlayCrimeReport as pedCrimeReportTemp 0x10150D00 @@ -45,7 +49,29 @@ DWORD dwParam1; DWORD dwParam2; DWORD dwParamThis; +DWORD unnamed_10113AD8 = 1; +DWORD unnamed_10113ADC = 0x7FB270; // RwRasterCreate +WORD unnamed_10113B2C; +BYTE unnamed_10113AE8[3] = {0xFF,0x52,0x20}; // original `call dword ptr [edx+20h]` at 0x4096AA + +PCHAR unnamed_10150734; +DWORD unnamed_10150960; +PCHAR unnamed_10151628; // gxt key, also reused as the ped a use-gun task runs on +DWORD unnamed_101516C8; +DWORD unnamed_101516D0; DWORD unnamed_101516D4; +DWORD unnamed_101516D8; // vehicle being rendered +DWORD unnamed_10151710; +DWORD unnamed_1015172C; +DWORD unnamed_10151804; +bool unnamed_10151808; +DWORD unnamed_1015180C; +DWORD unnamed_10151810; +DWORD unnamed_10151814; +DWORD unnamed_10151818; +DWORD unnamed_10151824; + +CObjectPool *unnamed_1015177C; WORD wLastRendObj=0; @@ -125,7 +151,7 @@ void ProcessHudScaleFix() } } -void FUNC_100A24C0() +inline void FUNC_100A24C0() { if(bSomeHudScaleFixFlag == true) { @@ -218,79 +244,792 @@ NUDE CCamera__Process_Hook() //----------------------------------------------------------- -NUDE CGame__Process_Hook() +void CGame__Process_Hook() { - // TODO: CGame__Process_Hook + if(pGame) pGame->sub_100A1C10(); + + // every pool update gets its own seh block, tagged so exc_filter can name it + if(pNetGame && pNetGame->GetObjectPool()) + { + __try + { + pNetGame->GetObjectPool()->FUNC_10012A50(); + } + __except(exc_filter(GetExceptionCode(), GetExceptionInformation(), "s003")) { } + } + + if(unnamed_1014FFAC) + { + __try + { + unnamed_1014FFAC->FUNC_10012A50(); + } + __except(exc_filter(GetExceptionCode(), GetExceptionInformation(), "s003d")) { } + } + + if(pNetGame && pNetGame->GetVehiclePool()) pNetGame->GetVehiclePool()->FUNC_1001ED70(); + if(pNetGame && pNetGame->GetObjectPool()) pNetGame->GetObjectPool()->FUNC_10012B10(); + if(pChatWindow) pChatWindow->FUNC_10067ED0(); + + __try + { + _asm mov edx, 0x53BEE0 + _asm call edx + } + __except(exc_filter(GetExceptionCode(), GetExceptionInformation(), "s007")) { } + + if(pNetGame && pNetGame->GetTextDrawPool()) pNetGame->GetTextDrawPool()->FUNC_1001E870(); } //----------------------------------------------------------- +void FUNC_100C3D30(BOOL bFogEnable); // todo: implement `FUNC_100C3D30`, lives in another file +extern DWORD GLOBAL_1026EBDC; // todo: define, lives in another file + NUDE CPed_Render_Hook() { - // TODO: CPed_Render_Hook + __asm pushad + + FUNC_100C3D30(FALSE); + + __asm + { + popad + + // call original CPed::Render + mov edx, 0x5E7680 + call edx + + pushad + } + + if (GLOBAL_1026EBDC) FUNC_100C3D30(TRUE); + else FUNC_100C3D30(FALSE); + + __asm + { + popad + retn + } } //----------------------------------------------------------- +// saved return address, shared by the task and anim hooks below +DWORD dwHookRetnAddr; + +// animation the local player got told to play, picked up for sync +DWORD dwAnimClump; +DWORD dwAnimGroup; +DWORD dwAnimID; +DWORD dwAnimBlend; +DWORD dwLocalPlayerClump; + +DWORD dwSyncAnimType; +DWORD dwSyncAnimGroup; +DWORD dwSyncAnimID; +DWORD dwSyncAnimBlend; + +DWORD dwAddAnimationRetAddr = 0x4D3AAA; +DWORD dwBlendAnimationRetAddr = 0x4D4617; + NUDE CAnimManager__AddAnimation_Hook() { - // TODO: CAnimManager__AddAnimation_Hook + _asm mov edx, [esp] + _asm mov dwHookRetnAddr, edx + _asm mov edx, [esp+4] + _asm mov dwAnimClump, edx + _asm mov edx, [esp+8] + _asm mov dwAnimGroup, edx + _asm mov edx, [esp+12] + _asm mov dwAnimID, edx + _asm pushad + + _pPlayer = GamePool_FindPlayerPed(); + if(_pPlayer) dwLocalPlayerClump = (DWORD)_pPlayer->entity.pdwRenderWare; + else dwLocalPlayerClump = 0; + + if(dwAnimClump == dwLocalPlayerClump && (dwAnimGroup || dwAnimID != 0xA0)) + { + dwSyncAnimType = 1; + dwSyncAnimGroup = dwAnimGroup; + dwSyncAnimID = dwAnimID; + } + + _asm popad + _asm mov eax, [esp+12] + _asm mov edx, ds:0xB4EA34 + _asm jmp dwAddAnimationRetAddr } //----------------------------------------------------------- NUDE CAnimManager__BlendAnimation_Hook() { - // TODO: CAnimManager__BlendAnimation_Hook + _asm mov edx, [esp] + _asm mov dwHookRetnAddr, edx + _asm mov edx, [esp+4] + _asm mov dwAnimClump, edx + _asm mov edx, [esp+8] + _asm mov dwAnimGroup, edx + _asm mov edx, [esp+12] + _asm mov dwAnimID, edx + _asm mov edx, [esp+16] + _asm mov dwAnimBlend, edx + _asm pushad + + _pPlayer = GamePool_FindPlayerPed(); + if(_pPlayer) dwLocalPlayerClump = (DWORD)_pPlayer->entity.pdwRenderWare; + else dwLocalPlayerClump = 0; + + if(dwAnimClump == dwLocalPlayerClump && (dwAnimGroup || dwAnimID != 0xA0)) + { + dwSyncAnimType = 2; + dwSyncAnimGroup = dwAnimGroup; + dwSyncAnimID = dwAnimID; + dwSyncAnimBlend = dwAnimBlend; + } + + _asm popad + _asm sub esp, 0x14 + _asm mov ecx, [esp+0x18] + _asm jmp dwBlendAnimationRetAddr } //----------------------------------------------------------- +DWORD dwProcessControlPed; +PED_TYPE *_pProcessControlPed; +BYTE byteSavedCurrentPlayer; +BYTE byteProcessControlPlayerID; +WORD wSavedCameraMode2; +DWORD dwSavedPedHealth; +GTA_CONTROLSET *pProcessControlKeys; + +// `fst [esi+0x55C]` heading writes at 0x6884C4 and 0x688200, only enabled for the local player +BYTE PedHeadingCode1[] = {0xD9,0x96,0x5C,0x05,0x00,0x00}; +BYTE PedHeadingCode2[] = {0xD9,0x96,0x5C,0x05,0x00,0x00}; +// `call 0x5E1B10` at 0x5E92F4, nopped out while a remote ped processes +BYTE RemotePedProcessCode[] = {0xE8,0x17,0x88,0xFF,0xFF}; + +CPlayerPool *pRemotePedPlayerPool; +PLAYERID remotePedPlayerId; +CRemotePlayer *pRemotePedPlayer; + +// runs the original CPlayerPed::ProcessControl for a remote ped under SEH; a fault +// there kicks the player rather than taking the whole client down +void ProcessControlRemotePed() +{ + __try + { + _asm mov ecx, dwProcessControlPed + _asm mov edx, 0x60EA90 + _asm call edx + } + __except(exc_filter(GetExceptionCode(), GetExceptionInformation(), "playerped")) + { + if(*pbyteCurrentPlayer) + { + if(pNetGame) + { + pRemotePedPlayerPool = pNetGame->GetPlayerPool(); + + if(pRemotePedPlayerPool) + { + remotePedPlayerId = pRemotePedPlayerPool->FUNC_100138C0(dwProcessControlPed); + + if(remotePedPlayerId != 0xFFFF) + { + pRemotePedPlayer = pRemotePedPlayerPool->GetAt(remotePedPlayerId); + + if(pRemotePedPlayer) pRemotePedPlayer->FUNC_10017530(); + + if(pChatWindow) + pChatWindow->AddDebugMessage("Removed player %u due to error.", remotePedPlayerId); + } + } + } + } + } +} + +// .text:100A28C0 : nop the two ped heading writes back out +void FUNC_100A28C0() +{ + memset((void *)0x6884C4, 0x90, 6); + memset((void *)0x688200, 0x90, 6); +} + +// .text:100A28E0 : put the two ped heading writes back +void FUNC_100A28E0() +{ + memcpy((void *)0x6884C4, PedHeadingCode1, 6); + memcpy((void *)0x688200, PedHeadingCode2, 6); +} + +// .text:100A2920 : nop the remote ped process call out +void FUNC_100A2920() +{ + UnFuck(0x5E92F4, 5); + memset((void *)0x5E92F4, 0x90, 5); +} + +// .text:100A2940 : put the remote ped process call back +void FUNC_100A2940() +{ + memcpy((void *)0x5E92F4, RemotePedProcessCode, 5); +} + NUDE CPlayerPed_ProcessControl_Hook() { - // TODO: CPlayerPed_ProcessControl_Hook + _asm mov dwProcessControlPed, ecx + _asm pushad + + _pProcessControlPed = (PED_TYPE *)dwProcessControlPed; + byteSavedCurrentPlayer = *pbyteCurrentPlayer; + byteProcessControlPlayerID = FindPlayerNumFromPedPtr(dwProcessControlPed); + + if(dwProcessControlPed && byteProcessControlPlayerID && !byteSavedCurrentPlayer) + { + GameStoreLocalPlayerKeys(); + GameSetRemotePlayerKeys(byteProcessControlPlayerID); + + byteSavedCameraMode = *pbyteCameraMode; + *pbyteCameraMode = GameGetPlayerCameraMode(byteProcessControlPlayerID); + wSavedCameraMode2 = *wCameraMode2; + *wCameraMode2 = GameGetPlayerCameraMode(byteProcessControlPlayerID); + if(*wCameraMode2 == 4) *wCameraMode2 = 0; + + GameStoreLocalPlayerCameraExtZoom(); + GameSetRemotePlayerCameraExtZoom(byteProcessControlPlayerID); + GameStoreLocalPlayerAim(); + GameSetRemotePlayerAim(byteProcessControlPlayerID); + GameStoreLocalPlayerWeaponSkills(); + GameSetRemotePlayerWeaponSkills(byteProcessControlPlayerID); + + *pbyteCurrentPlayer = byteProcessControlPlayerID; + dwSavedPedHealth = *(DWORD *)&_pProcessControlPed->fHealth; + + UnFuck(0x5E92F4, 5); + memset((void *)0x5E92F4, 0x90, 5); + ProcessControlRemotePed(); + memcpy((void *)0x5E92F4, RemotePedProcessCode, 5); + + GameSetLocalPlayerWeaponSkills(); + *pbyteCameraMode = byteSavedCameraMode; + *wCameraMode2 = wSavedCameraMode2; + GameSetLocalPlayerCameraExtZoom(); + *pbyteCurrentPlayer = 0; + GameSetLocalPlayerKeys(); + GameSetLocalPlayerAim(); + } + else + { + if(pNetGame && pNetGame->GetPlayerPool()->GetLocalPlayer()->GetField_F0()) + { + struc_41 *pSettings = pNetGame->GetSettings(); + + if(pGame->FindPlayerPed()->HasExceededWorldBoundries(pSettings->fWorldBoundryPX, + pSettings->fWorldBoundryZX, pSettings->fWorldBoundryPY, pSettings->fWorldBoundryNY)) + { + if(!pGame->GetActiveInterior()) + { + DWORD dwStateFlags = _pProcessControlPed->dwStateFlags; + + if(!(dwStateFlags & 0x100) && (dwStateFlags & 3)) + { + pProcessControlKeys = GameGetInternalKeys(); + pProcessControlKeys->wKeys1[14] = 255; + pProcessControlKeys->wKeys2[14] = 0; + } + } + } + } + + if(pGame->FindPlayerPed()) + { + if(pGame->FindPlayerPed()->field_2F7) + { + pProcessControlKeys = GameGetInternalKeys(); + pProcessControlKeys->wKeys1[18] = 0; + pProcessControlKeys->wKeys2[18] = 0; + } + + if(pGame->FindPlayerPed()->field_2F6) + { + pProcessControlKeys = GameGetInternalKeys(); + pProcessControlKeys->wKeys1[14] = 0; + pProcessControlKeys->wKeys2[14] = 0; + } + + if(pGame->FindPlayerPed()->field_2F8) + { + pProcessControlKeys = GameGetInternalKeys(); + pProcessControlKeys->wKeys1[16] = 0; + pProcessControlKeys->wKeys2[16] = 0; + } + } + + memcpy((void *)0x6884C4, PedHeadingCode1, 6); + memcpy((void *)0x688200, PedHeadingCode2, 6); + + _asm mov ecx, dwProcessControlPed + _asm mov edx, 0x60EA90 + _asm call edx + + memset((void *)0x6884C4, 0x90, 6); + memset((void *)0x688200, 0x90, 6); + } + + _asm popad + _asm retn } //----------------------------------------------------------- +MATRIX4X4 unnamed_10150A48; // ped matrix saved across ProcessControl +float unnamed_10150C1C; +float unnamed_10150C20; +float unnamed_10150C24; + NUDE CCivillianPed__ProcessControl_Hook() { - // TODO: CCivillianPed__ProcessControl_Hook + _asm mov dwProcessControlPed, ecx + _asm pushad + + _pProcessControlPed = (PED_TYPE *)dwProcessControlPed; + + // hold attached/frozen peds still while the game processes them + if((_pProcessControlPed->entity.dwPhysFlags & 0x40000) && + (_pProcessControlPed->entity.dwPhysFlags & 0x800000) && + (_pProcessControlPed->entity.dwPhysFlags & 0x200000)) + { + _pProcessControlPed->entity.vecMoveSpeed.X = 0.0f; + _pProcessControlPed->entity.vecMoveSpeed.Y = 0.0f; + } + + if(_pProcessControlPed->entity.mat) + { + unnamed_10150A48 = *_pProcessControlPed->entity.mat; + unnamed_10150C1C = _pProcessControlPed->entity.vecMoveSpeed.X; + unnamed_10150C20 = _pProcessControlPed->entity.vecMoveSpeed.Y; + unnamed_10150C24 = _pProcessControlPed->entity.vecMoveSpeed.Z; + } + + _asm mov ecx, dwProcessControlPed + _asm mov edx, 0x5DDBF0 ;// CCivilianPed::ProcessControl + _asm call edx + + if((_pProcessControlPed->entity.dwPhysFlags & 0x40000) && + (_pProcessControlPed->entity.dwPhysFlags & 0x800000) && + (_pProcessControlPed->entity.dwPhysFlags & 0x200000)) + { + if(_pProcessControlPed->entity.mat) + { + _pProcessControlPed->entity.mat->pos.X = unnamed_10150A48.pos.X; + _pProcessControlPed->entity.mat->pos.Y = unnamed_10150A48.pos.Y; + _pProcessControlPed->entity.mat->pos.Z = unnamed_10150A48.pos.Z; + } + + _pProcessControlPed->entity.vecMoveSpeed.X = 0.0f; + _pProcessControlPed->entity.vecMoveSpeed.Y = 0.0f; + } + + _asm popad + _asm retn } //----------------------------------------------------------- +WORD unnamed_10150730; +BYTE unnamed_10150BC0; +DWORD unnamed_1015168C; + NUDE TaskUseGun_Hook() { - // TODO: TaskUseGun_Hook + _asm mov unnamed_10151628, ecx + _asm mov eax, [esp+4] + _asm mov dwProcessControlPed, eax + + byteSavedCurrentPlayer = *pbyteCurrentPlayer; + byteProcessControlPlayerID = FindPlayerNumFromPedPtr(dwProcessControlPed); + + if(dwProcessControlPed && byteProcessControlPlayerID && !byteSavedCurrentPlayer) + { + // run the task with the remote player's keys, camera and skills in place + GameStoreLocalPlayerKeys(); + GameSetRemotePlayerKeys(byteProcessControlPlayerID); + + unnamed_10150BC0 = *pbyteCameraMode; + *pbyteCameraMode = GameGetPlayerCameraMode(byteProcessControlPlayerID); + + unnamed_10150730 = *wCameraMode2; + *wCameraMode2 = GameGetPlayerCameraMode(byteProcessControlPlayerID); + if(*wCameraMode2 == 4) *wCameraMode2 = 0; + + GameStoreLocalPlayerCameraExtZoom(); + GameSetRemotePlayerCameraExtZoom(byteProcessControlPlayerID); + + GameStoreLocalPlayerAim(); + GameSetRemotePlayerAim(byteProcessControlPlayerID); + + GameStoreLocalPlayerWeaponSkills(); + GameSetRemotePlayerWeaponSkills(byteProcessControlPlayerID); + + *pbyteCurrentPlayer = byteProcessControlPlayerID; + + _asm mov ecx, unnamed_10151628 + _asm push dwProcessControlPed + _asm mov eax, 0x624ED0 ;// CTaskSimpleUseGun::ProcessPed + _asm call eax + + *pbyteCameraMode = unnamed_10150BC0; + *wCameraMode2 = unnamed_10150730; + + GameSetLocalPlayerWeaponSkills(); + GameSetLocalPlayerCameraExtZoom(); + *pbyteCurrentPlayer = 0; + GameSetLocalPlayerAim(); + GameSetLocalPlayerKeys(); + } + else + { + unnamed_1015168C = 1; + + _asm mov ecx, unnamed_10151628 + _asm push dwProcessControlPed + _asm mov eax, 0x624ED0 + _asm call eax + + unnamed_1015168C = 0; + } + + _asm retn 4 } //----------------------------------------------------------- +DWORD unnamed_10151684; +DWORD unnamed_10151688; + NUDE WeaponRender__GetWeaponSkill_Hook() { - // TODO: WeaponRender__GetWeaponSkill_Hook + _asm mov dwProcessControlPed, ecx + + _pProcessControlPed = (PED_TYPE *)dwProcessControlPed; + byteSavedCurrentPlayer = *pbyteCurrentPlayer; + byteProcessControlPlayerID = FindPlayerNumFromPedPtr(dwProcessControlPed); + + if(dwProcessControlPed && byteProcessControlPlayerID && !byteSavedCurrentPlayer) + { + // render a remote player with his own skill level + GameStoreLocalPlayerWeaponSkills(); + GameSetRemotePlayerWeaponSkills(byteProcessControlPlayerID); + unnamed_10151688 = 1; + } + + _asm mov ecx, dwProcessControlPed + _asm movsx eax, byte ptr [ecx+0x718] + _asm imul eax, 0x1C + _asm mov eax, [eax+ecx+0x5A0] + _asm push eax + _asm mov edx, 0x5E3B60 ;// CPed::GetWeaponSkill + _asm call edx + _asm mov unnamed_10151684, eax + + if(unnamed_10151688) + { + GameSetLocalPlayerWeaponSkills(); + unnamed_10151688 = 0; + } + + _asm mov eax, unnamed_10151684 + _asm retn } //----------------------------------------------------------- +DWORD dwAttachedEntitiesPlayer; +CRemotePlayer *pAttachedEntitiesRemotePlayer; + +void FUNC_10014650(); // .text:10014650 + NUDE CWorld__ProcessAttachedEntities_Hook() { - // TODO: CWorld__ProcessAttachedEntities_Hook + __asm + { + // call original CWorld::ProcessAttachedEntities + mov edx, 0x5647F0 + call edx + + pushad + } + + if(pNetGame) + { + if(pNetGame->GetPlayerPool()) + pNetGame->GetPlayerPool()->m_pLocalPlayer->FUNC_10003570(); + + if(pNetGame && pNetGame->GetPlayerPool()) + { + for(dwAttachedEntitiesPlayer = 0; dwAttachedEntitiesPlayer != MAX_PLAYERS; dwAttachedEntitiesPlayer++) + { + // GetAt's bound check and body, written out; the PLAYERID cast is what truncates to a word + PLAYERID playerId = (PLAYERID)dwAttachedEntitiesPlayer; + if(playerId < MAX_PLAYERS) + { + if(pNetGame->GetPlayerPool()->field_2A[playerId]) + { + CNetPlayer *pNetPlayer = pNetGame->GetPlayerPool()->m_pPlayers[playerId]; + CRemotePlayer *pRemotePlayer; + + if(pNetPlayer) pRemotePlayer = pNetPlayer->m_pRemotePlayer; + else pRemotePlayer = NULL; + + pAttachedEntitiesRemotePlayer = pRemotePlayer; + + if(pRemotePlayer && pRemotePlayer->m_pPlayerPed && pRemotePlayer->field_10A) + { + FUNC_10014650(); + } + } + } + } + } + } + + __asm + { + popad + retn + } } //----------------------------------------------------------- NUDE CWorld__ProcessPedsAfterPreRender_Hook() { - // TODO: CWorld__ProcessPedsAfterPreRender_Hook -} + __asm + { + // call original CWorld::ProcessPedsAfterPreRender + mov edx, 0x563430 + call edx + + pushad + } + + if (pNetGame && pNetGame->GetPlayerPool()) + pNetGame->GetPlayerPool()->FUNC_10013960(); + + __asm + { + popad + retn + }} //----------------------------------------------------------- +DWORD unnamed_10150978; // original ProcessControl of the vehicle class +float unnamed_10150A34; +VEHICLE_TYPE *unnamed_10150A38; +BYTE unnamed_10150AA0; +DWORD unnamed_10150BBC; // vtable of the vehicle being processed +DWORD unnamed_10150C88; +DWORD unnamed_10151690; + NUDE AllVehicles_ProcessControl_Hook() { - // TODO: AllVehicles_ProcessControl_Hook + _asm mov unnamed_10150A38, ecx + _asm mov eax, [ecx] + _asm mov unnamed_10150BBC, eax + _asm pushad + + if(unnamed_10150BBC == 0x871120) unnamed_10150978 = 0x6B1880; // CAutomobile + else if(unnamed_10150BBC == 0x8721A0) unnamed_10150978 = 0x6F1770; // CBoat + else if(unnamed_10150BBC == 0x871360) unnamed_10150978 = 0x6B9250; // CBike + else if(unnamed_10150BBC == 0x871948) unnamed_10150978 = 0x6C9260; // CPlane + else if(unnamed_10150BBC == 0x871680) unnamed_10150978 = 0x6C7050; // CHeli + else if(unnamed_10150BBC == 0x871528) unnamed_10150978 = 0x6BFA30; // CBmx + else if(unnamed_10150BBC == 0x8717D8) unnamed_10150978 = 0x6C8250; // CMonsterTruck + else if(unnamed_10150BBC == 0x871AE8) unnamed_10150978 = 0x6CDCC0; // CQuadBike + else if(unnamed_10150BBC == 0x872370) unnamed_10150978 = 0x6F86A0; // CTrain + else if(unnamed_10150BBC == 0x871C28) unnamed_10150978 = 0x6CED20; // CTrailer + + byteSavedCurrentPlayer = *(BYTE *)0xB7CD74; + + PED_TYPE *pDriver; + pDriver = unnamed_10150A38->pDriver; + + // a remote player drives it, run the class ProcessControl with his keys and aim + if(unnamed_10150A38->pDriver && pDriver->dwPedType == 0 && + pDriver != GamePool_FindPlayerPed() && byteSavedCurrentPlayer == 0) + { + byteProcessControlPlayerID = FindPlayerNumFromPedPtr((DWORD)pDriver); + + GameStoreLocalPlayerKeys(); + GameSetRemotePlayerKeys(byteProcessControlPlayerID); + + unnamed_10150BC0 = *pbyteCameraMode; + *pbyteCameraMode = GameGetPlayerCameraMode(byteProcessControlPlayerID); + unnamed_10150730 = *wCameraMode2; + *wCameraMode2 = GameGetPlayerCameraMode(byteProcessControlPlayerID); + if(*wCameraMode2 == 4) *wCameraMode2 = 0; + + GameStoreLocalPlayerAim(); + GameSetRemotePlayerAim(byteProcessControlPlayerID); + + if((unnamed_10150A38->byteFlags & 0x10) == 0) + { + if(unnamed_10150BBC == 0x871680) unnamed_10150A38->entity.nControlFlags |= 0x20; + + pProcessControlKeys = GameGetInternalKeys(); + pProcessControlKeys->wKeys1[14] = 0; + pProcessControlKeys->wKeys2[14] = 0; + pProcessControlKeys->wKeys1[16] = 0; + pProcessControlKeys->wKeys2[16] = 0; + } else { + unnamed_10150A38->entity.nControlFlags &= 0xDF; + } + + unnamed_10150C88 = *(DWORD *)((BYTE *)unnamed_10150A38 + 0x648); + *(DWORD *)&unnamed_10150A34 = *(DWORD *)((BYTE *)unnamed_10150A38 + 0x64C); + + // let the game process it as a civillian driven vehicle + *pbyteCurrentPlayer = 0; + unnamed_10150A38->pDriver->dwPedType = 4; + unnamed_10150AA0 = unnamed_10150A38->entity.nControlFlags; + unnamed_10150A38->entity.nControlFlags = 0x1A; + + _asm mov edx, unnamed_10150A38 + _asm lea ecx, [edx+0x138] + _asm mov edx, 0x502280 + _asm call edx + + unnamed_10150A38->entity.nControlFlags = unnamed_10150AA0; + unnamed_10150A38->pDriver->dwPedType = 0; + *pbyteCurrentPlayer = byteProcessControlPlayerID; + + MATRIX4X4 *pMatrix = unnamed_10150A38->entity.mat; + if(pMatrix) + { + unnamed_10150A48 = *pMatrix; + *(DWORD *)&unnamed_10150C1C = *(DWORD *)&unnamed_10150A38->entity.vecMoveSpeed.X; + *(DWORD *)&unnamed_10150C20 = *(DWORD *)&unnamed_10150A38->entity.vecMoveSpeed.Y; + *(DWORD *)&unnamed_10150C24 = *(DWORD *)&unnamed_10150A38->entity.vecMoveSpeed.Z; + } + + _asm mov ecx, unnamed_10150A38 + _asm mov eax, unnamed_10150978 + _asm call eax + + if(unnamed_10150A38->entity.mat && !FUNC_100B6950(unnamed_10150A38) && + !IsATrainPart(&unnamed_10150A38->entity)) + { + // keep the height and vertical speed the server gave us + *(DWORD *)&unnamed_10150A38->entity.mat->pos.Z = *(DWORD *)&unnamed_10150A48.pos.Z; + unnamed_10150A38->entity.vecMoveSpeed.Z = unnamed_10150C24; + } + else if(unnamed_10150A38->entity.mat && (unnamed_10150978 == 0x6F1770 || + unnamed_10150978 == 0x6C9260 || unnamed_10150978 == 0x6C7050)) + { + *(DWORD *)&unnamed_10150A38->entity.mat->pos.Z = *(DWORD *)&unnamed_10150A48.pos.Z; + unnamed_10150A38->entity.vecMoveSpeed.Z = unnamed_10150C24; + } + + if(unnamed_10150978 == 0x6BFA30 || unnamed_10150978 == 0x6B9250) + { + // bikes, ease the lean angle half way back to the synced one + *(DWORD *)((BYTE *)unnamed_10150A38 + 0x648) = unnamed_10150C88; + *(float *)((BYTE *)unnamed_10150A38 + 0x64C) = unnamed_10150A34; + + if(*(float *)((BYTE *)unnamed_10150A38 + 0x64C) > *(float *)((BYTE *)unnamed_10150A38 + 0x648)) + { + *(float *)((BYTE *)unnamed_10150A38 + 0x648) = (*(float *)((BYTE *)unnamed_10150A38 + 0x64C) - + *(float *)((BYTE *)unnamed_10150A38 + 0x648)) * 0.5f + *(float *)((BYTE *)unnamed_10150A38 + 0x648); + } + else if(*(float *)((BYTE *)unnamed_10150A38 + 0x64C) < *(float *)((BYTE *)unnamed_10150A38 + 0x648)) + { + *(float *)((BYTE *)unnamed_10150A38 + 0x648) = *(float *)((BYTE *)unnamed_10150A38 + 0x648) - + (*(float *)((BYTE *)unnamed_10150A38 + 0x648) - *(float *)((BYTE *)unnamed_10150A38 + 0x64C)) * 0.5f; + } + } + + *pbyteCurrentPlayer = 0; + GameSetLocalPlayerKeys(); + *pbyteCameraMode = unnamed_10150BC0; + *wCameraMode2 = unnamed_10150730; + GameSetLocalPlayerAim(); + + _asm popad + _asm retn + } + else if(pDriver && pDriver->dwPedType == 0 && + unnamed_10150A38->pDriver == GamePool_FindPlayerPed()) + { + if((unnamed_10150A38->byteFlags & 0x10) == 0) + { + if(unnamed_10150BBC == 0x871680) unnamed_10150A38->entity.nControlFlags |= 0x20; + + unnamed_10151690 = 1; + GameStoreLocalPlayerKeys(); + + pProcessControlKeys = GameGetInternalKeys(); + pProcessControlKeys->wKeys1[14] = 0; + pProcessControlKeys->wKeys2[14] = 0; + pProcessControlKeys->wKeys1[16] = 0; + pProcessControlKeys->wKeys2[16] = 0; + } else { + unnamed_10150A38->entity.nControlFlags &= 0xDF; + } + + _asm mov edx, unnamed_10150A38 + _asm lea ecx, [edx+0x138] + _asm mov edx, 0x502280 + _asm call edx + + _asm mov ecx, unnamed_10150A38 + _asm mov eax, unnamed_10150978 + _asm call eax + + if(unnamed_10151690) + { + unnamed_10151690 = 0; + GameSetLocalPlayerKeys(); + } + + _asm popad + _asm retn + } + else + { + if(FUNC_100B4860(unnamed_10150A38) || IsATrainPart(&unnamed_10150A38->entity) || + FUNC_100B6950(unnamed_10150A38)) + { + MATRIX4X4 *pMatrix = unnamed_10150A38->entity.mat; + if(pMatrix) + { + unnamed_10150A48 = *pMatrix; + *(DWORD *)&unnamed_10150C1C = *(DWORD *)&unnamed_10150A38->entity.vecMoveSpeed.X; + *(DWORD *)&unnamed_10150C20 = *(DWORD *)&unnamed_10150A38->entity.vecMoveSpeed.Y; + *(DWORD *)&unnamed_10150C24 = *(DWORD *)&unnamed_10150A38->entity.vecMoveSpeed.Z; + } + + _asm mov edx, unnamed_10150A38 + _asm lea ecx, [edx+0x138] + _asm mov edx, 0x502280 + _asm call edx + + _asm mov ecx, unnamed_10150A38 + _asm mov eax, unnamed_10150978 + _asm call eax + } + + _asm popad + _asm retn + } + + _asm popad + _asm retn } //----------------------------------------------------------- @@ -405,7 +1144,17 @@ NUDE PlayerWalk_Hook() NUDE PickUpPickup_Hook() { - // TODO: PickUpPickup_Hook + _asm mov unnamed_10150960, esi + _asm pushad + + if (pNetGame && pNetGame->GetPickupPool()) + pNetGame->GetPickupPool()->sub_10013440((unnamed_10150960 - 0x9788C0) >> 5); + + _asm popad + _asm mov al, [esi+0x1C] + _asm cmp al, 6 + _asm push 0x4579CB + _asm ret } //----------------------------------------------------------- @@ -448,16 +1197,114 @@ NUDE CTrain_ProcessControl_Derailment() //----------------------------------------------------------- +int FUNC_100A36C0(); // todo: implement `FUNC_100A36C0` + +DWORD dwEnterExitVehicle; + NUDE TaskEnterVehicleDriver_Hook() { - // TODO: TaskEnterVehicleDriver_Hook + _asm mov eax, [esp] + _asm mov dwHookRetnAddr, eax + _asm mov eax, [esp+4] + _asm mov dwEnterExitVehicle, eax + _asm pushad + + if((dwHookRetnAddr == 0x570A20 || dwHookRetnAddr == 0x570A99) && !FUNC_100A36C0()) + { + _asm + { + popad + + // kill the task, we're not letting them in + mov ebx, [ecx] + test ebx, ebx + jz noTaskDelete + push 1 + call dword ptr [ebx] +noTaskDelete: + pop eax + pop eax + mov eax, 0x570A9E + jmp eax + } + } + + _asm popad + _asm mov eax, [esp+4] + _asm push esi + _asm push 0 + _asm mov ebx, 0x6402F7 + _asm jmp ebx } //----------------------------------------------------------- +DWORD dwExitTaskThis; +DWORD dwPutOutOfVehicle = 0xFFFFFFFF; +CVehiclePool *pExitVehiclePool; +WORD wExitVehicleID; +CVehicle *pExitVehicle; +CLocalPlayer *pExitLocalPlayer; +MATRIX4X4 matExitVehicle; + NUDE TaskExitVehicle() { - // TODO: TaskExitVehicle + _asm mov ebp, esp + _asm mov dwExitTaskThis, ecx + _asm mov eax, [ebp] + _asm mov dwHookRetnAddr, eax + _asm mov eax, [ebp+4] + _asm mov dwEnterExitVehicle, eax + + dwPutOutOfVehicle = 0xFFFFFFFF; + + if(!bIgnoreNextExit && + (dwHookRetnAddr == 0x5704A1 || dwHookRetnAddr == 0x5703FC)) + { + if(pNetGame) + { + if(GamePool_FindPlayerPed()->pVehicle == dwEnterExitVehicle) + { + pExitVehiclePool = pNetGame->GetVehiclePool(); + wExitVehicleID = pExitVehiclePool->FUNC_1001EB90((int)GamePool_FindPlayerPed()->pVehicle); + + if(wExitVehicleID != 0xFFFF) + { + pExitVehicle = (CVehicle *)pExitVehiclePool->FUNC_10001120(wExitVehicleID); + pExitLocalPlayer = pNetGame->GetPlayerPool()->GetLocalPlayer(); + + if(pExitVehicle && pExitLocalPlayer) + { + if(pExitVehicle->IsATrainPart()) + { + // exiting a train part normally doesn't work, dump them next to it + dwPutOutOfVehicle = 1; + pExitVehicle->GetMatrix(&matExitVehicle); + pExitLocalPlayer->GetPlayerPed()->RemoveFromVehicleAndPutAt( + matExitVehicle.pos.X + 2.5f, + matExitVehicle.pos.Y + 2.5f, + matExitVehicle.pos.Z); + } + else + { + pExitLocalPlayer->FUNC_10005BF0(wExitVehicleID); + } + } + } + } + } + } + else + { + bIgnoreNextExit = FALSE; + } + + _asm push dwPutOutOfVehicle + _asm push 0x841618 + _asm mov ecx, dwExitTaskThis + _asm mov edx, 0x63B8C0 + _asm add edx, 7 + _asm jmp edx } //----------------------------------------------------------- @@ -541,7 +1388,167 @@ NUDE CGameShutdownHook() //----------------------------------------------------------- -NUDE PedDamage_Hook() {} +DWORD unnamed_101516A0; // CEventDamage * +DWORD unnamed_101516A4; // CPed * taking the damage + +int __stdcall FUNC_100A3BB0(DWORD *pEventDamage, PED_TYPE *pPed); // todo: implement `FUNC_100A3BB0` + +// maps a ped pointer to a player id, falling back to the driver of the vehicle it is in +PLAYERID FUNC_100A3AE0(DWORD dwPed) +{ + if(!dwPed) return 0xFFFF; + + PLAYERID playerId; + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + + playerId = pPlayerPool->FUNC_100138C0(dwPed); + + if(playerId == 0xFFFF) + { + VEHICLEID vehicleId = pNetGame->GetVehiclePool()->FUNC_1001EB90(dwPed); + + if(vehicleId != 0xFFFF) + { + int iHighestId = pPlayerPool->field_2F3A; + + for(int i = 0; i <= iHighestId; i++) + { + PLAYERID id = (PLAYERID)i; + + if(id < MAX_PLAYERS && pPlayerPool->field_2A[id]) + { + CNetPlayer *pNetPlayer = pPlayerPool->m_pPlayers[id]; + CRemotePlayer *pRemotePlayer; + + if(pNetPlayer) pRemotePlayer = pNetPlayer->m_pRemotePlayer; + else pRemotePlayer = NULL; + + // no null check, same as retail + if(pRemotePlayer->field_1E7 == vehicleId) + { + playerId = id; + break; + } + } + } + } + } + + return playerId; +} + +//----------------------------------------------------------- + +// maps a ped pointer to the actor that owns it +ACTORID FUNC_100A3B80(DWORD dwPed) +{ + if(!dwPed) return 0xFFFF; + return pNetGame->GetActorPool()->FUNC_100018B0(dwPed); +} + +//----------------------------------------------------------- + +// 0x100A3BB0 : decides whether a ped damage event gets reported to the server +BOOL __stdcall FUNC_100A3BB0(DWORD *pEvent, PED_TYPE *pPed) +{ + PED_TYPE *pPlayerPed = GamePool_FindPlayerPed(); + bool bLocalTarget = false; + + if(!pNetGame) return FALSE; + + if((PED_TYPE *)pEvent[0] != pPlayerPed && pPed && *(DWORD *)pPed == 0x86C0A8) return TRUE; + + if(pPed != pPlayerPed) + { + if((PED_TYPE *)pEvent[0] != pPlayerPed) return FALSE; + } + else bLocalTarget = true; + + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + PLAYERID playerId; + + if(bLocalTarget) + { + DWORD dwTask = pPlayerPool->m_pLocalPlayer->field_2DA; + + if(dwTask) + { + if(*(DWORD *)(dwTask + 0x40) == pEvent[0]) return TRUE; + if(pEvent[3] == 0x36) return TRUE; + } + + playerId = FUNC_100A3AE0(pEvent[0]); + pPlayerPool->m_pLocalPlayer->FUNC_100068A0(playerId, *(float *)&pEvent[1], pEvent[3], pEvent[2]); + } + else + { + playerId = FUNC_100A3AE0((DWORD)pPed); + + if(playerId != 0xFFFF) + { + pPlayerPool->m_pLocalPlayer->FUNC_100069B0(playerId, *(float *)&pEvent[1], pEvent[3], pEvent[2]); + + if(pPlayerPool->GetAt(playerId)->field_10C) return TRUE; + } + else + { + ACTORID actorId = FUNC_100A3B80((DWORD)pPed); + + if(actorId != 0xFFFF) + { + pPlayerPool->m_pLocalPlayer->FUNC_10006AC0(actorId, *(float *)&pEvent[1], pEvent[3], pEvent[2]); + return TRUE; + } + } + } + + if(pNetGame->GetSettings()->field_22) + { + BYTE byteTeam = pPlayerPool->m_pLocalPlayer->m_byteTeam; + + if(byteTeam == 0xFF) return FALSE; + if(playerId == 0xFFFF) return FALSE; + + if((BYTE)pNetGame->GetPlayerPool()->GetAt(playerId)->field_109 == byteTeam) return TRUE; + } + + return FALSE; +} + +//----------------------------------------------------------- + +NUDE PedDamage_Hook() +{ + __asm + { + mov eax, fs:0 + test ecx, ecx + jz computeResponse + mov unnamed_101516A0, ecx + mov ebx, [esp+4] + test ebx, ebx + jz computeResponse + mov unnamed_101516A4, ebx + pushad + + mov eax, unnamed_101516A4 + push eax + mov ecx, unnamed_101516A0 + push ecx + call FUNC_100A3BB0 + test eax, eax + jz computeResponseAfterSync + + // the netcode dealt with it, don't let the game apply the damage + popad + retn 0Ch + computeResponseAfterSync: + popad + computeResponse: + mov edx, 0x4B5AC6 ;// CEventDamage::ComputeDamageResponse + 6 + jmp edx + } +} //----------------------------------------------------------- @@ -722,49 +1729,282 @@ exitFn: NUDE GetText_Hook() { - // TODO: GetText_Hook + __asm + { + // stolen prologue from CText::Get + sub esp, 0x20 + push esi + push edi + mov edi, [esp+0x2C] + pushad + mov unnamed_10151628, edi + } + + unnamed_10150734 = 0; + + // SAMP prefixed keys are server menu text, not gxt entries + if(pNetGame && unnamed_10151628[0] == 'S' && unnamed_10151628[1] == 'A' && + unnamed_10151628[2] == 'M' && unnamed_10151628[3] == 'P' && pNetGame->GetMenuPool()) + { + unnamed_10150734 = pNetGame->GetMenuPool()->GetTextPointer(unnamed_10151628 + 4); + } + + __asm + { + popad + cmp unnamed_10150734, 0 + jz callOriginal + mov eax, unnamed_10150734 + pop edi + pop esi + add esp, 0x20 + retn 4 + +callOriginal: + mov eax, 0x6A0059 + jmp eax + } +} + +//----------------------------------------------------------- + +DWORD unnamed_101516CC; + +// .text:100A4470 : caches the d3d texture the plate raster carries at +0x34 +DWORD FUNC_100A4470() +{ + if(unnamed_101516C8) + { + _asm mov eax, unnamed_101516C8 + _asm mov edx, 0x34 + _asm lea esi, [edx+eax] + _asm mov eax, [esi] + _asm mov unnamed_101516CC, eax + + return unnamed_101516CC; + } + + return 0; +} + +//----------------------------------------------------------- + +// .text:100A44A0 : drops the cached plate raster +void FUNC_100A44A0() +{ + DWORD dwRasterDestroy = 0x7FB060; + if(iGtaVersion == GTASA_VERSION_USA10) dwRasterDestroy = 0x7FB020; + + if(unnamed_101516C8) + { + _asm push unnamed_101516C8 + _asm mov edx, dwRasterDestroy + _asm call edx + _asm pop edx + } + + unnamed_101516C8 = 0; +} + +//----------------------------------------------------------- + +// .text:100A44E0 : creates the plate raster, unconditionally +void FUNC_100A44E0() +{ + if(iGtaVersion == GTASA_VERSION_USA10) unnamed_10113ADC = 0x7FB230; + + _asm push 0x604 + _asm push 0x20 + _asm push 0x10 + _asm push 0x40 + _asm mov ebx, unnamed_10113ADC + _asm call ebx + _asm mov unnamed_101516C8, eax + _asm pop ebx + _asm pop ebx + _asm pop ebx + _asm pop ebx } //----------------------------------------------------------- NUDE CCustomCarPlateMgr__CreatePlateTexture__RwRasterCreate_Hook() { - // TODO: CCustomCarPlateMgr__CreatePlateTexture__RwRasterCreate_Hook + __asm + { + // only ever create the plate raster once + mov eax, unnamed_101516C8 + test eax, eax + jnz exitFn + + cmp iGtaVersion, GTASA_VERSION_USA10 + jnz createRaster + mov unnamed_10113ADC, 0x7FB230 + +createRaster: + push 0x604 + push 0x20 + push 0x10 + push 0x40 + mov ebx, unnamed_10113ADC + call ebx + mov unnamed_101516C8, eax + pop ebx + pop ebx + pop ebx + pop ebx + +exitFn: + mov eax, unnamed_101516C8 + retn + } } //----------------------------------------------------------- NUDE RwRasterDestroy_Hook() { - // TODO: RwRasterDestroy_Hook + __asm + { + mov eax, [esp+4] ;// arg0 (RwRaster *) + mov unnamed_101516D0, eax + pushad + + // never let the game destroy our cached plate raster + cmp unnamed_10113AD8, 0 + jz destroyRaster + cmp unnamed_101516C8, 0 + jz destroyRaster + mov eax, unnamed_101516D0 + cmp eax, unnamed_101516C8 + jnz destroyRaster + + popad + mov eax, 1 + retn + +destroyRaster: + popad + push esi + mov esi, [esp+8] + push esi + mov edx, unnamed_101516D4 + jmp edx + } } //----------------------------------------------------------- NUDE CVehicle__Render_Hook() { - // TODO: CVehicle__Render_Hook + _asm mov unnamed_101516D8, esi + _asm ret } //----------------------------------------------------------- +DWORD dwRenderObjectRet = 0; +ENTITY_TYPE *_pRenderEntity; +CObject *_pRenderNetObject; +CPlayerPool *_pRenderPlayerPool; + NUDE CObject__Render_Hook() { - // TODO: CObject__Render_Hook + _asm mov eax, [esp] + _asm mov dwRenderObjectRet, eax + _asm mov _pRenderEntity, ecx + + _asm pushad + + if (_pRenderEntity) wLastRendObj = _pRenderEntity->nModelIndex; + + _pRenderNetObject = NULL; + + if (pNetGame && _pRenderEntity && (_pRenderEntity->dwProcessingFlags & 0x80000000)) + { + if ((_pRenderNetObject = pNetGame->GetObjectPool()->FUNC_100129D0(_pRenderEntity)) != NULL || + ((_pRenderPlayerPool = pNetGame->GetPlayerPool()) != NULL && + (_pRenderNetObject = _pRenderPlayerPool->FUNC_10013B70(_pRenderEntity)) != NULL)) + { + _pRenderNetObject->FUNC_100A9E30(); + } + } + + if (unnamed_1014FFAC && _pRenderEntity && (_pRenderEntity->dwProcessingFlags & 0x80000000)) + { + _pRenderNetObject = unnamed_1014FFAC->FUNC_100129D0(_pRenderEntity); + if (_pRenderNetObject) _pRenderNetObject->FUNC_100A9E30(); + } + + _asm + { + popad + + // call original CObject::Render + mov eax, 0x59F180 + call eax + + pushad + } + + if (pNetGame && _pRenderNetObject) _pRenderNetObject->FUNC_100A9E70(); + if (unnamed_1014FFAC && _pRenderNetObject) _pRenderNetObject->FUNC_100A9E70(); + + _asm popad + _asm retn } //----------------------------------------------------------- NUDE CRadar__DrawMap__FindPlayerSpeed_Hook() { - // TODO: CRadar__DrawMap__FindPlayerSpeed_Hook + // keep the map still while the local player is a vehicle passenger + if(pGame && pGame->FindPlayerPed() && pGame->FindPlayerPed()->IsInVehicle() && + pGame->FindPlayerPed()->IsAPassenger()) + { + _asm lea eax, unnamed_10151710 + _asm ret + } + + _asm mov edx, 0x56E090 ;// FindPlayerSpeed + _asm jmp edx } //----------------------------------------------------------- +DWORD dwObjectInstance = 0; +DWORD dwObjectInstanceUnused = 0; +BOOL bReplaceAllObjects = FALSE; + +int FUNC_1009D050(int iModelID, float fX, float fY, float fZ); // todo: implement `IsObjectRemoved` + NUDE CFileLoader__LoadObjectInstance_Hook() { - // TODO: CFileLoader__LoadObjectInstance_Hook + _asm mov eax, [esp+4] + _asm mov dwObjectInstance, eax + _asm mov eax, [esp+8] + _asm mov dwObjectInstanceUnused, eax + + _asm pushad + + // replace removed objects with an invisible model + if (dwObjectInstance && FUNC_1009D050(*(int *)(dwObjectInstance + 0x1C), *(float *)dwObjectInstance, + *(float *)(dwObjectInstance + 4), *(float *)(dwObjectInstance + 8))) + { + *(int *)(dwObjectInstance + 0x1C) = 19300; + } + + if (bReplaceAllObjects) *(int *)(dwObjectInstance + 0x1C) = 19300; + + _asm + { + popad + + push -1 + push 0x83C931 + mov eax, 0x538097 + jmp eax + } } //----------------------------------------------------------- @@ -806,145 +2046,724 @@ NUDE CEscalator__Update_Hook() NUDE CObject__CreateRwObject_Hook() { - // TODO: CObject__CreateRwObject_Hook + _asm mov eax, [esp] + _asm mov unnamed_1015172C, eax + _asm pushad + + // stop the original from calling the roadsign/plate atomic setup + UnFuck(0x4096AA, 3); + memset((void *)0x4096AA, 0x90, 3); + + __asm + { + popad + + mov eax, 0x533D30 // CObject::CreateRwObject + call eax + + pushad + } + + memcpy((void *)0x4096AA, unnamed_10113AE8, 3); + + __asm popad + __asm ret } //----------------------------------------------------------- +DWORD dwDeleteRwObjectRet = 0; +ENTITY_TYPE *_pDeleteRwObjectEntity; +BYTE byteSavedRoadsignCode[5] = {0xE8,0x3C,0xFF,0xFF,0xFF}; + NUDE CEntity__DeleteRwObject_Hook() { - // TODO: CEntity__DeleteRwObject_Hook + _asm mov eax, [esp] + _asm mov dwDeleteRwObjectRet, eax + _asm mov _pDeleteRwObjectEntity, ecx + + _asm pushad + + if (_pDeleteRwObjectEntity->nModelIndex == 3586 || _pDeleteRwObjectEntity->nModelIndex == 3743 || + _pDeleteRwObjectEntity->nModelIndex == 8980 || _pDeleteRwObjectEntity->nModelIndex == 8979) + { + // nop the CCustomRoadsignMgr call these models crash in + UnFuck(0x71791F, 5); + memset((void *)0x71791F, 0x90, 5); + + _asm + { + popad + + // call original CEntity::DeleteRwObject + mov eax, 0x534030 + call eax + + pushad + } + + memcpy((void *)0x71791F, byteSavedRoadsignCode, 5); + + _asm popad + _asm retn + } + + _asm popad + _asm mov eax, 0x534030 + _asm jmp eax } //----------------------------------------------------------- +VEHICLE_TYPE *_pBreakTowLinkVehicle; +VEHICLEID _BreakTowLinkTractorId; +CVehiclePool *_pBreakTowLinkPool; +// set while samp detaches a trailer itself, so gta gets to break the link +BOOL bIgnoreBreakTowLinkHook; +CVehicle *_pBreakTowLinkTractor; + NUDE CAutomobile__BreakTowLink_Hook() { - // TODO: CAutomobile__BreakTowLink_Hook + __asm + { + mov _pBreakTowLinkVehicle, ecx + + pushad + } + + if (pNetGame && !bIgnoreBreakTowLinkHook && _pBreakTowLinkVehicle && + _pBreakTowLinkVehicle->fHealth > 0.0f) + { + _pBreakTowLinkPool = pNetGame->FUNC_10001180(); + _BreakTowLinkTractorId = _pBreakTowLinkPool->FUNC_1001EB90(_pBreakTowLinkVehicle->dwTractor); + + if (_BreakTowLinkTractorId != 0xFFFF) + { + _pBreakTowLinkTractor = (CVehicle *)_pBreakTowLinkPool->FUNC_10001120(_BreakTowLinkTractorId); + + if (_pBreakTowLinkTractor && !_pBreakTowLinkTractor->IsDriverLocalPlayer() && + *(DWORD *)_pBreakTowLinkTractor->_gap48) + { + __asm + { + popad + retn + } + } + } + } + + __asm + { + popad + + // call original CAutomobile::BreakTowLink + mov eax, 0x6A4400 + jmp eax + } } //----------------------------------------------------------- +ENTITY_TYPE *_pAttachedEntity; +CObject *_pAttachedObject; +DWORD _dwAttachedEntityEsp; +DWORD _dwAttachedEntityResult; + NUDE CWorld__ProcessAttachedEntities__PositionAttachedEntity_Hook() { - // TODO: CWorld__ProcessAttachedEntities__PositionAttachedEntity_Hook -} + __asm + { + mov _dwAttachedEntityEsp, esp + mov _pAttachedEntity, ecx + + pushad + + // call original CEntity::PositionAttachedEntity + mov ecx, _pAttachedEntity + mov ebx, 0x546FF0 + call ebx + + mov _dwAttachedEntityResult, eax + } + + if (pNetGame && pNetGame->GetObjectPool()) + { + _pAttachedObject = pNetGame->GetObjectPool()->FUNC_100129D0(_pAttachedEntity); + if (_pAttachedObject) _pAttachedObject->FUNC_100A7E20(); + } + + __asm + { + popad + mov eax, _dwAttachedEntityResult + retn + }} //----------------------------------------------------------- NUDE CRenderer__RenderEverythingBarRoads_Hook() { - // TODO: CRenderer__RenderEverythingBarRoads_Hook + _asm mov eax, 0x553AA0 ;// CRenderer::RenderEverythingBarRoads + _asm call eax + + if(pNetGame) { + unnamed_1015177C = pNetGame->GetObjectPool(); + if(unnamed_1015177C) { + unnamed_1015177C->FUNC_10012B90(); + } + } + + if(unnamed_1014FFAC) { + unnamed_1014FFAC->FUNC_10012B90(); + } + + _asm ret } //----------------------------------------------------------- NUDE CRenderer__RenderFadingInEntities_Hook() { - // TODO: CRenderer__RenderFadingInEntities_Hook + _asm mov eax, 0x5531E0 ;// CRenderer::RenderFadingInEntities + _asm call eax + + if(pNetGame) { + unnamed_1015177C = pNetGame->GetObjectPool(); + if(unnamed_1015177C) { + unnamed_1015177C->FUNC_10012BD0(); + } + } + + if(unnamed_1014FFAC) { + unnamed_1014FFAC->FUNC_10012BD0(); + } + + _asm ret } //----------------------------------------------------------- +ENTITY_TYPE *_pRenderListEntity; + NUDE CRenderer__AddEntityToRenderList_Hook() { - // TODO: CRenderer__AddEntityToRenderList_Hook + _asm push esi + _asm mov esi, [esp+8] + _asm mov _pRenderListEntity, esi + + _asm pushad + + // keep objects out of the render list once they are past their draw distance + if (pNetGame && _pRenderListEntity && _pRenderListEntity->vtable == 0x866F60 && + (_pRenderListEntity->dwProcessingFlags & 0x80000000)) + { + _pRenderNetObject = pNetGame->FUNC_10002E10()->FUNC_100129D0(_pRenderListEntity); + + if (_pRenderNetObject && (_pRenderNetObject->field_118B != 0 || + _pRenderNetObject->field_54 > 300.0f || _pRenderNetObject->field_1199 != 0)) + { + _asm popad + _asm pop esi + _asm retn + } + } + + if (unnamed_1014FFAC && _pRenderListEntity && _pRenderListEntity->vtable == 0x866F60 && + (_pRenderListEntity->dwProcessingFlags & 0x80000000)) + { + _pRenderNetObject = unnamed_1014FFAC->FUNC_100129D0(_pRenderListEntity); + + if (_pRenderNetObject && (_pRenderNetObject->field_118B != 0 || + _pRenderNetObject->field_54 > 300.0f)) + { + _asm popad + _asm pop esi + _asm retn + } + } + + _asm + { + popad + + // original code + mov esi, _pRenderListEntity + movsx eax, word ptr [esi+22h] + mov edx, 0x5534B9 + jmp edx + } } //----------------------------------------------------------- NUDE CEntity__RenderEffects__RenderRoadsignAtomic_Hook() { - // TODO: CEntity__RenderEffects__RenderRoadsignAtomic_Hook + __asm + { + mov eax, [esp+4] ;// arg0 (RpAtomic *) + test eax, eax + jz exitFn + mov eax, 0x6FF350 ;// CCustomRoadsignMgr::RenderRoadsignAtomic + jmp eax +exitFn: + ret + } } //----------------------------------------------------------- +DWORD dwEventDamageThis; +DWORD dwEventDamagePed; + NUDE CEventDamage__AffectsPed_Hook() { - // TODO: CEventDamage__AffectsPed_Hook + __asm + { + mov dwEventDamageThis, ecx + mov eax, [esp+4] + mov dwEventDamagePed, eax + pushad + cmp pNetGame, 0 + jz do_affects_ped + call GamePool_FindPlayerPed + cmp dwEventDamagePed, eax + jnz do_affects_ped + mov eax, pbyteCurrentPlayer + movzx ecx, byte ptr [eax] + test ecx, ecx + jz do_affects_ped + popad + + // inside a remote player context: always report the event as affecting the ped + xor eax, eax + mov al, 1 + retn 4 + do_affects_ped: + popad + sub esp, 0xC + push esi + mov esi, ecx + mov eax, 0x4B35A6 // CEventDamage::AffectsPed + 6 + jmp eax + } } //----------------------------------------------------------- -NUDE CCollision__BuildCacheOfCameraCollision_Hook() +// the game hands two 20 byte blocks (a position plus two more floats) to +// CCollision::BuildCacheOfCameraCollision +typedef struct _CAMCOL_POINT { - // TODO: CCollision__BuildCacheOfCameraCollision_Hook + VECTOR vecPos; + float field_C; + float field_10; +} CAMCOL_POINT; + +CAMCOL_POINT _camColStart; +CAMCOL_POINT _camColEnd; +BYTE _byteCamColResult; + +BYTE CCollision__BuildCacheOfCameraCollision_Hook(CAMCOL_POINT *pStart, CAMCOL_POINT *pEnd) +{ + _camColStart = *pStart; + _camColEnd = *pEnd; + + _byteCamColResult = 0; + + __asm + { + push pEnd + push pStart + + // call original CCollision::BuildCacheOfCameraCollision + mov edx, 0x41AC40 + call edx + + mov _byteCamColResult, al + + pop edx + pop edx + } + + // gta only caches collision inside the map bounds, so samp entities placed + // outside of them have to be checked by hand + if (pEnd->vecPos.X > 3000.0f || pEnd->vecPos.X < -3000.0f || + pEnd->vecPos.Y > 3000.0f || pEnd->vecPos.Y < -3000.0f) + { + if (pNetGame && !_byteCamColResult && + pNetGame->GetObjectPool()->FUNC_10012DE0(&_camColStart.vecPos, &_camColEnd.vecPos)) + _byteCamColResult = 1; + + if (unnamed_1014FFAC && !_byteCamColResult && + unnamed_1014FFAC->FUNC_10012DE0(&_camColStart.vecPos, &_camColEnd.vecPos)) + _byteCamColResult = 1; + + if (pNetGame && !pNetGame->GetField232() && !_byteCamColResult && + pNetGame->GetVehiclePool()->FUNC_1001EE20(&_camColStart.vecPos, &_camColEnd.vecPos)) + _byteCamColResult = 1; + } + + return _byteCamColResult; } //----------------------------------------------------------- NUDE CCollision__CheckCameraCollisionVehicles_Hook() { - // TODO: CCollision__CheckCameraCollisionVehicles_Hook + __asm + { + pushad + cmp pNetGame, 0 + jz do_check + mov eax, pNetGame + movzx ecx, byte ptr [eax+0x232] // pNetGame->field_232 + test ecx, ecx + jz do_check + popad + xor eax, eax + retn + do_check: + popad + mov eax, 0x41A990 // CCollision::CheckCameraCollisionVehicles + jmp eax + } } //----------------------------------------------------------- NUDE CWorld__CameraToIgnoreThisObject_Hook() { - // TODO: CWorld__CameraToIgnoreThisObject_Hook + __asm + { + mov eax, [esp+4] // pEntity + test eax, eax + jz ignore_this_object + mov ecx, [eax+0xFC] + cmp ecx, 0 + jnz ignore_this_object + mov edx, 0x563F40 // CWorld::CameraToIgnoreThisObject + jmp edx + ignore_this_object: + mov eax, 1 + retn + } } //----------------------------------------------------------- +// shared by the four ProcessEntityCollision hooks below +DWORD dwCollisionThis; +DWORD dwCollisionEntity; +DWORD dwCollisionRetAddr; + NUDE CAutomobile__ProcessEntityCollision_Hook() { - // TODO: CAutomobile__ProcessEntityCollision_Hook + __asm + { + mov dwCollisionThis, ecx + mov edx, [esp] + mov dwCollisionRetAddr, edx + mov edx, [esp+4] + mov dwCollisionEntity, edx + pushad + cmp pNetGame, 0 + jz process_collision + mov eax, pNetGame + movzx ecx, byte ptr [eax+0x232] // pNetGame->field_232 + test ecx, ecx + jz process_collision + cmp dwCollisionThis, 0 + jz process_collision + cmp dwCollisionEntity, 0 + jz process_collision + mov edx, dwCollisionEntity + movzx eax, word ptr [edx+0x22] // entity.nModelIndex + cmp eax, 400 + jl process_collision + mov ecx, dwCollisionEntity + movzx edx, word ptr [ecx+0x22] + cmp edx, 611 // only vehicle models pass + jg process_collision + mov eax, dwCollisionThis + cmp dword ptr [eax+0x460], 0 // pVehicle->pDriver + jz process_collision + mov ecx, dwCollisionEntity + cmp dword ptr [ecx+0x460], 0 + jz process_collision + popad + + // both vehicles are being driven, so don't collide them + mov eax, 0 + retn 8 + process_collision: + popad + mov edx, 0x6ACE70 // CAutomobile::ProcessEntityCollision + jmp edx + } } //----------------------------------------------------------- NUDE CBike__ProcessEntityCollision_Hook() { - // TODO: CBike__ProcessEntityCollision_Hook + __asm + { + mov dwCollisionThis, ecx + mov edx, [esp] + mov dwCollisionRetAddr, edx + mov edx, [esp+4] + mov dwCollisionEntity, edx + pushad + cmp pNetGame, 0 + jz process_collision + mov eax, pNetGame + movzx ecx, byte ptr [eax+0x232] // pNetGame->field_232 + test ecx, ecx + jz process_collision + cmp dwCollisionThis, 0 + jz process_collision + cmp dwCollisionEntity, 0 + jz process_collision + mov edx, dwCollisionEntity + movzx eax, word ptr [edx+0x22] // entity.nModelIndex + cmp eax, 400 + jl process_collision + mov ecx, dwCollisionEntity + movzx edx, word ptr [ecx+0x22] + cmp edx, 611 // only vehicle models pass + jg process_collision + mov eax, dwCollisionThis + cmp dword ptr [eax+0x460], 0 // pVehicle->pDriver + jz process_collision + mov ecx, dwCollisionEntity + cmp dword ptr [ecx+0x460], 0 + jz process_collision + popad + + // both vehicles are being driven, so don't collide them + mov eax, 0 + retn 8 + process_collision: + popad + mov edx, 0x6BDEA0 // CBike::ProcessEntityCollision + jmp edx + } } //----------------------------------------------------------- NUDE CMonsterTruck__ProcessEntityCollision_Hook() { - // TODO: CMonsterTruck__ProcessEntityCollision_Hook + __asm + { + mov dwCollisionThis, ecx + mov edx, [esp] + mov dwCollisionRetAddr, edx + mov edx, [esp+4] + mov dwCollisionEntity, edx + pushad + cmp pNetGame, 0 + jz process_collision + mov eax, pNetGame + movzx ecx, byte ptr [eax+0x232] // pNetGame->field_232 + test ecx, ecx + jz process_collision + cmp dwCollisionThis, 0 + jz process_collision + cmp dwCollisionEntity, 0 + jz process_collision + mov edx, dwCollisionEntity + movzx eax, word ptr [edx+0x22] // entity.nModelIndex + cmp eax, 400 + jl process_collision + mov ecx, dwCollisionEntity + movzx edx, word ptr [ecx+0x22] + cmp edx, 611 // only vehicle models pass + jg process_collision + mov eax, dwCollisionThis + cmp dword ptr [eax+0x460], 0 // pVehicle->pDriver + jz process_collision + mov ecx, dwCollisionEntity + cmp dword ptr [ecx+0x460], 0 + jz process_collision + popad + + // both vehicles are being driven, so don't collide them + mov eax, 0 + retn 8 + process_collision: + popad + mov edx, 0x6C8AE0 // CMonsterTruck::ProcessEntityCollision + jmp edx + } } //----------------------------------------------------------- NUDE CPhysical__ProcessEntityCollision_Hook() { - // TODO: CPhysical__ProcessEntityCollision_Hook + __asm + { + mov dwCollisionThis, ecx + mov edx, [esp] + mov dwCollisionRetAddr, edx + mov edx, [esp+4] + mov dwCollisionEntity, edx + pushad + cmp pNetGame, 0 + jz process_collision + mov eax, pNetGame + movzx ecx, byte ptr [eax+0x232] // pNetGame->field_232 + test ecx, ecx + jz process_collision + cmp dwCollisionThis, 0 + jz process_collision + cmp dwCollisionEntity, 0 + jz process_collision + mov edx, dwCollisionEntity + movzx eax, word ptr [edx+0x22] // entity.nModelIndex + cmp eax, 400 + jl process_collision + mov ecx, dwCollisionEntity + movzx edx, word ptr [ecx+0x22] + cmp edx, 611 // only vehicle models pass + jg process_collision + mov eax, dwCollisionThis + cmp dword ptr [eax+0x460], 0 // pVehicle->pDriver + jz process_collision + mov ecx, dwCollisionEntity + cmp dword ptr [ecx+0x460], 0 + jz process_collision + popad + + // both vehicles are being driven, so don't collide them + mov eax, 0 + retn 8 + process_collision: + popad + mov edx, 0x546D00 // CPhysical::ProcessEntityCollision + jmp edx + } } //----------------------------------------------------------- NUDE CVehicle__UsesSiren_Hook() { - // TODO: CVehicle__UsesSiren_Hook + __asm + { + // the streamed in check below already answered for this vehicle + mov eax, unnamed_1015180C + test eax, eax + jz usesSiren + mov unnamed_1015180C, 0 + mov al, 1 + retn + +usesSiren: + mov unnamed_10151804, ecx + mov edx, 0x6D8470 // CVehicle::UsesSiren + call edx + mov unnamed_10151808, al + } + + if(!unnamed_10151808) + { + _asm pushad + + // the game doesn't know about server sided sirens, ask our own vehicle + if(pNetGame) + { + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + if(pVehiclePool) + { + unnamed_10113B2C = pVehiclePool->FUNC_1001EB90(unnamed_10151804); + if(unnamed_10113B2C != 0xFFFF) + { + CVehicle *pVehicle = (CVehicle *)pNetGame->GetVehiclePool()->FUNC_10001120(unnamed_10113B2C); + if(pVehicle->FUNC_100B8330()) unnamed_10151808 = 1; + } + } + } + + _asm popad + } + + _asm mov al, unnamed_10151808 + _asm retn } //----------------------------------------------------------- NUDE CAEWeatherAudioEntity__UpdateParameters_Hook() { - // TODO: CAEWeatherAudioEntity__UpdateParameters_Hook + _asm mov edx, [esp+4] + _asm mov unnamed_10151814, edx + _asm mov edx, [esp+8] + _asm mov unnamed_10151818, edx + unnamed_10151810 = *(DWORD *)0xB72914; + + // mute the rain/wind audio while the outdoor ambience track is disabled + if(pGame) + { + CAudio *pAudio = pGame->m_pGameAudio; + if(pAudio->field_4) *(DWORD *)0xB72914 = 1; + } + + _asm push unnamed_10151818 + _asm push unnamed_10151814 + _asm mov edx, 0x505A00 + _asm call edx + + *(DWORD *)0xB72914 = unnamed_10151810; + _asm ret 8 } //----------------------------------------------------------- NUDE CWorld__ProcessVerticalLine_Hook() { - // TODO: CWorld__ProcessVerticalLine_Hook + __asm + { + mov eax, [esp] ;// caller + mov unnamed_10151824, eax + cmp eax, 0x59E8F9 + jz callFn + mov eax, [esp+0x14] + test eax, eax + jz callFn + mov byte ptr [esp+0x20], 1 +callFn: + sub esp, 0x2C + mov eax, 0x407C37 ;// CWorld::ProcessVerticalLine + jmp eax + } } //----------------------------------------------------------- NUDE CStreaming__RequestModel_Hook() { - // TODO: CStreaming__RequestModel_Hook + __asm + { + cmp ebp, 0 ;// model id + jl exitFn + push esi + push edi + lea edi, [ebp+ebp*4] + mov eax, 0x4087F0 ;// CStreaming::RequestModel + jmp eax +exitFn: + pop ebp + pop ebx + ret + } } //----------------------------------------------------------- -// todo: implement fully +// arguments of the shot being fired, shared with CWeapon__FireSniper_Hook ENTITY_TYPE* pFiringEntity = 0; VECTOR* pPosn = 0; VECTOR* pEffectPosn = 0; @@ -987,9 +2806,14 @@ NUDE CWeapon__FireInstantHit_Hook() } } - // todo: implement sub_10013C90 - /*if ( pNetGame && pNetGame->m_pPools->pPlayerPool ) - sub_10013C90();*/ + if(pNetGame && pNetGame->GetPlayerPool()) + pNetGame->GetPlayerPool()->sub_10013C90(); + + if(pGame && pGame->FindPlayerPed()) + pGame->FindPlayerPed()->FUNC_100AFA70(); + + if(pNetGame && pNetGame->GetPlayerPool()) + pNetGame->GetPlayerPool()->sub_10013D10(); _asm popad _asm ret 0x20 @@ -997,30 +2821,268 @@ NUDE CWeapon__FireInstantHit_Hook() //----------------------------------------------------------- -NUDE CWorld__ProcessLineOfSight_Hook() +extern DWORD unnamed_10118990; // 0x10118990, lives in another translation unit + +// todo: implement sub_100A5410 +void __stdcall sub_100A5410(VECTOR *vecOrigin, VECTOR *vecLine, VECTOR *colPoint, DWORD *pHitEntity); + +VECTOR vecShotOrigin; // 0x10150CE4 +BOOL bLineOfSightResult = 0; // 0x101517B0 +CPlayerPed *pShotSyncPlayer = 0; // 0x101517CC +SHOT_SYNC_DATA *pShotSyncData = 0; // 0x101517D0 + +BOOL CWorld__ProcessLineOfSight_Hook(VECTOR *vecOrigin, VECTOR *vecLine, VECTOR *colPoint, + DWORD *pHitEntity, int bCheckBuildings, int bCheckVehicles, int bCheckPeds, + int bCheckObjects, int bCheckDummies, int bSeeThroughStuff, + int bIgnoreSomeObjectsForCamera, int bUnk1) { - // TODO: CWorld__ProcessLineOfSight_Hook + // aim the ray at where the remote player actually shot from + if(unnamed_10118990 != 2 && pShotSyncPlayer && pShotSyncPlayer != pGame->FindPlayerPed() && + pShotSyncData && pShotSyncData->pAttachedTo && + pShotSyncData->pAttachedTo->vtable != 0x863C40 && + pShotSyncData->pAttachedTo->mat) + { + if(unnamed_10118990 == 0) + { + FUNC_100B4D10(&vecShotOrigin, pShotSyncData->pAttachedTo->mat, &pShotSyncData->vecOffset); + } + else + { + vecShotOrigin.X = pShotSyncData->pAttachedTo->mat->pos.X + pShotSyncData->vecOffset.X; + vecShotOrigin.Y = pShotSyncData->pAttachedTo->mat->pos.Y + pShotSyncData->vecOffset.Y; + vecShotOrigin.Z = pShotSyncData->pAttachedTo->mat->pos.Z + pShotSyncData->vecOffset.Z; + } + + vecLine->X = vecShotOrigin.X - vecOrigin->X + vecShotOrigin.X; + vecLine->Y = vecShotOrigin.Y - vecOrigin->Y + vecShotOrigin.Y; + vecLine->Z = vecShotOrigin.Z - vecOrigin->Z + vecShotOrigin.Z; + } + + bLineOfSightResult = ProcessLineOfSight(vecOrigin, vecLine, colPoint, pHitEntity, + bCheckBuildings, bCheckVehicles, bCheckPeds, bCheckObjects, bCheckDummies, + bSeeThroughStuff, bIgnoreSomeObjectsForCamera, bUnk1); + + // a remote player's shot must not collide with the local player + if(unnamed_10118990 != 2 && pShotSyncPlayer && pShotSyncPlayer != pGame->FindPlayerPed() && + pShotSyncData && !pShotSyncData->pAttachedTo) + { + if(*pHitEntity == (DWORD)GamePool_FindPlayerPed() || + (IN_VEHICLE(GamePool_FindPlayerPed()) && + *pHitEntity == GamePool_FindPlayerPed()->pVehicle)) + { + *pHitEntity = 0; + colPoint->X = 0.0f; + colPoint->Y = 0.0f; + colPoint->Z = 0.0f; + return FALSE; + } + } + + if(pShotSyncPlayer && pShotSyncPlayer == pGame->FindPlayerPed()) + sub_100A5410(vecOrigin, vecLine, colPoint, pHitEntity); + + return bLineOfSightResult; +} + +//----------------------------------------------------------- + +SHOT_SYNC_DATA unnamed_101506F8; +MATRIX4X4 unnamed_10150990; +MATRIX4X4 unnamed_10150BC8; +ENTITY_TYPE *unnamed_101517AC; + +void FUNC_100B5910(MATRIX4X4 *pOut, MATRIX4X4 *pMatrix); // .text:100B5910, lives in another translation unit +void FUNC_100B5740(MATRIX4X4 *pOut, MATRIX4X4 *pMatrix); // .text:100B5740, lives in another translation unit + +// .text:100A5410 : records where the local player's own shot landed, in the hit +// entity's space when there is one +void __stdcall sub_100A5410(VECTOR *vecOrigin, VECTOR *vecLine, VECTOR *colPoint, DWORD *pHitEntity) +{ + memset(&unnamed_101506F8, 0, sizeof(unnamed_101506F8)); + + // element wise, a VECTOR assignment would cost a scratch base register + *(DWORD *)&unnamed_101506F8.vecOrigin.X = *(DWORD *)&vecOrigin->X; + *(DWORD *)&unnamed_101506F8.vecOrigin.Y = *(DWORD *)&vecOrigin->Y; + *(DWORD *)&unnamed_101506F8.vecOrigin.Z = *(DWORD *)&vecOrigin->Z; + *(DWORD *)&unnamed_101506F8.vecColPoint.X = *(DWORD *)&colPoint->X; + *(DWORD *)&unnamed_101506F8.vecColPoint.Y = *(DWORD *)&colPoint->Y; + *(DWORD *)&unnamed_101506F8.vecColPoint.Z = *(DWORD *)&colPoint->Z; + + if(pHitEntity) + { + unnamed_101517AC = (ENTITY_TYPE *)*pHitEntity; + + if(unnamed_101517AC) + { + if(unnamed_101517AC->mat) + { + if(unnamed_10118990 == 0) + { + memset(&unnamed_10150990, 0, sizeof(unnamed_10150990)); + memset(&unnamed_10150BC8, 0, sizeof(unnamed_10150BC8)); + + FUNC_100B5910(&unnamed_10150BC8, unnamed_101517AC->mat); + FUNC_100B5740(&unnamed_10150990, &unnamed_10150BC8); + FUNC_100B4D10(&unnamed_101506F8.vecOffset, &unnamed_10150990, colPoint); + } + else + { + unnamed_101506F8.vecOffset.X = colPoint->X - unnamed_101517AC->mat->pos.X; + unnamed_101506F8.vecOffset.Y = colPoint->Y - unnamed_101517AC->mat->pos.Y; + unnamed_101506F8.vecOffset.Z = colPoint->Z - unnamed_101517AC->mat->pos.Z; + } + + unnamed_101506F8.pAttachedTo = unnamed_101517AC; + } + } + } + + pGame->FindPlayerPed()->FUNC_100AF280(&unnamed_101506F8); } //----------------------------------------------------------- NUDE CWeapon__FireSniper_Hook() { - // TODO: CWeapon__FireSniper_Hook + _asm mov eax, [esp+0x4] + _asm mov pFiringEntity, eax + _asm pushad + + if(pFiringEntity != (ENTITY_TYPE*)GamePool_FindPlayerPed()) + { + __asm + { + popad + retn 0xC + } + } + + if(pGame && pGame->FindPlayerPed()) + pGame->FindPlayerPed()->FUNC_100AFA70(); + + _asm popad + _asm ret 0xC } //----------------------------------------------------------- +// bullets get a 50x velocity so the shot lands on the frame it was fired +const float fBulletVelocityScale = 50.0f; + NUDE CBulletInfo__AddBullet_Hook() { - // TODO: CBulletInfo__AddBullet_Hook + __asm + { + push ebp + mov ebp, esp + + fld dword ptr [ebp+0x1C] + fmul fBulletVelocityScale + fstp dword ptr [ebp+0x1C] + fld dword ptr [ebp+0x20] + fmul fBulletVelocityScale + fstp dword ptr [ebp+0x20] + fld dword ptr [ebp+0x24] + fmul fBulletVelocityScale + fstp dword ptr [ebp+0x24] + + push dword ptr [ebp+0x24] + push dword ptr [ebp+0x20] + push dword ptr [ebp+0x1C] + push dword ptr [ebp+0x18] + push dword ptr [ebp+0x14] + push dword ptr [ebp+0x10] + push dword ptr [ebp+0xC] + push dword ptr [ebp+0x8] + mov edx, 0x736010 ;// CBulletInfo::AddBullet + call edx + add esp, 0x20 + + mov edx, 0x7360D0 ;// CBulletInfo::Update + call edx + + pop ebp + retn + } +} + +//----------------------------------------------------------- + +// todo: implement sub_100A5240, it tells whether the damage came from a team mate +BOOL sub_100A5240(DWORD dwVehicle, DWORD dwInflictor, DWORD dwWeapon); + +DWORD dwDamageInflictor = 0; +DWORD dwDamagedVehicle = 0; +DWORD dwDamageWeapon = 0; +BOOL bIgnoreVehicleDamage = 0; +DWORD dwInflictDamageRetAddr = 0; + +PLAYERID wInflictDamageDriver; +CPlayerPool *pInflictDamagePool; + +// true when the local player drives dwVehicle and dwPed belongs to a team mate +BOOL FUNC_100A5240(DWORD dwVehicle, DWORD dwPed) +{ + if(!pNetGame || !pGame || !pGame->FindPlayerPed() + || !pGame->FindPlayerPed()->IsInVehicle() + || pGame->FindPlayerPed()->IsAPassenger() + || (int)pGame->FindPlayerPed()->GetGtaVehicle() != (int)dwVehicle) + return FALSE; + + wInflictDamageDriver = FUNC_100A3AE0(dwPed); + if(wInflictDamageDriver == 0xFFFF) return FALSE; + + pInflictDamagePool = pNetGame->GetPlayerPool(); + if(!pInflictDamagePool) return FALSE; + + if(pNetGame->GetSettings()->field_22) + { + BYTE byteTeam = pInflictDamagePool->m_pLocalPlayer->m_byteTeam; + + if(byteTeam == 0xFF) return FALSE; + + if((BYTE)pNetGame->GetPlayerPool()->GetAt(wInflictDamageDriver)->field_109 == byteTeam) + return TRUE; + } + + return FALSE; } //----------------------------------------------------------- NUDE CVehicle__InflictDamage_Hook() { - // TODO: CVehicle__InflictDamage_Hook + _asm mov dwDamagedVehicle, ecx + _asm mov eax, [esp] + _asm mov dwInflictDamageRetAddr, eax + _asm mov eax, [esp+0x4] + _asm mov dwDamageInflictor, eax + _asm mov eax, [esp+0x8] + _asm mov dwDamageWeapon, eax + _asm pushad + + bIgnoreVehicleDamage = sub_100A5240(dwDamagedVehicle, dwDamageInflictor, dwDamageWeapon); + + _asm popad + + if(bIgnoreVehicleDamage) + { + _asm xor eax, eax + _asm retn 0x18 + } + + if(iGtaVersion == 1) + { + _asm push 0xFFFFFFFF + _asm mov eax, 0x404CDC + _asm jmp eax + } + else + { + _asm push 0xFFFFFFFF + _asm mov eax, 0x404CE3 + _asm jmp eax + } } //----------------------------------------------------------- @@ -1033,6 +3095,15 @@ NUDE CTimer__GetCurrentTimeInCycles_Hook() _asm jmp edx } +//----------------------------------------------------------- + +// .text:100A23F0 : drops a 0x190 byte game frame and returns +NUDE FUNC_100A23F0() +{ + _asm add esp, 0x190 + _asm retn +} + //----------------------------------------------------------- @@ -1121,7 +3192,766 @@ NUDE FUNC_100A5FF0() //----------------------------------------------------------- -void InstallMethodHook( DWORD dwInstallAddress, +extern char szUnk_10113968[8]; // lives in game.cpp + +// .text:100A41A0 : hands the caller a copy of the eight byte version tag +bool FUNC_100A41A0(char *pszTag) +{ + memset(pszTag, 0, 8); + memcpy(pszTag, szUnk_10113968, 8); + return true; +} + +//----------------------------------------------------------- + +DWORD unnamed_1015162C; +DWORD unnamed_101506EC; +DWORD unnamed_10150C84; +DWORD unnamed_101516B0; +DWORD unnamed_101516B4; +DWORD unnamed_101516B8; +DWORD unnamed_101516C0; +DWORD unnamed_101516C4; +DWORD unnamed_10150CF0; +PED_TYPE *unnamed_10150CF8; +DWORD unnamed_10113ACC; +DWORD unnamed_10113AD0; +WORD unnamed_10113AD4; + +WORD FUNC_1009CFD0(int a1); // .text:1009CFD0, lives in another translation unit + +// .text:100A4340 +NUDE FUNC_100A4340() +{ + _asm mov edx, [esp] + _asm mov unnamed_1015162C, edx + _asm mov edx, [esp+4] + _asm mov unnamed_101506EC, edx + _asm mov edx, [esp+8] + _asm mov unnamed_10150C84, edx + _asm mov edx, [esp+12] + _asm mov unnamed_101516B4, edx + _asm mov edx, [esp+16] + _asm mov unnamed_101516B8, edx + _asm pushad + + unnamed_10150CF8 = GamePool_FindPlayerPed(); + if(unnamed_10150CF8) unnamed_10150CF0 = (DWORD)unnamed_10150CF8->entity.pdwRenderWare; + else unnamed_10150CF0 = 0; + + _asm popad + _asm push 0xFFFFFFFF + _asm push 0x83BD6B + _asm jmp unnamed_10113ACC +} + +//----------------------------------------------------------- + +// .text:100A43B0 +NUDE FUNC_100A43B0() +{ + _asm mov edx, [esp+4] + _asm mov unnamed_101506EC, edx + _asm mov edx, [esp+8] + _asm mov unnamed_101516C0, edx + _asm mov edx, [esp+12] + _asm mov unnamed_101516B0, edx + _asm mov edx, [esp+16] + _asm mov unnamed_101516B4, edx + _asm pushad + _asm mov eax, unnamed_101516B4 + _asm push eax + _asm mov ecx, unnamed_101516B0 + _asm push ecx + _asm call FUNC_100B4430 + _asm mov unnamed_101516C4, eax + _asm mov edx, unnamed_101516C4 + _asm push edx + _asm call FUNC_1009CFD0 + _asm add esp, 4 + _asm mov unnamed_10113AD4, ax + _asm popad + _asm jmp unnamed_10113AD0 +} + +//----------------------------------------------------------- + +DWORD unnamed_101506A8; +DWORD unnamed_101514C8; +DWORD unnamed_10150C08; +DWORD unnamed_101509DC; +DWORD unnamed_10150CD8; +DWORD unnamed_10150980; +DWORD unnamed_10150A8C; + +// .text:100A4D40 +NUDE FUNC_100A4D40() +{ + _asm mov unnamed_101506A8, ecx + _asm mov eax, [esp+4] + _asm mov unnamed_101514C8, eax + _asm mov eax, [esp+8] + _asm mov unnamed_10150C08, eax + _asm mov eax, [esp+12] + _asm mov unnamed_101509DC, eax + _asm mov eax, [esp+16] + _asm mov unnamed_10150CD8, eax + _asm mov eax, [esp+20] + _asm mov unnamed_10150980, eax + _asm mov eax, [esp+24] + _asm mov unnamed_10150A8C, eax + _asm pushad + _asm popad + _asm push unnamed_10150A8C + _asm push unnamed_10150980 + _asm push unnamed_10150CD8 + _asm push unnamed_101509DC + _asm push unnamed_10150C08 + _asm push unnamed_101514C8 + _asm mov ecx, unnamed_101506A8 + _asm mov eax, 0x514970 + _asm call eax + _asm pushad + _asm popad + _asm retn 0x18 +} + +//----------------------------------------------------------- + +DWORD unnamed_10151618; +DWORD unnamed_101506F0; +DWORD unnamed_101514E4; + +// .text:100A2F60 : runs the call with the 0x858EF0 float forced to 2.0 +NUDE FUNC_100A2F60() +{ + _asm mov unnamed_10151628, ecx + _asm mov eax, [esp+4] + _asm mov unnamed_10151618, eax + + UnFuck(0x858EF0, 4); + unnamed_101506F0 = *(DWORD *)0x858EF0; + *(DWORD *)0x858EF0 = 0x40000000; + + _asm mov ecx, unnamed_10151628 + _asm push unnamed_10151618 + _asm mov eax, 0x62A380 + _asm call eax + _asm mov unnamed_101514E4, eax + + *(DWORD *)0x858EF0 = unnamed_101506F0; + + _asm mov eax, unnamed_101514E4 + _asm retn 4 +} + +//----------------------------------------------------------- + +DWORD unnamed_101517C0; + +// .text:100A5530 : skips the game call once we are connected +NUDE FUNC_100A5530() +{ + _asm mov unnamed_101517C0, ecx + _asm pushad + + if(pGame && pGame->sub_100A1BC0()) + { + _asm popad + _asm mov ecx, unnamed_101517C0 + _asm retn + } + + _asm mov ecx, unnamed_101517C0 + _asm mov edx, 0x60DC50 + _asm call edx + _asm popad + _asm mov ecx, unnamed_101517C0 + _asm retn +} + +//----------------------------------------------------------- + +DWORD unnamed_1015174C; +DWORD unnamed_101506CC; +DWORD unnamed_1015097C; +VECTOR unnamed_10150968; +VECTOR unnamed_10150BA8; + +// .text:100A4DC0 +NUDE FUNC_100A4DC0() +{ + _asm mov ebx, [esp+4] + _asm mov unnamed_1015174C, ebx + _asm mov ebx, [esp+8] + _asm mov unnamed_101506CC, ebx + _asm mov ebx, [esp+12] + _asm mov unnamed_1015097C, ebx + _asm pushad + + if(pGame && pGame->FindPlayerPed()) + pGame->FindPlayerPed()->FUNC_100AFC70(&unnamed_10150BA8, &unnamed_10150968); + + _asm popad + _asm mov ebx, 0x742300 + _asm jmp ebx +} + +//----------------------------------------------------------- + +VECTOR *unnamed_101517C4; +DWORD unnamed_101517C8; + +// .text:100A5570 +NUDE FUNC_100A5570() +{ + _asm mov unnamed_101517C8, ecx + _asm mov eax, [esp+4] + _asm mov unnamed_101517C4, eax + _asm pushad + + if(pNetGame && pChatWindow) + { + pChatWindow->AddDebugMessage("Joypad: %d LocalContext: %u UpdateCameraAim: %f %f %f", + pGame->sub_100A1BC0(), unnamed_1015168C, + unnamed_101517C4->X, unnamed_101517C4->Y, unnamed_101517C4->Z); + } + + _asm mov ecx, unnamed_101517C8 + _asm push unnamed_101517C4 + _asm mov eax, 0x50CB10 + _asm call eax + _asm popad + _asm retn 4 +} + +//----------------------------------------------------------- + +char unnamed_101514F0[256]; + +// .text:100A5EB0 : the game only has 24 bytes for a frame node name +int FUNC_100A5EB0(DWORD dwFrame, DWORD dwNameLen, DWORD a3) +{ + DWORD dwRet = 0; + + if(dwNameLen >= 0x18) + { + if(pGame) + { + _snprintf(unnamed_101514F0, 256, + "SA-MP: FrameNode name was too long (%u). SA-MP must exit.", dwNameLen); + ShowWindow(pGame->GetMainWindowHwnd(), SW_MINIMIZE); + MessageBox(NULL, unnamed_101514F0, "SA-MP", MB_ICONWARNING); + } + + _asm mov esp, 0 + _asm int 3 + } + + _asm push a3 + _asm push dwNameLen + _asm push dwFrame + _asm mov edx, 0x72FA50 + _asm call edx + _asm mov dwRet, eax + _asm pop edx + _asm pop edx + _asm pop edx + + return dwRet; +} + +//----------------------------------------------------------- + +DWORD unnamed_101515F4; +BYTE unnamed_10151614; +BYTE unnamed_1015161C; + +// .text:100A3070 : runs the game call with the remote player's weapon skills in place +NUDE FUNC_100A3070() +{ + _asm mov eax, [esp+4] + _asm mov unnamed_10151618, eax + _asm mov unnamed_101515F4, ecx + + unnamed_10151614 = *pbyteCurrentPlayer; + unnamed_1015161C = FindPlayerNumFromPedPtr(unnamed_10151618); + + if(unnamed_10151618 && unnamed_1015161C && !unnamed_10151614) + { + GameStoreLocalPlayerWeaponSkills(); + GameSetRemotePlayerWeaponSkills(unnamed_1015161C); + + _asm push unnamed_10151618 + _asm mov ecx, unnamed_101515F4 + _asm mov edx, 0x688810 + _asm call edx + + GameSetLocalPlayerWeaponSkills(); + } + else + { + _asm push unnamed_10151618 + _asm mov ecx, unnamed_101515F4 + _asm mov edx, 0x688810 + _asm call edx + } + + _asm retn 4 +} + +//----------------------------------------------------------- + +DWORD unnamed_10150C10; +PED_TYPE *unnamed_101516FC; +DWORD unnamed_10150CE0; +DWORD unnamed_1015072C; +DWORD unnamed_10150740; +DWORD unnamed_101514BC; + +// .text:100A4670 : the local player only keeps the game behaviour while the flag is set +NUDE FUNC_100A4670() +{ + _asm mov eax, [esp] + _asm mov unnamed_10150C10, eax + _asm mov eax, [esp+4] + _asm mov unnamed_101516FC, eax + _asm mov eax, [esp+8] + _asm mov unnamed_10150CE0, eax + _asm mov eax, [esp+12] + _asm mov unnamed_1015072C, eax + _asm mov eax, [esp+16] + _asm mov unnamed_10150740, eax + _asm mov eax, [esp+20] + _asm mov unnamed_101514BC, eax + _asm pushad + + if(unnamed_101516FC == GamePool_FindPlayerPed() && pNetGame && + pNetGame->GetPlayerPool()->GetLocalPlayer()->GetPlayerPed()->FUNC_100ACEF0()) + { + _asm popad + _asm retn 0x14 + } + + _asm popad + _asm mov eax, 0x6A6AE0 + _asm jmp eax +} + +//----------------------------------------------------------- + +DWORD unnamed_10150A3C; +DWORD unnamed_101514B4; +DWORD unnamed_101509D4; +DWORD unnamed_101514D0; + +// .text:100A4E20 +NUDE FUNC_100A4E20() +{ + _asm mov ebx, [esp+4] + _asm mov unnamed_1015174C, ebx + _asm mov ebx, [esp+8] + _asm mov unnamed_101506CC, ebx + _asm mov ebx, [esp+12] + _asm mov unnamed_1015097C, ebx + _asm mov ebx, [esp+16] + _asm mov unnamed_10150A3C, ebx + _asm mov ebx, [esp+20] + _asm mov unnamed_101514B4, ebx + _asm mov ebx, [esp+24] + _asm mov unnamed_101509D4, ebx + _asm mov ebx, [esp+28] + _asm mov unnamed_101514D0, ebx + _asm pushad + + if(pGame && pGame->FindPlayerPed()) + pGame->FindPlayerPed()->FUNC_100AFC70(&unnamed_10150BA8, &unnamed_10150968); + + _asm popad + _asm mov ebx, 0x73FB10 + _asm jmp ebx +} + +//----------------------------------------------------------- + +BYTE unnamed_1015181C; +void *unnamed_10151820; + +int FUNC_100B1E40(int a1, void *a2, size_t a3); // .text:100B1E40, lives in another translation unit +void FUNC_100B6AD0(void *a1, size_t a2, int a3); // .text:100B6AD0, lives in another translation unit + +// .text:100A5F30 : reads the stream into a scratch block first when the flag is set +int FUNC_100A5F30(int a1, size_t a2, int a3) +{ + DWORD dwRet = 0; + + if(unnamed_1015181C) + { + unnamed_10151820 = calloc(1, a2); + FUNC_100B1E40(a1, unnamed_10151820, a2); + FUNC_100B6AD0(unnamed_10151820, a2, a3); + + if(unnamed_10151820) + { + free(unnamed_10151820); + unnamed_10151820 = NULL; + } + + return a1; + } + + _asm push a3 + _asm push a2 + _asm push a1 + _asm mov eax, 0x6F9FD0 + _asm call eax + _asm mov dwRet, eax + _asm pop eax + _asm pop eax + _asm pop eax + + return dwRet; +} + +//----------------------------------------------------------- + +DWORD unnamed_101506B0; +DWORD unnamed_101514CC; + +// .text:100A2FC0 : runs the game call with the remote player's weapon skills in place +NUDE FUNC_100A2FC0() +{ + _asm mov unnamed_101515F4, ecx + _asm mov eax, [esp+4] + _asm mov unnamed_10151618, eax + _asm mov eax, [esp+8] + _asm mov unnamed_101506B0, eax + _asm mov eax, [esp+12] + _asm mov unnamed_101514CC, eax + + unnamed_10151614 = *pbyteCurrentPlayer; + unnamed_1015161C = FindPlayerNumFromPedPtr(unnamed_10151618); + + if(unnamed_10151618 && unnamed_1015161C && !unnamed_10151614) + { + GameStoreLocalPlayerWeaponSkills(); + GameSetRemotePlayerWeaponSkills(unnamed_1015161C); + + _asm push unnamed_101514CC + _asm push unnamed_101506B0 + _asm push unnamed_10151618 + _asm mov ecx, unnamed_101515F4 + _asm mov edx, 0x6857E0 + _asm call edx + + GameSetLocalPlayerWeaponSkills(); + } + else + { + _asm push unnamed_101514CC + _asm push unnamed_101506B0 + _asm push unnamed_10151618 + _asm mov ecx, unnamed_101515F4 + _asm mov edx, 0x6857E0 + _asm call edx + } + + _asm retn 0xC +} + +//----------------------------------------------------------- + +DWORD unnamed_10151700; +CVehiclePool *unnamed_10151704; +CVehicle *unnamed_10151708; +WORD unnamed_10113AE4; + +// .text:100A46F0 : mirrors our own siren state into the game's vehicle flags +NUDE FUNC_100A46F0() +{ + _asm mov unnamed_10151700, esi + _asm pushad + + if(pNetGame) + { + unnamed_10151704 = pNetGame->GetVehiclePool(); + unnamed_10113AE4 = unnamed_10151704->FUNC_1001EB90(unnamed_10151700); + + if(unnamed_10113AE4 != 0xFFFF) + { + unnamed_10151708 = (CVehicle *)unnamed_10151704->FUNC_10001120(unnamed_10113AE4); + + if(unnamed_10151708) + { + if(unnamed_10151708->FUNC_100B8240()) + *(BYTE *)(unnamed_10151700 + 0x428) |= 0x40; + else + *(BYTE *)(unnamed_10151700 + 0x428) &= 0xBF; + } + } + } + + _asm popad + _asm mov eax, 0x6D55C0 + _asm jmp eax +} + +//----------------------------------------------------------- + +//----------------------------------------------------------- + +DWORD unnamed_10151770; +DWORD unnamed_10151774; +DWORD unnamed_101514B0; +DWORD unnamed_10151778; +float unnamed_1015094C[3]; + +// the triple forced into 0xB76870 while the game runs its own check +float unnamed_10113AF4[3] = {0.0f, 0.0f, 15.0f}; +// original code at 0x5543C0, and the `jz` at 0x5543DC +BYTE unnamed_10113B00[22] = {0x8B,0xCE,0xE8,0x79,0x01,0xFE,0xFF,0x84,0xC0,0x74,0xB5, + 0x8B,0xCE,0xE8,0x0E,0xB7,0x1C,0x00,0x84,0xC0,0x75,0xAA}; +WORD unnamed_10113B18 = 0x4774; + +// .text:100A4F60 : only one entity gets the patched check +NUDE FUNC_100A4F60() +{ + __asm + { + mov eax, [esp+4] + mov edx, unnamed_10151770 + cmp eax, edx + jz runHook + mov edx, 0x554230 + jmp edx + +runHook: + mov edx, [esp+4] + mov unnamed_10151774, edx + mov edx, [esp+8] + mov unnamed_101514B0, edx + pushad + } + + memcpy(unnamed_1015094C, (void *)0xB76870, 12); + memcpy((void *)0xB76870, unnamed_10113AF4, 12); + + UnFuck(0x5543C0, 0x20); + memset((void *)0x5543C0, 0x90, 22); + memset((void *)0x5543DC, 0x90, 2); + + __asm + { + push unnamed_101514B0 + push unnamed_10151774 + mov edx, 0x554230 + call edx + mov unnamed_10151778, eax + pop edx + pop edx + } + + memcpy((void *)0xB76870, unnamed_1015094C, 12); + memcpy((void *)0x5543C0, unnamed_10113B00, 22); + *(WORD *)0x5543DC = unnamed_10113B18; + + __asm + { + popad + mov eax, unnamed_10151778 + retn + } +} + +//----------------------------------------------------------- + +DWORD unnamed_10150974; +DWORD unnamed_101514C4; +DWORD unnamed_101514C0; +DWORD unnamed_101506B4; +DWORD unnamed_10150A94; +DWORD unnamed_101514A0; +VEHICLE_TYPE *unnamed_101514E8; +MATRIX4X4 unnamed_101509E0; +MATRIX4X4 unnamed_10150C40; +DWORD unnamed_10150CDC; + +void FUNC_100B5790(MATRIX4X4 *pMatrix, int iAxis, float fAngle); // .text:100B5790, lives in another translation unit + +// .text:100A47B0 : aims the drive by camera along the vehicle's own axes +NUDE FUNC_100A47B0() +{ + __asm + { + mov unnamed_10150974, ecx + mov eax, [esp] + mov unnamed_101514C4, eax + mov eax, [esp+4] + mov unnamed_101514C0, eax + mov eax, [esp+8] + mov unnamed_101506B4, eax + mov eax, [esp+12] + mov unnamed_10150A94, eax + mov eax, [esp+16] + mov unnamed_101514A0, eax + pushad + + push unnamed_101514A0 + push unnamed_10150A94 + push unnamed_101506B4 + push unnamed_101514C0 + mov ecx, unnamed_10150974 + mov edx, 0x521500 + call edx + } + + pGame->FindPlayerPed()->FUNC_100AEEE0(&unnamed_101509E0); + + if(pGame->FindPlayerPed()->IsInVehicle()) + { + unnamed_101514E8 = pGame->FindPlayerPed()->GetGtaVehicle(); + + if(unnamed_101514E8) + { + memcpy(&unnamed_10150C40, unnamed_101514E8->entity.mat, sizeof(MATRIX4X4)); + FUNC_100B5790(&unnamed_10150C40, 2, 90.0f); + FUNC_100B5790(&unnamed_10150C40, 0, 90.0f); + + unnamed_10150CDC = (DWORD)GameGetInternalAim(); + *(DWORD *)unnamed_10150CDC = *(DWORD *)&unnamed_10150C40.right.X; + *(float *)(unnamed_10150CDC + 4) = unnamed_10150C40.right.Y; + *(float *)(unnamed_10150CDC + 8) = unnamed_10150C40.right.Z; + *(float *)(unnamed_10150CDC + 0x24) = unnamed_10150C40.up.X; + *(float *)(unnamed_10150CDC + 0x28) = unnamed_10150C40.up.Y; + *(float *)(unnamed_10150CDC + 0x2C) = unnamed_10150C40.up.Z; + } + } + + *(float *)(unnamed_10150974 + 0x19C) = unnamed_101509E0.pos.X; + *(float *)(unnamed_10150974 + 0x1A0) = unnamed_101509E0.pos.Y; + *(float *)(unnamed_10150974 + 0x1A4) = unnamed_101509E0.pos.Z; + + __asm + { + popad + retn 0x10 + } +} + +//----------------------------------------------------------- +// trampolines that only snapshot the caller's arguments before handing the +// call on to GTA + +DWORD unnamed_10150A20; +DWORD unnamed_10151730; +DWORD unnamed_10150C14; +DWORD unnamed_101515F8; +DWORD unnamed_101514AC; + +// .text:100A4410 +NUDE FUNC_100A4410() +{ + _asm mov eax, [esp] + _asm mov unnamed_10150A20, eax + _asm pushad + _asm popad + _asm mov eax, 0x4C95C0 + _asm jmp eax +} + +//----------------------------------------------------------- + +// .text:100A4B40 +NUDE FUNC_100A4B40() +{ + _asm mov eax, [esp] + _asm mov unnamed_1015172C, eax + _asm mov unnamed_10151730, ecx + _asm pushad + _asm popad + _asm mov eax, 0x5A2BD0 + _asm jmp eax +} + +//----------------------------------------------------------- + +// .text:100A4920 +NUDE FUNC_100A4920() +{ + _asm mov eax, [esp+4] + _asm mov unnamed_10150C14, eax + _asm mov eax, [esp+8] + _asm mov unnamed_101515F8, eax + _asm mov eax, [esp+12] + _asm mov unnamed_101514AC, eax + _asm pushad + _asm popad + _asm mov eax, 0x40C6B0 + _asm jmp eax +} + +//----------------------------------------------------------- + +// .text:100A4F50 +NUDE FUNC_100A4F50() +{ + _asm pushad + _asm popad + _asm mov eax, 0x61E3F0 + _asm jmp eax +} + +//----------------------------------------------------------- + +// .text:100A4F30 : only let the game run its own handler while we are not connected +NUDE FUNC_100A4F30() +{ + if(pGame->sub_100A0920()) + { + _asm retn + } + + _asm mov edx, 0x69EFC0 + _asm jmp edx +} + +//----------------------------------------------------------- + +// .text:100A4EB0 +NUDE FUNC_100A4EB0() +{ + _asm + { + mov eax, [esp+4] + push eax + mov eax, 0x5618D0 + call eax + pop edx + pushad + } + + if(pGame) pGame->sub_100A1C10(); + + _asm + { + popad + retn + } +} + +//----------------------------------------------------------- + +// .text:100A5E20 +NUDE FUNC_100A5E20() +{ + _asm mov edx, 0x72B850 + _asm call edx + _asm retn +} + +//----------------------------------------------------------- + +inline void InstallMethodHook( DWORD dwInstallAddress, DWORD dwHookFunction ) { DWORD oldProt, oldProt2; @@ -1153,7 +3983,7 @@ void InstallHook( DWORD dwInstallAddress, //----------------------------------------------------------- -void InstallCallHook(DWORD dwInstallAddress, DWORD dwHookFunction, BYTE byteJumpCode = 0xE8) +inline void InstallCallHook(DWORD dwInstallAddress, DWORD dwHookFunction, BYTE byteJumpCode = 0xE8) { DWORD oldProt, oldProt2; DWORD disp = dwHookFunction - (dwInstallAddress + 5); diff --git a/saco/game/patches.cpp b/saco/game/patches.cpp index ceb3e21..8708264 100644 --- a/saco/game/patches.cpp +++ b/saco/game/patches.cpp @@ -49,8 +49,17 @@ void SetIPLs(int iIPLs, int iGtaVersion) patch(0x156495A,bIPLs); patch(0x156115C,bIPLs); } - patch(0x40619B,bIPLs); - patch(0x405C3D,bIPLs); + + // the last two are _patch spelled out, not a call to it + DWORD dwProtect[2]; + + VirtualProtect((PVOID)0x40619B,sizeof(bIPLs),PAGE_EXECUTE_READWRITE,&dwProtect[0]); + memcpy((PVOID)0x40619B,bIPLs,sizeof(bIPLs)); + VirtualProtect((PVOID)0x40619B,sizeof(bIPLs),dwProtect[0],&dwProtect[1]); + + VirtualProtect((PVOID)0x405C3D,sizeof(bIPLs),PAGE_EXECUTE_READWRITE,&dwProtect[0]); + memcpy((PVOID)0x405C3D,bIPLs,sizeof(bIPLs)); + VirtualProtect((PVOID)0x405C3D,sizeof(bIPLs),dwProtect[0],&dwProtect[1]); } } @@ -130,7 +139,8 @@ void ApplyNewColStoreSizePatch(DWORD dwCount) int x=0; while(x!=7) { - UnFuck(dwPatchAddrColStoreSize[x] + 2, 4); + DWORD oldProt2; + VirtualProtect((PVOID)(dwPatchAddrColStoreSize[x] + 2),4,PAGE_EXECUTE_READWRITE,&oldProt2); *(PDWORD)(dwPatchAddrColStoreSize[x] + 2) = 44 * dwCount; x++; } @@ -473,6 +483,7 @@ PED_MODEL PedModelsMemory[319]; void RelocatePedsListHack() { + DWORD oldProt; BYTE *aPedsListMemory = (BYTE*)&PedModelsMemory[0]; // Init the mem @@ -484,7 +495,7 @@ void RelocatePedsListHack() } // Patch the GetPedsModelInfo to use us // instead of the gta_sa.exe mem. - UnFuck(0x4C67AD,4); + VirtualProtect((PVOID)0x4C67AD,4,PAGE_EXECUTE_READWRITE,&oldProt); *(DWORD *)0x4C67AD = (DWORD)aPedsListMemory; } diff --git a/saco/game/playerped.cpp b/saco/game/playerped.cpp index 948f883..23cb75a 100644 --- a/saco/game/playerped.cpp +++ b/saco/game/playerped.cpp @@ -16,6 +16,19 @@ extern BOOL bIgnoreNextEntry; extern BOOL bIgnoreNextExit; extern BYTE *pbyteCurrentPlayer; +extern int iGtaVersion; + +// all three live in the translation unit at 0x100A4xxx, which also reads them +extern int VAR_10151658; +extern int VAR_1015165C; +extern float VAR_10151660; + +// .text:1009CFD0, lives in another translation unit; it returns -1 when the lookup +// fails, so the return has to be signed +short FUNC_1009CFD0(int a1); + +typedef char CPlayerPed_size_probe[sizeof(CPlayerPed) == 0x32D ? 1 : -1]; +typedef char CPlayerPed_2BD_probe[offsetof(CPlayerPed, field_2BD) == 0x2BD ? 1 : -1]; //----------------------------------------------------------- // Used for instancing the local player. @@ -35,7 +48,7 @@ CPlayerPed::CPlayerPed() field_2AC = 1; m_dwArrow = 0; field_2B9 = 0; - m_iPissingState = 0; + field_2F2 = 0; m_iDanceState = 0; field_2DE = 0; field_2E2 = 0; @@ -132,10 +145,9 @@ void CPlayerPed::SetKeys(WORD wKeys, WORD lrAnalog, WORD udAnalog) pPlayerKeys->wKeys1[4] = 0x00; pPlayerKeys->wKeys2[4] = 0x00; } - wKeys >>= 1; // 1 // CROUCHING TOGGLE (18) - if(IsInVehicle() && wKeys & 1) { + if((m_pPed && IN_VEHICLE(m_pPed)) && (wKeys & 2)) { if(pPlayerKeys->wKeys1[18]) { pPlayerKeys->wKeys1[18] = 0xFF; pPlayerKeys->wKeys2[18] = 0xFF; @@ -147,10 +159,9 @@ void CPlayerPed::SetKeys(WORD wKeys, WORD lrAnalog, WORD udAnalog) pPlayerKeys->wKeys1[18] = 0x00; pPlayerKeys->wKeys2[18] = 0x00; } - wKeys >>= 1; // 2 // FIRING (17) - if(wKeys & 1) { + if(wKeys & 4) { if(pPlayerKeys->wKeys1[17]) { pPlayerKeys->wKeys1[17] = 0xFF; pPlayerKeys->wKeys2[17] = 0xFF; @@ -162,10 +173,9 @@ void CPlayerPed::SetKeys(WORD wKeys, WORD lrAnalog, WORD udAnalog) pPlayerKeys->wKeys1[17] = 0x00; pPlayerKeys->wKeys2[17] = 0x00; } - wKeys >>= 1; // 3 // SPRINT (16) - if(wKeys & 1) { + if(wKeys & 8) { if(pPlayerKeys->wKeys1[16]) { pPlayerKeys->wKeys1[16] = 0xFF; pPlayerKeys->wKeys2[16] = 0xFF; @@ -177,11 +187,12 @@ void CPlayerPed::SetKeys(WORD wKeys, WORD lrAnalog, WORD udAnalog) pPlayerKeys->wKeys1[16] = 0x00; pPlayerKeys->wKeys2[16] = 0x00; } - wKeys >>= 1; // 4 // SECONDARY ONFOOT ATTACK (15) - if( (wKeys & 1) && !IsInJetpackMode() ) { + if( (wKeys & 0x10) && + (!m_pPed || IN_VEHICLE(m_pPed) || m_pPed->Tasks->pdwJumpJetPack == NULL || + m_pPed->Tasks->pdwJumpJetPack[0] != 0x8705C4) ) { if(pPlayerKeys->wKeys1[15]) { pPlayerKeys->wKeys1[15] = 0xFF; pPlayerKeys->wKeys2[15] = 0xFF; @@ -193,10 +204,9 @@ void CPlayerPed::SetKeys(WORD wKeys, WORD lrAnalog, WORD udAnalog) pPlayerKeys->wKeys1[15] = 0x00; pPlayerKeys->wKeys2[15] = 0x00; } - wKeys >>= 1; // 5 // JUMP (14) - if(wKeys & 1) { + if(wKeys & 0x20) { if(pPlayerKeys->wKeys1[14]) { pPlayerKeys->wKeys1[14] = 0xFF; pPlayerKeys->wKeys2[14] = 0xFF; @@ -208,10 +218,9 @@ void CPlayerPed::SetKeys(WORD wKeys, WORD lrAnalog, WORD udAnalog) pPlayerKeys->wKeys1[14] = 0x00; pPlayerKeys->wKeys2[14] = 0x00; } - wKeys >>= 1; // 6 // INCAR LOOK RIGHT (7) - if(wKeys & 1) { + if(wKeys & 0x40) { if(pPlayerKeys->wKeys1[7]) { pPlayerKeys->wKeys1[7] = 0xFF; pPlayerKeys->wKeys2[7] = 0xFF; @@ -223,10 +232,9 @@ void CPlayerPed::SetKeys(WORD wKeys, WORD lrAnalog, WORD udAnalog) pPlayerKeys->wKeys1[7] = 0x00; pPlayerKeys->wKeys2[7] = 0x00; } - wKeys >>= 1; // 7 // INCAR HANDBRAKE / ONFOOT TARGET (6) - if(wKeys & 1) { + if(wKeys & 0x80) { if(pPlayerKeys->wKeys1[6]) { pPlayerKeys->wKeys1[6] = 0xFF; pPlayerKeys->wKeys2[6] = 0xFF; @@ -238,10 +246,9 @@ void CPlayerPed::SetKeys(WORD wKeys, WORD lrAnalog, WORD udAnalog) pPlayerKeys->wKeys1[6] = 0x00; pPlayerKeys->wKeys2[6] = 0x00; } - wKeys >>= 1; // 8 // INCAR LOOK LEFT (5) - if(wKeys & 1) { + if(wKeys & 0x100) { if(pPlayerKeys->wKeys1[5]) { pPlayerKeys->wKeys1[5] = 0xFF; pPlayerKeys->wKeys2[5] = 0xFF; @@ -253,10 +260,9 @@ void CPlayerPed::SetKeys(WORD wKeys, WORD lrAnalog, WORD udAnalog) pPlayerKeys->wKeys1[5] = 0x00; pPlayerKeys->wKeys2[5] = 0x00; } - wKeys >>= 1; // 9 // SUBMISSION (19) - if(wKeys & 1) { + if(wKeys & 0x200) { if(pPlayerKeys->wKeys1[19] == 0xFF) { pPlayerKeys->wKeys1[19] = 0xFF; pPlayerKeys->wKeys2[19] = 0xFF; @@ -268,10 +274,9 @@ void CPlayerPed::SetKeys(WORD wKeys, WORD lrAnalog, WORD udAnalog) pPlayerKeys->wKeys1[19] = 0x00; pPlayerKeys->wKeys2[19] = 0x00; } - wKeys >>= 1; // 10 // WALKING (21) - if(wKeys & 1) { + if(wKeys & 0x400) { if(pPlayerKeys->wKeys1[21] == 0xFF) { pPlayerKeys->wKeys1[21] = 0xFF; pPlayerKeys->wKeys2[21] = 0xFF; @@ -283,10 +288,9 @@ void CPlayerPed::SetKeys(WORD wKeys, WORD lrAnalog, WORD udAnalog) pPlayerKeys->wKeys1[21] = 0x00; pPlayerKeys->wKeys2[21] = 0x00; } - wKeys >>= 1; // 11 // ANALOG2 D/U - BYTE byteVal = wKeys & 3; + BYTE byteVal = (wKeys >> 11) & 3; if(byteVal==2) { pPlayerKeys->wKeys1[3] = (WORD)(128); @@ -298,10 +302,8 @@ void CPlayerPed::SetKeys(WORD wKeys, WORD lrAnalog, WORD udAnalog) pPlayerKeys->wKeys1[3] = 0; pPlayerKeys->wKeys2[3] = 0; } - wKeys >>= 2; // 12-13 - // ANALOG2 L/R - byteVal = wKeys & 3; + byteVal = (BYTE)((wKeys >> 11) >> 2) & 3; if(byteVal==2) { pPlayerKeys->wKeys1[2] = (WORD)(128); @@ -346,25 +348,30 @@ WORD CPlayerPed::GetKeys(WORD * lrAnalog, WORD * udAnalog) if(pInternalKeys->wKeys1[19]) wRet |= 1; // submission wRet <<= 1; - if(IsInVehicle() && pInternalKeys->wKeys1[5]) wRet |= 1; // incar look left + if((m_pPed && IN_VEHICLE(m_pPed)) && pInternalKeys->wKeys1[5]) wRet |= 1; // incar look left wRet <<= 1; if(pInternalKeys->wKeys1[6]) wRet |= 1; // incar handbrake / target wRet <<= 1; - if(IsInVehicle() && pInternalKeys->wKeys1[7]) wRet |= 1; // incar look right + if((m_pPed && IN_VEHICLE(m_pPed)) && pInternalKeys->wKeys1[7]) wRet |= 1; // incar look right wRet <<= 1; if(pInternalKeys->wKeys1[14]) wRet |= 1; // jump wRet <<= 1; - if(!IsInJetpackMode() && pInternalKeys->wKeys1[15]) wRet |= 1; // secondary onfoot attack + if((!m_pPed || IN_VEHICLE(m_pPed) || m_pPed->Tasks->pdwJumpJetPack == NULL || + m_pPed->Tasks->pdwJumpJetPack[0] != 0x8705C4) && pInternalKeys->wKeys1[15]) wRet |= 1; // secondary onfoot attack wRet <<= 1; if(pInternalKeys->wKeys1[16]) wRet |= 1; // sprint wRet <<= 1; - if(HasAmmoForCurrentWeapon() && pInternalKeys->wKeys1[17]) wRet |= 1; // fire + WEAPON_SLOT_TYPE *pFireSlot; + if((!m_pPed || (pFireSlot = &m_pPed->WeaponSlots[m_pPed->byteCurWeaponSlot]) == NULL || + pFireSlot->dwType <= WEAPON_CANE || + pFireSlot->dwType == WEAPON_PARACHUTE || + pFireSlot->dwAmmo) && pInternalKeys->wKeys1[17]) wRet |= 1; // fire wRet <<= 1; if(pInternalKeys->wKeys1[18]) wRet |= 1; // crouch @@ -419,20 +426,16 @@ BYTE CPlayerPed::GetCurrentWeapon() //----------------------------------------------------------- -int CPlayerPed::GetCurrentVehicleID() -{ - if(!m_pPed) return 0; - - VEHICLE_TYPE *pVehicle = (VEHICLE_TYPE *)m_pPed->pVehicle; - return GamePool_Vehicle_GetIndex(pVehicle); -} //----------------------------------------------------------- // Shows the normal marker void CPlayerPed::ShowMarker(int iMarkerColorID) { - if (m_dwArrow) HideMarker(); + if (m_dwArrow) { + ScriptCommand(&disable_marker, m_dwArrow); + m_dwArrow = 0; + } ScriptCommand(&create_arrow_above_actor, m_dwGTAId, &m_dwArrow); ScriptCommand(&set_marker_color, m_dwArrow, iMarkerColorID); ScriptCommand(&show_on_radar2, m_dwArrow, 2); @@ -440,6 +443,26 @@ void CPlayerPed::ShowMarker(int iMarkerColorID) //----------------------------------------------------------- +void CPlayerPed::SetInterior(BYTE byteInterior, BOOL bRefresh) +{ + MATRIX4X4 mat; + + if(!m_pPed) return; + + if(!m_bytePlayerNumber) { + ScriptCommand(&select_interior, byteInterior); + ScriptCommand(&link_actor_to_interior, m_dwGTAId, byteInterior); + if(bRefresh) { + GetMatrix(&mat); + ScriptCommand(&refresh_streaming_at, mat.pos.X, mat.pos.Y); + } + } else { + ScriptCommand(&link_actor_to_interior, m_dwGTAId, byteInterior); + } +} + +//----------------------------------------------------------- + void CPlayerPed::HideMarker() { if (m_dwArrow) ScriptCommand(&disable_marker, m_dwArrow); @@ -586,8 +609,8 @@ void CPlayerPed::ForceTargetRotation(float fRotation) if(!m_pPed) return; if(!GamePool_Ped_GetAt(m_dwGTAId)) return; - m_pPed->fRotation1 = DegToRad(fRotation); m_pPed->fRotation2 = DegToRad(fRotation); + m_pPed->fRotation1 = DegToRad(fRotation); ScriptCommand(&set_actor_z_angle,m_dwGTAId,fRotation); } @@ -735,7 +758,7 @@ WEAPON_SLOT_TYPE * CPlayerPed::GetCurrentWeaponSlot() BOOL CPlayerPed::HasAmmoForCurrentWeapon() { if(m_pPed) { - WEAPON_SLOT_TYPE * WeaponSlot = GetCurrentWeaponSlot(); + WEAPON_SLOT_TYPE * WeaponSlot = &m_pPed->WeaponSlots[m_pPed->byteCurWeaponSlot]; if(!WeaponSlot) return TRUE; @@ -772,7 +795,7 @@ int CPlayerPed::GetVehicleSeatID() { VEHICLE_TYPE *pVehicle; - if( GetActionTrigger() == ACTION_INCAR && (pVehicle = (VEHICLE_TYPE *)m_pPed->pVehicle) != 0 ) { + if( (BYTE)m_pPed->dwAction == ACTION_INCAR && (pVehicle = (VEHICLE_TYPE *)m_pPed->pVehicle) != 0 ) { if(pVehicle->pDriver == m_pPed) return 0; if(pVehicle->pPassengers[0] == m_pPed) return 1; if(pVehicle->pPassengers[1] == m_pPed) return 2; @@ -795,7 +818,7 @@ void CPlayerPed::PutDirectlyInVehicle(int iVehicleID, int iSeat) if(!GamePool_Ped_GetAt(m_dwGTAId)) return; if(GetCurrentWeapon() == WEAPON_PARACHUTE) { - SetArmedWeapon(0); + SetArmedWeapon(0, false); } VEHICLE_TYPE *pVehicle = GamePool_Vehicle_GetAt(iVehicleID); @@ -826,10 +849,11 @@ void CPlayerPed::PutDirectlyInVehicle(int iVehicleID, int iSeat) if(pNetGame) { CVehiclePool* pVehiclePool = pNetGame->GetVehiclePool(); - VEHICLEID TrainVehicleId = pVehiclePool->FindIDFromGtaPtr(pVehicle); - if(TrainVehicleId == INVALID_VEHICLE_ID || TrainVehicleId > MAX_VEHICLES) return; + VEHICLEID TrainVehicleId = pVehiclePool->FUNC_1001EB90((int)pVehicle); + // 0xFFFF is the vehicle pool not-found sentinel + if(TrainVehicleId == 0xFFFF || TrainVehicleId > MAX_VEHICLES) return; - CVehicle* pTrain = pVehiclePool->GetAt(TrainVehicleId); + CVehicle* pTrain = (CVehicle *)pVehiclePool->FUNC_10001120(TrainVehicleId); if ( pTrain && pTrain->IsATrainPart() && m_pPed == GamePool_FindPlayerPed() ) { ScriptCommand(&camera_on_vehicle, pTrain->m_dwGTAId, 3, 2); } @@ -848,7 +872,7 @@ void CPlayerPed::EnterVehicle(int iVehicleID, BOOL bPassenger) bIgnoreNextEntry = TRUE; if(GetCurrentWeapon() == WEAPON_PARACHUTE) { - SetArmedWeapon(0); + SetArmedWeapon(0, false); } if(bPassenger) { @@ -871,7 +895,6 @@ void CPlayerPed::ExitCurrentVehicle() if(!GamePool_Ped_GetAt(m_dwGTAId)) return; if(IN_VEHICLE(m_pPed)) { if(GetCurrentVehicleID()) { - int iVehicleID; VEHICLE_TYPE *pVehicle = GamePool_Vehicle_GetAt(GetCurrentVehicleID()); if(pVehicle) { if( pVehicle->entity.nModelIndex != TRAIN_PASSENGER && @@ -908,7 +931,8 @@ void CPlayerPed::TogglePlayerControllable(int iControllable) } else { ScriptCommand(&toggle_player_controllable,m_bytePlayerNumber,1); ScriptCommand(&lock_actor,m_dwGTAId,0); - if(!IsInVehicle()) { + // retail writes the IsInVehicle test out inline + if(!(m_pPed && IN_VEHICLE(m_pPed))) { GetMatrix(&mat); TeleportTo(mat.pos.X,mat.pos.Y,mat.pos.Z); } @@ -1054,7 +1078,8 @@ BOOL CPlayerPed::IsInJetpackMode() void CPlayerPed::StartGoggles() { - if (HasGoggles()) return; + // retail writes the HasGoggles test out inline + if (m_pPed && (m_pPed->dwActiveVision != 0 || m_bGoggleState)) return; if (FindWeaponSlot( 44 ) == NULL && FindWeaponSlot( 45 ) == NULL) GiveWeapon( 44, 1 ); // Prevents crashing due to lack of animations. @@ -1067,13 +1092,16 @@ void CPlayerPed::StartGoggles() void CPlayerPed::StopGoggles() { - if (!m_pPed || !HasGoggles()) return; + if (!m_pPed) return; - m_bGoggleState = FALSE; - DWORD dwPedPointer = (DWORD)m_pPed; - _asm mov ecx, dwPedPointer - _asm mov eax, 0x5E6010 - _asm call eax + // retail writes the HasGoggles test out inline + if (m_pPed->dwActiveVision != 0 || m_bGoggleState) { + m_bGoggleState = FALSE; + DWORD dwPedPointer = (DWORD)m_pPed; + _asm mov ecx, dwPedPointer + _asm mov eax, 0x5E6010 + _asm call eax + } } //----------------------------------------------------------- @@ -1089,7 +1117,7 @@ BOOL CPlayerPed::HasGoggles() WORD CPlayerPed::GetAmmo() { if(m_pPed) { - WEAPON_SLOT_TYPE * WeaponSlot = GetCurrentWeaponSlot(); + WEAPON_SLOT_TYPE * WeaponSlot = &m_pPed->WeaponSlots[m_pPed->byteCurWeaponSlot]; if(!WeaponSlot) return -1; @@ -1252,6 +1280,306 @@ BOOL CPlayerPed::IsDancing() //----------------------------------------------------------- +void CPlayerPed::SetSkin(int iSkin) +{ + DWORD dwPedPtr = (DWORD)m_pPed; + + if(!GamePool_Ped_GetAt(m_dwGTAId)) return; + + if(!IsValidPedModel(iSkin)) iSkin = 0; + + if(dwPedPtr) { + // the skin swap trips an assert inside the streamer, so ret it out first + UnFuck(0x5A82C0, 1); + *(BYTE *)0x5A82C0 = 0xC3; + + DestroyFollowPedTask(); + SetModelIndex(iSkin); + + _asm mov eax, dwPedPtr + _asm lea ecx, [eax+0x294] + _asm push eax + _asm mov ebx, 0x4E68D0 + _asm call ebx + } +} + +//----------------------------------------------------------- + +CPlayerPed::~CPlayerPed() +{ + Destroy(); +} + +//----------------------------------------------------------- + +// kills the ped after unwinding the jetpack and the attach slots +void CPlayerPed::FUNC_100AFFD0() +{ + MATRIX4X4 mat; + + if(!m_dwGTAId) return; + if(!m_pPed) return; + + if(!IN_VEHICLE(m_pPed)) { + DWORD *pTask = m_pPed->Tasks->pdwJumpJetPack; + if(pTask && pTask[0] == 0x8705C4) StopJetpack(); + } + + FUNC_100AD0F0(); + + GetMatrix(&mat); + TeleportTo(mat.pos.X, mat.pos.Y, mat.pos.Z); + + *(DWORD *)((DWORD)m_pPed + 0x540) = 0; + + *pbyteCurrentPlayer = m_bytePlayerNumber; + ScriptCommand(&kill_actor, m_dwGTAId); + *pbyteCurrentPlayer = 0; +} + +//----------------------------------------------------------- +// only referenced by ProcessDrunkCamera; rate limits the steering drift +DWORD dwLastDrunkTick; + +void CPlayerPed::ProcessDrunkCamera() +{ + float fDrift = 0.0f; + + if(field_2C9 > 2000 && !m_bytePlayerNumber) { + int iSeverity = (int)(field_2C9 * 0.02f); + if(iSeverity > 250) iSeverity = 250; + else if(iSeverity < 5) iSeverity = 0; + + ScriptCommand(&set_player_drunk_visuals, 0, iSeverity); + + if(field_2C9 > 2000 && m_pPed && IN_VEHICLE(m_pPed) && !IsAPassenger()) { + VEHICLE_TYPE *pVehicle = (VEHICLE_TYPE *)m_pPed->pVehicle; + if(pVehicle) { + if(!dwLastDrunkTick || (GetTickCount() - dwLastDrunkTick) > 200) { + int iRand = rand() % 40; + if(iRand >= 20) { + if(iRand > 30) { + if(field_2C9 < 5000) fDrift = 0.012f; + else fDrift = 0.015f; + } else { + fDrift = -0.012f; + if(field_2C9 >= 5000) fDrift = -0.015f; + } + } + + if(FloatOffset(pVehicle->entity.vecMoveSpeed.X, 0.0f) > 0.05f || + FloatOffset(pVehicle->entity.vecMoveSpeed.Y, 0.0f) > 0.05f) + { + pVehicle->entity.vecTurnSpeed.Z += fDrift; + } + + dwLastDrunkTick = GetTickCount(); + } + } + } + + field_2C9--; + return; + } + + if(field_2C9 <= 2000 && !m_bytePlayerNumber) { + if(field_2C9 > 0) field_2C9--; + ScriptCommand(&set_player_drunk_visuals, 0, 0); + } +} + +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100AD0F0() +{ + if(!m_pPed) return; + + DWORD dwPtr = *(DWORD *)((DWORD)m_pPed + 0x730); + if(dwPtr) { + _asm mov ecx, dwPtr + _asm mov eax, 0x5393F0 + _asm call eax + } + + if(IN_VEHICLE(m_pPed)) { + VEHICLE_TYPE *pVehicle = (VEHICLE_TYPE *)m_pPed->pVehicle; + if(pVehicle) { + dwPtr = *(DWORD *)((DWORD)pVehicle + 0x490); + if(dwPtr) { + _asm mov ecx, dwPtr + _asm mov eax, 0x5393F0 + _asm call eax + } + } + } +} + +//----------------------------------------------------------- + +// 2 = entering as driver, 1 = entering as passenger, 0 = not entering +BOOL CPlayerPed::FUNC_100AC5D0() +{ + if(GamePool_Ped_GetAt(m_dwGTAId) && m_pPed && m_pPed->Tasks && + m_pPed->Tasks->pdwJumpJetPack) + { + int iTask = GetTaskTypeFromTask(m_pPed->Tasks->pdwJumpJetPack); + if(iTask == 700 || iTask == 712) return 2; + if(iTask == 701 || iTask == 713) return 1; + } + return 0; +} + +//----------------------------------------------------------- + +// TRUE while the ped is running the leave-car task +BOOL CPlayerPed::FUNC_100AC640() +{ + if(GamePool_Ped_GetAt(m_dwGTAId) && m_pPed && m_pPed->Tasks && + m_pPed->Tasks->pdwJumpJetPack && + GetTaskTypeFromTask(m_pPed->Tasks->pdwJumpJetPack) == 704) return TRUE; + + return FALSE; +} + +//----------------------------------------------------------- + +void CPlayerPed::SetWeaponAmmo(BYTE byteWeapon, WORD wAmmo) +{ + if(!m_pPed) return; + + WEAPON_SLOT_TYPE *pSlot = FindWeaponSlot(byteWeapon); + if(pSlot) pSlot->dwAmmo = wAmmo; +} + +//----------------------------------------------------------- + +void CPlayerPed::Destroy() +{ + DWORD dwPedPtr = (DWORD)m_pPed; + + GameResetPlayerKeys(m_bytePlayerNumber); + SetPlayerPedPtrRecord(m_bytePlayerNumber, 0); + + if(m_pPed && GamePool_Ped_GetAt(m_dwGTAId) && m_pPed->entity.vtable != 0x863C40) { + if(field_2B9) { + ScriptCommand(&disassociate_object, field_2B9, 0.0f, 0.0f, 0.0f, 0); + ScriptCommand(&destroy_object_with_fade, field_2B9); + field_2B9 = 0; + } + + if(HasObjectAttached()) RemoveAllAttachedObjects(); + if(field_2C1) StopCarrying(); + + if(IN_VEHICLE(m_pPed)) RemoveFromVehicleAndPutAt(100.0f, 100.0f, 10.0f); + + *(DWORD *)(*(DWORD *)(dwPedPtr + 1152) + 76) = 0; + + _asm mov ecx, dwPedPtr + _asm push 1 + _asm mov eax, [ecx] + _asm call dword ptr [eax] + + m_pPed = NULL; + m_pEntity = NULL; + } else { + m_pPed = NULL; + m_pEntity = NULL; + m_dwGTAId = 0; + } +} + +//----------------------------------------------------------- + +void CPlayerPed::RemoveAllAttachedObjects() +{ + // retail spells the RemoveAttachedObject body out here + for(int x = 0; x < 10; x++) { + if(x >= 0 && x < 10 && field_4C[x] == 1) { + CEntity *pObject = (CEntity *)field_27C[x]; + if(pObject) { + delete pObject; + field_27C[x] = 0; + } + memset(&field_74[x], 0, sizeof(struc_97)); + field_4C[x] = 0; + } + } +} + +//----------------------------------------------------------- + +void CPlayerPed::StopCarrying() +{ + MATRIX4X4 mat; + + if(field_2C5) { + ScriptCommand(&task_pick_up_object, m_dwGTAId, field_2C5, 0.0f, 0.0f, 0.0f, 6, 16, "NULL", "NULL", 0); + field_2C5 = 0; + } + + GetMatrix(&mat); + TeleportTo(mat.pos.X, mat.pos.Y, mat.pos.Z); + field_2C1 = 0; +} + +//----------------------------------------------------------- + +BOOL CPlayerPed::HasObjectAttached() +{ + for(int x = 0; x < 10; x++) { + if(field_4C[x] == 1) return TRUE; + } + return FALSE; +} + +//----------------------------------------------------------- + +void CPlayerPed::RemoveAttachedObject(int iIndex) +{ + if(iIndex >= 0 && iIndex < 10 && field_4C[iIndex] == 1) { + CEntity *pObject = (CEntity *)field_27C[iIndex]; + if(pObject) { + delete pObject; + field_27C[iIndex] = 0; + } + memset(&field_74[iIndex], 0, sizeof(struc_97)); + field_4C[iIndex] = 0; + } +} + +//----------------------------------------------------------- + +BOOL CPlayerPed::GetAttachedObjectSlotState(int iIndex) +{ + if(iIndex < 0 || iIndex >= 10) return FALSE; + return (field_4C[iIndex] == 1); +} + +//----------------------------------------------------------- +// drunk level, driven from the net layer and read by the blur overlay + +int CPlayerPed::sub_100ADFA0() +{ + return field_2C9; +} + +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100ADFB0(int iLevel) +{ + field_2C9 = iLevel; +} + +//----------------------------------------------------------- + +int CPlayerPed::FUNC_100ADC90() +{ + return field_2C1; +} + +//----------------------------------------------------------- + void CPlayerPed::ProcessMarkers(BOOL bMarkerStreamingEnabled, float fMarkerStreamRadius, BOOL bVisible) { if(!m_pPed) return; @@ -1482,3 +1810,574 @@ VECTOR* CPlayerPed::GetTransformedBonePosition(int iBone, VECTOR *vecOffset) } //----------------------------------------------------------- + +int CPlayerPed::FUNC_100AE260() +{ + return field_2F2; +} + +//----------------------------------------------------------- + +BOOL CPlayerPed::FUNC_100ACE40() +{ + return field_2E2 != 0; +} + +//----------------------------------------------------------- + +BOOL CPlayerPed::FUNC_100ACEF0() +{ + return field_2DE != 0; +} + +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100ACF00(int a1) +{ + field_2DE = a1; + field_2F8 = (a1 != 0); +} + +//----------------------------------------------------------- + +BOOL CPlayerPed::FUNC_100AD860() +{ + if(m_pPed && (m_pPed->dwStateFlags & 3)) return TRUE; + return FALSE; +} + +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100AD880() +{ + if(!m_pPed) return; + + *(DWORD *)((DWORD)m_pPed + 0x764) = 0; + *(DWORD *)((DWORD)m_pPed + 0x760) = 0xFF; +} + +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100AC720() +{ + if(!GamePool_Ped_GetAt(m_dwGTAId)) return; + if(!m_pPed) return; + + m_pPed->dwStateFlags &= ~0x4000000; +} + +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100AEC90() +{ + DWORD dwPedPtr = (DWORD)m_pPed; + if(!dwPedPtr) return; + + _asm mov ecx, dwPedPtr + _asm mov eax, 0x532B20 + _asm call eax +} + +//----------------------------------------------------------- + +int CPlayerPed::FUNC_100AEF60() +{ + if(!m_pPed) return 0; + if(m_pPed->entity.vtable == 0x863C40) return 0; + if(!m_pPed->entity.pdwRenderWare) return 0; + + return *(int *)((DWORD)m_pPed + 0x79C); +} + +//----------------------------------------------------------- + +void FUNC_100AE520() +{ + *(WORD *)0xB7CEDC = 0; +} + +//----------------------------------------------------------- + +int CPlayerPed::FUNC_100AC690() +{ + if(GamePool_Ped_GetAt(m_dwGTAId) && m_pPed) + return (m_pPed->dwStateFlags >> 26) & 1; + + return 0; +} +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100ACE50(int a1) +{ + field_2E2 = a1; + + if(a1) { + field_2F7 = 1; + field_2F6 = 1; + field_2F8 = 1; + } else { + field_2F7 = 0; + field_2F6 = 0; + field_2F8 = 0; + } +} +//----------------------------------------------------------- + +BOOL CPlayerPed::FUNC_100ADA00() +{ + if(!m_pPed) { + return FALSE; + } else { + if(!IsAdded()) return FALSE; + return m_pPed->Tasks->pdwJumpJetPack != 0; + } +} +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100AD3E0() +{ + if(field_2B9) { + ScriptCommand(&disassociate_object, field_2B9, 0.0f, 0.0f, 0.0f, 0); + ScriptCommand(&destroy_object, field_2B9); + field_2B9 = 0; + } +} +//----------------------------------------------------------- + +BOOL CPlayerPed::FUNC_100AD030() +{ + if(m_pPed) { + if(GamePool_Ped_GetAt(m_dwGTAId)) { + if(m_pPed->Tasks->pdwJumpJetPack) { + if(GetTaskTypeFromTask(m_pPed->Tasks->pdwJumpJetPack) == 1022) return TRUE; + } + } + } + return FALSE; +} +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100AD080() +{ + if(!m_pPed) return; + if(!GamePool_Ped_GetAt(m_dwGTAId)) return; + + DWORD dwTask = (DWORD)m_pPed->Tasks->pdwJumpJetPack; + if(!dwTask) return; + + if(GetTaskTypeFromTask((DWORD *)dwTask) != 1022) return; + + _asm mov ecx, dwTask + _asm mov edx, 0x6275E0 + _asm push 1 + _asm call edx + + m_pPed->Tasks->pdwJumpJetPack = 0; +} +//----------------------------------------------------------- + +int CPlayerPed::FUNC_100AE960(int a1) +{ + int iResult; + DWORD dwPedPtr = (DWORD)m_pPed; + + _asm mov edi, dwPedPtr + _asm mov eax, a1 + _asm mov edx, [edi+eax*4+0x488] + _asm mov eax, [edx+0x14] + _asm mov iResult, eax + + return iResult; +} +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100AD8B0(int iWeapon) +{ + if(!m_pPed) return; + + DWORD dwPedPtr = (DWORD)m_pPed; + iWeapon = (int)pGame->GetWeaponInfo(iWeapon, 1); + + _asm mov ebx, iWeapon + _asm mov eax, [ebx+0xC] + _asm push eax + _asm mov ecx, dwPedPtr + _asm mov edx, 0x5E3990 + _asm call edx +} +//----------------------------------------------------------- + +struc_13 * CPlayerPed::FUNC_100AE5D0() +{ + if(m_bytePlayerNumber) return FUNC_100B43D0(m_bytePlayerNumber); + + return NULL; +} +//----------------------------------------------------------- + +DWORD CPlayerPed::FUNC_100AEBB0(int a1) +{ + DWORD dwClump; + DWORD dwGetClump; + DWORD dwRenderWare; + + if(!m_pPed) return 0; + if(m_pPed->entity.vtable == 0x863C40) return 0; + if(!m_pPed->entity.pdwRenderWare) return 0; + + dwRenderWare = (DWORD)m_pPed->entity.pdwRenderWare; + dwClump = 0; + dwGetClump = 0x734A40; + + _asm push dwRenderWare + _asm mov edx, dwGetClump + _asm call edx + _asm pop edx + _asm mov dwClump, eax + + DWORD dwAnim = *(DWORD *)(dwClump + 0x20); + + return dwAnim + a1 * *(DWORD *)(dwAnim + 0x24) + 0x4C; +} +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100AE560(int iType, int a2, int a3, int a4) +{ + if(!m_pPed) return; + if(m_pPed->entity.vtable == 0x863C40) return; + if(!m_pPed->entity.pdwRenderWare) return; + + DWORD dwRenderWare = (DWORD)m_pPed->entity.pdwRenderWare; + + if(iType == 1) { + _asm push a3 + _asm push a2 + _asm push dwRenderWare + _asm mov edx, 0x4D3AA0 + _asm call edx + _asm add esp, 0xC + return; + } + + if(iType == 2) { + _asm push a4 + _asm push a3 + _asm push a2 + _asm push dwRenderWare + _asm mov edx, 0x4D4610 + _asm call edx + _asm add esp, 0x10 + } +} +//----------------------------------------------------------- + +DWORD CPlayerPed::FUNC_100AE990() +{ + DWORD dwGetClump; + DWORD dwRenderWare; + DWORD dwResult; + + if(!m_pPed) return 0; + if(m_pPed->entity.vtable == 0x863C40) return 0; + if(!m_pPed->entity.pdwRenderWare) return 0; + + dwRenderWare = (DWORD)m_pPed->entity.pdwRenderWare; + dwGetClump = 0x734A40; + + _asm push dwRenderWare + _asm mov edx, dwGetClump + _asm call edx + _asm pop edx + _asm mov edx, [eax] + _asm mov dwResult, edx + + return dwResult; +} +//----------------------------------------------------------- + +DWORD CPlayerPed::FUNC_100AE9E0() +{ + DWORD dwGetClump; + DWORD dwRenderWare; + DWORD dwResult; + + if(!m_pPed) return 0; + if(m_pPed->entity.vtable == 0x863C40) return 0; + if(!m_pPed->entity.pdwRenderWare) return 0; + + dwRenderWare = (DWORD)m_pPed->entity.pdwRenderWare; + dwGetClump = 0x734A40; + + _asm push dwRenderWare + _asm mov edx, dwGetClump + _asm call edx + _asm pop edx + _asm mov edx, [eax+4] + _asm mov dwResult, edx + + return dwResult; +} +//----------------------------------------------------------- + +DWORD CPlayerPed::FUNC_100AEA30() +{ + DWORD dwGetClump; + DWORD dwRenderWare; + DWORD dwResult; + + if(!m_pPed) return 0; + if(m_pPed->entity.vtable == 0x863C40) return 0; + if(!m_pPed->entity.pdwRenderWare) return 0; + + dwRenderWare = (DWORD)m_pPed->entity.pdwRenderWare; + dwGetClump = 0x734A40; + + _asm push dwRenderWare + _asm mov edx, dwGetClump + _asm call edx + _asm pop edx + _asm mov edx, [eax+8] + _asm mov dwResult, edx + + return dwResult; +} +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100AC6C0() +{ + if(!GamePool_Ped_GetAt(m_dwGTAId)) return; + if(!m_pPed) return; + if(IN_VEHICLE(m_pPed)) return; + if(FUNC_100AC690()) return; + + DWORD dwTasks = (DWORD)m_pPed->Tasks; + if(!dwTasks) return; + + _asm push 0 + _asm mov ecx, dwTasks + _asm mov edx, 0x601230 + _asm call edx +} +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100AE1E0() +{ + MATRIX4X4 mat; + + if(!m_pPed) return; + if(!GamePool_Ped_GetAt(m_dwGTAId)) return; + if(!field_2F2) return; + + if(field_2BD) { + ScriptCommand(&script_0650, field_2BD); + field_2BD = 0; + } + + GetMatrix(&mat); + TeleportTo(mat.pos.X, mat.pos.Y, mat.pos.Z); + + field_2F2 = 0; +} +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100ACE90() +{ + int iWeapon; + + if(!field_2E2) return; + if(!m_pPed) return; + if(!GamePool_Ped_GetAt(m_dwGTAId)) return; + + ScriptCommand(&get_actor_armed_weapon, m_dwGTAId, &iWeapon); + + if((BYTE)iWeapon) SetArmedWeapon(0, 0); +} +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100AEC10(MATRIX4X4 *pMatrix, int iBone) +{ + int iFrame; + DWORD dwPedPtr = (DWORD)m_pPed; + + _asm mov edi, dwPedPtr + _asm mov eax, iBone + _asm mov edx, [edi+eax*4+0x488] + _asm mov eax, [edx+0x14] + _asm mov iFrame, eax + + FUNC_100AEA80(pMatrix, iFrame); +} +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100AEC50(MATRIX4X4 *pMatrix, int iBone) +{ + int iFrame; + DWORD dwPedPtr = (DWORD)m_pPed; + + _asm mov edi, dwPedPtr + _asm mov eax, iBone + _asm mov edx, [edi+eax*4+0x488] + _asm mov eax, [edx+0x14] + _asm mov iFrame, eax + + FUNC_100AEB10(pMatrix, iFrame); +} +//----------------------------------------------------------- + +int CPlayerPed::FUNC_100AD9C0() +{ + if(!m_pPed) return -1; + + return FUNC_100B4100(m_pPed->entity.nModelIndex); +} +//----------------------------------------------------------- + +int CPlayerPed::FUNC_100AD9E0() +{ + if(!m_pPed) return -1; + + return FUNC_100B4180(m_pPed->entity.nModelIndex); +} +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100AF1F0() +{ + DWORD dwPedPtr = (DWORD)m_pPed; + + if(dwPedPtr) { + _asm mov ecx, dwPedPtr + _asm mov eax, 0x532B20 + _asm call eax + } + + FUNC_100AEF90(); +} +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100AE530(int a1, int a2) +{ + if(!m_pPed) return; + + DWORD *pRenderWare = FUNC_1009FA60(); + if(!pRenderWare) return; + + FUNC_100B1DA0(pRenderWare, a1, a2); +} +//----------------------------------------------------------- + +float CPlayerPed::FUNC_100AF220() +{ + VECTOR vec; + int iUnk; + + DWORD dwFrame = FUNC_100AEBB0(0); + if(!dwFrame) return 0.0f; + + vec.X = 0.0f; + vec.Y = 0.0f; + vec.Z = 0.0f; + iUnk = 0; + + FUNC_100B58A0(dwFrame, &vec, &iUnk); + + return 1.0f - vec.Z; +} +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100AEEE0(MATRIX4X4 *pMatrix) +{ + int iFrame; + DWORD dwPedPtr; + int iBone; + + iBone = 2; + dwPedPtr = (DWORD)m_pPed; + + _asm mov edi, dwPedPtr + _asm mov eax, iBone + _asm mov edx, [edi+eax*4+0x488] + _asm mov eax, [edx+0x14] + _asm mov iFrame, eax + + FUNC_100AEA80(pMatrix, iFrame); + + CAMERA_AIM *pAim = GameGetInternalAim(); + + pMatrix->pos.X = pAim->f1x * 0.04f + pMatrix->pos.X; + pMatrix->pos.Y = pAim->f1y * 0.04f + pMatrix->pos.Y; + pMatrix->pos.Z = pMatrix->pos.Z + 0.08f; +} +//----------------------------------------------------------- + +DWORD CPlayerPed::FUNC_100AE790() +{ + if(!m_pPed) return 0; + if(m_pPed->entity.vtable == 0x863C40) return 0; + if(!m_pPed->entity.pdwRenderWare) return 0; + + DWORD *pTask = FUNC_100AE6E0(); + if(pTask) return FUNC_100AE5F0(pTask); + + return FUNC_100AE6A0(); +} +//----------------------------------------------------------- + +void CPlayerPed::FUNC_100AD440() +{ + if(!GamePool_Ped_GetAt(m_dwGTAId)) return; + if(!IsAdded()) return; + if(!field_2B9) return; + if(FUNC_100ABC50() == 46) return; + + ScriptCommand(&disassociate_object, field_2B9, 0.0f, 0.0f, 0.0f, 0); + ScriptCommand(&destroy_object_with_fade, field_2B9); + field_2B9 = 0; +} +//----------------------------------------------------------- + +char szDanceG[16][16] = { + "DANCE_G1", "DANCE_G2", "DANCE_G3", "DANCE_G4", + "DANCE_G5", "DANCE_G6", "DANCE_G7", "DANCE_G8", + "DANCE_G9", "DANCE_G10", "DANCE_G11", "DANCE_G12", + "DANCE_G13", "DANCE_G14", "DANCE_G15", "DANCE_G16" +}; + +char szDanceB[16][16] = { + "DANCE_B1", "DANCE_B2", "DANCE_B3", "DANCE_B4", + "DANCE_B5", "DANCE_B6", "DANCE_B7", "DANCE_B8", + "DANCE_B9", "DANCE_B10", "DANCE_B11", "DANCE_B12", + "DANCE_B13", "DANCE_B14", "DANCE_B15", "DANCE_B16" +}; + +char szStrip[16][16] = { + "strip_A", "strip_B", "strip_C", "strip_D", + "strip_E", "strip_F", "strip_G", "STR_A2B", + "STR_B2A", "STR_B2C", "STR_C1", "STR_C2", + "STR_C2B", "STR_A2B", "STR_B2C", "STR_C2" +}; + +char * CPlayerPed::FUNC_100ADB70(int iIndex) +{ + if(!m_iDanceState) return ""; + + if(m_iDanceStyle >= 0 && m_iDanceStyle <= 2) { + if(!m_pPed || FUNC_100B4100(m_pPed->entity.nModelIndex) != 5) { + if(FUNC_100AD9C0() != 22) return szDanceB[iIndex]; + } + + return szDanceG[iIndex]; + } + + if(m_iDanceStyle == 3) return szStrip[iIndex]; + + return ""; +} +//----------------------------------------------------------- + +DWORD CPlayerPed::FUNC_100AE6A0() +{ + short sIndex = FUNC_1009CFD0(FUNC_100B4430(VAR_10151658, VAR_1015165C)); + + return (((BYTE)VAR_10151660 | 0xFFFF8000) << 16) | sIndex; +} +//----------------------------------------------------------- diff --git a/saco/game/playerped.h b/saco/game/playerped.h index 003e817..e247653 100644 --- a/saco/game/playerped.h +++ b/saco/game/playerped.h @@ -5,6 +5,21 @@ #include "aimstuff.h" #include "entity.h" +struct struc_13; + +// where a synced shot came from, snapshotted by CPlayerPed::FUNC_100AFA70 +struct SHOT_SYNC_DATA +{ + DWORD field_0; + VECTOR vecOrigin; + VECTOR vecColPoint; + VECTOR vecOffset; + ENTITY_TYPE *pAttachedTo; +}; + +DWORD __stdcall GamePool_Vehicle_GetIndex(VEHICLE_TYPE *pVehicle); +void FUNC_100AE520(); // .text:100AE520 + struct struc_97 { char _gap0[52]; @@ -34,19 +49,41 @@ public: return GameGetPlayerCameraMode(m_bytePlayerNumber); }; + void SetCameraMode(BYTE byteMode) { // .text:10014340 + GameSetPlayerCameraMode(byteMode, m_bytePlayerNumber); + }; + + void SetCameraExtZoom(float fZoom, float fRatio) { // .text:10014360 + GameSetPlayerCameraExtZoom(m_bytePlayerNumber, fZoom, fRatio); + }; + + void SetInterior(BYTE byteInterior, BOOL bRefresh); // .text:100AD340 void ShowMarker(int iMarkerColorID); void HideMarker(); BYTE GetCurrentWeapon(); - int GetCurrentVehicleID(); + int GetCurrentVehicleID() { + if(!m_pPed) return 0; + VEHICLE_TYPE *pVehicle = (VEHICLE_TYPE *)m_pPed->pVehicle; + return GamePool_Vehicle_GetIndex(pVehicle); + }; BOOL IsOnScreen(); float GetHealth(); void SetHealth(float fHealth); float GetArmour(); void SetArmour(float fArmour); + + void SetSkin(int iSkin); // .text:100AFF50 + void FUNC_100ABBD0(); // .text:100ABBD0 + void FUNC_100ABD70(float a1); // .text:100ABD70 + void FUNC_100AC010(); // .text:100AC010 + void FUNC_100AC790(int a1); // .text:100AC790 + BYTE FUNC_100AC850(WORD *pKiller); // .text:100AC850 DWORD GetStateFlags(); void SetStateFlags(DWORD dwStateFlags); BOOL IsDead(); BOOL IsInVehicle(); + BOOL FUNC_100AC5D0(); // .text:100AC5D0 + BOOL FUNC_100AC640(); // .text:100AC640 BYTE GetActionTrigger(); void SetActionTrigger(BYTE byteTrigger); @@ -61,6 +98,7 @@ public: void GiveWeapon(int iWeaponID, int iAmmo); void SetArmedWeapon(int iWeaponType, bool bUnk); + void FUNC_100AC290(int iGtaVehicleID, int iSeat); // .text:100AC290 void ClearAllWeapons(); void RemoveWeaponWhenEnteringVehicle(); WEAPON_SLOT_TYPE * GetCurrentWeaponSlot(); @@ -101,6 +139,17 @@ public: BOOL IsPerformingAnimation(char *szAnimName); CPlayerPed(); + ~CPlayerPed(); + + int sub_100ADFA0(); // .text:100ADFA0 + BOOL FUNC_100ABFC0(); // .text:100ABFC0 + BYTE FUNC_100ABC50(); // .text:100ABC50 + void FUNC_100AC410(DWORD dwGtaId, int a2); // .text:100AC410 + void FUNC_100ADFB0(int iLevel); // .text:100ADFB0 + int FUNC_100ADC90(); // .text:100ADC90 + void FUNC_100ADFD0(char *szName, int a2, int a3, int a4, VECTOR *pvecPos, int a6, float a7, int a8, int a9, int a10); // .text:100ADFD0 + void FUNC_100ABF10(float fAngle); // .text:100ABF10 + void ProcessVehicleHorn(); @@ -134,10 +183,90 @@ public: void SetShopName(char *szNewShopName); void SetWeaponSkillLevel(int iSkill, WORD wLevel); + void SetWeaponAmmo(BYTE byteWeapon, WORD wAmmo); // .text:100B0080 VECTOR* GetBonePosition(int iBone, VECTOR *vecPos); VECTOR* GetTransformedBonePosition(int iBone, VECTOR *vecOffset); + BOOL FUNC_100AFA70(); // .text:100AFA70 + void FUNC_100AFC70(VECTOR *vecIn, VECTOR *vecOut); // .text:100AFC70 + BOOL FUNC_100ACEF0(); // .text:100ACEF0 + void FUNC_100AF280(void *pShotSync); // .text:100AF280 + void FUNC_100AEEE0(MATRIX4X4 *pMatrix); // .text:100AEEE0 + + BOOL HasObjectAttached(); // .text:100AEE30 + void RemoveAttachedObject(int iIndex); // .text:100AEE50 + void FUNC_100B0B10(int iIndex, char *pAttachData); // .text:100B0B10 + BOOL GetAttachedObjectSlotState(int iIndex); // .text:100AEEB0 + void RemoveAllAttachedObjects(); // .text:100B0AB0 + void StopCarrying(); // .text:100ADC00 + void Destroy(); // .text:100B0FA0 + void ProcessDrunkCamera(); // .text:100ADDF0 + void FUNC_100AD0F0(); // .text:100AD0F0 + void FUNC_100AFFD0(); // .text:100AFFD0 + + int FUNC_100AE260(); // .text:100AE260 + int FUNC_100ACDC0(); // .text:100ACDC0 + int FUNC_100ACB60(); // .text:100ACB60 + int FUNC_100ADB60(); // .text:100ADB60 + int FUNC_100AE0A0(); // .text:100AE0A0 + void FUNC_100AE100(); // .text:100AE100 + void FUNC_100ADCA0(); // .text:100ADCA0 + void FUNC_100B02D0(int a1); // .text:100B02D0 + BOOL FUNC_100ACE40(); // .text:100ACE40 + void FUNC_100ACF00(int a1); // .text:100ACF00 + BOOL FUNC_100AD860(); // .text:100AD860 + int FUNC_100ABDD0(); // .text:100ABDD0 + void FUNC_100ABDF0(int a1); // .text:100ABDF0 + void FUNC_100AD880(); // .text:100AD880 + void FUNC_100AC720(); // .text:100AC720 + void FUNC_100AEC90(); // .text:100AEC90 + int FUNC_100AEF60(); // .text:100AEF60 + + int FUNC_100AC690(); // .text:100AC690 + void FUNC_100ACE50(int a1); // .text:100ACE50 + BOOL FUNC_100ADA00(); // .text:100ADA00 + void FUNC_100AD3E0(); // .text:100AD3E0 + BOOL FUNC_100AD030(); // .text:100AD030 + void FUNC_100AD080(); // .text:100AD080 + int FUNC_100AE960(int a1); // .text:100AE960 + void FUNC_100AD8B0(int iWeapon); // .text:100AD8B0 + struc_13 * FUNC_100AE5D0(); // .text:100AE5D0 + + DWORD FUNC_100AEBB0(int a1); // .text:100AEBB0 + void FUNC_100AE560(int iType, int a2, int a3, int a4); // .text:100AE560 + + DWORD FUNC_100AE990(); // .text:100AE990 + DWORD FUNC_100AE9E0(); // .text:100AE9E0 + DWORD FUNC_100AEA30(); // .text:100AEA30 + void FUNC_100AC6C0(); // .text:100AC6C0 + + void FUNC_100AE1E0(); // .text:100AE1E0 + + void FUNC_100AEA80(MATRIX4X4 *pMatrix, int iFrame); // .text:100AEA80 + void FUNC_100AEB10(MATRIX4X4 *pMatrix, int iFrame); // .text:100AEB10 + + void FUNC_100ACE90(); // .text:100ACE90 + + void FUNC_100AEC10(MATRIX4X4 *pMatrix, int iBone); // .text:100AEC10 + void FUNC_100AEC50(MATRIX4X4 *pMatrix, int iBone); // .text:100AEC50 + + int FUNC_100AD9C0(); // .text:100AD9C0 + int FUNC_100AD9E0(); // .text:100AD9E0 + void FUNC_100AF1F0(); // .text:100AF1F0 + void FUNC_100AEF90(); // .text:100AEF90 + void FUNC_100AE530(int a1, int a2); // .text:100AE530 + + float FUNC_100AF220(); // .text:100AF220 + DWORD FUNC_100AE790(); // .text:100AE790 + DWORD * FUNC_100AE6E0(); // .text:100AE6E0 + DWORD FUNC_100AE5F0(DWORD *pTask); // .text:100AE5F0 + DWORD FUNC_100AE6A0(); // .text:100AE6A0 + + void FUNC_100AD440(); // .text:100AD440 + + char * FUNC_100ADB70(int iIndex); // .text:100ADB70 + int m_iCellPhoneEnabled; int field_4C[10]; struc_97 field_74[10]; @@ -148,7 +277,7 @@ public: BYTE m_bytePlayerNumber; char _gap2B1[8]; int field_2B9; - char _gap2BD[4]; + int field_2BD; int field_2C1; int field_2C5; int field_2C9; @@ -166,7 +295,8 @@ public: char field_2F7; char field_2F8; int field_2F9; - char _gap2FD[48]; + char field_2FD[44]; + int field_329; }; diff --git a/saco/game/scripting.h b/saco/game/scripting.h index edb3f0c..2625f0b 100644 --- a/saco/game/scripting.h +++ b/saco/game/scripting.h @@ -53,6 +53,8 @@ const SCRIPT_COMMAND set_marker_color = { 0x0165, "ii" }; // var_marker, co const SCRIPT_COMMAND enable_zone_names = { 0x09BA, "i" }; const SCRIPT_COMMAND create_radar_marker_without_sphere = { 0x04CE, "fffiv" }; // x, y, z, marker_type, var_marker const SCRIPT_COMMAND show_on_radar = { 0x0168, "ii" }; // var_marker, size +const SCRIPT_COMMAND create_arrow_above_actor = { 0x0187, "iv" }; // var_actor, var_marker +const SCRIPT_COMMAND show_on_radar2 = { 0x018B, "ii" }; // var_marker, size const SCRIPT_COMMAND create_icon_marker_sphere = { 0x02A7, "fffiv" }; @@ -94,9 +96,20 @@ const SCRIPT_COMMAND put_train_at = { 0x07c7, "ifff" }; const SCRIPT_COMMAND get_active_interior = { 0x077e, "v" }; const SCRIPT_COMMAND set_actor_z_angle = { 0x0173, "if" }; +const SCRIPT_COMMAND get_actor_z_angle = { 0x0172, "iv" }; +const SCRIPT_COMMAND get_car_z_angle = { 0x0174, "iv" }; const SCRIPT_COMMAND lock_actor = { 0x04d7, "ii" }; const SCRIPT_COMMAND refresh_streaming_at = { 0x04E4, "ff" }; +const SCRIPT_COMMAND select_interior = { 0x04BB, "i" }; // interior +const SCRIPT_COMMAND disassociate_object = { 0x0682, "ifffi" }; // var_object, x, y, z, ukn +const SCRIPT_COMMAND destroy_object_with_fade = { 0x09A2, "i" }; // var_object +// the two object commands samp.dll references that were missing here; the diff cannot +// see a wrong command id, the operand is a masked relocation +const SCRIPT_COMMAND destroy_object = { 0x0108, "i" }; // var_object +const SCRIPT_COMMAND script_0650 = { 0x0650, "i" }; // var_object, name not resolved +const SCRIPT_COMMAND task_pick_up_object = { 0x070A, "iifffiissi" }; +const SCRIPT_COMMAND link_actor_to_interior = { 0x0860, "ii" }; // var_actor, interior const SCRIPT_COMMAND put_actor_in_car2 = { 0x0430, "iii" }; const SCRIPT_COMMAND kill_actor = { 0x0321, "i" }; @@ -154,6 +167,12 @@ const SCRIPT_COMMAND is_wav_loaded = { 0x3D0, "i" }; const SCRIPT_COMMAND play_wav = { 0x3D1, "i" }; const SCRIPT_COMMAND unload_wav = { 0x40D, "i" }; const SCRIPT_COMMAND set_wav_at_location = { 0x3D7, "ifff" }; +const SCRIPT_COMMAND is_line_of_sight_clear = { 0x06BD, "ffffffiiiii" }; + +const SCRIPT_COMMAND set_widescreen = { 0x02A3, "i" }; +// the fifth argument is declared "i" but samp passes a float +const SCRIPT_COMMAND script_command_0948 = { 0x0948, "fffii" }; +const SCRIPT_COMMAND attach_object_to_player = { 0x069B, "iiffffff" }; // object, char, offset xyz, rot xyz diff --git a/saco/game/util.cpp b/saco/game/util.cpp index b430805..11131fc 100644 --- a/saco/game/util.cpp +++ b/saco/game/util.cpp @@ -5,6 +5,8 @@ #include "util.h" #include +extern int iGtaVersion; + DWORD dwPlayerPedPtrs[PLAYER_PED_SLOTS]; struc_13 VAR_1026C258[PLAYER_PED_SLOTS]; @@ -13,7 +15,7 @@ struc_13 VAR_1026C258[PLAYER_PED_SLOTS]; //----------------------------------------------------------- -void __declspec(naked) ProcessLineOfSight(VECTOR *vecOrigin, VECTOR *vecLine, VECTOR *colPoint, +BOOL __declspec(naked) ProcessLineOfSight(VECTOR *vecOrigin, VECTOR *vecLine, VECTOR *colPoint, DWORD *pHitEntity, int bCheckBuildings, int bCheckVehicles, int bCheckPeds, int bCheckObjects, int bCheckDummies, int bSeeThroughStuff, int bIgnoreSomeObjectsForCamera, int bUnk1) @@ -179,6 +181,64 @@ OBJECT_TYPE * GamePool_GetObject() return pObjectRet; } +// the pool slot in CPools; the used-slot count walks the byte map that starts +// four bytes into the pool itself +struct GAME_POOL_HOLDER +{ + BYTE *pPool; + + int CountUsedSlots() + { + int iCount = 0; + int iSize = *(int *)(pPool + 8); + + for(int i = 0; i < iSize; i++) + { + if(pPool[i + 4] > 0) + iCount++; + } + return iCount; + } +}; + +//----------------------------------------------------------- + +int FUNC_100B3CD0() +{ + return ((GAME_POOL_HOLDER *)0xB7449C)->CountUsedSlots(); +} + +//----------------------------------------------------------- + +int FUNC_100B3D00() +{ + return ((GAME_POOL_HOLDER *)0xC8800C)->CountUsedSlots(); +} + +//----------------------------------------------------------- + +bool FUNC_100B3D30(int nModelIndex) +{ + OBJECT_TYPE *pPoolStart; + + _asm mov eax, 0xB7449C + _asm mov edx, [eax] + _asm mov eax, [edx] + _asm mov pPoolStart, eax + + OBJECT_TYPE *pObject = pPoolStart; + + for(int i = 0; i != 3000; i++, pObject++) + { + if(pObject && pObject->vtable && pObject->vtable != 0x863C40 && + pObject->nModelIndex == nModelIndex) + { + return true; + } + } + return false; +} + //----------------------------------------------------------- void ReplaceBuildingModel(ENTITY_TYPE *pEntity, int iModelID) @@ -191,6 +251,146 @@ void ReplaceBuildingModel(ENTITY_TYPE *pEntity, int iModelID) //----------------------------------------------------------- +// set by the base-model-info relocation hack; the setter is not implemented yet +BOOL bBaseModelInfoRelocated; +DWORD **ppRelocatedModelInfo; + +DWORD * __stdcall GetModelInfo(int iModel) +{ + if(bBaseModelInfoRelocated) + return (DWORD *)ppRelocatedModelInfo[iModel]; + + if(iModel < 0 || iModel > 20000) return NULL; + + return ((DWORD **)0xA9B0C8)[iModel]; +} + +//----------------------------------------------------------- + +BOOL __stdcall IsValidModel(int iModel) +{ + return GetModelInfo(iModel) != NULL; +} + +//----------------------------------------------------------- + +BOOL __stdcall IsValidPedModel(int iModel) +{ + DWORD *pModelInfo; + + if(iModel >= 0 && iModel <= 30000 && + (pModelInfo = GetModelInfo(iModel)) != NULL && + *pModelInfo == 0x85BDC0) + { + return TRUE; + } + return FALSE; +} + +//----------------------------------------------------------- + +int FUNC_100B41F0(void *pData) +{ + int iRet = 0; + DWORD dwFunc; + DWORD dwCamera = *(DWORD *)0xC1703C; + + if(!dwCamera) return iRet; + + dwFunc = (iGtaVersion != GTASA_VERSION_USA10) ? 0x7EE310 : 0x7EE2D0; + + _asm push pData + _asm push dwCamera + _asm mov edx, dwFunc + _asm call edx + _asm pop edx + _asm pop edx + _asm mov iRet, eax + + return iRet; +} + +//----------------------------------------------------------- + +int __stdcall FUNC_100B4240(DWORD *pdwIn, DWORD dwParam) +{ + DWORD dwCamera; + DWORD dwFunc; + DWORD *pData; + DWORD data[4]; + int iRet; + + data[0] = pdwIn[0]; + data[1] = pdwIn[1]; + data[2] = pdwIn[2]; + data[3] = dwParam; + pData = data; + iRet = 0; + + dwCamera = *(DWORD *)0xC1703C; + if(!dwCamera) return iRet; + + dwFunc = (iGtaVersion != GTASA_VERSION_USA10) ? 0x7EE310 : 0x7EE2D0; + + _asm push pData + _asm push dwCamera + _asm mov edx, dwFunc + _asm call edx + _asm pop edx + _asm pop edx + _asm mov iRet, eax + + return iRet; +} + +//----------------------------------------------------------- + +void __stdcall FUNC_100B42B0(DWORD *pdwEntity) +{ + if(pdwEntity && pdwEntity[0x118]) + { + DWORD dwState = ((DWORD *)pdwEntity[0x118])[0x166]; + if(dwState != 0 && dwState != 1) + { + DWORD *pdwObject = (DWORD *)pdwEntity[0x118]; + + _asm mov ecx, pdwObject + _asm mov ebx, [ecx] + _asm push 1 + _asm call dword ptr [ebx] + + pdwEntity[0x118] = 0; + } + } +} + +//----------------------------------------------------------- + +DWORD __stdcall FUNC_100B4430(int a1, int a2) +{ + DWORD *pFound = NULL; + DWORD dwResult = 0; + + _asm push a2 + _asm push a1 + _asm mov edx, 0x4D3A60 + _asm call edx + _asm mov pFound, eax + _asm pop edx + _asm pop edx + + if(pFound) + { + _asm mov edx, [eax+0x10] + _asm mov eax, [edx] + _asm mov dwResult, eax + } + + return dwResult; +} + +//----------------------------------------------------------- + int GetInvalidModelInfoCount() { DWORD *dwModelInfos = (DWORD *)0xA9C068; @@ -507,6 +707,19 @@ DWORD __stdcall CRC32FromUpcaseString(char *szString) +bool FUNC_100B4A70(VECTOR *vec) +{ + if( vec->X <= 1.0f && vec->X >= -1.0f && + vec->Y <= 1.0f && vec->Y >= -1.0f && + vec->Z <= 1.0f && vec->Z >= -1.0f ) + { + return true; + } + return false; +} + +//----------------------------------------------------------- + bool FUNC_100B4B50(VECTOR *vecPos) { if( vecPos->X < 20000.0f && vecPos->X > -20000.0f && @@ -518,6 +731,45 @@ bool FUNC_100B4B50(VECTOR *vecPos) return false; } +//----------------------------------------------------------- + +int FUNC_100B4BC0(int a1, int a2, int a3) +{ + _asm push a3 + _asm push a2 + _asm push a1 + _asm mov edx, 0x59C730 + _asm call edx + _asm pop edx + _asm pop edx + _asm pop edx +} + +//----------------------------------------------------------- + +int FUNC_100B4BE0(int a1, int a2, int a3) +{ + _asm push a3 + _asm push a2 + _asm push a1 + _asm mov edx, 0x59C790 + _asm call edx +} + +//----------------------------------------------------------- + +int FUNC_100B4C00(int a1, int a2, int a3) +{ + _asm push a3 + _asm push a2 + _asm push a1 + _asm mov edx, 0x59C810 + _asm call edx + _asm pop edx + _asm pop edx + _asm pop edx +} + @@ -658,11 +910,57 @@ void _VectorNormalise(VECTOR *vec) //---------------------------------------------------- +void VectorCrossProduct(VECTOR *vecOut, VECTOR *vec1, VECTOR *vec2) +{ + float f1, f2; + + // each product needs its own statement; in one expression MSVC canonicalises + // the commutative operands and picks the other side to fld + f1 = vec2->Z * vec1->Y; + f2 = vec2->Y * vec1->Z; + vecOut->X = f1 - f2; + f1 = vec2->X * vec1->Z; + f2 = vec2->Z * vec1->X; + vecOut->Y = f1 - f2; + f1 = vec2->Y * vec1->X; + f2 = vec2->X * vec1->Y; + vecOut->Z = f1 - f2; +} + +//---------------------------------------------------- + +void VectorNormaliseTo(VECTOR *vecOut, VECTOR *vecIn) +{ + float fInv = 1.0f / (float)sqrt((double)(vecIn->X * vecIn->X + vecIn->Y * vecIn->Y + + vecIn->Z * vecIn->Z)); + + vecOut->X = fInv * vecIn->X; + vecOut->Y = fInv * vecIn->Y; + vecOut->Z = fInv * vecIn->Z; +} + +//---------------------------------------------------- + float GetNormalisation(VECTOR *vec) { return ((vec->X * vec->X) + (vec->Y * vec->Y) + (vec->Z * vec->Z)); } +//---------------------------------------------------- +// newton step on the 0x5F3759D5 inverse-sqrt seed, scaled back up by x + +float FastSqrt(float x) +{ + float fOrig = x; + float fHalf = x * 0.5f; + int i = *(int *)&x; + + i = 0x5F3759D5 - (i >> 1); + x = *(float *)&i; + + return fOrig * (x * (1.5f - fHalf * x * x)); +} + //---------------------------------------------------- float DegToRad(float fDegrees) @@ -877,6 +1175,50 @@ void CreateCameraRaster() //---------------------------------------------------- +DWORD CamFrameBufferSave; +DWORD CamZBufferSave; + +DWORD *pRwSceneCamera = (DWORD *)0xC1703C; + +void FUNC_100B5D20() +{ + DWORD dwCamera; + + _asm pushad + + CreateCameraRaster(); + + dwCamera = *pRwSceneCamera; + *(DWORD *)0xC9BCC0 = dwCamera; + + _asm mov ebx, dwCamera + _asm mov edx, [ebx+0x60] + _asm mov CamFrameBufferSave, edx + _asm mov edx, [ebx+0x64] + _asm mov CamZBufferSave, edx + _asm mov edx, CamFrameBuffer2 + _asm mov [ebx+0x60], edx + _asm mov edx, CamZBuffer2 + _asm mov [ebx+0x64], edx + + _asm mov edx, 0x734650 + _asm call edx + _asm mov edx, 0x53DF40 + _asm call edx + _asm mov edx, 0x732F30 + _asm call edx + + _asm mov ebx, dwCamera + _asm mov edx, CamFrameBufferSave + _asm mov [ebx+0x60], edx + _asm mov edx, CamZBufferSave + _asm mov [ebx+0x64], edx + + _asm popad +} + +//---------------------------------------------------- + void ResetLocalPad(int unk1, int unk2) { // CPad__GetPadAt(int index) @@ -905,6 +1247,16 @@ BOOL IsFileOrDirectoryExists(char * szPath) //---------------------------------------------------- +void ReplaceUnprintableChars(char *szString) +{ + while(*szString) + { + if((BYTE)*szString > 0x7F || (BYTE)*szString < ' ') + *szString = ' '; + szString++; + } +} + BOOL IsHexChar(char c) { return c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f'; @@ -918,7 +1270,7 @@ BOOL IsHexChar(wchar_t c) DWORD GetColorFromEmbedCode(char *szString) { if(szString[0] && szString[0] == '{' - && szString[1] && IsHexChar(szString[1]) + && szString[1] && (szString[1] >= '0' && szString[1] <= '9' || szString[1] >= 'A' && szString[1] <= 'F' || szString[1] >= 'a' && szString[1] <= 'f') && szString[2] && IsHexChar(szString[2]) && szString[3] && IsHexChar(szString[3]) && szString[4] && IsHexChar(szString[4]) @@ -992,6 +1344,43 @@ DWORD unnamed_100B6100(char *szString, int nMaxLen) return 0; } +int FUNC_100B61D0(int a1) +{ + _asm mov eax, 0x745C70 + _asm push a1 + _asm call eax + _asm pop edx +} + +int __stdcall FUNC_100B64E0(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9) +{ + _asm push a9 + _asm push a8 + _asm push a7 + _asm push a6 + _asm push a5 + _asm push a4 + _asm push a3 + _asm push a2 + _asm mov ecx, a1 + _asm mov edx, 0x73FB10 + _asm call edx +} + +//----------------------------------------------------------- + +int __stdcall FUNC_100B6510(int a1, int a2) +{ + _asm push 0 + _asm push 0 + _asm push a2 + _asm mov ecx, a1 + _asm mov edx, 0x73AAC0 + _asm call edx +} + +//----------------------------------------------------------- + UINT GetVehicleSubtypeFromVehiclePtr(VEHICLE_TYPE *pVehicle) { if(!pVehicle) return 0; @@ -1021,3 +1410,650 @@ UINT GetVehicleSubtypeFromVehiclePtr(VEHICLE_TYPE *pVehicle) + +//----------------------------------------------------------- + +void FUNC_100B4D10(VECTOR *vecOut, MATRIX4X4 *pMatrix, VECTOR *vecOffset) +{ + vecOut->X = pMatrix->at.X * vecOffset->Z + pMatrix->up.X * vecOffset->Y + + pMatrix->right.X * vecOffset->X + pMatrix->pos.X; + vecOut->Y = ((pMatrix->at.Y * vecOffset->Z + pMatrix->up.Y * vecOffset->Y) + + pMatrix->right.Y * vecOffset->X) + pMatrix->pos.Y; + vecOut->Z = ((pMatrix->at.Z * vecOffset->Z + pMatrix->up.Z * vecOffset->Y) + + pMatrix->right.Z * vecOffset->X) + pMatrix->pos.Z; +} + +//----------------------------------------------------------- + +DWORD unnamed_1026BBB0; + +BOOL __stdcall FUNC_100B4860(VEHICLE_TYPE *pVehicle) +{ + BOOL bTowed = FALSE; + + if(pVehicle) + { + VEHICLE_TYPE *pTractor = (VEHICLE_TYPE *)pVehicle->dwTractor; + if(pTractor) + { + unnamed_1026BBB0 = pVehicle->dwTractor; + + // towed by a vehicle somebody is sitting in + PED_TYPE *pDriver = pTractor->pDriver; + if(pDriver) + { + if(pDriver->dwStateFlags & 0x100) return TRUE; + } + } + } + + return bTowed; +} + +//----------------------------------------------------------- + +DWORD VAR_1026BBB8[PLAYER_PED_SLOTS]; + +void __stdcall FUNC_100B44C0(BYTE bytePlayer, DWORD dwValue) +{ + VAR_1026BBB8[bytePlayer] = dwValue; +} + +//----------------------------------------------------------- + +DWORD __stdcall FUNC_100B4520(DWORD *pdw) +{ + return *pdw; +} + +//----------------------------------------------------------- + +void __stdcall FUNC_100B4530(DWORD *pdwVtable) +{ + if(pdwVtable && *pdwVtable == 0x85BBF0) + *pdwVtable = 0x85BD30; +} + +//----------------------------------------------------------- + +void __stdcall FUNC_100B4550(DWORD *pdw, float fValue) +{ + *(float *)((BYTE *)pdw + 0x18) = fValue; +} + +//----------------------------------------------------------- + +float __stdcall FUNC_100B4560(DWORD *pdw) +{ + return *(float *)((BYTE *)pdw + 0x18); +} + +//----------------------------------------------------------- + +void __stdcall FUNC_100B4660(int iModel, int nTxdIndex) +{ + DWORD *pModelInfo = GetModelInfo(iModel); + + _asm mov edx, pModelInfo + _asm mov eax, nTxdIndex + _asm mov [edx+0x0A], ax +} + +//----------------------------------------------------------- + +void FUNC_100B46C0(DWORD dwValue) +{ + *(DWORD *)0x9689E0 = dwValue; +} + +//----------------------------------------------------------- + +float FUNC_100B4FE0(float x, float y, float z) +{ + return (x * x) + (y * y) + (z * z); +} + +//----------------------------------------------------------- + +int FUNC_100B56D0(int a1, int a2, int a3) +{ + _asm push a3 + _asm push a2 + _asm push a1 + _asm mov edx, 0x7EDDC0 + _asm call edx + _asm pop edx + _asm pop edx + _asm pop edx +} + +//----------------------------------------------------------- + +DWORD VAR_1026DF80; +DWORD VAR_1026DF84; +DWORD VAR_1026DF88; + +void FUNC_100B5720() +{ + _asm pushad + _asm push VAR_1026DF80 + _asm push VAR_1026DF84 + _asm mov edx, VAR_1026DF88 + _asm call edx + _asm pop edx + _asm pop edx + _asm popad +} + +//----------------------------------------------------------- + +BOOL __stdcall FUNC_100B4570(int iModel) +{ + DWORD *pModelInfo = GetModelInfo(iModel); + + // retail tests the sum, so a compare against -20 emits the wrong shape + if(pModelInfo && *pModelInfo + 20 != 0) + { + return TRUE; + } + return FALSE; +} + +//----------------------------------------------------------- + +DWORD __stdcall FUNC_100B45A0(int iModel) +{ + DWORD *pModelInfo = GetModelInfo(iModel); + DWORD dwRet = 0; + + _asm mov eax, pModelInfo + _asm mov edx, [eax+0x1C] + _asm mov dwRet, edx + + return dwRet; +} + +//----------------------------------------------------------- + +void __stdcall FUNC_100B45D0(int iModel, int a2) +{ + DWORD *pModelInfo = GetModelInfo(iModel); + + _asm mov ecx, pModelInfo + _asm mov edx, a2 + _asm mov [ecx+0x1C], edx + _asm push a2 + _asm mov edx, [ecx] + _asm call dword ptr [edx+0x40] +} + +//----------------------------------------------------------- + +void __stdcall FUNC_100B4600(int iModel, int a2) +{ + DWORD *pModelInfo = GetModelInfo(iModel); + + _asm mov ecx, pModelInfo + _asm mov edx, a2 + _asm mov [ecx+0x1C], edx + _asm push a2 + _asm mov edx, [ecx] + _asm call dword ptr [edx+0x3C] +} + +//----------------------------------------------------------- + +void __stdcall FUNC_100B4630(int iModel, int a2) +{ + DWORD *pModelInfo = GetModelInfo(iModel); + + _asm mov ecx, pModelInfo + _asm push 0 + _asm push a2 + _asm mov edx, 0x4C4BC0 + _asm call edx +} + +//----------------------------------------------------------- + +DWORD * __stdcall FUNC_100B46D0(int iModel) +{ + DWORD *pModelInfo = GetModelInfo(iModel); + DWORD *pRet = 0; + + _asm mov eax, pModelInfo + _asm mov edx, [eax+0x14] + _asm mov pRet, edx + + return pRet; +} + +//----------------------------------------------------------- + +WORD __stdcall FUNC_100B4700(int iModel) +{ + DWORD *pModelInfo = GetModelInfo(iModel); + WORD wRet = 0; + + _asm mov edx, pModelInfo + _asm mov bx, [edx+8] + _asm mov wRet, bx + + return wRet; +} + +//----------------------------------------------------------- + +void __stdcall FUNC_100B4730(int iModel, WORD wValue) +{ + DWORD *pModelInfo = GetModelInfo(iModel); + + if(pModelInfo) + { + _asm mov edx, pModelInfo + _asm mov bx, wValue + _asm mov [edx+8], bx + } +} + +//----------------------------------------------------------- + +BOOL __stdcall FUNC_100B44E0(DWORD *pModelInfo) +{ + if(pModelInfo && (*pModelInfo == 0x85BBF0 || *pModelInfo == 0x85BC30 || + *pModelInfo == 0x85BC70 || *pModelInfo == 0x85BCB0 || *pModelInfo == 0x85BCF0)) + { + return TRUE; + } + return FALSE; +} + +//----------------------------------------------------------- + +WORD FUNC_100B6470(int a1, int a2, int a3, int a4, int a5) +{ + if(!a1) return 0; + + WORD wRet = 0; + + _asm lea edx, a1 + _asm xor eax, eax + _asm mov ax, [edx+0x12] + _asm mov wRet, ax + + return wRet; +} + +//----------------------------------------------------------- + +void VectorScale(VECTOR *vecOut, VECTOR *vecIn, float fScale) +{ + vecOut->X = fScale * vecIn->X; + vecOut->Y = fScale * vecIn->Y; + vecOut->Z = fScale * vecIn->Z; +} + +//----------------------------------------------------------- + +void FUNC_100B51E0(int a1, int a2) +{ + DWORD dwFunc = (iGtaVersion != GTASA_VERSION_USA10) ? 0x7EB600 : 0x7EB5C0; + + _asm push a2 + _asm push a1 + _asm mov eax, dwFunc + _asm call eax + _asm pop edx + _asm pop edx +} + +//----------------------------------------------------------- + +float FUNC_100B5180(float *pQuat) +{ + return pQuat[0] * pQuat[0] + (pQuat[1] * pQuat[1] + pQuat[2] * pQuat[2] + + pQuat[3] * pQuat[3]); +} + +//----------------------------------------------------------- + +void FUNC_100B5910(int a1, int a2) +{ + DWORD dwFunc = (iGtaVersion != GTASA_VERSION_USA10) ? 0x7F1960 : 0x7F1920; + + _asm push a2 + _asm push a1 + _asm mov edx, dwFunc + _asm call edx + _asm pop edx + _asm pop edx +} + +//----------------------------------------------------------- + +void FUNC_100B5940(int a1, int a2) +{ + DWORD dwFunc = (iGtaVersion != GTASA_VERSION_USA10) ? 0x7F2300 : 0x7F22C0; + + _asm push 1 + _asm push a2 + _asm push a1 + _asm mov edx, dwFunc + _asm call edx + _asm pop edx + _asm pop edx + _asm pop edx +} + +//----------------------------------------------------------- + +void FUNC_100B58D0(int a1, int a2, int a3) +{ + int nCopy = a2; + DWORD dwFunc = (iGtaVersion != GTASA_VERSION_USA10) ? 0x7F2010 : 0x7F1FD0; + + _asm push 1 + _asm push a3 + _asm push nCopy + _asm push a1 + _asm mov edx, dwFunc + _asm call edx + _asm pop edx + _asm pop edx + _asm pop edx + _asm pop edx +} + +//----------------------------------------------------------- + +// patched to the EU entry point on first use, so it has to be a real variable +DWORD VAR_101173FC = 0x7EDD90; + +void FUNC_100B56F0(int a1, int a2, int a3) +{ + if(iGtaVersion == GTASA_VERSION_EU10) + VAR_101173FC = 0x7EDDD0; + + _asm push a3 + _asm push 1 + _asm push a2 + _asm push a1 + _asm mov edx, VAR_101173FC + _asm call edx + _asm pop edx + _asm pop edx + _asm pop edx + _asm pop edx +} + +//----------------------------------------------------------- + +void FUNC_100B62B0(BYTE *pObject) +{ + if(pObject) + { + if(*pObject == 1) + { + DWORD dwOther = *(DWORD *)(pObject + 4); + + FUNC_100B1800(pObject); + + if(dwOther) FUNC_100B17D0(dwOther); + } + else if(*pObject == 2) + { + FUNC_100B1C80(pObject); + } + } +} + +//----------------------------------------------------------- + +BOOL __stdcall FUNC_100B62F0(BYTE *pObject) +{ + if(!pObject || *pObject != 1) return FALSE; + + DWORD dwFunc = (DWORD)FUNC_100B1430(pObject); + + _asm push pObject + _asm mov ebx, dwFunc + _asm call ebx + _asm pop ebx + + return TRUE; +} + +//----------------------------------------------------------- + +void __stdcall FUNC_100B6330(BYTE *pObject) +{ + if(pObject) + { + if(*pObject == 1) + FUNC_100B62F0(pObject); + else if(*pObject == 2) + FUNC_100B19D0(pObject); + } +} + +//----------------------------------------------------------- + +void FUNC_100B5260(MATRIX4X4 *pMatrix, float *pQuatOut) +{ + FUNC_100B4D70(pMatrix->right.X, pMatrix->right.Y, pMatrix->right.Z, + pMatrix->up.X, pMatrix->up.Y, pMatrix->up.Z, + pMatrix->at.X, pMatrix->at.Y, pMatrix->at.Z, + &pQuatOut[0], &pQuatOut[1], &pQuatOut[2], &pQuatOut[3]); +} + +//----------------------------------------------------------- + +void FUNC_100B5420(float *pQuat, MATRIX4X4 *pMatrix) +{ + FUNC_100B4F10(pQuat[0], pQuat[1], pQuat[2], pQuat[3], + &pMatrix->right.X, &pMatrix->right.Y, &pMatrix->right.Z, + &pMatrix->up.X, &pMatrix->up.Y, &pMatrix->up.Z, + &pMatrix->at.X, &pMatrix->at.Y, &pMatrix->at.Z); +} + +//----------------------------------------------------------- + +VECTOR VAR_10117400[3]; +DWORD VAR_10117424; + +void FUNC_100B57E0(int a1, int nIndex, int a3, int a4) +{ + VECTOR *pEntry = &VAR_10117400[nIndex]; + DWORD dwFunc = (iGtaVersion != GTASA_VERSION_USA10) ? 0x7EB800 : 0x7EB7C0; + + _asm push a4 + _asm push a3 + _asm push pEntry + _asm push a1 + _asm mov edx, dwFunc + _asm call edx + _asm pop edx + _asm pop edx + _asm pop edx + _asm pop edx +} + +//----------------------------------------------------------- + +void FUNC_100B5790(int a1, int nIndex, int a3) +{ + VECTOR *pEntry = &VAR_10117400[nIndex]; + + VAR_10117424 = (iGtaVersion != GTASA_VERSION_USA10) ? 0x7F2010 : 0x7F1FD0; + + _asm push 1 + _asm push a3 + _asm push pEntry + _asm push a1 + _asm mov ebx, VAR_10117424 + _asm call ebx + _asm pop ebx + _asm pop ebx + _asm pop ebx + _asm pop ebx +} + +//----------------------------------------------------------- + +void FUNC_100B5740(int a1, int a2) +{ + VAR_1026DF88 = (iGtaVersion != GTASA_VERSION_USA10) ? 0x7F20B0 : 0x7F2070; + VAR_1026DF80 = a2; + VAR_1026DF84 = a1; + + _asm pushad + _asm push VAR_1026DF80 + _asm push VAR_1026DF84 + _asm mov edx, VAR_1026DF88 + _asm call edx + _asm pop edx + _asm pop edx + _asm popad +} + +//----------------------------------------------------------- +// same body as FUNC_100B5260; retail keeps both copies + +void FUNC_100B52B0(MATRIX4X4 *pMatrix, float *pQuatOut) +{ + FUNC_100B4D70(pMatrix->right.X, pMatrix->right.Y, pMatrix->right.Z, + pMatrix->up.X, pMatrix->up.Y, pMatrix->up.Z, + pMatrix->at.X, pMatrix->at.Y, pMatrix->at.Z, + &pQuatOut[0], &pQuatOut[1], &pQuatOut[2], &pQuatOut[3]); +} + +//----------------------------------------------------------- + +void FUNC_100B5210(int a1, void *a2) +{ + float quat[4]; + float *pQuat; + DWORD dwFunc; + + pQuat = quat; + dwFunc = (iGtaVersion != GTASA_VERSION_USA10) ? 0x7EB600 : 0x7EB5C0; + + _asm push a1 + _asm push pQuat + _asm mov eax, dwFunc + _asm call eax + _asm pop edx + _asm pop edx + + ((float *)a2)[0] = quat[3]; + ((float *)a2)[1] = quat[0]; + ((float *)a2)[2] = quat[1]; + ((float *)a2)[3] = quat[2]; +} + +//----------------------------------------------------------- + +void FUNC_100B5500(float *pQuat) +{ + D3DXQUATERNION qOut; + D3DXQUATERNION qIn; + + qIn.w = pQuat[0]; + qIn.x = pQuat[1]; + qIn.y = pQuat[2]; + qIn.z = pQuat[3]; + + D3DXQuaternionNormalize(&qOut, &qIn); + + pQuat[0] = qOut.w; + pQuat[1] = qOut.x; + pQuat[2] = qOut.y; + pQuat[3] = qOut.z; +} + +//----------------------------------------------------------- + +void FUNC_100B58A0(DWORD dwFrame, VECTOR *vecOut, int *a3) +{ + DWORD dwFunc = (iGtaVersion != GTASA_VERSION_USA10) ? 0x7EBAC0 : 0x7EBA80; + + _asm push a3 + _asm push vecOut + _asm push dwFrame + _asm mov edx, dwFunc + _asm call edx + _asm pop edx + _asm pop edx + _asm pop edx +} + +//----------------------------------------------------------- + +VECTOR VAR_1026DF90; +VECTOR VAR_1026DF9C; +float VAR_1026CF7C; + +void FUNC_100B5830(int a1, int nIndex, float *pfOut) +{ + VAR_1026DF9C = VAR_10117400[nIndex]; + + DWORD dwFunc = (iGtaVersion != GTASA_VERSION_USA10) ? 0x7F2760 : 0x7F2720; + + _asm lea eax, VAR_1026DF90 + _asm push eax + _asm lea eax, VAR_1026CF7C + _asm push eax + _asm lea eax, VAR_1026DF9C + _asm push eax + _asm push a1 + _asm mov edx, dwFunc + _asm call edx + _asm pop edx + _asm pop edx + _asm pop edx + _asm pop edx + + *pfOut = VAR_1026CF7C; +} + +//----------------------------------------------------------- + +void FUNC_100B5480(float *pQuatOut, float *pQuatFrom, float *pQuatTo, float fT) +{ + D3DXQUATERNION qOut; + D3DXQUATERNION q1; + D3DXQUATERNION q2; + + q1.w = pQuatFrom[0]; + q1.x = pQuatFrom[1]; + q1.y = pQuatFrom[2]; + q1.z = pQuatFrom[3]; + + q2.w = pQuatTo[0]; + q2.x = pQuatTo[1]; + q2.y = pQuatTo[2]; + q2.z = pQuatTo[3]; + + D3DXQuaternionSlerp(&qOut, &q1, &q2, fT); + + pQuatOut[0] = qOut.w; + pQuatOut[1] = qOut.x; + pQuatOut[2] = qOut.y; + pQuatOut[3] = qOut.z; +} + +//----------------------------------------------------------- + +void FUNC_100B5620(float *pQuat, float *pEuler) +{ + float f0 = pQuat[0] * pQuat[0]; + float f1 = pQuat[1] * pQuat[1]; + float f2 = pQuat[2] * pQuat[2]; + float f3 = pQuat[3] * pQuat[3]; + + pEuler[0] = (float)atan2((pQuat[2] * pQuat[1] + pQuat[3] * pQuat[0]) * 2.0f, + f1 - f2 - f3 + f0); + pEuler[1] = (float)asin((pQuat[3] * pQuat[1] - pQuat[2] * pQuat[0]) * -2.0f); + pEuler[2] = (float)atan2((pQuat[3] * pQuat[2] + pQuat[1] * pQuat[0]) * 2.0f, + f3 - (f2 + f1) + f0); +} diff --git a/saco/game/util.h b/saco/game/util.h index 5001a17..a3dfcfe 100644 --- a/saco/game/util.h +++ b/saco/game/util.h @@ -9,7 +9,7 @@ struct struc_13 DWORD field_C; }; -void ProcessLineOfSight(VECTOR *vecOrigin, VECTOR *vecLine, VECTOR *colPoint, +BOOL ProcessLineOfSight(VECTOR *vecOrigin, VECTOR *vecLine, VECTOR *colPoint, DWORD *pHitEntity, int bCheckBuildings, int bCheckVehicles, int bCheckPeds, int bCheckObjects, int bCheckDummies, int bSeeThroughStuff, int bIgnoreSomeObjectsForCamera, int bUnk1); @@ -17,7 +17,7 @@ void ProcessLineOfSight(VECTOR *vecOrigin, VECTOR *vecLine, VECTOR *colPoint, float GetNormalisation(VECTOR *vec); float FloatDifference(float f1, float f2); -float FloatOffset(float f1, float f2); +float FloatOffset(float f1, float f2); // .text:100B48B0 float NormalizeAngle(float fAngle); void __stdcall WorldRemoveEntity(DWORD *dwEnt); @@ -42,12 +42,12 @@ void __stdcall FUNC_100B4390(BYTE bytePlayer, DWORD a2, DWORD a3, DWORD a4, DWOR struc_13 * __stdcall FUNC_100B43D0(BYTE bytePlayer); BYTE __stdcall FUNC_100B43F0(DWORD *pdwRenderWare); float __stdcall SquaredDistanceBetweenHorizontalPoints(float x1, float y1, float x2, float y2); -float DistanceBetweenHorizontalPoints(float x1, float y1, float x2, float y2); -float DistanceBetweenPoints(float x1, float y1, float z1, float x2, float y2, float z2); +float __stdcall DistanceBetweenHorizontalPoints(float x1, float y1, float x2, float y2); +float __stdcall DistanceBetweenPoints(float x1, float y1, float z1, float x2, float y2, float z2); float __stdcall SquaredDistanceBetweenPoints(float x1, float y1, float z1, float x2, float y2, float z2); float __stdcall SquaredDistanceBetweenPoints(VECTOR *vec1, VECTOR *vec2); -void __stdcall _VectorNormalise(VECTOR *vec); +void _VectorNormalise(VECTOR *vec); DWORD __stdcall CRC32FromUpcaseString(char *szString); float DegToRad(float fDegrees); @@ -58,8 +58,13 @@ void SaveCameraRaster(char *filename); BOOL IsFileOrDirectoryExists(char * szPath); DWORD unnamed_100B6100(char *szString, int nMaxLen); +int FUNC_100B61D0(int a1); // .text:100B61D0 bool FUNC_100B4B50(VECTOR *vecPos); +bool FUNC_100B4AE0(VECTOR *vec); // .text:100B4AE0 +void FUNC_100B4D10(VECTOR *vecOut, MATRIX4X4 *pMatrix, VECTOR *vecOffset); +int FUNC_100B4BE0(int a1, int a2, int a3); // .text:100B4BE0 +int FUNC_100B4C00(int a1, int a2, int a3); // .text:100B4C00 BUILDING_TYPE * GamePool_GetBuilding(); DUMMY_TYPE * GamePool_GetDummy(); @@ -67,14 +72,27 @@ OBJECT_TYPE * GamePool_GetObject(); void ReplaceBuildingModel(ENTITY_TYPE *pEntity, int iModelID); int GetInvalidModelInfoCount(); +int FUNC_100B3CD0(); // .text:100B3CD0 +int FUNC_100B3D00(); // .text:100B3D00 + +DWORD * __stdcall GetModelInfo(int iModel); // .text:100A7A40 +BOOL __stdcall IsValidModel(int iModel); // .text:100B3DB0 +BOOL __stdcall IsValidPedModel(int iModel); // .text:100B3DD0 +DWORD * __stdcall FUNC_100B46D0(int iModel); // .text:100B46D0 +WORD __stdcall FUNC_100B4700(int iModel); // .text:100B4700 bool HasCollisionLoaded(VECTOR *vec); BOOL __stdcall IsATrainPart(ENTITY_TYPE *pEntity); +BOOL __stdcall FUNC_100B4860(VEHICLE_TYPE *pVehicle); // .text:100B4860 +BOOL __stdcall FUNC_100B6950(VEHICLE_TYPE *pVehicle); // .text:100B6950 +int __stdcall FUNC_100B64E0(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9); // .text:100B64E0 +int __stdcall FUNC_100B6510(int a1, int a2); // .text:100B6510 BOOL IsHexChar(char c); BOOL IsHexChar(wchar_t c); DWORD GetColorFromEmbedCode(char *szString); DWORD GetColorFromEmbedCode(wchar_t *szString); +void ReplaceUnprintableChars(char *szString); // .text:100B5DE0 void RemoveColorEmbedsFromString(char *szString); UINT GetVehicleSubtypeFromVehiclePtr(VEHICLE_TYPE *pVehicle); @@ -83,3 +101,73 @@ DWORD * GetNextTaskFromTask(DWORD *task); int GetTaskTypeFromTask(DWORD *task); const char* GetTaskNameFromTask(DWORD *task); +int FUNC_100B4BC0(int a1, int a2, int a3); // .text:100B4BC0 +float FastSqrt(float x); // .text:100B5A60 +void VectorCrossProduct(VECTOR *vecOut, VECTOR *vec1, VECTOR *vec2); // .text:100B4C20 +void VectorNormaliseTo(VECTOR *vecOut, VECTOR *vecIn); // .text:100B4CC0 +bool FUNC_100B4A70(VECTOR *vec); // .text:100B4A70 +bool FUNC_100B3D30(int nModelIndex); // .text:100B3D30 +int FUNC_100B41F0(void *pData); // .text:100B41F0 +int __stdcall FUNC_100B4240(DWORD *pdwIn, DWORD dwParam); // .text:100B4240 +void __stdcall FUNC_100B42B0(DWORD *pdwEntity); // .text:100B42B0 +DWORD __stdcall FUNC_100B4430(int a1, int a2); // .text:100B4430 +void FUNC_100B5D20(); // .text:100B5D20 +void FUNC_100B52B0(MATRIX4X4 *pMatrix, float *pQuatOut); // .text:100B52B0 +void FUNC_100B5480(float *pQuatOut, float *pQuatFrom, float *pQuatTo, float fT); // .text:100B5480 +void FUNC_100B6A80(float *pQuat, MATRIX4X4 *pMatrix); // .text:100B6A80 +void __stdcall FUNC_100B6900(VECTOR *pFront, VECTOR *pOut); // .text:100B6900 +void FUNC_100B5470(float *pQuat, MATRIX4X4 *pMatrix); // .text:100B5470 +void FUNC_100B5500(float *pQuat); // .text:100B5500 +int FUNC_100B4100(int iModel); // .text:100B4100 +int FUNC_100B4180(int iModel); // .text:100B4180 +int FUNC_100B1DA0(DWORD *pRenderWare, int a2, int a3); // .text:100B1DA0 +void FUNC_100B58A0(DWORD dwFrame, VECTOR *vecOut, int *a3); // .text:100B58A0 +void __stdcall FUNC_100B44C0(BYTE bytePlayer, DWORD dwValue); // .text:100B44C0 +DWORD __stdcall FUNC_100B4520(DWORD *pdw); // .text:100B4520 +void __stdcall FUNC_100B4530(DWORD *pdwVtable); // .text:100B4530 +void __stdcall FUNC_100B4550(DWORD *pdw, float fValue); // .text:100B4550 +float __stdcall FUNC_100B4560(DWORD *pdw); // .text:100B4560 +void __stdcall FUNC_100B4660(int iModel, int nTxdIndex); // .text:100B4660 +void FUNC_100B46C0(DWORD dwValue); // .text:100B46C0 +float FUNC_100B4FE0(float x, float y, float z); // .text:100B4FE0 +int FUNC_100B56D0(int a1, int a2, int a3); // .text:100B56D0 +void FUNC_100B5720(); // .text:100B5720 +BOOL __stdcall FUNC_100B4570(int iModel); // .text:100B4570 +DWORD __stdcall FUNC_100B45A0(int iModel); // .text:100B45A0 +void __stdcall FUNC_100B45D0(int iModel, int a2); // .text:100B45D0 +void __stdcall FUNC_100B4600(int iModel, int a2); // .text:100B4600 +void __stdcall FUNC_100B4630(int iModel, int a2); // .text:100B4630 +WORD __stdcall FUNC_100B4700(int iModel); // .text:100B4700 +void __stdcall FUNC_100B4730(int iModel, WORD wValue); // .text:100B4730 +BOOL __stdcall FUNC_100B44E0(DWORD *pModelInfo); // .text:100B44E0 +WORD FUNC_100B6470(int a1, int a2, int a3, int a4, int a5); // .text:100B6470 +void VectorScale(VECTOR *vecOut, VECTOR *vecIn, float fScale); // .text:100B51B0 +void FUNC_100B51E0(int a1, int a2); // .text:100B51E0 +float FUNC_100B5180(float *pQuat); // .text:100B5180 +DWORD FUNC_100B1430(BYTE *pObject); // .text:100B1430, todo: implement +void FUNC_100B17D0(DWORD dwOther); // .text:100B17D0, todo: implement +void FUNC_100B1800(BYTE *pObject); // .text:100B1800, todo: implement +void FUNC_100B19D0(BYTE *pObject); // .text:100B19D0, todo: implement +void FUNC_100B1C80(BYTE *pObject); // .text:100B1C80, todo: implement +void FUNC_100B5910(int a1, int a2); // .text:100B5910 +void FUNC_100B5940(int a1, int a2); // .text:100B5940 +void FUNC_100B58D0(int a1, int a2, int a3); // .text:100B58D0 +void FUNC_100B56F0(int a1, int a2, int a3); // .text:100B56F0 +void FUNC_100B62B0(BYTE *pObject); // .text:100B62B0 +BOOL __stdcall FUNC_100B62F0(BYTE *pObject); // .text:100B62F0 +void __stdcall FUNC_100B6330(BYTE *pObject); // .text:100B6330 +void FUNC_100B4D70(float m00, float m01, float m02, float m10, float m11, float m12, + float m20, float m21, float m22, float *pW, float *pX, float *pY, float *pZ); // .text:100B4D70, todo: implement +void FUNC_100B4F10(float w, float x, float y, float z, float *pM00, float *pM01, + float *pM02, float *pM10, float *pM11, float *pM12, float *pM20, float *pM21, + float *pM22); // .text:100B4F10, todo: implement +void FUNC_100B5260(MATRIX4X4 *pMatrix, float *pQuatOut); // .text:100B5260 +void FUNC_100B5420(float *pQuat, MATRIX4X4 *pMatrix); // .text:100B5420 +void FUNC_100B57E0(int a1, int nIndex, int a3, int a4); // .text:100B57E0 +void FUNC_100B5790(int a1, int nIndex, int a3); // .text:100B5790 +void FUNC_100B5740(int a1, int a2); // .text:100B5740 +void FUNC_100B5830(int a1, int nIndex, float *pfOut); // .text:100B5830 +void FUNC_100B5620(float *pQuat, float *pEuler); // .text:100B5620 +void FUNC_100B5210(int a1, void *a2); // .text:100B5210, __cdecl: the caller's +// stack cleanup is folded into the next call's argument setup + diff --git a/saco/game/vehicle.h b/saco/game/vehicle.h index 5eda50e..832baa4 100644 --- a/saco/game/vehicle.h +++ b/saco/game/vehicle.h @@ -20,7 +20,12 @@ public: VEHICLE_TYPE *m_pVehicle; - char _gap50[33]; + char _gap50[20]; + + BYTE field_64; + int field_65; + + char _gap69[8]; int field_71; @@ -30,7 +35,9 @@ public: BYTE m_byteColor2; BOOL m_bHasNewColor; - char _gap7F[57]; + char _gap7F[4]; + int field_83; + char _gap87[49]; CVehicle( int iType, float fPosX, float fPosY, float fPosZ, float fRotation = 0.0f, BOOL bKeepModelLoaded = FALSE, int a8 = 0); @@ -51,6 +58,12 @@ public: BOOL HasTurret(); BOOL HasADriver(); + void sub_100B7AC0(int iPanelDamage, int iDoorDamage, BYTE byteLightDamage); // .text:100B7AC0 + void sub_100B7940(BYTE byteTyreDamage); // .text:100B7940 + void FUNC_100B72C0(int a1); // .text:100B72C0 + void FUNC_100B8170(float a1); // .text:100B8170 + void FUNC_100B80B0(short a1); // .text:100B80B0 + void SetHydraThrusters(DWORD dwDirection); DWORD GetHydraThrusters(); @@ -71,6 +84,27 @@ public: BOOL IsOccupied(); + BOOL FUNC_100B8240(); // .text:100B8240 + BOOL FUNC_100B8330(); // .text:100B8330 + BOOL FUNC_100B7420(); // .text:100B7420 + + void FUNC_100B7090(int a1); // .text:100B7090 + void FUNC_100B7840(int a1); // .text:100B7840 + void FUNC_100B7900(float fT); // .text:100B7900 + void FUNC_100B81A0(); // .text:100B81A0 + void FUNC_100B81F0(); // .text:100B81F0 + void FUNC_100B8150(char *szText); // .text:100B8150 + void FUNC_100B7C10(); // .text:100B7C10 + void FUNC_100B7C80(int a1); // .text:100B7C80 + CVehicle *FUNC_100B7C90(); // .text:100B7C90 + BOOL FUNC_100B7D70(); // .text:100B7D70 + BOOL FUNC_100B7DD0(); // .text:100B7DD0 + BOOL FUNC_100B70E0(); // .text:100B70E0 + BOOL FUNC_100B7CF0(); // .text:100B7CF0 + + BOOL FUNC_100B7460(); // .text:100B7460 + BOOL FUNC_100B7E00(); // .text:100B7E00 + }; diff --git a/saco/main.cpp b/saco/main.cpp index 0c1d42b..39ac35b 100644 --- a/saco/main.cpp +++ b/saco/main.cpp @@ -20,7 +20,8 @@ CDeathWindow *pDeathWindow=0; CAudioStream *pAudioStream=0; CSpawnScreen *pSpawnScreen=0; CNetGame *pNetGame=0; -//DWORD dword_1026EB98=0; +class CDownloadListDispatch; +CDownloadListDispatch *dword_1026EB98=0; CFontRender *pDefaultFont=0; CUnkClass13 *pUnkClass13=0; CUnkClass14 *pUnkClass14=0; diff --git a/saco/main.h b/saco/main.h index d7397e8..ce8f899 100644 --- a/saco/main.h +++ b/saco/main.h @@ -55,6 +55,7 @@ typedef struct _GAME_SETTINGS { #include "net/menupool.h" #include "net/textdrawpool.h" #include "net/labelpool.h" +#include "net/downloader.h" #include "net/netgame.h" #include "net/scriptrpc.h" diff --git a/saco/net/actorpool.cpp b/saco/net/actorpool.cpp index 74c6cc0..3796a72 100644 --- a/saco/net/actorpool.cpp +++ b/saco/net/actorpool.cpp @@ -14,3 +14,11 @@ CActorPool::CActorPool() } //---------------------------------------------------- + +CActorPool::~CActorPool() +{ + for(ACTORID ActorID = 0; ActorID < MAX_ACTORS; ActorID++) + sub_100016F0(ActorID); +} + +//---------------------------------------------------- diff --git a/saco/net/actorpool.h b/saco/net/actorpool.h index 3bf4197..ccf6bdb 100644 --- a/saco/net/actorpool.h +++ b/saco/net/actorpool.h @@ -3,9 +3,22 @@ //---------------------------------------------------- +// 27 bytes, the block the create-actor RPC reads off the wire +typedef struct _ACTOR_INFO +{ + ACTORID ActorID; + int iModel; + VECTOR vecPos; + float fRotation; + float fHealth; + BYTE byteInvulnerable; +} ACTOR_INFO; + +//---------------------------------------------------- + class CActorPool { -private: +public: int field_0; int field_4[MAX_ACTORS]; int field_FA4[MAX_ACTORS]; @@ -16,6 +29,17 @@ private: public: CActorPool(); + ~CActorPool(); // .text:100018E0 + + void sub_100016F0(ACTORID ActorID); // .text:100016F0 + void sub_10001900(ACTOR_INFO *pActorInfo); // .text:10001900 + + int GetAt(ACTORID ActorID) { + if(ActorID >= MAX_ACTORS) return 0; + if(!field_FA4[ActorID]) return 0; + return field_4[ActorID]; + } + ACTORID FUNC_100018B0(DWORD dwPtr); // .text:100018B0 }; //---------------------------------------------------- diff --git a/saco/net/downloader.cpp b/saco/net/downloader.cpp new file mode 100644 index 0000000..49e91b9 --- /dev/null +++ b/saco/net/downloader.cpp @@ -0,0 +1,432 @@ +#include "../main.h" + +extern CChatWindow *pChatWindow; +extern CConfig *pConfig; + +CDownloadManager *dword_10118A24; + +int sub_100B5DC0(char *szPath); // .text:100B5DC0 +int sub_100A7C30(DWORD a1, DWORD a2, char *a3, char *a4, char *a5); // .text:100A7C30 +int sub_100A7BD0(DWORD a1, DWORD a2, char *a3, char *a4, char *a5); // .text:100A7BD0 +int sub_100B2040(DWORD dwCrc); // .text:100B2040 + +//---------------------------------------------------- + +// layout locks: every offset below came out of the disassembly, so a wrong one +// fails to compile instead of quietly mismatching +typedef char AssertSlotSize[sizeof(DOWNLOAD_SLOT) == 0x230 ? 1 : -1]; +typedef char AssertSlotPath[offsetof(DOWNLOAD_SLOT, szPath) == 0xC ? 1 : -1]; +typedef char AssertSlotRequest[offsetof(DOWNLOAD_SLOT, Request) == 0x112 ? 1 : -1]; +typedef char AssertRequestSize[sizeof(DOWNLOAD_REQUEST) == 0x11E ? 1 : -1]; +typedef char AssertRequestCrc[offsetof(DOWNLOAD_REQUEST, dwCrc) == 0x11 ? 1 : -1]; +typedef char AssertReadyFlag[offsetof(CDownloadManager, m_bSlotsReady) == 0x1344 ? 1 : -1]; +typedef char AssertSlotArray[offsetof(CDownloadManager, m_Slots) == 0x1345 ? 1 : -1]; +typedef char AssertEntryCount[offsetof(CDownloadList, m_dwCount) == 4 ? 1 : -1]; +typedef char AssertEntryState[offsetof(DOWNLOAD_ENTRY, field_7) == 7 ? 1 : -1]; +typedef char AssertEntryCrc[offsetof(DOWNLOAD_ENTRY, dwCrc) == 0x10 ? 1 : -1]; +typedef char AssertEntry46[offsetof(DOWNLOAD_ENTRY, field_46) == 0x46 ? 1 : -1]; +typedef char AssertEntry58[offsetof(DOWNLOAD_ENTRY, field_58) == 0x58 ? 1 : -1]; +typedef char AssertEntry5A[offsetof(DOWNLOAD_ENTRY, field_5A) == 0x5A ? 1 : -1]; + +//---------------------------------------------------- + +// retail spawns the workers on an empty body +void __cdecl DownloadThreadProc(void *pParam) +{ +} + +//---------------------------------------------------- + +// spawns a worker for every idle slot and clears its request block +bool CDownloadManager::FUNC_1000C010() +{ + for(int i = 0; i < MAX_DOWNLOAD_SLOTS; i++) + { + if(m_Slots[i].field_6) + continue; + + m_Slots[i].dwThread = _beginthread(DownloadThreadProc, 0, &m_Slots[i]); + if(m_Slots[i].dwThread == -1) + { + m_Slots[i].field_6 = 0; + m_Slots[i].field_10D = 1; + m_Slots[i].field_10E = -1; + return false; + } + + m_Slots[i].field_10E = -1; + m_Slots[i].dwSlotId = i + 1; + m_Slots[i].field_6 = 1; + m_Slots[i].field_7 = 0; + m_Slots[i].field_4 = 0; + m_Slots[i].field_10D = 0; + memset(m_Slots[i].szPath, 0, sizeof(m_Slots[i].szPath)); + memset(&m_Slots[i].Request, 0, sizeof(m_Slots[i].Request)); + } + + m_bSlotsReady = 1; + return true; +} + +//---------------------------------------------------- + +// the three removal helpers below only ever look in the first search dir +BOOL CDownloadList::FUNC_1000C590(DWORD dwId) +{ + char szPath[261]; + + _snprintf(szPath, sizeof(szPath) - 1, "%s\\0x%X.dff", szDir1, dwId); + return DeleteFile(szPath); +} + +//---------------------------------------------------- + +BOOL CDownloadList::FUNC_1000C5D0(DWORD dwId) +{ + char szPath[261]; + + _snprintf(szPath, sizeof(szPath) - 1, "%s\\0x%X.txd", szDir1, dwId); + return DeleteFile(szPath); +} + +//---------------------------------------------------- + +BOOL CDownloadList::FUNC_1000C610(char *szName) +{ + char szPath[261]; + + _snprintf(szPath, sizeof(szPath) - 1, "%s\\%s", szDir1, szName); + return DeleteFile(szPath); +} + +//---------------------------------------------------- + +bool CDownloadList::FUNC_1000D110(DWORD dwCrc) +{ + for(DWORD i = 0; i != m_dwCount; i++) + { + DOWNLOAD_ENTRY *pEntry = GetAt(i); + if(pEntry->dwCrc == dwCrc && pEntry->field_7 == 1) + return true; + } + + return false; +} + +//---------------------------------------------------- + +bool CDownloadList::FUNC_1000D150(DWORD dwCrc) +{ + for(DWORD i = 0; i != m_dwCount; i++) + { + DOWNLOAD_ENTRY *pEntry = GetAt(i); + if(pEntry->dwCrc == dwCrc && pEntry->field_7 == 2) + return true; + } + + return false; +} + +//---------------------------------------------------- + +char CDownloadList::FUNC_1000D190(DWORD dwCrc) +{ + for(DWORD i = 0; i != m_dwCount; i++) + { + DOWNLOAD_ENTRY *pEntry = GetAt(i); + if(pEntry->dwCrc == dwCrc) + return pEntry->field_58; + } + + return 0; +} + +//---------------------------------------------------- + +// ticks the retry countdown on every finished entry and retires it at zero +void CDownloadList::FUNC_1000D1E0() +{ + for(DWORD i = 0; i != m_dwCount; i++) + { + DOWNLOAD_ENTRY *pEntry = GetAt(i); + if(pEntry->field_58 && pEntry->field_59) + { + if(!pEntry->field_5A) + { + sub_100B2040(pEntry->dwCrc); + pEntry->field_59 = 0; + pEntry->field_58 = 0; + } + else + { + pEntry->field_5A--; + } + } + } +} + +//---------------------------------------------------- + +DWORD CDownloadList::FUNC_1000D240(DWORD dwCrc) +{ + for(DWORD i = 0; i != m_dwCount; i++) + { + DOWNLOAD_ENTRY *pEntry = GetAt(i); + if(pEntry && pEntry->dwCrc == dwCrc && pEntry->field_7 == 2) + return pEntry->field_46; + } + + return 0; +} + +//---------------------------------------------------- + +DWORD CDownloadList::FUNC_1000D280(DWORD dwCrc) +{ + for(DWORD i = 0; i != m_dwCount; i++) + { + DOWNLOAD_ENTRY *pEntry = GetAt(i); + if(pEntry && pEntry->dwCrc == dwCrc && pEntry->field_7 == 2) + return pEntry->field_4A; + } + + return 0; +} + +//---------------------------------------------------- + +// the FUNC_1000D320 twin for entries still in state 1 +int CDownloadList::FUNC_1000D2C0(DWORD dwCrc) +{ + for(DWORD i = 0; i != m_dwCount; i++) + { + DOWNLOAD_ENTRY *pEntry = GetAt(i); + if(pEntry->dwCrc != dwCrc || pEntry->field_7 != 1) + continue; + + if(!FUNC_1000C650(pEntry->field_C, pEntry->dwCrc, pEntry->field_46, pEntry->field_4A)) + return 0; + + pEntry->field_59 = 0; + pEntry->field_58 = 1; + return 1; + } + + return 0; +} + +//---------------------------------------------------- + +// same two-dir probe as FUNC_1000C770, only the handoff differs +int CDownloadList::FUNC_1000C650(DWORD a1, DWORD dwCrc, DWORD a3, DWORD a4) +{ + char szCrc[28]; + char szDff[261]; + char szTxd[261]; + + sprintf(szCrc, "%X", a4); + + sprintf(szDff, "%s\\0x%X.dff", szDir1, a3); + if(!sub_100B5DC0(szDff)) + { + sprintf(szDff, "%s\\0x%X.dff", szDir2, a3); + if(!sub_100B5DC0(szDff)) + return 0; + } + + sprintf(szTxd, "%s\\0x%X.txd", szDir1, a4); + if(!sub_100B5DC0(szTxd)) + { + sprintf(szTxd, "%s\\0x%X.txd", szDir2, a4); + if(!sub_100B5DC0(szTxd)) + return 0; + } + + return sub_100A7BD0(a1, dwCrc, szCrc, szDff, szTxd) != 0; +} + +//---------------------------------------------------- + +// both model files have to be present in one of the two search dirs +int CDownloadList::FUNC_1000C770(DWORD a1, DWORD dwCrc, DWORD a3, DWORD a4) +{ + char szCrc[28]; + char szDff[261]; + char szTxd[261]; + + sprintf(szCrc, "%X", a4); + + sprintf(szDff, "%s\\0x%X.dff", szDir1, a3); + if(!sub_100B5DC0(szDff)) + { + sprintf(szDff, "%s\\0x%X.dff", szDir2, a3); + if(!sub_100B5DC0(szDff)) + return 0; + } + + sprintf(szTxd, "%s\\0x%X.txd", szDir1, a4); + if(!sub_100B5DC0(szTxd)) + { + sprintf(szTxd, "%s\\0x%X.txd", szDir2, a4); + if(!sub_100B5DC0(szTxd)) + return 0; + } + + return sub_100A7C30(a1, dwCrc, szCrc, szDff, szTxd) != 0; +} + +//---------------------------------------------------- + +// promotes a staged entry once its file checks out; any failed check gives up +// on the whole lookup rather than scanning on +int CDownloadList::FUNC_1000D320(DWORD dwCrc) +{ + for(DWORD i = 0; i != m_dwCount; i++) + { + DOWNLOAD_ENTRY *pEntry = GetAt(i); + if(!pEntry) + continue; + if(pEntry->dwCrc != dwCrc || pEntry->field_7 != 2) + continue; + + if(!pEntry->field_0) + return 0; + if(!pEntry->field_1) + return 0; + if(!FUNC_1000C770(pEntry->field_C, pEntry->dwCrc, pEntry->field_46, pEntry->field_4A)) + return 0; + + pEntry->field_59 = 0; + pEntry->field_58 = 1; + return 1; + } + + return 0; +} + +//---------------------------------------------------- + +// parks a request in the first live idle slot; the workers pick it up from there +void CDownloadManager::AddURLRequest(DOWNLOAD_REQUEST *pRequest, char *szUrl) +{ + if(pChatWindow && pConfig && pConfig->GetIntVariable("logurls")) + pChatWindow->AddInfoMessage("[url 0x%X]: %s", pRequest->dwCrc, szUrl); + + dword_10118A24 = this; + + if(!m_bSlotsReady) + FUNC_1000C010(); + + for(int i = 0; i < MAX_DOWNLOAD_SLOTS; i++) + { + if(m_Slots[i].field_6 && !m_Slots[i].field_4) + { + memcpy(&m_Slots[i].Request, pRequest, sizeof(DOWNLOAD_REQUEST)); + strncpy(m_Slots[i].szPath, szUrl, sizeof(m_Slots[i].szPath) - 1); + m_Slots[i].field_4 = 1; + m_Slots[i].field_5 = 0; + return; + } + } + + if(pChatWindow) + pChatWindow->AddDebugMessage("[error] AddURLRequest: No free slots!"); +} + +//---------------------------------------------------- + +// flags every slot for cancellation and drops the ready bit so the next +// request re-spawns the workers +void CDownloadManager::FUNC_1000C0C0() +{ + for(int i = 0; i < MAX_DOWNLOAD_SLOTS; i++) + m_Slots[i].field_7 = 1; + + m_bSlotsReady = 0; +} + +//---------------------------------------------------- + +// claims the first idle slot; field_5 is the hold flag FUNC_1000C140 releases +bool CDownloadManager::FUNC_1000C0F0() +{ + if(!m_bSlotsReady) + return false; + + for(int i = 0; i < MAX_DOWNLOAD_SLOTS; i++) + { + if(m_Slots[i].field_6 && !m_Slots[i].field_4 && !m_Slots[i].field_5) + { + m_Slots[i].field_5 = 1; + return true; + } + } + + return false; +} + +//---------------------------------------------------- + +void CDownloadManager::FUNC_1000C140() +{ + if(!m_bSlotsReady) + return; + + for(int i = 0; i < MAX_DOWNLOAD_SLOTS; i++) + { + if(m_Slots[i].field_6 && !m_Slots[i].field_4 && m_Slots[i].field_5) + { + m_Slots[i].field_5 = 0; + return; + } + } +} + +//---------------------------------------------------- + +// the three counters below are 6-way unrolled by the compiler +int CDownloadManager::FUNC_1000C190() +{ + int iCount = 0; + + for(int i = 0; i < MAX_DOWNLOAD_SLOTS; i++) + { + if(m_Slots[i].field_6 && !m_Slots[i].field_4 && !m_Slots[i].field_5) + iCount++; + } + + return iCount; +} + +//---------------------------------------------------- + +int CDownloadManager::FUNC_1000C260() +{ + int iCount = 0; + + for(int i = 0; i < MAX_DOWNLOAD_SLOTS; i++) + { + if(m_Slots[i].field_6 && m_Slots[i].field_4) + iCount++; + } + + return iCount; +} + +//---------------------------------------------------- + +int CDownloadManager::FUNC_1000C2F0() +{ + int iCount = 0; + + for(int i = 0; i < MAX_DOWNLOAD_SLOTS; i++) + { + if(m_Slots[i].field_6 && m_Slots[i].field_5) + iCount++; + } + + return iCount; +} + +//---------------------------------------------------- +// EOF diff --git a/saco/net/downloader.h b/saco/net/downloader.h new file mode 100644 index 0000000..7b14cc7 --- /dev/null +++ b/saco/net/downloader.h @@ -0,0 +1,120 @@ +#pragma once + +//---------------------------------------------------- + +#define MAX_DOWNLOAD_SLOTS 6 + +//---------------------------------------------------- + +// the block AddURLRequest copies into a slot, 0x11E bytes +struct DOWNLOAD_REQUEST +{ + char _gap0[0x11]; + DWORD dwCrc; + char _gap15[0x109]; +}; + +//---------------------------------------------------- + +// one worker slot, 0x230 bytes; offsets pinned by 0x1000C010 and 0x1000C0C0 +struct DOWNLOAD_SLOT +{ + DWORD dwSlotId; // set to the 1-based slot index + char field_4; + char field_5; + char field_6; // slot live flag + char field_7; // cancel flag + DWORD dwThread; // _beginthread handle, -1 when the spawn failed + char szPath[257]; + char field_10D; + DWORD field_10E; // initialised to -1 + DOWNLOAD_REQUEST Request; +}; + +//---------------------------------------------------- + +// the record the lookups below match on; only the touched fields are known +struct DOWNLOAD_ENTRY +{ + char field_0; + char field_1; + char _gap2[5]; + char field_7; + char _gap8[4]; + DWORD field_C; + DWORD dwCrc; + char _gap14[0x32]; + DWORD field_46; + DWORD field_4A; + char _gap4E[0xA]; + char field_58; + char field_59; + DWORD field_5A; // retry countdown +}; + +//---------------------------------------------------- + +// the list the game side queries; whether it is embedded in CDownloadManager +// at +0 is still open, the offsets are the same either way +class CDownloadList +{ +public: + + DOWNLOAD_ENTRY **m_pEntries; + DWORD m_dwCount; + char field_8; + char szDir1[0x105]; + char szDir2[0x105]; + char _gap213[4]; + DWORD field_217; + char _gap21B[5]; + BYTE field_220; + BYTE field_221; + + void FUNC_10017D10(int a1) { // .text:10017D10 + if(field_217 != a1) { + field_217 = a1; + field_221 = 0; + field_220 = 0; + } + }; + + DOWNLOAD_ENTRY *GetAt(DWORD dwIndex) { if(dwIndex >= m_dwCount) return NULL; return m_pEntries[dwIndex]; } + + BOOL FUNC_1000C590(DWORD dwId); // .text:1000C590 + BOOL FUNC_1000C5D0(DWORD dwId); // .text:1000C5D0 + BOOL FUNC_1000C610(char *szName); // .text:1000C610 + bool FUNC_1000D110(DWORD dwCrc); // .text:1000D110 + bool FUNC_1000D150(DWORD dwCrc); // .text:1000D150 + char FUNC_1000D190(DWORD dwCrc); // .text:1000D190 + void FUNC_1000D1E0(); // .text:1000D1E0 + DWORD FUNC_1000D240(DWORD dwCrc); // .text:1000D240 + DWORD FUNC_1000D280(DWORD dwCrc); // .text:1000D280 + int FUNC_1000D2C0(DWORD dwCrc); // .text:1000D2C0 + int FUNC_1000C650(DWORD a1, DWORD dwCrc, DWORD a3, DWORD a4); // .text:1000C650 + int FUNC_1000C770(DWORD a1, DWORD dwCrc, DWORD a3, DWORD a4); // .text:1000C770 + int FUNC_1000D320(DWORD dwCrc); // .text:1000D320 +}; + +//---------------------------------------------------- + +class CDownloadManager +{ +public: + + char _gap0[0x1344]; + BYTE m_bSlotsReady; + DOWNLOAD_SLOT m_Slots[MAX_DOWNLOAD_SLOTS]; + + bool FUNC_1000C010(); // .text:1000C010 + void AddURLRequest(DOWNLOAD_REQUEST *pRequest, char *szUrl); // .text:1000C380 + void FUNC_1000C0C0(); // .text:1000C0C0 + bool FUNC_1000C0F0(); // .text:1000C0F0 + void FUNC_1000C140(); // .text:1000C140 + int FUNC_1000C190(); // .text:1000C190 + int FUNC_1000C260(); // .text:1000C260 + int FUNC_1000C2F0(); // .text:1000C2F0 +}; + +//---------------------------------------------------- +// EOF diff --git a/saco/net/labelpool.h b/saco/net/labelpool.h index 51e1010..6e8a98d 100644 --- a/saco/net/labelpool.h +++ b/saco/net/labelpool.h @@ -9,7 +9,7 @@ typedef struct _LABEL class CLabelPool { -private: +public: LABEL m_Labels[MAX_LABELS]; BOOL m_bLabelSlotState[MAX_LABELS]; public: @@ -18,4 +18,10 @@ public: BOOL Delete(WORD wLabelID); + void FUNC_10001350(); // .text:10001350 + + // .text:100011D0 + void sub_100011D0(WORD wLabelID, PCHAR szText, DWORD dwColor, float fX, float fY, float fZ, + float fDrawDistance, BYTE byteShowBehindWalls, WORD wAttachedPlayer, WORD wAttachedVehicle); + }; diff --git a/saco/net/localplayer.cpp b/saco/net/localplayer.cpp index db6f8fc..1967182 100644 --- a/saco/net/localplayer.cpp +++ b/saco/net/localplayer.cpp @@ -1,9 +1,29 @@ #include "../main.h" #include "../game/util.h" +#include "../game/keystuff.h" +#include "../game/aimstuff.h" extern CGame *pGame; extern CNetGame *pNetGame; +extern CCmdWindow *pCmdWindow; +extern CUnkClass3 *pUnkClass3; +extern CUnkClass10 *pUnkClass10; +extern CDXUTDialog *pClassSelectDialog; + +DWORD dword_100FE0A4 = -1; +DWORD dword_100FE0A8 = 30; +DWORD dword_100FE0AC = 30; +DWORD dword_101189A0; +DWORD dword_101189A4; + +//---------------------------------------------------------- + +// layout locks for the fields split out of the pads +typedef char AssertLocalSize[sizeof(CLocalPlayer) == 0x324 ? 1 : -1]; +typedef char AssertField14B[offsetof(CLocalPlayer, field_14B) == 0x14B ? 1 : -1]; +typedef char AssertSpawnInfo[offsetof(CLocalPlayer, m_SpawnInfo) == 0x14F ? 1 : -1]; +typedef char AssertField314[offsetof(CLocalPlayer, field_314) == 0x314 ? 1 : -1]; //---------------------------------------------------------- @@ -83,7 +103,12 @@ void CLocalPlayer::ResetAllSyncAttributes() void CLocalPlayer::Say(PCHAR szText) { - // TODO: CLocalPlayer::Say .text:10005A10 + BYTE byteTextLength = strlen(szText); + RakNet::BitStream bsSend; + + bsSend.Write(byteTextLength); + bsSend.Write(szText, byteTextLength); + pNetGame->GetRakClient()->RPC(RPC_Chat, &bsSend, HIGH_PRIORITY, RELIABLE, 0, FALSE); } //---------------------------------------------------------- @@ -109,3 +134,716 @@ DWORD CLocalPlayer::GetPlayerColorAsARGB() //---------------------------------------------------- +//---------------------------------------------------- + +// the damage reports scale the amount down to a third before sending +void CLocalPlayer::FUNC_100068A0(WORD a1, float a2, int a3, int a4) +{ + a2 *= 0.33f; + + RakNet::BitStream bsSend; + + bsSend.Write1(); + bsSend.Write(a1); + bsSend.Write(a2); + bsSend.Write(a3); + bsSend.Write(a4); + pNetGame->GetRakClient()->RPC(RPC_PlayerDamage, &bsSend, HIGH_PRIORITY, RELIABLE_ORDERED, 0, FALSE); +} + +//---------------------------------------------------- + +void CLocalPlayer::FUNC_100069B0(WORD a1, float a2, int a3, int a4) +{ + a2 *= 0.33f; + + RakNet::BitStream bsSend; + + bsSend.Write0(); + bsSend.Write(a1); + bsSend.Write(a2); + bsSend.Write(a3); + bsSend.Write(a4); + pNetGame->GetRakClient()->RPC(RPC_PlayerDamage, &bsSend, HIGH_PRIORITY, RELIABLE_ORDERED, 0, FALSE); +} + +//---------------------------------------------------- + +void CLocalPlayer::FUNC_10006AC0(WORD a1, float a2, int a3, int a4) +{ + RakNet::BitStream bsSend; + + bsSend.Write0(); + bsSend.Write(a1); + bsSend.Write(a2); + bsSend.Write(a3); + bsSend.Write(a4); + pNetGame->GetRakClient()->RPC(RPC_ActorDamage, &bsSend, HIGH_PRIORITY, RELIABLE_ORDERED, 0, FALSE); +} + +//---------------------------------------------------- + +void CLocalPlayer::RequestClass(int iClass) +{ + RakNet::BitStream bsSend; + + bsSend.Write(iClass); + pNetGame->GetRakClient()->RPC(RPC_RequestClass, &bsSend, HIGH_PRIORITY, RELIABLE, 0, FALSE); +} + +//---------------------------------------------------- + +void CLocalPlayer::SendStatsUpdate() +{ + RakNet::BitStream bsSend; + + int iMoney = pGame->FUNC_100A0F90(); + int iDrunkLevel = m_pPlayerPed->sub_100ADFA0(); + + bsSend.Write((BYTE)ID_STATS_UPDATE); + bsSend.Write(iMoney); + bsSend.Write(iDrunkLevel); + pNetGame->GetRakClient()->Send(&bsSend, HIGH_PRIORITY, UNRELIABLE, 0); +} + +//---------------------------------------------------- + +// class selection cycles field_2FE and re-requests on every step +void CLocalPlayer::SelectNextClass() +{ + if(!field_306) + return; + + MATRIX4X4 mat; + + field_143 = 0; + m_pPlayerPed->GetMatrix(&mat); + + if(field_2FE == pNetGame->GetSettings()->field_23 - 1) + field_2FE = 0; + else + field_2FE++; + + pGame->m_pGameAudio->PlaySound(1052, mat.pos.X, mat.pos.Y, mat.pos.Z); + RequestClass(field_2FE); +} + +//---------------------------------------------------- + +void CLocalPlayer::SelectPreviousClass() +{ + if(!field_306) + return; + + MATRIX4X4 mat; + + field_143 = 0; + m_pPlayerPed->GetMatrix(&mat); + + DWORD dwClass = field_2FE; + if(!dwClass) + dwClass = pNetGame->GetSettings()->field_23; + + field_2FE = dwClass - 1; + pGame->m_pGameAudio->PlaySound(1053, mat.pos.X, mat.pos.Y, mat.pos.Z); + RequestClass(field_2FE); +} + +//---------------------------------------------------- + +// leaving a vehicle; every guard shares one exit so the nesting has to stay +void CLocalPlayer::FUNC_10005BF0(WORD a1) +{ + RakNet::BitStream bsSend; + + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + + if(a1 < MAX_VEHICLES && pVehiclePool->field_3074[a1]) + { + int pVehicle = pVehiclePool->field_1134[a1]; + if(pVehicle) + { + if(!m_pPlayerPed->FUNC_100ABFC0()) + field_FA = a1; + + if(((CVehicle *)pVehicle)->FUNC_100B7460()) + pGame->m_pGameCamera->FUNC_1009D740(); + + if(!((CVehicle *)pVehicle)->FUNC_100B7E00()) + { + bsSend.Write(a1); + pNetGame->GetRakClient()->RPC(RPC_ExitVehicle, &bsSend, HIGH_PRIORITY, RELIABLE_SEQUENCED, 0, FALSE); + } + } + } +} + +//---------------------------------------------------- + +// entering a vehicle; the camera hand-off only runs for the vehicles that ask +void CLocalPlayer::FUNC_10005AD0(WORD a1, int a2) +{ + RakNet::BitStream bsSend; + + BYTE byteFlag = 0; + + if(a2) + byteFlag = 1; + + bsSend.Write(a1); + bsSend.Write(byteFlag); + pNetGame->GetRakClient()->RPC(RPC_EnterVehicle, &bsSend, HIGH_PRIORITY, RELIABLE_SEQUENCED, 0, FALSE); + + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + + if(a1 < MAX_VEHICLES && pVehiclePool->field_3074[a1]) + { + CVehicle *pVehicle = (CVehicle *)pVehiclePool->field_1134[a1]; + if(pVehicle && pVehicle->FUNC_100B7460()) + { + DWORD dwGtaId = pVehicle->m_dwGTAId; + + ScriptCommand(&camera_on_vehicle, dwGtaId, 3, 2); + dword_100FE0A4 = GetTickCount(); + } + } +} + +//---------------------------------------------------- + +// the enter-vehicle key handler; 46 is the parachute, which has to be put away +void CLocalPlayer::FUNC_10006FE0() +{ + GTA_CONTROLSET *pKeys = GameGetInternalKeys(); + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + + if(pKeys->wKeys1[8] && !pKeys->wKeys2[8]) + { + WORD wVehicleId = pVehiclePool->FUNC_1001EC00(); + if(wVehicleId < MAX_VEHICLES && pVehiclePool->field_3074[wVehicleId]) + { + CVehicle *pVehicle = (CVehicle *)pVehiclePool->FUNC_10001120(wVehicleId); + if(pVehicle->FUNC_1009F0C0() < 8.0f) + { + if(m_pPlayerPed->FUNC_100ABC50() == 46) + m_pPlayerPed->SetArmedWeapon(0, 0); + + m_pPlayerPed->FUNC_100AC410(pVehicle->m_dwGTAId, 1); + FUNC_10005AD0(wVehicleId, 1); + } + } + } +} + +//---------------------------------------------------- + +void CLocalPlayer::sub_10003710(int a1, int a2) +{ + field_2DA = a1; + field_2DE = a2; +} + +//---------------------------------------------------- + +void CLocalPlayer::sub_10004060() +{ + if(field_306) + { + RequestSpawn(); + field_302 = 1; + } +} + +//---------------------------------------------------- + +// on foot and in a vehicle report their update rate separately +int CLocalPlayer::FUNC_10003B30() +{ + if(m_pPlayerPed) + { + if(m_pPlayerPed->IsInVehicle()) + return FUNC_10003AB0(); + + return FUNC_10003AF0(); + } + + return 1000; +} + +//---------------------------------------------------- + +// the three words live inside field_0, so they are read through it +BOOL CLocalPlayer::FUNC_10004340(WORD a1, WORD a2, WORD a3) +{ + if(a1 == *(WORD *)&field_0[6] && a2 == *(WORD *)&field_0[4] && a3 == *(WORD *)&field_0[2]) + return FALSE; + + return TRUE; +} + +//---------------------------------------------------- + +// the sync interval, 15ms while the server asks for it and 30ms otherwise +int CLocalPlayer::FUNC_10003AB0() +{ + if(m_pPlayerPed) + { + if(pNetGame->GetField239()) + return 15; + + return dword_100FE0AC + pGame->FUNC_100A00F0(); + } + + return 1000; +} + +//---------------------------------------------------- + +int CLocalPlayer::FUNC_10003AF0() +{ + if(m_pPlayerPed) + { + if(pNetGame->GetField239()) + return 15; + + return dword_100FE0A8 + pGame->FUNC_100A00F0(); + } + + return 1000; +} + +//---------------------------------------------------- + +BOOL CLocalPlayer::FUNC_10004300(WORD a1, WORD a2, WORD a3) +{ + if(a1 == *(WORD *)&field_94[4] && a2 == *(WORD *)&field_94[2] && a3 == *(WORD *)&field_94[0]) + return FALSE; + + return TRUE; +} + +//---------------------------------------------------- + +PLAYERID CLocalPlayer::FUNC_10004B70() +{ + if(!m_pPlayerPed) + return -1; + + int iPed = m_pPlayerPed->FUNC_100AEF60(); + if(!iPed) + return -1; + + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + if(!pPlayerPool) + return -1; + + return pPlayerPool->FUNC_100138C0(iPed); +} + +//---------------------------------------------------- + +ACTORID CLocalPlayer::FUNC_10004BB0() +{ + if(!m_pPlayerPed) + return -1; + + int iPed = m_pPlayerPed->FUNC_100AEF60(); + if(!iPed) + return -1; + + CActorPool *pActorPool = pNetGame->GetActorPool(); + if(!pActorPool) + return -1; + + return pActorPool->FUNC_100018B0(iPed); +} + +//---------------------------------------------------- + +// only accepts a spawn point the game says is inside the world +void CLocalPlayer::sub_10003BE0(SPAWN_INFO *pSpawnInfo) +{ + if(FUNC_100B4B50(&pSpawnInfo->vecPos)) + { + memcpy(&m_SpawnInfo, pSpawnInfo, sizeof(SPAWN_INFO)); + field_17D = 1; + } +} + +//---------------------------------------------------- + +void CLocalPlayer::sub_100040E0(BOOL bSpawn) +{ + if(bSpawn) + { + if(m_pPlayerPed) + { + m_pPlayerPed->FUNC_100AC010(); + m_pPlayerPed->SetSkin(m_SpawnInfo.iSkin); + } + + field_143 = 1; + } +} + +//---------------------------------------------------- + +void CLocalPlayer::FUNC_10004080() +{ + field_143 = 0; + + if(m_pPlayerPed) + { + m_pPlayerPed->FUNC_100ABBD0(); + m_pPlayerPed->FUNC_100ABD70(100.0f); + m_pPlayerPed->FUNC_100AC790(0); + } + + field_14B = GetTickCount(); + field_147 = GetTickCount(); +} + +//---------------------------------------------------- + +// resends at most twice a second unless the block actually changed +BOOL CLocalPlayer::FUNC_10003A60(void *a2, void *a3, unsigned int a4) +{ + if(GetTickCount() - field_13F > 500) + { + field_13F = GetTickCount(); + return TRUE; + } + + if(memcmp(a2, a3, a4)) + { + field_13F = GetTickCount(); + return TRUE; + } + + return FALSE; +} + +//---------------------------------------------------- + +void CLocalPlayer::FUNC_10004230(WORD a1) +{ + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + + if(pVehiclePool && a1 < MAX_VEHICLES && pVehiclePool->field_3074[a1]) + { + field_30F = 2; + field_310 = a1; + field_314 = 0; + } +} + +//---------------------------------------------------- + +void CLocalPlayer::RequestSpawn() +{ + RakNet::BitStream bsSend; + + pNetGame->GetRakClient()->RPC(RPC_RequestSpawn, &bsSend, HIGH_PRIORITY, RELIABLE, 0, FALSE); +} + +//---------------------------------------------------- + +// straying outside the server boundary disarms and nags +void CLocalPlayer::FUNC_10003F50() +{ + if(!pGame->FUNC_100A0EE0() && m_pPlayerPed->FUNC_1009F420(pNetGame->GetSettings()->fWorldBoundryPX, + pNetGame->GetSettings()->fWorldBoundryZX, pNetGame->GetSettings()->fWorldBoundryPY, + pNetGame->GetSettings()->fWorldBoundryNY)) + { + m_pPlayerPed->SetArmedWeapon(0, 0); + pGame->DisplayGameText("Stay within the ~r~world boundries", 1000, 5); + } +} + +//---------------------------------------------------- + +// leaving spectate only resets the spawn state when another class is pending +void CLocalPlayer::FUNC_100041C0(BOOL bSpectating) +{ + if(m_bIsSpectating && !m_bWantsAnotherClass && !bSpectating) + sub_10003C20(); + else if(m_bWantsAnotherClass && !bSpectating) + { + field_F0 = 0; + field_17D = 0; + } + + m_bIsSpectating = bSpectating; + field_30F = 0; + field_314 = 0; + field_310 = -1; +} + +//---------------------------------------------------- + +// picks a player to spectate; state 32 is not a spectatable one +void CLocalPlayer::FUNC_10004280(PLAYERID playerId) +{ + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + + if(pPlayerPool && pPlayerPool->GetSlotState(playerId) && pPlayerPool->GetAt(playerId)->field_10A + && pPlayerPool->GetAt(playerId)->field_10A != 32) + { + field_30F = 1; + field_310 = playerId; + field_314 = 0; + } +} + +//---------------------------------------------------- + +// the in-vehicle half of the boundary check, keyed off the driven vehicle +void CLocalPlayer::FUNC_10003FC0() +{ + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + WORD wVehicleId = pVehiclePool->FUNC_1001EB90((int)m_pPlayerPed->GetGtaVehicle()); + + if(!pGame->FUNC_100A0EE0() && wVehicleId != INVALID_VEHICLE_ID && wVehicleId < MAX_VEHICLES + && pVehiclePool->field_3074[wVehicleId]) + { + CVehicle *pVehicle = (CVehicle *)pVehiclePool->field_1134[wVehicleId]; + + if(pVehicle && pVehicle->FUNC_1009F420(pNetGame->GetSettings()->fWorldBoundryPX, + pNetGame->GetSettings()->fWorldBoundryZX, pNetGame->GetSettings()->fWorldBoundryPY, + pNetGame->GetSettings()->fWorldBoundryNY)) + pGame->DisplayGameText("Stay within the ~r~world boundries", 1000, 5); + } +} + +//---------------------------------------------------- + +// counts the spawned players, recomputed once every 30 calls +int FUNC_10004120() +{ + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + WORD i = 0; + + if(dword_101189A4) + { + dword_101189A4--; + return dword_101189A0; + } + + int iCount = 0; + + dword_101189A4 = 30; + dword_101189A0 = 0; + + if(pPlayerPool) + { + int iMax = pPlayerPool->field_2F3A; + + for(; i <= iMax; i++) + { + if(pPlayerPool->GetSlotState(i)) + { + CRemotePlayer *pRemotePlayer = pPlayerPool->GetAt(i); + + if(pRemotePlayer->m_pPlayerPed && pRemotePlayer->field_10A) + dword_101189A0 = ++iCount; + } + } + } + + return iCount; +} + +//---------------------------------------------------- + +// reports the death reason and who did it +void CLocalPlayer::SendDeath() +{ + RakNet::BitStream bsSend; + WORD wKiller; + + BYTE byteReason = m_pPlayerPed->FUNC_100AC850(&wKiller); + + bsSend.Write(byteReason); + bsSend.Write(wKiller); + pNetGame->GetRakClient()->RPC(RPC_Death, &bsSend, HIGH_PRIORITY, RELIABLE_ORDERED, 0, FALSE); +} + +//---------------------------------------------------- + +void CLocalPlayer::SetInteriorId(BYTE byteInteriorId) +{ + field_1CF = byteInteriorId; + + RakNet::BitStream bsSend; + + bsSend.Write(byteInteriorId); + pNetGame->GetRakClient()->RPC(RPC_SetInteriorId, &bsSend, HIGH_PRIORITY, RELIABLE, 0, FALSE); +} + +//---------------------------------------------------- + +// drives class selection: centres the dialog on entry, then steps on the arrows +void CLocalPlayer::FUNC_10006100() +{ + if(!pGame->sub_100A0920()) + { + pGame->sub_100A1DB0(0); + pGame->ToggleKeyInputsDisabled(2, 0); + } + + if(pUnkClass3 && pUnkClass3->GetField28()) + return; + + if(!field_306) + { + RECT rect; + + pClassSelectDialog->SetVisible(true); + GetClientRect(pGame->GetMainWindowHwnd(), &rect); + pClassSelectDialog->SetLocation(rect.right / 2 - pClassSelectDialog->GetWidth() / 2, + rect.bottom - pClassSelectDialog->GetHeight() - 50); + + field_306 = 1; + RequestClass(field_2FE); + return; + } + + if(GetTickCount() - field_14B < 2000) + return; + + if(!field_302) + { + if(!field_143) + return; + + if((GetAsyncKeyState(VK_SHIFT) & 0x8000) && !pCmdWindow->GetField14E0()) + { + sub_10004060(); + return; + } + } + + if(!field_143) + return; + + DWORD dwElapsed = GetTickCount() - field_147; + + if((GetAsyncKeyState(VK_LEFT) & 0x8000) && dwElapsed > 250) + { + field_147 = GetTickCount(); + SelectPreviousClass(); + return; + } + + if((GetAsyncKeyState(VK_RIGHT) & 0x8000) && dwElapsed > 250) + { + field_147 = GetTickCount(); + SelectNextClass(); + } +} + +//---------------------------------------------------- + +// keeps the FollowPedSA marker on the aim point, refreshed five times a second +void CLocalPlayer::FUNC_10002FA0() +{ + if(m_pPlayerPed && (!pGame || pGame->m_bHeadMove) && !m_pPlayerPed->FUNC_100ADC90() + && (!pUnkClass10 || !pUnkClass10->GetField80())) + { + MATRIX4X4 mat; + + m_pPlayerPed->GetMatrix(&mat); + + DWORD dwTick = GetTickCount(); + + if(dwTick - field_137 > 1000) + { + CAMERA_AIM *pAim = GameGetInternalAim(); + + float fX = pAim->f1x * 20.0f + mat.pos.X; + float fY = pAim->f1y * 20.0f + mat.pos.Y; + float fZ = pAim->f1z * 20.0f + mat.pos.Z; + + field_137 = dwTick; + *(float *)&field_12B[0] = fX - mat.pos.X; + *(float *)&field_12B[4] = fY - mat.pos.Y; + *(float *)&field_12B[8] = fZ - mat.pos.Z; + } + + if(dwTick - field_13B > 200) + { + VECTOR vecTarget; + + vecTarget.X = mat.pos.X + *(float *)&field_12B[0]; + vecTarget.Y = mat.pos.Y + *(float *)&field_12B[4]; + vecTarget.Z = mat.pos.Z + *(float *)&field_12B[8]; + + m_pPlayerPed->FUNC_100ADFD0("FollowPedSA", 0, 2000, -1, &vecTarget, 0, 0.1f, 500, 3, 0); + field_13B = dwTick; + } + } +} + +//---------------------------------------------------- + +// the special action code the sync packets carry; 5 to 8 are the dance styles +BYTE CLocalPlayer::GetSpecialAction() +{ + if(m_pPlayerPed->FUNC_100ACDC0()) + return 2; + + if(m_pPlayerPed->FUNC_100ADB60()) + { + switch(m_pPlayerPed->m_iDanceStyle) + { + case 0: + return 5; + case 1: + return 6; + case 2: + return 7; + case 3: + return 8; + } + } + + if(m_pPlayerPed->FUNC_100ACB60()) + return 10; + + if(m_pPlayerPed->FUNC_100AE0A0()) + return 11; + + if(m_pPlayerPed->FUNC_100AE260()) + return 68; + + if(m_pPlayerPed->FUNC_100AC690()) + return 1; + + if(m_pPlayerPed->FUNC_100AC5D0()) + return 3; + + if(m_pPlayerPed->FUNC_100AC640()) + return 4; + + if(m_pPlayerPed->FUNC_100ADC90()) + { + if(m_pPlayerPed->FUNC_100ADC90() == 1) + return 20; + + if(m_pPlayerPed->FUNC_100ADC90() == 2) + return 22; + + if(m_pPlayerPed->FUNC_100ADC90() == 3) + return 23; + + if(m_pPlayerPed->FUNC_100ADC90() == 4) + return 21; + } + + if(m_pPlayerPed->FUNC_100ACEF0()) + return 24; + + BYTE byteAction = m_pPlayerPed->FUNC_100ACE40() ? 25 : 0; + + return byteAction; +} diff --git a/saco/net/localplayer.h b/saco/net/localplayer.h index b72d541..3ac47ef 100644 --- a/saco/net/localplayer.h +++ b/saco/net/localplayer.h @@ -3,10 +3,21 @@ //---------------------------------------------------------- +#pragma pack(1) +typedef struct _SPAWN_INFO { + BYTE byteTeam; + int iSkin; + BYTE byteUnk; + VECTOR vecPos; + float fRotation; + int iSpawnWeapons[3]; + int iSpawnWeaponsAmmo[3]; +} SPAWN_INFO, *PSPAWN_INFO; + #pragma pack(1) class CLocalPlayer { -private: +public: BYTE field_0[63]; //BYTE field_3C[3]; @@ -43,11 +54,12 @@ private: DWORD field_13B; DWORD field_13F; - BYTE _pad143[4]; + DWORD field_143; DWORD field_147; - BYTE _pad14B[50]; + DWORD field_14B; + SPAWN_INFO m_SpawnInfo; DWORD field_17D; DWORD field_181; @@ -78,12 +90,12 @@ private: DWORD field_306; DWORD field_30A; - BYTE _pad30E; + BYTE field_30E; BYTE field_30F; DWORD field_310; - BYTE _pad314[4]; + DWORD field_314; WORD field_318; @@ -102,10 +114,53 @@ public: DWORD GetPlayerColorAsRGBA(); DWORD GetPlayerColorAsARGB(); BOOL IsSpectating() { return m_bIsSpectating; }; + void SetTeam(BYTE byteTeam) { m_byteTeam = byteTeam; }; void ReturnToClassSelection() { m_bWantsAnotherClass = TRUE; }; BOOL IsInRCMode() { return m_bInRCMode; }; + + void SelectNextClass(); // .text:10005FF0 + void SelectPreviousClass(); // .text:10006080 + void RequestClass(int iClass); // .text:100058D0 + void SendStatsUpdate(); // .text:10005D00 + void FUNC_10005AD0(WORD a1, int a2); // .text:10005AD0 + void FUNC_10006FE0(); // .text:10006FE0 + void FUNC_10005BF0(WORD a1); // .text:10005BF0 + void FUNC_10003570(); // .text:10003570 + + // the three ped-damage reports; all take `this` in ecx but never read it + void FUNC_100068A0(WORD a1, float a2, int a3, int a4); // .text:100068A0 + void FUNC_100069B0(WORD a1, float a2, int a3, int a4); // .text:100069B0 + void FUNC_10006AC0(WORD a1, float a2, int a3, int a4); // .text:10006AC0 + void sub_10003C20(); // .text:10003C20 + void sub_10003BE0(SPAWN_INFO *pSpawnInfo); // .text:10003BE0 + void sub_100040E0(BOOL bSpawn); // .text:100040E0 + void sub_100030F0(BYTE a1); // .text:100030F0 + void sub_10003710(int a1, int a2); // .text:10003710 + void sub_10004060(); // .text:10004060 + int FUNC_10003AB0(); // .text:10003AB0 + int FUNC_10003AF0(); // .text:10003AF0 + int FUNC_10003B30(); // .text:10003B30 + void RequestSpawn(); // .text:10003B60 + BOOL FUNC_10004340(WORD a1, WORD a2, WORD a3); // .text:10004340 + BOOL FUNC_10004300(WORD a1, WORD a2, WORD a3); // .text:10004300 + PLAYERID FUNC_10004B70(); // .text:10004B70 + ACTORID FUNC_10004BB0(); // .text:10004BB0 + BOOL FUNC_10003A60(void *a2, void *a3, unsigned int a4); // .text:10003A60 + void FUNC_10004080(); // .text:10004080 + void FUNC_10004230(WORD a1); // .text:10004230 + void FUNC_10004280(PLAYERID playerId); // .text:10004280 + void FUNC_10002FA0(); // .text:10002FA0 + BYTE GetSpecialAction(); // .text:10003410 + void FUNC_10006100(); // .text:10006100 + void SendDeath(); // .text:10005810 + void SetInteriorId(BYTE byteInteriorId); // .text:10005970 + void FUNC_10003F50(); // .text:10003F50 + void FUNC_10003FC0(); // .text:10003FC0 + void FUNC_100041C0(BOOL bSpectating); // .text:100041C0 + + DWORD GetField_F0() { return field_F0; }; }; //---------------------------------------------------------- diff --git a/saco/net/menupool.h b/saco/net/menupool.h index 9dac02e..26f47c9 100644 --- a/saco/net/menupool.h +++ b/saco/net/menupool.h @@ -5,7 +5,7 @@ class CMenuPool { -private: +public: CMenu *m_pMenus[MAX_MENUS]; BOOL m_bMenuSlotState[MAX_MENUS]; @@ -13,6 +13,8 @@ private: BYTE m_byteExited; public: + int GetSlotState(BYTE byteMenuID) { if(byteMenuID > MAX_MENUS) return 0; return m_bMenuSlotState[byteMenuID]; } + CMenuPool(); ~CMenuPool(); diff --git a/saco/net/netgame.cpp b/saco/net/netgame.cpp index 7e17a3f..8ad4d91 100644 --- a/saco/net/netgame.cpp +++ b/saco/net/netgame.cpp @@ -4,9 +4,16 @@ extern CGame *pGame; extern CChatWindow *pChatWindow; +extern CConfig *pConfig; +extern CAudioStream *pAudioStream; + +void FUNC_100AABE0(); // .text:100AABE0 + +DWORD dword_10118A18; //---------------------------------------------------- +// MATCH BYTE __stdcall GetPacketID(Packet *p) { if (p==0) return 255; @@ -22,6 +29,7 @@ BYTE __stdcall GetPacketID(Packet *p) //---------------------------------------------------- +// MATCH bool __stdcall HasTimestamps(Packet *p) { if (p==0) return 0; @@ -86,6 +94,7 @@ void CNetGame::UpdateNetwork() // TODO: CNetGame::UpdateNetwork() } +// MATCH void CNetGame::InitPools() { m_pPools = (NETGAME_POOLS *)calloc(1, sizeof(NETGAME_POOLS)); @@ -101,6 +110,7 @@ void CNetGame::InitPools() m_pPools->pActorPool = new CActorPool(); } +// MATCH DWORD CNetGame::GetTime() { return (DWORD)RakNet::GetTime(); @@ -109,10 +119,15 @@ DWORD CNetGame::GetTime() //----------------------------------------------------------- // Puts a personal marker using any of the radar icons on the map +// MATCH void CNetGame::SetMapIcon(BYTE byteIndex, float fX, float fY, float fZ, BYTE byteIcon, DWORD dwColor, int iStyle) { if (byteIndex >= 100) return; - if (m_dwMapIcon[byteIndex] != NULL) DisableMapIcon(byteIndex); + if (m_dwMapIcon[byteIndex] != NULL) { + if (byteIndex >= 100) return; + ScriptCommand(&disable_marker, m_dwMapIcon[byteIndex]); + m_dwMapIcon[byteIndex] = NULL; + } //ScriptCommand(&create_radar_marker_without_sphere, fX, fY, fZ, byteIcon, &m_dwMapIcon); m_dwMapIcon[byteIndex] = pGame->CreateRadarMarkerIcon(byteIcon, fX, fY, fZ, dwColor, iStyle); } @@ -120,6 +135,27 @@ void CNetGame::SetMapIcon(BYTE byteIndex, float fX, float fY, float fZ, BYTE byt //----------------------------------------------------------- // Removes the Map Icon + +//---------------------------------------------------- + +// MATCH +// MATCH +void CNetGame::ResetMapIcons() +{ + BYTE i; + for (i = 0; i < 100; i++) + { + if (m_dwMapIcon[i] != NULL) { + if (i >= 100) continue; + ScriptCommand(&disable_marker, m_dwMapIcon[i]); + m_dwMapIcon[i] = NULL; + } + } +} + +//---------------------------------------------------- + +// MATCH void CNetGame::DisableMapIcon(BYTE byteIndex) { if (byteIndex >= 100) return; @@ -129,13 +165,282 @@ void CNetGame::DisableMapIcon(BYTE byteIndex) //---------------------------------------------------- -void CNetGame::ResetMapIcons() +//---------------------------------------------------- + +void CNetGame::Packet_RSAPublicKeyMismatch(Packet *p) { - BYTE i; - for (i = 0; i < 100; i++) + pChatWindow->AddDebugMessage("Failed to initialize encryption."); +} + +//---------------------------------------------------- + +void CNetGame::Packet_ConnectionBanned(Packet *p) +{ + pChatWindow->AddDebugMessage("You are banned from this server."); +} + +//---------------------------------------------------- + +void CNetGame::Packet_ConnectionSucceeded(Packet *p) +{ + pChatWindow->AddDebugMessage("Server has accepted the connection."); +} + +//---------------------------------------------------- + +// MATCH +void CNetGame::Packet_NoFreeIncomingConnections(Packet *p) +{ + pChatWindow->AddDebugMessage("The server is full. Retrying..."); + m_iGameState = GAMESTATE_WAIT_CONNECT; +} + +//---------------------------------------------------- + +// MATCH +void CNetGame::Packet_InvalidPassword(Packet *p) +{ + pChatWindow->AddDebugMessage("Wrong server password."); + GetRakClient()->Disconnect(0,0); +} + +//---------------------------------------------------- + +// MATCH +void CNetGame::Packet_ConnectionAttemptFailed(Packet *p) +{ + pChatWindow->AddDebugMessage("The server didn't respond. Retrying.."); + m_iGameState = GAMESTATE_WAIT_CONNECT; +} + +//---------------------------------------------------- + +// MATCH +void CNetGame::ToggleNameTagStatus() +{ + if(!field_234) { - if (m_dwMapIcon[i] != NULL) DisableMapIcon(i); + pChatWindow->AddInfoMessage("NameTag Player Status: ON"); + if(pConfig) pConfig->SetIntVariable("nonametagstatus",0); + field_234 = 1; + } + else + { + pChatWindow->AddInfoMessage("NameTag Player Status: OFF"); + if(pConfig) pConfig->SetIntVariable("nonametagstatus",1); + field_234 = 0; } } //---------------------------------------------------- + +// MATCH +void CNetGame::AttemptConnect() +{ + if(GetTickCount() - field_3D1 > 3000) + { + if(pChatWindow) + pChatWindow->AddDebugMessage("Connecting to %s:%d...",m_szHostOrIp,m_iPort); + + m_pRakClient->Connect(m_szHostOrIp,m_iPort,0,0,2); + + field_3D1 = GetTickCount(); + m_iGameState = GAMESTATE_CONNECTING; + } +} + +//---------------------------------------------------- + +void CNetGame::UpdateScoresPingsIPs() +{ + if(GetTickCount() - dword_10118A18 > 3000) + { + dword_10118A18 = GetTickCount(); + + RakNet::BitStream bsSend; + if(GetRakClient()) + GetRakClient()->RPC(RPC_UpdateScoresPingsIPs, &bsSend, HIGH_PRIORITY, RELIABLE, 0, FALSE); + } +} + +//---------------------------------------------------- + +void CNetGame::ResetWorldBounds() +{ + if(field_3D5->field_2B) FUNC_100AABE0(); + + field_3D5->fWorldBoundryPX = 20000.0f; + field_3D5->fWorldBoundryZX = -20000.0f; + field_3D5->fWorldBoundryPY = 20000.0f; + field_3D5->fWorldBoundryNY = -20000.0f; +} + +//---------------------------------------------------- + +// MATCH +void CNetGame::ResetPlayerPool() +{ + delete m_pPools->pPlayerPool; + m_pPools->pPlayerPool = new CPlayerPool(); +} + +//---------------------------------------------------- + +// MATCH +void CNetGame::ResetGangZonePool() +{ + delete m_pPools->pGangZonePool; + m_pPools->pGangZonePool = new CGangZonePool(); +} + +//---------------------------------------------------- + +// MATCH +void CNetGame::ResetPickupPool() +{ + delete m_pPools->pPickupPool; + m_pPools->pPickupPool = new CPickupPool(); +} + +//---------------------------------------------------- + +// MATCH +void CNetGame::ResetMenuPool() +{ + delete m_pPools->pMenuPool; + m_pPools->pMenuPool = new CMenuPool(); +} + +//---------------------------------------------------- + +// MATCH +void CNetGame::ResetLabelPool() +{ + delete m_pPools->pLabelPool; + m_pPools->pLabelPool = new CLabelPool(); +} + +//---------------------------------------------------- + +// MATCH +void CNetGame::ResetActorPool() +{ + delete m_pPools->pActorPool; + m_pPools->pActorPool = new CActorPool(); +} + +//---------------------------------------------------- + +// MATCH +void CNetGame::ResetVehiclePool() +{ + delete m_pPools->pVehiclePool; + m_pPools->pVehiclePool = new CVehiclePool(); +} + +//---------------------------------------------------- + +// MATCH +void CNetGame::ResetTextDrawPool() +{ + delete m_pPools->pTextDrawPool; + m_pPools->pTextDrawPool = new CTextDrawPool(); +} + +//---------------------------------------------------- + +// MATCH +void CNetGame::ResetObjectPool() +{ + delete m_pPools->pObjectPool; + m_pPools->pObjectPool = new CObjectPool(); +} + +//---------------------------------------------------- + +// MATCH +void CNetGame::Packet_DisconnectionNotification(Packet *p) +{ + pChatWindow->AddDebugMessage("Server closed the connection."); + + if(pAudioStream) pAudioStream->Stop(1); + + GetRakClient()->Disconnect(2000,0); +} + +//---------------------------------------------------- + +void CNetGame::Packet_AimSync(Packet *p) +{ + RakNet::BitStream bsAimSync((char *)p->data, p->length, false); + char aimSync[31]; + WORD wPlayerID; + BYTE bytePacketID = 0; + + if(m_iGameState != GAMESTATE_CONNECTED) return; + + bsAimSync.Read(bytePacketID); + bsAimSync.Read(wPlayerID); + bsAimSync.Read(aimSync, sizeof(aimSync)); + + CPlayerPool *pPlayerPool = GetPlayerPool(); + + if(wPlayerID <= MAX_PLAYERS) + { + CNetPlayer *pPlayer = pPlayerPool->m_pPlayers[wPlayerID]; + if(pPlayer && pPlayer->m_pRemotePlayer) + pPlayer->m_pRemotePlayer->FUNC_10015760(aimSync); + } +} + +//---------------------------------------------------- + +void CNetGame::Packet_PassengerSync(Packet *p) +{ + RakNet::BitStream bsPassengerSync((char *)p->data, p->length, false); + char passengerSync[24]; + WORD wPlayerID; + BYTE bytePacketID = 0; + + if(m_iGameState != GAMESTATE_CONNECTED) return; + + bsPassengerSync.Read(bytePacketID); + bsPassengerSync.Read(wPlayerID); + bsPassengerSync.Read(passengerSync, sizeof(passengerSync)); + + CPlayerPool *pPlayerPool = GetPlayerPool(); + + if(wPlayerID <= MAX_PLAYERS) + { + CNetPlayer *pPlayer = pPlayerPool->m_pPlayers[wPlayerID]; + if(pPlayer && pPlayer->m_pRemotePlayer) + pPlayer->m_pRemotePlayer->FUNC_10017440(passengerSync); + } +} + +//---------------------------------------------------- + +void CNetGame::Packet_TrailerSync(Packet *p) +{ + RakNet::BitStream bsTrailerSync((char *)p->data, p->length, false); + char trailerSync[54]; + WORD wPlayerID; + BYTE bytePacketID; + + if(m_iGameState != GAMESTATE_CONNECTED) return; + + bytePacketID = 0; + + bsTrailerSync.Read(bytePacketID); + bsTrailerSync.Read(wPlayerID); + bsTrailerSync.Read(trailerSync, sizeof(trailerSync)); + + CPlayerPool *pPlayerPool = GetPlayerPool(); + + if(wPlayerID <= MAX_PLAYERS) + { + CNetPlayer *pPlayer = pPlayerPool->m_pPlayers[wPlayerID]; + if(pPlayer && pPlayer->m_pRemotePlayer) + pPlayer->m_pRemotePlayer->FUNC_10015C90(trailerSync); + } +} diff --git a/saco/net/netgame.h b/saco/net/netgame.h index 9c52196..55761cb 100644 --- a/saco/net/netgame.h +++ b/saco/net/netgame.h @@ -5,11 +5,17 @@ #define GAMESTATE_WAIT_CONNECT 1 +#define GAMESTATE_CONNECTING 2 + struct struc_41 { bool bUseCJWalk; int field_1; - char _gap5[17]; + float fWorldBoundryPX; + float fWorldBoundryZX; + float fWorldBoundryPY; + float fWorldBoundryNY; + char _gap15; float field_16; char field_1A; char _gap1B[4]; @@ -19,12 +25,15 @@ struct struc_41 char field_22; int field_23; float field_27; - char _gap2B; - char field_2C; - char field_2D; + char field_2B; + BYTE field_2C; + BYTE field_2D; char field_2E; char field_2F; - char _gap30[10]; + int field_30; + char _gap34[4]; + BYTE field_38; + char _gap39; }; typedef struct _NETGAME_POOLS { @@ -51,8 +60,10 @@ private: char m_szHostOrIp[257]; char m_szHostName[257]; char field_232; +public: char field_233; - char field_234; + BYTE field_234; +private: int m_iPort; int field_239; DWORD m_dwMapIcon[100]; @@ -73,13 +84,60 @@ public: DWORD GetTime(); int GetGameState() { return m_iGameState; }; + char GetField232() { return field_232; }; + int GetField239() { return field_239; }; + void SetField232(char cValue) { field_232 = cValue; }; BOOL GetWalkStyle() { return field_3D5->bUseCJWalk; }; + struc_41 * GetSettings() { return field_3D5; }; + void SetWorldTime(BYTE byteHour) { field_3D5->field_2C = byteHour; }; + void SetWorldMinute(BYTE byteMinute) { field_3D5->field_2D = byteMinute; }; + void SetWeather(BYTE byteWeather) { field_3D5->field_2E = byteWeather; }; + CVehiclePool * GetVehiclePool() { return m_pPools->pVehiclePool; }; + CVehiclePool * FUNC_10001180(); // .text:10001180, out of line copy of GetVehiclePool CPlayerPool * GetPlayerPool() { return m_pPools->pPlayerPool; }; + CPickupPool * GetPickupPool() { return m_pPools->pPickupPool; }; + CActorPool * GetActorPool() { return m_pPools->pActorPool; }; + CObjectPool * GetObjectPool() { return m_pPools->pObjectPool; }; + CObjectPool * FUNC_10002E10() { return m_pPools->pObjectPool; }; // .text:10002E10, out of line copy of GetObjectPool CGangZonePool * GetGangZonePool() { return m_pPools->pGangZonePool; }; + CLabelPool * GetLabelPool() { return m_pPools->pLabelPool; }; + CTextDrawPool * GetTextDrawPool() { return m_pPools->pTextDrawPool; }; RakClientInterface * GetRakClient() { return m_pRakClient; }; void ResetMapIcons(); void SetMapIcon(BYTE byteIndex, float fX, float fY, float fZ, BYTE byteIcon, DWORD dwColor, int iStyle); - void DisableMapIcon(BYTE byteIndex); + // MATCH + void DisableMapIcon(BYTE byteIndex); // .text:10008FB0 + + CMenuPool * GetMenuPool() { return m_pPools->pMenuPool; }; // .text:100084F0 + void ResetPlayerPool(); // .text:10008FE0 + void ResetVehiclePool(); // .text:10009080 + void ResetTextDrawPool(); // .text:10009110 + void ResetObjectPool(); // .text:100091B0 + void ResetGangZonePool(); // .text:10009250 + void ResetPickupPool(); // .text:100092F0 + void ResetMenuPool(); // .text:10009350 + void ResetLabelPool(); // .text:100093F0 + void ResetActorPool(); // .text:10009490 + + void AttemptConnect(); // .text:10008940 + + void ResetWorldBounds(); // .text:100088F0 + + void Packet_AimSync(Packet *p); // .text:10009C40 + void Packet_PassengerSync(Packet *p); // .text:10009D30 + void Packet_TrailerSync(Packet *p); // .text:10009E20 + void Packet_RSAPublicKeyMismatch(Packet *p); // .text:10008D50 + void Packet_ConnectionBanned(Packet *p); // .text:10008D70 + void Packet_ConnectionSucceeded(Packet *p); // .text:10008D90 + void Packet_NoFreeIncomingConnections(Packet *p); // .text:10008DB0 + void Packet_DisconnectionNotification(Packet *p); // .text:10008DE0 + void Packet_InvalidPassword(Packet *p); // .text:10008E20 + void Packet_ConnectionAttemptFailed(Packet *p); // .text:10008E60 + + void UpdateScoresPingsIPs(); // .text:10008F10 + + void ToggleNameTagStatus(); // .text:10008E90 + void sub_1000A540(); // .text:1000A540 }; diff --git a/saco/net/netplayer.h b/saco/net/netplayer.h index a0cab8f..1086a43 100644 --- a/saco/net/netplayer.h +++ b/saco/net/netplayer.h @@ -16,6 +16,12 @@ public: CRemotePlayer *m_pRemotePlayer; std::string m_PlayerName; + void SetPlayerName(PCHAR szName) { m_PlayerName = szName; }; + int GetScore() { return field_4; }; + int GetPing() { return field_C; }; + + PCHAR GetName() { return (PCHAR)m_PlayerName.c_str(); }; // .text:100175B0 + CNetPlayer(PCHAR szPlayerName, BOOL bIsNPC); ~CNetPlayer(); }; diff --git a/saco/net/netrpc.cpp b/saco/net/netrpc.cpp index 798cdaf..cc3a5e3 100644 --- a/saco/net/netrpc.cpp +++ b/saco/net/netrpc.cpp @@ -1,56 +1,1232 @@ #include "../main.h" +#include "../game/util.h" +#include "../../raknet/StringCompressor.h" + +extern CNetGame* pNetGame; +extern CGame * pGame; +extern CChatWindow *pChatWindow; +extern RakNetStatisticsStruct RakServerStats; +extern CScoreBoard *pScoreBoard; +extern HANDLE hInstance; + +// the download list global lives in main.cpp's data; only the touched fields are known +// the 94-byte record FUNC_10011EB0 reads off the wire +typedef struct _DOWNLOAD_INFO +{ + char _gap0[7]; + BYTE field_7; + DWORD field_8; + DWORD field_C; + DWORD dwCrc; + char _gap14[0x32]; + DWORD field_46; + DWORD field_4A; + DWORD field_4E; + DWORD field_52; + BYTE field_56; + BYTE field_57; + char _gap58[6]; +} DOWNLOAD_INFO; + +class CDownloadListDispatch +{ +public: + + char _gap0[0x221]; + char field_221; + // the size is pinned by the `new` in FUNC_10011EB0 + char _gap222[0x1E4B]; + + CDownloadListDispatch(); // .text:1000DE20 + + void FUNC_1000BD60(); // .text:1000BD60 + void FUNC_1000D4C0(DWORD dwUnk); // .text:1000D4C0 + void FUNC_1000DAE0(DWORD dwUnk, DOWNLOAD_INFO *pInfo); // .text:1000DAE0 + void FUNC_1000DB30(DWORD dwIndex); // .text:1000DB30 + void FUNC_1000C0C0(); // .text:1000C0C0 + void FUNC_1000C140(); // .text:1000C140 + void FUNC_1000CBF0(int a1, int a2, int a3); // .text:1000CBF0 + void FUNC_1000D500(BYTE byteUnk, DWORD dwUnk, PCHAR pData, int iLength); // .text:1000D500 + void FUNC_1000D650(BYTE byteUnk, DWORD dwUnk, PCHAR szName); // .text:1000D650 + void FUNC_1000D750(BYTE byteUnk, DWORD dwUnk); // .text:1000D750 + void FUNC_1000E160(int a1); // .text:1000E160 +}; + +extern CDownloadListDispatch *dword_1026EB98; + +// dispatch helpers invoked on the editor globals (thiscall); only the touched fields are known +class CEditDispatch +{ +public: + + DWORD field_0; + char _gap4[0x7C]; + DWORD field_80; + + void FUNC_10071520(); // .text:10071520 + void FUNC_1006DB80(BOOL bEnable); // .text:1006DB80 + void FUNC_10072BC0(BOOL bEnable); // .text:10072BC0 + void FUNC_100724E0(DWORD dwIndex); // .text:100724E0 + void FUNC_10072420(WORD wObjectID, BOOL bPlayerObject); // .text:10072420 +}; + +extern int dword_1026EB60; +extern int dword_1026EB64; +extern int dword_1026EB68; + +// the chat-bubble overlay global also lives in IDirect3DDevice9Hook.cpp +class CChatBubbleDispatch +{ +public: + + void FUNC_10066E10(PLAYERID playerId, PCHAR szText, DWORD dwColor, float fDrawDistance, DWORD dwExpireTime); // .text:10066E10 +}; + +extern int dword_1026EB78; + +// the dialog global, same story +class CDialogDispatch +{ +public: + + void FUNC_1006FFB0(int iDialogID, int iStyle, PCHAR szCaption, PCHAR szInfo, + PCHAR szButton1, PCHAR szButton2, int iUnk); // .text:1006FFB0 +}; + +extern int dword_1026EB50; + +// MATCH +BYTE Checksum(BYTE *pData, WORD wLen) +{ + BYTE byteSum = 0; + + for(DWORD i = 0; i != wLen; i++) + byteSum ^= pData[i] & 0xCC; + + return byteSum; +} + +// MATCH +void LoadModelsThread(void *pParam) +{ + pGame->LoadRequestedModels(); + _endthread(); +} // TODO: these RPCs -void Unk22(RPCParameters *rpcParams) {} -void Unk24(RPCParameters *rpcParams) {} -void Unk3A(RPCParameters *rpcParams) {} -void Unk3B(RPCParameters *rpcParams) {} -void Unk3D(RPCParameters *rpcParams) {} -void SetCheckpoint(RPCParameters *rpcParams) {} -void DisableCheckpoint(RPCParameters *rpcParams) {} -void SetRaceCheckpoint(RPCParameters *rpcParams) {} -void DisableRaceCheckpoint(RPCParameters *rpcParams) {} -void UpdateScoresPingsIPs(RPCParameters *rpcParams) {} -void SvrStats(RPCParameters *rpcParams) {} -void GameModeRestart(RPCParameters *rpcParams) {} -void ConnectionRejected(RPCParameters *rpcParams) {} -void ClientMessage(RPCParameters *rpcParams) {} -void WorldTime(RPCParameters *rpcParams) {} -void Pickup(RPCParameters *rpcParams) {} -void DestroyPickup(RPCParameters *rpcParams) {} -void DestroyWeaponPickup(RPCParameters *rpcParams) {} -void ScmEvent(RPCParameters *rpcParams) {} -void Weather(RPCParameters *rpcParams) {} -void SetTimeEx(RPCParameters *rpcParams) {} +void Unk22(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + PLAYERID playerId; + int iSkill; + WORD wLevel; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(playerId); + bsData.Read(iSkill); + bsData.Read(wLevel); + + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + + if(pPlayerPool) + { + if(playerId == pPlayerPool->GetLocalPlayerID()) + { + CPlayerPed *pPlayerPed = pGame->FindPlayerPed(); + if(pPlayerPed) + pPlayerPed->SetWeaponSkillLevel(iSkill,wLevel); + } + else if(playerId < MAX_PLAYERS) + { + if(pPlayerPool->field_2A[playerId]) + { + CNetPlayer *pPlayer = pPlayerPool->m_pPlayers[playerId]; + if(pPlayer) + { + CRemotePlayer *pRemotePlayer = pPlayer->m_pRemotePlayer; + if(pRemotePlayer) + { + if(pRemotePlayer->FUNC_10001080() && pRemotePlayer->m_pPlayerPed) + pRemotePlayer->m_pPlayerPed->SetWeaponSkillLevel(iSkill,wLevel); + } + } + } + } + } +} +void Unk24(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + WORD wLabelID; + DWORD dwColor; + float fX, fY, fZ; + float fDrawDistance; + WORD wAttachedPlayer; + WORD wAttachedVehicle; + char szText[2049]; + + CLabelPool *pLabelPool = pNetGame->GetLabelPool(); + + if(pLabelPool) + { + BYTE byteShowBehindWalls = 0; + + memset(szText,0,sizeof(szText)); + + bsData.Read(wLabelID); + bsData.Read(dwColor); + bsData.Read(fX); + bsData.Read(fY); + bsData.Read(fZ); + bsData.Read(fDrawDistance); + bsData.Read(byteShowBehindWalls); + bsData.Read(wAttachedPlayer); + bsData.Read(wAttachedVehicle); + + stringCompressor->DecodeString(szText,2048,&bsData); + + if(wLabelID < MAX_LABELS) + pLabelPool->sub_100011D0(wLabelID,szText,dwColor,fX,fY,fZ,fDrawDistance, + byteShowBehindWalls,wAttachedPlayer,wAttachedVehicle); + } +} +// MATCH +void Unk3A(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + WORD wLabelID; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + CLabelPool *pLabelPool = pNetGame->GetLabelPool(); + if(!pLabelPool) return; + + bsData.Read(wLabelID); + + if(wLabelID < MAX_LABELS && pLabelPool->m_bLabelSlotState[wLabelID]) + pLabelPool->Delete(wLabelID); +} +void Unk3B(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + PLAYERID playerId; + DWORD dwColor; + float fDrawDistance; + DWORD dwExpireTime; + BYTE byteTextLen; + char szText[257]; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + if(pNetGame) + { + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + if(pPlayerPool) + { + memset(szText,0,sizeof(szText)); + + bsData.Read(playerId); + bsData.Read(dwColor); + bsData.Read(fDrawDistance); + bsData.Read(dwExpireTime); + bsData.Read(byteTextLen); + + if(byteTextLen <= 144) + { + bsData.Read(szText,byteTextLen); + szText[byteTextLen] = '\0'; + + if(pPlayerPool->GetSlotState(playerId)) + ((CChatBubbleDispatch *)dword_1026EB78)->FUNC_10066E10(playerId,szText,dwColor,fDrawDistance,dwExpireTime); + } + } + } +} +void Unk3D(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + short sDialogID; + BYTE byteStyle; + BYTE byteLen; + DWORD dwLen; + char szCaption[257]; + char szButton2[257]; + char szButton1[257]; + char szInfo[4097]; + + memset(szCaption,0,sizeof(szCaption)); + memset(szButton1,0,sizeof(szButton1)); + memset(szButton2,0,sizeof(szButton2)); + memset(szInfo,0,sizeof(szInfo)); + + bsData.Read(sDialogID); + bsData.Read(byteStyle); + + bsData.Read(byteLen); + bsData.Read(szCaption,byteLen); + + bsData.Read(byteLen); + bsData.Read(szButton1,byteLen); + + bsData.Read(byteLen); + bsData.Read(szButton2,byteLen); + + stringCompressor->DecodeString(szInfo,4096,&bsData); + + dwLen = strlen(szCaption); + if(dwLen) + { + dwLen = strlen(szInfo); + if(dwLen) + ((CDialogDispatch *)dword_1026EB50)->FUNC_1006FFB0(sDialogID,byteStyle, + szCaption,szInfo,szButton1,szButton2,1); + } +} +// MATCH +void SetCheckpoint(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + float fX, fY, fZ, fSize; + VECTOR vecPos, vecExtent; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(fX); + bsData.Read(fY); + bsData.Read(fZ); + bsData.Read(fSize); + + vecPos.X = fX; + vecPos.Y = fY; + vecPos.Z = fZ; + + vecExtent.X = fSize; + vecExtent.Y = fSize; + vecExtent.Z = fSize; + + pGame->SetCheckpointInformation(&vecPos,&vecExtent); + pGame->m_bCheckpointsEnabled = TRUE; +} +// MATCH +void DisableCheckpoint(RPCParameters *rpcParams) +{ + pGame->m_bCheckpointsEnabled = FALSE; +} +// MATCH +void SetRaceCheckpoint(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + BYTE byteType; + float fX, fY, fZ; + VECTOR vecPos, vecNext; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(byteType); + + bsData.Read(fX); + bsData.Read(fY); + bsData.Read(fZ); + + vecPos.X = fX; + vecPos.Y = fY; + vecPos.Z = fZ; + + bsData.Read(fX); + bsData.Read(fY); + bsData.Read(fZ); + + vecNext.X = fX; + vecNext.Y = fY; + vecNext.Z = fZ; + + bsData.Read(fX); + + pGame->SetRaceCheckpointInformation(byteType,&vecPos,&vecNext,fX); + pGame->m_bRaceCheckpointsEnabled = TRUE; +} +// MATCH +void DisableRaceCheckpoint(RPCParameters *rpcParams) +{ + pGame->m_bRaceCheckpointsEnabled = FALSE; +} +void UpdateScoresPingsIPs(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + PLAYERID playerId; + int iScore; + int iPing; + + DWORD dwBytes = iBitLength/8; + + RakNet::BitStream bsData(Data,dwBytes+1,false); + + if(dwBytes) + { + DWORD dwCount = dwBytes/10; + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + + for(WORD i = 0; i < dwCount; i++) + { + bsData.Read(playerId); + bsData.Read(iScore); + bsData.Read(iPing); + + if(playerId < MAX_PLAYERS) + { + if(playerId == pPlayerPool->GetLocalPlayerID()) + pPlayerPool->field_0 = iScore; + else + { + CNetPlayer *pPlayer = pPlayerPool->m_pPlayers[playerId]; + if(pPlayer) pPlayer->field_4 = iScore; + } + + if(playerId == pPlayerPool->GetLocalPlayerID()) + pPlayerPool->field_22 = iPing; + else if(playerId <= MAX_PLAYERS) + { + CNetPlayer *pPlayer = pPlayerPool->m_pPlayers[playerId]; + if(pPlayer) pPlayer->field_C = iPing; + } + } + } + + if(pScoreBoard) + pScoreBoard->FUNC_1006ED30(); + } +} +// MATCH +void SvrStats(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read((PCHAR)&RakServerStats,sizeof(RakNetStatisticsStruct)); +} +// MATCH +void GameModeRestart(RPCParameters *rpcParams) +{ + pNetGame->sub_1000A540(); +} +// MATCH +void ConnectionRejected(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + BYTE byteRejectReason; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(byteRejectReason); + + if(byteRejectReason == 1) + { + pChatWindow->AddInfoMessage("CONNECTION REJECTED: Incorrect Version."); + } + else if(byteRejectReason == 2) + { + pChatWindow->AddInfoMessage("CONNECTION REJECTED: Unacceptable NickName"); + pChatWindow->AddInfoMessage("Please choose another nick between and 3-20 characters"); + pChatWindow->AddInfoMessage("Please use only a-z, A-Z, 0-9"); + pChatWindow->AddInfoMessage("Use /quit to exit or press ESC and select Quit Game"); + } + else if(byteRejectReason == 3) + { + pChatWindow->AddInfoMessage("CONNECTION REJECTED: Bad mod version."); + } + else if(byteRejectReason == 4) + { + pChatWindow->AddInfoMessage("CONNECTION REJECTED: Unable to allocate a player slot."); + } + + pNetGame->GetRakClient()->Disconnect(500,0); +} +// MATCH +void ClientMessage(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + DWORD dwColor; + DWORD dwStrLen; + char szMessage[256]; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + memset(szMessage,0,sizeof(szMessage)); + + bsData.Read(dwColor); + bsData.Read(dwStrLen); + + if(dwStrLen > 255) return; + + bsData.Read(szMessage,dwStrLen); + szMessage[dwStrLen] = '\0'; + + pChatWindow->AddClientMessage(dwColor,szMessage); +} +// MATCH +void WorldTime(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + BYTE byteWorldTime; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(byteWorldTime); + + pNetGame->SetWorldTime(byteWorldTime); +} +// MATCH +void Pickup(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + int iPickup; + PICKUP Pickup; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(iPickup); + bsData.Read((PCHAR)&Pickup,sizeof(PICKUP)); + + CPickupPool *pPickupPool = pNetGame->GetPickupPool(); + if(pPickupPool) + pPickupPool->sub_10013270(&Pickup,iPickup); +} +// MATCH +void DestroyPickup(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + int iPickup; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(iPickup); + + CPickupPool *pPickupPool = pNetGame->GetPickupPool(); + if(pPickupPool) + pPickupPool->sub_10013320(iPickup); +} +// MATCH +void DestroyWeaponPickup(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + BYTE bytePickupIndex; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(bytePickupIndex); + + pNetGame->GetPickupPool()->sub_10013380(bytePickupIndex); +} +// MATCH +void ScmEvent(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + PLAYERID playerId; + int iEvent; + int iVehicle; + int iArg1; + int iArg2; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(playerId); + bsData.Read(iEvent); + bsData.Read(iVehicle); + bsData.Read(iArg1); + bsData.Read(iArg2); + + sub_10001A60(playerId,iEvent,iVehicle,iArg1,iArg2); +} +// MATCH +void Weather(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + BYTE byteWeather; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(byteWeather); + + pNetGame->SetWeather(byteWeather); + pGame->SetWorldWeather(byteWeather); +} +// MATCH +void SetTimeEx(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + BYTE byteHour; + BYTE byteMinute; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(byteHour); + bsData.Read(byteMinute); + + pGame->SetWorldTime(byteHour,byteMinute); + pNetGame->SetWorldTime(byteHour); + pNetGame->SetWorldMinute(byteMinute); +} void ToggleClock(RPCParameters *rpcParams) {} -void Unk3C(RPCParameters *rpcParams) {} +// MATCH +void Unk3C(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + DWORD dwTime = 0; + bsData.Read(dwTime); + + pGame->SetTimeInMilliseconds(dwTime); +} void WorldPlayerAdd(RPCParameters *rpcParams) {} -void WorldPlayerDeath(RPCParameters *rpcParams) {} -void WorldPlayerRemove(RPCParameters *rpcParams) {} +// MATCH +void WorldPlayerDeath(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + PLAYERID playerId; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + + bsData.Read(playerId); + + if(pPlayerPool) + { + CRemotePlayer *pRemotePlayer = pPlayerPool->GetAt(playerId); + if(pRemotePlayer) + pRemotePlayer->FUNC_10017570(); + } +} +// MATCH +void WorldPlayerRemove(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + PLAYERID playerId = 0; + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + + bsData.Read(playerId); + + if(pPlayerPool) + { + CRemotePlayer *pRemotePlayer = pNetGame->GetPlayerPool()->GetAt(playerId); + if(pRemotePlayer) + pRemotePlayer->FUNC_10017530(); + } +} void WorldVehicleAdd(RPCParameters *rpcParams) {} void WorldVehicleRemove(RPCParameters *rpcParams) {} -void DamageVehicle(RPCParameters *rpcParams) {} +// MATCH +void DamageVehicle(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + VEHICLEID VehicleID; + int iPanelDamage; + int iDoorDamage; + BYTE byteLightDamage; + BYTE byteTyreDamage; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(VehicleID); + bsData.Read(iPanelDamage); + bsData.Read(iDoorDamage); + bsData.Read(byteLightDamage); + bsData.Read(byteTyreDamage); + + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + + if(pVehiclePool->GetSlotState(VehicleID)) + { + CVehicle *pVehicle = (CVehicle *)pVehiclePool->GetAt(VehicleID); + if(pVehicle) + { + pVehicle->sub_100B7AC0(iPanelDamage,iDoorDamage,byteLightDamage); + pVehicle->sub_100B7940(byteTyreDamage); + } + } +} void Unk18(RPCParameters *rpcParams) {} -void EnterVehicle(RPCParameters *rpcParams) {} -void ExitVehicle(RPCParameters *rpcParams) {} +// MATCH +void EnterVehicle(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + PLAYERID playerId; + VEHICLEID VehicleID = 0; + BYTE bytePassenger = 0; + BOOL bPassenger = FALSE; + + bsData.Read(playerId); + bsData.Read(VehicleID); + bsData.Read(bytePassenger); + + if(bytePassenger) bPassenger = TRUE; + + CRemotePlayer *pRemotePlayer = pNetGame->GetPlayerPool()->GetAt(playerId); + if(pRemotePlayer) + { + if(pRemotePlayer->FUNC_10016120() < 200.0f) + pRemotePlayer->EnterVehicle(VehicleID,bPassenger); + } +} +// MATCH +void ExitVehicle(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + PLAYERID playerId; + VEHICLEID VehicleID = 0; + + bsData.Read(playerId); + bsData.Read(VehicleID); + + CRemotePlayer *pRemotePlayer = pNetGame->GetPlayerPool()->GetAt(playerId); + if(pRemotePlayer) + { + if(pRemotePlayer->FUNC_10016120() < 200.0f) + pRemotePlayer->ExitVehicle(); + } +} void ServerJoin(RPCParameters *rpcParams) {} -void ServerQuit(RPCParameters *rpcParams) {} +// MATCH +void ServerQuit(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + PLAYERID playerId; + BYTE byteReason; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + + bsData.Read(playerId); + bsData.Read(byteReason); + + pPlayerPool->sub_10014090(playerId,byteReason); +} void InitGame(RPCParameters *rpcParams) {} -void Chat(RPCParameters *rpcParams) {} -void RequestClass(RPCParameters *rpcParams) {} -void RequestSpawn(RPCParameters *rpcParams) {} -void EditAttachedObject(RPCParameters *rpcParams) {} -void EditObject(RPCParameters *rpcParams) {} -void SelectObject(RPCParameters *rpcParams) {} -void Unk1C(RPCParameters *rpcParams) {} -void UnkAA(RPCParameters *rpcParams) {} -void ClientCheck(RPCParameters *rpcParams) {} -void UnkAB(RPCParameters *rpcParams) {} -void UnkAC(RPCParameters *rpcParams) {} +// MATCH +void Chat(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + PLAYERID playerId; + BYTE byteTextLen; + char szText[256]; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + if(pNetGame->GetGameState() != GAMESTATE_CONNECTED) return; + + memset(szText,0,sizeof(szText)); + + bsData.Read(playerId); + bsData.Read(byteTextLen); + bsData.Read(szText,byteTextLen); + szText[byteTextLen] = '\0'; + + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + + if(playerId == pPlayerPool->GetLocalPlayerID()) + { + pChatWindow->AddChatMessage(pPlayerPool->GetLocalPlayerName(), + pPlayerPool->GetLocalPlayer()->GetPlayerColorAsARGB(),szText); + } + else + { + CRemotePlayer *pRemotePlayer = pPlayerPool->GetAt(playerId); + if(pRemotePlayer) + pRemotePlayer->sub_10017610(szText); + } +} +// MATCH +void RequestClass(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + BYTE byteRequestOutcome = 0; + SPAWN_INFO SpawnInfo; + + CLocalPlayer *pLocalPlayer = pNetGame->GetPlayerPool()->GetLocalPlayer(); + + bsData.Read(byteRequestOutcome); + bsData.Read((PCHAR)&SpawnInfo,sizeof(SPAWN_INFO)); + + if(byteRequestOutcome) + { + pLocalPlayer->sub_10003BE0(&SpawnInfo); + pLocalPlayer->sub_100040E0(TRUE); + } + else + { + pLocalPlayer->sub_100040E0(FALSE); + } +} +// MATCH +void RequestSpawn(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + BYTE byteRequestOutcome = 0; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(byteRequestOutcome); + + CLocalPlayer *pLocalPlayer = pNetGame->GetPlayerPool()->GetLocalPlayer(); + + if(byteRequestOutcome == 2 || (byteRequestOutcome && pLocalPlayer->field_302)) + pLocalPlayer->sub_10003C20(); + else + pLocalPlayer->field_302 = 0; +} +void EditAttachedObject(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + DWORD dwIndex = 0; + bsData.Read(dwIndex); + + if(dword_1026EB64 && ((CEditDispatch *)dword_1026EB64)->field_0) + ((CEditDispatch *)dword_1026EB64)->FUNC_1006DB80(FALSE); + + ((CEditDispatch *)dword_1026EB60)->FUNC_100724E0(dwIndex); +} +void EditObject(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bool bPlayerObject = false; + WORD wObjectID = 0xFFFF; + + bsData.Read(bPlayerObject); + bsData.Read(wObjectID); + + if(dword_1026EB64 && ((CEditDispatch *)dword_1026EB64)->field_0) + ((CEditDispatch *)dword_1026EB64)->FUNC_1006DB80(FALSE); + + if(dword_1026EB68 && ((CEditDispatch *)dword_1026EB68)->field_0) + ((CEditDispatch *)dword_1026EB68)->FUNC_10071520(); + + if(bPlayerObject) + ((CEditDispatch *)dword_1026EB60)->FUNC_10072420(wObjectID,TRUE); + else + ((CEditDispatch *)dword_1026EB60)->FUNC_10072420(wObjectID,FALSE); +} +void SelectObject(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + if(dword_1026EB68 && ((CEditDispatch *)dword_1026EB68)->field_0) + ((CEditDispatch *)dword_1026EB68)->FUNC_10071520(); + + if(dword_1026EB60 && ((CEditDispatch *)dword_1026EB60)->field_80) + ((CEditDispatch *)dword_1026EB60)->FUNC_10072BC0(FALSE); + + if(dword_1026EB64) + ((CEditDispatch *)dword_1026EB64)->FUNC_1006DB80(TRUE); +} +void Unk1C(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + if(dword_1026EB60 && ((CEditDispatch *)dword_1026EB60)->field_80) + ((CEditDispatch *)dword_1026EB60)->FUNC_10072BC0(FALSE); + + if(dword_1026EB64 && ((CEditDispatch *)dword_1026EB64)->field_0) + ((CEditDispatch *)dword_1026EB64)->FUNC_1006DB80(FALSE); +} +void UnkAA(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bool bValue = false; + bsData.Read(bValue); + + if(pNetGame) pNetGame->field_233 = bValue; +} +void FUNC_10011990(BYTE byteType, DWORD dwValue, BYTE byteResult) +{ + RakNet::BitStream bsSend; + + bsSend.Write(byteType); + bsSend.Write(dwValue); + bsSend.Write(byteResult); + + pNetGame->GetRakClient()->RPC(RPC_ClientCheck,&bsSend,HIGH_PRIORITY,RELIABLE_ORDERED,0,FALSE); +} +void ClientCheck(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + BYTE byteType = 0; + WORD wOffset = 0; + WORD wLength = 0; + BYTE byteSum = 0; + DWORD dwAddress = 0; + + bsData.Read(byteType); + bsData.Read(dwAddress); + bsData.Read(wOffset); + bsData.Read(wLength); + + if(wOffset > 256 || wLength > 256 || wLength < 2) return; + + if(byteType == 0x46) + { + if(IsValidModel(dwAddress)) + { + DWORD *pModelInfo = GetModelInfo(dwAddress); + if(pModelInfo) + byteSum = Checksum((BYTE *)pModelInfo + wOffset,wLength); + } + + FUNC_10011990(0x46,dwAddress,byteSum); + return; + } + + if(byteType == 2) + { + if(pGame->FindPlayerPed()) + { + if(pGame->FindPlayerPed()->IsInVehicle() && !pGame->FindPlayerPed()->FUNC_100ABFC0()) + { + VEHICLE_TYPE *pVehicle = (VEHICLE_TYPE *)pGame->FindPlayerPed()->m_pPed->pVehicle; + if(pVehicle) + { + dwAddress = pVehicle->entity.dwPhysFlags; + byteSum = 2; + } + } + else + { + PED_TYPE *pPed = pGame->FindPlayerPed()->m_pPed; + if(pPed) + { + dwAddress = pPed->entity.dwPhysFlags; + byteSum = 1; + } + } + } + + FUNC_10011990(2,dwAddress,byteSum); + return; + } + + if(byteType == 0x47) + { + if(IsValidModel(dwAddress)) + { + int iTries = 0; + + if(!pGame->sub_100A0970(dwAddress)) + { + pGame->sub_100A0940(dwAddress,2); + pGame->sub_100A0960(); + + while(!pGame->sub_100A0970(dwAddress)) + { + iTries++; + if(iTries > 1000) break; + Sleep(1); + } + } + + if(pGame->sub_100A0970(dwAddress)) + { + DWORD *pModelInfo = FUNC_100B46D0(dwAddress); + if(pModelInfo) + byteSum = Checksum((BYTE *)pModelInfo + wOffset,wLength); + + if(!FUNC_100B4700(dwAddress)) + pGame->sub_100A09A0(dwAddress,0); + } + } + + FUNC_10011990(0x47,dwAddress,byteSum); + return; + } + + if(byteType == 5) + { + if(dwAddress >= 0x400000 && dwAddress <= 0x856E00) + { + BYTE *pAddress = (BYTE *)dwAddress; + if(pAddress) + byteSum = Checksum(pAddress + wOffset,wLength); + + FUNC_10011990(5,dwAddress,byteSum); + } + + return; + } + + if(byteType == 0x45) + { + if(dwAddress <= 0xC3500) + { + BYTE *pAddress = (BYTE *)hInstance + dwAddress; + if(pAddress) + byteSum = Checksum(pAddress + wOffset,wLength); + + FUNC_10011990(0x45,dwAddress,byteSum); + } + + return; + } + + if(byteType == 0x48) + { + FUNC_10011990(0x48,(RakNet::GetTime() & 0xFFFFFFF) | 0x30000000,0); + } +} +void UnkAB(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + ACTOR_INFO ActorInfo; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + CActorPool *pActorPool = pNetGame->GetActorPool(); + + if(!pActorPool) return; + + memset(&ActorInfo,0,sizeof(ACTOR_INFO)); + bsData.Read((PCHAR)&ActorInfo,sizeof(ACTOR_INFO)); + + if(IsValidPedModel(ActorInfo.iModel)) + { + pActorPool->sub_10001900(&ActorInfo); + + // retail still does the lookup here even though nothing follows it + if(!pActorPool->GetAt(ActorInfo.ActorID)) return; + } +} +// not registered, kept alive only by the rest of the section +void FUNC_1000EBA0(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + if(dword_1026EB98) + { + dword_1026EB98->FUNC_1000BD60(); + dword_1026EB98->FUNC_1000DB30(0); + } +} +// not registered either +void FUNC_10011EB0(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + DOWNLOAD_INFO Info; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + memset(&Info,0,sizeof(DOWNLOAD_INFO)); + + DWORD dwUnk1 = 0; + DWORD dwUnk2 = 0; + + if(!dword_1026EB98) + { + dword_1026EB98 = new CDownloadListDispatch; + dword_1026EB98->FUNC_1000BD60(); + } + + if(dword_1026EB98) + { + bsData.Read(dwUnk1); + bsData.Read(dwUnk2); + bsData.Read(Info.field_7); + bsData.Read(Info.field_8); + bsData.Read(Info.field_C); + bsData.Read(Info.dwCrc); + bsData.Read(Info.field_46); + bsData.Read(Info.field_4A); + bsData.Read(Info.field_4E); + bsData.Read(Info.field_52); + bsData.Read(Info.field_56); + bsData.Read(Info.field_57); + + dword_1026EB98->FUNC_1000DAE0(dwUnk1,&Info); + dword_1026EB98->FUNC_1000D4C0(dwUnk2); + } +} +// not registered either +void FUNC_100120B0(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + char szName[257]; + char szData[4096]; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + memset(szName,0,sizeof(szName)); + + BYTE byteType = 0; + BYTE byteUnk1 = 0; + int iLength = 0; + DWORD dwUnk2 = 0; + BYTE byteNameLen = 0; + BYTE byteUnk3 = 0; + DWORD dwUnk4 = 0; + + bsData.Read(byteType); + + if(byteType == 0) + { + dword_1026EB98->FUNC_1000C140(); + dword_1026EB98->FUNC_1000CBF0(0,6001,0); + } + else if(byteType == 2) + { + dword_1026EB98->FUNC_1000E160(0); + } + else if(byteType == 6) + { + bsData.Read(byteUnk3); + bsData.Read(dwUnk4); + bsData.Read(byteNameLen); + bsData.Read(szName,byteNameLen); + + dword_1026EB98->FUNC_1000C140(); + dword_1026EB98->FUNC_1000D650(byteUnk3,dwUnk4,szName); + } + else if(byteType == 5) + { + bsData.Read(byteUnk3); + bsData.Read(dwUnk4); + + dword_1026EB98->FUNC_1000C140(); + dword_1026EB98->FUNC_1000D750(byteUnk3,dwUnk4); + } + else if(byteType == 1) + { + bsData.Read(byteUnk1); + bsData.Read(dwUnk2); + bsData.Read(iLength); + + if(iLength >= 0 && iLength <= 4096) + { + bsData.Read(szData,iLength); + dword_1026EB98->FUNC_1000D500(byteUnk1,dwUnk2,szData,iLength); + } + } +} +// not registered either +void FUNC_1000EC30(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + if(dword_1026EB98) + { + CDownloadListDispatch *pDownloadList = dword_1026EB98; + pDownloadList->field_221 = 1; + pDownloadList->FUNC_1000C0C0(); + } +} +// MATCH +void UnkAC(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + ACTORID ActorID; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + CActorPool *pActorPool = pNetGame->GetActorPool(); + if(!pActorPool) return; + + bsData.Read(ActorID); + + pActorPool->sub_100016F0(ActorID); +} //---------------------------------------------------- +// MATCH void RegisterRPCs(RakClientInterface * pRakClient) { REGISTER_STATIC_RPC(pRakClient,Unk22); @@ -103,6 +1279,7 @@ void RegisterRPCs(RakClientInterface * pRakClient) //---------------------------------------------------- +// MATCH void UnRegisterRPCs(RakClientInterface * pRakClient) { UNREGISTER_STATIC_RPC(pRakClient,Unk3C); diff --git a/saco/net/netrpc.h b/saco/net/netrpc.h index d5e3856..2c9ddd3 100644 --- a/saco/net/netrpc.h +++ b/saco/net/netrpc.h @@ -3,6 +3,8 @@ //---------------------------------------------------- +void sub_10001A60(PLAYERID playerId, int iEvent, int iVehicle, int iArg1, int iArg2); // .text:10001A60 + void RegisterRPCs(RakClientInterface *); void UnRegisterRPCs(RakClientInterface *); diff --git a/saco/net/objectpool.cpp b/saco/net/objectpool.cpp index e851fcc..d667a12 100644 --- a/saco/net/objectpool.cpp +++ b/saco/net/objectpool.cpp @@ -10,3 +10,18 @@ CObjectPool::CObjectPool() } field_0 = 0; }; + +//---------------------------------------------------- + +CObjectPool::~CObjectPool() +{ + for(int i = 0; i < MAX_OBJECTS; i++) + FUNC_10012850(i); +} + +//---------------------------------------------------- + +void CObjectPool::FUNC_10012850(WORD wObject) +{ + // TODO: CObjectPool::FUNC_10012850() .text:10012850 +} diff --git a/saco/net/objectpool.h b/saco/net/objectpool.h index c0b04f7..6520af7 100644 --- a/saco/net/objectpool.h +++ b/saco/net/objectpool.h @@ -1,13 +1,49 @@ #pragma once +// samp object; holds the material/text data +class CObject +{ +public: + char _gap0[68]; + int field_44; + char _gap48[11]; + BYTE field_53; + float field_54; + char _gap58[4403]; + int field_118B; + char _gap118F[10]; + BYTE field_1199; + +public: + void FUNC_100A7E20(); + void FUNC_100A9E30(); + void FUNC_100A9E70(); + void FUNC_100A80A0(VECTOR *pPos); // .text:100A80A0 + void FUNC_100A8030(); // .text:100A8030 + void FUNC_100A8530(int iModel, int iIndex, char *szTxd, char *szTexture, DWORD dwColour); // .text:100A8530 + void FUNC_100A86B0(int iIndex, char *szText, BYTE byteSize, char *szFont, BYTE byteBold, + BYTE byteAlign, DWORD dwFontColour, DWORD dwBackColour, BYTE byteUnk); // .text:100A86B0 +}; + class CObjectPool { -private: +public: int field_0; int field_4[MAX_OBJECTS]; int field_FA4[MAX_OBJECTS]; public: - CObjectPool(); + char FUNC_10012DE0(VECTOR *vecStart, VECTOR *vecEnd); // .text:10012DE0 + CObjectPool(); + ~CObjectPool(); // .text:10013160 + + void FUNC_10012850(WORD wObject); // .text:10012850 + + void FUNC_10012A50(); // .text:10012A50 + void FUNC_10012B10(); // .text:10012B10 + void FUNC_10012B90(); // .text:10012B90 + void FUNC_10012BD0(); // .text:10012BD0 + + CObject * FUNC_100129D0(ENTITY_TYPE *pEntity); // todo: implement }; diff --git a/saco/net/pickuppool.h b/saco/net/pickuppool.h index 9ec0908..a16328f 100644 --- a/saco/net/pickuppool.h +++ b/saco/net/pickuppool.h @@ -3,6 +3,15 @@ #define MAX_PICKUPS 4096 +#pragma pack(1) +typedef struct _PICKUP { + int iModel; + int iType; + float fX; + float fY; + float fZ; +} PICKUP, *PPICKUP; + //---------------------------------------------------- class CPickupPool @@ -20,6 +29,7 @@ private: public: + // MATCH CPickupPool() { memset(field_F004, 0, sizeof(field_F004)); field_0 = 0; @@ -33,6 +43,11 @@ public: ~CPickupPool(); + void sub_10013440(int iPickup); // .text:10013440 + void sub_10013320(int iPickup); // .text:10013320 + void sub_10013270(PICKUP *pPickup, int iPickup); // .text:10013270 + void sub_10013380(WORD wPickup); // .text:10013380 + }; //---------------------------------------------------- \ No newline at end of file diff --git a/saco/net/playerpool.cpp b/saco/net/playerpool.cpp index 7b366c0..cb09a73 100644 --- a/saco/net/playerpool.cpp +++ b/saco/net/playerpool.cpp @@ -43,3 +43,14 @@ CPlayerPool::CPlayerPool() } //---------------------------------------------------- + +//---------------------------------------------------- + +CPlayerPool::~CPlayerPool() +{ + delete m_pLocalPlayer; + m_pLocalPlayer = NULL; + + for(PLAYERID playerId = 0; playerId < MAX_PLAYERS; playerId++) + sub_10014090(playerId,0); +} diff --git a/saco/net/playerpool.h b/saco/net/playerpool.h index 50b4eba..cb53c20 100644 --- a/saco/net/playerpool.h +++ b/saco/net/playerpool.h @@ -10,7 +10,7 @@ class CPlayerPool { -private: +public: int field_0; PLAYERID m_LocalPlayerID; @@ -32,11 +32,26 @@ public: if(playerId > MAX_PLAYERS) return; CNetPlayer* pPlayer = m_pPlayers[playerId]; if(!pPlayer) return; - pPlayer->m_PlayerName = szName; + pPlayer->SetPlayerName(szName); } + PCHAR GetPlayerName(PLAYERID playerId) { // .text:100175C0 + if(playerId == m_LocalPlayerID) return (PCHAR)field_6.c_str(); + CNetPlayer *pPlayer = playerId > MAX_PLAYERS ? NULL : m_pPlayers[playerId]; + if(!pPlayer) return NULL; + return pPlayer->GetName(); + }; + CLocalPlayer * GetLocalPlayer() { return m_pLocalPlayer; }; + void FUNC_10013960(); // .text:10013960 + + BOOL sub_10014090(PLAYERID playerId, BYTE byteReason); // .text:10014090 + + BOOL sub_10014250(PLAYERID playerId, PCHAR szPlayerName, BOOL bIsNPC); // .text:10014250 + + PLAYERID FUNC_100138C0(DWORD dwPedPtr); // .text:100138C0 + CRemotePlayer* GetAt(PLAYERID playerId) { if(playerId > MAX_PLAYERS) { return NULL; } CNetPlayer *pNetPlayer = m_pPlayers[playerId]; @@ -46,7 +61,37 @@ public: PLAYERID GetLocalPlayerID() { return m_LocalPlayerID; }; + int GetSlotState(PLAYERID playerId) { if(playerId >= MAX_PLAYERS) return 0; return field_2A[playerId]; } + + PCHAR FUNC_100175C0(int iPlayerId); // .text:100175C0 + + BOOL IsPlayerNPC(PLAYERID playerId) { + if(playerId > MAX_PLAYERS) return FALSE; + CNetPlayer *pPlayer = m_pPlayers[playerId]; + if(!pPlayer) return FALSE; + return pPlayer->m_bIsNPC; + } + int GetPlayerScore(PLAYERID playerId) { + if(playerId > MAX_PLAYERS) return 0; + CNetPlayer *pPlayer = m_pPlayers[playerId]; + if(!pPlayer) return 0; + return pPlayer->GetScore(); + } + int GetPlayerPing(PLAYERID playerId) { + if(playerId > MAX_PLAYERS) return 0; + CNetPlayer *pPlayer = m_pPlayers[playerId]; + if(!pPlayer) return 0; + return pPlayer->GetPing(); + } + int GetPlayerCount(BOOL bIncludeNPCs); // .text:100139F0, todo: implement + + class CObject *FUNC_10013B70(ENTITY_TYPE *pEntity); // todo: implement + + void sub_10013C90(); // todo: implement + void sub_10013D10(); // todo: implement + CPlayerPool(); + ~CPlayerPool(); // .text:10014120 }; //---------------------------------------------------- diff --git a/saco/net/remoteplayer.cpp b/saco/net/remoteplayer.cpp index b33d1ff..dc1552b 100644 --- a/saco/net/remoteplayer.cpp +++ b/saco/net/remoteplayer.cpp @@ -6,6 +6,52 @@ extern CGame *pGame; using namespace RakNet; extern CNetGame* pNetGame; +extern CChatWindow *pChatWindow; + +float fPosLerp = 0.1f; +float fVehLerp = 0.06f; +int bClampVehicleTurnSpeed = 1; + +//---------------------------------------------------- +// layout locks: a negative array size fails the build if any offset moves + +typedef char CRemotePlayer_offsets[( + offsetof(CRemotePlayer, field_C5) == 0xC5 && + offsetof(CRemotePlayer, field_C5.quat) == 0xD7 && + offsetof(CRemotePlayer, field_C5.vecOffset) == 0xF7 && + offsetof(CRemotePlayer, field_C5.wVehicleID) == 0x103 && + sizeof(((CRemotePlayer *)0)->field_C5) == 68 && + offsetof(CRemotePlayer, field_109) == 0x109 && + offsetof(CRemotePlayer, field_10A) == 0x10A && + offsetof(CRemotePlayer, field_10B) == 0x10B && + offsetof(CRemotePlayer, field_10C) == 0x10C && + offsetof(CRemotePlayer, field_160) == 0x160 && + offsetof(CRemotePlayer, field_17C) == 0x17C && + offsetof(CRemotePlayer, field_188) == 0x188 && + offsetof(CRemotePlayer, field_194) == 0x194 && + offsetof(CRemotePlayer, field_1A0) == 0x1A0 && + offsetof(CRemotePlayer, field_1AC) == 0x1AC && + offsetof(CRemotePlayer, field_1C5) == 0x1C5 && + offsetof(CRemotePlayer, field_1C9) == 0x1C9 && + offsetof(CRemotePlayer, m_pPlayerPed) == 0x1DD && + offsetof(CRemotePlayer, field_1E1) == 0x1E1 && + offsetof(CRemotePlayer, m_PlayerID) == 0x1E5 && + offsetof(CRemotePlayer, field_1E7) == 0x1E7 && + offsetof(CRemotePlayer, field_1F9) == 0x1F9) ? 1 : -1]; + +typedef char CNetPlayer_offsets[(offsetof(CNetPlayer, m_PlayerName) == 0x14) ? 1 : -1]; +typedef char BULLET_SYNC_offsets[(offsetof(BULLET_SYNC_DATA, byteWeaponID) == 0x27 && + sizeof(BULLET_SYNC_DATA) == 0x28) ? 1 : -1]; +typedef char TRAILER_SYNC_offsets[(offsetof(TRAILER_SYNC_DATA, vecTurnSpeed) == 0x2A && + sizeof(TRAILER_SYNC_DATA) == 0x36) ? 1 : -1]; +typedef char UNOCCUPIED_SYNC_offsets[(offsetof(UNOCCUPIED_SYNC_DATA, vecTurnSpeed) == 0x33 && + offsetof(CVehiclePool, field_8E34) == 0x8E34 && offsetof(CVehicle, field_83) == 0x83) ? 1 : -1]; +typedef char struc_41_offsets[(offsetof(struc_41, field_30) == 0x30) ? 1 : -1]; +typedef char WEAPON_SLOT_offsets[(offsetof(WEAPON_SLOT_TYPE, field_8) == 8 && + sizeof(WEAPON_SLOT_TYPE) == 28) ? 1 : -1]; +typedef char CPlayerPool_offsets[(offsetof(CPlayerPool, field_6) == 6 && + sizeof(std::string) == 0x1C) ? 1 : -1]; + //---------------------------------------------------- @@ -46,11 +92,11 @@ void CRemotePlayer::ResetAllSyncAttributes() field_C = 0; field_1B4 = 0; field_1C5 = 2; - memset(field_C5, 0, sizeof(field_C5)); + memset(&field_C5, 0, sizeof(field_C5)); memset(field_19, 0, sizeof(field_19)); memset(field_AD, 0, sizeof(field_AD)); memset(field_8E, 0, sizeof(field_8E)); - memset(field_1C9, 0, sizeof(field_1C9)); + memset(&field_1C9, 0, sizeof(field_1C9)); if(field_1F9) { pGame->DisableMarker(field_1F9); @@ -87,7 +133,7 @@ DWORD CRemotePlayer::GetPlayerColorAsARGB() void CRemotePlayer::EnterVehicle(VEHICLEID VehicleID, BOOL bPassenger) { CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); - CVehicle *pVehicle = pVehiclePool->GetAt(VehicleID); + CVehicle *pVehicle = (CVehicle *)pVehiclePool->GetAt(VehicleID); if(m_pPlayerPed && pVehicle && !m_pPlayerPed->IsInVehicle()) { int iGtaVehicleID = pVehiclePool->FindGtaIDFromID(VehicleID); @@ -100,6 +146,948 @@ void CRemotePlayer::EnterVehicle(VEHICLEID VehicleID, BOOL bPassenger) //---------------------------------------------------- +void CRemotePlayer::FUNC_10017530() +{ + if(m_pPlayerPed) { + ResetAllSyncAttributes(); + pGame->DeletePlayerPed((int)m_pPlayerPed); + m_pPlayerPed = NULL; + } + field_10A = 0; + field_4 = 1; +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10017570() +{ + if(m_pPlayerPed) { + m_pPlayerPed->SetKeys(0,0,0); + m_pPlayerPed->FUNC_100AFFD0(); + } + if(field_10A != 32) field_10A = 32; + ResetAllSyncAttributes(); +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_100143A0() +{ + if(!m_pPlayerPed) return; + if(!m_pPlayerPed->IsAdded()) return; + if(FUNC_10014390()) return; + if(pGame && !pGame->m_bHeadMove) return; + if(m_pPlayerPed->FUNC_100ADC90()) return; + + MATRIX4X4 mat; + m_pPlayerPed->GetMatrix(&mat); + + DWORD dwTick = GetTickCount(); + + if((dwTick - field_1D5) > 1000) { + CAMERA_AIM *pAim = GameGetRemotePlayerAim(m_pPlayerPed->m_bytePlayerNumber); + VECTOR vecAim; + vecAim.X = pAim->f1x * 20.0f + mat.pos.X; + vecAim.Y = pAim->f1y * 20.0f + mat.pos.Y; + vecAim.Z = pAim->f1z * 20.0f + mat.pos.Z; + field_1D5 = dwTick; + field_1C9.X = vecAim.X - mat.pos.X; + field_1C9.Y = vecAim.Y - mat.pos.Y; + field_1C9.Z = vecAim.Z - mat.pos.Z; + } + + if((dwTick - field_1D9) > 200) { + VECTOR vec; + vec.X = mat.pos.X + field_1C9.X; + vec.Y = mat.pos.Y + field_1C9.Y; + vec.Z = mat.pos.Z + field_1C9.Z; + m_pPlayerPed->ApplyCommandTask("FollowPedSA", 0, 1000, -1, &vec, 0, 0.1f, 500, 3, 0); + field_1D9 = dwTick; + } +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_100150D0(VECTOR *pPos, VECTOR *pMoveSpeed) +{ + field_17C.X = pPos->X; + field_17C.Y = pPos->Y; + field_17C.Z = pPos->Z; + field_188.X = pMoveSpeed->X; + field_188.Y = pMoveSpeed->Y; + field_188.Z = pMoveSpeed->Z; + m_pPlayerPed->SetMoveSpeedVector(field_188); +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_100155E0(int a1, VECTOR *pPos, VECTOR *pMoveSpeed) +{ + FUNC_100B5210(a1, quat16C); + field_194 = *pPos; + field_1A0 = *pMoveSpeed; + field_1E1->SetMoveSpeedVector(*pMoveSpeed); +} + +//---------------------------------------------------- + +float CRemotePlayer::FUNC_100160A0(CRemotePlayer *pOther) +{ + if(!m_pPlayerPed) return 10000.0f; + + MATRIX4X4 matThis, matOther; + m_pPlayerPed->GetMatrix(&matThis); + pOther->m_pPlayerPed->GetMatrix(&matOther); + + return sqrt((matThis.pos.X - matOther.pos.X) * (matThis.pos.X - matOther.pos.X) + + (matThis.pos.Y - matOther.pos.Y) * (matThis.pos.Y - matOther.pos.Y) + + (matThis.pos.Z - matOther.pos.Z) * (matThis.pos.Z - matOther.pos.Z)); +} + +//---------------------------------------------------- + +float CRemotePlayer::FUNC_10016120() +{ + CEntity *pEntity = m_pPlayerPed; + if(!pEntity) return 10000.0f; + + if(field_10A == 19 && field_1E1) pEntity = field_1E1; + + return pEntity->FUNC_1009F0C0(); +} + +//---------------------------------------------------- + +int CRemotePlayer::FUNC_10016330() +{ + if(m_pPlayerPed && m_pPlayerPed->IsAdded() && !m_pPlayerPed->FUNC_100AC5D0() && + !m_pPlayerPed->FUNC_100AC640()) return field_1C5; + + return 0; +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10017610(char *szText) +{ + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + pChatWindow->AddChatMessage(pPlayerPool->GetPlayerName(m_PlayerID), + (TranslateColorCodeToRGBA(m_PlayerID) >> 8) | 0xFF000000, szText); +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10014540(short a1, short a2, short a3) +{ + field_1E9 = 1; + + if(field_1F9) { + pGame->DisableMarker(field_1F9); + field_1F9 = 0; + } + + if(m_pPlayerPed) return; + + field_1F9 = pGame->FUNC_100A0D90(0, (float)a1, (float)a2, (float)a3, m_PlayerID, 0); + field_1ED = a1; + field_1F1 = a2; + field_1F5 = a3; +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10014650() +{ + if(field_10A != 17) return; + if(!field_C5.wVehicleID) return; + if(field_C5.wVehicleID == INVALID_VEHICLE_ID) return; + if(field_C5.wVehicleID >= (MAX_VEHICLES + 1000)) return; + + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + CObjectPool *pObjectPool = pNetGame->GetObjectPool(); + CEntity *pEntity; + + if(field_C5.wVehicleID < MAX_VEHICLES) { + if(!pVehiclePool->field_3074[field_C5.wVehicleID]) return; + pEntity = (CEntity *)pVehiclePool->field_1134[field_C5.wVehicleID]; + } else { + WORD wObjectID = field_C5.wVehicleID - MAX_VEHICLES; + if(wObjectID > MAX_OBJECTS) return; + if(!pObjectPool->field_4[wObjectID]) return; + pEntity = (CEntity *)pObjectPool->field_FA4[wObjectID]; + } + + if(!pEntity) return; + + if(field_C5.vecOffset.X > 100.0f) return; + if(field_C5.vecOffset.X < -100.0f) return; + if(field_C5.vecOffset.Y > 100.0f) return; + if(field_C5.vecOffset.Y < -100.0f) return; + if(field_C5.vecOffset.Z > 100.0f) return; + if(field_C5.vecOffset.Z < -100.0f) return; + + MATRIX4X4 matEntity; + MATRIX4X4 matPed; + VECTOR vecMove; + VECTOR vecTurn; + VECTOR vecOut; + + pEntity->GetMatrix(&matEntity); + pEntity->GetMoveSpeedVector(&vecMove); + pEntity->GetTurnSpeedVector(&vecTurn); + m_pPlayerPed->GetMatrix(&matPed); + + FUNC_100B4D10(&vecOut, &matEntity, &field_C5.vecOffset); + matPed.pos = vecOut; + m_pPlayerPed->SetMatrix(matPed); +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10014800() +{ + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + + if(!m_pPlayerPed) return; + if(m_pPlayerPed->IsInVehicle()) return; + if(field_1E7 >= MAX_VEHICLES) return; + if(!pVehiclePool->field_3074[field_1E7]) return; + if(!pVehiclePool->field_1134[field_1E7]) return; + + BYTE byteWeapon; + if(field_10B) byteWeapon = field_AD[3]; + else byteWeapon = field_19[54]; + byteWeapon &= 0x3F; + + if(m_pPlayerPed->GetCurrentWeapon() != byteWeapon) { + m_pPlayerPed->SetArmedWeapon(byteWeapon, false); + if(m_pPlayerPed->GetCurrentWeapon() != byteWeapon) { + m_pPlayerPed->GiveWeapon(byteWeapon, 9999); + m_pPlayerPed->SetArmedWeapon(byteWeapon, false); + } + } + + int iGtaVehicleID = pVehiclePool->FindGtaIDFromID(field_1E7); + m_pPlayerPed->FUNC_100AC290(iGtaVehicleID, field_10B); +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10014FF0() +{ + if(!m_pPlayerPed) return; + + MATRIX4X4 mat; + float quatBlend[4]; + float quatCur[4]; + + m_pPlayerPed->GetMatrix(&mat); + FUNC_100B52B0(&mat, quatCur); + FUNC_100B5480(quatBlend, field_C5.quat, quatCur, 0.75f); + FUNC_100B6A80(quatBlend, &mat); + m_pPlayerPed->SetMatrix(mat); + + float fAngle = atan2(-mat.up.X, mat.up.Y) * 57.2957764f; + if(fAngle < 0.0f) fAngle += 360.0f; + else if(fAngle >= 360.0f) fAngle -= 360.0f; + m_pPlayerPed->FUNC_100ABF10(fAngle); +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10017260(BYTE *pSync, int iTime) +{ + field_1E1 = NULL; + if(iTime && (iTime - field_1BD) < 0) return; + + field_1BD = iTime; + memcpy(&field_C5, pSync, sizeof(field_C5)); + field_1B0 = pSync[0x22]; + field_1AC = pSync[0x23]; + field_1B8 = 16; + field_1B9 = GetTickCount(); + field_C = pSync[0x25]; + // the sign-bit mask is what emits jns; '< 0' emits jge + if(field_C5.iAnimation & 0x80000000) field_C5.iAnimation = 0; + + if(m_pPlayerPed && m_pPlayerPed->IsInVehicle() && FUNC_10014380() != 3 && FUNC_10014380() != 4 && + !m_pPlayerPed->FUNC_100AC640()) FUNC_100148F0(); + + if(field_10A != 17) field_10A = 17; +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10016370(BULLET_SYNC_DATA *pSync) +{ + if(!m_pPlayerPed) return; + if(!m_pPlayerPed->IsAdded()) return; + + SHOT_SYNC_DATA shotSync; + + memset(&shotSync, 0, sizeof(shotSync)); + shotSync.vecOrigin.X = pSync->vecHitOrigin.X; + shotSync.vecOrigin.Y = pSync->vecHitOrigin.Y; + shotSync.vecOrigin.Z = pSync->vecHitOrigin.Z; + shotSync.vecColPoint.X = pSync->vecHitTarget.X; + shotSync.vecColPoint.Y = pSync->vecHitTarget.Y; + shotSync.vecColPoint.Z = pSync->vecHitTarget.Z; + shotSync.vecOffset.X = pSync->vecCenterOfHit.X; + shotSync.vecOffset.Y = pSync->vecCenterOfHit.Y; + shotSync.vecOffset.Z = pSync->vecCenterOfHit.Z; + + if(pSync->byteHitType) { + if(shotSync.vecOffset.X > 300.0f || shotSync.vecOffset.X < -300.0f || + shotSync.vecOffset.Y > 300.0f || shotSync.vecOffset.Y < -300.0f || + shotSync.vecOffset.Z > 300.0f || shotSync.vecOffset.Z < -300.0f) return; + + if(pSync->byteHitType == 1) { + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + if(pPlayerPool) { + PLAYERID playerId = pSync->wHitID; + if(playerId == pPlayerPool->m_LocalPlayerID) { + shotSync.pAttachedTo = (ENTITY_TYPE *)pGame->FindPlayerPed()->m_pPed; + } else if(playerId == m_PlayerID) return; + else if(pPlayerPool->GetSlotState(playerId)) { + CPlayerPed *pPlayerPed = pPlayerPool->GetAt(playerId)->m_pPlayerPed; + if(pPlayerPed) shotSync.pAttachedTo = (ENTITY_TYPE *)pPlayerPed->m_pPed; + } + } + } else if(pSync->byteHitType == 2) { + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + if(pVehiclePool) { + VEHICLEID VehicleID = pSync->wHitID; + if(pVehiclePool->GetSlotState(VehicleID)) + shotSync.pAttachedTo = + (ENTITY_TYPE *)((CVehicle *)pVehiclePool->GetAt(VehicleID))->m_pVehicle; + } + } + } + + if(m_pPlayerPed->IsAdded()) { + if(m_pPlayerPed->GetCurrentWeapon() != pSync->byteWeaponID) { + m_pPlayerPed->SetArmedWeapon(pSync->byteWeaponID, true); + if(m_pPlayerPed->GetCurrentWeapon() != pSync->byteWeaponID) { + m_pPlayerPed->GiveWeapon(pSync->byteWeaponID, 9999); + m_pPlayerPed->SetArmedWeapon(pSync->byteWeaponID, true); + } + } + } + + m_pPlayerPed->FUNC_100AF280(&shotSync); + m_pPlayerPed->FUNC_100AFA70(); +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_100158D0(UNOCCUPIED_SYNC_DATA *pSync) +{ + if(!pSync->VehicleID) return; + if(pSync->VehicleID == INVALID_VEHICLE_ID) return; + + MATRIX4X4 mat; + VECTOR vecMove; + + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + if(!pVehiclePool) return; + + CVehicle *pVehicle = (CVehicle *)pVehiclePool->GetAt(pSync->VehicleID); + pVehiclePool->field_8E34[pSync->VehicleID] = m_PlayerID; + if(!pVehicle) return; + + if(pVehicle->FUNC_100B70E0()) return; + if(pVehicle->FUNC_100B7CF0()) return; + if(!FUNC_100B4A70(&pSync->vecDirection)) return; + if(!FUNC_100B4A70(&pSync->vecRoll)) return; + if(!FUNC_100B4B50(&pSync->vecPos)) return; + if(!FUNC_100B4AE0(&pSync->vecMoveSpeed)) return; + if(!FUNC_100B4AE0(&pSync->vecTurnSpeed)) return; + + pVehicle->GetMatrix(&mat); + + mat.up.X = pSync->vecDirection.X; + mat.up.Y = pSync->vecDirection.Y; + mat.up.Z = pSync->vecDirection.Z; + mat.right.X = pSync->vecRoll.X; + mat.right.Y = pSync->vecRoll.Y; + mat.right.Z = pSync->vecRoll.Z; + + if(FloatOffset(pSync->vecPos.X, mat.pos.X) <= 0.1f && + FloatOffset(pSync->vecPos.Y, mat.pos.Y) <= 0.1f && + FloatOffset(pSync->vecPos.Z, mat.pos.Z) <= 0.1f) return; + + if(FloatOffset(pSync->vecPos.X, mat.pos.X) > 6.0f || + FloatOffset(pSync->vecPos.Y, mat.pos.Y) > 6.0f || + FloatOffset(pSync->vecPos.Z, mat.pos.Z) > 3.0f) { + mat.pos.X = pSync->vecPos.X; + mat.pos.Y = pSync->vecPos.Y; + mat.pos.Z = pSync->vecPos.Z; + pVehicle->SetMatrix(mat); + pVehicle->SetMoveSpeedVector(pSync->vecMoveSpeed); + pVehicle->SetTurnSpeedVector(pSync->vecTurnSpeed); + return; + } + + pVehicle->SetMatrix(mat); + pVehicle->SetMoveSpeedVector(pSync->vecMoveSpeed); + pVehicle->SetTurnSpeedVector(pSync->vecTurnSpeed); + + vecMove.X = 0.0f; + vecMove.Y = 0.0f; + vecMove.Z = 0.0f; + pVehicle->GetMoveSpeedVector(&vecMove); + + if(FloatOffset(pSync->vecPos.X, mat.pos.X) > 0.05) + vecMove.X = (pSync->vecPos.X - mat.pos.X) * fVehLerp + vecMove.X; + if(FloatOffset(pSync->vecPos.Y, mat.pos.Y) > 0.05) + vecMove.Y = (pSync->vecPos.Y - mat.pos.Y) * fVehLerp + vecMove.Y; + if(FloatOffset(pSync->vecPos.Z, mat.pos.Z) > 0.05) + vecMove.Z = (pSync->vecPos.Z - mat.pos.Z) * fVehLerp + vecMove.Z; + + pVehicle->SetMoveSpeedVector(vecMove); + pVehicle->field_83 = 1; +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10015140() +{ + MATRIX4X4 mat; + VECTOR vecMove; + float fZLimit; + + vecMove.X = 0.0f; + vecMove.Y = 0.0f; + vecMove.Z = 0.0f; + + if(!field_1E1) return; + + field_1E1->GetMatrix(&mat); + + if(!field_1E1->IsAdded()) { + mat.pos.X = field_194.X; + mat.pos.Y = field_194.Y; + mat.pos.Z = field_194.Z; + field_1E1->SetMatrix(mat); + return; + } + + field_160 = FloatOffset(field_194.X, mat.pos.X); + field_164 = FloatOffset(field_194.Y, mat.pos.Y); + field_168 = FloatOffset(field_194.Z, mat.pos.Z); + + if(field_160 <= 0.05f && field_164 <= 0.05f && field_168 <= 0.05f) return; + + fZLimit = 0.5f; + if(field_1E1->GetVehicleSubtype() == 4 || field_1E1->GetVehicleSubtype() == 5 || + field_1E1->GetVehicleSubtype() == 3) fZLimit = 2.0f; + + if(field_160 > 8.0f || field_164 > 8.0f || field_168 > fZLimit) { + mat.pos.X = field_194.X; + mat.pos.Y = field_194.Y; + mat.pos.Z = field_194.Z; + field_1E1->SetMatrix(mat); + field_1E1->SetMoveSpeedVector(field_1A0); + return; + } + + field_1E1->GetMoveSpeedVector(&vecMove); + if(field_160 > 0.05f) vecMove.X = (field_194.X - mat.pos.X) * fVehLerp + vecMove.X; + if(field_164 > 0.05f) vecMove.Y = (field_194.Y - mat.pos.Y) * fVehLerp + vecMove.Y; + if(field_168 > 0.05f) vecMove.Z = (field_194.Z - mat.pos.Z) * fVehLerp + vecMove.Z; + + if(FloatOffset(vecMove.X, 0.0f) > 0.01f || FloatOffset(vecMove.Y, 0.0f) > 0.01f || + FloatOffset(vecMove.Z, 0.0f) > 0.01f) field_1E1->SetMoveSpeedVector(vecMove); +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10015C90(TRAILER_SYNC_DATA *pTrailer) +{ + if(!pTrailer->TrailerID) return; + if(pTrailer->TrailerID == INVALID_VEHICLE_ID) return; + + MATRIX4X4 mat; + VECTOR vecMove; + + CVehicle *pTrailerVeh = (CVehicle *)pNetGame->GetVehiclePool()->GetAt(pTrailer->TrailerID); + CVehicle *pVehicle = field_1E1; + if(!pVehicle) return; + if(!pTrailerVeh) return; + + if(!pTrailerVeh->FUNC_100B7D70() && !pVehicle->FUNC_100B7DD0()) return; + + if(pVehicle->FUNC_100B7C90() != pTrailerVeh) { + pVehicle->FUNC_100B7C80((int)pTrailerVeh); + pVehicle->FUNC_100B7C10(); + } + + pTrailerVeh->GetMatrix(&mat); + FUNC_100B6A80(pTrailer->quat, &mat); + + if(FloatOffset(pTrailer->vecPos.X, mat.pos.X) <= 0.5f && + FloatOffset(pTrailer->vecPos.Y, mat.pos.Y) <= 0.5f && + FloatOffset(pTrailer->vecPos.Z, mat.pos.Z) <= 0.5f) return; + + if(FloatOffset(pTrailer->vecPos.X, mat.pos.X) > 6.0f || + FloatOffset(pTrailer->vecPos.Y, mat.pos.Y) > 6.0f || + FloatOffset(pTrailer->vecPos.Z, mat.pos.Z) > 3.0f) { + mat.pos.X = pTrailer->vecPos.X; + mat.pos.Y = pTrailer->vecPos.Y; + mat.pos.Z = pTrailer->vecPos.Z; + pTrailerVeh->SetMatrix(mat); + pTrailerVeh->SetMoveSpeedVector(pTrailer->vecMoveSpeed); + pTrailerVeh->SetTurnSpeedVector(pTrailer->vecTurnSpeed); + return; + } + + pTrailerVeh->SetMatrix(mat); + pTrailerVeh->SetTurnSpeedVector(pTrailer->vecTurnSpeed); + + vecMove.X = 0.0f; + vecMove.Y = 0.0f; + vecMove.Z = 0.0f; + pTrailerVeh->GetMoveSpeedVector(&vecMove); + + if(FloatOffset(pTrailer->vecPos.X, mat.pos.X) > 0.05f) + vecMove.X = (pTrailer->vecPos.X - mat.pos.X) * 0.025f + vecMove.X; + if(FloatOffset(pTrailer->vecPos.Y, mat.pos.Y) > 0.05f) + vecMove.Y = (pTrailer->vecPos.Y - mat.pos.Y) * 0.025f + vecMove.Y; + if(FloatOffset(pTrailer->vecPos.Z, mat.pos.Z) > 0.05f) + vecMove.Z = (pTrailer->vecPos.Z - mat.pos.Z) * 0.025f + vecMove.Z; + + pTrailerVeh->SetMoveSpeedVector(vecMove); +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10014950() +{ + if(!m_pPlayerPed) return; + if(!m_pPlayerPed->IsAdded()) return; + + if(field_C == 1 && !m_pPlayerPed->FUNC_100AC690()) { + m_pPlayerPed->FUNC_100AC6C0(); + return; + } + if(field_C != 1 && m_pPlayerPed->FUNC_100AC690()) m_pPlayerPed->FUNC_100AC720(); + + if(field_C == 11 && !m_pPlayerPed->IsCellphoneEnabled()) { + m_pPlayerPed->ToggleCellphone(1); + return; + } + if(field_C != 11 && m_pPlayerPed->IsCellphoneEnabled()) { + m_pPlayerPed->ToggleCellphone(0); + return; + } + + if(field_C == 2 && !m_pPlayerPed->IsInJetpackMode()) { + field_8 = 1; + m_pPlayerPed->StartJetpack(); + return; + } + if(field_C != 2 && m_pPlayerPed->IsInJetpackMode()) { + field_8 = 0; + m_pPlayerPed->StopJetpack(); + return; + } + + if(field_C == 10 && !m_pPlayerPed->HasHandsUp()) m_pPlayerPed->HandsUp(); + if(field_C != 10 && m_pPlayerPed->HasHandsUp()) m_pPlayerPed->StopDancing(); + + if(!m_pPlayerPed->FUNC_100AE260() && field_C == 68) m_pPlayerPed->FUNC_100AE100(); + if(m_pPlayerPed->FUNC_100AE260() && field_C != 68) m_pPlayerPed->FUNC_100AE1E0(); + + if(field_C == 20 && m_pPlayerPed->FUNC_100ADC90() != 1) m_pPlayerPed->FUNC_100B02D0(1); + if(field_C == 22 && m_pPlayerPed->FUNC_100ADC90() != 2) m_pPlayerPed->FUNC_100B02D0(2); + if(field_C == 23 && m_pPlayerPed->FUNC_100ADC90() != 3) m_pPlayerPed->FUNC_100B02D0(3); + if(field_C == 21 && m_pPlayerPed->FUNC_100ADC90() != 4) m_pPlayerPed->FUNC_100B02D0(4); + + if(m_pPlayerPed->FUNC_100ADC90()) { + if(field_C == 20 || field_C == 22 || field_C == 23 || field_C == 21) + m_pPlayerPed->FUNC_100ADCA0(); + else m_pPlayerPed->StopCarrying(); + } + + if(!m_pPlayerPed->FUNC_100ACEF0() && field_C == 24) m_pPlayerPed->FUNC_100ACF00(1); + if(m_pPlayerPed->FUNC_100ACEF0() && field_C != 24 && field_C != 3 && field_C != 4) + m_pPlayerPed->FUNC_100ACF00(0); + + if(!m_pPlayerPed->FUNC_100ACE40() && field_C == 25) m_pPlayerPed->FUNC_100ACE50(1); + if(m_pPlayerPed->FUNC_100ACE40() && field_C != 25 && field_C != 3 && field_C != 4) + m_pPlayerPed->FUNC_100ACE50(0); + + m_pPlayerPed->FUNC_100AD440(); +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10014C40() +{ + if(!m_pPlayerPed) return; + + MATRIX4X4 mat; + VECTOR vecMove; + float fOffZ; + + m_pPlayerPed->GetMatrix(&mat); + + if(!m_pPlayerPed->IsAdded()) { + mat.pos.X = field_17C.X; + mat.pos.Y = field_17C.Y; + mat.pos.Z = field_17C.Z; + m_pPlayerPed->SetMatrix(mat); + return; + } + + field_160 = FloatOffset(field_17C.X, mat.pos.X); + field_164 = FloatOffset(field_17C.Y, mat.pos.Y); + fOffZ = FloatOffset(field_17C.Z, mat.pos.Z); + field_168 = fOffZ; + + if(field_160 <= 0.00001f && field_164 <= 0.00001f && fOffZ <= 0.00001f) return; + + if(field_160 > 2.0f || field_164 > 2.0f || fOffZ > 1.0f) { + mat.pos.X = field_17C.X; + mat.pos.Y = field_17C.Y; + mat.pos.Z = field_17C.Z; + m_pPlayerPed->SetMatrix(mat); + return; + } + + m_pPlayerPed->GetMoveSpeedVector(&vecMove); + float fLerp = fPosLerp; + if(field_160 > 0.00001f) vecMove.X = (field_17C.X - mat.pos.X) * fLerp + vecMove.X; + if(field_164 > 0.00001f) vecMove.Y = (field_17C.Y - mat.pos.Y) * fLerp + vecMove.Y; + if(field_168 > 0.00001f) vecMove.Z = (field_17C.Z - mat.pos.Z) * fLerp + vecMove.Z; + m_pPlayerPed->SetMoveSpeedVector(vecMove); +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10014E60() +{ + if(!m_pPlayerPed) return; + + MATRIX4X4 mat; + VECTOR vecMove; + + m_pPlayerPed->GetMatrix(&mat); + + if(!m_pPlayerPed->IsAdded()) { + mat.pos.X = field_17C.X; + mat.pos.Y = field_17C.Y; + m_pPlayerPed->SetMatrix(mat); + return; + } + + field_160 = FloatOffset(field_17C.X, mat.pos.X); + field_164 = FloatOffset(field_17C.Y, mat.pos.Y); + + if(field_160 <= 0.01f && field_164 <= 0.01f) return; + + if(field_160 > 2.0f || field_164 > 2.0f) { + mat.pos.X = field_17C.X; + mat.pos.Y = field_17C.Y; + m_pPlayerPed->SetMatrix(mat); + return; + } + + m_pPlayerPed->GetMoveSpeedVector(&vecMove); + if(field_160 > 0.001f) vecMove.X = (field_17C.X - mat.pos.X) * 0.1f + vecMove.X; + if(field_164 > 0.001f) vecMove.Y = (field_17C.Y - mat.pos.Y) * 0.1f + vecMove.Y; + m_pPlayerPed->SetMoveSpeedVector(vecMove); +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10015460() +{ + MATRIX4X4 mat; + float quatCur[4]; + float quatBlend[4]; + VECTOR vecTurn; + + vecTurn.X = 0.0f; + vecTurn.Y = 0.0f; + vecTurn.Z = 0.0f; + + if(!field_1E1) return; + + if(bClampVehicleTurnSpeed) { + field_1E1->GetTurnSpeedVector(&vecTurn); + + if(vecTurn.X > 0.02f) vecTurn.X = 0.02f; + else if(vecTurn.X < -0.02f) vecTurn.X = -0.02f; + if(vecTurn.Y > 0.02f) vecTurn.Y = 0.02f; + else if(vecTurn.Y < -0.02f) vecTurn.Y = -0.02f; + if(vecTurn.Z > 0.02f) vecTurn.Z = 0.02f; + else if(vecTurn.Z < -0.02f) vecTurn.Z = -0.02f; + + field_1E1->SetTurnSpeedVector(vecTurn); + } + + field_1E1->GetMatrix(&mat); + FUNC_100B5210((int)&mat, quatCur); + FUNC_100B5480(quatBlend, quat16C, quatCur, 0.75f); + FUNC_100B5500(quatBlend); + FUNC_100B5470(quatBlend, &mat); + field_1E1->SetMatrix(mat); +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10015760(BYTE *pAim) +{ + if(!m_pPlayerPed) return; + + m_pPlayerPed->SetCameraMode(pAim[0]); + + CAMERA_AIM aim; + VECTOR vecFront; + VECTOR vecUp; + + vecUp.X = 0.0f; + vecUp.Y = 0.0f; + vecUp.Z = 0.0f; + + aim.f1x = *(float *)(pAim + 1); + aim.f1y = *(float *)(pAim + 5); + aim.f1z = *(float *)(pAim + 9); + vecFront.X = *(float *)(pAim + 1); + vecFront.Y = *(float *)(pAim + 5); + vecFront.Z = *(float *)(pAim + 9); + + FUNC_100B6900(&vecFront, &vecUp); + + aim.f2x = vecUp.X; + aim.f2y = vecUp.Y; + aim.f2z = vecUp.Z; + aim.pos1x = *(float *)(pAim + 0xD); + aim.pos1y = *(float *)(pAim + 0x11); + aim.pos1z = *(float *)(pAim + 0x15); + aim.pos2x = *(float *)(pAim + 0xD); + aim.pos2y = *(float *)(pAim + 0x11); + aim.pos2z = *(float *)(pAim + 0x15); + + m_pPlayerPed->SetCurrentAim(&aim); + m_pPlayerPed->SetAimZ(*(float *)(pAim + 0x19)); + m_pPlayerPed->SetCameraExtZoom((float)(pAim[0x1D] & 0x3F) * 0.015873017f, + (float)pAim[0x1E] * 0.0039215689f); + + WEAPON_SLOT_TYPE *pSlot = m_pPlayerPed->GetCurrentWeaponSlot(); + BYTE byteState = pAim[0x1D] >> 6; + if(byteState == 3) pSlot->field_4 = 2; + else if(byteState != 2) pSlot->field_8 = pAim[0x1D] >> 6; + else if(pSlot->field_8 < 2) pSlot->field_8 = 2; +} + +//---------------------------------------------------- + +BOOL CRemotePlayer::FUNC_10017130(int a1, int iSkin, int a3, VECTOR *pPos, float fRotation, + DWORD dwColor, BYTE byteStyle) +{ + if(!pGame->IsGameLoaded()) return FALSE; + + if(m_pPlayerPed) { + pGame->DeletePlayerPed((int)m_pPlayerPed); + m_pPlayerPed = NULL; + } + + CPlayerPed *pPlayerPed = pGame->sub_100A0110(iSkin, pPos->X, pPos->Y, pPos->Z, + fRotation, 1, field_10C); + if(pPlayerPed) { + if(dwColor) SetRadarColor(m_PlayerID, dwColor); + + if(field_1F9) { + pGame->DisableMarker(field_1F9); + field_1F9 = 0; + } + + if(pNetGame->GetSettings()->field_30) pPlayerPed->ShowMarker(m_PlayerID); + + m_pPlayerPed = pPlayerPed; + field_1B0 = 100.0f; + pPlayerPed->SetKeys(0, 0, 0); + + if(byteStyle != 4) pPlayerPed->SetFightingStyle(byteStyle); + + if(field_10A != 33) field_10A = 33; + return TRUE; + } + + if(field_10A) field_10A = 0; + return FALSE; +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10015650(MATRIX4X4 *pMat, VECTOR *pMoveSpeed, float fT) +{ + int bTeleport = 0; + + if(!m_pPlayerPed) return; + if(!field_1E1) return; + + MATRIX4X4 mat; + VECTOR vecMove; + float fDiff; + + field_1E1->GetMatrix(&mat); + + if(pMat->pos.X >= mat.pos.X) fDiff = pMat->pos.X - mat.pos.X; + else fDiff = mat.pos.X - pMat->pos.X; + if(fDiff > 10.0f) bTeleport = 1; + + if(pMat->pos.Y >= mat.pos.Y) fDiff = pMat->pos.Y - mat.pos.Y; + else fDiff = mat.pos.Y - pMat->pos.Y; + if(fDiff > 10.0f || bTeleport) + field_1E1->TeleportTo(pMat->pos.X, pMat->pos.Y, pMat->pos.Z); + + field_1E1->GetMoveSpeedVector(&vecMove); + vecMove.X = pMoveSpeed->X; + vecMove.Y = pMoveSpeed->Y; + vecMove.Z = pMoveSpeed->Z; + field_1E1->SetMoveSpeedVector(vecMove); + field_1E1->FUNC_100B7900(fT); +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10017340(BYTE *pSync, int iTime) +{ + if(iTime && (iTime - field_1BD) < 0) return; + + field_1BD = iTime; + memcpy(field_19, pSync, sizeof(field_19)); + field_1E7 = *(WORD *)pSync; + field_1E1 = (CVehicle *)pNetGame->GetVehiclePool()->GetAt(field_1E7); + field_10B = 0; + field_1B0 = pSync[0x34]; + field_1AC = pSync[0x35]; + field_1B8 = 17; + field_1B9 = GetTickCount(); + field_C = 0; + + if(m_pPlayerPed && !m_pPlayerPed->IsInVehicle()) FUNC_10014800(); + + if(field_10A != 19) { + FUNC_10016270(19, field_10A); + field_10A = 19; + } +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10017440(BYTE *pSync) +{ + memcpy(field_AD, pSync, sizeof(field_AD)); + field_1E7 = *(WORD *)pSync; + field_10B = pSync[2] & 0x3F; + field_1E1 = (CVehicle *)pNetGame->GetVehiclePool()->GetAt(field_1E7); + field_1B0 = pSync[4]; + field_1AC = pSync[5]; + field_1B8 = 18; + field_1B9 = GetTickCount(); + field_C = 0; + + if(m_pPlayerPed && !m_pPlayerPed->IsInVehicle()) FUNC_10014800(); + + if(field_10A != 18) field_10A = 18; +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_100148F0() +{ + if(m_pPlayerPed && m_pPlayerPed->IsInVehicle()) { + MATRIX4X4 mat; + m_pPlayerPed->GetMatrix(&mat); + m_pPlayerPed->RemoveFromVehicleAndPutAt(mat.pos.X, mat.pos.Y, mat.pos.Z); + field_1E1 = NULL; + } +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10014500(int a1) +{ + if(!a1) { + if(field_1F9) { + pGame->DisableMarker(field_1F9); + field_1F9 = a1; + } + } + field_1E9 = a1; +} + +//---------------------------------------------------- + +BOOL CRemotePlayer::FUNC_100145F0() +{ + if(field_10A == 17 && field_C5.wVehicleID && field_C5.wVehicleID != INVALID_VEHICLE_ID && + field_C5.wVehicleID < (MAX_VEHICLES + 1000)) return TRUE; + + return FALSE; +} + +//---------------------------------------------------- + +BOOL CRemotePlayer::FUNC_10014620() +{ + if(field_10A == 17 && field_C5.wVehicleID && field_C5.wVehicleID != INVALID_VEHICLE_ID && + field_C5.wVehicleID >= MAX_VEHICLES && field_C5.wVehicleID < (MAX_VEHICLES + 1000)) return TRUE; + + return FALSE; +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10016270(BYTE byteNewState, BYTE byteOldState) +{ + if(byteNewState != 19 || byteOldState != 17) return; + + CPlayerPed *pPlayerPed = pGame->FindPlayerPed(); + if(!pPlayerPed) return; + if(!pPlayerPed->IsInVehicle()) return; + if(pPlayerPed->IsAPassenger()) return; + + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + if((WORD)pVehiclePool->FUNC_1001EB90((int)pPlayerPed->GetGtaVehicle()) == field_1E7) { + MATRIX4X4 mat; + pPlayerPed->GetMatrix(&mat); + pPlayerPed->RemoveFromVehicleAndPutAt(mat.pos.X, mat.pos.Y, mat.pos.Z + 1.0f); + pGame->DisplayGameText("~r~Car Jacked~w~!", 1000, 5); + } +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_100165B0(BYTE byteNewState) +{ + if(byteNewState != field_10A) { + FUNC_10016270(byteNewState, field_10A); + field_10A = byteNewState; + } +} + +//---------------------------------------------------- + +void CRemotePlayer::FUNC_10016660() +{ + if(field_1F9) { + pGame->DisableMarker(field_1F9); + field_1F9 = 0; + } + field_1E9 = 0; + if(m_pPlayerPed) { + pGame->DeletePlayerPed((int)m_pPlayerPed); + m_pPlayerPed = NULL; + } +} + +//---------------------------------------------------- + void CRemotePlayer::ExitVehicle() { if(m_pPlayerPed && m_pPlayerPed->IsInVehicle()) { diff --git a/saco/net/remoteplayer.h b/saco/net/remoteplayer.h index 16be238..5068910 100644 --- a/saco/net/remoteplayer.h +++ b/saco/net/remoteplayer.h @@ -3,10 +3,41 @@ //---------------------------------------------------- +struct UNOCCUPIED_SYNC_DATA +{ + VEHICLEID VehicleID; + BYTE byteSeatID; + VECTOR vecRoll; + VECTOR vecDirection; + VECTOR vecPos; + VECTOR vecMoveSpeed; + VECTOR vecTurnSpeed; +}; + +struct TRAILER_SYNC_DATA +{ + VEHICLEID TrailerID; + VECTOR vecPos; + float quat[4]; + VECTOR vecMoveSpeed; + VECTOR vecTurnSpeed; +}; + +// the bullet sync payload, 0x28 bytes on the wire +struct BULLET_SYNC_DATA +{ + BYTE byteHitType; + WORD wHitID; + VECTOR vecHitOrigin; + VECTOR vecHitTarget; + VECTOR vecCenterOfHit; + BYTE byteWeaponID; +}; + #pragma pack(1) class CRemotePlayer { -private: +public: //char _gap0[509]; int field_0; int field_4; @@ -21,30 +52,46 @@ private: char field_8E[31]; char field_AD[24]; - char field_C5[68]; + // one 68-byte block: ResetAllSyncAttributes memsets it whole + struct { + char _pad0[18]; + float quat[4]; // 0xD7 + char _padE7[16]; + VECTOR vecOffset; // 0xF7 + WORD wVehicleID; // 0x103 + int iAnimation; // 0x105 + } field_C5; char field_109; - char field_10A; - char field_10B; + BYTE field_10A; + BYTE field_10B; int field_10C; int field_110; - char _gap114[152]; + char _gap114[76]; + float field_160; + float field_164; + float field_168; + float quat16C[4]; + VECTOR field_17C; + VECTOR field_188; + VECTOR field_194; + VECTOR field_1A0; - int field_1AC; - int field_1B0; + float field_1AC; + float field_1B0; int field_1B4; char field_1B8; DWORD field_1B9; - DWORD field_1BD; + int field_1BD; int field_1C1; int field_1C5; - char field_1C9[12]; + VECTOR field_1C9; DWORD field_1D5; DWORD field_1D9; CPlayerPed *m_pPlayerPed; - int field_1E1; + CVehicle *field_1E1; PLAYERID m_PlayerID; - short field_1E7; + WORD field_1E7; int field_1E9; int field_1ED; int field_1F1; @@ -61,8 +108,55 @@ public: DWORD GetPlayerColorAsRGBA(); DWORD GetPlayerColorAsARGB(); + void FUNC_10017C80(BYTE byteValue) { field_4 = (byteValue != 0); }; // .text:10017C80 + + BOOL FUNC_10001080() { return m_pPlayerPed && field_10A; }; // .text:10001080 + + char FUNC_10014380() { return field_C; }; // .text:10014380 + int FUNC_10014390() { return field_10C; }; // .text:10014390 + void EnterVehicle(VEHICLEID VehicleID, BOOL bPassenger); void ExitVehicle(); + + void FUNC_100143A0(); // .text:100143A0 + void FUNC_10014500(int a1); // .text:10014500 + void FUNC_10014540(short a1, short a2, short a3); // .text:10014540 + void FUNC_10014650(); // .text:10014650 + void FUNC_100148F0(); // .text:100148F0 + void FUNC_10014FF0(); // .text:10014FF0 + void FUNC_10017260(BYTE *pSync, int iTime); // .text:10017260 + void FUNC_10014C40(); // .text:10014C40 + void FUNC_10016370(BULLET_SYNC_DATA *pSync); // .text:10016370 + void FUNC_10014950(); // .text:10014950 + void FUNC_10015140(); // .text:10015140 + void FUNC_100158D0(UNOCCUPIED_SYNC_DATA *pSync); // .text:100158D0 + void FUNC_10015C90(TRAILER_SYNC_DATA *pTrailer); // .text:10015C90 + void FUNC_10014E60(); // .text:10014E60 + void FUNC_10015460(); // .text:10015460 + void FUNC_10015760(BYTE *pAim); // .text:10015760 + BOOL FUNC_10017130(int a1, int iSkin, int a3, VECTOR *pPos, float fRotation, + DWORD dwColor, BYTE byteStyle); // .text:10017130 + void FUNC_10015650(MATRIX4X4 *pMat, VECTOR *pMoveSpeed, float fT); // .text:10015650 + void FUNC_10017340(BYTE *pSync, int iTime); // .text:10017340 + void FUNC_10017440(BYTE *pSync); // .text:10017440 + void FUNC_10014800(); // .text:10014800 + void FUNC_10017610(char *szText); // .text:10017610 + void FUNC_100150D0(VECTOR *pPos, VECTOR *pMoveSpeed); // .text:100150D0 + void FUNC_100155E0(int a1, VECTOR *pPos, VECTOR *pMoveSpeed); // .text:100155E0 + void FUNC_10015760(char *pAimSync); // .text:10015760 + void FUNC_10015C90(char *pTrailerSync); // .text:10015C90 + void FUNC_10017440(char *pPassengerSync); // .text:10017440 + BOOL FUNC_100145F0(); // .text:100145F0 + void FUNC_10016270(BYTE byteNewState, BYTE byteOldState); // .text:10016270 + void FUNC_100165B0(BYTE byteNewState); // .text:100165B0 + BOOL FUNC_10014620(); // .text:10014620 + void FUNC_10016660(); // .text:10016660 + float FUNC_100160A0(CRemotePlayer *pOther); // .text:100160A0 + float FUNC_10016120(); // .text:10016120 + void sub_10017610(PCHAR szText); // .text:10017610 + void FUNC_10017530(); // .text:10017530 + void FUNC_10017570(); // .text:10017570 + int FUNC_10016330(); // .text:10016330 }; //---------------------------------------------------- diff --git a/saco/net/scriptrpc.cpp b/saco/net/scriptrpc.cpp index c000286..c595a90 100644 --- a/saco/net/scriptrpc.cpp +++ b/saco/net/scriptrpc.cpp @@ -1,101 +1,1777 @@ #include "../main.h" +#include "../game/util.h" using namespace RakNet; extern CNetGame* pNetGame; extern CGame * pGame; +extern CAudioStream * pAudioStream; +extern CChatWindow * pChatWindow; +extern CDownloadList * dword_1026EB98; + +// the three UI dispatch objects the script RPCs poke; net/netrpc.cpp keeps its own +// copy of this shape as CEditDispatch +#pragma pack(1) +class CScrEditDispatch +{ +public: + DWORD field_0; + char _gap4[0x7C]; + DWORD field_80; + + void FUNC_1006DB80(BOOL bEnable); // .text:1006DB80 + void FUNC_10071440(int a1); // .text:10071440 + void FUNC_10071520(); // .text:10071520 + void FUNC_10072BC0(BOOL bEnable); // .text:10072BC0 +}; + +extern CScrEditDispatch * dword_1026EB60; +extern CScrEditDispatch * dword_1026EB64; +extern CScrEditDispatch * dword_1026EB68; + +char pText[257]; //---------------------------------------------------- // TODO: These script RPCs -void ScrUnkA7(RPCParameters *rpcParams) {} -void ScrUnk38(RPCParameters *rpcParams) {} -void ScrUnk90(RPCParameters *rpcParams) {} -void ScrUnk91(RPCParameters *rpcParams) {} -void ScrSetGravity(RPCParameters *rpcParams) {} -void ScrUnk93(RPCParameters *rpcParams) {} -void ScrUnk94(RPCParameters *rpcParams) {} +void ScrUnkA7(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + bool bValue; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bValue = false; + bsData.Read(bValue); + + if(pNetGame) pNetGame->SetField232(bValue); +} +void ScrUnk38(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + BYTE byteIndex; + VECTOR vecPos; + BYTE byteIcon; + DWORD dwColor; + BYTE byteStyle; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(byteIndex); + bsData.Read(vecPos.X); + bsData.Read(vecPos.Y); + bsData.Read(vecPos.Z); + bsData.Read(byteIcon); + bsData.Read(dwColor); + bsData.Read(byteStyle); + + pNetGame->SetMapIcon(byteIndex, vecPos.X, vecPos.Y, vecPos.Z, byteIcon, dwColor, byteStyle); +} +void ScrUnk90(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + BYTE byteIndex; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(byteIndex); + + pNetGame->DisableMapIcon(byteIndex); +} +void ScrUnk91(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + CLocalPlayer *pLocalPlayer = pNetGame->GetPlayerPool()->GetLocalPlayer(); + BYTE byteWeapon; + WORD wAmmo; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(byteWeapon); + bsData.Read(wAmmo); + + pLocalPlayer->GetPlayerPed()->SetWeaponAmmo(byteWeapon, wAmmo); +} +void ScrSetGravity(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + float fGravity; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(fGravity); + + pGame->SetGravity(fGravity); +} +void ScrUnk93(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + VEHICLEID vehicleId; + int iUnk; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(vehicleId); + bsData.Read(iUnk); + + if(pNetGame->GetVehiclePool()) { + if(pNetGame->GetVehiclePool()->GetSlotState(vehicleId)) { + CVehicle *pVehicle = (CVehicle *)pNetGame->GetVehiclePool()->GetAt(vehicleId); + if(pVehicle) pVehicle->FUNC_100B72C0(iUnk); + } + } +} +void ScrUnk94(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + VEHICLEID vehicleId; + VEHICLEID trailerId; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + if(!pNetGame->GetVehiclePool()) return; + + bsData.Read(vehicleId); + bsData.Read(trailerId); + + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + + int iVehicle = pVehiclePool->GetAt(vehicleId); + CVehicle *pTrailer = (CVehicle *)pVehiclePool->GetAt(trailerId); + + if(iVehicle) { + if(pTrailer) { + pTrailer->FUNC_100B7C80(iVehicle); + pTrailer->FUNC_100B7C10(); + } + } +} void ScrUnk95(RPCParameters *rpcParams) {} +// the object at 0x10053C30; only the bitstream string reader this file needs +class CScrStringReader +{ +public: + bool FUNC_100542D0(char *szBuffer, int iMaxLen, RakNet::BitStream *pData, int a4); // .text:100542D0 +}; + +CScrStringReader * FUNC_10053C30(); // .text:10053C30 + +// walks byteCount material entries off the wire and applies them to pObject +void FUNC_1001B070(BYTE byteCount, CObject *pObject, RakNet::BitStream *pData) +{ + BYTE byteLen; + BYTE byteIndex; + BYTE byteType; + WORD wModel; + DWORD dwColour; + int iCount; + DWORD dwFontColour; + BYTE byteSize; + BYTE byteAlign; + BYTE byteBold; + BYTE byteUnk; + DWORD dwBackColour; + int iTextLen; + char szTxd[257]; + char szFont[257]; + char szTexture[257]; + char szText[2049]; + + int iEntries = byteCount; + + dwColour = 0; + byteLen = 0; + + if(iEntries) { + iCount = iEntries; + + do { + memset(szTxd, 0, sizeof(szTxd)); + memset(szTexture, 0, sizeof(szTexture)); + memset(szFont, 0, sizeof(szFont)); + memset(szText, 0, sizeof(szText)); + + pData->Read(byteType); + + if(byteType == 1) { + pData->Read(byteIndex); + pData->Read(wModel); + pData->Read(byteLen); + pData->Read(szTxd, byteLen); + pData->Read(byteLen); + pData->Read(szTexture, byteLen); + pData->Read(dwColour); + + if(strlen(szTxd) < 32 && strlen(szTexture) < 32) { + int iModel = wModel; + if(iModel == 0xFFFF || iModel > 20000) iModel = -1; + if(pObject) pObject->FUNC_100A8530(iModel, byteIndex, szTxd, szTexture, dwColour); + } + } else if(byteType == 2) { + pData->Read(byteIndex); + pData->Read(byteSize); + pData->Read(byteLen); + pData->Read(szFont, byteLen); + pData->Read(byteBold); + pData->Read(byteAlign); + pData->Read(dwFontColour); + pData->Read(dwBackColour); + pData->Read(byteUnk); + + FUNC_10053C30()->FUNC_100542D0(szText, 2048, pData, 0); + + if(strlen(szFont) < 32) { + iTextLen = strlen(szText); + if(iTextLen) { + if(pObject) pObject->FUNC_100A86B0(byteIndex, szText, byteSize, szFont, + byteBold, byteAlign, dwFontColour, dwBackColour, byteUnk); + } + } + } + } while(--iCount); + } +} void ScrUnk2C(RPCParameters *rpcParams) {} -void ScrUnk2D(RPCParameters *rpcParams) {} -void ScrUnk2E(RPCParameters *rpcParams) {} -void ScrUnk2F(RPCParameters *rpcParams) {} -void ScrUnk4F(RPCParameters *rpcParams) {} -void ScrUnk50(RPCParameters *rpcParams) {} +void ScrUnk2D(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + WORD objectId; + float fX, fY, fZ; + int iUnk; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(objectId); + bsData.Read(fX); + bsData.Read(fY); + bsData.Read(fZ); + bsData.Read(iUnk); + + CObjectPool *pObjectPool = pNetGame->GetObjectPool(); + if(objectId <= MAX_OBJECTS) { + if(pObjectPool->field_4[objectId]) { + CEntity *pObject = (CEntity *)pObjectPool->field_FA4[objectId]; + if(pObject) pObject->TeleportTo(fX, fY, fZ); + } + } +} +void ScrUnk2E(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + WORD objectId; + VECTOR vecPos; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(objectId); + bsData.Read(vecPos.X); + bsData.Read(vecPos.Y); + bsData.Read(vecPos.Z); + + CObjectPool *pObjectPool = pNetGame->GetObjectPool(); + if(pObjectPool && objectId <= MAX_OBJECTS && pObjectPool->field_4[objectId] + && pObjectPool->field_FA4[objectId]) { + ((CObject *)pObjectPool->field_FA4[objectId])->FUNC_100A80A0(&vecPos); + } +} +void ScrUnk2F(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + WORD objectId; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(objectId); + + CObjectPool *pObjectPool = pNetGame->GetObjectPool(); + CLocalPlayer *pLocalPlayer = pNetGame->GetPlayerPool()->GetLocalPlayer(); + + if(objectId <= MAX_OBJECTS && pObjectPool->field_4[objectId]) { + int iObject = pObjectPool->field_FA4[objectId]; + if(iObject) { + if(pLocalPlayer && pLocalPlayer->field_2DA == (DWORD)iObject) { + pLocalPlayer->field_2DA = 0; + pLocalPlayer->field_2E2 = 0; + } + pObjectPool->FUNC_10012850(objectId); + } + } +} +void ScrUnk4F(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + float fX, fY, fZ, fUnk; + int iUnk; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(fX); + bsData.Read(fY); + bsData.Read(fZ); + bsData.Read(iUnk); + bsData.Read(fUnk); + + ScriptCommand(&script_command_0948, fX, fY, fZ, iUnk, fUnk); +} +void ScrUnk50(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + PLAYERID playerId; + BYTE byteUnk; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(playerId); + bsData.Read(byteUnk); + + if(pNetGame->GetPlayerPool()->GetSlotState(playerId)) { + pNetGame->GetPlayerPool()->GetAt(playerId)->FUNC_10017C80(byteUnk); + } +} void ScrUnk63(RPCParameters *rpcParams) {} -void ScrUnk7A(RPCParameters *rpcParams) {} -void ScrUnk7B(RPCParameters *rpcParams) {} -void ScrUnk7C(RPCParameters *rpcParams) {} +void ScrUnk7A(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + WORD objectId; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(objectId); + + CObjectPool *pObjectPool = pNetGame->GetObjectPool(); + if(objectId <= MAX_OBJECTS) { + if(pObjectPool->field_4[objectId]) { + CObject *pObject = (CObject *)pObjectPool->field_FA4[objectId]; + if(pObject) pObject->FUNC_100A8030(); + } + } +} +void ScrUnk7B(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + VEHICLEID vehicleId; + BYTE byteLen; + char szText[33]; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + byteLen = 0; + + memset(szText, 0, sizeof(szText)); + + bsData.Read(vehicleId); + bsData.Read(byteLen); + + if(byteLen <= 32) { + bsData.Read(szText, byteLen); + + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + if(pVehiclePool) { + if(pVehiclePool->GetSlotState(vehicleId)) { + ((CVehicle *)pVehiclePool->GetAt(vehicleId))->FUNC_100B8150(szText); + } + } + } +} +void ScrUnk7C(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + int iUnk; + + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(iUnk); + + pPlayerPool->GetLocalPlayer()->FUNC_100041C0(iUnk); +} void ScrUnk7D(RPCParameters *rpcParams) { // nothing } -void ScrUnk7E(RPCParameters *rpcParams) {} -void ScrUnk7F(RPCParameters *rpcParams) {} -void ScrUnk39(RPCParameters *rpcParams) {} -void ScrUnk4B(RPCParameters *rpcParams) {} -void ScrUnk85(RPCParameters *rpcParams) {} -void ScrUnk86(RPCParameters *rpcParams) {} -void ScrUnk87(RPCParameters *rpcParams) {} -void ScrUnk69(RPCParameters *rpcParams) {} -void ScrAddGangZone(RPCParameters *rpcParams) {} -void ScrRemoveGangZone(RPCParameters *rpcParams) {} -void ScrFlashGangZone(RPCParameters *rpcParams) {} -void ScrStopFlashGangZone(RPCParameters *rpcParams) {} -void ScrUnk56(RPCParameters *rpcParams) {} -void ScrUnk57(RPCParameters *rpcParams) {} -void ScrUnk58(RPCParameters *rpcParams) {} +void ScrUnk7E(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + PLAYERID playerId; + BYTE byteMode; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(playerId); + bsData.Read(byteMode); + + switch(byteMode) { + case 2: byteMode = 15; break; + case 3: byteMode = 14; break; + default: byteMode = 4; break; + } + + CLocalPlayer *pLocalPlayer = pNetGame->GetPlayerPool()->GetLocalPlayer(); + pLocalPlayer->field_30E = byteMode; + pLocalPlayer->FUNC_10004280(playerId); +} +void ScrUnk7F(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + PLAYERID playerId; + BYTE byteMode; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(playerId); + bsData.Read(byteMode); + + switch(byteMode) { + case 2: byteMode = 15; break; + case 3: byteMode = 14; break; + default: byteMode = 3; break; + } + + CLocalPlayer *pLocalPlayer = pNetGame->GetPlayerPool()->GetLocalPlayer(); + pLocalPlayer->field_30E = byteMode; + pLocalPlayer->FUNC_10004230(playerId); +} +void ScrUnk39(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + VEHICLEID vehicleId; + short sUnk; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + if(!pVehiclePool) return; + + bsData.Read(vehicleId); + bsData.Read(sUnk); + + if(pVehiclePool->GetSlotState(vehicleId)) + ((CVehicle *)pVehiclePool->GetAt(vehicleId))->FUNC_100B80B0(sUnk); +} +void ScrUnk4B(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + WORD objectId; + PLAYERID playerId; + float fOffsetX, fOffsetY, fOffsetZ, fRotX, fRotY, fRotZ; + CPlayerPed *pPlayerPed; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(objectId); + bsData.Read(playerId); + bsData.Read(fOffsetX); + bsData.Read(fOffsetY); + bsData.Read(fOffsetZ); + bsData.Read(fRotX); + bsData.Read(fRotY); + bsData.Read(fRotZ); + + CObjectPool *pObjectPool = pNetGame->FUNC_10002E10(); + if(objectId > MAX_OBJECTS) return; + if(!pObjectPool->field_4[objectId]) return; + CObject *pObject = (CObject *)pObjectPool->field_FA4[objectId]; + if(!pObject) return; + + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + + if(playerId == pPlayerPool->GetLocalPlayerID()) { + pPlayerPed = pPlayerPool->GetLocalPlayer()->GetPlayerPed(); + } else { + if(playerId > MAX_PLAYERS) return; + if(!pPlayerPool->m_pPlayers[playerId]) return; + if(!pPlayerPool->m_pPlayers[playerId]->m_pRemotePlayer) return; + pPlayerPed = pPlayerPool->m_pPlayers[playerId]->m_pRemotePlayer->m_pPlayerPed; + } + + ScriptCommand(&attach_object_to_player, pObject->field_44, pPlayerPed->m_dwGTAId, + fOffsetX, fOffsetY, fOffsetZ, fRotX, fRotY, fRotZ); +} +void ScrUnk85(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + BYTE byteLevel; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + if(!pGame) return; + + bsData.Read(byteLevel); + pGame->SetWantedLevel(byteLevel); +} +void ScrUnk86(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + WORD wTextDrawId; + WORD wTextLen; + char szTransmit[64]; + char szText[800]; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + wTextLen = 0; + + CTextDrawPool *pTextDrawPool = pNetGame->GetTextDrawPool(); + if(pTextDrawPool) { + bsData.Read(wTextDrawId); + bsData.Read(szTransmit, 63); + bsData.Read(wTextLen); + if(wTextLen >= 800) { + if(pChatWindow) pChatWindow->AddDebugMessage("Warning: ignoring large TextDraw size=%u", wTextLen); + } else { + bsData.Read(szText, wTextLen); + szText[wTextLen] = 0; + pTextDrawPool->FUNC_1001E910(wTextDrawId, szTransmit, szText); + } + } +} +void ScrUnk87(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + WORD wTextDrawId; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + CTextDrawPool *pTextDrawPool = pNetGame->GetTextDrawPool(); + if(pTextDrawPool) { + bsData.Read(wTextDrawId); + pTextDrawPool->FUNC_1001E7F0(wTextDrawId); + } +} +void ScrUnk69(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + WORD wTextDrawId; + WORD wTextLen; + char szText[800]; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + wTextLen = 0; + + CTextDrawPool *pTextDrawPool = pNetGame->GetTextDrawPool(); + if(pTextDrawPool) { + bsData.Read(wTextDrawId); + bsData.Read(wTextLen); + if(wTextLen <= 800) { + bsData.Read(szText, wTextLen); + szText[wTextLen] = 0; + CTextDraw *pTextDraw = (CTextDraw *)pTextDrawPool->GetAt(wTextDrawId); + if(pTextDraw) pTextDraw->FUNC_100B2F60(szText); + } + } +} +void ScrAddGangZone(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + WORD wZone; + float fMinX, fMinY, fMaxX, fMaxY; + DWORD dwColor; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + CGangZonePool *pGangZonePool = pNetGame->GetGangZonePool(); + if(pGangZonePool) { + bsData.Read(wZone); + bsData.Read(fMinX); + bsData.Read(fMinY); + bsData.Read(fMaxX); + bsData.Read(fMaxY); + bsData.Read(dwColor); + pGangZonePool->New(wZone, fMinX, fMinY, fMaxX, fMaxY, dwColor); + } +} +void ScrRemoveGangZone(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + WORD wZone; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + CGangZonePool *pGangZonePool = pNetGame->GetGangZonePool(); + if(pGangZonePool) { + bsData.Read(wZone); + pGangZonePool->Delete(wZone); + } +} +void ScrFlashGangZone(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + WORD wZone; + DWORD dwColor; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + CGangZonePool *pGangZonePool = pNetGame->GetGangZonePool(); + if(pGangZonePool) { + bsData.Read(wZone); + bsData.Read(dwColor); + pGangZonePool->Flash(wZone, dwColor); + } +} +void ScrStopFlashGangZone(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + WORD wZone; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + CGangZonePool *pGangZonePool = pNetGame->GetGangZonePool(); + if(pGangZonePool) { + bsData.Read(wZone); + pGangZonePool->StopFlash(wZone); + } +} +void ScrUnk56(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + PLAYERID playerId; + BYTE byteAnimFileLen; + BYTE byteAnimNameLen; + char szAnimFile[256]; + char szAnimName[256]; + float fT; + bool bUnk1, bUnk2, bUnk3, bUnk4; + int iUnk; + CPlayerPed *pPlayerPed; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + memset(szAnimFile, 0, sizeof(szAnimFile)); + memset(szAnimName, 0, sizeof(szAnimName)); + + bsData.Read(playerId); + bsData.Read(byteAnimFileLen); + bsData.Read(szAnimFile, byteAnimFileLen); + bsData.Read(byteAnimNameLen); + bsData.Read(szAnimName, byteAnimNameLen); + bsData.Read(fT); + bsData.Read(bUnk1); + bsData.Read(bUnk2); + bsData.Read(bUnk4); + bsData.Read(bUnk3); + bsData.Read(iUnk); + + szAnimFile[byteAnimFileLen] = 0; + szAnimName[byteAnimNameLen] = 0; + + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + if(!pPlayerPool) return; + + if(playerId == pPlayerPool->GetLocalPlayerID()) { + pPlayerPed = pPlayerPool->GetLocalPlayer()->GetPlayerPed(); + } else { + if(playerId >= MAX_PLAYERS) return; + if(!pPlayerPool->field_2A[playerId]) return; + CNetPlayer *pNetPlayer = pPlayerPool->m_pPlayers[playerId]; + if(!pNetPlayer) return; + CRemotePlayer *pRemotePlayer = pNetPlayer->m_pRemotePlayer; + if(!pRemotePlayer) return; + pPlayerPed = pRemotePlayer->m_pPlayerPed; + pRemotePlayer->field_1C1 = 1; + } + + if(!pPlayerPed) return; + + pPlayerPed->ApplyAnimation(szAnimName, szAnimFile, fT, bUnk1, bUnk2, bUnk4, bUnk3, iUnk); +} +void ScrUnk57(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + PLAYERID playerId; + MATRIX4X4 matPlayer; + CPlayerPed *pPlayerPed; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(playerId); + + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + if(!pPlayerPool) goto done; + + if(playerId == pPlayerPool->GetLocalPlayerID()) { + pPlayerPed = pPlayerPool->GetLocalPlayer()->GetPlayerPed(); + } else { + if(playerId >= MAX_PLAYERS) goto done; + if(!pPlayerPool->field_2A[playerId]) goto done; + CNetPlayer *pNetPlayer = pPlayerPool->m_pPlayers[playerId]; + if(!pNetPlayer) goto done; + CRemotePlayer *pRemotePlayer = pNetPlayer->m_pRemotePlayer; + if(!pRemotePlayer) goto done; + pPlayerPed = pRemotePlayer->m_pPlayerPed; + pRemotePlayer->field_1C1 = 0; + } + + if(!pPlayerPed) goto done; + + pPlayerPed->GetMatrix(&matPlayer); + pPlayerPed->TeleportTo(matPlayer.pos.X, matPlayer.pos.Y, matPlayer.pos.Z); + +done: + ; +} +void ScrUnk58(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + BYTE byteUnk; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(byteUnk); + + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + if(pPlayerPool) pPlayerPool->GetLocalPlayer()->sub_100030F0(byteUnk); +} void ScrUnk59(RPCParameters *rpcParams) {} -void ScrUnk5A(RPCParameters *rpcParams) {} -void ScrUnk5B(RPCParameters *rpcParams) {} -void ScrUnk6F(RPCParameters *rpcParams) {} -void ScrUnk62(RPCParameters *rpcParams) {} -void ScrUnk70(RPCParameters *rpcParams) {} -void ScrSetSpawnInfo(RPCParameters *rpcParams) {} -void ScrUnk45(RPCParameters *rpcParams) {} -void ScrUnk99(RPCParameters *rpcParams) {} -void ScrSetPlayerPos(RPCParameters *rpcParams) {} -void ScrUnk0D(RPCParameters *rpcParams) {} -void ScrPutPlayerInVehicle(RPCParameters *rpcParams) {} -void ScrRemovePlayerFromVehicle(RPCParameters *rpcParams) {} -void ScrDisplayGameText(RPCParameters *rpcParams) {} -void ScrSetInterior(RPCParameters *rpcParams) {} -void ScrUnk9F(RPCParameters *rpcParams) {} -void ScrUnkA0(RPCParameters *rpcParams) {} -void ScrUnkA1(RPCParameters *rpcParams) {} -void ScrUnk0F(RPCParameters *rpcParams) {} -void ScrUnk10(RPCParameters *rpcParams) {} -void ScrUnk11(RPCParameters *rpcParams) {} -void ScrUnk12(RPCParameters *rpcParams) {} -void ScrSetPlayerFacingAngle(RPCParameters *rpcParams) {} -void ScrUnk16(RPCParameters *rpcParams) {} -void ScrUnk40(RPCParameters *rpcParams) {} -void ScrUnk41(RPCParameters *rpcParams) {} -void ScrUnk42(RPCParameters *rpcParams) {} +void ScrUnk5A(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + VECTOR vecMoveSpeed; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(vecMoveSpeed.X); + bsData.Read(vecMoveSpeed.Y); + bsData.Read(vecMoveSpeed.Z); + + CPlayerPed *pPlayerPed = pGame->FindPlayerPed(); + if(pPlayerPed) { + if(pPlayerPed->FUNC_100AD860()) { + pPlayerPed->FUNC_100ABDF0(pPlayerPed->FUNC_100ABDD0() ^ 3); + } + + pPlayerPed->SetMoveSpeedVector(vecMoveSpeed); + } +} +void ScrUnk5B(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + BYTE byteType; + VECTOR vecVelocity; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + byteType = 0; + bsData.Read(byteType); + bsData.Read(vecVelocity.X); + bsData.Read(vecVelocity.Y); + bsData.Read(vecVelocity.Z); + + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + CPlayerPed *pPlayerPed = pGame->FindPlayerPed(); + + if(pPlayerPed && pPlayerPed->IsInVehicle()) { + VEHICLEID vehicleId = pVehiclePool->FUNC_1001EB90((int)pPlayerPed->GetGtaVehicle()); + if(vehicleId != INVALID_VEHICLE_ID) { + CEntity *pVehicle = (CEntity *)pVehiclePool->GetAt(vehicleId); + if(pVehicle) { + if(byteType == 0) pVehicle->SetMoveSpeedVector(vecVelocity); + else if(byteType == 1) pVehicle->SetTurnSpeedVector(vecVelocity); + } + } + } +} +void ScrUnk6F(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + BYTE byteEnable; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(byteEnable); + + pChatWindow->AddDebugMessage("Widescreen = %i", byteEnable); + ScriptCommand(&set_widescreen, byteEnable); +} +void ScrUnk62(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + VEHICLEID vehicleId; + BYTE byteTyreDamage; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(vehicleId); + bsData.Read(byteTyreDamage); + + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + CVehicle *pVehicle = (CVehicle *)pVehiclePool->GetAt(vehicleId); + + if(pVehiclePool && pVehicle) pVehicle->sub_100B7940(byteTyreDamage); +} +void ScrUnk70(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + WORD wUnk; + int iUnk1, iUnk2, iUnk3, iUnk4; + VECTOR vecPos; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + iUnk1 = 0; + iUnk2 = 0; + iUnk3 = 0; + iUnk4 = 0; + + bsData.Read(wUnk); + bsData.Read(iUnk1); + bsData.Read(iUnk2); + bsData.Read(iUnk3); + bsData.Read(iUnk4); + bsData.Read(vecPos.X); + bsData.Read(vecPos.Y); + bsData.Read(vecPos.Z); + + pGame->PlayCrimeReport(iUnk4, &vecPos, iUnk1, iUnk2, iUnk3); +} +void ScrSetSpawnInfo(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + SPAWN_INFO SpawnInfo; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + bsData.Read((PCHAR)&SpawnInfo, 46); + + pPlayerPool->GetLocalPlayer()->sub_10003BE0(&SpawnInfo); +} +void ScrUnk45(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + PLAYERID playerId; + BYTE byteTeam; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + + bsData.Read(playerId); + bsData.Read(byteTeam); + + if(playerId == pPlayerPool->GetLocalPlayerID()) { + pPlayerPool->GetLocalPlayer()->SetTeam(byteTeam); + } else { + CRemotePlayer *pRemotePlayer = pPlayerPool->GetAt(playerId); + if(pRemotePlayer) pRemotePlayer->field_109 = byteTeam; + } +} +void ScrUnk99(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + int iPlayerId; + int iSkin = 0; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + if(!pPlayerPool) return; + + bsData.Read(iPlayerId); + bsData.Read(iSkin); + + if(!IsValidPedModel(iSkin)) { + if(pChatWindow) pChatWindow->AddDebugMessage("Warning: SetPlayerSkin %d isn't a valid ped model.", iSkin); + return; + } + + if(iPlayerId == pPlayerPool->GetLocalPlayerID()) { + pPlayerPool->GetLocalPlayer()->GetPlayerPed()->SetSkin(iSkin); + } else if(pPlayerPool->GetSlotState(iPlayerId) && pPlayerPool->GetAt(iPlayerId)->FUNC_10001080() + && pPlayerPool->GetAt(iPlayerId)->m_pPlayerPed) { + pPlayerPool->GetAt(iPlayerId)->m_pPlayerPed->SetSkin(iSkin); + } +} +void ScrSetPlayerPos(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + VECTOR vecPos; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + if(!pPlayerPool) return; + + CLocalPlayer *pLocalPlayer = pPlayerPool->GetLocalPlayer(); + if(!pLocalPlayer) return; + + bsData.Read(vecPos.X); + bsData.Read(vecPos.Y); + bsData.Read(vecPos.Z); + + pLocalPlayer->field_2E2 = 0; + pLocalPlayer->sub_10003710(0, 0); + pLocalPlayer->GetPlayerPed()->TeleportTo(vecPos.X, vecPos.Y, vecPos.Z); +} +void ScrUnk0D(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + VECTOR vecPos; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + CLocalPlayer *pLocalPlayer = pNetGame->GetPlayerPool()->GetLocalPlayer(); + + bsData.Read(vecPos.X); + bsData.Read(vecPos.Y); + bsData.Read(vecPos.Z); + + vecPos.Z = pGame->FindGroundZForCoord(vecPos.X, vecPos.Y, vecPos.Z) + 1.5f; + + pLocalPlayer->field_2E2 = 0; + pLocalPlayer->sub_10003710(0, 0); + pLocalPlayer->GetPlayerPed()->TeleportTo(vecPos.X, vecPos.Y, vecPos.Z); +} +void ScrPutPlayerInVehicle(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + VEHICLEID vehicleId; + BYTE byteSeatId; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(vehicleId); + bsData.Read(byteSeatId); + + int iGtaVehicleId = pNetGame->GetVehiclePool()->FindGtaIDFromID(vehicleId); + int iVehicle = pNetGame->GetVehiclePool()->GetAt(vehicleId); + + if(iGtaVehicleId && iVehicle) { + pGame->FindPlayerPed()->FUNC_100AC290(iGtaVehicleId, byteSeatId); + } +} +void ScrRemovePlayerFromVehicle(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + pNetGame->GetPlayerPool()->GetLocalPlayer()->GetPlayerPed()->ExitCurrentVehicle(); +} +void ScrDisplayGameText(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + int iStyle, iTime, iLength; + char szText[256]; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + memset(szText, 0, sizeof(szText)); + + bsData.Read(iStyle); + bsData.Read(iTime); + bsData.Read(iLength); + + if(iLength >= 1 && iLength <= 200) { + bsData.Read(szText, iLength); + szText[iLength] = 0; + pGame->DisplayGameText(szText, iTime, iStyle); + } +} +void ScrSetInterior(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + BYTE byteInterior; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(byteInterior); + + pGame->FindPlayerPed()->SetInterior(byteInterior, TRUE); +} +void ScrUnk9F(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + VEHICLEID vehicleId; + float fX, fY, fZ; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(vehicleId); + bsData.Read(fX); + bsData.Read(fY); + bsData.Read(fZ); + + if(!pNetGame) return; + if(!pNetGame->GetVehiclePool()) return; + + if(pNetGame->GetVehiclePool()->GetSlotState(vehicleId)) { + ((CEntity *)pNetGame->GetVehiclePool()->GetAt(vehicleId))->TeleportTo(fX, fY, fZ); + } +} +void ScrUnkA0(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + VEHICLEID vehicleId; + float fUnk; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(vehicleId); + bsData.Read(fUnk); + + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + if(pVehiclePool && pVehiclePool->GetSlotState(vehicleId)) + ((CVehicle *)pVehiclePool->GetAt(vehicleId))->FUNC_100B8170(fUnk); +} +void ScrUnkA1(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + VEHICLEID vehicleId; + BYTE byteA, byteB; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(vehicleId); + bsData.Read(byteA); + bsData.Read(byteB); + + CVehiclePool *pVehiclePool = pNetGame->GetVehiclePool(); + if(pVehiclePool) pVehiclePool->FUNC_1001EB30(vehicleId, byteA, byteB); +} +void ScrUnk0F(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + BYTE byteControllable; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(byteControllable); + + pGame->FindPlayerPed()->TogglePlayerControllable(byteControllable); +} +void ScrUnk10(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + int iSound; + float fX, fY, fZ; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(iSound); + bsData.Read(fX); + bsData.Read(fY); + bsData.Read(fZ); + + CAudio *pAudio = pGame->m_pGameAudio; + pAudio->PlaySound(iSound, fX, fY, fZ); +} +void ScrUnk11(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(pNetGame->GetSettings()->fWorldBoundryPX); + bsData.Read(pNetGame->GetSettings()->fWorldBoundryZX); + bsData.Read(pNetGame->GetSettings()->fWorldBoundryPY); + bsData.Read(pNetGame->GetSettings()->fWorldBoundryNY); +} +void ScrUnk12(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + int iMoney; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(iMoney); + + pGame->AddToLocalMoney(iMoney); +} +void ScrSetPlayerFacingAngle(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + float fAngle; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(fAngle); + + pGame->FindPlayerPed()->ForceTargetRotation(fAngle); +} +void ScrUnk16(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + int iWeapon, iAmmo; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(iWeapon); + bsData.Read(iAmmo); + + pNetGame->GetPlayerPool()->GetLocalPlayer()->GetPlayerPed()->GiveWeapon(iWeapon, iAmmo); +} +void ScrUnk40(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + PLAYERID playerId; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(playerId); +} +void ScrUnk41(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + VEHICLEID vehicleId; + BYTE byteUnk; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(vehicleId); + bsData.Read(byteUnk); + + pNetGame->GetVehiclePool()->FUNC_1001EB00(vehicleId, byteUnk); +} +void ScrUnk42(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + CLocalPlayer *pLocalPlayer = pNetGame->GetPlayerPool()->GetLocalPlayer(); + float fArmour; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(fArmour); + + pLocalPlayer->GetPlayerPed()->SetArmour(fArmour); +} void ScrUnk37(RPCParameters *rpcParams) {} -void ScrUnk21(RPCParameters *rpcParams) {} -void ScrUnk23(RPCParameters *rpcParams) {} -void ScrUnk43(RPCParameters *rpcParams) {} -void ScrUnk71(RPCParameters *rpcParams) {} -void ScrUnk29(RPCParameters *rpcParams) {} -void ScrUnk2A(RPCParameters *rpcParams) {} -void ScrUnk2B(RPCParameters *rpcParams) {} +void ScrUnk21(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + int iLen; + char szName[33]; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + memset(szName, 0, sizeof(szName)); + + bsData.Read(szName, 32); + + CPlayerPed *pPlayerPed = pGame->FindPlayerPed(); + if(pPlayerPed) { + iLen = strlen(szName); + if(!iLen) { + pPlayerPed->SetShopName(NULL); + pPlayerPed->LoadShoppingDataSubsection(""); + } else { + pPlayerPed->SetShopName(szName); + pPlayerPed->LoadShoppingDataSubsection(szName); + } + } +} +void ScrUnk23(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + int iLevel; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + bsData.Read(iLevel); + + CPlayerPed *pPlayerPed = pGame->FindPlayerPed(); + if(pPlayerPed) pPlayerPed->FUNC_100ADFB0(iLevel); +} +void ScrUnk43(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + int iWeapon; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + CPlayerPed *pPlayerPed = pGame->FindPlayerPed(); + if(!pPlayerPed) return; + + bsData.Read(iWeapon); + + if(iWeapon >= 0 && iWeapon <= 46) pPlayerPed->SetArmedWeapon(iWeapon, false); +} +void ScrUnk71(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + bool bAttach; + int iIndex; + PLAYERID playerId; + char szAttach[52]; + CPlayerPed *pPlayerPed; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + memset(szAttach, 0, sizeof(szAttach)); + + bsData.Read(playerId); + bsData.Read(iIndex); + bsData.Read(bAttach); + + if(bAttach) bsData.Read(szAttach, 52); + + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + if(pPlayerPool) { + if(playerId == pPlayerPool->GetLocalPlayerID()) { + pPlayerPed = pPlayerPool->GetLocalPlayer()->GetPlayerPed(); + } else { + if(playerId >= MAX_PLAYERS) return; + if(!pPlayerPool->field_2A[playerId]) return; + CNetPlayer *pNetPlayer = pPlayerPool->m_pPlayers[playerId]; + pPlayerPed = (pNetPlayer ? pNetPlayer->m_pRemotePlayer : NULL)->m_pPlayerPed; + } + + if(pPlayerPed) { + if(!bAttach) pPlayerPed->RemoveAttachedObject(iIndex); + else pPlayerPed->FUNC_100B0B10(iIndex, szAttach); + } + } +} +void ScrUnk29(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + BYTE byteLen; + VECTOR vecPos; + float fRadius; + BYTE byteUsePos; + char szURL[257]; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + memset(szURL, 0, sizeof(szURL)); + + byteLen = 0; + vecPos.X = 0.0f; + vecPos.Y = 0.0f; + vecPos.Z = 0.0f; + fRadius = 0.0f; + byteUsePos = 0; + + bsData.Read(byteLen); + bsData.Read(szURL, byteLen); + bsData.Read(vecPos.X); + bsData.Read(vecPos.Y); + bsData.Read(vecPos.Z); + bsData.Read(fRadius); + bsData.Read(byteUsePos); + + if(pAudioStream) pAudioStream->FUNC_10066960(szURL, vecPos.X, vecPos.Y, vecPos.Z, fRadius, byteUsePos); +} +void ScrUnk2A(RPCParameters *rpcParams) +{ + if(pAudioStream) pAudioStream->Stop(0); +} +void ScrUnk2B(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + int iModel; + float fX, fY, fZ, fRadius; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + iModel = 0; + fX = 0.0f; + fY = 0.0f; + fZ = 0.0f; + fRadius = 0.0f; + + bsData.Read(iModel); + bsData.Read(fX); + bsData.Read(fY); + bsData.Read(fZ); + bsData.Read(fRadius); + + FUNC_1009D3D0(iModel, fX, fY, fZ, fRadius); +} void ScrUnk51(RPCParameters *rpcParams) {} -void ScrUnk52(RPCParameters *rpcParams) {} -void ScrUnk53(RPCParameters *rpcParams) {} +void ScrUnk52(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + bool bPosition; + BYTE byteCut; + int iTime; + VECTOR vecFrom, vecTo; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bPosition = true; + bsData.Read(bPosition); + bsData.Read(vecFrom.X); + bsData.Read(vecFrom.Y); + bsData.Read(vecFrom.Z); + bsData.Read(vecTo.X); + bsData.Read(vecTo.Y); + bsData.Read(vecTo.Z); + bsData.Read(iTime); + bsData.Read(byteCut); + + if(byteCut < 1 || byteCut > 2) byteCut = 2; + + if(iTime > 0) { + pNetGame->GetPlayerPool()->GetLocalPlayer()->field_314 = 1; + if(bPosition) pGame->GetCamera()->InterpolatePosition(&vecFrom, &vecTo, iTime, byteCut); + else pGame->GetCamera()->InterpolateLookAt(&vecFrom, &vecTo, iTime, byteCut); + } +} +void ScrUnk53(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + bool bUnk; + int iUnk; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bUnk = false; + iUnk = 0; + + bsData.Read(bUnk); + bsData.Read(iUnk); + + if(dword_1026EB64 && dword_1026EB64->field_0) dword_1026EB64->FUNC_1006DB80(FALSE); + if(dword_1026EB60 && dword_1026EB60->field_80) dword_1026EB60->FUNC_10072BC0(FALSE); + + if(bUnk) { + if(dword_1026EB68) dword_1026EB68->FUNC_10071440(iUnk); + } else if(dword_1026EB68) { + dword_1026EB68->FUNC_10071520(); + } +} void ScrUnk54(RPCParameters *rpcParams) {} -void ScrUnkA9(RPCParameters *rpcParams) {} -void ScrUnkAD(RPCParameters *rpcParams) {} -void ScrUnkAE(RPCParameters *rpcParams) {} -void ScrUnkAF(RPCParameters *rpcParams) {} -void ScrUnkB0(RPCParameters *rpcParams) {} -void ScrUnkB2(RPCParameters *rpcParams) {} -void ScrUnk30(RPCParameters *rpcParams) {} -void ScrInitMenu(RPCParameters *rpcParams) {} -void ScrShowMenu(RPCParameters *rpcParams) {} -void ScrHideMenu(RPCParameters *rpcParams) {} +void ScrUnkA9(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + WORD objectId; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(objectId); + + CObjectPool *pObjectPool = pNetGame->GetObjectPool(); + if(objectId <= MAX_OBJECTS) { + if(pObjectPool->field_4[objectId]) { + CObject *pObject = (CObject *)pObjectPool->field_FA4[objectId]; + if(pObject) pObject->field_53 = 1; + } + } +} +void ScrUnkAD(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + ACTORID actorId; + BYTE byteAnimFileLen; + BYTE byteAnimNameLen; + char szAnimFile[256]; + char szAnimName[256]; + float fT; + bool bUnk1, bUnk2, bUnk3, bUnk4; + int iUnk; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + CActorPool *pActorPool = pNetGame->GetActorPool(); + if(pActorPool) { + memset(szAnimFile, 0, sizeof(szAnimFile)); + memset(szAnimName, 0, sizeof(szAnimName)); + + bsData.Read(actorId); + bsData.Read(byteAnimFileLen); + bsData.Read(szAnimFile, byteAnimFileLen); + bsData.Read(byteAnimNameLen); + bsData.Read(szAnimName, byteAnimNameLen); + bsData.Read(fT); + bsData.Read(bUnk1); + bsData.Read(bUnk2); + bsData.Read(bUnk4); + bsData.Read(bUnk3); + bsData.Read(iUnk); + + szAnimFile[byteAnimFileLen] = 0; + szAnimName[byteAnimNameLen] = 0; + + CActorPed *pActor = (CActorPed *)pActorPool->GetAt(actorId); + if(pActor) pActor->ApplyAnimation(szAnimName, szAnimFile, fT, bUnk1, bUnk2, bUnk4, bUnk3, iUnk); + } +} +void ScrUnkAE(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + ACTORID actorId; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(actorId); + + CActorPool *pActorPool = pNetGame->GetActorPool(); + if(pActorPool) { + CActorPed *pActor = (CActorPed *)pActorPool->GetAt(actorId); + if(pActor) pActor->FUNC_1009C550(); + } +} +void ScrUnkAF(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + ACTORID actorId; + float fUnk; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(actorId); + bsData.Read(fUnk); + + CActorPool *pActorPool = pNetGame->GetActorPool(); + if(pActorPool) { + CActorPed *pActor = (CActorPed *)pActorPool->GetAt(actorId); + if(pActor) pActor->FUNC_1009C570(fUnk); + } +} +void ScrUnkB0(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + ACTORID actorId; + VECTOR vecPos; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(actorId); + bsData.Read(vecPos.X); + bsData.Read(vecPos.Y); + bsData.Read(vecPos.Z); + + CActorPool *pActorPool = pNetGame->GetActorPool(); + if(pActorPool) { + if(actorId < MAX_ACTORS) { + if(pActorPool->field_FA4[actorId]) { + CEntity *pActor = (CEntity *)pActorPool->field_4[actorId]; + if(pActor) pActor->TeleportTo(vecPos.X, vecPos.Y, vecPos.Z); + } + } + } +} +void ScrUnkB2(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + ACTORID actorId; + int iUnk; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + bsData.Read(actorId); + bsData.Read(iUnk); + + CActorPool *pActorPool = pNetGame->GetActorPool(); + if(pActorPool) { + CActorPed *pActor = (CActorPed *)pActorPool->GetAt(actorId); + if(pActor) pActor->FUNC_1009C5D0(iUnk); + } +} +void ScrUnk30(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + int iUnk; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + iUnk = 0; + bsData.Read(iUnk); + + if(dword_1026EB98) dword_1026EB98->FUNC_10017D10(iUnk); +} +void ScrInitMenu(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + BYTE byteMenuID; + int iColumns; + float fX, fY, fCol1Width, fCol2Width; + MENU_INT MenuInteraction; + BYTE byteRows, byteRow; + int i; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + if(!pNetGame) return; + + CMenuPool *pMenuPool = pNetGame->GetMenuPool(); + + fCol2Width = 0.0f; + + bsData.Read(byteMenuID); + bsData.Read(iColumns); + + memset(pText, 0, sizeof(pText)); + bsData.Read(pText, 32); + + bsData.Read(fX); + bsData.Read(fY); + bsData.Read(fCol1Width); + if(iColumns) bsData.Read(fCol2Width); + + bsData.Read(MenuInteraction.bMenu); + for(i = 0; i < MAX_MENU_ITEMS; i++) bsData.Read(MenuInteraction.bRow[i]); + + if(pMenuPool->GetSlotState(byteMenuID)) pMenuPool->Delete(byteMenuID); + + CMenu *pMenu = pMenuPool->New(byteMenuID, fX, fY, iColumns + 1, fCol1Width, fCol2Width, &MenuInteraction); + if(!pMenu) return; + + pMenu->SetTitle(pText); + + memset(pText, 0, sizeof(pText)); + bsData.Read(pText, 32); + pMenu->SetColumnTitle(0, pText); + + bsData.Read(byteRows); + if(byteRows > MAX_MENU_ITEMS) return; + + for(byteRow = 0; byteRow < byteRows; byteRow++) { + memset(pText, 0, sizeof(pText)); + bsData.Read(pText, 32); + pMenu->AddMenuItem(0, byteRow, pText); + } + + if(!iColumns) return; + + memset(pText, 0, sizeof(pText)); + bsData.Read(pText, 32); + pMenu->SetColumnTitle(1, pText); + + bsData.Read(byteRows); + if(byteRows > MAX_MENU_ITEMS) return; + + for(byteRow = 0; byteRow < byteRows; byteRow++) { + memset(pText, 0, sizeof(pText)); + bsData.Read(pText, 32); + pMenu->AddMenuItem(1, byteRow, pText); + } +} +void ScrShowMenu(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + BYTE byteMenuID; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + if(!pNetGame) return; + + bsData.Read(byteMenuID); + pNetGame->GetMenuPool()->ShowMenu(byteMenuID); +} +void ScrHideMenu(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + PlayerID sender = rpcParams->sender; + + BYTE byteMenuID; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + if(!pNetGame) return; + + bsData.Read(byteMenuID); + pNetGame->GetMenuPool()->HideMenu(byteMenuID); +} //---------------------------------------------------- diff --git a/saco/net/textdrawpool.cpp b/saco/net/textdrawpool.cpp index 59e68e6..848adf4 100644 --- a/saco/net/textdrawpool.cpp +++ b/saco/net/textdrawpool.cpp @@ -2,6 +2,7 @@ #include "../main.h" BOOL unnamed_100B2AF0(); +extern int dword_1026EB4C; //---------------------------------------------------- @@ -17,3 +18,61 @@ CTextDrawPool::CTextDrawPool() } //---------------------------------------------------- + +void CTextDrawPool::FUNC_1001E7F0(unsigned short TextDrawID) +{ + CTextDraw *pTextDraw = (CTextDraw *)field_2400[TextDrawID]; + if(pTextDraw) { + delete pTextDraw; + field_2400[TextDrawID] = 0; + field_0[TextDrawID] = 0; + } +} + +//---------------------------------------------------- + +// MATCH +CTextDrawPool::~CTextDrawPool() +{ + for(int i = 0; i < MAX_TEXT_DRAWS; i++) { + CTextDraw *pTextDraw = (CTextDraw *)field_2400[i]; + if(pTextDraw) { + delete pTextDraw; + field_2400[i] = 0; + field_0[i] = 0; + } + } +} + +//---------------------------------------------------- + +void CTextDrawPool::FUNC_1001E870() +{ + for(int i = 0; i != MAX_TEXT_DRAWS; i++) { + if(field_0[i]) ((CTextDraw *)field_2400[i])->FUNC_100B34A0(); + } +} + +//---------------------------------------------------- + +void CTextDrawPool::FUNC_1001E8A0() +{ + for(int i = 0; i != MAX_TEXT_DRAWS; i++) { + if(field_0[i]) ((CTextDraw *)field_2400[i])->FUNC_100B36B0(); + } +} + +//---------------------------------------------------- + +void CTextDrawPool::FUNC_1001E830() +{ + int i = 0; + if(!dword_1026EB4C || !*(int *)dword_1026EB4C) { + while(i != MAX_TEXT_DRAWS) { + if(field_0[i]) ((CTextDraw *)field_2400[i])->FUNC_100B3480(); + i++; + } + } +} + +//---------------------------------------------------- diff --git a/saco/net/textdrawpool.h b/saco/net/textdrawpool.h index 6a89a0a..3b47422 100644 --- a/saco/net/textdrawpool.h +++ b/saco/net/textdrawpool.h @@ -3,13 +3,46 @@ //---------------------------------------------------- +class CTextDraw +{ +public: + ~CTextDraw(); // .text:100B2F50 + + void FUNC_100B3480(); // .text:100B3480 + void FUNC_100B34A0(); // .text:100B34A0 + void FUNC_100B36B0(); // .text:100B36B0 + void FUNC_100B2F60(char *szText); // .text:100B2F60 +}; + +//---------------------------------------------------- + class CTextDrawPool { private: int field_0[MAX_TEXT_DRAWS]; int field_2400[MAX_TEXT_DRAWS]; public: + int GetAt(unsigned short TextDrawID) + { + if(TextDrawID >= MAX_TEXT_DRAWS) return 0; + if(!field_0[TextDrawID]) return 0; + return field_2400[TextDrawID]; + } + CTextDrawPool(); + + void FUNC_1001E7F0(unsigned short TextDrawID); // .text:1001E7F0 + + void FUNC_1001E830(); // .text:1001E830 + + void FUNC_1001E870(); // .text:1001E870 + + ~CTextDrawPool(); // .text:1001E8D0 + + void FUNC_1001E8A0(); // .text:1001E8A0 + + // creates the slot's CTextDraw, replacing any existing one; returns it or 0 + int FUNC_1001E910(unsigned short TextDrawID, char *pTransmit, char *szText); // .text:1001E910 }; //---------------------------------------------------- diff --git a/saco/net/vehiclepool.cpp b/saco/net/vehiclepool.cpp index 1a28c23..78b48a9 100644 --- a/saco/net/vehiclepool.cpp +++ b/saco/net/vehiclepool.cpp @@ -1,5 +1,45 @@ #include "../main.h" +#include "../game/util.h" + +extern CChatWindow *pChatWindow; +extern int dword_1026EBA0; + +// same layout as the camera collision points that game/hooks.cpp passes around +typedef struct +{ + VECTOR vecPos; + float field_C; + float field_10; +} CAMCOL_POINT; + +char __stdcall FUNC_100B65D0(CAMCOL_POINT *pStart, CAMCOL_POINT *pEnd, ENTITY_TYPE *pEntity, + VECTOR *vecBoundCentre, float fScale, int a6); // .text:100B65D0 + +// the number plate renderer lives on a global, only its texture builder is needed here +class CPlateRenderer +{ +public: + int FUNC_1006D880(char *szPlate); // .text:1006D880 +}; + +// layout locks: these fail to compile if a field offset or the size moves +typedef char chk_CVehiclePool_size[sizeof(CVehiclePool) == 0x17898 ? 1 : -1]; +typedef char chk_VEHICLE_WAITING_size[sizeof(VEHICLE_WAITING) == 40 ? 1 : -1]; +typedef char chk_field_4[offsetof(CVehiclePool, field_4) == 4 ? 1 : -1]; +typedef char chk_field_FA4[offsetof(CVehiclePool, field_FA4) == 0xFA4 ? 1 : -1]; +typedef char chk_field_1134[offsetof(CVehiclePool, field_1134) == 0x1134 ? 1 : -1]; +typedef char chk_field_3074[offsetof(CVehiclePool, field_3074) == 0x3074 ? 1 : -1]; +typedef char chk_field_4FB4[offsetof(CVehiclePool, field_4FB4) == 0x4FB4 ? 1 : -1]; +typedef char chk_field_9DD4[offsetof(CVehiclePool, field_9DD4) == 0x9DD4 ? 1 : -1]; +typedef char chk_field_BD14[offsetof(CVehiclePool, field_BD14) == 0xBD14 ? 1 : -1]; +typedef char chk_field_DC54[offsetof(CVehiclePool, field_DC54) == 0xDC54 ? 1 : -1]; +typedef char chk_field_FB94[offsetof(CVehiclePool, field_FB94) == 0xFB94 ? 1 : -1]; +typedef char chk_field_11AD4[offsetof(CVehiclePool, field_11AD4) == 0x11AD4 ? 1 : -1]; +typedef char chk_field_17894[offsetof(CVehiclePool, field_17894) == 0x17894 ? 1 : -1]; +typedef char chk_CVehicle_field_64[offsetof(CVehicle, field_64) == 0x64 ? 1 : -1]; +typedef char chk_CVehicle_field_65[offsetof(CVehicle, field_65) == 0x65 ? 1 : -1]; +typedef char chk_CVehicle_field_71[offsetof(CVehicle, field_71) == 0x71 ? 1 : -1]; //---------------------------------------------------- @@ -18,3 +58,239 @@ CVehiclePool::CVehiclePool() } //---------------------------------------------------- + +int CVehiclePool::FindGtaIDFromID(int VehicleID) +{ + if(field_4FB4[VehicleID]) return GamePool_Vehicle_GetIndex((VEHICLE_TYPE *)field_4FB4[VehicleID]); + return INVALID_VEHICLE_ID; +} + +//---------------------------------------------------- + +void CVehiclePool::FUNC_1001E9B0() +{ + int iHighest = 0; + for(int i = 0; i < MAX_VEHICLES; i++) { + if(field_3074[i]) iHighest = i; + } + field_0 = iHighest; +} + +//---------------------------------------------------- + +int __stdcall FUNC_1001EBE0(VEHICLE_TYPE *pVehicle) +{ + if(pVehicle) return GamePool_Vehicle_GetIndex(pVehicle); + return INVALID_VEHICLE_ID; +} + +//---------------------------------------------------- + +short CVehiclePool::FUNC_1001EB90(int a1) +{ + for(int i = 1; i <= field_0; i++) { + if(a1 == field_4FB4[i]) return i; + } + return -1; +} + +//---------------------------------------------------- + +// MATCH +CVehiclePool::~CVehiclePool() +{ + for(VEHICLEID VehicleID = 0; VehicleID < MAX_VEHICLES; VehicleID++) { + FUNC_1001EA80(VehicleID); + } +} + +//---------------------------------------------------- + +void CVehiclePool::FUNC_1001EDE0() +{ + for(VEHICLEID VehicleID = 0; VehicleID <= field_0; VehicleID++) { + if(field_3074[VehicleID]) ((CVehicle *)field_1134[VehicleID])->FUNC_100B81F0(); + } +} + +//---------------------------------------------------- + +void CVehiclePool::FUNC_1001EB00(unsigned short VehicleID, int a2) +{ + if(field_3074[VehicleID]) ((CVehicle *)field_1134[VehicleID])->FUNC_100B7090(a2); +} + +//---------------------------------------------------- + +int CVehiclePool::FUNC_1001EA80(unsigned short VehicleID) +{ + if(VehicleID < MAX_VEHICLES && field_3074[VehicleID] && field_1134[VehicleID]) { + ((CVehicle *)field_1134[VehicleID])->FUNC_100B81F0(); + field_3074[VehicleID] = 0; + delete (CVehicle *)field_1134[VehicleID]; + field_1134[VehicleID] = 0; + field_4FB4[VehicleID] = 0; + FUNC_1001E9B0(); + return 1; + } + return 0; +} + +//---------------------------------------------------- + +void CVehiclePool::FUNC_1001EB30(unsigned short VehicleID, char a2, BYTE a3) +{ + if(VehicleID < MAX_VEHICLES && field_3074[VehicleID] && field_1134[VehicleID] && field_BD14[VehicleID]) { + CVehicle *pVehicle = (CVehicle *)field_1134[VehicleID]; + if(a2) { + pVehicle->field_64 = 1; + pVehicle->field_65 = 0; + } + pVehicle->FUNC_100B7840(a3); + } +} + +//---------------------------------------------------- + +int CVehiclePool::FUNC_1001EC00() +{ + float fClosestDistance = 10000.0f; + VEHICLEID ClosestVehicleID = INVALID_VEHICLE_ID; + + for(VEHICLEID VehicleID = 0; VehicleID <= field_0; VehicleID++) { + if(VehicleID < MAX_VEHICLES && field_3074[VehicleID] && field_BD14[VehicleID]) { + float fDistance = ((CVehicle *)field_1134[VehicleID])->FUNC_1009F0C0(); + if(fDistance < fClosestDistance) { + fClosestDistance = fDistance; + ClosestVehicleID = VehicleID; + } + } + } + return ClosestVehicleID; +} + +//---------------------------------------------------- + +void CVehiclePool::FUNC_1001ED70() +{ + if(!field_17894) return; + + if(dword_1026EBA0 && !*(DWORD *)(dword_1026EBA0 + 0x20)) + *(DWORD *)(dword_1026EBA0 + 0x20) = ((CPlateRenderer *)dword_1026EBA0)->FUNC_1006D880("XYZSR998"); + + for(VEHICLEID VehicleID = 0; VehicleID <= field_0; VehicleID++) { + if(field_3074[VehicleID]) ((CVehicle *)field_1134[VehicleID])->FUNC_100B81A0(); + } +} + +//---------------------------------------------------- + +short CVehiclePool::FUNC_1001EEB0(int a1, VECTOR *vecOrigin, VECTOR *vecLine) +{ + char colPoint[52]; + DWORD dwHitEntity = 0; + + ProcessLineOfSight(vecOrigin, vecLine, (VECTOR *)colPoint, &dwHitEntity, 0, 1, 0, 0, 0, 0, 0, 0); + if(!dwHitEntity) return -1; + + // the sentinel has to stay on the loop exit path, folding it into the return narrows it to 16 bits + int iVehicleID; + for(int i = 1; ; i++) { + if(i > field_0) { iVehicleID = INVALID_VEHICLE_ID; break; } + if(dwHitEntity == field_4FB4[i]) { iVehicleID = i; break; } + } + return iVehicleID; +} + +//---------------------------------------------------- + +char CVehiclePool::FUNC_1001EE20(VECTOR *vecStart, VECTOR *vecEnd) +{ + for(int i = 0; i <= field_0; i++) { + if(field_3074[i] && field_1134[i]) { + CVehicle *pVehicle = (CVehicle *)field_1134[i]; + if(pVehicle->IsAdded() && pVehicle->FUNC_1009F190() < 300.0f + && !pVehicle->FUNC_1009FCE0() + && pVehicle->FUNC_1009FC00(vecStart, vecEnd, 1)) return 1; + } + } + return 0; +} + +//---------------------------------------------------- + +int CVehiclePool::FUNC_1001EC70(float fX, float fY, float fZ) +{ + float fClosestDistance = 20000.0f; + VEHICLEID ClosestVehicleID = INVALID_VEHICLE_ID; + + for(VEHICLEID VehicleID = 0; VehicleID <= field_0; VehicleID++) { + if(field_3074[VehicleID] && field_BD14[VehicleID]) { + float fDistance = ((CVehicle *)field_1134[VehicleID])->GetDistanceFromPoint(fX, fY, fZ); + if(fDistance < fClosestDistance) { + fClosestDistance = fDistance; + ClosestVehicleID = VehicleID; + } + } + } + return ClosestVehicleID; +} + +//---------------------------------------------------- + +short CVehiclePool::FUNC_1001EF30(float fMaxDistance, VECTOR *vecStart, VECTOR *vecEnd, float fRadius) +{ + CAMCOL_POINT colStart; + CAMCOL_POINT colEnd; + VECTOR vecBoundCentre; + + // copying the points field by field is what keeps `vecStart` in a register over the walk + colStart.vecPos.X = vecStart->X; + colStart.vecPos.Y = vecStart->Y; + colStart.vecPos.Z = vecStart->Z; + colStart.field_C = fRadius; + colEnd.vecPos.X = vecEnd->X; + colEnd.vecPos.Y = vecEnd->Y; + colEnd.vecPos.Z = vecEnd->Z; + colEnd.field_C = fRadius; + + float fClosestDistance = 100000.0f; + VEHICLEID ClosestVehicleID = INVALID_VEHICLE_ID; + + for(int i = 0; i <= field_0; i++) { + if(field_3074[i] && field_1134[i]) { + CVehicle *pVehicle = (CVehicle *)field_1134[i]; + if(pVehicle->IsAdded() && pVehicle->FUNC_1009F190() < fMaxDistance && !pVehicle->FUNC_100B7420()) { + pVehicle->GetBoundCentre(&vecBoundCentre); + if(FUNC_100B65D0(&colStart, &colEnd, pVehicle->m_pEntity, &vecBoundCentre, 1.0f, 1)) { + float fDistance = pVehicle->GetDistanceFromPoint(vecStart->X, vecStart->Y, vecStart->Z); + if(fDistance < fClosestDistance) { + fClosestDistance = fDistance; + ClosestVehicleID = i; + } + } + } + } + } + return ClosestVehicleID; +} + +//---------------------------------------------------- + +int CVehiclePool::FUNC_1001ED10(VEHICLE_WAITING *pWaiting) +{ + // do/while with a `!=` bound, a for or while here gets unrolled or rotated + int i = 0; + do { + if(!field_FA4[i]) break; + i++; + } while(i != MAX_WAITING_VEHICLES); + if(i == MAX_WAITING_VEHICLES) + return pChatWindow->AddDebugMessage("All vehicle waiting slots are consumed!"); + + field_FA4[i] = 1; + field_4[i] = *pWaiting; + return i; +} + +//---------------------------------------------------- diff --git a/saco/net/vehiclepool.h b/saco/net/vehiclepool.h index 2022a1f..2ff7096 100644 --- a/saco/net/vehiclepool.h +++ b/saco/net/vehiclepool.h @@ -1,6 +1,17 @@ #pragma once +#define INVALID_VEHICLE_ID 0xFFFF +#define MAX_WAITING_VEHICLES 100 + +//---------------------------------------------------- + +// a vehicle the pool cannot create yet is parked in one of these; the payload layout is unresolved +struct VEHICLE_WAITING +{ + char field_0[40]; +}; + //---------------------------------------------------- class CVehiclePool @@ -8,21 +19,64 @@ class CVehiclePool public: int field_0; - char _gap4[4000]; + VEHICLE_WAITING field_4[MAX_WAITING_VEHICLES]; - char field_FA4[400]; + int field_FA4[MAX_WAITING_VEHICLES]; int field_1134[MAX_VEHICLES]; int field_3074[MAX_VEHICLES]; int field_4FB4[MAX_VEHICLES]; - char _gap6EF4[12000]; + char _gap6EF4[8000]; + WORD field_8E34[MAX_VEHICLES]; DWORD field_9DD4[MAX_VEHICLES]; - char _gapBD14[48000]; + int field_BD14[MAX_VEHICLES]; + int field_DC54[MAX_VEHICLES]; + int field_FB94[MAX_VEHICLES]; + VECTOR field_11AD4[MAX_VEHICLES]; int field_17894; + int GetSlotState(unsigned short VehicleID) { if(VehicleID >= MAX_VEHICLES) return 0; return field_3074[VehicleID]; } + int GetAt(unsigned short VehicleID) + { + if(VehicleID < MAX_VEHICLES && field_3074[VehicleID]) return field_1134[VehicleID]; + return 0; + } + + void FUNC_1001E9B0(); // .text:1001E9B0 + + int FUNC_1001EA80(unsigned short VehicleID); // .text:1001EA80 + + ~CVehiclePool(); // .text:1001F060 + + void FUNC_1001EB00(unsigned short VehicleID, int a2); // .text:1001EB00 + + void FUNC_1001EB30(unsigned short VehicleID, char a2, BYTE a3); // .text:1001EB30 + + void FUNC_1001EDE0(); // .text:1001EDE0 + + short FUNC_1001EB90(int a1); // .text:1001EB90 + + int FindGtaIDFromID(int VehicleID); // .text:1001EBC0 + + int FUNC_10001120(unsigned short a1); // .text:10001120 + + int FUNC_1001EC00(); // .text:1001EC00 + + int FUNC_1001ED10(VEHICLE_WAITING *pWaiting); // .text:1001ED10 + + int FUNC_1001EC70(float fX, float fY, float fZ); // .text:1001EC70 + + short FUNC_1001EF30(float fMaxDistance, VECTOR *vecStart, VECTOR *vecEnd, float fRadius); // .text:1001EF30 + + short FUNC_1001EEB0(int a1, VECTOR *vecOrigin, VECTOR *vecLine); // .text:1001EEB0 + + void FUNC_1001ED70(); // .text:1001ED70 + + char FUNC_1001EE20(VECTOR *vecStart, VECTOR *vecEnd); // .text:1001EE20 + CVehiclePool(); }; diff --git a/saco/newplayertags.cpp b/saco/newplayertags.cpp index 3a5aa78..4c8b410 100644 --- a/saco/newplayertags.cpp +++ b/saco/newplayertags.cpp @@ -26,6 +26,8 @@ HealthBarVertices1_s HealthBarVertices1[4] = }; extern D3DXMATRIX matView, matProj; +extern CFontRender *pDefaultFont; +extern CDeathWindow *pDeathWindow; CNewPlayerTags::CNewPlayerTags(IDirect3DDevice9* pDevice) { @@ -76,6 +78,88 @@ void CNewPlayerTags::End() m_pStates->Apply(); } +// the nametag pass brackets its batch with these two instead of Begin/End +void CNewPlayerTags::FUNC_1006CD80() +{ + if(m_pSprite) + m_pSprite->Begin(D3DXSPRITE_ALPHABLEND); +} + +void CNewPlayerTags::FUNC_1006CD90() +{ + if(m_pSprite) + m_pSprite->End(); +} + + +void CNewPlayerTags::Draw(D3DXVECTOR3* pPlayerPos, PCHAR szName, DWORD dwColor, float fDistanceFromLocalPlayer, + BYTE bNameTagStatus, int nStatusStyle) +{ + D3DXVECTOR3 TagPos = *pPlayerPos; + TagPos.z += 0.2f + (fDistanceFromLocalPlayer * 0.0475f); + + D3DVIEWPORT9 Viewport; + m_pDevice->GetViewport(&Viewport); + + D3DXVECTOR3 Out; + D3DXMATRIX matIdent; + D3DXMatrixIdentity(&matIdent); + D3DXVec3Project(&Out, &TagPos, &Viewport, &matProj, &matView, &matIdent); + + if(Out.z > 1.0f) + return; + + RECT rectStatus; + rectStatus.left = (int)Out.x; + rectStatus.top = (int)Out.y; + rectStatus.right = (int)Out.x; + rectStatus.bottom = (int)Out.y; + + RECT rectText = rectStatus; + + SIZE size = pDefaultFont->MeasureText2(szName, 0); + + rectText.left -= size.cx / 2; + rectText.right += size.cx / 2; + rectText.top += 17 - size.cy; + pDefaultFont->RenderText(m_pSprite, szName, rectText, 256, dwColor, 1); + + if(bNameTagStatus) + { + if(pDeathWindow) + { + if(!pDeathWindow->field_14B) + pDeathWindow->CreateAuxFonts(); + + ID3DXFont *pFont1 = pDeathWindow->field_14F; + ID3DXFont *pFont2 = pDeathWindow->field_153; + + if(pFont1 && pFont2) + { + RECT rectCalc; + rectCalc.left = 0; + rectCalc.top = 0; + rectCalc.right = 0; + rectCalc.bottom = 0; + pFont2->DrawText(NULL, "C", 1, &rectCalc, DT_CALCRECT|DT_NOCLIP|DT_VCENTER|DT_CENTER, 0); + + rectStatus.top += (rectCalc.bottom - rectCalc.top) / 2 + 17; + rectStatus.bottom = rectStatus.top + 2; + rectStatus.left += -22 - (rectCalc.right - rectCalc.left) / 2; + rectStatus.right = rectStatus.left + 1; + + if(nStatusStyle == 2) + { + pFont2->DrawText(NULL, "C", 1, &rectStatus, DT_NOCLIP|DT_VCENTER|DT_CENTER, 0xFF000000); + rectStatus.top -= 2; + rectStatus.left--; + pFont1->DrawText(NULL, "E", 1, &rectStatus, DT_NOCLIP|DT_VCENTER|DT_CENTER, 0xFFE3E1E3); + } + } + } + } +} + void CNewPlayerTags::DeleteDeviceObjects() { SAFE_RELEASE(m_pStates); diff --git a/saco/newplayertags.h b/saco/newplayertags.h index 3cfa273..b5197db 100644 --- a/saco/newplayertags.h +++ b/saco/newplayertags.h @@ -25,7 +25,12 @@ public: void Begin(); void End(); + void FUNC_1006CD80(); + void FUNC_1006CD90(); void Draw(D3DXVECTOR3* pPlayerPos, float fHealth, float fArmor, float fDistanceFromLocalPlayer); + // the nametag label pass, 0x1006CDA0 + void Draw(D3DXVECTOR3* pPlayerPos, PCHAR szName, DWORD dwColor, float fDistanceFromLocalPlayer, + BYTE bNameTagStatus, int nStatusStyle); void DeleteDeviceObjects(); void RestoreDeviceObjects(); diff --git a/saco/saco.vcproj b/saco/saco.vcproj index 718032f..a2cfe6c 100644 --- a/saco/saco.vcproj +++ b/saco/saco.vcproj @@ -79,6 +79,7 @@ OmitFramePointers="TRUE" AdditionalIncludeDirectories="d3d9\include\" PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;SACO_EXPORTS;SAMPCLI" + StringPooling="TRUE" ExceptionHandling="TRUE" RuntimeLibrary="0" StructMemberAlignment="1" @@ -329,6 +330,12 @@ + + + + @@ -1003,6 +1010,12 @@ + + + @@ -1054,6 +1067,9 @@ + + OnInit(); m_pListBox->GetElement(0)->TextureColor.Init(D3DCOLOR_ARGB( 200, 255, 255, 255 )); m_pListBox->field_4D = 3; - m_pListBox->field_51 = field_24 * field_14; - m_pListBox->field_59 = field_28 * field_14; - m_pListBox->field_55 = field_2C * field_14; + m_pListBox->field_51[0] = field_24 * field_14; + m_pListBox->field_51[2] = field_28 * field_14; + m_pListBox->field_51[1] = field_2C * field_14; m_pListBox->SetEnabled(false); m_pListBox->SetVisible(false); FUNC_1006E930(); } } + +void CScoreBoard::GetRect(RECT *pRect) +{ + pRect->left = (int)field_8; + pRect->right = pRect->left + (int)field_14; + pRect->top = (int)field_C; + pRect->bottom = pRect->top + (int)field_18; +} + +void CScoreBoard::Hide(bool bResetInput) +{ + if(!field_0) return; + if(!m_pDialog) return; + + m_pDialog->SetVisible(false); + m_pListBox->SetEnabled(false); + m_pListBox->SetVisible(false); + + if(bResetInput) + pGame->ToggleKeyInputsDisabled(0, FALSE); + + field_0 = 0; +} + +// swapping rows through a temp is what retail inlines at both sort sites +inline void SwapScoreboardRows(SCOREBOARD_ROW *pA, SCOREBOARD_ROW *pB) +{ + SCOREBOARD_ROW tmp = *pA; + *pA = *pB; + *pB = tmp; +} + +void CScoreBoard::FUNC_1006ED30() +{ + if(!pNetGame) return; + if(!pNetGame->GetPlayerPool()) return; + if(!field_0) return; + if(!m_pDialog) return; + + int nSelected = m_pListBox->GetSelectedIndex(); + CDXUTScrollBar *pScrollBar = m_pListBox->GetScrollBar(); + int nTrackPos = pScrollBar->GetTrackPos(); + m_pListBox->RemoveAllItems(); + + CPlayerPool *pPlayerPool = pNetGame->GetPlayerPool(); + int nRows = pPlayerPool->GetPlayerCount(0) + 1; + int localPlayerId = pPlayerPool->GetLocalPlayerID(); + SCOREBOARD_ROW *pRows = (SCOREBOARD_ROW *)calloc(nRows, sizeof(SCOREBOARD_ROW)); + + strcpy(pRows->szName, pPlayerPool->GetLocalPlayerName()); + pRows->Color = pPlayerPool->GetLocalPlayer()->GetPlayerColorAsARGB(); + pRows->iScore = pPlayerPool->field_0; + pRows->iPing = pPlayerPool->field_22; + pRows->iPlayerId = pPlayerPool->GetLocalPlayerID(); + + SCOREBOARD_ROW *pRow = pRows + 1; + for(int playerId = 0; playerId < MAX_PLAYERS; playerId++) + { + if(pPlayerPool->GetSlotState((PLAYERID)playerId) != 1) continue; + if(playerId == localPlayerId) continue; + if(pPlayerPool->IsPlayerNPC((PLAYERID)playerId)) continue; + + pRow->iPing = pPlayerPool->GetPlayerPing((PLAYERID)playerId); + strcpy(pRow->szName, pPlayerPool->FUNC_100175C0(playerId)); + pRow->iScore = pPlayerPool->GetPlayerScore((PLAYERID)playerId); + pRow->Color = pPlayerPool->GetAt((PLAYERID)playerId)->GetPlayerColorAsARGB(); + pRow->iPlayerId = playerId; + pRow++; + } + + if(field_40 == 1) + { + for(int i = nRows - 1; i > 0; i--) + for(int j = 0; j < i; j++) + if(strcmp(pRows[j + 1].szName, pRows[j].szName) < 0) + SwapScoreboardRows(&pRows[j], &pRows[j + 1]); + } + else if(field_40 == 2) + { + for(int i = nRows - 1; i > 0; i--) + for(int j = 0; j < i; j++) + if(pRows[j + 1].iScore > pRows[j].iScore) + SwapScoreboardRows(&pRows[j], &pRows[j + 1]); + } + + char szText[256]; + SCOREBOARD_ROW *pFill = pRows; + for(int i = 0; i < nRows; i++) + { + sprintf(szText, "%u", pFill->iPlayerId); + m_pListBox->AddItem(szText, (void *)pFill->iPlayerId, pFill->Color); + + if(m_pListBox->GetItem(i) && pFill->szName[0]) + { + m_pListBox->SetItemColumnText(i, 0, pFill->szName); + sprintf(szText, "%d", pFill->iScore); + m_pListBox->SetItemColumnText(i, 1, szText); + sprintf(szText, "%u", pFill->iPing); + m_pListBox->SetItemColumnText(i, 2, szText); + } + + pFill++; + } + + if(nSelected < 0) + m_pListBox->SelectItem(-1); + else + m_pListBox->SelectItem(nSelected); + + free(pRows); + + pScrollBar->SetTrackPos(nTrackPos); +} + +void CScoreBoard::Show() +{ + if(field_0) return; + if(!m_pDialog) return; + + m_pDialog->SetVisible(true); + m_pListBox->SetEnabled(true); + m_pListBox->UpdateRects(); + m_pListBox->SetVisible(true); + + FUNC_1006ED30(); + pGame->ToggleKeyInputsDisabled(3, FALSE); + + field_0 = 1; +} + +void CScoreBoard::ClickPlayer() +{ + if(!field_0) return; + if(!m_pDialog) return; + + DXUTListBoxItem *pItem = m_pListBox->GetSelectedItem(); + if(pItem) + { + PLAYERID playerId = (PLAYERID)atoi(pItem->strText); + RakNet::BitStream bsSend; + bsSend.Write(playerId); + bsSend.Write((BYTE)0); + pNetGame->GetRakClient()->RPC(RPC_ClickPlayer, &bsSend, HIGH_PRIORITY, RELIABLE_ORDERED, 0, FALSE); + } + + if(!field_0) return; + if(!m_pDialog) return; + + m_pDialog->SetVisible(false); + m_pListBox->SetEnabled(false); + m_pListBox->SetVisible(false); + pGame->ToggleKeyInputsDisabled(0, FALSE); + + field_0 = 0; +} diff --git a/saco/scoreboard.h b/saco/scoreboard.h index 33b6745..1dcd925 100644 --- a/saco/scoreboard.h +++ b/saco/scoreboard.h @@ -1,6 +1,15 @@ #pragma once +struct SCOREBOARD_ROW +{ + D3DCOLOR Color; + char szName[29]; + int iPlayerId; + int iScore; + int iPing; +}; + class CScoreBoard { private: @@ -27,4 +36,9 @@ public: void FUNC_1006E930(); void ResetDialogControls(CDXUTDialog *pDialog); + void GetRect(RECT *pRect); + void FUNC_1006ED30(); + void Show(); + void ClickPlayer(); + void Hide(bool bResetInput); }; diff --git a/saco/unkclass10.cpp b/saco/unkclass10.cpp index c80fae2..49c57c0 100644 --- a/saco/unkclass10.cpp +++ b/saco/unkclass10.cpp @@ -1,6 +1,9 @@ #include "main.h" +extern CGame *pGame; +extern D3DXMATRIX matView, matProj; + CUnkClass10::CUnkClass10(IDirect3DDevice9 *pD3DDevice) { m_pD3DDevice = pD3DDevice; @@ -19,8 +22,104 @@ CUnkClass10::CUnkClass10(IDirect3DDevice9 *pD3DDevice) D3DXCreateLine(pD3DDevice, &m_pD3DLine); - D3DXCreateFont(m_pD3DDevice, 22, 0, FW_NORMAL, 1, FALSE, + D3DXCreateFont(pD3DDevice, 22, 0, FW_NORMAL, 1, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "SAMPAUX3", &m_pD3DFont1); - D3DXCreateFont(m_pD3DDevice, 28, 0, FW_NORMAL, 1, FALSE, + D3DXCreateFont(pD3DDevice, 28, 0, FW_NORMAL, 1, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "SAMPAUX3", &m_pD3DFont2); } + + +float CUnkClass10::FUNC_10071C20(D3DXVECTOR3 *pPos, D3DXVECTOR2 *pOut) +{ + D3DXVECTOR3 In = *pPos; + D3DVIEWPORT9 Viewport; + D3DXVECTOR3 Out; + D3DXMATRIX matIdent; + + m_pD3DDevice->GetViewport(&Viewport); + + D3DXMatrixIdentity(&matIdent); + D3DXVec3Project(&Out, &In, &Viewport, &matProj, &matView, &matIdent); + + pOut->x = Out.x; + pOut->y = Out.y; + + return Out.z; +} + + +PCHAR CUnkClass10::FUNC_10071F90() +{ + return "C"; +} + +// SAMPAUX3 glyph for the marker style in field_7C +PCHAR CUnkClass10::FUNC_10071FA0(int nIndex) +{ + if(nIndex == 0) + { + if(field_7C == 0) return "6"; + if(field_7C == 1) return "3"; + if(field_7C == 2) return "9"; + } + else if(nIndex == 1) + { + if(field_7C == 0) return "7"; + if(field_7C == 1) return "4"; + if(field_7C == 2) return "A"; + } + else if(nIndex == 2) + { + if(field_7C == 0) return "8"; + if(field_7C == 1) return "5"; + if(field_7C == 2) return "B"; + } + + return "0"; +} + + +void CUnkClass10::FUNC_10072040(RECT *pRect, POINT *pPoint) +{ + pRect->left = pPoint->x - field_0 / 2; + pRect->right = pRect->left + field_0; + pRect->top = pPoint->y - field_4 / 2; + pRect->bottom = pRect->top + field_4; +} + +// picks the hovered cell out of the seven hit rects +void CUnkClass10::FUNC_100720A0() +{ + POINT point; + GetCursorPos(&point); + ScreenToClient(pGame->GetMainWindowHwnd(), &point); + + if(!field_84) field_113 = -1; + else if(!pGame->field_61) field_113 = -1; + else if(PtInRect(&field_8[0], point)) field_113 = 0; + else if(PtInRect(&field_8[1], point)) field_113 = 1; + else if(PtInRect(&field_8[2], point)) field_113 = 2; + else if(PtInRect(&field_8[3], point)) field_113 = 3; + else if(PtInRect(&field_8[4], point)) field_113 = 4; + else if(PtInRect(&field_8[5], point)) field_113 = 5; + else field_113 = PtInRect(&field_8[6], point) ? 10 : -1; +} + +void CUnkClass10::FUNC_100721F0(int nStyle) +{ + if(field_78 == 1 && nStyle == 2) + nStyle = 0; + + field_7C = nStyle; +} + +BOOL CUnkClass10::FUNC_100723C0() +{ + RECT rect; + GetClientRect(pGame->GetMainWindowHwnd(), &rect); + + field_A7 = (rect.right - rect.left) / 2; + field_AB = (rect.bottom - rect.top) / 2; + + return SetCursorPos(field_A7, field_AB); +} diff --git a/saco/unkclass10.h b/saco/unkclass10.h index c7430e3..1db4220 100644 --- a/saco/unkclass10.h +++ b/saco/unkclass10.h @@ -4,7 +4,9 @@ class CUnkClass10 { private: - char _gap0[120]; + int field_0; + int field_4; + RECT field_8[7]; int field_78; int field_7C; int field_80; @@ -17,7 +19,9 @@ private: char field_A4; char field_A5; char field_A6; - char _gapA7[92]; + int field_A7; + int field_AB; + char _gapAF[84]; IDirect3DDevice9 *m_pD3DDevice; ID3DXLine *m_pD3DLine; ID3DXFont *m_pD3DFont1; @@ -25,6 +29,17 @@ private: int field_113; public: + + int GetField80() { return field_80; }; // .text:10002E70, out of line copy + CUnkClass10(IDirect3DDevice9 *pD3DDevice); + float FUNC_10071C20(D3DXVECTOR3 *pPos, D3DXVECTOR2 *pOut); + PCHAR FUNC_10071F90(); + PCHAR FUNC_10071FA0(int nIndex); + void FUNC_10072040(RECT *pRect, POINT *pPoint); + void FUNC_100720A0(); + void FUNC_100721F0(int nStyle); + BOOL FUNC_100723C0(); + }; diff --git a/saco/unkclass3.cpp b/saco/unkclass3.cpp index 85c13af..3876585 100644 --- a/saco/unkclass3.cpp +++ b/saco/unkclass3.cpp @@ -15,7 +15,8 @@ CUnkClass3::CUnkClass3(IDirect3DDevice9 *pD3DDevice) field_10 = 300; field_14 = 100; field_18 = 30; - memset(field_89, 0, sizeof(field_89)); + // retail zeroes this with a loop, not memset: the inline rep stosd sets edi up first + for(int i = 0; i < 516; i++) field_89[i] = 0; } void CUnkClass3::ResetDialogControls(CDXUTDialog *pDialog) diff --git a/saco/unkclass3.h b/saco/unkclass3.h index 1a644ec..1050d2b 100644 --- a/saco/unkclass3.h +++ b/saco/unkclass3.h @@ -25,4 +25,6 @@ public: CUnkClass3(IDirect3DDevice9 *pD3DDevice); void ResetDialogControls(CDXUTDialog *pDialog); + + int GetField28() { return field_28; }; // .text:10002E60, out of line copy }; diff --git a/saco/unkclass5.cpp b/saco/unkclass5.cpp index 22eb402..1baf8be 100644 --- a/saco/unkclass5.cpp +++ b/saco/unkclass5.cpp @@ -1,10 +1,37 @@ #include "main.h" +CIntArray::~CIntArray() +{ + SetSize(0); +} + +bool CIntArray::SetSize(unsigned int nSize) +{ + if(!nSize) { + if(m_pData) { + free(m_pData); + m_pData = NULL; + } + m_nSize = 0; + return true; + } + + int *pNew = (int *)realloc(m_pData, nSize * sizeof(int)); + if(!pNew) return false; + + m_pData = pNew; + if(m_nSize < nSize) { + for(unsigned int i = m_nSize; i != nSize; i++) + m_pData[i] = 0; + } + + m_nSize = nSize; + return true; +} + CUnkClass5::CUnkClass5(IDirect3DDevice9 *pD3DDevice) { - field_0 = 0; - field_4 = 0; m_pD3DDevice = pD3DDevice; m_pDialog = NULL; field_30 = 0; @@ -22,9 +49,9 @@ CUnkClass5::CUnkClass5(IDirect3DDevice9 *pD3DDevice) field_240 = 220; field_24C = 0; field_250 = -1; + field_254 = RakNet::GetTime(); field_25C = 0; field_258 = 0; - field_254 = RakNet::GetTime(); } void CUnkClass5::ResetDialogControls(CDXUTDialog *pDialog) @@ -41,7 +68,7 @@ void CUnkClass5::ResetDialogControls(CDXUTDialog *pDialog) m_pListBox->SetEnabled(false); m_pListBox->SetVisible(false); m_pListBox->field_4D = 2; - m_pListBox->field_51 = field_23C; - m_pListBox->field_55 = field_240; + m_pListBox->field_51[0] = field_23C; + m_pListBox->field_51[1] = field_240; } } diff --git a/saco/unkclass5.h b/saco/unkclass5.h index ec3b34d..a2915be 100644 --- a/saco/unkclass5.h +++ b/saco/unkclass5.h @@ -1,11 +1,22 @@ #pragma once +class CIntArray +{ +public: + CIntArray() { m_pData = NULL; m_nSize = 0; } + ~CIntArray(); + + bool SetSize(unsigned int nSize); + + int *m_pData; + unsigned int m_nSize; +}; + class CUnkClass5 { private: - int field_0; - int field_4; + CIntArray m_Array; IDirect3DDevice9* m_pD3DDevice; char _gapC[8]; int field_14;