diff --git a/src/FileLoader.cpp b/src/FileLoader.cpp index f50638b4..dd58614d 100644 --- a/src/FileLoader.cpp +++ b/src/FileLoader.cpp @@ -501,7 +501,7 @@ CFileLoader::LoadObjectTypes(const char *filename) CARS, PEDS, PATH, - TWO2FX + TWODFX }; char *line; int fd; @@ -528,7 +528,7 @@ CFileLoader::LoadObjectTypes(const char *filename) else if(strncmp(line, "cars", 4) == 0) section = CARS; else if(strncmp(line, "peds", 4) == 0) section = PEDS; else if(strncmp(line, "path", 4) == 0) section = PATH; - else if(strncmp(line, "2dfx", 4) == 0) section = TWO2FX; + else if(strncmp(line, "2dfx", 4) == 0) section = TWODFX; }else if(strncmp(line, "end", 3) == 0){ section = section == MLO ? OBJS : NONE; }else switch(section){ @@ -571,7 +571,7 @@ CFileLoader::LoadObjectTypes(const char *filename) pathIndex = -1; } break; - case TWO2FX: + case TWODFX: Load2dEffect(line); break; } @@ -848,6 +848,7 @@ CFileLoader::LoadCarPathNode(const char *line, int id, int node) ThePaths.StoreNodeInfoCar(id, node, type, next, x, y, z, 0, numLeft, numRight); } + void CFileLoader::Load2dEffect(const char *line) { diff --git a/src/Frontend.cpp b/src/Frontend.cpp index b8ee10f4..de9e8a65 100644 --- a/src/Frontend.cpp +++ b/src/Frontend.cpp @@ -1,6 +1,25 @@ +#define DIRECTINPUT_VERSION 0x0800 +#include #include "common.h" #include "patcher.h" +#include "win.h" #include "Frontend.h" +#include "Font.h" +#include "Pad.h" +#include "Text.h" +#include "main.h" +#include "Timer.h" +#include "Game.h" +#include "DMAudio.h" +#include "MusicManager.h" +#include "FileMgr.h" +#include "Streaming.h" +#include "TxdStore.h" +#include "General.h" +#include "PCSave.h" +#include "Script.h" +#include "Camera.h" +#include "MenuScreens.h" int32 &CMenuManager::OS_Language = *(int32*)0x5F2F78; int8 &CMenuManager::m_PrefsUseVibration = *(int8*)0x95CD92; @@ -17,6 +36,7 @@ int8 &CMenuManager::m_PrefsLanguage = *(int8*)0x941238; bool &CMenuManager::m_PrefsAllowNastyGame = *(bool*)0x5F2E64; bool &CMenuManager::m_bStartUpFrontEndRequested = *(bool*)0x95CCF4; +bool &CMenuManager::m_bShutDownFrontEndRequested = *(bool*)0x95CD6A; int8 &CMenuManager::m_PrefsUseWideScreen = *(int8*)0x95CD23; int8 &CMenuManager::m_PrefsRadioStation = *(int8*)0x95CDA4; @@ -29,17 +49,806 @@ int32 &CMenuManager::m_PrefsSfxVolume = *(int32*)0x5F2E48; CMenuManager &FrontEndMenuManager = *(CMenuManager*)0x8F59D8; -WRAPPER void CMenuManager::Process(void) { EAXJMP(0x485100); } -WRAPPER void CMenuManager::DrawFrontEnd(void) { EAXJMP(0x47A540); } -WRAPPER void CMenuManager::UnloadTextures(void) { EAXJMP(0x47A440); } -WRAPPER void CMenuManager::LoadAllTextures(void) { EAXJMP(0x47A230); } -WRAPPER void CMenuManager::LoadSettings(void) { EAXJMP(0x488EE0); } -WRAPPER void CMenuManager::WaitForUserCD(void) { EAXJMP(0x48ADD0); } +char *FrontendFilenames[] = { + "fe2_mainpanel_ul", + "fe2_mainpanel_ur", + "fe2_mainpanel_dl", + "fe2_mainpanel_dr", + "fe2_mainpanel_dr2", + "fe2_tabactive", + "fe_iconbrief", + "fe_iconstats", + "fe_iconcontrols", + "fe_iconsave", + "fe_iconaudio", + "fe_icondisplay", + "fe_iconlanguage", + "fe_controller", + "fe_controllersh", + "fe_arrows1", + "fe_arrows2", + "fe_arrows3", + "fe_arrows4", + "fe_radio1", // HEAD_RADIO + "fe_radio2", // DOUBLE_CLEF + "fe_radio5", // JAH_RADIO + "fe_radio7", // RISE_FM + "fe_radio8", // LIPS_106 + "fe_radio3", // GAME_FM + "fe_radio4", // MSX_FM + "fe_radio6", // FLASHBACK + "fe_radio9", // CHATTERBOX +}; -int CMenuManager::FadeIn(int alpha) { - if (m_nCurrScreen == MENU_LOADING_IN_PROGRESS || - m_nCurrScreen == MENU_SAVING_IN_PROGRESS || - m_nCurrScreen == MENU_DELETING) +char *MenuFilenames[] = { + "connection24", "", + "findgame24", "", + "hostgame24", "", + "mainmenu24", "", + "Playersetup24", "", + "singleplayer24", "", + "multiplayer24", "", + "dmalogo128", "dmalogo128m", + "gtaLogo128", "gtaLogo128", + "rockstarLogo128", "rockstarlogo128m", + "gamespy256", "gamespy256a", + "mouse", "mousetimera", + "mousetimer", "mousetimera", + "mp3logo", "mp3logoA", + "downOFF", "buttonA", + "downON", "buttonA", + "upOFF", "buttonA", + "upON", "buttonA", + "gta3logo256", "gta3logo256m", + nil, nil +}; + +#if 1 +WRAPPER void CMenuManager::BuildStatLine(char *, void *, uint16, void *) { EAXJMP(0x483870); } +#else +void CMenuManager::BuildStatLine(char *, void *, uint16, void *) +{ + +} +#endif + +#if 0 +WRAPPER void CMenuManager::CentreMousePointer() { EAXJMP(0x48ACE0); } +#else +void CMenuManager::CentreMousePointer() +{ + tagPOINT Point; + + if (SCREENW * 0.5f == 0.0f && 0.0f == SCREENH * 0.5f) { + Point.x = SCREEN_WIDTH / 2; + Point.y = SCREEN_HEIGHT / 2; + ClientToScreen(PSGLOBAL(window), &Point); + SetCursorPos(Point.x, Point.y); + + PSGLOBAL(lastMousePos.x) = SCREEN_WIDTH / 2; + PSGLOBAL(lastMousePos.y) = SCREEN_HEIGHT / 2; + } +} +#endif + +#if 1 +WRAPPER void CMenuManager::CheckCodesForControls(int, int) { EAXJMP(0x48A950); } +#else +void CMenuManager::CheckCodesForControls() +{ + +} +#endif + +#if 0 +WRAPPER bool CMenuManager::CheckHover(int, int, int, int) { EAXJMP(0x48ACA0); } +#else +bool CMenuManager::CheckHover(int x1, int x2, int y1, int y2) +{ + return m_nMousePosX > x1 && m_nMousePosX < x2 && + m_nMousePosY > y1 && m_nMousePosY < y2; +} +#endif + +#if 1 +WRAPPER int CMenuManager::CostructStatLine(int) { EAXJMP(0x482800); } +#else +int CMenuManager::CostructStatLine(int) +{ + +} +#endif + +#if 0 +WRAPPER void CMenuManager::DisplayHelperText() { EAXJMP(0x48B490); } +#else +void CMenuManager::DisplayHelperText() +{ + wchar *str = nil; + switch (m_nHelperTextMsgId) { + case 0: + str = TheText.Get("FET_MIG"); + break; + case 1: + str = TheText.Get("FET_APP"); + break; + case 2: + str = TheText.Get("FET_HRD"); + break; + case 3: + str = TheText.Get("FET_RSO"); + break; + case 4: + str = TheText.Get("FET_RSC"); + break; + default: + break; + }; + + CFont::SetAlignment(ALIGN_CENTER); + CFont::SetScale(SCREEN_SCALE_X(0.4f), SCREEN_SCALE_Y(0.6f)); + CFont::SetFontStyle(FONT_HEADING); + CFont::SetDropColor(CRGBA(0, 0, 0, MENUDROP_COLOR_A)); + CFont::SetDropShadowPosition(MENUDROP_COLOR_SIZE); + + CFont::SetColor(CRGBA(255, 255, 255, 255)); + CFont::PrintString(SCREEN_WIDTH / 2, SCREEN_SCALE_FROM_BOTTOM(120.0f), str); +} +#endif + +#if 0 +WRAPPER float CMenuManager::DisplaySlider(float, float, float, float, float, float) { EAXJMP(0x488420); } +#else +float CMenuManager::DisplaySlider(float x, float y, float leftSize, float rightSize, float rectSize, float progress) +{ + CRGBA color; + float sizeRange; + + float input = 0.0f; + for (int i = 0; i < 16; i++) { + input = i * rectSize/16.0f + x; + + if (i/16.0f + 1/32.0f < progress) + color = CRGBA(255, 217, 106, FadeIn(255)); + else + color = CRGBA(185, 120, 0, FadeIn(255)); + + sizeRange = max(leftSize, rightSize); + + float _x = i * rectSize/16.0f + x; + float _y = y + sizeRange - ((16 - i) * leftSize + i * rightSize)/16.0f; + float _w = SCREEN_SCALE_X(10.0f) + i * rectSize/16.0f + x; + float _h = y + sizeRange; + float _s = SCREEN_SCALE_X(2.0f); + CSprite2d::DrawRect(CRect(_x + _s, _y + _s, _w + _s, _h + _s), CRGBA(0, 0, 0, FadeIn(255))); // Shadow + CSprite2d::DrawRect(CRect(i * rectSize/16.0f + x, y + sizeRange - ((16 - i) * leftSize + i * rightSize)/16.0f, SCREEN_SCALE_X(10.0f) + i * rectSize/16.0f + x, y + sizeRange), color); + }; + return input; +} +#endif + +#if 1 +WRAPPER void CMenuManager::DoSettingsBeforeStartingAGame() { EAXJMP(0x48AB40); } +#else +WRAPPER void CMenuManager::DoSettingsBeforeStartingAGame() +{ + +} +#endif + +#if 0 +WRAPPER void CMenuManager::Draw() { EAXJMP(0x47AE00); } +#else +void CMenuManager::Draw() +{ + CFont::SetBackgroundOff(); + CFont::SetPropOn(); + CFont::SetCentreOff(); + CFont::SetJustifyOn(); + CFont::SetBackGroundOnlyTextOn(); + CFont::SetWrapx(SCREEN_SCALE_FROM_RIGHT(40.0f)); + CFont::SetRightJustifyWrap(0.0f); + CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(MENUDROP_COLOR_A))); + + switch (m_nCurrScreen) { + case MENUPAGE_STATS: + PrintStats(); + break; + case MENUPAGE_BRIEFS: + PrintBriefs(); + break; + case MENUPAGE_CONTROLLER_DEBUG: + DrawControllerScreenExtraText(0, 350, 20); + break; + } + + // Header. + if (aScreens[m_nCurrScreen].m_ScreenName[0]) { + CFont::SetDropShadowPosition(0); + CFont::SetColor(CRGBA(0, 0, 0, FadeIn(255))); + CFont::SetRightJustifyOn(); + CFont::SetFontStyle(FONT_HEADING); + CFont::SetScale(SCREEN_SCALE_X(MENUHEADER_WIDTH), SCREEN_SCALE_Y(MENUHEADER_HEIGHT)); + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(MENUHEADER_POS_X), SCREEN_SCALE_FROM_BOTTOM(MENUHEADER_POS_Y), TheText.Get(aScreens[m_nCurrScreen].m_ScreenName)); + } + + // Action text. + wchar *str; + if (aScreens[m_nCurrScreen].m_aEntries[0].m_Action == MENUACTION_LABEL) { + switch (m_nCurrScreen) { + case MENUPAGE_LOAD_SLOT_CONFIRM: + if (m_bGameNotLoaded) + str = TheText.Get("FES_LCG"); + else + str = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[0].m_EntryName); + break; + case MENUPAGE_SAVE_OVERWRITE_CONFIRM: + if (Slots[m_nCurrSaveSlot] == 1) + str = TheText.Get("FESZ_QZ"); + else + str = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[0].m_EntryName); + break; + case MENUPAGE_EXIT: + if (m_bGameNotLoaded) + str = TheText.Get("FEQ_SRW"); + else + str = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[0].m_EntryName); + break; + default: + str = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[0].m_EntryName); + break; + }; + + CFont::SetDropShadowPosition(MENUDROP_COLOR_SIZE); + CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(MENUDROP_COLOR_A))); + CFont::SetFontStyle(FONT_BANK); + CFont::SetScale(SCREEN_SCALE_X(MENUACTION_WIDTH), SCREEN_SCALE_Y(MENUACTION_HEIGHT)); + CFont::SetAlignment(ALIGN_LEFT); + CFont::SetColor(CRGBA(235, 170, 50, FadeIn(255))); + CFont::PrintString(SCREEN_SCALE_X(MENUACTION_POS_X), SCREEN_SCALE_Y(MENUACTION_POS_Y), str); + } + + for (int i = 0; i < MENUROWS; ++i) { + if (aScreens[m_nCurrScreen].m_aEntries[i].m_Action != MENUACTION_LABEL && aScreens[m_nCurrScreen].m_aEntries[i].m_EntryName[0]) { + wchar *textToPrint[MENUCOLUMNS] = { nil, nil }; + + if (aScreens[m_nCurrScreen].m_aEntries[i].m_SaveSlot >= SAVESLOT_1 && aScreens[m_nCurrScreen].m_aEntries[i].m_SaveSlot <= SAVESLOT_8) { + textToPrint[MENUCOLUMN_LEFT] = GetNameOfSavedGame(i - 1); + textToPrint[MENUCOLUMN_RIGHT] = GetSavedGameDateAndTime(i - 1); + + if (!textToPrint[MENUCOLUMN_LEFT][0]) { + sprintf(gString, "FEM_SL%d", i); + textToPrint[MENUCOLUMN_LEFT] = TheText.Get(gString); + } + } + else { + textToPrint[MENUCOLUMN_LEFT] = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[i].m_EntryName); + } + + switch (aScreens[m_nCurrScreen].m_aEntries[i].m_Action) { + case MENUACTION_CTRLVIBRATION: + break; + case MENUACTION_CTRLCONFIG: + switch (CPad::GetPad(0)->Mode) { + case 0: + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEC_CF1"); + break; + case 1: + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEC_CF2"); + break; + case 2: + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEC_CF3"); + break; + case 3: + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEC_CF4"); + break; + }; + break; + case MENUACTION_CTRLDISPLAY: + break; + case MENUACTION_FRAMESYNC: + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(m_PrefsVsyncDisp ? "FEM_ON" : "FEM_OFF"); + break; + case MENUACTION_FRAMELIMIT: + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(m_PrefsFrameLimiter ? "FEM_ON" : "FEM_OFF"); + break; + case MENUACTION_TRAILS: + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(BlurOn ? "FEM_ON" : "FEM_OFF"); + break; + case MENUACTION_SUBTITLES: + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(m_PrefsShowSubtitles ? "FEM_ON" : "FEM_OFF"); + break; + case MENUACTION_WIDESCREEN: + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(m_PrefsUseWideScreen ? "FEM_ON" : "FEM_OFF"); + break; + case MENUACTION_RADIO: + sprintf(gString, "FEA_FM%d", m_PrefsRadioStation); + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(gString); + break; + case MENUACTION_SETDBGFLAG: + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(CTheScripts::DbgFlag ? "FEM_ON" : "FEM_OFF"); + break; + case MENUACTION_SWITCHBIGWHITEDEBUGLIGHT: + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(CTheScripts::DbgFlag ? "FEM_ON" : "FEM_OFF"); + break; + case MENUACTION_INVVERT: + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(MousePointerStateHelper.bInvertVertically ? "FEM_ON" : "FEM_OFF"); + break; + case MENUACTION_SCREENRES: + { + char *res = _psGetVideoModeList()[m_nDisplayVideoMode]; + + if (!res) + res = ""; + + AsciiToUnicode(res, gUString); + textToPrint[MENUCOLUMN_RIGHT] = gUString; + } + break; + case MENUACTION_AUDIOHW: + if (FrontEndMenuManager.m_nPrefsAudio3DProviderIndex == -1) + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEA_NAH"); + else { + char *provider = MusicManager.Get3DProviderName(FrontEndMenuManager.m_nPrefsAudio3DProviderIndex); + AsciiToUnicode(provider, gUString); + textToPrint[MENUCOLUMN_RIGHT] = gUString; + } + break; + case MENUACTION_SPEAKERCONF: + if (FrontEndMenuManager.m_nPrefsAudio3DProviderIndex == -1) + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEA_NAH"); + else { + switch (m_PrefsSpeakers) { + case 0: + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEA_2SP"); + break; + case 1: + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEA_EAR"); + break; + case 2: + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEA_4SP"); + break; + }; + } + break; + case MENUACTION_CTRLMETHOD: + switch (m_ControlMethod) { + case 0: + textToPrint[MENUCOLUMN_LEFT] = TheText.Get("FET_SCN"); + break; + case 1: + textToPrint[MENUCOLUMN_LEFT] = TheText.Get("FET_CCN"); + break; + }; + break; + case MENUACTION_DYNAMICACOUSTIC: + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(m_PrefsDMA ? "FEM_ON" : "FEM_OFF"); + break; + case MENUACTION_MOUSESTEER: + textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(m_bDisableMouseSteering ? "FEM_ON" : "FEM_OFF"); + break; + }; + + CFont::SetDropShadowPosition(MENUDROP_COLOR_SIZE); + CFont::SetDropColor(CRGBA(0, 0, 0, FadeIn(MENUDROP_COLOR_A))); + CFont::SetCentreSize(SCREEN_WIDTH); + CFont::SetWrapx(SCREEN_WIDTH); + CFont::SetRightJustifyWrap(-SCREEN_WIDTH); + + // Set alignment. + CVector2D vecPositions = { 0.0f, 0.0f }; + float fVerticalSpacing; + float fBarSize; + + int SavePageSlot = + m_nCurrScreen == MENUPAGE_CHOOSE_LOAD_SLOT || + m_nCurrScreen == MENUPAGE_CHOOSE_DELETE_SLOT || + m_nCurrScreen == MENUPAGE_CHOOSE_SAVE_SLOT; + + if (SavePageSlot) { + CFont::SetFontStyle(FONT_BANK); + CFont::SetAlignment(ALIGN_LEFT); + CFont::SetScale(SCREEN_SCALE_X(0.45f), SCREEN_SCALE_Y(0.7f)); + fVerticalSpacing = MENUCOLUMN_SPACING_MIN; + fBarSize = MENUSELECT_BOX_MIN; + + vecPositions.x = SCREEN_SCALE_X(MENUCOLUMN_SAVE_X); + vecPositions.y = (SCREEN_HEIGHT / 2) - SCREEN_SCALE_Y(MENUCOLUMN_SAVE_Y); + } + else { + CFont::SetFontStyle(FONT_HEADING); + + int LeftMenuColumn = + m_nCurrScreen == MENUPAGE_SOUND_SETTINGS || + m_nCurrScreen == MENUPAGE_GRAPHICS_SETTINGS || + m_nCurrScreen == MENUPAGE_MOUSE_CONTROLS; + + if (LeftMenuColumn) { + CFont::SetAlignment(ALIGN_LEFT); + CFont::SetScale(SCREEN_SCALE_X(0.55f), SCREEN_SCALE_Y(0.8f)); + fVerticalSpacing = MENUCOLUMN_SPACING_MIN; + fBarSize = MENUSELECT_BOX_MIN; + } + else { + CFont::SetAlignment(ALIGN_CENTER); + CFont::SetScale(SCREEN_SCALE_X(0.75f), SCREEN_SCALE_Y(0.9f)); + fVerticalSpacing = MENUCOLUMN_SPACING_MAX; + fBarSize = MENUSELECT_BOX_MAX; + } + + // Set positions. + if (CFont::GetDetails().centre) + vecPositions.x = SCREEN_WIDTH / 2; + else + vecPositions.x = SCREEN_SCALE_X(MENUCOLUMN_POS_X); + + switch (m_nCurrScreen) { + case MENUPAGE_BRIEFS: + case MENUPAGE_STATS: + vecPositions.y = SCREEN_SCALE_FROM_BOTTOM(MENUCOLUMN_FEDS); + break; + case MENUPAGE_SOUND_SETTINGS: + vecPositions.y = (SCREEN_HEIGHT / 2) - SCREEN_SCALE_Y(MENUCOLUMN_MAX_Y); + + if (i > 5) + vecPositions.y += SCREEN_SCALE_Y(MENURADIO_ICON_H * 1.16f); + break; + case MENUPAGE_LANGUAGE_SETTINGS: + vecPositions.y = (SCREEN_HEIGHT / 2) - SCREEN_SCALE_Y(MENUCOLUMN_MIN_Y); + break; + case MENUPAGE_GRAPHICS_SETTINGS: + vecPositions.y = (SCREEN_HEIGHT / 2) - SCREEN_SCALE_Y(MENUCOLUMN_MAX_Y); + break; + case MENUPAGE_OPTIONS: + vecPositions.y = (SCREEN_HEIGHT / 2) - SCREEN_SCALE_Y(MENUCOLUMN_MID_Y); + break; + case MENUPAGE_PAUSE_MENU: + vecPositions.y = (SCREEN_HEIGHT / 2) - SCREEN_SCALE_Y(MENUCOLUMN_PAUSE_Y); + break; + case MENUPAGE_NEW_GAME: + vecPositions.y = (SCREEN_HEIGHT / 2) - SCREEN_SCALE_Y(MENUCOLUMN_MID_Y); + break; + case MENUPAGE_START_MENU: + vecPositions.y = (SCREEN_HEIGHT / 2) - SCREEN_SCALE_Y(MENUCOLUMN_START_Y); + break; + default: + vecPositions.y = (SCREEN_HEIGHT / 2) - SCREEN_SCALE_Y(MENUCOLUMN_MID_Y); + break; + }; + } + + if (i > 0) + vecPositions.y += SCREEN_SCALE_Y(fVerticalSpacing * i); + + // Set color and draw selection bar. + if (i == m_nCurrOption && m_nMenuFadeAlpha >= 255) { + CFont::SetColor(CRGBA(255, 217, 106, FadeIn(255))); + CSprite2d::DrawRect(CRect(SCREEN_STRETCH_X(11.0f), vecPositions.y - SCREEN_STRETCH_Y(fBarSize * 0.13f), SCREEN_STRETCH_FROM_RIGHT(11.0f), vecPositions.y + SCREEN_STRETCH_Y(fBarSize)), CRGBA(100, 200, 50, 50)); + } + else + CFont::SetColor(CRGBA(235, 170, 50, FadeIn(255))); + + // Draw + if (textToPrint[MENUCOLUMN_LEFT]) + CFont::PrintString(vecPositions.x, vecPositions.y, textToPrint[MENUCOLUMN_LEFT]); + + if (textToPrint[MENUCOLUMN_RIGHT]) { + CFont::SetAlignment(ALIGN_RIGHT); + CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(SavePageSlot ? MENUCOLUMN_SAVE_X : MENUCOLUMN_POS_X), vecPositions.y, textToPrint[MENUCOLUMN_RIGHT]); + } + + // Mouse support. + bool bIsMouseInPosition = false; + if (m_nMenuFadeAlpha >= 255) { + CVector2D vecInputSize = { SCREEN_SCALE_X(20.0f), SCREEN_SCALE_FROM_RIGHT(20.0f) }; + if (m_bShowMouse && + ((CheckHover(vecInputSize.x, vecInputSize.y, vecPositions.y, vecPositions.y + SCREEN_STRETCH_Y(20.0f))))) + bIsMouseInPosition = true; + else + bIsMouseInPosition = false; + + if (bIsMouseInPosition) { + if (m_nCurrOption != i) { + m_nCurrOption = i; + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_DENIED, 0); + } + + m_nCurrentInput = m_nCurrOption; + + if (CPad::GetPad(0)->NewMouseControllerState.LMB && !CPad::GetPad(0)->OldMouseControllerState.LMB) + m_nHoverOption = IGNORE_OPTION; + else + m_nHoverOption = ACTIVATE_OPTION; + } + } + + // Sliders + // TODO: CheckHover + switch (aScreens[m_nCurrScreen].m_aEntries[i].m_Action) { + case MENUACTION_BRIGHTNESS: + DisplaySlider(SCREEN_SCALE_FROM_RIGHT(MENUSLIDER_X), vecPositions.y - SCREEN_SCALE_Y(3.0f), SCREEN_SCALE_Y(2.0f), SCREEN_SCALE_Y(18.0f), SCREEN_SCALE_X(256.0f), m_PrefsBrightness/512.0f); + break; + case MENUACTION_DRAWDIST: + DisplaySlider(SCREEN_SCALE_FROM_RIGHT(MENUSLIDER_X), vecPositions.y - SCREEN_SCALE_Y(3.0f), SCREEN_SCALE_Y(2.0f), SCREEN_SCALE_Y(18.0f), SCREEN_SCALE_X(256.0f), (m_PrefsLOD - 0.8f) * 1.0f); + break; + case MENUACTION_MUSICVOLUME: + DisplaySlider(SCREEN_SCALE_FROM_RIGHT(MENUSLIDER_X), vecPositions.y - SCREEN_SCALE_Y(3.0f), SCREEN_SCALE_Y(2.0f), SCREEN_SCALE_Y(18.0f), SCREEN_SCALE_X(256.0f), m_PrefsMusicVolume/128.0f); + break; + case MENUACTION_SFXVOLUME: + DisplaySlider(SCREEN_SCALE_FROM_RIGHT(MENUSLIDER_X), vecPositions.y - SCREEN_SCALE_Y(3.0f), SCREEN_SCALE_Y(2.0f), SCREEN_SCALE_Y(18.0f), SCREEN_SCALE_X(256.0f), m_PrefsSfxVolume/128.0f); + break; + case MENUACTION_MOUSESENS: + DisplaySlider(SCREEN_SCALE_FROM_RIGHT(MENUSLIDER_X), vecPositions.y - SCREEN_SCALE_Y(3.0f), SCREEN_SCALE_Y(2.0f), SCREEN_SCALE_Y(18.0f), SCREEN_SCALE_X(256.0f), TheCamera.m_fMouseAccelHorzntl * 200.0f); + break; + }; + + // Radio icons. + float fIconSpacing = 59.52f; + if (m_nCurrScreen == MENUPAGE_SOUND_SETTINGS) { + for (int i = 0; i < POLICE_RADIO; i++) { + + if (i == MSX_FM) + fIconSpacing -= 1.5f; + + if (i < USERTRACK) + m_aFrontEndSprites[i + FE_RADIO1].Draw(SCREEN_STRETCH_X(MENURADIO_ICON_X + fIconSpacing * i), (SCREEN_HEIGHT / 2) - SCREEN_SCALE_Y(MENURADIO_ICON_Y), SCREEN_SCALE_X(MENURADIO_ICON_W), SCREEN_SCALE_Y(MENURADIO_ICON_H), i == m_PrefsRadioStation ? CRGBA(255, 255, 255, 255) : CRGBA(225, 0, 0, 170)); + if (i > CHATTERBOX) + m_aMenuSprites[MENUSPRITE_MP3LOGO].Draw(SCREEN_STRETCH_X(MENURADIO_ICON_X + fIconSpacing * i), (SCREEN_HEIGHT / 2) - SCREEN_SCALE_Y(MENURADIO_ICON_Y), SCREEN_SCALE_X(MENURADIO_ICON_W), SCREEN_SCALE_Y(MENURADIO_ICON_H), i == m_PrefsRadioStation ? CRGBA(255, 255, 255, 255) : CRGBA(225, 0, 0, DMAudio.IsMP3RadioChannelAvailable() ? 170 : 25)); + } + } + + // Helpers + if (!strcmp(aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_EntryName, "FED_RES")) { + if (m_nDisplayVideoMode == m_nPrefsVideoMode) { + if (m_nHelperTextMsgId == 1) + ResetHelperText(); + } + else + SetHelperText(1); + } + else { + if (m_nDisplayVideoMode != m_nPrefsVideoMode) { + m_nDisplayVideoMode = m_nPrefsVideoMode; + SetHelperText(3); + } + } + + switch (m_nCurrScreen) { + case MENUPAGE_CONTROLLER_SETTINGS: + case MENUPAGE_SOUND_SETTINGS: + case MENUPAGE_GRAPHICS_SETTINGS: + case MENUPAGE_SKIN_SELECT: + case MENUPAGE_CONTROLLER_PC: + case MENUPAGE_MOUSE_CONTROLS: + DisplayHelperText(); + break; + }; + } + }; +} +#endif + +#if 1 +WRAPPER void CMenuManager::DrawControllerBound(int, int, int, uint8) { EAXJMP(0x489710); } +#else +void CMenuManager::DrawControllerBound(int, int, int, uint8) +{ + +} +#endif + +#if 1 +WRAPPER void CMenuManager::DrawControllerScreenExtraText(int, int, int) { EAXJMP(0x4892F0); } +#else +void CMenuManager::DrawControllerScreenExtraText(int, int, int) +{ + +} +#endif + +#if 1 +WRAPPER void CMenuManager::DrawControllerSetupScreen() { EAXJMP(0x481210); } +#else +void CMenuManager::DrawControllerSetupScreen() +{ + +} +#endif + +#if 0 +WRAPPER void CMenuManager::DrawFrontEnd(void) { EAXJMP(0x47A540); } +#else +void CMenuManager::DrawFrontEnd() +{ + CFont::SetAlphaFade(255.0f); + + if (m_nCurrScreen == MENUPAGE_NONE) { + m_nMenuFadeAlpha = 0; + + if (m_bGameNotLoaded) + m_nCurrScreen = MENUPAGE_START_MENU; + else + m_nCurrScreen = MENUPAGE_PAUSE_MENU; + } + + if (!m_nCurrOption && aScreens[m_nCurrScreen].m_aEntries[0].m_Action == MENUACTION_LABEL) + m_nCurrOption = MENUROW_1; + + CMenuManager::DrawFrontEndNormal(); + CMenuManager::PrintErrorMessage(); +} +#endif + +#if 0 +WRAPPER void CMenuManager::DrawFrontEndNormal(void) { EAXJMP(0x47A5B0); } +#else +void CMenuManager::DrawFrontEndNormal() +{ + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void *)TRUE); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)FALSE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void *)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void *)rwTEXTUREADDRESSCLAMP); + + CSprite2d::InitPerFrame(); + CFont::InitPerFrame(); + + eMenuSprites previousSprite = MENUSPRITE_MAINMENU; + if (m_nMenuFadeAlpha < 255) { + switch (m_nPrevScreen) { + case MENUPAGE_STATS: + case MENUPAGE_START_MENU: + case MENUPAGE_PAUSE_MENU: + previousSprite = MENUSPRITE_MAINMENU; + break; + case MENUPAGE_NEW_GAME: + case MENUPAGE_CHOOSE_LOAD_SLOT: + case MENUPAGE_CHOOSE_DELETE_SLOT: + case MENUPAGE_NEW_GAME_RELOAD: + case MENUPAGE_LOAD_SLOT_CONFIRM: + case MENUPAGE_DELETE_SLOT_CONFIRM: + case MENUPAGE_EXIT: + previousSprite = MENUSPRITE_SINGLEPLAYER; + break; + case MENUPAGE_MULTIPLAYER_MAIN: + previousSprite = MENUSPRITE_MULTIPLAYER; + break; + case MENUPAGE_MULTIPLAYER_MAP: + case MENUPAGE_MULTIPLAYER_FIND_GAME: + case MENUPAGE_SKIN_SELECT: + case MENUPAGE_KEYBOARD_CONTROLS: + case MENUPAGE_MOUSE_CONTROLS: + previousSprite = MENUSPRITE_FINDGAME; + break; + case MENUPAGE_MULTIPLAYER_CONNECTION: + case MENUPAGE_MULTIPLAYER_MODE: + previousSprite = MENUSPRITE_CONNECTION; + break; + case MENUPAGE_MULTIPLAYER_CREATE: + previousSprite = MENUSPRITE_HOSTGAME; + break; + case MENUPAGE_SKIN_SELECT_OLD: + case MENUPAGE_OPTIONS: + previousSprite = MENUSPRITE_PLAYERSET; + break; + }; + + if (m_nPrevScreen == MENUPAGE_NONE) + CSprite2d::DrawRect(CRect(0.0f, 0.0f, SCREENW, SCREENH), CRGBA(0, 0, 0, 255)); + else + m_aMenuSprites[previousSprite].Draw(CRect(0.0f, 0.0f, SCREENW, SCREENH), CRGBA(255, 255, 255, 255)); + } + + eMenuSprites currentSprite = MENUSPRITE_MAINMENU; + switch (m_nCurrScreen) { + case MENUPAGE_STATS: + case MENUPAGE_START_MENU: + case MENUPAGE_PAUSE_MENU: + currentSprite = MENUSPRITE_MAINMENU; + break; + case MENUPAGE_NEW_GAME: + case MENUPAGE_CHOOSE_LOAD_SLOT: + case MENUPAGE_CHOOSE_DELETE_SLOT: + case MENUPAGE_NEW_GAME_RELOAD: + case MENUPAGE_LOAD_SLOT_CONFIRM: + case MENUPAGE_DELETE_SLOT_CONFIRM: + case MENUPAGE_EXIT: + currentSprite = MENUSPRITE_SINGLEPLAYER; + break; + case MENUPAGE_MULTIPLAYER_MAIN: + currentSprite = MENUSPRITE_MULTIPLAYER; + break; + case MENUPAGE_MULTIPLAYER_MAP: + case MENUPAGE_MULTIPLAYER_FIND_GAME: + case MENUPAGE_SKIN_SELECT: + case MENUPAGE_KEYBOARD_CONTROLS: + case MENUPAGE_MOUSE_CONTROLS: + currentSprite = MENUSPRITE_FINDGAME; + break; + case MENUPAGE_MULTIPLAYER_CONNECTION: + case MENUPAGE_MULTIPLAYER_MODE: + currentSprite = MENUSPRITE_CONNECTION; + break; + case MENUPAGE_MULTIPLAYER_CREATE: + currentSprite = MENUSPRITE_HOSTGAME; + break; + case MENUPAGE_SKIN_SELECT_OLD: + case MENUPAGE_OPTIONS: + currentSprite = MENUSPRITE_PLAYERSET; + break; + }; + + uint32 savedShade; + uint32 savedAlpha; + RwRenderStateGet(rwRENDERSTATESHADEMODE, &savedShade); + RwRenderStateSet(rwRENDERSTATESHADEMODE, reinterpret_cast(rwSHADEMODEGOURAUD)); + RwRenderStateGet(rwRENDERSTATEVERTEXALPHAENABLE, &savedAlpha); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, reinterpret_cast(TRUE)); + if (m_nMenuFadeAlpha >= 255) { + m_aMenuSprites[currentSprite].Draw(CRect(0.0f, 0.0f, SCREENW, SCREENH), CRGBA(255, 255, 255, 255)); + } + else { + if (m_nMenuFadeAlpha < 255) { + m_nMenuFadeAlpha += 0.1f * 255.0f; + + if (m_nMenuFadeAlpha >= 255) + m_nMenuFadeAlpha = 255; + + m_aMenuSprites[currentSprite].Draw(CRect(0.0f, 0.0f, SCREENW, SCREENH), CRGBA(255, 255, 255, m_nMenuFadeAlpha)); + } + else + m_aMenuSprites[currentSprite].Draw(CRect(0.0f, 0.0f, SCREENW, SCREENH), CRGBA(255, 255, 255, 255)); + } + + // GTA LOGO + if (m_nCurrScreen == MENUPAGE_START_MENU || m_nCurrScreen == MENUPAGE_PAUSE_MENU) { + if (CGame::frenchGame || CGame::germanGame || !CGame::nastyGame) + m_aMenuSprites[MENUSPRITE_GTA3LOGO].Draw(CRect((SCREEN_WIDTH / 2) - SCREEN_SCALE_X(115.0f), SCREEN_SCALE_Y(70.0f), (SCREEN_WIDTH / 2) + SCREEN_SCALE_X(115.0f), SCREEN_SCALE_Y(180.0f)), CRGBA(255, 255, 255, FadeIn(255))); + else + m_aMenuSprites[MENUSPRITE_GTALOGO].Draw(CRect((SCREEN_WIDTH / 2) - SCREEN_SCALE_X(95.0f), SCREEN_SCALE_Y(40.0f), (SCREEN_WIDTH / 2) + SCREEN_SCALE_X(95.0f), SCREEN_SCALE_Y(210.0f)), CRGBA(255, 255, 255, FadeIn(255))); + } + RwRenderStateSet(rwRENDERSTATESHADEMODE, reinterpret_cast(savedShade)); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, reinterpret_cast(savedAlpha)); + + switch (m_nCurrScreen) { + case MENUPAGE_SKIN_SELECT: + CMenuManager::DrawPlayerSetupScreen(); + break; + case MENUPAGE_KEYBOARD_CONTROLS: + CMenuManager::DrawControllerSetupScreen(); + break; + default: + CMenuManager::Draw(); + break; + }; + + CFont::DrawFonts(); + + // Draw mouse + if (m_bShowMouse) + m_aMenuSprites[MENUSPRITE_MOUSE].Draw(m_nMousePosX, m_nMousePosY, SCREEN_SCALE_X(60.0f), SCREEN_SCALE_Y(60.0f), CRGBA(255, 255, 255, 255)); +} +#endif + +#if 1 +WRAPPER void CMenuManager::DrawPlayerSetupScreen() { EAXJMP(0x47F2B0); } +#else +void CMenuManager::DrawPlayerSetupScreen() +{ + +} +#endif + +#if 0 +WRAPPER int CMenuManager::FadeIn(int alpha) { EAXJMP(0x48AC60); } +#else +int CMenuManager::FadeIn(int alpha) +{ + if (m_nCurrScreen == MENUPAGE_LOADING_IN_PROGRESS || + m_nCurrScreen == MENUPAGE_SAVING_IN_PROGRESS || + m_nCurrScreen == MENUPAGE_DELETING) return alpha; if (m_nMenuFadeAlpha >= alpha) @@ -47,7 +856,328 @@ int CMenuManager::FadeIn(int alpha) { return m_nMenuFadeAlpha; } +#endif + +#if 1 +WRAPPER void CMenuManager::FilterOutColorMarkersFromString(uint16, CRGBA &) { EAXJMP(0x4889C0); } +#else +void CMenuManager::FilterOutColorMarkersFromString(uint16, CRGBA &) +{ + +} +#endif + +#if 1 +WRAPPER int CMenuManager::GetStartOptionsCntrlConfigScreens() { EAXJMP(0x489270); } +#else +int CMenuManager::GetStartOptionsCntrlConfigScreens() +{ + +} +#endif + +#if 1 +WRAPPER void CMenuManager::InitialiseChangedLanguageSettings() { EAXJMP(0x47A4D0); } +#else +void CMenuManager::InitialiseChangedLanguageSettings() +{ + +} +#endif + +#if 0 +WRAPPER void CMenuManager::LoadAllTextures() { EAXJMP(0x47A230); } +#else +void CMenuManager::LoadAllTextures() +{ + if (!m_bSpritesLoaded) { + CMenuManager::CentreMousePointer(); + DMAudio.ChangeMusicMode(0); + DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_STARTING, 0); + m_nCurrOption = MENUROW_0; + m_PrefsRadioStation = DMAudio.GetRadioInCar(); + + if (DMAudio.IsMP3RadioChannelAvailable()) { + if (CMenuManager::m_PrefsRadioStation > USERTRACK) + CMenuManager::m_PrefsRadioStation = CGeneral::GetRandomNumber() % 10; + } + else if (CMenuManager::m_PrefsRadioStation > CHATTERBOX) + CMenuManager::m_PrefsRadioStation = CGeneral::GetRandomNumber() % 9; + + CFileMgr::SetDir(""); + CTimer::Stop(); + CStreaming::MakeSpaceFor(716800); + CStreaming::ImGonnaUseStreamingMemory(); + CTxdStore::PushCurrentTxd(); + + int frontend = CTxdStore::AddTxdSlot("frontend"); + CTxdStore::LoadTxd(frontend, "MODELS/FRONTEND.TXD"); + CTxdStore::AddRef(frontend); + CTxdStore::SetCurrentTxd(frontend); + CStreaming::IHaveUsedStreamingMemory(); + CTimer::Update(); + + debug("LOAD frontend\n"); + for (int i = 0; i < ARRAY_SIZE(FrontendFilenames); i++) { + m_aFrontEndSprites[i].SetTexture(FrontendFilenames[i]); + m_aFrontEndSprites[i].SetAddressing(rwTEXTUREADDRESSBORDER); + }; + + CTxdStore::PopCurrentTxd(); + + int menu = CTxdStore::AddTxdSlot("menu"); + CTxdStore::LoadTxd(menu, "MODELS/MENU.TXD"); + CTxdStore::AddRef(menu); + CTxdStore::SetCurrentTxd(menu); + + debug("LOAD sprite\n"); + for (int i = 0; i < ARRAY_SIZE(MenuFilenames)/2; i++) { + m_aMenuSprites[i].SetTexture(MenuFilenames[i*2], MenuFilenames[i*2+1]); + m_aMenuSprites[i].SetAddressing(rwTEXTUREADDRESSBORDER); + }; + + CTxdStore::PopCurrentTxd(); + + m_bSpritesLoaded = true; + } +} +#endif + +#if 1 +WRAPPER void CMenuManager::LoadSettings() { EAXJMP(0x488EE0); } +#else +void CMenuManager::LoadSettings() +{ + +} +#endif + +#if 1 +WRAPPER void CMenuManager::MessageScreen(char *) { EAXJMP(0x48B7E0); } +#else +void CMenuManager::MessageScreen(char *) +{ + +} +#endif + +#if 1 +WRAPPER void CMenuManager::PickNewPlayerColour() { EAXJMP(0x488C40); } +#else +void CMenuManager::PickNewPlayerColour() +{ + +} +#endif + +#if 1 +WRAPPER void CMenuManager::PrintBriefs() { EAXJMP(0x484D60); } +#else +void CMenuManager::PrintBriefs() +{ + +} +#endif + +#if 0 +WRAPPER void CMenuManager::PrintErrorMessage() { EAXJMP(0x484F70); } +#else +void CMenuManager::PrintErrorMessage() +{ + if (!CPad::bDisplayNoControllerMessage && !CPad::bObsoleteControllerMessage) + return; + + CSprite2d::DrawRect(CRect(SCREEN_SCALE_X(20.0f), SCREEN_SCALE_Y(140.0f), SCREEN_WIDTH - SCREEN_SCALE_X(20.0f), SCREEN_HEIGHT - SCREEN_SCALE_Y(140.0f)), CRGBA(64, 16, 16, 224)); + CFont::SetFontStyle(FONT_BANK); + CFont::SetBackgroundOff(); + CFont::SetPropOn(); + CFont::SetCentreOff(); + CFont::SetJustifyOn(); + CFont::SetRightJustifyOff(); + CFont::SetBackGroundOnlyTextOn(); + CFont::SetWrapx(SCREEN_WIDTH - 40.0f); + CFont::SetColor(CRGBA(165, 165, 165, 255)); + CFont::SetScale(SCREEN_SCALE_X(0.9f), SCREEN_SCALE_Y(0.9f)); + CFont::PrintString(SCREEN_SCALE_X(40.0f), (SCREEN_HEIGHT / 2) - SCREEN_SCALE_Y(60.0f), TheText.Get(CPad::bDisplayNoControllerMessage ? "NOCONT" : "WRCONT")); + CFont::DrawFonts(); +} +#endif + +#if 1 +WRAPPER void CMenuManager::PrintStats() { EAXJMP(0x482100); } +#else +void CMenuManager::PrintStats() +{ + +} +#endif + + +#if 1 +WRAPPER void CMenuManager::Process(void) { EAXJMP(0x485100); } +#else +void CMenuManager::Process(void) +{ + +} +#endif + +#if 1 +WRAPPER void CMenuManager::ProcessButtonPresses() { EAXJMP(0x4856F0); } +#else +void CMenuManager::ProcessButtonPresses() +{ + +} +#endif + +#if 1 +WRAPPER void CMenuManager::ProcessOnOffMenuOptions() { EAXJMP(0x48AE60); } +#else +void CMenuManager::ProcessOnOffMenuOptions() +{ + +} +#endif + +#if 0 +WRAPPER void CMenuManager::RequestFrontEndShutdown() { EAXJMP(0x488750); } +#else +void CMenuManager::RequestFrontEndShutdown() +{ + m_bShutDownFrontEndRequested = true; + DMAudio.ChangeMusicMode(1); +} +#endif + +#if 0 +WRAPPER void CMenuManager::RequestFrontEndStartUp() { EAXJMP(0x488770); } +#else +void CMenuManager::RequestFrontEndStartUp() +{ + m_bStartUpFrontEndRequested = 1; +} +#endif + +#if 0 +WRAPPER void CMenuManager::ResetHelperText() { EAXJMP(0x48B470); } +#else +void CMenuManager::ResetHelperText() +{ + m_nHelperTextMsgId = 0; + m_nHelperTextAlpha = 300; +} +#endif + +#if 1 +WRAPPER void CMenuManager::SaveLoadFileError_SetUpErrorScreen() { EAXJMP(0x488930); } +#else +void CMenuManager::SaveLoadFileError_SetUpErrorScreen() +{ + +} +#endif + +#if 0 +WRAPPER void CMenuManager::SetHelperText() { EAXJMP(0x48B450); } +#else +void CMenuManager::SetHelperText(int text) +{ + m_nHelperTextMsgId = text; + m_nHelperTextAlpha = 300; +} +#endif + +#if 1 +WRAPPER void CMenuManager::SaveSettings() { EAXJMP(0x488CC0); } +#else +void CMenuManager::SaveSettings() +{ + +} +#endif + +#if 0 +WRAPPER void CMenuManager::ShutdownJustMenu() { EAXJMP(0x488920); } +#else +void CMenuManager::ShutdownJustMenu() +{ + m_bMenuActive = false; + CTimer::EndUserPause(); +} +#endif + +#if 0 +WRAPPER float CMenuManager::StretchX(float) { EAXJMP(0x48ABE0); } +#else +float CMenuManager::StretchX(float x) +{ + if (SCREENW == 640) + return x; + else + return SCREENW * x * 0.0015625f; +} +#endif + +#if 0 +WRAPPER float CMenuManager::StretchY(float) { EAXJMP(0x48AC20); } +#else +float CMenuManager::StretchY(float y) +{ + if (SCREENH == 448) + return y; + else + return SCREENH * y * 0.002232143f; +} +#endif + +#if 0 +WRAPPER void CMenuManager::SwitchMenuOnAndOff() { EAXJMP(0x488790); } +#else +void CMenuManager::SwitchMenuOnAndOff() +{ + +} +#endif + +#if 0 +WRAPPER void CMenuManager::UnloadTextures() { EAXJMP(0x47A440); } +#else +void CMenuManager::UnloadTextures() +{ + if (m_bSpritesLoaded) { + debug("Remove frontend\n"); + for (int i = 0; i < ARRAY_SIZE(FrontendFilenames); ++i) + m_aFrontEndSprites[i].Delete(); + + int frontend = CTxdStore::FindTxdSlot("frontend"); + CTxdStore::RemoveTxdSlot(frontend); + + debug("Remove menu textures\n"); + for (int i = 0; i < ARRAY_SIZE(MenuFilenames)/2; ++i) + m_aMenuSprites[i].Delete(); + + int menu = CTxdStore::FindTxdSlot("menu"); + CTxdStore::RemoveTxdSlot(menu); + + m_bSpritesLoaded = false; + } +} +#endif + +#if 1 +WRAPPER void CMenuManager::WaitForUserCD(void) { EAXJMP(0x48ADD0); } +#else +void CMenuManager::WaitForUserCD() +{ + +} +#endif STARTPATCHES - InjectHook(0x48AC60, &CMenuManager::FadeIn, PATCH_JUMP); + InjectHook(0x47A230, &CMenuManager::LoadAllTextures, PATCH_JUMP); + InjectHook(0x47A440, &CMenuManager::UnloadTextures, PATCH_JUMP); + + for (int i = 1; i < ARRAY_SIZE(aScreens); i++) + Patch(0x611930 + sizeof(CMenuScreen) * i, aScreens[i]); ENDPATCHES \ No newline at end of file diff --git a/src/Frontend.h b/src/Frontend.h index 2e95d582..dd6464b8 100644 --- a/src/Frontend.h +++ b/src/Frontend.h @@ -2,7 +2,45 @@ #include "Sprite2d.h" -enum { +#define MENUHEADER_POS_X 35.0f +#define MENUHEADER_POS_Y 93.0f +#define MENUHEADER_WIDTH 0.84f +#define MENUHEADER_HEIGHT 1.6f + +#define MENUACTION_POS_X 20.0f +#define MENUACTION_POS_Y 37.5f +#define MENUACTION_WIDTH 0.675f +#define MENUACTION_HEIGHT 0.81f + +#define MENUCOLUMN_POS_X MENUHEADER_POS_X + 16.0f +#define MENUCOLUMN_MAX_Y 149.0f +#define MENUCOLUMN_MID_Y 100.0f +#define MENUCOLUMN_MIN_Y 110.0f +#define MENUCOLUMN_PAUSE_Y 25.0f +#define MENUCOLUMN_START_Y 9.0f +#define MENUCOLUMN_FEDS 139.0f + +#define MENUCOLUMN_SAVE_X 121.0f +#define MENUCOLUMN_SAVE_Y 111.0f + +#define MENUCOLUMN_SPACING_MAX 24.0f +#define MENUCOLUMN_SPACING_MIN 20.0f + +#define MENUSELECT_BOX_MAX 20.5f +#define MENUSELECT_BOX_MIN 17.0f + +#define MENURADIO_ICON_X 31.5f +#define MENURADIO_ICON_Y 29.5f +#define MENURADIO_ICON_W 60.0f +#define MENURADIO_ICON_H 60.0f + +#define MENUDROP_COLOR_A 150 +#define MENUDROP_COLOR_SIZE -1 + +#define MENUSLIDER_X 306.0f + +enum eLanguages +{ LANGUAGE_AMERICAN, LANGUAGE_FRENCH, LANGUAGE_GERMAN, @@ -10,66 +48,291 @@ enum { LANGUAGE_SPANISH, }; -enum eMenuScreen { - MENU_NONE = 0, - MENU_STATS = 1, - MENU_NEW_GAME = 2, - MENU_BRIEFS = 3, - MENU_CONTROLLER_SETTINGS = 4, - MENU_SOUND_SETTINGS = 5, - MENU_GRAPHICS_SETTINGS = 6, - MENU_LANGUAGE_SETTINGS = 7, - MENU_CHOOSE_LOAD_SLOT = 8, - MENU_CHOOSE_DELETE_SLOT = 9, - MENU_NEW_GAME_RELOAD = 10, - MENU_LOAD_SLOT_CONFIRM = 11, - MENU_DELETE_SLOT_CONFIRM = 12, - MENU_13 = 13, - MENU_LOADING_IN_PROGRESS = 14, - MENU_DELETING_IN_PROGRESS = 15, - MENU_16 = 16, - MENU_DELETE_FAILED = 17, - MENU_DEBUG_MENU = 18, - MENU_MEMORY_CARD_1 = 19, - MENU_MEMORY_CARD_2 = 20, - MENU_MULTIPLAYER_MAIN = 21, - MENU_SAVE_FAILED_1 = 22, - MENU_SAVE_FAILED_2 = 23, - MENU_SAVE = 24, - MENU_NO_MEMORY_CARD = 25, - MENU_CHOOSE_SAVE_SLOT = 26, - MENU_SAVE_OVERWRITE_CONFIRM = 27, - MENU_MULTIPLAYER_MAP = 28, - MENU_MULTIPLAYER_CONNECTION = 29, - MENU_MULTIPLAYER_FIND_GAME = 30, - MENU_MULTIPLAYER_MODE = 31, - MENU_MULTIPLAYER_CREATE = 32, - MENU_MULTIPLAYER_START = 33, - MENU_SKIN_SELECT_OLD = 34, - MENU_CONTROLLER_PC = 35, - MENU_CONTROLLER_PC_OLD1 = 36, - MENU_CONTROLLER_PC_OLD2 = 37, - MENU_CONTROLLER_PC_OLD3 = 38, - MENU_CONTROLLER_PC_OLD4 = 39, - MENU_CONTROLLER_DEBUG = 40, - MENU_OPTIONS = 41, - MENU_EXIT = 42, - MENU_SAVING_IN_PROGRESS = 43, - MENU_SAVE_SUCCESSFUL = 44, - MENU_DELETING = 45, - MENU_DELETE_SUCCESS = 46, - MENU_SAVE_FAILED = 47, - MENU_LOAD_FAILED = 48, - MENU_LOAD_FAILED_2 = 49, - MENU_FILTER_GAME = 50, - MENU_START_MENU = 51, - MENU_PAUSE_MENU = 52, - MENU_CHOOSE_MODE = 53, - MENU_SKIN_SELECT = 54, - MENU_KEYBOARD_CONTROLS = 55, - MENU_MOUSE_CONTROLS = 56, - MENU_57 = 57, - MENU_58 = 58, +enum eFrontendSprites +{ + FE2_MAINPANEL_UL, + FE2_MAINPANEL_UR, + FE2_MAINPANEL_DL, + FE2_MAINPANEL_DR, + FE2_MAINPANEL_DR2, + FE2_TABACTIVE, + FE_ICONBRIEF, + FE_ICONSTATS, + FE_ICONCONTROLS, + FE_ICONSAVE, + FE_ICONAUDIO, + FE_ICONDISPLAY, + FE_ICONLANGUAGE, + FE_CONTROLLER, + FE_CONTROLLERSH, + FE_ARROWS1, + FE_ARROWS2, + FE_ARROWS3, + FE_ARROWS4, + FE_RADIO1, + FE_RADIO2, + FE_RADIO3, + FE_RADIO4, + FE_RADIO5, + FE_RADIO6, + FE_RADIO7, + FE_RADIO8, + FE_RADIO9, +}; + +enum eMenuSprites +{ + MENUSPRITE_CONNECTION, + MENUSPRITE_FINDGAME, + MENUSPRITE_HOSTGAME, + MENUSPRITE_MAINMENU, + MENUSPRITE_PLAYERSET, + MENUSPRITE_SINGLEPLAYER, + MENUSPRITE_MULTIPLAYER, + MENUSPRITE_DMALOGO, + MENUSPRITE_GTALOGO, + MENUSPRITE_RSTARLOGO, + MENUSPRITE_GAMESPY, + MENUSPRITE_MOUSE, + MENUSPRITE_MOUSET, + MENUSPRITE_MP3LOGO, + MENUSPRITE_DOWNOFF, + MENUSPRITE_DOWNON, + MENUSPRITE_UPOFF, + MENUSPRITE_UPON, + MENUSPRITE_GTA3LOGO, +}; + +enum eSaveSlot +{ + SAVESLOT_NONE, + SAVESLOT_0, + SAVESLOT_1, + SAVESLOT_2, + SAVESLOT_3, + SAVESLOT_4, + SAVESLOT_5, + SAVESLOT_6, + SAVESLOT_7, + SAVESLOT_8, + SAVESLOT_LABEL = 36 +}; + +enum eMenuScreen +{ + MENUPAGE_DISABLED = -1, + MENUPAGE_NONE = 0, + MENUPAGE_STATS = 1, + MENUPAGE_NEW_GAME = 2, + MENUPAGE_BRIEFS = 3, + MENUPAGE_CONTROLLER_SETTINGS = 4, + MENUPAGE_SOUND_SETTINGS = 5, + MENUPAGE_GRAPHICS_SETTINGS = 6, + MENUPAGE_LANGUAGE_SETTINGS = 7, + MENUPAGE_CHOOSE_LOAD_SLOT = 8, + MENUPAGE_CHOOSE_DELETE_SLOT = 9, + MENUPAGE_NEW_GAME_RELOAD = 10, + MENUPAGE_LOAD_SLOT_CONFIRM = 11, + MENUPAGE_DELETE_SLOT_CONFIRM = 12, + MENUPAGE_13 = 13, + MENUPAGE_LOADING_IN_PROGRESS = 14, + MENUPAGE_DELETING_IN_PROGRESS = 15, + MENUPAGE_16 = 16, + MENUPAGE_DELETE_FAILED = 17, + MENUPAGE_DEBUG_MENU = 18, + MENUPAGE_MEMORY_CARD_1 = 19, + MENUPAGE_MEMORY_CARD_2 = 20, + MENUPAGE_MULTIPLAYER_MAIN = 21, + MENUPAGE_SAVE_FAILED_1 = 22, + MENUPAGE_SAVE_FAILED_2 = 23, + MENUPAGE_SAVE = 24, + MENUPAGE_NO_MEMORY_CARD = 25, + MENUPAGE_CHOOSE_SAVE_SLOT = 26, + MENUPAGE_SAVE_OVERWRITE_CONFIRM = 27, + MENUPAGE_MULTIPLAYER_MAP = 28, + MENUPAGE_MULTIPLAYER_CONNECTION = 29, + MENUPAGE_MULTIPLAYER_FIND_GAME = 30, + MENUPAGE_MULTIPLAYER_MODE = 31, + MENUPAGE_MULTIPLAYER_CREATE = 32, + MENUPAGE_MULTIPLAYER_START = 33, + MENUPAGE_SKIN_SELECT_OLD = 34, + MENUPAGE_CONTROLLER_PC = 35, + MENUPAGE_CONTROLLER_PC_OLD1 = 36, + MENUPAGE_CONTROLLER_PC_OLD2 = 37, + MENUPAGE_CONTROLLER_PC_OLD3 = 38, + MENUPAGE_CONTROLLER_PC_OLD4 = 39, + MENUPAGE_CONTROLLER_DEBUG = 40, + MENUPAGE_OPTIONS = 41, + MENUPAGE_EXIT = 42, + MENUPAGE_SAVING_IN_PROGRESS = 43, + MENUPAGE_SAVE_SUCCESSFUL = 44, + MENUPAGE_DELETING = 45, + MENUPAGE_DELETE_SUCCESS = 46, + MENUPAGE_SAVE_FAILED = 47, + MENUPAGE_LOAD_FAILED = 48, + MENUPAGE_LOAD_FAILED_2 = 49, + MENUPAGE_FILTER_GAME = 50, + MENUPAGE_START_MENU = 51, + MENUPAGE_PAUSE_MENU = 52, + MENUPAGE_CHOOSE_MODE = 53, + MENUPAGE_SKIN_SELECT = 54, + MENUPAGE_KEYBOARD_CONTROLS = 55, + MENUPAGE_MOUSE_CONTROLS = 56, + MENUPAGE_57 = 57, + MENUPAGE_58 = 58, + MENUPAGES +}; + +enum eMenuAction +{ + MENUACTION_NOTHING, + MENUACTION_LABEL, + MENUACTION_CHANGEMENU, + MENUACTION_CTRLVIBRATION, + MENUACTION_CTRLCONFIG, + MENUACTION_CTRLDISPLAY, + MENUACTION_FRAMESYNC, + MENUACTION_FRAMELIMIT, + MENUACTION_TRAILS, + MENUACTION_SUBTITLES, + MENUACTION_WIDESCREEN, + MENUACTION_BRIGHTNESS, + MENUACTION_DRAWDIST, + MENUACTION_MUSICVOLUME, + MENUACTION_SFXVOLUME, + MENUACTION_UNK15, + MENUACTION_RADIO, + MENUACTION_LANG_ENG, + MENUACTION_LANG_FRE, + MENUACTION_LANG_GER, + MENUACTION_LANG_ITA, + MENUACTION_LANG_SPA, + MENUACTION_UPDATESAVE, + MENUACTION_CHECKSAVE, + MENUACTION_UNK24, + MENUACTION_NEWGAME, + MENUACTION_RELOADIDE, + MENUACTION_RELOADIPL, + MENUACTION_SETDBGFLAG, + MENUACTION_SWITCHBIGWHITEDEBUGLIGHT, + MENUACTION_PEDROADGROUPS, + MENUACTION_CARROADGROUPS, + MENUACTION_COLLISIONPOLYS, + MENUACTION_REGMEMCARD1, + MENUACTION_TESTFORMATMEMCARD1, + MENUACTION_TESTUNFORMATMEMCARD1, + MENUACTION_CREATEROOTDIR, + MENUACTION_CREATELOADICONS, + MENUACTION_FILLWITHGUFF, + MENUACTION_SAVEONLYTHEGAME, + MENUACTION_SAVEGAME, + MENUACTION_SAVEGAMEUNDERGTA, + MENUACTION_CREATECOPYPROTECTED, + MENUACTION_TESTSAVE, + MENUACTION_TESTLOAD, + MENUACTION_TESTDELETE, + MENUACTION_PARSEHEAP, + MENUACTION_SHOWCULL, + MENUACTION_MEMCARDSAVECONFIRM, + MENUACTION_UPDATEMEMCARDSAVE, + MENUACTION_UNK50, + MENUACTION_DEBUGSTREAM, + MENUACTION_MPMAP_LIBERTY, + MENUACTION_MPMAP_REDLIGHT, + MENUACTION_MPMAP_CHINATOWN, + MENUACTION_MPMAP_TOWER, + MENUACTION_MPMAP_SEWER, + MENUACTION_MPMAP_INDUSTPARK, + MENUACTION_MPMAP_DOCKS, + MENUACTION_MPMAP_STAUNTON, + MENUACTION_MPMAP_DEATHMATCH1, + MENUACTION_MPMAP_DEATHMATCH2, + MENUACTION_MPMAP_TEAMDEATH1, + MENUACTION_MPMAP_TEAMDEATH2, + MENUACTION_MPMAP_STASH, + MENUACTION_MPMAP_CAPTURE, + MENUACTION_MPMAP_RATRACE, + MENUACTION_MPMAP_DOMINATION, + MENUACTION_STARTMP, + MENUACTION_UNK69, + MENUACTION_UNK70, + MENUACTION_FINDMP, + MENUACTION_REDEFCTRL, + MENUACTION_UNK73, + MENUACTION_INITMP, + MENUACTION_MP_PLAYERCOLOR, + MENUACTION_MP_PLAYERNAME, + MENUACTION_MP_GAMENAME, + MENUACTION_GETKEY, + MENUACTION_SHOWHEADBOB, + MENUACTION_UNK80, + MENUACTION_INVVERT, + MENUACTION_CANCLEGAME, + MENUACTION_MP_PLAYERNUMBER, + MENUACTION_MOUSESENS, + MENUACTION_CHECKMPGAMES, + MENUACTION_CHECKMPPING, + MENUACTION_MP_SERVER, + MENUACTION_MP_MAP, + MENUACTION_MP_GAMETYPE, + MENUACTION_MP_LAN, + MENUACTION_MP_INTERNET, + MENUACTION_RESUME, + MENUACTION_DONTCANCLE, + MENUACTION_SCREENRES, + MENUACTION_AUDIOHW, + MENUACTION_SPEAKERCONF, + MENUACTION_PLAYERSETUP, + MENUACTION_RESTOREDEF, + MENUACTION_CTRLMETHOD, + MENUACTION_DYNAMICACOUSTIC, + MENUACTION_LOADRADIO, + MENUACTION_MOUSESTEER, + MENUACTION_UNK103, + MENUACTION_UNK104, + MENUACTION_UNK105, + MENUACTION_UNK106, + MENUACTION_UNK107, + MENUACTION_UNK108, + MENUACTION_UNK109, + MENUACTION_UNK110, +}; + +enum eCheckHover +{ + ACTIVATE_OPTION = 2, + IGNORE_OPTION = 42, +}; + +enum eMenuColumns +{ + MENUCOLUMN_LEFT, + MENUCOLUMN_CENTER, + MENUCOLUMN_RIGHT, + MENUCOLUMNS, +}; + +enum eMenuRow +{ + MENUROW_0, + MENUROW_1, + MENUROW_2, + MENUROW_3, + MENUROW_4, + MENUROW_5, + MENUROW_6, + MENUROW_7, + MENUROW_8, + MENUROW_9, + MENUROW_10, + MENUROW_11, + MENUROW_12, + MENUROW_13, + MENUROW_14, + MENUROW_15, + MENUROW_16, + MENUROW_17, + MENUROWS, }; struct tSkinInfo @@ -81,6 +344,21 @@ struct tSkinInfo int field_304; }; +struct CMenuScreen +{ + char m_ScreenName[8]; + int32 m_PreviousPage[3]; // eMenuScreen + int32 m_ParentEntry[2]; // eMenuRow + + struct CMenuEntry + { + int32 m_Action; // eMenuAction + char m_EntryName[8]; + int32 m_SaveSlot; // eSaveSlot + int32 m_TargetMenu; // eMenuScreen + } m_aEntries[MENUROWS]; +}; + class CMenuManager { public: @@ -136,10 +414,10 @@ public: int m_nHelperTextAlpha; int m_nMouseOldPosX; int m_nMouseOldPosY; - int field_544; + int m_nHoverOption; int m_nCurrScreen; int m_nCurrOption; - int field_550; + int m_nCurrentInput; int m_nPrevScreen; int field_558; int m_nCurrSaveSlot; @@ -165,17 +443,52 @@ public: static int8 &m_bFrontEnd_ReloadObrTxtGxt; static int32 &m_PrefsMusicVolume; static int32 &m_PrefsSfxVolume; - - static bool &m_PrefsAllowNastyGame; static bool &m_bStartUpFrontEndRequested; + static bool &m_bShutDownFrontEndRequested; + static bool &m_PrefsAllowNastyGame; - void Process(void); - void DrawFrontEnd(void); - void UnloadTextures(void); - void LoadAllTextures(void); - void LoadSettings(void); - void WaitForUserCD(void); +public: + void BuildStatLine(char *, void *, uint16, void *); + static void CentreMousePointer(); + void CheckCodesForControls(int, int); + bool CheckHover(int x1, int x2, int y1, int y2); + int CostructStatLine(int); + void DisplayHelperText(); + float DisplaySlider(float, float, float, float, float, float); + void DoSettingsBeforeStartingAGame(); + void Draw(); + void DrawControllerBound(int, int, int, uint8); + void DrawControllerScreenExtraText(int, int, int); + void DrawControllerSetupScreen(); + void DrawFrontEnd(); + void DrawFrontEndNormal(); + void DrawPlayerSetupScreen(); int FadeIn(int alpha); + void FilterOutColorMarkersFromString(uint16, CRGBA &); + int GetStartOptionsCntrlConfigScreens(); + static void InitialiseChangedLanguageSettings(); + void LoadAllTextures(); + void LoadSettings(); + static void MessageScreen(char *); + static void PickNewPlayerColour(); + void PrintBriefs(); + static void PrintErrorMessage(); + void PrintStats(); + void Process(); + void ProcessButtonPresses(); + void ProcessOnOffMenuOptions(); + static void RequestFrontEndShutdown(); + static void RequestFrontEndStartUp(); + void ResetHelperText(); + void SaveLoadFileError_SetUpErrorScreen(); + void SaveSettings(); + void SetHelperText(int text); + void ShutdownJustMenu(); + static float StretchX(float); + static float StretchY(float ); + void SwitchMenuOnAndOff(); + void UnloadTextures(); + void WaitForUserCD(); }; static_assert(sizeof(CMenuManager) == 0x564, "CMenuManager: error"); diff --git a/src/MenuScreens.h b/src/MenuScreens.h new file mode 100644 index 00000000..640952ed --- /dev/null +++ b/src/MenuScreens.h @@ -0,0 +1,380 @@ +#pragma once + +const CMenuScreen aScreens[] = { + // MENUPAGE_NONE = 0 + { "", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, }, + + // MENUPAGE_STATS = 1 + { "FET_STA", MENUPAGE_NONE, MENUPAGE_NONE, MENUPAGE_NONE, MENUROW_5, MENUROW_2, + MENUACTION_CHANGEMENU, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, + }, + + // MENUPAGE_NEW_GAME = 2 + { "FET_SGA", MENUPAGE_NONE, MENUPAGE_NONE, MENUPAGE_NONE, MENUROW_0, MENUROW_1, + MENUACTION_CHANGEMENU, "FES_SNG", SAVESLOT_NONE, MENUPAGE_NEW_GAME_RELOAD, + MENUACTION_UPDATESAVE, "GMLOAD", SAVESLOT_NONE, MENUPAGE_CHOOSE_LOAD_SLOT, + MENUACTION_CHANGEMENU, "FES_DGA", SAVESLOT_NONE, MENUPAGE_CHOOSE_DELETE_SLOT, + MENUACTION_CHANGEMENU, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, + }, + + // MENUPAGE_BRIEFS = 3 + { "FET_BRE", MENUPAGE_NONE, MENUPAGE_NONE, MENUPAGE_NONE, MENUROW_6, MENUROW_3, + MENUACTION_CHANGEMENU, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, + }, + + // MENU_CONTROLLER_SETTINGS = 4 + { "FET_CON", MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_SOUND_SETTINGS = 5 + { "FET_AUD", MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, MENUROW_1, MENUROW_1, + MENUACTION_MUSICVOLUME, "FEA_MUS", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, + MENUACTION_SFXVOLUME, "FEA_SFX", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, + MENUACTION_AUDIOHW, "FEA_3DH", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, + MENUACTION_SPEAKERCONF, "FEA_SPK", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, + MENUACTION_DYNAMICACOUSTIC, "FET_DAM", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, + MENUACTION_RADIO, "FEA_RSS", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, + MENUACTION_RESTOREDEF, "FET_DEF", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, + MENUACTION_CHANGEMENU, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, + }, + + // MENUPAGE_GRAPHICS_SETTINGS = 6 + { "FET_DIS", MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, MENUROW_2, MENUROW_2, + MENUACTION_BRIGHTNESS, "FED_BRI", SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS, + MENUACTION_DRAWDIST, "FEM_LOD", SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS, + MENUACTION_FRAMESYNC, "FEM_VSC", SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS, + MENUACTION_FRAMELIMIT, "FEM_FRM", SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS, + MENUACTION_TRAILS, "FED_TRA", SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS, + MENUACTION_SUBTITLES, "FED_SUB", SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS, + MENUACTION_WIDESCREEN, "FED_WIS", SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS, + MENUACTION_SCREENRES, "FED_RES", SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS, + MENUACTION_RESTOREDEF, "FET_DEF", SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS, + MENUACTION_CHANGEMENU, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, + }, + + // MENUPAGE_LANGUAGE_SETTINGS = 7 + { "FET_LAN", MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, MENUROW_3, MENUROW_3, + MENUACTION_LANG_ENG, "FEL_ENG", SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS, + MENUACTION_LANG_FRE, "FEL_FRE", SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS, + MENUACTION_LANG_GER, "FEL_GER", SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS, + MENUACTION_LANG_ITA, "FEL_ITA", SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS, + MENUACTION_LANG_SPA, "FEL_SPA", SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS, + MENUACTION_CHANGEMENU, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, + }, + + // MENUPAGE_CHOOSE_LOAD_SLOT = 8 + { "FET_LG", MENUPAGE_NEW_GAME, MENUPAGE_NEW_GAME, MENUPAGE_NEW_GAME, MENUROW_1, MENUROW_1, + MENUACTION_CHANGEMENU, "FESZ_CA", SAVESLOT_NONE, MENUPAGE_NEW_GAME, + MENUACTION_CHECKSAVE, "FEM_SL1", SAVESLOT_1, MENUPAGE_LOAD_SLOT_CONFIRM, + MENUACTION_CHECKSAVE, "FEM_SL2", SAVESLOT_2, MENUPAGE_LOAD_SLOT_CONFIRM, + MENUACTION_CHECKSAVE, "FEM_SL3", SAVESLOT_3, MENUPAGE_LOAD_SLOT_CONFIRM, + MENUACTION_CHECKSAVE, "FEM_SL4", SAVESLOT_4, MENUPAGE_LOAD_SLOT_CONFIRM, + MENUACTION_CHECKSAVE, "FEM_SL5", SAVESLOT_5, MENUPAGE_LOAD_SLOT_CONFIRM, + MENUACTION_CHECKSAVE, "FEM_SL6", SAVESLOT_6, MENUPAGE_LOAD_SLOT_CONFIRM, + MENUACTION_CHECKSAVE, "FEM_SL7", SAVESLOT_7, MENUPAGE_LOAD_SLOT_CONFIRM, + MENUACTION_CHECKSAVE, "FEM_SL8", SAVESLOT_8, MENUPAGE_LOAD_SLOT_CONFIRM, + }, + + // MENUPAGE_CHOOSE_DELETE_SLOT = 9 + { "FET_DG", MENUPAGE_NEW_GAME, MENUPAGE_NEW_GAME, MENUPAGE_NEW_GAME, MENUROW_2, MENUROW_2, + MENUACTION_CHANGEMENU, "FESZ_CA", SAVESLOT_NONE, MENUPAGE_NEW_GAME, + MENUACTION_CHANGEMENU, "FEM_SL1", SAVESLOT_1, MENUPAGE_DELETE_SLOT_CONFIRM, + MENUACTION_CHANGEMENU, "FEM_SL2", SAVESLOT_2, MENUPAGE_DELETE_SLOT_CONFIRM, + MENUACTION_CHANGEMENU, "FEM_SL3", SAVESLOT_3, MENUPAGE_DELETE_SLOT_CONFIRM, + MENUACTION_CHANGEMENU, "FEM_SL4", SAVESLOT_4, MENUPAGE_DELETE_SLOT_CONFIRM, + MENUACTION_CHANGEMENU, "FEM_SL5", SAVESLOT_5, MENUPAGE_DELETE_SLOT_CONFIRM, + MENUACTION_CHANGEMENU, "FEM_SL6", SAVESLOT_6, MENUPAGE_DELETE_SLOT_CONFIRM, + MENUACTION_CHANGEMENU, "FEM_SL7", SAVESLOT_7, MENUPAGE_DELETE_SLOT_CONFIRM, + MENUACTION_CHANGEMENU, "FEM_SL8", SAVESLOT_8, MENUPAGE_DELETE_SLOT_CONFIRM, + }, + + // MENUPAGE_NEW_GAME_RELOAD = 10 + { "FET_NG", MENUPAGE_NEW_GAME, MENUPAGE_NEW_GAME, MENUPAGE_NEW_GAME, MENUROW_0, MENUROW_0, + MENUACTION_LABEL, "FESZ_QR", SAVESLOT_NONE, MENUPAGE_NONE, + MENUACTION_CHANGEMENU, "FEM_NO", SAVESLOT_NONE, MENUPAGE_NEW_GAME, + MENUACTION_NEWGAME, "FEM_YES", SAVESLOT_NONE, MENUPAGE_NEW_GAME_RELOAD, + }, + + // MENUPAGE_LOAD_SLOT_CONFIRM = 11 + { "FET_LG", MENUPAGE_CHOOSE_LOAD_SLOT, MENUPAGE_CHOOSE_LOAD_SLOT, MENUPAGE_CHOOSE_LOAD_SLOT, MENUROW_0, MENUROW_0, + MENUACTION_LABEL, "FESZ_QL", SAVESLOT_NONE, MENUPAGE_NONE, + MENUACTION_CHANGEMENU, "FEM_NO", SAVESLOT_NONE, MENUPAGE_CHOOSE_LOAD_SLOT, + MENUACTION_CHANGEMENU, "FEM_YES", SAVESLOT_NONE, MENUPAGE_LOADING_IN_PROGRESS, + }, + + // MENUPAGE_DELETE_SLOT_CONFIRM = 12 + { "FET_DG", MENUPAGE_CHOOSE_DELETE_SLOT, MENUPAGE_CHOOSE_DELETE_SLOT, MENUPAGE_CHOOSE_DELETE_SLOT, MENUROW_0, MENUROW_0, + MENUACTION_LABEL, "FESZ_QD", SAVESLOT_NONE, MENUPAGE_NONE, + MENUACTION_CHANGEMENU, "FEM_NO", SAVESLOT_NONE, MENUPAGE_CHOOSE_DELETE_SLOT, + MENUACTION_CHANGEMENU, "FEM_YES", SAVESLOT_NONE, MENUPAGE_DELETING, + }, + + // MENUPAGE_13 = 13 + { "FES_NOC", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_LOADING_IN_PROGRESS = 14 + { "FET_LG", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + MENUACTION_LABEL, "FED_LDW", SAVESLOT_NONE, MENUPAGE_LOAD_SLOT_CONFIRM, + }, + + // MENUPAGE_DELETING_IN_PROGRESS = 15 + { "FET_DG", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + MENUACTION_LABEL, "FEDL_WR", SAVESLOT_NONE, MENUPAGE_NONE, + }, + + // MENUPAGE_16 = 16 + { "FET_LG", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + MENUACTION_LABEL, "FES_LOE", SAVESLOT_NONE, MENUPAGE_NONE, + }, + + // MENUPAGE_DELETE_FAILED = 17 + { "FET_DG", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + MENUACTION_LABEL, "FES_DEE", SAVESLOT_NONE, MENUPAGE_NONE, + MENUACTION_CHANGEMENU, "FEC_OKK", SAVESLOT_NONE, MENUPAGE_CHOOSE_DELETE_SLOT, + }, + + // MENUPAGE_DEBUG_MENU = 18 + { "FED_DBG", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_MEMORY_CARD_1 = 19 + { "FEM_MCM", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_MEMORY_CARD_2 = 20 + { "FEM_MC2", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_MULTIPLAYER_MAIN = 21 + { "FET_MP", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_SAVE_FAILED_1 = 22 + { "MCDNSP", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + MENUACTION_MEMCARDSAVECONFIRM, "JAILB_U", SAVESLOT_NONE, MENUPAGE_NONE, + }, + + // MENUPAGE_SAVE_FAILED_2 = 23 + { "MCGNSP", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + MENUACTION_MEMCARDSAVECONFIRM, "JAILB_U", SAVESLOT_NONE, MENUPAGE_NONE, + }, + + // MENUPAGE_SAVE = 24 + { "FET_SG", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + MENUACTION_LABEL, "FES_SCG", SAVESLOT_NONE, MENUPAGE_NONE, + MENUACTION_UPDATESAVE, "GMSAVE", SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT, + MENUACTION_UPDATEMEMCARDSAVE, "FESZ_CA", SAVESLOT_NONE, MENUPAGE_NONE, + }, + + // MENUPAGE_NO_MEMORY_CARD = 25 + { "FES_NOC", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_CHOOSE_SAVE_SLOT = 26 + { "FET_SG", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + MENUACTION_UPDATEMEMCARDSAVE, "FESZ_CA", SAVESLOT_NONE, MENUPAGE_NONE, + MENUACTION_CHANGEMENU, "FEM_SL1", SAVESLOT_1, MENUPAGE_SAVE_OVERWRITE_CONFIRM, + MENUACTION_CHANGEMENU, "FEM_SL2", SAVESLOT_2, MENUPAGE_SAVE_OVERWRITE_CONFIRM, + MENUACTION_CHANGEMENU, "FEM_SL3", SAVESLOT_3, MENUPAGE_SAVE_OVERWRITE_CONFIRM, + MENUACTION_CHANGEMENU, "FEM_SL4", SAVESLOT_4, MENUPAGE_SAVE_OVERWRITE_CONFIRM, + MENUACTION_CHANGEMENU, "FEM_SL5", SAVESLOT_5, MENUPAGE_SAVE_OVERWRITE_CONFIRM, + MENUACTION_CHANGEMENU, "FEM_SL6", SAVESLOT_6, MENUPAGE_SAVE_OVERWRITE_CONFIRM, + MENUACTION_CHANGEMENU, "FEM_SL7", SAVESLOT_7, MENUPAGE_SAVE_OVERWRITE_CONFIRM, + MENUACTION_CHANGEMENU, "FEM_SL8", SAVESLOT_8, MENUPAGE_SAVE_OVERWRITE_CONFIRM, + }, + + // MENUPAGE_SAVE_OVERWRITE_CONFIRM = 27 + { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, MENUPAGE_CHOOSE_SAVE_SLOT, MENUPAGE_CHOOSE_SAVE_SLOT, MENUROW_0, MENUROW_0, + MENUACTION_LABEL, "FESZ_QO", SAVESLOT_NONE, MENUPAGE_NONE, + MENUACTION_CHANGEMENU, "FEM_YES", SAVESLOT_NONE, MENUPAGE_SAVING_IN_PROGRESS, + MENUACTION_CHANGEMENU, "FEM_NO", SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT, + }, + + // MENUPAGE_MULTIPLAYER_MAP = 28 + { "FET_MAP", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_MULTIPLAYER_CONNECTION = 29 + { "FET_CON", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_MULTIPLAYER_FIND_GAME = 30 + { "FET_FG", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_MULTIPLAYER_MODE = 31 + { "FET_GT", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_MULTIPLAYER_CREATE = 32 + { "FET_HG", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_MULTIPLAYER_START = 33 + { "FEN_STA", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_SKIN_SELECT_OLD = 34 + { "FET_PS", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_CONTROLLER_PC = 35 + { "FET_CTL", MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, MENUROW_0, MENUROW_0, + MENUACTION_CTRLMETHOD, "FET_CME", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC, + MENUACTION_REDEFCTRL, "FET_RDK", SAVESLOT_NONE, MENUPAGE_KEYBOARD_CONTROLS, + MENUACTION_CHANGEMENU, "FET_AMS", SAVESLOT_NONE, MENUPAGE_MOUSE_CONTROLS, + MENUACTION_RESTOREDEF, "FET_DEF", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC, + MENUACTION_CHANGEMENU, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, + }, + + // MENUPAGE_CONTROLLER_PC_OLD1 = 36 + { "FET_CTL", MENUPAGE_CONTROLLER_PC, MENUPAGE_CONTROLLER_PC, MENUPAGE_CONTROLLER_PC, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_CONTROLLER_PC_OLD2 = 37 + { "FET_CTL", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_CONTROLLER_PC_OLD3 = 38 + { "FET_CTL", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_CONTROLLER_PC_OLD4 = 39 + { "FET_CTL", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_CONTROLLER_DEBUG = 40 + { "FEC_DBG", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_OPTIONS = 41 + { "FET_OPT", MENUPAGE_NONE, MENUPAGE_NONE, MENUPAGE_NONE, MENUROW_1, MENUROW_4, + MENUACTION_CHANGEMENU, "FET_CTL", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC, + MENUACTION_LOADRADIO, "FET_AUD", SAVESLOT_NONE, MENUPAGE_SOUND_SETTINGS, + MENUACTION_CHANGEMENU, "FET_DIS", SAVESLOT_NONE, MENUPAGE_GRAPHICS_SETTINGS, + MENUACTION_CHANGEMENU, "FET_LAN", SAVESLOT_NONE, MENUPAGE_LANGUAGE_SETTINGS, + MENUACTION_PLAYERSETUP, "FET_PSU", SAVESLOT_NONE, MENUPAGE_SKIN_SELECT, + MENUACTION_CHANGEMENU, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, + }, + + // MENUPAGE_EXIT = 42 + { "FET_QG", MENUPAGE_NONE, MENUPAGE_NONE, MENUPAGE_NONE, MENUROW_2, MENUROW_5, + MENUACTION_LABEL, "FEQ_SRE", SAVESLOT_NONE, MENUPAGE_NONE, + MENUACTION_DONTCANCLE, "FEM_NO", SAVESLOT_NONE, MENUPAGE_NONE, + MENUACTION_CANCLEGAME, "FEM_YES", SAVESLOT_NONE, MENUPAGE_NONE, + }, + + // MENUPAGE_SAVING_IN_PROGRESS = 43 + { "", MENUPAGE_CHOOSE_SAVE_SLOT, MENUPAGE_CHOOSE_SAVE_SLOT, MENUPAGE_CHOOSE_SAVE_SLOT, MENUROW_0, MENUROW_0, + MENUACTION_LABEL, "FES_WAR", SAVESLOT_NONE, MENUPAGE_NONE, + }, + + // MENUPAGE_SAVE_SUCCESSFUL = 44 + { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, MENUPAGE_CHOOSE_SAVE_SLOT, MENUPAGE_CHOOSE_SAVE_SLOT, MENUROW_0, MENUROW_0, + MENUACTION_LABEL, "FES_SSC", SAVESLOT_LABEL, MENUPAGE_NONE, + MENUACTION_UPDATEMEMCARDSAVE, "FEC_OKK", SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT, + }, + + // MENUPAGE_DELETING = 45 + { "FET_DG", MENUPAGE_CHOOSE_DELETE_SLOT, MENUPAGE_CHOOSE_DELETE_SLOT, MENUPAGE_CHOOSE_DELETE_SLOT, MENUROW_0, MENUROW_0, + MENUACTION_LABEL, "FED_DLW", SAVESLOT_NONE, MENUPAGE_NONE, + }, + + // MENUPAGE_DELETE_SUCCESS = 46 + { "FET_DG", MENUPAGE_CHOOSE_DELETE_SLOT, MENUPAGE_CHOOSE_DELETE_SLOT, MENUPAGE_CHOOSE_DELETE_SLOT, MENUROW_0, MENUROW_0, + MENUACTION_LABEL, "DEL_FNM", SAVESLOT_NONE, MENUPAGE_NONE, + MENUACTION_CHANGEMENU, "FEC_OKK", SAVESLOT_NONE, MENUPAGE_CHOOSE_DELETE_SLOT, + }, + + // MENUPAGE_SAVE_FAILED = 47 + { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, MENUPAGE_CHOOSE_SAVE_SLOT, MENUPAGE_CHOOSE_SAVE_SLOT, MENUROW_0, MENUROW_0, + MENUACTION_LABEL, "FEC_SVU", SAVESLOT_NONE, MENUPAGE_NONE, + MENUACTION_CHANGEMENU, "FEC_OKK", SAVESLOT_NONE, MENUPAGE_CHOOSE_SAVE_SLOT, + }, + + // MENUPAGE_LOAD_FAILED = 48 + { "FET_SG", MENUPAGE_CHOOSE_SAVE_SLOT, MENUPAGE_CHOOSE_SAVE_SLOT, MENUPAGE_CHOOSE_SAVE_SLOT, MENUROW_0, MENUROW_0, + MENUACTION_LABEL, "FEC_SVU", SAVESLOT_NONE, MENUPAGE_NONE, + }, + + // MENUPAGE_LOAD_FAILED_2 = 49 + { "FET_LG", MENUPAGE_CHOOSE_SAVE_SLOT, MENUPAGE_CHOOSE_SAVE_SLOT, MENUPAGE_CHOOSE_SAVE_SLOT, MENUROW_0, MENUROW_0, + MENUACTION_LABEL, "FEC_LUN", SAVESLOT_NONE, MENUPAGE_NONE, + MENUACTION_CHANGEMENU, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_CHOOSE_LOAD_SLOT, + }, + + // MENUPAGE_FILTER_GAME = 50 + { "FIL_FLT", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_START_MENU = 51 + { "FEM_MM", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + MENUACTION_CHANGEMENU, "FEN_STA", SAVESLOT_NONE, MENUPAGE_NEW_GAME, + MENUACTION_CHANGEMENU, "FET_OPT", SAVESLOT_NONE, MENUPAGE_OPTIONS, + MENUACTION_CHANGEMENU, "FEM_QT", SAVESLOT_NONE, MENUPAGE_EXIT, + }, + + // MENUPAGE_PAUSE_MENU = 52 + { "FET_PAU", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + MENUACTION_RESUME, "FEM_RES", SAVESLOT_NONE, MENUPAGE_NONE, + MENUACTION_CHANGEMENU, "FEN_STA", SAVESLOT_NONE, MENUPAGE_NEW_GAME, + MENUACTION_CHANGEMENU, "FEP_STA", SAVESLOT_NONE, MENUPAGE_STATS, + MENUACTION_CHANGEMENU, "FEP_BRI", SAVESLOT_NONE, MENUPAGE_BRIEFS, + MENUACTION_CHANGEMENU, "FET_OPT", SAVESLOT_NONE, MENUPAGE_OPTIONS, + MENUACTION_CHANGEMENU, "FEM_QT", SAVESLOT_NONE, MENUPAGE_EXIT, + }, + + // MENUPAGE_CHOOSE_MODE = 53 + { "FEN_STA", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_SKIN_SELECT = 54 + { "FET_PSU", MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, MENUROW_4, MENUROW_4, + MENUACTION_CHANGEMENU, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_MULTIPLAYER_MAIN, + }, + + // MENUPAGE_KEYBOARD_CONTROLS = 55 + { "FET_STI", MENUPAGE_CONTROLLER_PC, MENUPAGE_CONTROLLER_PC, MENUPAGE_CONTROLLER_PC, MENUROW_1, MENUROW_1, + MENUACTION_CHANGEMENU, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC, + }, + + // MENUPAGE_MOUSE_CONTROLS = 56 + { "FET_MTI", MENUPAGE_CONTROLLER_PC, MENUPAGE_CONTROLLER_PC, MENUPAGE_CONTROLLER_PC, MENUROW_2, MENUROW_2, + MENUACTION_MOUSESENS, "FEC_MSH", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC, + MENUACTION_INVVERT, "FEC_IVV", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC, + MENUACTION_MOUSESTEER, "FET_MST", SAVESLOT_NONE, MENUPAGE_CONTROLLER_PC, + MENUACTION_CHANGEMENU, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE, + }, + + // MENUPAGE_57 = 57 + { "", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, + + // MENUPAGE_58 = 58 + { "", MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUPAGE_DISABLED, MENUROW_0, MENUROW_0, + + }, +}; diff --git a/src/PCSave.cpp b/src/PCSave.cpp index 2d49b23f..ae5e43b0 100644 --- a/src/PCSave.cpp +++ b/src/PCSave.cpp @@ -4,4 +4,9 @@ #include "PCSave.h" WRAPPER void C_PcSave::SetSaveDirectory(const char *path) { EAXJMP(0x591EA0); } +WRAPPER wchar *GetNameOfSavedGame(int counter) { EAXJMP(0x591B60); } +WRAPPER wchar *GetSavedGameDateAndTime(int counter) { EAXJMP(0x591B50); } +int *Slots = (int*)0x728040; +int *SlotFileName = (int*)0x6F07C8; +int *SlotSaveDate = (int*)0x72B858; diff --git a/src/PCSave.h b/src/PCSave.h index 324a3421..79202bc9 100644 --- a/src/PCSave.h +++ b/src/PCSave.h @@ -4,4 +4,11 @@ class C_PcSave { public: static void SetSaveDirectory(const char *path); -}; \ No newline at end of file +}; + +extern wchar *GetNameOfSavedGame(int counter); +extern wchar *GetSavedGameDateAndTime(int counter); + +extern int *Slots; +extern int *SlotFileName; +extern int *SlotSaveDate; diff --git a/src/Placeable.cpp b/src/Placeable.cpp index 43708d3e..b4b2a37b 100644 --- a/src/Placeable.cpp +++ b/src/Placeable.cpp @@ -20,56 +20,47 @@ CPlaceable::SetHeading(float angle) bool CPlaceable::IsWithinArea(float x1, float y1, float x2, float y2) { - float x, xmin, xmax; - float y, ymin, ymax; - xmin = x1; - xmax = x2; - ymin = y1; - ymax = y2; - if(x2 > x1){ - xmin = x2; - xmax = x1; + float tmp; + + if(x1 > x2){ + tmp = x1; + x1 = x2; + x2 = tmp; } - if(y2 > y1){ - ymin = y2; - ymax = y1; + if(y1 > y2){ + tmp = y1; + y1 = y2; + y2 = tmp; } - x = GetPosition().x; - y = GetPosition().y; - return xmin <= x && x <= xmax && - ymin <= y && y <= ymax; + + return x1 <= GetPosition().x && GetPosition().x <= x2 && + y1 <= GetPosition().y && GetPosition().y <= y2; } bool CPlaceable::IsWithinArea(float x1, float y1, float z1, float x2, float y2, float z2) { - float x, xmin, xmax; - float y, ymin, ymax; - float z, zmin, zmax; - xmin = x1; - xmax = x2; - ymin = y1; - ymax = y2; - zmin = z1; - zmax = z2; - if(x2 > x1){ - xmin = x2; - xmax = x1; + float tmp; + + if(x1 > x2){ + tmp = x1; + x1 = x2; + x2 = tmp; } - if(y2 > y1){ - ymin = y2; - ymax = y1; + if(y1 > y2){ + tmp = y1; + y1 = y2; + y2 = tmp; } - if(z2 > z1){ - zmin = z2; - zmax = z1; + if(z1 > z2){ + tmp = z1; + z1 = z2; + z2 = tmp; } - x = GetPosition().x; - y = GetPosition().y; - z = GetPosition().z; - return xmin <= x && x <= xmax && - ymin <= y && y <= ymax && - zmin <= z && z <= zmax; + + return x1 <= GetPosition().x && GetPosition().x <= x2 && + y1 <= GetPosition().y && GetPosition().y <= y2 && + z1 <= GetPosition().z && GetPosition().z <= z2; } STARTPATCHES diff --git a/src/Radar.cpp b/src/Radar.cpp index 155ec5e0..1b6ca527 100644 --- a/src/Radar.cpp +++ b/src/Radar.cpp @@ -1,6 +1,7 @@ #include "config.h" #include "common.h" #include "patcher.h" +#include "RwHelper.h" #include "Radar.h" #include "Camera.h" #include "Hud.h" @@ -14,16 +15,10 @@ #include "World.h" #include "Streaming.h" -WRAPPER void CRadar::Draw3dMarkers() { EAXJMP(0x4A4C70); } -WRAPPER int CRadar::ClipRadarPoly(CVector2D *out, CVector2D *in) { EAXJMP(0x4A64A0); } -WRAPPER void CRadar::TransformRadarPointToRealWorldSpace(CVector2D *out, CVector2D *in) { EAXJMP(0x4A5300); } - float &CRadar::m_RadarRange = *(float*)0x8E281C; -CVector2D &CRadar::vec2DRadarOrigin = *(CVector2D*)0x6299B8; CBlip *CRadar::ms_RadarTrace = (CBlip*)0x6ED5E0; -float CRadar::cachedSin; -float CRadar::cachedCos; +CVector2D &vec2DRadarOrigin = *(CVector2D*)0x6299B8; int *gRadarTxdIds = (int*)0x6299C0; CSprite2d *CRadar::AsukaSprite = (CSprite2d*)0x8F1A40; @@ -71,6 +66,313 @@ CSprite2d *CRadar::RadarSprites[RADAR_SPRITE_COUNT] = { WeaponSprite }; +#define RADAR_NUM_TILES (8) +#define RADAR_TILE_SIZE (WORLD_SIZE_X / RADAR_NUM_TILES) +static_assert(RADAR_TILE_SIZE == (WORLD_SIZE_Y / RADAR_NUM_TILES), "CRadar: not a square"); + +#define RADAR_MIN_RANGE (120.0f) +#define RADAR_MAX_RANGE (350.0f) +#define RADAR_MIN_SPEED (0.3f) +#define RADAR_MAX_SPEED (0.9f) + +#if 0 +WRAPPER void CRadar::CalculateBlipAlpha(float) { EAXJMP(0x4A4F90); } +#else +int CRadar::CalculateBlipAlpha(float dist) +{ + if (dist <= 1.0f) + return 255; + + if (dist <= 5.0f) + return (((1.0f - ((dist * 0.25f) - 0.25f)) * 255.0f) + (((dist * 0.25f) - 0.25f) * 128.0f)); + + return 128; +} +#endif + +#if 1 +WRAPPER void CRadar::ChangeBlipBrightness(int32, int32) { EAXJMP(0x4A57A0); } +#else +void CRadar::ChangeBlipBrightness(int32 i, int32 bright) +{ + +} +#endif + +#if 1 +WRAPPER void CRadar::ChangeBlipColour(int32) { EAXJMP(0x4A5770); } +#else +void CRadar::ChangeBlipColour(int32 i) +{ + +} +#endif + +#if 1 +WRAPPER void CRadar::ChangeBlipDisplay(int32, int16) { EAXJMP(0x4A5810); } +#else +void CRadar::ChangeBlipDisplay(int32 i, int16 flag) +{ + +} +#endif + +#if 1 +WRAPPER void CRadar::ChangeBlipScale(int32, int16) { EAXJMP(0x4A57E0); } +#else +void CRadar::ChangeBlipScale(int32 i, int16 scale) +{ + +} +#endif + +#if 1 +WRAPPER void CRadar::ClearBlip(int32) { EAXJMP(0x4A5720); } +#else +void CRadar::ClearBlip(int32 i) +{ + +} +#endif + +#if 0 +WRAPPER void CRadar::ClearBlipForEntity(int16, int32) { EAXJMP(0x4A56C0); } +#else +void CRadar::ClearBlipForEntity(int16 type, int32 id) +{ + for (int i = 0; i < NUMRADARBLIPS; i++) { + if (type == ms_RadarTrace[i].m_eBlipType && id == ms_RadarTrace[i].m_nEntityHandle) { + SetRadarMarkerState(i, 0); + ms_RadarTrace[i].m_bInUse = 0; + ms_RadarTrace[i].m_eBlipType = 0; + ms_RadarTrace[i].m_eBlipDisplay = 0; + ms_RadarTrace[i].m_IconID = 0; + } + }; +} +#endif + +#if 0 +WRAPPER int CRadar::ClipRadarPoly(CVector2D *poly, const CVector2D *in) { EAXJMP(0x4A64A0); } +#else +// Why not a proper clipping algorithm? +int CRadar::ClipRadarPoly(CVector2D *poly, const CVector2D *rect) +{ + CVector2D corners[4] = { + { 1.0f, -1.0f }, // top right + { 1.0f, 1.0f }, // bottom right + { -1.0f, 1.0f }, // bottom left + { -1.0f, -1.0f }, // top left + }; + CVector2D tmp; + int i, j, n; + int laste, e, e1, e2;; + bool inside[4]; + + for (i = 0; i < 4; i++) + inside[i] = IsPointInsideRadar(rect[i]); + + laste = -1; + n = 0; + for (i = 0; i < 4; i++) + if (inside[i]) { + // point is inside, just add + poly[n++] = rect[i]; + } + else { + // point is outside but line to this point might be clipped + e1 = LineRadarBoxCollision(poly[n], rect[i], rect[(i + 4 - 1) % 4]); + if (e1 != -1) { + laste = e1; + n++; + } + // and line from this point might be clipped as well + e2 = LineRadarBoxCollision(poly[n], rect[i], rect[(i + 1) % 4]); + if (e2 != -1) { + if (e1 == -1) { + // if other line wasn't clipped, i.e. it was complete outside, + // we may have to insert another vertex if last clipped line + // was on a different edge + + // find the last intersection if we haven't seen it yet + if (laste == -1) + for (j = 3; j >= i; j--) { + // game uses an if here for j == 0 + e = LineRadarBoxCollision(tmp, rect[j], rect[(j + 4 - 1) % 4]); + if (e != -1) { + laste = e; + break; + } + } + assert(laste != -1); + + // insert corners that were skipped + tmp = poly[n]; + for (e = laste; e != e2; e = (e + 1) % 4) + poly[n++] = corners[e]; + poly[n] = tmp; + } + n++; + } + } + + if (n == 0) { + // If no points, either the rectangle is completely outside or completely surrounds the radar + // no idea what's going on here... + float m = (rect[0].y - rect[1].y) / (rect[0].x - rect[1].x); + if ((m*rect[3].x - rect[3].y) * (m*rect[0].x - rect[0].y) < 0.0f) { + m = (rect[0].y - rect[3].y) / (rect[0].x - rect[3].x); + if ((m*rect[1].x - rect[1].y) * (m*rect[0].x - rect[0].y) < 0.0f) { + poly[0] = corners[0]; + poly[1] = corners[1]; + poly[2] = corners[2]; + poly[3] = corners[3]; + n = 4; + } + } + } + + return n; +} +#endif + +bool CRadar::DisplayThisBlip(int32 counter) +{ + switch (ms_RadarTrace[counter].m_IconID) { + case RADAR_SPRITE_BOMB: + case RADAR_SPRITE_SPRAY: + case RADAR_SPRITE_WEAPON: + return true; + default: + return false; + } +} + +#if 1 +WRAPPER void CRadar::Draw3dMarkers() { EAXJMP(0x4A4C70); } +#else +void CRadar::Draw3dMarkers() +{ + +} +#endif + + +#if 0 +WRAPPER void CRadar::DrawBlips() { EAXJMP(0x4A42F0); } +#else +void CRadar::DrawBlips() +{ + if (!TheCamera.m_WideScreenOn && CHud::m_Wants_To_Draw_Hud) { + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); + + CVector2D out; + CVector2D in = CVector2D(0.0f, 0.0f); + TransformRadarPointToScreenSpace(out, in); + + float angle; + if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN1) + angle = PI + FindPlayerHeading(); + else + angle = FindPlayerHeading() - (PI + atan2(-TheCamera.GetForward().x, TheCamera.GetForward().y)); + + DrawRotatingRadarSprite(CentreSprite, out.x, out.y, angle, 255); + + CVector2D vec2d; + vec2d.x = vec2DRadarOrigin.x; + vec2d.y = M_SQRT2 * m_RadarRange + vec2DRadarOrigin.y; + TransformRealWorldPointToRadarSpace(in, vec2d); + LimitRadarPoint(in); + TransformRadarPointToScreenSpace(out, in); + DrawRadarSprite(RADAR_SPRITE_NORTH, out.x, out.y, 255); + + /* + DrawEntityBlip + */ + for (int i = 0; i < NUMRADARBLIPS; i++) { + if (ms_RadarTrace[i].m_bInUse) { + if (ms_RadarTrace[i].m_eBlipType <= BLIP_OBJECT) { + CEntity *e = nil; + switch (ms_RadarTrace[i].m_eBlipType) { + case BLIP_CAR: + e = CPools::GetVehiclePool()->GetAt(ms_RadarTrace[i].m_nEntityHandle); + break; + case BLIP_CHAR: + e = CPools::GetPedPool()->GetAt(ms_RadarTrace[i].m_nEntityHandle); + break; + case BLIP_OBJECT: + e = CPools::GetObjectPool()->GetAt(ms_RadarTrace[i].m_nEntityHandle); + break; + }; + + if (e) { + if (ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_MARKER_ONLY) { + if (CTheScripts::DbgFlag) { + ShowRadarMarker(e->GetPosition(), GetRadarTraceColour(ms_RadarTrace[i].m_nColor, ms_RadarTrace[i].m_bDim), ms_RadarTrace->m_Radius); + + ms_RadarTrace[i].m_Radius = ms_RadarTrace[i].m_Radius - 0.1f; + if (ms_RadarTrace[i].m_Radius >= 1.0f) + ms_RadarTrace[i].m_Radius = 5.0; + } + } + if (ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BLIP_ONLY) { + vec2d = e->GetPosition(); + TransformRealWorldPointToRadarSpace(in, vec2d); + float dist = LimitRadarPoint(in); + int a = CalculateBlipAlpha(dist); + TransformRadarPointToScreenSpace(out, in); + + int32 col = GetRadarTraceColour(ms_RadarTrace[i].m_nColor, ms_RadarTrace[i].m_bDim); + + if (ms_RadarTrace[i].m_IconID) + DrawRadarSprite(ms_RadarTrace[i].m_IconID, out.x, out.y, a); + else + ShowRadarTrace(out.x, out.y, ms_RadarTrace[i].m_wScale, ((col >> 24)), ((col >> 16) & 0xFF), ((col >> 8)), 255); + } + } + } + + /* + DrawCoordBlip + */ + if (ms_RadarTrace[i].m_eBlipType >= BLIP_COORD) { + if (ms_RadarTrace[i].m_eBlipType != BLIP_CONTACT_POINT || ms_RadarTrace[i].m_eBlipType == BLIP_CONTACT_POINT && DisplayThisBlip(i) || !CTheScripts::IsPlayerOnAMission()) { + if (ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_MARKER_ONLY) { + if (CTheScripts::DbgFlag) { + ShowRadarMarker(ms_RadarTrace[i].m_vecPos, GetRadarTraceColour(ms_RadarTrace[i].m_nColor, ms_RadarTrace[i].m_bDim), ms_RadarTrace->m_Radius); + ms_RadarTrace[i].m_Radius = ms_RadarTrace[i].m_Radius - 0.1f; + if (ms_RadarTrace[i].m_Radius >= 1.0f) + ms_RadarTrace[i].m_Radius = 5.0f; + } + } + + if (ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BLIP_ONLY) { + TransformRealWorldPointToRadarSpace(in, ms_RadarTrace[i].m_vec2DPos); + float dist = LimitRadarPoint(in); + int a = CalculateBlipAlpha(dist); + TransformRadarPointToScreenSpace(out, in); + + int32 col = GetRadarTraceColour(ms_RadarTrace[i].m_nColor, ms_RadarTrace[i].m_bDim); + + if (ms_RadarTrace[i].m_IconID) + DrawRadarSprite(ms_RadarTrace[i].m_IconID, out.x, out.y, a); + else + ShowRadarTrace(out.x, out.y, ms_RadarTrace[i].m_wScale, ((col >> 24)), ((col >> 16) & 0xFF), ((col >> 8)), 255); + } + } + } + }; + } + } +} +#endif + + #if 0 WRAPPER void CRadar::DrawMap () { EAXJMP(0x4A4200); } #else @@ -78,31 +380,66 @@ void CRadar::DrawMap() { if (!TheCamera.m_WideScreenOn && CHud::m_Wants_To_Draw_Hud) { if (FindPlayerVehicle()) { - if (FindPlayerVehicle()->GetSpeed().Magnitude() > 0.3f) { - if (FindPlayerVehicle()->GetSpeed().Magnitude() > 0.9f) - CRadar::m_RadarRange = 350.0f; - else - CRadar::m_RadarRange = (FindPlayerVehicle()->GetSpeed().Magnitude() + 0.3f) * 200.0f; - } + float speed = FindPlayerSpeed().Magnitude(); + if (speed < RADAR_MIN_SPEED) + m_RadarRange = RADAR_MIN_RANGE; + else if (speed < RADAR_MAX_SPEED) + m_RadarRange = (speed - RADAR_MIN_SPEED)/(RADAR_MAX_SPEED-RADAR_MIN_SPEED) * (RADAR_MAX_RANGE-RADAR_MIN_RANGE) + RADAR_MIN_RANGE; else - CRadar::m_RadarRange = 120.0f; + m_RadarRange = RADAR_MAX_RANGE; } else - CRadar::m_RadarRange = 120.0f; + m_RadarRange = RADAR_MIN_RANGE; - vec2DRadarOrigin.x = FindPlayerCentreOfWorld_NoSniperShift().x; - vec2DRadarOrigin.y = FindPlayerCentreOfWorld_NoSniperShift().y; - CRadar::DrawRadarMap(); + vec2DRadarOrigin = CVector2D(FindPlayerCentreOfWorld_NoSniperShift()); + DrawRadarMap(); } } #endif +#if 0 +WRAPPER void CRadar::DrawRadarMap() { EAXJMP(0x4A6C20); } +#else +void CRadar::DrawRadarMap() +{ + // Game calculates an unused CRect here + + DrawRadarMask(); + + // top left ist (0, 0) + int x = floorf((vec2DRadarOrigin.x - WORLD_MIN_X) / RADAR_TILE_SIZE); + int y = ceilf((RADAR_NUM_TILES - 1) - (vec2DRadarOrigin.y - WORLD_MIN_Y) / RADAR_TILE_SIZE); + StreamRadarSections(x, y); + + RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); + RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); + RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEFLAT); + RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); + RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); + RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP); + RwRenderStateSet(rwRENDERSTATETEXTUREPERSPECTIVE, (void*)FALSE); + + DrawRadarSection(x - 1, y - 1); + DrawRadarSection(x, y - 1); + DrawRadarSection(x + 1, y - 1); + DrawRadarSection(x - 1, y); + DrawRadarSection(x, y); + DrawRadarSection(x + 1, y); + DrawRadarSection(x - 1, y + 1); + DrawRadarSection(x, y + 1); + DrawRadarSection(x + 1, y + 1); +} +#endif + #if 0 WRAPPER void CRadar::DrawRadarMask() { EAXJMP(0x4A69C0); } #else void CRadar::DrawRadarMask() { - CVector2D vec2d[4] = { + CVector2D corners[4] = { CVector2D(1.0f, -1.0f), CVector2D(1.0f, 1.0f), CVector2D(-1.0f, 1.0f), @@ -123,19 +460,18 @@ void CRadar::DrawRadarMask() CVector2D out[8]; CVector2D in; + // Draw the shape we want to mask out from the radar in four segments for (int i = 0; i < 4; i++) { - in.x = vec2d[i].x; - in.y = vec2d[i].y; - - CRadar::TransformRadarPointToScreenSpace(out, &in); + // First point is always the corner itself + in.x = corners[i].x; + in.y = corners[i].y; + TransformRadarPointToScreenSpace(out[0], in); + // Then generate a quarter of the circle for (int j = 0; j < 7; j++) { - CRadar::cachedCos = cos(j * (PI / 2.0f / 6.0f)); - CRadar::cachedSin = sin(j * (PI / 2.0f / 6.0f)); - - in.x = vec2d[i].x * cachedCos; - in.y = vec2d[i].y * cachedSin; - CRadar::TransformRadarPointToScreenSpace(&out[j + 1], &in); + in.x = corners[i].x * cos(j * (PI / 2.0f / 6.0f)); + in.y = corners[i].y * sin(j * (PI / 2.0f / 6.0f)); + TransformRadarPointToScreenSpace(out[j + 1], in); }; CSprite2d::SetMaskVertices(8, (float *)out); @@ -147,9 +483,281 @@ void CRadar::DrawRadarMask() #endif #if 0 -WRAPPER void CRadar::SetRadarMarkerState(int counter, int flag) { EAXJMP(0x4A5C60); } +WRAPPER void CRadar::DrawRadarSection(int32, int32) { EAXJMP(0x4A67E0); } #else -void CRadar::SetRadarMarkerState(int counter, int flag) +void CRadar::DrawRadarSection(int32 x, int32 y) +{ + int i; + RwTexDictionary *txd; + CVector2D worldPoly[8]; + CVector2D radarCorners[4]; + CVector2D radarPoly[8]; + CVector2D texCoords[8]; + CVector2D screenPoly[8]; + int numVertices; + RwTexture *texture = nil; + + GetTextureCorners(x, y, worldPoly); + ClipRadarTileCoords(x, y); + + assert(CTxdStore::GetSlot(gRadarTxdIds[x + RADAR_NUM_TILES * y])); + txd = CTxdStore::GetSlot(gRadarTxdIds[x + RADAR_NUM_TILES * y])->texDict; + if (txd) + texture = GetFirstTexture(txd); + if (texture == nil) + return; + + for (i = 0; i < 4; i++) + TransformRealWorldPointToRadarSpace(radarCorners[i], worldPoly[i]); + + numVertices = ClipRadarPoly(radarPoly, radarCorners); + + // FIX: can return earlier here +// if(numVertices == 0) + if (numVertices < 3) + return; + + for (i = 0; i < numVertices; i++) { + TransformRadarPointToRealWorldSpace(worldPoly[i], radarPoly[i]); + TransformRealWorldToTexCoordSpace(texCoords[i], worldPoly[i], x, y); + TransformRadarPointToScreenSpace(screenPoly[i], radarPoly[i]); + } + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(texture)); + CSprite2d::SetVertices(numVertices, (float*)screenPoly, (float*)texCoords, CRGBA(255, 255, 255, 255)); + // check done above now +// if(numVertices > 2) + RwIm2DRenderPrimitive(rwPRIMTYPETRIFAN, CSprite2d::GetVertices(), numVertices); +} +#endif + +#if 0 +WRAPPER void CRadar::DrawRadarSprite(int32 sprite, float x, float y, int32 alpha) { EAXJMP(0x4A5EF0); } +#else +void CRadar::DrawRadarSprite(int32 sprite, float x, float y, int32 alpha) +{ + RadarSprites[sprite]->Draw(CRect(x - SCREEN_SCALE_X(8.0f), y - SCREEN_SCALE_Y(8.0f), x + SCREEN_SCALE_X(8.0f), y + SCREEN_SCALE_Y(8.0f)), CRGBA(255, 255, 255, alpha)); +} +#endif + +#if 0 +WRAPPER void CRadar::DrawRotatingRadarSprite(CSprite2d* sprite, float x, float y, float angle, int32 alpha) { EAXJMP(0x4A5D10); } +#else +void CRadar::DrawRotatingRadarSprite(CSprite2d* sprite, float x, float y, float angle, int32 alpha) +{ + CVector curPosn[4]; + CVector oldPosn[4]; + + curPosn[0].x = x - SCREEN_SCALE_X(5.6f); + curPosn[0].y = y + SCREEN_SCALE_Y(5.6f); + + curPosn[1].x = x + SCREEN_SCALE_X(5.6f); + curPosn[1].y = y + SCREEN_SCALE_Y(5.6f); + + curPosn[2].x = x - SCREEN_SCALE_X(5.6f); + curPosn[2].y = y - SCREEN_SCALE_Y(5.6f); + + curPosn[3].x = x + SCREEN_SCALE_X(5.6f); + curPosn[3].y = y - SCREEN_SCALE_Y(5.6f); + + for (uint32 i = 0; i < 4; i++) { + oldPosn[i] = curPosn[i]; + + curPosn[i].x = x + (oldPosn[i].x - x) * cosf(angle) + (oldPosn[i].y - y) * sinf(angle); + curPosn[i].y = y - (oldPosn[i].x - x) * sinf(angle) + (oldPosn[i].y - y) * cosf(angle); + } + + sprite->Draw(curPosn[2].x, curPosn[2].y, curPosn[3].x, curPosn[3].y, curPosn[0].x, curPosn[0].y, curPosn[1].x, curPosn[1].y, CRGBA(255, 255, 255, alpha)); +} +#endif + +#if 1 +WRAPPER int32 CRadar::GetActualBlipArray(int32) { EAXJMP(0x4A41C0); } +#else +int32 CRadar::GetActualBlipArray(int32 i) +{ + return int32(); +} +#endif + +#if 1 +WRAPPER int32 CRadar::GetNewUniqueBlipIndex(int32) { EAXJMP(0x4A4180); } +#else +int32 CRadar::GetNewUniqueBlipIndex(int32 i) +{ + return int32(); +} +#endif + +#if 0 +WRAPPER int32 CRadar::GetRadarTraceColour(int32 color, bool bright) { EAXJMP(0x4A5BB0); } +#else +int32 CRadar::GetRadarTraceColour(int32 color, bool bright) +{ + int32 c; + switch (color) { + case 0: + if (bright) + c = 0x712B49FF; + else + c = 0x7F0000FF; + break; + case 1: + if (bright) + c = 0x5FA06AFF; + else + c = 0x7F00FF; + break; + case 2: + if (bright) + c = 0x80A7F3FF; + else + c = 0x007FFF; + break; + case 3: + if (bright) + c = 0xE1E1E1FF; + else + c = 0x7F7F7FFF; + break; + case 4: + if (bright) + c = 0xFFFF00FF; + else + c = 0x7F7F00FF; + break; + case 5: + if (bright) + c = 0xFF00FFFF; + else + c = 0x7F007FFF; + break; + case 6: + if (bright) + c = 0xFFFFFF; + else + c = 0x7F7FFF; + break; + default: + c = color; + break; + }; + return c; +} +#endif + +#if 0 +WRAPPER void CRadar::Initialise() { EAXJMP(0x4A3EF0); } +#else +void CRadar::Initialise() +{ + +} +#endif + +#if 0 +WRAPPER float CRadar::LimitRadarPoint(CVector2D &point) { EAXJMP(0x4A4F30); } +#else +float CRadar::LimitRadarPoint(CVector2D &point) +{ + float dist, invdist; + + dist = point.Magnitude(); + if (dist > 1.0f) { + invdist = 1.0f / dist; + point.x *= invdist; + point.y *= invdist; + } + return dist; +} +#endif + +#if 0 +WRAPPER void CRadar::LoadAllRadarBlips() { EAXJMP(0x4A6F30); } +#else +void CRadar::LoadAllRadarBlips(int32) +{ + +} +#endif + +#if 0 +WRAPPER void CRadar::LoadTextures() { EAXJMP(0x4A4030); } +#else +void CRadar::LoadTextures() +{ + +} +#endif + +#if 0 +WRAPPER void CRadar::RemoveRadarSections() { EAXJMP(0x00); } +#else +void CRadar::RemoveRadarSections() +{ + +} +#endif + +#if 0 +WRAPPER void CRadar::RemoveMapSection(int32, int32) { EAXJMP(0x00); } +#else +void CRadar::RemoveMapSection(int32 x, int32 y) +{ + if (x >= 0 && x <= 7 && y >= 0 && y <= 7) + CStreaming::RemoveTxd(gRadarTxdIds[x + RADAR_NUM_TILES * y]); +} +#endif + +#if 0 +WRAPPER void CRadar::RequestMapSection(int32, int32) { EAXJMP(0x00); } +#else +void CRadar::RequestMapSection(int32 x, int32 y) +{ + ClipRadarTileCoords(x, y); + CStreaming::RequestTxd(gRadarTxdIds[x + RADAR_NUM_TILES * y], STREAMFLAGS_DONT_REMOVE | STREAMFLAGS_DEPENDENCY); +} +#endif + +#if 0 +WRAPPER void CRadar::SaveAllRadarBlips(int32) { EAXJMP(0x00); } +#else +void CRadar::SaveAllRadarBlips(int32) +{ + +} +#endif + +#if 0 +WRAPPER void CRadar::SetBlipSprite(int32, int32) { EAXJMP(0x00); } +#else +void CRadar::SetBlipSprite(int32 i, int32 icon) +{ + +} +#endif + +#if 0 +WRAPPER void CRadar::SetCoordBlip(int32, CVector, int32) { EAXJMP(0x00); } +#else +int CRadar::SetCoordBlip(int32 type, CVector pos, int32 flag) +{ + return 0; +} +#endif + +#if 0 +WRAPPER void CRadar::SetEntityBlip(int32 type, CVector pos, int32 color, int32 flag) { EAXJMP(0x00); } +#else +int CRadar::SetEntityBlip(int32 type, CVector pos, int32 color, int32 flag) +{ + return 0; +} +#endif + +#if 0 +WRAPPER void CRadar::SetRadarMarkerState(int32, int32) { EAXJMP(0x4A5C60); } +#else +void CRadar::SetRadarMarkerState(int32 counter, int32 flag) { CEntity *e; switch (ms_RadarTrace[counter].m_eBlipType) { @@ -164,346 +772,36 @@ void CRadar::SetRadarMarkerState(int counter, int flag) break; default: return; - }; + } if (e) e->bHasBlip = flag; - } #endif #if 0 -WRAPPER void CRadar::ClearBlipForEntity(eBlipType type, int32 id) { EAXJMP(0x4A56C0); } +WRAPPER void CRadar::ShowRadarMarker(CVector pos, int16 color, float radius) { EAXJMP(0x4A59C0); } #else -void CRadar::ClearBlipForEntity(eBlipType type, int32 id) -{ - for (int i = 0; i < NUMBLIPS; i++) { - if (type == ms_RadarTrace[i].m_eBlipType && id == ms_RadarTrace[i].m_nEntityHandle) { - CRadar::SetRadarMarkerState(i, 0); - ms_RadarTrace[i].m_bInUse = 0; - ms_RadarTrace[i].m_eBlipType = 0; - ms_RadarTrace[i].m_eBlipDisplay = 0; - ms_RadarTrace[i].m_IconID = 0; - } - }; -} -#endif +void CRadar::ShowRadarMarker(CVector pos, int16 color, float radius) { + float f1 = radius * 0.5f; + float f2 = radius * 1.4f; + CVector p1, p2; -#if 1 -WRAPPER void CRadar::DrawRadarSection(int x, int y) { EAXJMP(0x4A67E0); } -#else -void CRadar::DrawRadarSection(int x, int y) -{ - -} -#endif + p1 = pos + TheCamera.GetUp()*f1; + p2 = pos + TheCamera.GetUp()*f2; + CTheScripts::ScriptDebugLine3D(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, color, color); -void CRadar::RequestMapSection(int x, int y) -{ - ClipRadarTileCoords(&x, &y); - CStreaming::RequestModel(gRadarTxdIds[x + 8 * y] + 5500, 5); -} + p1 = pos - TheCamera.GetUp()*f1; + p2 = pos - TheCamera.GetUp()*f2; + CTheScripts::ScriptDebugLine3D(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, color, color); -void CRadar::RemoveMapSection(int x, int y) -{ - if (x >= 0 && x <= 7 && y >= 0 && y <= 7) - CStreaming::RemoveModel(gRadarTxdIds[x + 8 * y] + 5500); -} + p1 = pos + TheCamera.GetRight()*f1; + p2 = pos + TheCamera.GetRight()*f2; + CTheScripts::ScriptDebugLine3D(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, color, color); -#if 0 -WRAPPER void CRadar::StreamRadarSections(int x, int y) { EAXJMP(0x4A6100); } -#else -void CRadar::StreamRadarSections(int x, int y) -{ - for (int i = 0; i < 8; ++i) { - for (int j = 0; j < 8; ++j) { - if ((i >= x - 1 && i <= x + 1) && (j >= y - 1 && j <= y + 1)) - RequestMapSection(i, j); - else - RemoveMapSection(i, j); - }; - }; -} -#endif - -#if 0 -WRAPPER float CRadar::LimitRadarPoint(CVector2D *point) { EAXJMP(0x4A4F30); } -#else -float CRadar::LimitRadarPoint(CVector2D *point) -{ - float div; - - if (point->Magnitude() > 1.0f) { - div = 1.0f / point->Magnitude(); - point->x *= div; - point->y *= div; - } - return point->Magnitude(); -} -#endif - -#if 0 -WRAPPER void CRadar::TransformRealWorldToTexCoordSpace(CVector2D *out, CVector2D *in, int x, int y) { EAXJMP(0x4A5530); } -#else -void CRadar::TransformRealWorldToTexCoordSpace(CVector2D *out, CVector2D *in, int x, int y) { - out->x = in->x - (x * 500.0f - WORLD_MAX_X); - out->y = -(in->y - ((8 - y) * 500.0f - WORLD_MAX_Y)); - out->x *= 0.002f; - out->y *= 0.002f; -} -#endif - -#if 0 -WRAPPER void CRadar::DrawRadarMap() { EAXJMP(0x4A6C20); } -#else -void CRadar::DrawRadarMap() -{ - CRadar::DrawRadarMask(); - - int x = floorf((WORLD_MAX_X + vec2DRadarOrigin.x) * 0.002f); - int y = round(7.0f - (WORLD_MAX_Y + vec2DRadarOrigin.y) * 0.002f); - CRadar::StreamRadarSections(x, y); - - RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); - RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); - RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); - RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR); - RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEFLAT); - RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE); - RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); - RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE); - RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP); - RwRenderStateSet(rwRENDERSTATETEXTUREPERSPECTIVE, (void*)FALSE); - - CRadar::DrawRadarSection(x - 1, y - 1); - CRadar::DrawRadarSection(x, y - 1); - CRadar::DrawRadarSection(x + 1, y - 1); - CRadar::DrawRadarSection(x - 1, y); - CRadar::DrawRadarSection(x, y); - CRadar::DrawRadarSection(x + 1, y); - CRadar::DrawRadarSection(x - 1, y + 1); - CRadar::DrawRadarSection(x, y + 1); - CRadar::DrawRadarSection(x + 1, y + 1); -} -#endif - -#if 0 -WRAPPER void CRadar::DrawBlips() { EAXJMP(0x4A42F0); } -#else -void CRadar::DrawBlips() -{ - if (!TheCamera.m_WideScreenOn && CHud::m_Wants_To_Draw_Hud) { - RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE); - RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE); - RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE); - RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA); - RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA); - RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE); - - CVector2D out; - CVector2D in = CVector2D(0.0f, 0.0f); - CRadar::TransformRadarPointToScreenSpace(&out, &in); - - float angle; - if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN1) - angle = PI + FindPlayerHeading(); - else - angle = FindPlayerHeading() - (PI + atan2(-TheCamera.GetForward().x, TheCamera.GetForward().y)); - - CRadar::DrawRotatingRadarSprite(CentreSprite, out.x, out.y, angle, 255); - - CVector2D vec2d; - vec2d.x = vec2DRadarOrigin.x; - vec2d.y = M_SQRT2 * m_RadarRange + vec2DRadarOrigin.y; - CRadar::TransformRealWorldPointToRadarSpace(&in, &vec2d); - CRadar::LimitRadarPoint(&in); - CRadar::TransformRadarPointToScreenSpace(&out, &in); - CRadar::DrawRadarSprite(RADAR_SPRITE_NORTH, out.x, out.y, 255); - - /* - DrawEntityBlip - */ - for (int i = 0; i < NUMBLIPS; i++) { - if (ms_RadarTrace[i].m_bInUse) { - if (ms_RadarTrace[i].m_eBlipType <= BLIP_OBJECT) { - CEntity *e = nil; - switch (ms_RadarTrace[i].m_eBlipType) { - case BLIP_CAR: - e = CPools::GetVehiclePool()->GetAt(ms_RadarTrace[i].m_nEntityHandle); - break; - case BLIP_CHAR: - e = CPools::GetPedPool()->GetAt(ms_RadarTrace[i].m_nEntityHandle); - break; - case BLIP_OBJECT: - e = CPools::GetObjectPool()->GetAt(ms_RadarTrace[i].m_nEntityHandle); - break; - }; - - if (e) { - if (ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_MARKER_ONLY) { - if (CTheScripts::DbgFlag) { - CRadar::ShowRadarMarker(e->GetPosition(), GetRadarTraceColour(ms_RadarTrace[i].m_nColor, ms_RadarTrace[i].m_bDim), ms_RadarTrace->m_Radius); - - ms_RadarTrace[i].m_Radius = ms_RadarTrace[i].m_Radius - 0.1f; - if (ms_RadarTrace[i].m_Radius >= 1.0f) - ms_RadarTrace[i].m_Radius = 5.0; - } - } - if (ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BLIP_ONLY) { - vec2d = e->GetPosition(); - CRadar::TransformRealWorldPointToRadarSpace(&in, &vec2d); - float dist = CRadar::LimitRadarPoint(&in); - int a = CRadar::CalculateBlipAlpha(dist); - CRadar::TransformRadarPointToScreenSpace(&out, &in); - - CRGBA col = CRadar::GetRadarTraceColour(ms_RadarTrace[i].m_nColor, ms_RadarTrace[i].m_bDim); - - if (ms_RadarTrace[i].m_IconID) - CRadar::DrawRadarSprite(ms_RadarTrace[i].m_IconID, out.x, out.y, a); - else - CRadar::ShowRadarTrace(out.x, out.y, ms_RadarTrace[i].m_wScale, col.r, col.g, col.b, 255); - } - } - } - - /* - DrawCoordBlip - */ - if (ms_RadarTrace[i].m_eBlipType >= BLIP_COORD) { - if (ms_RadarTrace[i].m_eBlipType != BLIP_CONTACT_POINT || ms_RadarTrace[i].m_eBlipType == BLIP_CONTACT_POINT && DisplayThisBlip(i) || !CTheScripts::IsPlayerOnAMission()) { - if (ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_MARKER_ONLY) { - if (CTheScripts::DbgFlag) { - CRadar::ShowRadarMarker(ms_RadarTrace[i].m_vecPos, CRadar::GetRadarTraceColour(ms_RadarTrace[i].m_nColor, ms_RadarTrace[i].m_bDim), ms_RadarTrace->m_Radius); - ms_RadarTrace[i].m_Radius = ms_RadarTrace[i].m_Radius - 0.1f; - if (ms_RadarTrace[i].m_Radius >= 1.0f) - ms_RadarTrace[i].m_Radius = 5.0f; - } - } - - if (ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BOTH || ms_RadarTrace[i].m_eBlipDisplay == BLIP_DISPLAY_BLIP_ONLY) { - CRadar::TransformRealWorldPointToRadarSpace(&in, &ms_RadarTrace[i].m_vec2DPos); - float dist = CRadar::LimitRadarPoint(&in); - int a = CRadar::CalculateBlipAlpha(dist); - CRadar::TransformRadarPointToScreenSpace(&out, &in); - - CRGBA col = CRadar::GetRadarTraceColour(ms_RadarTrace[i].m_nColor, ms_RadarTrace[i].m_bDim); - - if (CRadar::ms_RadarTrace[i].m_IconID) - CRadar::DrawRadarSprite(ms_RadarTrace[i].m_IconID, out.x, out.y, a); - else - CRadar::ShowRadarTrace(out.x, out.y, ms_RadarTrace[i].m_wScale, col.r, col.g, col.b, 255); - } - } - } - }; - } - } -} -#endif - -int CRadar::CalculateBlipAlpha(float dist) -{ - if (dist <= 1.0f) - return 255; - - if (dist <= 5.0f) - return (((1.0f - ((dist * 0.25f) - 0.25f)) * 255.0f) + (((dist * 0.25f) - 0.25f) * 128.0f)); - - return 128; -} - -CRGBA CRadar::GetRadarTraceColour(uint32 color, bool bright) -{ - switch (color) { - case 0: - if (bright) - return CRGBA(113, 43, 73, 255); - else - return CRGBA(127, 0, 0, 255); - case 1: - if (bright) - return CRGBA(95, 160, 106, 255); - else - return CRGBA(127, 0, 255, 255); - case 2: - if (bright) - return CRGBA(128, 167, 243, 255); - else - return CRGBA(0, 127, 255, 255); - case 3: - if (bright) - return CRGBA(225, 225, 225, 255); - else - return CRGBA(127, 127, 127, 255); - case 4: - if (bright) - return CRGBA(255, 225, 0, 255); - else - return CRGBA(127, 127, 0, 255); - case 5: - if (bright) - return CRGBA(255, 0, 255, 255); - else - return CRGBA(127, 0, 127, 255); - case 6: - if (bright) - return CRGBA(255, 255, 255, 255); - else - return CRGBA(127, 127, 255, 255); - default: - return CRGBA(0, 0, 0, 255); - } -} - -void CRadar::TransformRadarPointToScreenSpace(CVector2D *out, CVector2D *in) -{ - out->x = in->x * SCREEN_SCALE_X(47.0f) + SCREEN_SCALE_X(47.0f + 20.0f); - out->y = (SCREEN_SCALE_Y(76.0f)) * 0.5f + SCREEN_HEIGHT - (SCREEN_SCALE_Y(123.0f)) - in->y * (SCREEN_SCALE_Y(76.0f)) * 0.5f; -} - -#if 0 -WRAPPER void CRadar::TransformRealWorldPointToRadarSpace(CVector2D *out, CVector2D *in) { EAXJMP(0x4A50D0); } -#else -void CRadar::TransformRealWorldPointToRadarSpace(CVector2D *out, CVector2D *in) -{ - if (TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_TOPDOWN1 && TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_TOPDOWNPED) { - if (TheCamera.GetLookDirection() != LOOKING_FORWARD) { - cachedSin = sin(atan2(-TheCamera.GetForward().x, TheCamera.GetForward().y)); - cachedCos = cos(atan2(-TheCamera.GetForward().x, TheCamera.GetForward().y)); - } - else { - CVector vecCamera; - - if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_FIRSTPERSON) { - vecCamera = TheCamera.Cams[TheCamera.ActiveCam].CamTargetEntity->m_matrix.m_matrix.up; - vecCamera.Normalise(); - } - else - vecCamera = TheCamera.GetForward(); - - cachedSin = sin(atan2(-vecCamera.x, vecCamera.y)); - cachedCos = cos(atan2(-vecCamera.x, vecCamera.y)); - } - } - else { - cachedSin = 0.0f; - cachedCos = 1.0f; - } - - float x = (in->x - vec2DRadarOrigin.x) * (1.0f / m_RadarRange); - float y = (in->y - vec2DRadarOrigin.y) * (1.0f / m_RadarRange); - - out->x = cachedSin * y + cachedCos * x; - out->y = cachedCos * y - cachedSin * x; -} -#endif - -#if 0 -WRAPPER void CRadar::DrawRadarSprite(int sprite, float x, float y, int alpha) { EAXJMP(0x4A5EF0); } -#else -void CRadar::DrawRadarSprite(int sprite, float x, float y, int alpha) -{ - RadarSprites[sprite]->Draw(CRect(x - SCREEN_SCALE_X(8.0f), y - SCREEN_SCALE_Y(8.0f), x + SCREEN_SCALE_X(8.0f), y + SCREEN_SCALE_Y(8.0f)), CRGBA(255, 255, 255, alpha)); + p1 = pos - TheCamera.GetRight()*f1; + p2 = pos - TheCamera.GetRight()*f2; + CTheScripts::ScriptDebugLine3D(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, color, color); } #endif @@ -518,114 +816,302 @@ void CRadar::ShowRadarTrace(float x, float y, uint32 size, uint32 red, uint32 gr #endif #if 0 -WRAPPER void CRadar::ShowRadarMarker(CVector pos, CRGBA color, float radius) { EAXJMP(0x4A59C0); } +WRAPPER void CRadar::Shutdown() { EAXJMP(0x4A3F60); } #else -void CRadar::ShowRadarMarker(CVector pos, CRGBA color, float radius) { - float z2 = pos.z + (0.5f * color.color32) * TheCamera.m_matrix.m_matrix.at.z; - float y2 = (0.5f * color.color32) * TheCamera.m_matrix.m_matrix.at.y + pos.y; - float x2 = (0.5f * color.color32) * TheCamera.m_matrix.m_matrix.at.x + pos.x; - float z1 = pos.z + (1.4f * color.color32) * TheCamera.m_matrix.m_matrix.at.z; - float y1 = (1.4f * color.color32) * TheCamera.m_matrix.m_matrix.at.y + pos.y; - float x1 = (1.4f * color.color32) * TheCamera.m_matrix.m_matrix.at.x + pos.x; - CTheScripts::ScriptDebugLine3D(x1, y1, z1, x2, y2, z2, color.color32, color.color32); +void CRadar::Shutdown() +{ - z2 = pos.z - (0.5f * color.color32) * TheCamera.m_matrix.m_matrix.at.x; - y2 = pos.y - (0.5f * color.color32) * TheCamera.m_matrix.m_matrix.at.x; - x2 = pos.x - (0.5f * color.color32) * TheCamera.m_matrix.m_matrix.at.x; - z1 = pos.z - (1.4f * color.color32) * TheCamera.m_matrix.m_matrix.at.x; - y1 = pos.y - (1.4f * color.color32) * TheCamera.m_matrix.m_matrix.at.x; - x1 = pos.x - (1.4f * color.color32) * TheCamera.m_matrix.m_matrix.at.x; - CTheScripts::ScriptDebugLine3D(x1, y1, z1, x2, y2, z2, color.color32, color.color32); - - z2 = (0.5f * color.color32) * TheCamera.m_matrix.m_matrix.right.x + pos.z; - y2 = (0.5f * color.color32) * TheCamera.m_matrix.m_matrix.right.x + pos.y; - x2 = (0.5f * color.color32) * TheCamera.m_matrix.m_matrix.right.x + pos.x; - z1 = (1.4f * color.color32) * TheCamera.m_matrix.m_matrix.right.x + pos.z; - y1 = (1.4f * color.color32) * TheCamera.m_matrix.m_matrix.right.x + pos.y; - x1 = (1.4f * color.color32) * TheCamera.m_matrix.m_matrix.right.x + pos.x; - CTheScripts::ScriptDebugLine3D(x1, y1, z1, x2, y2, z2, color.color32, color.color32); - - z2 = pos.z - (0.5f * color.color32) * TheCamera.m_matrix.m_matrix.right.x; - y2 = pos.y - (0.5f * color.color32) * TheCamera.m_matrix.m_matrix.right.x; - x2 = pos.x - (0.5f * color.color32) * TheCamera.m_matrix.m_matrix.right.x; - z1 = pos.z - (1.4f * color.color32) * TheCamera.m_matrix.m_matrix.right.x; - y1 = pos.y - (1.4f * color.color32) * TheCamera.m_matrix.m_matrix.right.x; - x1 = pos.x - (1.4f * color.color32) * TheCamera.m_matrix.m_matrix.right.x; - CTheScripts::ScriptDebugLine3D(x1, y1, z1, x2, y2, z2, color.color32, color.color32); } #endif #if 0 -WRAPPER void CRadar::DrawRotatingRadarSprite(CSprite2d* sprite, float x, float y, float angle, int alpha) { EAXJMP(0x4A5D10); } +WRAPPER void CRadar::StreamRadarSections() { EAXJMP(0x4A6B60); } #else -void CRadar::DrawRotatingRadarSprite(CSprite2d* sprite, float x, float y, float angle, int alpha) +void CRadar::StreamRadarSections(CVector posn) { - CVector curPosn[4]; - CVector oldPosn[4]; - curPosn[0].x = x - SCREEN_SCALE_X(5.6f); - curPosn[0].y = y + SCREEN_SCALE_Y(5.6f); - - curPosn[1].x = x + SCREEN_SCALE_X(5.6f); - curPosn[1].y = y + SCREEN_SCALE_Y(5.6f); - - curPosn[2].x = x - SCREEN_SCALE_X(5.6f); - curPosn[2].y = y - SCREEN_SCALE_Y(5.6f); - - curPosn[3].x = x + SCREEN_SCALE_X(5.6f); - curPosn[3].y = y - SCREEN_SCALE_Y(5.6f); - - for (uint32 i = 0; i < 4; i++) { - oldPosn[i] = curPosn[i]; - - curPosn[i].x = x + (oldPosn[i].x - x) * cosf(angle) + (oldPosn[i].y - y) * sinf(angle); - curPosn[i].y = y - (oldPosn[i].x - x) * sinf(angle) + (oldPosn[i].y - y) * cosf(angle); - } - - sprite->Draw(curPosn[2].x, curPosn[2].y, curPosn[3].x, curPosn[3].y, curPosn[0].x, curPosn[0].y, curPosn[1].x, curPosn[1].y, CRGBA(255, 255, 255, alpha)); } -#endif +#endif -bool CRadar::DisplayThisBlip(int counter) +#if 0 +WRAPPER void CRadar::StreamRadarSections(int32 x, int32 y) { EAXJMP(0x4A6100); } +#else +void CRadar::StreamRadarSections(int32 x, int32 y) { - switch (ms_RadarTrace[counter].m_IconID) { - case RADAR_SPRITE_BOMB: - case RADAR_SPRITE_SPRAY: - case RADAR_SPRITE_WEAPON: - return true; - default: - return false; + for (int i = 0; i < RADAR_NUM_TILES; ++i) { + for (int j = 0; j < RADAR_NUM_TILES; ++j) { + if ((i >= x - 1 && i <= x + 1) && (j >= y - 1 && j <= y + 1)) + RequestMapSection(i, j); + else + RemoveMapSection(i, j); + }; + }; +} +#endif + +#if 0 +WRAPPER void CRadar::TransformRealWorldToTexCoordSpace(CVector2D &out, const CVector2D &in, int32 x, int32 y) { EAXJMP(0x4A5530); } +#else +void CRadar::TransformRealWorldToTexCoordSpace(CVector2D &out, const CVector2D &in, int32 x, int32 y) +{ + out.x = in.x - (x * RADAR_TILE_SIZE + WORLD_MIN_X); + out.y = -(in.y - ((RADAR_NUM_TILES - y) * RADAR_TILE_SIZE + WORLD_MIN_Y)); + out.x /= RADAR_TILE_SIZE; + out.y /= RADAR_TILE_SIZE; +} +#endif + +#if 0 +WRAPPER void CRadar::TransformRadarPointToRealWorldSpace(CVector2D &out, const CVector2D &in) { EAXJMP(0x4A5300); } +#else +void CRadar::TransformRadarPointToRealWorldSpace(CVector2D &out, const CVector2D &in) +{ + float s, c; + + s = -sin(atan2(-TheCamera.GetForward().x, TheCamera.GetForward().y)); + c = cos(atan2(-TheCamera.GetForward().x, TheCamera.GetForward().y)); + + if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN1 || TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWNPED) { + s = 0.0f; + c = 1.0f; } -} + else if (TheCamera.GetLookDirection() != LOOKING_FORWARD) { + CVector forward; -#if 0 -WRAPPER void CRadar::GetTextureCorners(int x, int y, CVector2D *out) { EAXJMP(0x4A61C0); }; -#else -void CRadar::GetTextureCorners(int x, int y, CVector2D *out) -{ - out[0].x = 500.0f * (x - 4); - out[0].y = 500.0f * (3 - y); - out[1].x = 500.0f * (y - 4 + 1); - out[1].y = 500.0f * (3 - y); - out[2].x = 500.0f * (y - 4 + 1); - out[2].y = 500.0f * (3 - y + 1); - out[3].x = 500.0f * (x - 4); - out[3].y = 500.0f * (3 - y + 1); + if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_FIRSTPERSON) { + forward = TheCamera.Cams[TheCamera.ActiveCam].CamTargetEntity->GetForward(); + forward.Normalise(); // a bit useless... + } + else + forward = TheCamera.Cams[TheCamera.ActiveCam].CamTargetEntity->GetPosition() - TheCamera.Cams[TheCamera.ActiveCam].SourceBeforeLookBehind; + + s = -sin(atan2(-forward.x, forward.y)); + c = cos(atan2(-forward.x, forward.y)); + } + + out.x = s * in.y + c * in.x; + out.y = c * in.y - s * in.x; + + out = out * m_RadarRange + vec2DRadarOrigin; } #endif -void CRadar::ClipRadarTileCoords(int *x, int *y) +// Radar space goes from -1.0 to 1.0 in x and y, top right is (1.0, 1.0) +void CRadar::TransformRadarPointToScreenSpace(CVector2D &out, const CVector2D &in) { - if (*x < 0) - *x = 0; - if (*x > 7) - *x = 7; - if (*y < 0) - *y = 0; - if (*y > 7) - *y = 7; + // FIX: game doesn't scale RADAR_LEFT here + out.x = (in.x + 1.0f)*0.5f*SCREEN_SCALE_X(RADAR_WIDTH) + SCREEN_SCALE_X(RADAR_LEFT); + out.y = (1.0f - in.y)*0.5f*SCREEN_SCALE_Y(RADAR_HEIGHT) + SCREEN_SCALE_FROM_BOTTOM(RADAR_BOTTOM + RADAR_HEIGHT); } +#if 0 +WRAPPER void CRadar::TransformRealWorldPointToRadarSpace(CVector2D &out, const CVector2D &in) { EAXJMP(0x4A50D0); } +#else +void CRadar::TransformRealWorldPointToRadarSpace(CVector2D &out, const CVector2D &in) +{ + float s, c; + if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWN1 || TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_TOPDOWNPED) { + s = 0.0f; + c = 1.0f; + } + else if (TheCamera.GetLookDirection() == LOOKING_FORWARD) { + s = sin(atan2(-TheCamera.GetForward().x, TheCamera.GetForward().y)); + c = cos(atan2(-TheCamera.GetForward().x, TheCamera.GetForward().y)); + } + else { + CVector forward; + + if (TheCamera.Cams[TheCamera.ActiveCam].Mode == CCam::MODE_FIRSTPERSON) { + forward = TheCamera.Cams[TheCamera.ActiveCam].CamTargetEntity->GetForward(); + forward.Normalise(); // a bit useless... + } + else + forward = TheCamera.Cams[TheCamera.ActiveCam].CamTargetEntity->GetPosition() - TheCamera.Cams[TheCamera.ActiveCam].SourceBeforeLookBehind; + + s = sin(atan2(-forward.x, forward.y)); + c = cos(atan2(-forward.x, forward.y)); + } + + float x = (in.x - vec2DRadarOrigin.x) * (1.0f / m_RadarRange); + float y = (in.y - vec2DRadarOrigin.y) * (1.0f / m_RadarRange); + + out.x = s * y + c * x; + out.y = c * y - s * x; +} +#endif + +#if 0 +WRAPPER void CRadar::GetTextureCorners(int32 x, int32 y, CVector2D *out) { EAXJMP(0x4A61C0); }; +#else +// Transform from section indices to world coordinates +void CRadar::GetTextureCorners(int32 x, int32 y, CVector2D *out) +{ + x = x - RADAR_NUM_TILES/2; + y = -(y - RADAR_NUM_TILES/2); + + // bottom left + out[0].x = RADAR_TILE_SIZE * (x); + out[0].y = RADAR_TILE_SIZE * (y - 1); + + // bottom right + out[1].x = RADAR_TILE_SIZE * (x + 1); + out[1].y = RADAR_TILE_SIZE * (y - 1); + + // top right + out[2].x = RADAR_TILE_SIZE * (x + 1); + out[2].y = RADAR_TILE_SIZE * (y); + + // top left + out[3].x = RADAR_TILE_SIZE * (x); + out[3].y = RADAR_TILE_SIZE * (y); +} +#endif + +#if 0 +WRAPPER void CRadar::ClipRadarTileCoords(int32 &, int32 &) { EAXJMP(0x00); }; +#else +void CRadar::ClipRadarTileCoords(int32 &x, int32 &y) +{ + if (x < 0) + x = 0; + if (x > RADAR_NUM_TILES-1) + x = RADAR_NUM_TILES-1; + if (y < 0) + y = 0; + if (y > RADAR_NUM_TILES-1) + y = RADAR_NUM_TILES-1; +} +#endif + + +#if 0 +WRAPPER bool CRadar::IsPointInsideRadar(const CVector2D &) { EAXJMP(0x4A6160); } +#else +bool CRadar::IsPointInsideRadar(const CVector2D &point) +{ + if (point.x < -1.0f || point.x > 1.0f) return false; + if (point.y < -1.0f || point.y > 1.0f) return false; + return true; +} +#endif + +// clip line p1,p2 against (-1.0, 1.0) in x and y, set out to clipped point closest to p1 +#if 0 +WRAPPER int CRadar::LineRadarBoxCollision(CVector2D &, const CVector2D &, const CVector2D &) { EAXJMP(0x4A6250); } +#else +int CRadar::LineRadarBoxCollision(CVector2D &out, const CVector2D &p1, const CVector2D &p2) +{ + float d1, d2; + float t; + float x, y; + float shortest = 1.0f; + int edge = -1; + + // clip against left edge, x = -1.0 + d1 = -1.0f - p1.x; + d2 = -1.0f - p2.x; + if (d1 * d2 < 0.0f) { + // they are on opposite sides, get point of intersection + t = d1 / (d1 - d2); + y = (p2.y - p1.y)*t + p1.y; + if (y >= -1.0f && y <= 1.0f && t <= shortest) { + out.x = -1.0f; + out.y = y; + edge = 3; + shortest = t; + } + } + + // clip against right edge, x = 1.0 + d1 = p1.x - 1.0f; + d2 = p2.x - 1.0f; + if (d1 * d2 < 0.0f) { + // they are on opposite sides, get point of intersection + t = d1 / (d1 - d2); + y = (p2.y - p1.y)*t + p1.y; + if (y >= -1.0f && y <= 1.0f && t <= shortest) { + out.x = 1.0f; + out.y = y; + edge = 1; + shortest = t; + } + } + + // clip against top edge, y = -1.0 + d1 = -1.0f - p1.y; + d2 = -1.0f - p2.y; + if (d1 * d2 < 0.0f) { + // they are on opposite sides, get point of intersection + t = d1 / (d1 - d2); + x = (p2.x - p1.x)*t + p1.x; + if (x >= -1.0f && x <= 1.0f && t <= shortest) { + out.y = -1.0f; + out.x = x; + edge = 0; + shortest = t; + } + } + + // clip against bottom edge, y = 1.0 + d1 = p1.y - 1.0f; + d2 = p2.y - 1.0f; + if (d1 * d2 < 0.0f) { + // they are on opposite sides, get point of intersection + t = d1 / (d1 - d2); + x = (p2.x - p1.x)*t + p1.x; + if (x >= -1.0f && x <= 1.0f && t <= shortest) { + out.y = 1.0f; + out.x = x; + edge = 2; + shortest = t; + } + } + + return edge; +} +#endif + STARTPATCHES +// InjectHook(0x4A3EF0, CRadar::Initialise, PATCH_JUMP); +// InjectHook(0x4A3F60, CRadar::Shutdown, PATCH_JUMP); +// InjectHook(0x4A4030, CRadar::LoadTextures, PATCH_JUMP); +// InjectHook(0x4A4180, CRadar::GetNewUniqueBlipIndex, PATCH_JUMP); +// InjectHook(0x4A41C0, CRadar::GetActualBlipArrayIndex, PATCH_JUMP); + InjectHook(0x4A4200, CRadar::DrawMap, PATCH_JUMP); + InjectHook(0x4A42F0, CRadar::DrawBlips, PATCH_JUMP); +// InjectHook(0x4A4C70, CRadar::Draw3dMarkers, PATCH_JUMP); + InjectHook(0x4A4F30, CRadar::LimitRadarPoint, PATCH_JUMP); + InjectHook(0x4A4F90, CRadar::CalculateBlipAlpha, PATCH_JUMP); InjectHook(0x4A5040, CRadar::TransformRadarPointToScreenSpace, PATCH_JUMP); + InjectHook(0x4A50D0, CRadar::TransformRealWorldPointToRadarSpace, PATCH_JUMP); + InjectHook(0x4A5300, CRadar::TransformRadarPointToRealWorldSpace, PATCH_JUMP); + InjectHook(0x4A5530, CRadar::TransformRealWorldToTexCoordSpace, PATCH_JUMP); +// InjectHook(0x4A5590, CRadar::SetCoordBlip, PATCH_JUMP); +// InjectHook(0x4A5640, CRadar::SetEntityBlip, PATCH_JUMP); + InjectHook(0x4A56C0, CRadar::ClearBlipForEntity, PATCH_JUMP); +// InjectHook(0x4A5720, CRadar::ClearBlip, PATCH_JUMP); +// InjectHook(0x4A5770, CRadar::ChangeBlipColour, PATCH_JUMP); +// InjectHook(0x4A57A0, CRadar::ChangeBlipBrightness, PATCH_JUMP); +// InjectHook(0x4A57E0, CRadar::ChangeBlipScale, PATCH_JUMP); +// InjectHook(0x4A5810, CRadar::ChangeBlipDisplay, PATCH_JUMP); +// InjectHook(0x4A5840, CRadar::SetBlipSprite, PATCH_JUMP); + InjectHook(0x4A5870, CRadar::ShowRadarTrace, PATCH_JUMP); + InjectHook(0x4A59C0, CRadar::ShowRadarMarker, PATCH_JUMP); + //InjectHook(0x4A5BB0, CRadar::GetRadarTraceColour, PATCH_JUMP); + InjectHook(0x4A5C60, CRadar::SetRadarMarkerState, PATCH_JUMP); + InjectHook(0x4A5D10, CRadar::DrawRotatingRadarSprite, PATCH_JUMP); + InjectHook(0x4A5EF0, CRadar::DrawRadarSprite, PATCH_JUMP); +// InjectHook(0x4A60E0, CRadar::RemoveRadarSections, PATCH_JUMP); +// InjectHook(0x4A6100, CRadar::StreamRadarSections, PATCH_JUMP); + InjectHook(0x4A64A0, CRadar::ClipRadarPoly, PATCH_JUMP); + InjectHook(0x4A67E0, CRadar::DrawRadarSection, PATCH_JUMP); + InjectHook(0x4A69C0, CRadar::DrawRadarMask, PATCH_JUMP); +// InjectHook(0x4A6B60, CRadar::StreamRadarSections, PATCH_JUMP); + InjectHook(0x4A6C20, CRadar::DrawRadarMap, PATCH_JUMP); +// InjectHook(0x4A6E30, CRadar::SaveAllRadarBlips, PATCH_JUMP); +// InjectHook(0x4A6F30, CRadar::LoadAllRadarBlips, PATCH_JUMP); + + InjectHook(0x4A61C0, CRadar::GetTextureCorners, PATCH_JUMP); + InjectHook(0x4A6160, CRadar::IsPointInsideRadar, PATCH_JUMP); + InjectHook(0x4A6250, CRadar::LineRadarBoxCollision, PATCH_JUMP); ENDPATCHES diff --git a/src/Radar.h b/src/Radar.h index 19fc9038..5943498f 100644 --- a/src/Radar.h +++ b/src/Radar.h @@ -53,26 +53,26 @@ struct CBlip CVector2D m_vec2DPos; CVector m_vecPos; int16 m_BlipIndex; - int8 m_bDim; - int8 m_bInUse; + bool m_bDim; + bool m_bInUse; float m_Radius; int16 m_wScale; int16 m_eBlipDisplay; // eBlipDisplay int16 m_IconID; // eRadarSprite - char gap_46[2]; }; - static_assert(sizeof(CBlip) == 0x30, "CBlip: error"); +// Values for screen space +#define RADAR_LEFT (40.0f) +#define RADAR_BOTTOM (47.0f) +#define RADAR_WIDTH (94.0f) +#define RADAR_HEIGHT (76.0f) + class CRadar { - static float cachedSin; - static float cachedCos; - public: static float &m_RadarRange; - static CVector2D &vec2DRadarOrigin; - static CBlip *ms_RadarTrace; + static CBlip *ms_RadarTrace; //[NUMRADARBLIPS] static CSprite2d *AsukaSprite; static CSprite2d *BombSprite; static CSprite2d *CatSprite; @@ -96,30 +96,51 @@ public: static CSprite2d *RadarSprites[21]; public: - static void ClearBlipForEntity(eBlipType type, int32 id); - static void Draw3dMarkers(); - static void DrawMap(); - static void StreamRadarSections(int x, int y); - static int ClipRadarPoly(CVector2D *out, CVector2D *in); - static void TransformRealWorldToTexCoordSpace(CVector2D *out, CVector2D *in, int x, int y); - static void TransformRadarPointToRealWorldSpace(CVector2D *out, CVector2D *in); - static void DrawRadarSection(int x, int y); - static void RequestMapSection(int x, int y); - static void RemoveMapSection(int x, int y); - static void TransformRadarPointToScreenSpace(CVector2D * out, CVector2D * in); - static void DrawBlips(); static int CalculateBlipAlpha(float dist); - static CRGBA GetRadarTraceColour(uint32 color, bool bright); + static void ChangeBlipBrightness(int32 i, int32 bright); + static void ChangeBlipColour(int32 i); + static void ChangeBlipDisplay(int32 i, int16 flag); + static void ChangeBlipScale(int32 i, int16 scale); + static void ClearBlip(int32 i); + static void ClearBlipForEntity(int16 type, int32 id); + static int ClipRadarPoly(CVector2D *out, const CVector2D *in); + static bool DisplayThisBlip(int32 i); + static void Draw3dMarkers(); + static void DrawBlips(); + static void DrawMap(); static void DrawRadarMap(); - static void DrawRotatingRadarSprite(CSprite2d* sprite, float x, float y, float angle, int alpha); - static void TransformRealWorldPointToRadarSpace(CVector2D *out, CVector2D *in); - static float LimitRadarPoint(CVector2D *point); - static void DrawRadarSprite(int sprite, float x, float y, int alpha); - static void ShowRadarMarker(CVector pos, CRGBA color, float radius); - static void ShowRadarTrace(float x, float y, uint32 size, uint32 red, uint32 green, uint32 blue, uint32 alpha); static void DrawRadarMask(); - static void SetRadarMarkerState(int counter, int flag); - static bool DisplayThisBlip(int counter); - static void GetTextureCorners(int x, int y, CVector2D * out); - static void ClipRadarTileCoords(int *x, int *y); + static void DrawRadarSection(int32 x, int32 y); + static void DrawRadarSprite(int32 sprite, float x, float y, int32 alpha); + static void DrawRotatingRadarSprite(CSprite2d* sprite, float x, float y, float angle, int32 alpha); + static int32 GetActualBlipArray(int32 i); + static int32 GetNewUniqueBlipIndex(int32 i); + static int32 GetRadarTraceColour(int32 color, bool bright); + static void Initialise(); + static float LimitRadarPoint(CVector2D &point); + static void LoadAllRadarBlips(int32); + static void LoadTextures(); + static void RemoveRadarSections(); + static void RemoveMapSection(int32 x, int32 y); + static void RequestMapSection(int32 x, int32 y); + static void SaveAllRadarBlips(int32); + static void SetBlipSprite(int32 i, int32 icon); + static int SetCoordBlip(int32 type, CVector pos, int32 flag); + static int SetEntityBlip(int32 type, CVector pos, int32 color, int32 flag); + static void SetRadarMarkerState(int32 i, int32 flag); + static void ShowRadarMarker(CVector pos, int16 color, float radius); + static void ShowRadarTrace(float x, float y, uint32 size, uint32 red, uint32 green, uint32 blue, uint32 alpha); + static void Shutdown(); + static void StreamRadarSections(CVector posn); + static void StreamRadarSections(int32 x, int32 y); + static void TransformRealWorldToTexCoordSpace(CVector2D &out, const CVector2D &in, int32 x, int32 y); + static void TransformRadarPointToRealWorldSpace(CVector2D &out, const CVector2D &in); + static void TransformRadarPointToScreenSpace(CVector2D &out, const CVector2D &in); + static void TransformRealWorldPointToRadarSpace(CVector2D &out, const CVector2D &in); + + // no in CRadar in the game: + static void GetTextureCorners(int32 x, int32 y, CVector2D *out); + static void ClipRadarTileCoords(int32 &x, int32 &y); + static bool IsPointInsideRadar(const CVector2D &); + static int LineRadarBoxCollision(CVector2D &, const CVector2D &, const CVector2D &); }; diff --git a/src/RwHelper.cpp b/src/RwHelper.cpp index 5aa31e92..3c198272 100644 --- a/src/RwHelper.cpp +++ b/src/RwHelper.cpp @@ -108,6 +108,23 @@ GetFirstAtomic(RpClump *clump) return atm; } +RwTexture* +GetFirstTextureCallback(RwTexture *tex, void *data) +{ + *(RwTexture**)data = tex; + return nil; +} + +RwTexture* +GetFirstTexture(RwTexDictionary *txd) +{ + RwTexture *tex; + + tex = nil; + RwTexDictionaryForAllTextures(txd, GetFirstTextureCallback, &tex); + return tex; +} + void CameraSize(RwCamera * camera, RwRect * rect, RwReal viewWindow, RwReal aspectRatio) diff --git a/src/RwHelper.h b/src/RwHelper.h index e0ec00a3..ef20467d 100644 --- a/src/RwHelper.h +++ b/src/RwHelper.h @@ -7,6 +7,7 @@ void DefinedState(void); RwFrame *GetFirstChild(RwFrame *frame); RwObject *GetFirstObject(RwFrame *frame); RpAtomic *GetFirstAtomic(RpClump *clump); +RwTexture *GetFirstTexture(RwTexDictionary *txd); RwTexDictionary *RwTexDictionaryGtaStreamRead(RwStream *stream); RwTexDictionary *RwTexDictionaryGtaStreamRead1(RwStream *stream); diff --git a/src/Streaming.cpp b/src/Streaming.cpp index 2504ed9c..149de7c3 100644 --- a/src/Streaming.cpp +++ b/src/Streaming.cpp @@ -1,5 +1,8 @@ #include "common.h" #include "patcher.h" +#include "Pad.h" +#include "Hud.h" +#include "Text.h" #include "ModelInfo.h" #include "TxdStore.h" #include "ModelIndices.h" @@ -13,11 +16,6 @@ #include "CdStream.h" #include "Streaming.h" -/* -CStreaming::ms_channelError 0x880DB8 -CStreaming::ms_lastVehicleDeleted 0x95CBF8 -*/ - bool &CStreaming::ms_disableStreaming = *(bool*)0x95CD6E; bool &CStreaming::ms_bLoadingBigModel = *(bool*)0x95CDB0; int32 &CStreaming::ms_numModelsRequested = *(int32*)0x8E2C10; @@ -28,12 +26,14 @@ CStreamingInfo &CStreaming::ms_startRequestedList = *(CStreamingInfo*)0x8F1B3C; CStreamingInfo &CStreaming::ms_endRequestedList = *(CStreamingInfo*)0x940738; int32 &CStreaming::ms_oldSectorX = *(int32*)0x8F2C84; int32 &CStreaming::ms_oldSectorY = *(int32*)0x8F2C88; -uint32 &CStreaming::ms_streamingBufferSize = *(uint32*)0x942FB0; -uint8 **CStreaming::ms_pStreamingBuffer = (uint8**)0x87F818; +int32 &CStreaming::ms_streamingBufferSize = *(int32*)0x942FB0; +int8 **CStreaming::ms_pStreamingBuffer = (int8**)0x87F818; int32 &CStreaming::ms_memoryUsed = *(int32*)0x940568; CStreamingChannel *CStreaming::ms_channel = (CStreamingChannel*)0x727EE0; +int32 &CStreaming::ms_channelError = *(int32*)0x880DB8; int32 &CStreaming::ms_numVehiclesLoaded = *(int32*)0x8F2C80; int32 *CStreaming::ms_vehiclesLoaded = (int32*)0x773560; +int32 &CStreaming::ms_lastVehicleDeleted = *(int32*)0x95CBF8; CDirectory *&CStreaming::ms_pExtraObjectsDir = *(CDirectory**)0x95CB90; int32 &CStreaming::ms_numPriorityRequests = *(int32*)0x8F31C4; bool &CStreaming::ms_hasLoadedLODs = *(bool*)0x95CD47; @@ -60,8 +60,8 @@ int32 &islandLODsubInd = *(int32*)0x6212D4; int32 &islandLODsubCom = *(int32*)0x6212D8; WRAPPER void CStreaming::MakeSpaceFor(int32 size) { EAXJMP(0x409B70); } -WRAPPER bool CStreaming::IsTxdUsedByRequestedModels(int32 txdId) { EAXJMP(0x4094C0); } -WRAPPER bool CStreaming::AddToLoadedVehiclesList(int32 modelId) { EAXJMP(0x40B060); } +//WRAPPER bool CStreaming::IsTxdUsedByRequestedModels(int32 txdId) { EAXJMP(0x4094C0); } +//WRAPPER bool CStreaming::AddToLoadedVehiclesList(int32 modelId) { EAXJMP(0x40B060); } void @@ -78,6 +78,8 @@ CStreaming::Init(void) ms_aInfoForModel[i].m_position = 0; } + ms_channelError = -1; + // init lists ms_startLoadedList.m_next = &ms_endLoadedList; @@ -101,12 +103,12 @@ CStreaming::Init(void) // init channels - ms_channel[0].state = CHANNELSTATE_0; - ms_channel[1].state = CHANNELSTATE_0; + ms_channel[0].state = CHANNELSTATE_IDLE; + ms_channel[1].state = CHANNELSTATE_IDLE; for(i = 0; i < 4; i++){ - ms_channel[0].modelIds[i] = -1; + ms_channel[0].streamIds[i] = -1; ms_channel[0].offsets[i] = -1; - ms_channel[1].modelIds[i] = -1; + ms_channel[1].streamIds[i] = -1; ms_channel[1].offsets[i] = -1; } @@ -115,8 +117,8 @@ CStreaming::Init(void) for(i = 0; i < MODELINFOSIZE; i++){ CBaseModelInfo *mi = CModelInfo::GetModelInfo(i); if(mi && mi->GetRwObject()){ - ms_aInfoForModel[i + STREAM_OFFSET_MODEL].m_loadState = STREAMSTATE_LOADED; - ms_aInfoForModel[i + STREAM_OFFSET_MODEL].m_flags = STREAMFLAGS_DONT_REMOVE; + ms_aInfoForModel[i].m_loadState = STREAMSTATE_LOADED; + ms_aInfoForModel[i].m_flags = STREAMFLAGS_DONT_REMOVE; if(mi->IsSimple()) ((CSimpleModelInfo*)mi)->m_alpha = 255; } @@ -143,7 +145,7 @@ CStreaming::Init(void) // allocate streaming buffers if(ms_streamingBufferSize & 1) ms_streamingBufferSize++; - ms_pStreamingBuffer[0] = (uint8*)RwMallocAlign(ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE, CDSTREAM_SECTOR_SIZE); + ms_pStreamingBuffer[0] = (int8*)RwMallocAlign(ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE, CDSTREAM_SECTOR_SIZE); ms_streamingBufferSize /= 2; ms_pStreamingBuffer[1] = ms_pStreamingBuffer[0] + ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE; debug("Streaming buffer size is %d sectors", ms_streamingBufferSize); @@ -211,6 +213,9 @@ CStreaming::LoadCdDirectory(void) ms_imageOffsets[i] = -1; ms_imageSize = GetGTA3ImgSize(); + // PS2 uses CFileMgr::GetCdFile on all IMG files to fill the array + + i = CdStreamGetNumImages(); while(i-- >= 1){ strcpy(dirname, CdStreamGetImageName(i)); @@ -240,20 +245,20 @@ CStreaming::LoadCdDirectory(const char *dirname, int n) while(CFileMgr::Read(fd, (char*)&direntry, sizeof(direntry))){ dot = strchr(direntry.name, '.'); if(dot) *dot = '\0'; - if(direntry.size > ms_streamingBufferSize) + if(direntry.size > (uint32)ms_streamingBufferSize) ms_streamingBufferSize = direntry.size; if(strcmp(dot+1, "DFF") == 0 || strcmp(dot+1, "dff") == 0){ if(CModelInfo::GetModelInfo(direntry.name, &modelId)){ - if(ms_aInfoForModel[modelId + STREAM_OFFSET_MODEL].GetCdPosnAndSize(posn, size)){ + if(ms_aInfoForModel[modelId].GetCdPosnAndSize(posn, size)){ debug("%s appears more than once in %s\n", direntry.name, dirname); lastID = -1; }else{ direntry.offset |= imgSelector; - ms_aInfoForModel[modelId + STREAM_OFFSET_MODEL].SetCdPosnAndSize(direntry.offset, direntry.size); + ms_aInfoForModel[modelId].SetCdPosnAndSize(direntry.offset, direntry.size); if(lastID != -1) - ms_aInfoForModel[lastID].m_nextID = modelId + STREAM_OFFSET_MODEL; - lastID = modelId + STREAM_OFFSET_MODEL; + ms_aInfoForModel[lastID].m_nextID = modelId; + lastID = modelId; } }else{ // BUG: doesn't remember which cdimage this was in @@ -300,15 +305,14 @@ CStreaming::ConvertBufferToObject(int8 *buf, int32 streamId) if(streamId < STREAM_OFFSET_TXD){ // Model - mi = CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL); + mi = CModelInfo::GetModelInfo(streamId); // Txd has to be loaded if(CTxdStore::GetSlot(mi->GetTxdSlot())->texDict == nil){ debug("failed to load %s because TXD %s is not in memory\n", mi->GetName(), CTxdStore::GetTxdName(mi->GetTxdSlot())); RemoveModel(streamId); - RemoveModel(mi->GetTxdSlot() + STREAM_OFFSET_TXD); - // re-request - RequestModel(streamId, ms_aInfoForModel[streamId].m_flags); + RemoveTxd(mi->GetTxdSlot()); + ReRequestModel(streamId); RwStreamClose(stream, &mem); return false; } @@ -318,15 +322,15 @@ CStreaming::ConvertBufferToObject(int8 *buf, int32 streamId) CTxdStore::SetCurrentTxd(mi->GetTxdSlot()); if(mi->IsSimple()){ - success = CFileLoader::LoadAtomicFile(stream, streamId - STREAM_OFFSET_MODEL); + success = CFileLoader::LoadAtomicFile(stream, streamId); }else if(mi->m_type == MITYPE_VEHICLE){ // load vehicles in two parts - CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL)->AddRef(); - success = CFileLoader::StartLoadClumpFile(stream, streamId - STREAM_OFFSET_MODEL); + CModelInfo::GetModelInfo(streamId)->AddRef(); + success = CFileLoader::StartLoadClumpFile(stream, streamId); if(success) ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_STARTED; }else{ - success = CFileLoader::LoadClumpFile(stream, streamId - STREAM_OFFSET_MODEL); + success = CFileLoader::LoadClumpFile(stream, streamId); } UpdateMemoryUsed(); @@ -335,10 +339,9 @@ CStreaming::ConvertBufferToObject(int8 *buf, int32 streamId) CTxdStore::RemoveRefWithoutDelete(mi->GetTxdSlot()); if(!success){ - debug("Failed to load %s\n", CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL)->GetName()); + debug("Failed to load %s\n", CModelInfo::GetModelInfo(streamId)->GetName()); RemoveModel(streamId); - // re-request - RequestModel(streamId, ms_aInfoForModel[streamId].m_flags); + ReRequestModel(streamId); RwStreamClose(stream, &mem); return false; } @@ -363,8 +366,7 @@ CStreaming::ConvertBufferToObject(int8 *buf, int32 streamId) if(!success){ debug("Failed to load %s.txd\n", CTxdStore::GetTxdName(streamId - STREAM_OFFSET_TXD)); RemoveModel(streamId); - // re-request - RequestModel(streamId, ms_aInfoForModel[streamId].m_flags); + ReRequestModel(streamId); RwStreamClose(stream, &mem); return false; } @@ -374,7 +376,7 @@ CStreaming::ConvertBufferToObject(int8 *buf, int32 streamId) // We shouldn't even end up here unless load was successful if(!success){ - RequestModel(streamId, ms_aInfoForModel[streamId].m_flags); + ReRequestModel(streamId); if(streamId < STREAM_OFFSET_TXD) debug("Failed to load %s.dff\n", mi->GetName()); else @@ -415,7 +417,7 @@ CStreaming::ConvertBufferToObject(int8 *buf, int32 streamId) timeDiff = endTime - startTime; if(timeDiff > 5){ if(streamId < STREAM_OFFSET_TXD) - debug("model %s took %d ms\n", CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL)->GetName(), timeDiff); + debug("model %s took %d ms\n", CModelInfo::GetModelInfo(streamId)->GetName(), timeDiff); else debug("txd %s took %d ms\n", CTxdStore::GetTxdName(streamId - STREAM_OFFSET_TXD), timeDiff); } @@ -437,7 +439,7 @@ CStreaming::FinishLoadingLargeFile(int8 *buf, int32 streamId) if(ms_aInfoForModel[streamId].m_loadState != STREAMSTATE_STARTED){ if(streamId < STREAM_OFFSET_TXD) - CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL)->RemoveRef(); + CModelInfo::GetModelInfo(streamId)->RemoveRef(); return false; } @@ -447,7 +449,7 @@ CStreaming::FinishLoadingLargeFile(int8 *buf, int32 streamId) if(streamId < STREAM_OFFSET_TXD){ // Model - mi = CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL); + mi = CModelInfo::GetModelInfo(streamId); CTxdStore::SetCurrentTxd(mi->GetTxdSlot()); success = CFileLoader::FinishLoadClumpFile(stream, streamId); if(success) @@ -467,8 +469,7 @@ CStreaming::FinishLoadingLargeFile(int8 *buf, int32 streamId) if(!success){ RemoveModel(streamId); - // re-request - RequestModel(streamId, ms_aInfoForModel[streamId].m_flags); + ReRequestModel(streamId); UpdateMemoryUsed(); return false; } @@ -479,7 +480,7 @@ CStreaming::FinishLoadingLargeFile(int8 *buf, int32 streamId) timeDiff = endTime - startTime; if(timeDiff > 5){ if(streamId < STREAM_OFFSET_TXD) - debug("finish model %s took %d ms\n", CModelInfo::GetModelInfo(streamId - STREAM_OFFSET_MODEL)->GetName(), timeDiff); + debug("finish model %s took %d ms\n", CModelInfo::GetModelInfo(streamId)->GetName(), timeDiff); else debug("finish txd %s took %d ms\n", CTxdStore::GetTxdName(streamId - STREAM_OFFSET_TXD), timeDiff); } @@ -494,7 +495,7 @@ CStreaming::RequestModel(int32 id, int32 flags) if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_INQUEUE){ // updgrade to priority - if(flags & STREAMFLAGS_PRIORITY && (ms_aInfoForModel[id].m_flags & STREAMFLAGS_PRIORITY) == 0){ + if(flags & STREAMFLAGS_PRIORITY && !ms_aInfoForModel[id].IsPriority()){ ms_numPriorityRequests++; ms_aInfoForModel[id].m_flags |= STREAMFLAGS_PRIORITY; } @@ -507,7 +508,7 @@ CStreaming::RequestModel(int32 id, int32 flags) // Already loaded, only check changed flags if(ms_aInfoForModel[id].m_flags & STREAMFLAGS_NOFADE && id < STREAM_OFFSET_TXD){ - mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(id - STREAM_OFFSET_MODEL); + mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(id); if(mi->IsSimple()) mi->m_alpha = 255; } @@ -523,7 +524,7 @@ CStreaming::RequestModel(int32 id, int32 flags) if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_NOTLOADED){ if(id < STREAM_OFFSET_TXD) - RequestTxd(CModelInfo::GetModelInfo(id - STREAM_OFFSET_MODEL)->GetTxdSlot(), flags); + RequestTxd(CModelInfo::GetModelInfo(id)->GetTxdSlot(), flags); ms_aInfoForModel[id].AddToList(&ms_startRequestedList); ms_numModelsRequested++; if(flags & STREAMFLAGS_PRIORITY) @@ -657,6 +658,16 @@ CStreaming::RequestSpecialChar(int32 charId, const char *modelName, int32 flags) RequestSpecialModel(charId + MI_SPECIAL01, modelName, flags); } +void +CStreaming::DecrementRef(int32 id) +{ + ms_numModelsRequested--; + if(ms_aInfoForModel[id].IsPriority()){ + ms_aInfoForModel[id].m_flags &= ~STREAMFLAGS_PRIORITY; + ms_numPriorityRequests--; + } +} + void CStreaming::RemoveModel(int32 id) { @@ -667,7 +678,7 @@ CStreaming::RemoveModel(int32 id) if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_LOADED){ if(id < STREAM_OFFSET_TXD) - CModelInfo::GetModelInfo(id - STREAM_OFFSET_MODEL)->DeleteRwObject(); + CModelInfo::GetModelInfo(id)->DeleteRwObject(); else CTxdStore::RemoveTxd(id - STREAM_OFFSET_TXD); ms_memoryUsed -= ms_aInfoForModel[id].GetCdSize()*CDSTREAM_SECTOR_SIZE; @@ -675,20 +686,15 @@ CStreaming::RemoveModel(int32 id) if(ms_aInfoForModel[id].m_next){ // Remove from list, model is neither loaded nor requested - if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_INQUEUE){ - ms_numModelsRequested--; - if(ms_aInfoForModel[id].m_flags & STREAMFLAGS_PRIORITY){ - ms_aInfoForModel[id].m_flags &= ~STREAMFLAGS_PRIORITY; - ms_numPriorityRequests--; - } - } + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_INQUEUE) + DecrementRef(id); ms_aInfoForModel[id].RemoveFromList(); }else if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_READING){ for(i = 0; i < 4; i++){ - if(ms_channel[0].modelIds[i] == id - STREAM_OFFSET_MODEL) - ms_channel[0].modelIds[i] = -1; - if(ms_channel[1].modelIds[i] == id - STREAM_OFFSET_MODEL) - ms_channel[1].modelIds[i] = -1; + if(ms_channel[0].streamIds[i] == id) + ms_channel[0].streamIds[i] = -1; + if(ms_channel[1].streamIds[i] == id) + ms_channel[1].streamIds[i] = -1; } } @@ -702,10 +708,868 @@ CStreaming::RemoveModel(int32 id) ms_aInfoForModel[id].m_loadState = STREAMSTATE_NOTLOADED; } +void +CStreaming::RemoveUnusedBuildings(eLevelName level) +{ + if(level != LEVEL_INDUSTRIAL) + RemoveBuildings(LEVEL_INDUSTRIAL); + if(level != LEVEL_COMMERCIAL) + RemoveBuildings(LEVEL_COMMERCIAL); + if(level != LEVEL_SUBURBAN) + RemoveBuildings(LEVEL_SUBURBAN); +} + +void +CStreaming::RemoveBuildings(eLevelName level) +{ + int i, n; + CEntity *e; + CBaseModelInfo *mi; + + n = CPools::GetBuildingPool()->GetSize(); + for(i = 0; i < n; i++){ + e = CPools::GetBuildingPool()->GetSlot(i); + if(e && e->m_level == level){ + mi = CModelInfo::GetModelInfo(e->GetModelIndex()); + if(!e->bImBeingRendered){ + e->DeleteRwObject(); + if(mi->m_refCount == 0) + RemoveModel(e->GetModelIndex()); + } + } + } + + n = CPools::GetTreadablePool()->GetSize(); + for(i = 0; i < n; i++){ + e = CPools::GetTreadablePool()->GetSlot(i); + if(e && e->m_level == level){ + mi = CModelInfo::GetModelInfo(e->GetModelIndex()); + if(!e->bImBeingRendered){ + e->DeleteRwObject(); + if(mi->m_refCount == 0) + RemoveModel(e->GetModelIndex()); + } + } + } + + n = CPools::GetObjectPool()->GetSize(); + for(i = 0; i < n; i++){ + e = CPools::GetObjectPool()->GetSlot(i); + if(e && e->m_level == level){ + mi = CModelInfo::GetModelInfo(e->GetModelIndex()); + if(!e->bImBeingRendered && ((CObject*)e)->ObjectCreatedBy == GAME_OBJECT){ + e->DeleteRwObject(); + if(mi->m_refCount == 0) + RemoveModel(e->GetModelIndex()); + } + } + } + + n = CPools::GetDummyPool()->GetSize(); + for(i = 0; i < n; i++){ + e = CPools::GetDummyPool()->GetSlot(i); + if(e && e->m_level == level){ + mi = CModelInfo::GetModelInfo(e->GetModelIndex()); + if(!e->bImBeingRendered){ + e->DeleteRwObject(); + if(mi->m_refCount == 0) + RemoveModel(e->GetModelIndex()); + } + } + } +} + +void +CStreaming::RemoveUnusedBigBuildings(eLevelName level) +{ + if(level != LEVEL_INDUSTRIAL) + RemoveBigBuildings(LEVEL_INDUSTRIAL); + if(level != LEVEL_COMMERCIAL) + RemoveBigBuildings(LEVEL_COMMERCIAL); + if(level != LEVEL_SUBURBAN) + RemoveBigBuildings(LEVEL_SUBURBAN); + RemoveIslandsNotUsed(level); +} + +void +DeleteIsland(CEntity *island) +{ + if(island == nil) + return; + if(island->bImBeingRendered) + debug("Didn't delete island because it was being rendered\n"); + else{ + island->DeleteRwObject(); + CStreaming::RemoveModel(island->GetModelIndex()); + } +} + +void +CStreaming::RemoveIslandsNotUsed(eLevelName level) +{ + switch(level){ + case LEVEL_INDUSTRIAL: + DeleteIsland(pIslandLODindustEntity); + DeleteIsland(pIslandLODcomSubEntity); + DeleteIsland(pIslandLODsubComEntity); + break; + case LEVEL_COMMERCIAL: + DeleteIsland(pIslandLODcomIndEntity); + DeleteIsland(pIslandLODcomSubEntity); + DeleteIsland(pIslandLODsubIndEntity); + break; + case LEVEL_SUBURBAN: + DeleteIsland(pIslandLODsubIndEntity); + DeleteIsland(pIslandLODsubComEntity); + DeleteIsland(pIslandLODcomIndEntity); + break; + default: + DeleteIsland(pIslandLODindustEntity); + DeleteIsland(pIslandLODcomIndEntity); + DeleteIsland(pIslandLODcomSubEntity); + DeleteIsland(pIslandLODsubIndEntity); + DeleteIsland(pIslandLODsubComEntity); + break; + } +} + +void +CStreaming::RemoveBigBuildings(eLevelName level) +{ + int i, n; + CEntity *e; + CBaseModelInfo *mi; + + n = CPools::GetBuildingPool()->GetSize(); + for(i = 0; i < n; i++){ + e = CPools::GetBuildingPool()->GetSlot(i); + if(e && e->bIsBIGBuilding && e->m_level == level){ + mi = CModelInfo::GetModelInfo(e->GetModelIndex()); + if(!e->bImBeingRendered){ + e->DeleteRwObject(); + if(mi->m_refCount == 0) + RemoveModel(e->GetModelIndex()); + } + } + } +} + +bool +CStreaming::RemoveLoadedVehicle(void) +{ + int i, id; + + for(i = 0; i < MAXVEHICLESLOADED; i++){ + ms_lastVehicleDeleted++; + if(ms_lastVehicleDeleted == MAXVEHICLESLOADED) + ms_lastVehicleDeleted = 0; + id = ms_vehiclesLoaded[ms_lastVehicleDeleted]; + if(id != -1 && + (ms_aInfoForModel[id].m_flags & STREAMFLAGS_NOT_IN_LIST) == 0 && + CModelInfo::GetModelInfo(id)->m_refCount == 0 && + ms_aInfoForModel[id].m_loadState == STREAMSTATE_LOADED) + goto found; + } + return false; +found: + RemoveModel(ms_vehiclesLoaded[ms_lastVehicleDeleted]); + ms_numVehiclesLoaded--; + ms_vehiclesLoaded[ms_lastVehicleDeleted] = -1; + return true; +} + +bool +CStreaming::RemoveLeastUsedModel(void) +{ + CStreamingInfo *si; + int streamId; + + for(si = ms_endLoadedList.m_prev; si != &ms_startLoadedList; si = si->m_prev){ + streamId = si - ms_aInfoForModel; + if(streamId < STREAM_OFFSET_TXD){ + if(CModelInfo::GetModelInfo(streamId)->m_refCount == 0){ + RemoveModel(streamId); + return true; + } + }else{ + if(CTxdStore::GetNumRefs(streamId - STREAM_OFFSET_TXD) == 0 && + !IsTxdUsedByRequestedModels(streamId - STREAM_OFFSET_TXD)){ + RemoveModel(streamId); + return true; + } + } + } + return ms_numVehiclesLoaded > 7 && RemoveLoadedVehicle(); +} + +void +CStreaming::RemoveAllUnusedModels(void) +{ + int i; + + for(i = 0; i < MAXVEHICLESLOADED; i++) + RemoveLoadedVehicle(); + + for(i = NUM_DEFAULT_MODELS; i < MODELINFOSIZE; i++){ + if(ms_aInfoForModel[i].m_loadState == STREAMSTATE_LOADED && + ms_aInfoForModel[i].m_flags & STREAMFLAGS_DONT_REMOVE && + CModelInfo::GetModelInfo(i)->m_refCount == 0){ + RemoveModel(i); + ms_aInfoForModel[i].m_loadState = STREAMSTATE_NOTLOADED; + } + } +} + +bool +CStreaming::RemoveReferencedTxds(int32 mem) +{ + CStreamingInfo *si; + int streamId; + + for(si = ms_endLoadedList.m_prev; si != &ms_startLoadedList; si = si->m_prev){ + streamId = si - ms_aInfoForModel; + if(streamId >= STREAM_OFFSET_TXD && + CTxdStore::GetNumRefs(streamId-STREAM_OFFSET_TXD) == 0){ + RemoveModel(streamId); + if(ms_memoryUsed < mem) + return true; + } + } + return false; +} + +// TODO: RemoveCurrentZonesModels + +void +CStreaming::RemoveUnusedModelsInLoadedList(void) +{ + // empty +} + +bool +CStreaming::IsTxdUsedByRequestedModels(int32 txdId) +{ + CStreamingInfo *si; + int streamId; + int i; + + for(si = ms_startRequestedList.m_next; si != &ms_endRequestedList; si = si->m_next){ + streamId = si - ms_aInfoForModel; + if(streamId < STREAM_OFFSET_TXD && + CModelInfo::GetModelInfo(streamId)->GetTxdSlot() == txdId) + return true; + } + + for(i = 0; i < 4; i++){ + streamId = ms_channel[0].streamIds[i]; + if(streamId != -1 && streamId < STREAM_OFFSET_TXD && + CModelInfo::GetModelInfo(streamId)->GetTxdSlot() == txdId) + return true; + streamId = ms_channel[1].streamIds[i]; + if(streamId != -1 && streamId < STREAM_OFFSET_TXD && + CModelInfo::GetModelInfo(streamId)->GetTxdSlot() == txdId) + return true; + } + + return false; +} + +int32 +CStreaming::GetAvailableVehicleSlot(void) +{ + int i; + for(i = 0; i < MAXVEHICLESLOADED; i++) + if(ms_vehiclesLoaded[i] == -1) + return i; + return -1; +} + +bool +CStreaming::AddToLoadedVehiclesList(int32 modelId) +{ + int i; + int id; + + if(ms_numVehiclesLoaded < desiredNumVehiclesLoaded){ + // still room for vehicles + for(i = 0; i < MAXVEHICLESLOADED; i++){ + if(ms_vehiclesLoaded[ms_lastVehicleDeleted] == -1) + break; + ms_lastVehicleDeleted++; + if(ms_lastVehicleDeleted == MAXVEHICLESLOADED) + ms_lastVehicleDeleted = 0; + } + assert(ms_vehiclesLoaded[ms_lastVehicleDeleted] == -1); + ms_numVehiclesLoaded++; + }else{ + // find vehicle we can remove + for(i = 0; i < MAXVEHICLESLOADED; i++){ + id = ms_vehiclesLoaded[ms_lastVehicleDeleted]; + if(id != -1 && + (ms_aInfoForModel[id].m_flags & STREAMFLAGS_NOT_IN_LIST) == 0 && + CModelInfo::GetModelInfo(id)->m_refCount == 0) + goto found; + ms_lastVehicleDeleted++; + if(ms_lastVehicleDeleted == MAXVEHICLESLOADED) + ms_lastVehicleDeleted = 0; + } + id = -1; +found: + if(id == -1){ + // didn't find anything, try a free slot + id = GetAvailableVehicleSlot(); + if(id == -1) + return false; // still no luck + ms_lastVehicleDeleted = id; + // this is more that we wanted actually + ms_numVehiclesLoaded++; + }else + RemoveModel(id); + } + + ms_vehiclesLoaded[ms_lastVehicleDeleted++] = modelId; + if(ms_lastVehicleDeleted == MAXVEHICLESLOADED) + ms_lastVehicleDeleted = 0; + return true; +} + +bool +CStreaming::IsObjectInCdImage(int32 id) +{ + uint32 posn, size; + return ms_aInfoForModel[id].GetCdPosnAndSize(posn, size); +} + +void +CStreaming::HaveAllBigBuildingsLoaded(eLevelName level) +{ + int i, n; + CEntity *e; + + if(ms_hasLoadedLODs) + return; + + if(level == LEVEL_INDUSTRIAL){ + if(ms_aInfoForModel[islandLODcomInd].m_loadState != STREAMSTATE_LOADED || + ms_aInfoForModel[islandLODsubInd].m_loadState != STREAMSTATE_LOADED) + return; + }else if(level == LEVEL_COMMERCIAL){ + if(ms_aInfoForModel[islandLODindust].m_loadState != STREAMSTATE_LOADED || + ms_aInfoForModel[islandLODsubCom].m_loadState != STREAMSTATE_LOADED) + return; + }else if(level == LEVEL_SUBURBAN){ + if(ms_aInfoForModel[islandLODindust].m_loadState != STREAMSTATE_LOADED || + ms_aInfoForModel[islandLODcomSub].m_loadState != STREAMSTATE_LOADED) + return; + } + + n = CPools::GetBuildingPool()->GetSize(); + for(i = 0; i < n; i++){ + e = CPools::GetBuildingPool()->GetSlot(i); + if(e && e->bIsBIGBuilding && e->m_level == level && + ms_aInfoForModel[e->GetModelIndex()].m_loadState != STREAMSTATE_LOADED) + return; + } + + RemoveUnusedBigBuildings(level); + ms_hasLoadedLODs = true; +} + +void +CStreaming::SetModelIsDeletable(int32 id) +{ + ms_aInfoForModel[id].m_flags &= ~STREAMFLAGS_DONT_REMOVE; + if((id >= STREAM_OFFSET_TXD || CModelInfo::GetModelInfo(id)->m_type != MITYPE_VEHICLE) && + (ms_aInfoForModel[id].m_flags & STREAMFLAGS_SCRIPTOWNED) == 0){ + if(ms_aInfoForModel[id].m_loadState != STREAMSTATE_LOADED) + RemoveModel(id); + else if(ms_aInfoForModel[id].m_next == nil) + ms_aInfoForModel[id].AddToList(&ms_startLoadedList); + } +} + +void +CStreaming::SetModelTxdIsDeletable(int32 id) +{ + SetModelIsDeletable(CModelInfo::GetModelInfo(id)->GetTxdSlot() + STREAM_OFFSET_TXD); +} + +void +CStreaming::SetMissionDoesntRequireModel(int32 id) +{ + ms_aInfoForModel[id].m_flags &= ~STREAMFLAGS_SCRIPTOWNED; + if((id >= STREAM_OFFSET_TXD || CModelInfo::GetModelInfo(id)->m_type != MITYPE_VEHICLE) && + (ms_aInfoForModel[id].m_flags & STREAMFLAGS_DONT_REMOVE) == 0){ + if(ms_aInfoForModel[id].m_loadState != STREAMSTATE_LOADED) + RemoveModel(id); + else if(ms_aInfoForModel[id].m_next == nil) + ms_aInfoForModel[id].AddToList(&ms_startLoadedList); + } +} + +void +CStreaming::LoadInitialPeds(void) +{ + RequestModel(MI_COP, STREAMFLAGS_DONT_REMOVE); + RequestModel(MI_MALE01, STREAMFLAGS_DONT_REMOVE); + RequestModel(MI_TAXI_D, STREAMFLAGS_DONT_REMOVE); +} + +void +CStreaming::LoadInitialVehicles(void) +{ + int id; + + ms_numVehiclesLoaded = 0; + ms_lastVehicleDeleted = 0; + + if(CModelInfo::GetModelInfo("taxi", &id)) + RequestModel(id, STREAMFLAGS_DONT_REMOVE); + if(CModelInfo::GetModelInfo("police", &id)) + RequestModel(id, STREAMFLAGS_DONT_REMOVE); +} + + +// Find starting offset of the cdimage we next want to read +// Not useful at all on PC... +int32 +CStreaming::GetCdImageOffset(int32 lastPosn) +{ + int offset, off; + int i, img; + int dist, mindist; + + img = -1; + mindist = INT_MAX; + offset = ms_imageOffsets[ms_lastImageRead]; + if(lastPosn <= offset || lastPosn > offset + ms_imageSize){ + // last read position is not in last image + for(i = 0; i < NUMCDIMAGES; i++){ + off = ms_imageOffsets[i]; + if(off == -1) continue; + if((uint32)lastPosn > (uint32)off) + // after start of image, get distance from end + // negative if before end! + dist = lastPosn - (off + ms_imageSize); + else + // before image, get offset to start + // this will never be negative + dist = off - lastPosn; + if(dist < mindist){ + img = i; + mindist = dist; + } + } + assert(img >= 0); + offset = ms_imageOffsets[img]; + ms_lastImageRead = img; + } + return offset; +} + +inline bool +TxdAvailable(int32 txdId) +{ + CStreamingInfo *si = &CStreaming::ms_aInfoForModel[txdId + STREAM_OFFSET_TXD]; + return si->m_loadState == STREAMSTATE_LOADED || si->m_loadState == STREAMSTATE_READING; +} + +// Find stream id of next requested file in cdimage +int32 +CStreaming::GetNextFileOnCd(int32 lastPosn, bool priority) +{ + CStreamingInfo *si, *next; + int streamId; + uint32 posn, size; + int streamIdFirst, streamIdNext; + uint32 posnFirst, posnNext; + + streamIdFirst = -1; + streamIdNext = -1; + posnFirst = UINT_MAX; + posnNext = UINT_MAX; + + for(si = ms_startRequestedList.m_next; si != &ms_endRequestedList; si = next){ + next = si->m_next; + streamId = si - ms_aInfoForModel; + + // only priority requests if there are any + if(priority && ms_numPriorityRequests != 0 && !si->IsPriority()) + continue; + + // request Txd if necessary + if(streamId < STREAM_OFFSET_TXD && + !TxdAvailable(CModelInfo::GetModelInfo(streamId)->GetTxdSlot())){ + ReRequestTxd(CModelInfo::GetModelInfo(streamId)->GetTxdSlot()); + }else if(ms_aInfoForModel[streamId].GetCdPosnAndSize(posn, size)){ + if(posn < posnFirst){ + // find first requested file in image + streamIdFirst = streamId; + posnFirst = posn; + } + if(posn < posnNext && posn >= (uint32)lastPosn){ + // find first requested file after last read position + streamIdNext = streamId; + posnNext = posn; + } + }else{ + // empty file + DecrementRef(streamId); + si->RemoveFromList(); + si->m_loadState = STREAMSTATE_LOADED; + } + } + + // wrap around + if(streamIdNext == -1) + streamIdNext = streamIdFirst; + + if(streamIdNext == -1 && ms_numPriorityRequests != 0){ + // try non-priority files + ms_numPriorityRequests = 0; + streamIdNext = GetNextFileOnCd(lastPosn, false); + } + + return streamIdNext; +} + +/* + * Streaming buffer size is half of the largest file. + * Files larger than the buffer size can only be loaded by channel 0, + * which then uses both buffers, while channel 1 is idle. + * ms_bLoadingBigModel is set to true to indicate this state. + * + * TODO: two-part files + */ + +// Make channel read from disc +void +CStreaming::RequestModelStream(int32 ch) +{ + int lastPosn, imgOffset, streamId; + int totalSize; + uint32 posn, size, unused; + int i; + int haveBigFile, havePed; + + lastPosn = CdStreamGetLastPosn(); + imgOffset = GetCdImageOffset(lastPosn); + streamId = GetNextFileOnCd(lastPosn - imgOffset, true); + + if(streamId == -1) + return; + + // remove Txds that aren't requested anymore + while(streamId >= STREAM_OFFSET_TXD){ + if(ms_aInfoForModel[streamId].m_flags & STREAMFLAGS_KEEP_IN_MEMORY || + IsTxdUsedByRequestedModels(streamId - STREAM_OFFSET_TXD)) + break; + RemoveModel(streamId); + // so try next file + ms_aInfoForModel[streamId].GetCdPosnAndSize(posn, size); + streamId = GetNextFileOnCd(posn + size, true); + } + + if(streamId == -1) + return; + + ms_aInfoForModel[streamId].GetCdPosnAndSize(posn, size); + if(size > (uint32)ms_streamingBufferSize){ + // Can only load big models on channel 0, and 1 has to be idle + if(ch == 1 || ms_channel[1].state != CHANNELSTATE_IDLE) + return; + ms_bLoadingBigModel = true; + } + + // Load up to 4 adjacent files + haveBigFile = 0; + havePed = 0; + totalSize = 0; + for(i = 0; i < 4; i++){ + // no more files we can read + if(streamId == -1 || ms_aInfoForModel[streamId].m_loadState != STREAMSTATE_INQUEUE) + break; + + // also stop at non-priority files + ms_aInfoForModel[streamId].GetCdPosnAndSize(unused, size); + if(ms_numPriorityRequests != 0 && !ms_aInfoForModel[streamId].IsPriority()) + break; + + // Can't load certain combinations of files together + if(streamId < STREAM_OFFSET_TXD){ + if(havePed && CModelInfo::GetModelInfo(streamId)->m_type == MITYPE_PED || + haveBigFile && CModelInfo::GetModelInfo(streamId)->m_type == MITYPE_VEHICLE || + !TxdAvailable(CModelInfo::GetModelInfo(streamId)->GetTxdSlot())) + break; + }else{ + if(haveBigFile && size > 200) + break; + } + + // Now add the file + ms_channel[ch].streamIds[i] = streamId; + ms_channel[ch].offsets[i] = totalSize; + totalSize += size; + + // To big for buffer, remove again + if(totalSize > ms_streamingBufferSize && i > 0){ + totalSize -= size; + break; + } + if(streamId < STREAM_OFFSET_TXD){ + if(CModelInfo::GetModelInfo(streamId)->m_type == MITYPE_PED) + havePed = 1; + if(CModelInfo::GetModelInfo(streamId)->m_type == MITYPE_VEHICLE) + haveBigFile = 1; + }else{ + if(size > 200) + haveBigFile = 1; + } + ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_READING; + ms_aInfoForModel[streamId].RemoveFromList(); + DecrementRef(streamId); + + streamId = ms_aInfoForModel[streamId].m_nextID; + } + + // clear remaining slots + for(; i < 4; i++) + ms_channel[ch].streamIds[i] = -1; + // Now read the data + assert(!(ms_bLoadingBigModel && ch == 1)); // this would clobber the buffer + if(CdStreamRead(ch, ms_pStreamingBuffer[ch], imgOffset+posn, totalSize) == STREAM_NONE) + debug("FUCKFUCKFUCK\n"); + ms_channel[ch].state = CHANNELSTATE_READING; + ms_channel[ch].field24 = 0; + ms_channel[ch].size = totalSize; + ms_channel[ch].position = imgOffset+posn; + ms_channel[ch].numTries = 0; +} + +// Load data previously read from disc +bool +CStreaming::ProcessLoadingChannel(int32 ch) +{ + int status; + int i, id, cdsize; + + status = CdStreamGetStatus(ch); + if(status != STREAM_NONE){ + // busy + if(status != STREAM_READING && status != STREAM_WAITING){ + ms_channelError = ch; + ms_channel[ch].state = CHANNELSTATE_ERROR; + ms_channel[ch].status = status; + } + return false; + } + + if(ms_channel[ch].state == CHANNELSTATE_STARTED){ + ms_channel[ch].state = CHANNELSTATE_IDLE; + FinishLoadingLargeFile(&ms_pStreamingBuffer[ch][ms_channel[ch].offsets[0]*CDSTREAM_SECTOR_SIZE], + ms_channel[ch].streamIds[0]); + ms_channel[ch].streamIds[0] = -1; + }else{ + ms_channel[ch].state = CHANNELSTATE_IDLE; + for(i = 0; i < 4; i++){ + id = ms_channel[ch].streamIds[i]; + if(id == -1) + continue; + + cdsize = ms_aInfoForModel[id].GetCdSize(); + if(id < STREAM_OFFSET_TXD && + CModelInfo::GetModelInfo(id)->m_type == MITYPE_VEHICLE && + ms_numVehiclesLoaded >= desiredNumVehiclesLoaded && + !RemoveLoadedVehicle() && + ((ms_aInfoForModel[id].m_flags & STREAMFLAGS_NOT_IN_LIST) == 0 || GetAvailableVehicleSlot() == -1)){ + // can't load vehicle + RemoveModel(id); + if(ms_aInfoForModel[id].m_flags & STREAMFLAGS_NOT_IN_LIST) + ReRequestModel(id); + else if(CTxdStore::GetNumRefs(CModelInfo::GetModelInfo(id)->GetTxdSlot()) == 0) + RemoveTxd(CModelInfo::GetModelInfo(id)->GetTxdSlot()); + }else{ + MakeSpaceFor(cdsize * CDSTREAM_SECTOR_SIZE); + ConvertBufferToObject(&ms_pStreamingBuffer[ch][ms_channel[ch].offsets[i]*CDSTREAM_SECTOR_SIZE], + id); + if(ms_aInfoForModel[id].m_loadState == STREAMSTATE_STARTED){ + // queue for second part + ms_channel[ch].state = CHANNELSTATE_STARTED; + ms_channel[ch].offsets[0] = ms_channel[ch].offsets[i]; + ms_channel[ch].streamIds[0] = id; + if(i != 0) + ms_channel[ch].streamIds[i] = -1; + }else + ms_channel[ch].streamIds[i] = -1; + } + } + } + + if(ms_bLoadingBigModel && ms_channel[ch].state != CHANNELSTATE_STARTED){ + ms_bLoadingBigModel = false; + // reset channel 1 after loading a big model + for(i = 0; i < 4; i++) + ms_channel[1].streamIds[i] = -1; + ms_channel[1].state = CHANNELSTATE_IDLE; + } + + return true; +} + +void +CStreaming::RetryLoadFile(int32 ch) +{ + char *key; + + CPad::StopPadsShaking(); + + if(ms_channel[ch].numTries >= 3){ + switch(ms_channel[ch].status){ + case STREAM_ERROR_NOCD: key = "NOCD"; break; + case STREAM_ERROR_OPENCD: key = "OPENCD"; break; + case STREAM_ERROR_WRONGCD: key = "WRONGCD"; break; + default: key = "CDERROR"; break; + } + CHud::SetMessage(TheText.Get(key)); + CTimer::SetCodePause(true); + } + + switch(ms_channel[ch].state){ + case CHANNELSTATE_IDLE: +streamread: + CdStreamRead(ch, ms_pStreamingBuffer[ch], ms_channel[ch].position, ms_channel[ch].size); + ms_channel[ch].state = CHANNELSTATE_READING; + ms_channel[ch].field24 = -600; + break; + case CHANNELSTATE_READING: + if(ProcessLoadingChannel(ch)){ + ms_channelError = -1; + CTimer::SetCodePause(false); + } + break; + case CHANNELSTATE_ERROR: + ms_channel[ch].numTries++; + if(CdStreamGetStatus(ch) != STREAM_READING && CdStreamGetStatus(ch) != STREAM_WAITING) + goto streamread; + break; + } +} + +void +CStreaming::LoadRequestedModels(void) +{ + static int currentChannel = 0; + + // We can't read with channel 1 while channel 0 is using its buffer + if(ms_bLoadingBigModel) + currentChannel = 0; + + // We have data, load + if(ms_channel[currentChannel].state == CHANNELSTATE_READING || + ms_channel[currentChannel].state == CHANNELSTATE_STARTED) + ProcessLoadingChannel(currentChannel); + + if(ms_channelError == -1){ + // Channel is idle, read more data + if(ms_channel[currentChannel].state == CHANNELSTATE_IDLE) + RequestModelStream(currentChannel); + // Switch channel + if(ms_channel[currentChannel].state != CHANNELSTATE_STARTED) + currentChannel = 1 - currentChannel; + } +} + +void +CStreaming::LoadAllRequestedModels(bool priority) +{ + static bool bInsideLoadAll = false; + int imgOffset, streamId, status; + int i; + uint32 posn, size; + + if(bInsideLoadAll) + return; + + FlushChannels(); + imgOffset = GetCdImageOffset(CdStreamGetLastPosn()); + + while(ms_endRequestedList.m_prev != &ms_startRequestedList){ + streamId = GetNextFileOnCd(0, priority); + if(streamId == -1) + break; + + ms_aInfoForModel[streamId].RemoveFromList(); + DecrementRef(streamId); + + if(ms_aInfoForModel[streamId].GetCdPosnAndSize(posn, size)){ + do + status = CdStreamRead(0, ms_pStreamingBuffer[0], imgOffset+posn, size); + while(CdStreamSync(0) || status == STREAM_NONE); + ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_READING; + + MakeSpaceFor(size * CDSTREAM_SECTOR_SIZE); + ConvertBufferToObject(ms_pStreamingBuffer[0], streamId); + if(ms_aInfoForModel[streamId].m_loadState == STREAMSTATE_STARTED) + FinishLoadingLargeFile(ms_pStreamingBuffer[0], streamId); + + if(streamId < STREAM_OFFSET_TXD){ + CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(streamId); + if(mi->IsSimple()) + mi->m_alpha = 255; + } + }else{ + // empty + ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_LOADED; + } + } + + ms_bLoadingBigModel = false; + for(i = 0; i < 4; i++){ + ms_channel[1].streamIds[i] = -1; + ms_channel[1].offsets[i] = -1; + } + ms_channel[1].state = CHANNELSTATE_IDLE; + bInsideLoadAll = false; +} + +void +CStreaming::FlushChannels(void) +{ + if(ms_channel[1].state == CHANNELSTATE_STARTED) + ProcessLoadingChannel(1); + + if(ms_channel[0].state == CHANNELSTATE_READING){ + CdStreamSync(0); + ProcessLoadingChannel(0); + } + if(ms_channel[0].state == CHANNELSTATE_STARTED) + ProcessLoadingChannel(0); + + if(ms_channel[1].state == CHANNELSTATE_READING){ + CdStreamSync(1); + ProcessLoadingChannel(1); + } + if(ms_channel[1].state == CHANNELSTATE_STARTED) + ProcessLoadingChannel(1); +} + +void +CStreaming::FlushRequestList(void) +{ + CStreamingInfo *si, *next; + + for(si = ms_startRequestedList.m_next; si != &ms_endRequestedList; si = next){ + next = si->m_next; + RemoveModel(si - ms_aInfoForModel); + } + FlushChannels(); +} + void CStreaming::ImGonnaUseStreamingMemory(void) { + // empty } void @@ -717,6 +1581,7 @@ CStreaming::IHaveUsedStreamingMemory(void) void CStreaming::UpdateMemoryUsed(void) { + // empty } WRAPPER void CStreaming::LoadScene(CVector *pos) { EAXJMP(0x40A6D0); } @@ -771,7 +1636,39 @@ STARTPATCHES InjectHook(0x408210, CStreaming::RequestIslands, PATCH_JUMP); InjectHook(0x40A890, CStreaming::RequestSpecialModel, PATCH_JUMP); InjectHook(0x40ADA0, CStreaming::RequestSpecialChar, PATCH_JUMP); + InjectHook(0x408830, CStreaming::RemoveModel, PATCH_JUMP); + InjectHook(0x4083A0, CStreaming::RemoveUnusedBuildings, PATCH_JUMP); + InjectHook(0x4083D0, CStreaming::RemoveBuildings, PATCH_JUMP); + InjectHook(0x408640, CStreaming::RemoveUnusedBigBuildings, PATCH_JUMP); + InjectHook(0x408680, CStreaming::RemoveBigBuildings, PATCH_JUMP); + InjectHook(0x408780, CStreaming::RemoveIslandsNotUsed, PATCH_JUMP); + InjectHook(0x40B180, CStreaming::RemoveLoadedVehicle, PATCH_JUMP); + InjectHook(0x4089B0, CStreaming::RemoveLeastUsedModel, PATCH_JUMP); + InjectHook(0x408940, CStreaming::RemoveAllUnusedModels, PATCH_JUMP); + InjectHook(0x409450, CStreaming::RemoveReferencedTxds, PATCH_JUMP); + + InjectHook(0x40B160, CStreaming::GetAvailableVehicleSlot, PATCH_JUMP); + InjectHook(0x40B060, CStreaming::AddToLoadedVehiclesList, PATCH_JUMP); + InjectHook(0x4094C0, CStreaming::IsTxdUsedByRequestedModels, PATCH_JUMP); + InjectHook(0x407E70, CStreaming::IsObjectInCdImage, PATCH_JUMP); + InjectHook(0x408280, CStreaming::HaveAllBigBuildingsLoaded, PATCH_JUMP); + InjectHook(0x40A790, CStreaming::SetModelIsDeletable, PATCH_JUMP); + InjectHook(0x40A800, CStreaming::SetModelTxdIsDeletable, PATCH_JUMP); + InjectHook(0x40A820, CStreaming::SetMissionDoesntRequireModel, PATCH_JUMP); + + InjectHook(0x40AA00, CStreaming::LoadInitialPeds, PATCH_JUMP); + InjectHook(0x40ADF0, CStreaming::LoadInitialVehicles, PATCH_JUMP); + + InjectHook(0x409BE0, CStreaming::ProcessLoadingChannel, PATCH_JUMP); + InjectHook(0x40A610, CStreaming::FlushChannels, PATCH_JUMP); + InjectHook(0x40A680, CStreaming::FlushRequestList, PATCH_JUMP); + InjectHook(0x409FF0, CStreaming::GetCdImageOffset, PATCH_JUMP); + InjectHook(0x409E50, CStreaming::GetNextFileOnCd, PATCH_JUMP); + InjectHook(0x40A060, CStreaming::RequestModelStream, PATCH_JUMP); + InjectHook(0x4077F0, CStreaming::RetryLoadFile, PATCH_JUMP); + InjectHook(0x40A390, CStreaming::LoadRequestedModels, PATCH_JUMP); + InjectHook(0x40A440, CStreaming::LoadAllRequestedModels, PATCH_JUMP); InjectHook(0x4063E0, &CStreamingInfo::GetCdPosnAndSize, PATCH_JUMP); InjectHook(0x406410, &CStreamingInfo::SetCdPosnAndSize, PATCH_JUMP); diff --git a/src/Streaming.h b/src/Streaming.h index 2b2efe79..e92819e1 100644 --- a/src/Streaming.h +++ b/src/Streaming.h @@ -14,6 +14,7 @@ enum StreamFlags STREAMFLAGS_PRIORITY = 0x08, STREAMFLAGS_NOFADE = 0x10, + // TODO: this isn't named well, maybe CANT_REMOVE? STREAMFLAGS_NOT_IN_LIST = STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_SCRIPTOWNED, STREAMFLAGS_KEEP_IN_MEMORY = STREAMFLAGS_DONT_REMOVE|STREAMFLAGS_SCRIPTOWNED|STREAMFLAGS_DEPENDENCY, }; @@ -23,13 +24,16 @@ enum StreamLoadState STREAMSTATE_NOTLOADED = 0, STREAMSTATE_LOADED = 1, STREAMSTATE_INQUEUE = 2, - STREAMSTATE_READING = 3, // what is this? - STREAMSTATE_STARTED = 4, // first part read + STREAMSTATE_READING = 3, // channel is reading + STREAMSTATE_STARTED = 4, // first part loaded }; enum ChannelState { - CHANNELSTATE_0 = 0, + CHANNELSTATE_IDLE = 0, + CHANNELSTATE_READING = 1, + CHANNELSTATE_STARTED = 2, + CHANNELSTATE_ERROR = 3, }; class CStreamingInfo @@ -49,17 +53,18 @@ public: void AddToList(CStreamingInfo *link); void RemoveFromList(void); uint32 GetCdSize(void) { return m_size; } + bool IsPriority(void) { return !!(m_flags & STREAMFLAGS_PRIORITY); } }; struct CStreamingChannel { - int32 modelIds[4]; + int32 streamIds[4]; int32 offsets[4]; int32 state; int32 field24; int32 position; int32 size; - int32 field30; + int32 numTries; int32 status; // from CdStream }; @@ -79,12 +84,14 @@ public: static CStreamingInfo &ms_endRequestedList; static int32 &ms_oldSectorX; static int32 &ms_oldSectorY; - static uint32 &ms_streamingBufferSize; - static uint8 **ms_pStreamingBuffer; //[2] + static int32 &ms_streamingBufferSize; + static int8 **ms_pStreamingBuffer; //[2] static int32 &ms_memoryUsed; static CStreamingChannel *ms_channel; //[2] + static int32 &ms_channelError; static int32 &ms_numVehiclesLoaded; static int32 *ms_vehiclesLoaded; //[MAXVEHICLESLOADED] + static int32 &ms_lastVehicleDeleted; static CDirectory *&ms_pExtraObjectsDir; static int32 &ms_numPriorityRequests; static bool &ms_hasLoadedLODs; @@ -104,22 +111,55 @@ public: static bool ConvertBufferToObject(int8 *buf, int32 streamId); static bool FinishLoadingLargeFile(int8 *buf, int32 streamId); static void RequestModel(int32 model, int32 flags); + static void ReRequestModel(int32 model) { RequestModel(model, ms_aInfoForModel[model].m_flags); } static void RequestTxd(int32 txd, int32 flags) { RequestModel(txd + STREAM_OFFSET_TXD, flags); } + static void ReRequestTxd(int32 txd) { ReRequestModel(txd + STREAM_OFFSET_TXD); } static void RequestSubway(void); static void RequestBigBuildings(eLevelName level); static void RequestIslands(eLevelName level); static void RequestSpecialModel(int32 modelId, const char *modelName, int32 flags); static void RequestSpecialChar(int32 charId, const char *modelName, int32 flags); + static void DecrementRef(int32 id); static void RemoveModel(int32 id); - + static void RemoveTxd(int32 id) { RemoveModel(id + STREAM_OFFSET_TXD); } + static void RemoveUnusedBuildings(eLevelName level); + static void RemoveBuildings(eLevelName level); + static void RemoveUnusedBigBuildings(eLevelName level); + static void RemoveIslandsNotUsed(eLevelName level); + static void RemoveBigBuildings(eLevelName level); + static bool RemoveLoadedVehicle(void); + static bool RemoveLeastUsedModel(void); + static void RemoveAllUnusedModels(void); + static void RemoveUnusedModelsInLoadedList(void); + static bool RemoveReferencedTxds(int32 mem); + static int32 GetAvailableVehicleSlot(void); static bool IsTxdUsedByRequestedModels(int32 txdId); static bool AddToLoadedVehiclesList(int32 modelId); + static bool IsObjectInCdImage(int32 id); + static void HaveAllBigBuildingsLoaded(eLevelName level); + static void SetModelIsDeletable(int32 id); + static void SetModelTxdIsDeletable(int32 id); + static void SetMissionDoesntRequireModel(int32 id); + + static int32 GetCdImageOffset(int32 lastPosn); + static int32 GetNextFileOnCd(int32 position, bool priority); + static void RequestModelStream(int32 ch); + static bool ProcessLoadingChannel(int32 ch); + static void RetryLoadFile(int32 ch); + static void LoadRequestedModels(void); + static void LoadAllRequestedModels(bool priority); + static void FlushChannels(void); + static void FlushRequestList(void); static void MakeSpaceFor(int32 size); static void ImGonnaUseStreamingMemory(void); static void IHaveUsedStreamingMemory(void); static void UpdateMemoryUsed(void); + + static void LoadInitialPeds(void); + static void LoadInitialVehicles(void); + static void LoadScene(CVector *pos); static void LoadAllRequestedModels(bool); }; diff --git a/src/Timer.h b/src/Timer.h index b8b5864d..75d4048c 100644 --- a/src/Timer.h +++ b/src/Timer.h @@ -29,9 +29,10 @@ public: static inline void SetPreviousTimeInMilliseconds(uint32 t) { m_snPreviousTimeInMilliseconds = t; } static float GetTimeScale(void) { return ms_fTimeScale; } static inline void SetTimeScale(float ts) { ms_fTimeScale = ts; } - - static inline bool GetIsPaused() { return m_UserPause || m_CodePause; } - static inline bool GetIsUserPaused() { return m_UserPause; } + + static bool GetIsPaused() { return m_UserPause || m_CodePause; } + static bool GetIsUserPaused() { return m_UserPause; } + static void SetCodePause(bool pause) { m_CodePause = pause; } static void Initialise(void); static void Shutdown(void); diff --git a/src/World.cpp b/src/World.cpp index 20203297..0a83c595 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -592,6 +592,6 @@ WRAPPER CPlayerPed *FindPlayerPed(void) { EAXJMP(0x4A1150); } WRAPPER CVector &FindPlayerCoors(CVector &v) { EAXJMP(0x4A1030); } WRAPPER CVehicle *FindPlayerVehicle(void) { EAXJMP(0x4A10C0); } WRAPPER CVehicle *FindPlayerTrain(void) { EAXJMP(0x4A1120); } -WRAPPER CVector FindPlayerSpeed(void) { EAXJMP(0x4A1090); } +WRAPPER CVector &FindPlayerSpeed(void) { EAXJMP(0x4A1090); } WRAPPER CVector FindPlayerCentreOfWorld_NoSniperShift(void) { EAXJMP(0x4A11C0); } WRAPPER float FindPlayerHeading(void) { EAXJMP(0x4A1220); } diff --git a/src/World.h b/src/World.h index 13b9f254..e3a6a8f2 100644 --- a/src/World.h +++ b/src/World.h @@ -111,6 +111,6 @@ CPlayerPed *FindPlayerPed(void); CVector &FindPlayerCoors(CVector &v); CVehicle *FindPlayerVehicle(void); CVehicle *FindPlayerTrain(void); -CVector FindPlayerSpeed(void); +CVector &FindPlayerSpeed(void); CVector FindPlayerCentreOfWorld_NoSniperShift(void); float FindPlayerHeading(void); diff --git a/src/animation/AnimBlendAssociation.h b/src/animation/AnimBlendAssociation.h index 7eec69a0..076fa810 100644 --- a/src/animation/AnimBlendAssociation.h +++ b/src/animation/AnimBlendAssociation.h @@ -2,8 +2,7 @@ #include "AnimBlendList.h" #include "AnimBlendNode.h" - -class CAnimBlendHierarchy; +#include "AnimBlendHierarchy.h" enum { // TODO @@ -78,6 +77,8 @@ public: void UpdateTime(float timeDelta, float relSpeed); bool UpdateBlend(float timeDelta); + float GetTimeLeft() { return hierarchy->totalLength - currentTime; } + static CAnimBlendAssociation *FromLink(CAnimBlendLink *l) { return (CAnimBlendAssociation*)((uint8*)l - offsetof(CAnimBlendAssociation, link)); } diff --git a/src/audio/AudioManager.cpp b/src/audio/AudioManager.cpp new file mode 100644 index 00000000..757ffa79 --- /dev/null +++ b/src/audio/AudioManager.cpp @@ -0,0 +1,17 @@ +#include "common.h" +#include "patcher.h" +#include "AudioManager.h" + +cAudioManager &AudioManager = *(cAudioManager*)0x880FC0; + +void +cAudioManager::PlayerJustLeftCar(void) +{ + // UNUSED: This is a perfectly empty function. +} + +WRAPPER void cAudioManager::Service() { EAXJMP(0x57A2A0); } + +STARTPATCHES + InjectHook(0x56AD20, &cAudioManager::PlayerJustLeftCar, PATCH_JUMP); +ENDPATCHES \ No newline at end of file diff --git a/src/audio/AudioManager.h b/src/audio/AudioManager.h new file mode 100644 index 00000000..9e25b0a3 --- /dev/null +++ b/src/audio/AudioManager.h @@ -0,0 +1,9 @@ +#pragma once + +class cAudioManager { +public: + void PlayerJustLeftCar(void); + void Service(); +}; + +extern cAudioManager &AudioManager; \ No newline at end of file diff --git a/src/audio/DMAudio.h b/src/audio/DMAudio.h index 7a3d1477..6df2ceea 100644 --- a/src/audio/DMAudio.h +++ b/src/audio/DMAudio.h @@ -195,5 +195,7 @@ public: int32 CreateEntity(int, void*); void SetEntityStatus(int32, int8); void SetRadioInCar(int32); + uint8 IsMP3RadioChannelAvailable(); + }; extern cDMAudio &DMAudio; diff --git a/src/audio/MusicManager.cpp b/src/audio/MusicManager.cpp index f9c02739..6f2d3d86 100644 --- a/src/audio/MusicManager.cpp +++ b/src/audio/MusicManager.cpp @@ -16,6 +16,8 @@ int32 &gNumRetunePresses = *(int32*)0x650B80; wchar *pCurrentStation = (wchar*)0x650B9C; uint8 &cDisplay = *(uint8*)0x650BA1; +WRAPPER char* cMusicManager::Get3DProviderName(char) { EAXJMP(0x57A8C0); } + bool cMusicManager::PlayerInCar() { if (!FindPlayerVehicle()) diff --git a/src/audio/MusicManager.h b/src/audio/MusicManager.h index 644c3df3..dcb34daf 100644 --- a/src/audio/MusicManager.h +++ b/src/audio/MusicManager.h @@ -264,6 +264,7 @@ public: uint8 field_2395; public: + char *Get3DProviderName(char); bool PlayerInCar(); void DisplayRadioStationName(); }; diff --git a/src/config.h b/src/config.h index 7eb3238d..83aa7b8f 100644 --- a/src/config.h +++ b/src/config.h @@ -58,9 +58,8 @@ enum Config { NUMPOINTLIGHTS = 32, NUMONSCREENTIMERENTRIES = 1, - + NUMRADARBLIPS = 32, NUMPICKUPS = 336, - NUMBLIPS = 32 }; #define GTA3_1_1_PATCH @@ -71,3 +70,4 @@ enum Config { //#define NO_CDCHECK #define NO_MOVIES //#define USE_MY_DOCUMENTS +#define NASTY_GAME \ No newline at end of file diff --git a/src/control/CarCtrl.cpp b/src/control/CarCtrl.cpp index 730ccd16..4ce856f7 100644 --- a/src/control/CarCtrl.cpp +++ b/src/control/CarCtrl.cpp @@ -2,6 +2,8 @@ #include "patcher.h" #include "CarCtrl.h" +int &CCarCtrl::NumLawEnforcerCars = *(int*)0x8F1B38; + WRAPPER void CCarCtrl::SwitchVehicleToRealPhysics(CVehicle*) { EAXJMP(0x41F7F0); } WRAPPER void CCarCtrl::AddToCarArray(int id, int vehclass) { EAXJMP(0x4182F0); } WRAPPER void CCarCtrl::UpdateCarCount(CVehicle*, bool) { EAXJMP(0x4202E0); } \ No newline at end of file diff --git a/src/control/CarCtrl.h b/src/control/CarCtrl.h index a6537e34..cc9327ee 100644 --- a/src/control/CarCtrl.h +++ b/src/control/CarCtrl.h @@ -8,4 +8,6 @@ public: static void SwitchVehicleToRealPhysics(CVehicle*); static void AddToCarArray(int id, int vehclass); static void UpdateCarCount(CVehicle*, bool); + + static int32 &NumLawEnforcerCars; }; diff --git a/src/control/Garages.cpp b/src/control/Garages.cpp index 70a15476..d601db8e 100644 --- a/src/control/Garages.cpp +++ b/src/control/Garages.cpp @@ -68,7 +68,7 @@ WRAPPER void CGarages::PrintMessages(void) { EAXJMP(0x426310); } #else void CGarages::PrintMessages() { - if (CTimer::GetTimeInMilliseconds() > CGarages::MessageStartTime && CTimer::GetTimeInMilliseconds() < CGarages::MessageEndTime) { + if (CTimer::GetTimeInMilliseconds() > MessageStartTime && CTimer::GetTimeInMilliseconds() < MessageEndTime) { CFont::SetScale(SCREEN_SCALE_X(1.2f / 2), SCREEN_SCALE_Y(1.5f / 2)); // BUG: game doesn't use macro here. CFont::SetPropOn(); CFont::SetJustifyOff(); @@ -77,16 +77,16 @@ void CGarages::PrintMessages() CFont::SetCentreOn(); CFont::SetFontStyle(FONT_BANK); - if (CGarages::MessageNumberInString2 < 0) { - if (CGarages::MessageNumberInString < 0) { + if (MessageNumberInString2 < 0) { + if (MessageNumberInString < 0) { CFont::SetColor(CRGBA(0, 0, 0, 255)); - CFont::PrintString((SCREEN_WIDTH/ 2) + SCREEN_SCALE_X(2.0f), (SCREEN_HEIGHT / 2) + SCREEN_SCALE_Y(-84.0f), TheText.Get(CGarages::MessageIDString)); + CFont::PrintString((SCREEN_WIDTH/ 2) + SCREEN_SCALE_X(2.0f), (SCREEN_HEIGHT / 2) + SCREEN_SCALE_Y(-84.0f), TheText.Get(MessageIDString)); CFont::SetColor(CRGBA(89, 115, 150, 255)); - CFont::PrintString((SCREEN_WIDTH / 2), (SCREEN_HEIGHT / 2) + SCREEN_SCALE_Y(-84.0f), TheText.Get(CGarages::MessageIDString)); + CFont::PrintString((SCREEN_WIDTH / 2), (SCREEN_HEIGHT / 2) + SCREEN_SCALE_Y(-84.0f), TheText.Get(MessageIDString)); } else { - CMessages::InsertNumberInString(TheText.Get(CGarages::MessageIDString), CGarages::MessageNumberInString, -1, -1, -1, -1, -1, gUString); + CMessages::InsertNumberInString(TheText.Get(MessageIDString), MessageNumberInString, -1, -1, -1, -1, -1, gUString); CFont::SetColor(CRGBA(0, 0, 0, 255)); CFont::PrintString((SCREEN_WIDTH / 2) + SCREEN_SCALE_X(2.0f), (SCREEN_HEIGHT / 2) + SCREEN_SCALE_Y(-84.0f + 2.0 - 40.0f), gUString); @@ -96,7 +96,7 @@ void CGarages::PrintMessages() } } else { - CMessages::InsertNumberInString(TheText.Get(CGarages::MessageIDString), CGarages::MessageNumberInString2, -1, -1, -1, -1, -1, gUString); + CMessages::InsertNumberInString(TheText.Get(MessageIDString), MessageNumberInString, MessageNumberInString2, -1, -1, -1, -1, gUString); CFont::SetColor(CRGBA(0, 0, 0, 255)); CFont::PrintString((SCREEN_WIDTH / 2) + SCREEN_SCALE_X(2.0f), (SCREEN_HEIGHT / 2) + SCREEN_SCALE_Y(-84.0f + 2.0 - 40.0f), gUString); diff --git a/src/control/PedPlacement.cpp b/src/control/PedPlacement.cpp new file mode 100644 index 00000000..de4cdae1 --- /dev/null +++ b/src/control/PedPlacement.cpp @@ -0,0 +1,40 @@ +#include "common.h" +#include "patcher.h" +#include "PedPlacement.h" +#include "World.h" + +void +CPedPlacement::FindZCoorForPed(CVector* pos) +{ + float zForPed; + float startZ = pos->z - 100.0f; + float foundColZ = -100.0f; + float foundColZ2 = -100.0f; + CColPoint foundCol; + CEntity* foundEnt; + + CVector vec( + pos->x, + pos->y, + pos->z + 1.0f + ); + + if (CWorld::ProcessVerticalLine(vec, startZ, foundCol, foundEnt, true, false, false, false, true, false, false)) + foundColZ = foundCol.point.z; + + // Adjust coords and do a second test + vec.x += 0.1f; + vec.y += 0.1f; + + if (CWorld::ProcessVerticalLine(vec, startZ, foundCol, foundEnt, true, false, false, false, true, false, false)) + foundColZ2 = foundCol.point.z; + + zForPed = max(foundColZ, foundColZ2); + + if (zForPed > -99.0f) + pos->z = 1.04f + zForPed; +} + +STARTPATCHES + InjectHook(0x4EE340, &CPedPlacement::FindZCoorForPed, PATCH_JUMP); +ENDPATCHES diff --git a/src/control/PedPlacement.h b/src/control/PedPlacement.h new file mode 100644 index 00000000..4bd48b62 --- /dev/null +++ b/src/control/PedPlacement.h @@ -0,0 +1,8 @@ +#pragma once + +class CVector; + +class CPedPlacement { +public: + static void FindZCoorForPed(CVector* pos); +}; \ No newline at end of file diff --git a/src/control/PedType.h b/src/control/PedType.h index 02add8f6..455d8d8d 100644 --- a/src/control/PedType.h +++ b/src/control/PedType.h @@ -22,9 +22,9 @@ enum PEDTYPE_EMERGENCY, PEDTYPE_FIREMAN, PEDTYPE_CRIMINAL, - PEDTYPE_SPECIAL, - PEDTYPE_PROSTITUTE, PEDTYPE_UNUSED1, + PEDTYPE_PROSTITUTE, + PEDTYPE_SPECIAL, PEDTYPE_UNUSED2, NUM_PEDTYPES diff --git a/src/control/Replay.cpp b/src/control/Replay.cpp index dfc988c3..1c71f25f 100644 --- a/src/control/Replay.cpp +++ b/src/control/Replay.cpp @@ -24,6 +24,8 @@ #include "Timer.h" #include "Weather.h" #include "Zones.h" +#include "Font.h" +#include "Text.h" uint8 &CReplay::Mode = *(uint8*)0x95CD5B; CAddressInReplayBuffer &CReplay::Record = *(CAddressInReplayBuffer*)0x942F7C; @@ -451,7 +453,6 @@ WRAPPER bool CReplay::PlayBackThisFrameInterpolation(CAddressInReplayBuffer *buf WRAPPER void CReplay::FinishPlayback(void) { EAXJMP(0x595B20); } WRAPPER void CReplay::Shutdown(void) { EAXJMP(0x595BD0); } WRAPPER void CReplay::ProcessReplayCamera(void) { EAXJMP(0x595C40); } -WRAPPER void CReplay::Display(void) { EAXJMP(0x595EE0); } #if 0 WRAPPER void CReplay::TriggerPlayback(uint8 cam_mode, float cam_x, float cam_y, float cam_z, bool load_scene) { EAXJMP(0x596030); } @@ -732,6 +733,23 @@ WRAPPER bool CReplay::ShouldStandardCameraBeProcessed(void) { EAXJMP(0x597680); WRAPPER void CReplay::ProcessLookAroundCam(void) { EAXJMP(0x5976C0); } WRAPPER size_t CReplay::FindSizeOfPacket(uint8 type) { EAXJMP(0x597CC0); } +#if 0 +WRAPPER void CReplay::Display(void) { EAXJMP(0x595EE0); } +#else +void CReplay::Display() +{ + if (CReplay::IsPlayingBack() && CTimer::GetFrameCounter() + 1 & 0x20) { + CFont::SetPropOn(); + CFont::SetBackgroundOff(); + CFont::SetScale(SCREEN_SCALE_X(1.5f), SCREEN_SCALE_Y(1.5f)); + CFont::SetAlignment(ALIGN_LEFT); + CFont::SetColor(CRGBA(255, 255, 200, 200)); + CFont::SetFontStyle(FONT_BANK); + CFont::PrintString(SCREEN_SCALE_X(63.5f), SCREEN_SCALE_Y(30.0f), TheText.Get("REPLAY")); + } +} +#endif + STARTPATCHES InjectHook(0x592FC0, PrintElementsInPtrList, PATCH_JUMP); InjectHook(0x592FE0, CReplay::Init, PATCH_JUMP); diff --git a/src/entities/Building.cpp b/src/entities/Building.cpp index 9e56b3a2..d69a65fe 100644 --- a/src/entities/Building.cpp +++ b/src/entities/Building.cpp @@ -22,5 +22,6 @@ CBuilding::ReplaceWithNewModel(int32 id) } STARTPATCHES + InjectHook(0x4057D0, &CBuilding::ctor, PATCH_JUMP); InjectHook(0x405850, &CBuilding::ReplaceWithNewModel, PATCH_JUMP); ENDPATCHES diff --git a/src/entities/Building.h b/src/entities/Building.h index 7b837f46..b1f96bae 100644 --- a/src/entities/Building.h +++ b/src/entities/Building.h @@ -15,5 +15,7 @@ public: void ReplaceWithNewModel(int32 id); virtual bool GetIsATreadable(void) { return false; } + + CBuilding *ctor(void) { return ::new (this) CBuilding(); } }; static_assert(sizeof(CBuilding) == 0x64, "CBuilding: error"); diff --git a/src/entities/Entity.cpp b/src/entities/Entity.cpp index d2b2577d..294518c8 100644 --- a/src/entities/Entity.cpp +++ b/src/entities/Entity.cpp @@ -339,9 +339,9 @@ CEntity::GetBoundRect(void) return rect; } -void +WRAPPER void CEntity::PreRender(void) -{ +{ EAXJMP(0x474350); } void @@ -448,6 +448,7 @@ CEntity::PruneReferences(void) } STARTPATCHES + InjectHook(0x473C30, &CEntity::ctor, PATCH_JUMP); InjectHook(0x4742C0, (void (CEntity::*)(CVector&))&CEntity::GetBoundCentre, PATCH_JUMP); InjectHook(0x474310, &CEntity::GetBoundRadius, PATCH_JUMP); InjectHook(0x474C10, &CEntity::GetIsTouching, PATCH_JUMP); diff --git a/src/entities/Entity.h b/src/entities/Entity.h index 0ce47428..8816e206 100644 --- a/src/entities/Entity.h +++ b/src/entities/Entity.h @@ -155,6 +155,7 @@ public: // to make patching virtual functions possible + CEntity *ctor(void) { return ::new (this) CEntity(); } void Add_(void) { CEntity::Add(); } void Remove_(void) { CEntity::Remove(); } void CreateRwObject_(void) { CEntity::CreateRwObject(); } diff --git a/src/entities/Ped.cpp b/src/entities/Ped.cpp index 20a5b48c..c4109312 100644 --- a/src/entities/Ped.cpp +++ b/src/entities/Ped.cpp @@ -10,11 +10,19 @@ #include "PlayerPed.h" #include "General.h" #include "VisibilityPlugins.h" +#include "AudioManager.h" +#include "HandlingMgr.h" +#include "Replay.h" +#include "PedPlacement.h" bool &CPed::bNastyLimbsCheat = *(bool*)0x95CD44; bool &CPed::bPedCheat2 = *(bool*)0x95CD5A; bool &CPed::bPedCheat3 = *(bool*)0x95CD59; +CVector &CPed::offsetToOpenRegularCarDoor = *(CVector*)0x62E030; +CVector &CPed::offsetToOpenLowCarDoor = *(CVector*)0x62E03C; +CVector &CPed::offsetToOpenVanDoor = *(CVector*)0x62E048; + void *CPed::operator new(size_t sz) { return CPools::GetPedPool()->New(); } void CPed::operator delete(void *p, size_t sz) { CPools::GetPedPool()->Delete((CPed*)p); } @@ -24,6 +32,9 @@ WRAPPER void CPed::SetDie(AnimationId anim, float arg1, float arg2) { EAXJMP(0x4 WRAPPER void CPed::SpawnFlyingComponent(int, int8) { EAXJMP(0x4EB060); } WRAPPER void CPed::RestorePreviousState(void) { EAXJMP(0x4C5E30); } WRAPPER void CPed::ClearAttack(void) { EAXJMP(0x4E6790); } +WRAPPER void CPed::PedSetQuickDraggedOutCarPositionCB(CAnimBlendAssociation *dragAssoc, void *arg) { EAXJMP(0x4E2480); } +WRAPPER void CPed::PedSetDraggedOutCarPositionCB(CAnimBlendAssociation *dragAssoc, void *arg) { EAXJMP(0x4E2920); } +WRAPPER void CPed::SetPedPositionInCar(void) { EAXJMP(0x4D4970); } static char ObjectiveText[34][28] = { "No Obj", @@ -183,9 +194,8 @@ static char WaitStateText[21][16] = { static RwObject* RemoveAllModelCB(RwObject *object, void *data) { - RpAtomic* atomic = (RpAtomic*)object; - if (CVisibilityPlugins::GetAtomicModelInfo(atomic)) - { + RpAtomic *atomic = (RpAtomic*)object; + if (CVisibilityPlugins::GetAtomicModelInfo(atomic)) { RpClumpRemoveAtomic(atomic->clump, atomic); RpAtomicDestroy(atomic); } @@ -299,6 +309,14 @@ CPed::UseGroundColModel(void) m_nPedState == PED_DEAD; } +bool +CPed::CanSetPedState(void) +{ + return m_nPedState != PED_DIE && m_nPedState != PED_ARRESTED && + m_nPedState != PED_ENTER_CAR && m_nPedState != PED_CARJACK && m_nPedState != PED_DRAG_FROM_CAR && m_nPedState != PED_STEAL_CAR; +} + + void CPed::AddWeaponModel(int id) { @@ -398,8 +416,9 @@ CPed::RemoveBodyPart(PedNode nodeId, int8 unk) RecurseFrameChildrenVisibilityCB(frame, 0); pos.x = 0.0f; - pos.z = 0.0f; pos.y = 0.0f; + pos.z = 0.0f; + for (frame = RwFrameGetParent(frame); frame; frame = RwFrameGetParent(frame)) RwV3dTransformPoints(&pos, &pos, 1, RwFrameGetMatrix(frame)); @@ -490,11 +509,11 @@ CPed::OurPedCanSeeThisOne(CEntity *target) // Check if target is behind ped if (DotProduct2D(dist, CVector2D(this->GetForward())) < 0.0f) - return 0; + return false; // Check if target is too far away - if (dist.Magnitude() < 40.0f) - return 0; + if (dist.Magnitude() >= 40.0f) + return false; // Check line of sight from head CVector headPos = this->GetPosition(); @@ -589,7 +608,7 @@ CPed::IsPedHeadAbovePos(float zOffset) RwMatrix mat; CPedIK::GetWorldMatrix(GetNodeFrame(PED_HEAD), &mat); - return zOffset + GetPosition().z >= mat.pos.z; + return zOffset + GetPosition().z < RwMatrixGetPos(&mat)->z; } void @@ -602,22 +621,24 @@ CPed::FinishedAttackCB(CAnimBlendAssociation *attackAssoc, void *arg) if (attackAssoc) { switch (attackAssoc->animId) { case ANIM_WEAPON_START_THROW: - if ((!ped->IsPlayer() || ((CPlayerPed*)ped)->field_1376) && ped->IsPlayer()) + if ((!ped->IsPlayer() || ((CPlayerPed*)ped)->field_1380) && ped->IsPlayer()) { - attackAssoc->blendDelta = -1000.0; + attackAssoc->blendDelta = -1000.0f; newAnim = CAnimManager::AddAnimation((RpClump*)ped->m_rwObject, ASSOCGRP_STD, ANIM_WEAPON_THROWU); } else { - attackAssoc->blendDelta = -1000.0; + attackAssoc->blendDelta = -1000.0f; newAnim = CAnimManager::AddAnimation((RpClump*)ped->m_rwObject, ASSOCGRP_STD, ANIM_WEAPON_THROW); } - newAnim->SetFinishCallback(CPed::FinishedAttackCB, ped); - break; + newAnim->SetFinishCallback(FinishedAttackCB, ped); + return; + case ANIM_FIGHT_PPUNCH: - attackAssoc->blendDelta = -8.0; + attackAssoc->blendDelta = -8.0f; attackAssoc->flags |= ASSOC_DELETEFADEDOUT; ped->ClearAttack(); - break; + return; + case ANIM_WEAPON_THROW: case ANIM_WEAPON_THROWU: if (ped->GetWeapon()->m_nAmmoTotal > 0) { @@ -626,12 +647,11 @@ CPed::FinishedAttackCB(CAnimBlendAssociation *attackAssoc, void *arg) } break; default: - if (!ped->m_ped_flagA4) - ped->ClearAttack(); - break; } - } else if (!ped->m_ped_flagA4) + } + + if (!ped->m_ped_flagA4) ped->ClearAttack(); } @@ -658,7 +678,7 @@ CPed::Attack(void) ourWeaponFire = ourWeapon->m_eWeaponFire; weaponAnimAssoc = RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ourWeapon->m_AnimToPlay); lastReloadWasInFuture = m_ped_flagA4; - reloadAnimAssoc = 0; + reloadAnimAssoc = nil; reloadAnim = NUM_ANIMS; delayBetweenAnimAndFire = ourWeapon->m_fAnimFrameFire; weaponAnim = ourWeapon->m_AnimToPlay; @@ -676,7 +696,7 @@ CPed::Attack(void) if (reloadAnimAssoc) { if (!CPed::IsPlayer() || ((CPlayerPed*)this)->field_1380) - CPed::ClearAttack(); + ClearAttack(); return; } @@ -686,12 +706,13 @@ CPed::Attack(void) lastReloadWasInFuture = true; if (!weaponAnimAssoc) { - if (ourWeapon->m_bThrow) { + weaponAnimAssoc = RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ourWeapon->m_Anim2ToPlay); + delayBetweenAnimAndFire = ourWeapon->m_fAnim2FrameFire; + + // Long throw granade, molotov + if (!weaponAnimAssoc && ourWeapon->m_bThrow) { weaponAnimAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_WEAPON_THROWU); delayBetweenAnimAndFire = 0.2f; - } else { - weaponAnimAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ourWeapon->m_Anim2ToPlay); - delayBetweenAnimAndFire = ourWeapon->m_fAnim2FrameFire; } } if (weaponAnimAssoc) { @@ -739,15 +760,15 @@ CPed::Attack(void) // If reloading just began, start the animation if (GetWeapon()->m_eWeaponState == WEAPONSTATE_RELOADING && reloadAnim != NUM_ANIMS && !reloadAnimAssoc) { CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, reloadAnim, 8.0f); - CPed::ClearLookFlag(); - CPed::ClearAimFlag(); + ClearLookFlag(); + ClearAimFlag(); m_ped_flagA4 = false; - m_ped_flagA8 = false; + bIsPointingGunAt = false; m_lastHitTime = CTimer::GetTimeInMilliseconds(); return; } } else { - if (weaponAnimAssoc->animId <= ANIM_WEAPON_BAT_V) { + if (weaponAnimAssoc->animId == ANIM_WEAPON_BAT_V || weaponAnimAssoc->animId == ANIM_WEAPON_BAT_H) { DMAudio.PlayOneShot(uAudioEntityId, SOUND_WEAPON_BAT_ATTACK, 1.0f); } else if (weaponAnimAssoc->animId == ANIM_FIGHT_PPUNCH) { DMAudio.PlayOneShot(uAudioEntityId, SOUND_WEAPON_PUNCH_ATTACK, 0.0f); @@ -814,24 +835,22 @@ CPed::Attack(void) CAnimManager::BlendAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, ourWeapon->m_Anim2ToPlay, 8.0f); } } else { - CPed::ClearAimFlag(); + ClearAimFlag(); // Echoes of bullets, at the end of the attack. (Bug: doesn't play while reloading) - if (weaponAnimAssoc->currentTime - weaponAnimAssoc->timeStep < ourWeapon->m_fAnimLoopEnd) { - if (ourWeaponType < WEAPONTYPE_SNIPERRIFLE) { - switch (ourWeaponType) { - case WEAPONTYPE_UZI: - DMAudio.PlayOneShot(uAudioEntityId, SOUND_WEAPON_UZI_BULLET_ECHO, 0.0f); - break; - case WEAPONTYPE_AK47: - DMAudio.PlayOneShot(uAudioEntityId, SOUND_WEAPON_AK47_BULLET_ECHO, 0.0f); - break; - case WEAPONTYPE_M16: - DMAudio.PlayOneShot(uAudioEntityId, SOUND_WEAPON_M16_BULLET_ECHO, 0.0f); - break; - default: - break; - } + if (weaponAnimAssoc->currentTime - weaponAnimAssoc->timeStep <= ourWeapon->m_fAnimLoopEnd) { + switch (ourWeaponType) { + case WEAPONTYPE_UZI: + DMAudio.PlayOneShot(uAudioEntityId, SOUND_WEAPON_UZI_BULLET_ECHO, 0.0f); + break; + case WEAPONTYPE_AK47: + DMAudio.PlayOneShot(uAudioEntityId, SOUND_WEAPON_AK47_BULLET_ECHO, 0.0f); + break; + case WEAPONTYPE_M16: + DMAudio.PlayOneShot(uAudioEntityId, SOUND_WEAPON_M16_BULLET_ECHO, 0.0f); + break; + default: + break; } } @@ -885,7 +904,7 @@ CPed::RemoveWeaponModel(int modelId) void CPed::SetCurrentWeapon(eWeaponType weaponType) { - CWeaponInfo* weaponInfo; + CWeaponInfo *weaponInfo; if (HasWeapon(weaponType)) { weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); @@ -909,9 +928,7 @@ CPed::SelectGunIfArmed(void) if (m_weapons[i].m_nAmmoTotal > 0) { weaponType = m_weapons[i].m_eWeaponType; - // Original condition was ridiculous - // if (weaponType == WEAPONTYPE_COLT45 || weaponType < WEAPONTYPE_M16 || weaponType < WEAPONTYPE_FLAMETHROWER || weaponType == WEAPONTYPE_FLAMETHROWER) - if (weaponType < WEAPONTYPE_MOLOTOV) { + if (weaponType >= WEAPONTYPE_COLT45 && weaponType != WEAPONTYPE_M16 && weaponType <= WEAPONTYPE_FLAMETHROWER) { SetCurrentWeapon(weaponType); return true; } @@ -960,7 +977,7 @@ CPed::ClearPointGunAt(void) ClearLookFlag(); ClearAimFlag(); - m_ped_flagA8 = false; + bIsPointingGunAt = false; if (m_nPedState == PED_AIM_GUN) { RestorePreviousState(); weaponInfo = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); @@ -975,6 +992,477 @@ CPed::ClearPointGunAt(void) } } +void +CPed::BeingDraggedFromCar(void) +{ + CAnimBlendAssociation *animAssoc; + AnimationId enterAnim; + bool dontRunAnim = false; + PedLineUpPhase lineUpType; + + if (!m_pVehicleAnim) { + CAnimManager::BlendAnimation((RpClump*) m_rwObject, m_animGroup, ANIM_IDLE_STANCE, 100.0f); + animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_CAR_SIT); + if (!animAssoc) { + animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_CAR_LSIT); + if (!animAssoc) { + animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_CAR_SITP); + if (!animAssoc) + animAssoc = RpAnimBlendClumpGetAssociation((RpClump*) m_rwObject, ANIM_CAR_SITPLO); + } + } + if (animAssoc) + animAssoc->blendDelta = -1000.0f; + + if (m_vehEnterType == VEHICLE_ENTER_FRONT_LEFT || m_vehEnterType == VEHICLE_ENTER_REAR_LEFT) { + if (m_ped_flagF10) { + enterAnim = ANIM_CAR_QJACKED; + } else if (m_pMyVehicle->bIsLow) { + enterAnim = ANIM_CAR_LJACKED_LHS; + } else { + enterAnim = ANIM_CAR_JACKED_LHS; + } + } else if (m_vehEnterType == VEHICLE_ENTER_FRONT_RIGHT || m_vehEnterType == VEHICLE_ENTER_REAR_RIGHT) { + if (m_pMyVehicle->bIsLow) + enterAnim = ANIM_CAR_LJACKED_RHS; + else + enterAnim = ANIM_CAR_JACKED_RHS; + } else + dontRunAnim = true; + + + if (!dontRunAnim) + m_pVehicleAnim = CAnimManager::AddAnimation((RpClump*) m_rwObject, ASSOCGRP_STD, enterAnim); + + m_pVehicleAnim->SetFinishCallback(PedSetDraggedOutCarCB, this); + lineUpType = LINE_UP_TO_CAR_START; + } else if (m_pVehicleAnim->currentTime <= 1.4f) { + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + lineUpType = LINE_UP_TO_CAR_START; + } else { + lineUpType = LINE_UP_TO_CAR_2; + } + + LineUpPedWithCar(lineUpType); +} + +void +CPed::RestartNonPartialAnims(void) +{ + CAnimBlendAssociation* assoc; + + for (assoc = RpAnimBlendClumpGetFirstAssociation((RpClump*)m_rwObject); !assoc; assoc = RpAnimBlendGetNextAssociation(assoc)) { + if (!assoc->IsPartial()) + assoc->flags |= ASSOC_RUNNING; + } +} + +void +CPed::PedSetDraggedOutCarCB(CAnimBlendAssociation *dragAssoc, void *arg) +{ + CAnimBlendAssociation *quickJackedAssoc; + CVehicle *vehicle; + CPed *ped = (CPed*)arg; + eWeaponType weaponType = ped->GetWeapon()->m_eWeaponType; + + quickJackedAssoc = RpAnimBlendClumpGetAssociation((RpClump*) ped->m_rwObject, ANIM_CAR_QJACKED); + if (ped->m_nPedState != PED_ARRESTED) { + ped->m_nLastPedState = PED_NONE; + if (dragAssoc) + dragAssoc->blendDelta = -1000.0; + } + ped->RestartNonPartialAnims(); + ped->m_pVehicleAnim = nil; + ped->m_pSeekTarget = nil; + vehicle = ped->m_pMyVehicle; + + switch (ped->m_vehEnterType) { + case VEHICLE_ENTER_FRONT_RIGHT: + vehicle->m_nGettingOutFlags &= ~GETTING_IN_OUT_FR; + break; + case VEHICLE_ENTER_REAR_RIGHT: + vehicle->m_nGettingOutFlags &= ~GETTING_IN_OUT_RR; + break; + case VEHICLE_ENTER_FRONT_LEFT: + vehicle->m_nGettingOutFlags &= ~GETTING_IN_OUT_FL; + break; + case VEHICLE_ENTER_REAR_LEFT: + vehicle->m_nGettingOutFlags &= ~GETTING_IN_OUT_RL; + break; + } + + if (vehicle->pDriver == ped) { + vehicle->RemoveDriver(); + if (vehicle->m_nDoorLock == CARLOCK_COP_CAR) + vehicle->m_nDoorLock = CARLOCK_UNLOCKED; + + if (ped->m_nPedType == PEDTYPE_COP && vehicle->IsLawEnforcementVehicle()) + vehicle->ChangeLawEnforcerState(false); + } else { + for (int i = 0; i < vehicle->m_nNumMaxPassengers; i++) { + if (vehicle->pPassengers[i] == ped) { + vehicle->pPassengers[i] = nil; + vehicle->m_nNumPassengers--; + } + } + } + + ped->bInVehicle = false; + if (ped->IsPlayer()) + AudioManager.PlayerJustLeftCar(); + + if (quickJackedAssoc) { + dragAssoc->SetDeleteCallback(PedSetQuickDraggedOutCarPositionCB, ped); + } else { + dragAssoc->SetDeleteCallback(PedSetDraggedOutCarPositionCB, ped); + if (ped->CanSetPedState()) + CAnimManager::BlendAnimation((RpClump*) ped->m_rwObject, ASSOCGRP_STD, ANIM_GETUP1, 1000.0f); + } + + // Only uzi can be used on cars, so previous weapon was stored + if (ped->IsPlayer() && weaponType == WEAPONTYPE_UZI) { + if (ped->m_storedWeapon != NO_STORED_WEAPON) { + ped->SetCurrentWeapon(ped->m_storedWeapon); + ped->m_storedWeapon = NO_STORED_WEAPON; + } + } else { + ped->AddWeaponModel(CWeaponInfo::GetWeaponInfo(weaponType)->m_nModelId); + } + ped->m_nStoredActionState = 0; + ped->m_ped_flagI4 = false; +} + +void +CPed::GetLocalPositionToOpenCarDoor(CVector *output, CVehicle *veh, uint32 enterType, float seatPosMult) +{ + CVehicleModelInfo *vehModel; + CVector vehDoorPos; + CVector vehDoorOffset; + float seatOffset; + + vehModel = (CVehicleModelInfo*) CModelInfo::GetModelInfo(veh->m_modelIndex); + if (veh->bIsVan && (enterType == VEHICLE_ENTER_REAR_LEFT || enterType == VEHICLE_ENTER_REAR_RIGHT)) { + seatOffset = 0.0f; + vehDoorOffset = offsetToOpenVanDoor; + } else { + seatOffset = veh->m_handling->fSeatOffsetDistance * seatPosMult; + if (veh->bIsLow) { + vehDoorOffset = offsetToOpenLowCarDoor; + } else { + vehDoorOffset = offsetToOpenRegularCarDoor; + } + } + + switch (enterType) { + case VEHICLE_ENTER_FRONT_RIGHT: + if (vehModel->m_vehicleType == VEHICLE_TYPE_BOAT) + vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_BOAT_RUDDER]; + else + vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_FRONT_SEATS]; + + vehDoorPos.x += seatOffset; + vehDoorOffset.x = -vehDoorOffset.x; + break; + + case VEHICLE_ENTER_REAR_RIGHT: + vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_REAR_SEATS]; + vehDoorPos.x += seatOffset; + vehDoorOffset.x = -vehDoorOffset.x; + break; + + case VEHICLE_ENTER_FRONT_LEFT: + if (vehModel->m_vehicleType == VEHICLE_TYPE_BOAT) + vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_BOAT_RUDDER]; + else + vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_FRONT_SEATS]; + + vehDoorPos.x = -(vehDoorPos.x + seatOffset); + break; + + case VEHICLE_ENTER_REAR_LEFT: + vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_REAR_SEATS]; + vehDoorPos.x = -(vehDoorPos.x + seatOffset); + break; + + default: + if (vehModel->m_vehicleType == VEHICLE_TYPE_BOAT) + vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_BOAT_RUDDER]; + else + vehDoorPos = vehModel->m_positions[VEHICLE_DUMMY_FRONT_SEATS]; + + vehDoorOffset = CVector(0.0f, 0.0f, 0.0f); + } + *output = vehDoorPos - vehDoorOffset; +} + +// This function was mostly duplicate of GetLocalPositionToOpenCarDoor, so I've used it. +void +CPed::GetPositionToOpenCarDoor(CVector *output, CVehicle *veh, uint32 enterType) +{ + CVector localPos; + CVector vehDoorPos; + + GetLocalPositionToOpenCarDoor(&localPos, veh, enterType, 1.0f); + vehDoorPos = Multiply3x3(veh->GetMatrix(), localPos) + veh->GetPosition(); + +/* + // Not used. + CVector localVehDoorOffset; + + if (veh->bIsVan && (enterType == VEHICLE_ENTER_REAR_LEFT || enterType == VEHICLE_ENTER_REAR_RIGHT)) { + localVehDoorOffset = offsetToOpenVanDoor; + } else { + if (veh->bIsLow) { + localVehDoorOffset = offsetToOpenLowCarDoor; + } else { + localVehDoorOffset = offsetToOpenRegularCarDoor; + } + } + + vehDoorPosWithoutOffset = Multiply3x3(veh->GetMatrix(), localPos + localVehDoorOffset) + veh->GetPosition(); +*/ + *output = vehDoorPos; +} + +void +CPed::GetPositionToOpenCarDoor(CVector *output, CVehicle *veh, uint32 enterType, float offset) +{ + CVector doorPos; + CMatrix vehMat(veh->GetMatrix()); + + GetLocalPositionToOpenCarDoor(output, veh, enterType, offset); + doorPos = Multiply3x3(vehMat, *output); + + *output = *veh->GetPosition() + doorPos; +} + +void +CPed::LineUpPedWithCar(PedLineUpPhase phase) +{ + bool vehIsUpsideDown = false; + int vehAnim; + float seatPosMult = 0.0f; + float currentZ; + float adjustedTimeStep; + + if (CReplay::IsPlayingBack()) + return; + + if (!m_ped_flagC8 && phase != LINE_UP_TO_CAR_2) { + if (RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ANIM_CAR_SIT)) { + SetPedPositionInCar(); + return; + } + if (RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ANIM_CAR_LSIT)) { + SetPedPositionInCar(); + return; + } + if (RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ANIM_CAR_SITP)) { + SetPedPositionInCar(); + return; + } + if (RpAnimBlendClumpGetAssociation((RpClump*)m_rwObject, ANIM_CAR_SITPLO)) { + SetPedPositionInCar(); + return; + } + m_ped_flagC8 = 1; + } + if (phase == LINE_UP_TO_CAR_START) { + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + } + CVehicle *veh = m_pMyVehicle; + + // Not quite right, IsUpsideDown func. checks for <= -0.9f. + // Since that function is also used in this file, doesn't this variable indicate upsidedownness?! + if (veh->GetUp().z <= -0.8f) + vehIsUpsideDown = true; + + if (m_vehEnterType == VEHICLE_ENTER_FRONT_RIGHT || m_vehEnterType == VEHICLE_ENTER_REAR_RIGHT) { + if (vehIsUpsideDown) { + m_fRotationDest = -PI + atan2(-veh->GetForward().x, veh->GetForward().y); + } else if (veh->bIsBus) { + m_fRotationDest = 0.5 * PI + atan2(-veh->GetForward().x, veh->GetForward().y); + } else { + m_fRotationDest = atan2(-veh->GetForward().x, veh->GetForward().y); + } + } else if (m_vehEnterType == VEHICLE_ENTER_FRONT_LEFT || m_vehEnterType == VEHICLE_ENTER_REAR_LEFT) { + if (vehIsUpsideDown) { + m_fRotationDest = atan2(-veh->GetForward().x, veh->GetForward().y); + } else if (veh->bIsBus) { + m_fRotationDest = -0.5 * PI + atan2(-veh->GetForward().x, veh->GetForward().y); + } else { + m_fRotationDest = atan2(-veh->GetForward().x, veh->GetForward().y); + } + } + + if (!bInVehicle) + seatPosMult = 1.0f; + + if (m_pVehicleAnim) { + vehAnim = m_pVehicleAnim->animId; + + switch (vehAnim) { + case ANIM_CAR_JACKED_RHS: + case ANIM_CAR_LJACKED_RHS: + case ANIM_CAR_JACKED_LHS: + case ANIM_CAR_LJACKED_LHS: + case ANIM_CAR_QJACKED: + case ANIM_CAR_GETOUT_LHS: + case ANIM_CAR_GETOUT_LOW_LHS: + case ANIM_CAR_GETOUT_RHS: + case ANIM_CAR_GETOUT_LOW_RHS: + case ANIM_CAR_CRAWLOUT_RHS: + case ANIM_CAR_CRAWLOUT_RHS2: + case ANIM_VAN_GETIN_L: + case ANIM_VAN_GETOUT_L: + case ANIM_VAN_GETIN: + case ANIM_VAN_GETOUT: + seatPosMult = m_pVehicleAnim->currentTime / m_pVehicleAnim->hierarchy->totalLength; + break; + case ANIM_CAR_QJACK: + case ANIM_CAR_GETIN_LHS: + case ANIM_CAR_GETIN_LOW_LHS: + case ANIM_CAR_GETIN_RHS: + case ANIM_CAR_GETIN_LOW_RHS: + case ANIM_DRIVE_BOAT: + seatPosMult = m_pVehicleAnim->GetTimeLeft() / m_pVehicleAnim->hierarchy->totalLength; + break; + case ANIM_CAR_CLOSEDOOR_LHS: + case ANIM_CAR_CLOSEDOOR_LOW_LHS: + case ANIM_CAR_CLOSEDOOR_RHS: + case ANIM_CAR_CLOSEDOOR_LOW_RHS: + case ANIM_CAR_SHUFFLE_RHS: + case ANIM_CAR_LSHUFFLE_RHS: + seatPosMult = 0.0f; + break; + case ANIM_CAR_CLOSE_LHS: + case ANIM_CAR_CLOSE_RHS: + case ANIM_COACH_OPEN_L: + case ANIM_COACH_OPEN_R: + case ANIM_COACH_IN_L: + case ANIM_COACH_IN_R: + case ANIM_COACH_OUT_L: + seatPosMult = 1.0f; + break; + default: + break; + } + } + + CVector neededPos; + + if (phase == LINE_UP_TO_CAR_2) { + neededPos = *GetPosition(); + } else { + GetPositionToOpenCarDoor(&neededPos, veh, m_vehEnterType, seatPosMult); + } + + CVector autoZPos = neededPos; + + if (veh->bIsInWater) { + if (veh->m_vehType == VEHICLE_TYPE_BOAT && veh->IsUpsideDown()) + autoZPos.z += 1.0f; + } else { + CPedPlacement::FindZCoorForPed(&autoZPos); + } + + if (phase == LINE_UP_TO_CAR_END || phase == LINE_UP_TO_CAR_2) { + neededPos.z = GetPosition().z; + + // Getting out + if (!veh->bIsBus || (veh->bIsBus && vehIsUpsideDown)) { + float pedZSpeedOnExit = m_vecMoveSpeed.z - 0.008f * CTimer::GetTimeStep(); + + // If we're not in ground at next step, apply animation + if (neededPos.z + pedZSpeedOnExit > autoZPos.z) { + m_vecMoveSpeed.z = pedZSpeedOnExit; + ApplyMoveSpeed(); + // Removing below line breaks the animation + neededPos.z = GetPosition().z; + } else { + neededPos.z = autoZPos.z; + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + } + } + } + + if (autoZPos.z > neededPos.z) { + currentZ = GetPosition().z; + if (m_pVehicleAnim && vehAnim != ANIM_VAN_GETIN_L && vehAnim != ANIM_VAN_CLOSE_L && vehAnim != ANIM_VAN_CLOSE && vehAnim != ANIM_VAN_GETIN) { + neededPos.z = autoZPos.z; + m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f); + } else if (neededPos.z <= currentZ && m_pVehicleAnim && vehAnim != ANIM_VAN_CLOSE_L && vehAnim != ANIM_VAN_CLOSE) { + adjustedTimeStep = min(m_pVehicleAnim->timeStep, 0.1f); + + // Smoothly change ped position + neededPos.z = currentZ - (currentZ - neededPos.z) / (m_pVehicleAnim->GetTimeLeft() / adjustedTimeStep); + } + } else { + // We may need to raise up the ped + if (phase == LINE_UP_TO_CAR_START) { + currentZ = GetPosition().z; + + if (neededPos.z > currentZ) { + + if (m_pVehicleAnim && + (vehAnim == ANIM_CAR_GETIN_RHS || vehAnim == ANIM_CAR_GETIN_LOW_RHS || vehAnim == ANIM_CAR_GETIN_LHS || vehAnim == ANIM_CAR_GETIN_LOW_LHS + || vehAnim == ANIM_CAR_QJACK || vehAnim == ANIM_VAN_GETIN_L || vehAnim == ANIM_VAN_GETIN)) { + adjustedTimeStep = min(m_pVehicleAnim->timeStep, 0.1f); + + // Smoothly change ped position + neededPos.z = (neededPos.z - currentZ) / (m_pVehicleAnim->GetTimeLeft() / adjustedTimeStep) + currentZ; + } else if (m_nPedState == PED_ENTER_CAR || m_nPedState == PED_CARJACK) { + neededPos.z = max(currentZ, autoZPos.z); + } + } + } + } + + // I hope + bool stillGettingInOut = false; + if (CTimer::GetTimeInMilliseconds() < m_nPedStateTimer) + stillGettingInOut = veh->m_vehType != VEHICLE_TYPE_BOAT || m_ped_flagG10; + + if (!stillGettingInOut) { + m_fRotationCur = m_fRotationDest; + } else { + float limitedAngle = CGeneral::LimitRadianAngle(m_fRotationDest); + float timeUntilStateChange = (m_nPedStateTimer - CTimer::GetTimeInMilliseconds())/600.0f; + + m_vecOffsetSeek.z = 0.0; + if (timeUntilStateChange <= 0.0f) { + m_vecOffsetSeek.x = 0.0; + m_vecOffsetSeek.y = 0.0; + } else { + neededPos -= timeUntilStateChange * m_vecOffsetSeek; + } + + if (limitedAngle >= PI + m_fRotationCur) { + limitedAngle -= 2 * PI; + } else if (limitedAngle <= m_fRotationCur - PI) { + limitedAngle += 2 * PI; + } + m_fRotationCur -= (m_fRotationCur - limitedAngle) * (1.0f - timeUntilStateChange); + } + + if (seatPosMult > 0.2f || vehIsUpsideDown) { + GetPosition() = neededPos; + + GetMatrix().SetRotate(0.0f, 0.0f, m_fRotationCur); + + // It will be all 0 after rotate. + GetPosition() = neededPos; + } else { + CVector output; + CMatrix vehDoorMat(veh->GetMatrix()); + + GetLocalPositionToOpenCarDoor(&output, veh, m_vehEnterType, 0.0f); + *vehDoorMat.GetPosition() += Multiply3x3(vehDoorMat, output); + GetMatrix() = vehDoorMat; + } + +} + WRAPPER void CPed::PedGetupCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4CE810); } WRAPPER void CPed::PedStaggerCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4CE8D0); } WRAPPER void CPed::PedEvadeCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D36E0); } @@ -989,7 +1477,6 @@ WRAPPER void CPed::PedAnimDoorCloseCB(CAnimBlendAssociation *assoc, void *arg) { WRAPPER void CPed::SetInCarCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4CF220); } WRAPPER void CPed::PedSetOutCarCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4CE8F0); } WRAPPER void CPed::PedAnimAlignCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4DE130); } -WRAPPER void CPed::PedSetDraggedOutCarCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4CF000); } WRAPPER void CPed::PedAnimStepOutCarCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4DF5C0); } WRAPPER void CPed::PedSetInTrainCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4E3290); } WRAPPER void CPed::PedSetOutTrainCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4E36E0); } @@ -999,8 +1486,6 @@ WRAPPER void CPed::FinishJumpCB(CAnimBlendAssociation *assoc, void *arg) { EAXJM WRAPPER void CPed::PedLandCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4CE8A0); } WRAPPER void FinishFuckUCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4C6620); } WRAPPER void CPed::RestoreHeadingRateCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4D6550); } -WRAPPER void CPed::PedSetQuickDraggedOutCarPositionCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4E2480); } -WRAPPER void CPed::PedSetDraggedOutCarPositionCB(CAnimBlendAssociation *assoc, void *arg) { EAXJMP(0x4E2920); } STARTPATCHES InjectHook(0x4CF8F0, &CPed::AddWeaponModel, PATCH_JUMP); @@ -1024,4 +1509,11 @@ STARTPATCHES InjectHook(0x4E4A10, &CPed::Duck, PATCH_JUMP); InjectHook(0x4E4A30, &CPed::ClearDuck, PATCH_JUMP); InjectHook(0x4E6180, &CPed::ClearPointGunAt, PATCH_JUMP); + InjectHook(0x4E07D0, &CPed::BeingDraggedFromCar, PATCH_JUMP); + InjectHook(0x4CF000, &CPed::PedSetDraggedOutCarCB, PATCH_JUMP); + InjectHook(0x4C5D80, &CPed::RestartNonPartialAnims, PATCH_JUMP); + InjectHook(0x4E4730, &CPed::GetLocalPositionToOpenCarDoor, PATCH_JUMP); + InjectHook(0x4E4660, (void (*)(CVector*, CVehicle*, uint32, float)) CPed::GetPositionToOpenCarDoor, PATCH_JUMP); + InjectHook(0x4E1A30, (void (*)(CVector*, CVehicle*, uint32)) CPed::GetPositionToOpenCarDoor, PATCH_JUMP); + InjectHook(0x4DF940, &CPed::LineUpPedWithCar, PATCH_JUMP); ENDPATCHES diff --git a/src/entities/Ped.h b/src/entities/Ped.h index 43131533..34c7a485 100644 --- a/src/entities/Ped.h +++ b/src/entities/Ped.h @@ -12,6 +12,19 @@ struct CPathNode; +enum { + VEHICLE_ENTER_FRONT_RIGHT = 11, + VEHICLE_ENTER_REAR_RIGHT = 12, + VEHICLE_ENTER_FRONT_LEFT = 15, + VEHICLE_ENTER_REAR_LEFT = 16, +}; + +enum PedLineUpPhase { + LINE_UP_TO_CAR_START, + LINE_UP_TO_CAR_END, + LINE_UP_TO_CAR_2 +}; + enum PedOnGroundState { NO_PED, PED_BELOW_PLAYER, @@ -102,7 +115,7 @@ public: uint8 m_ped_flagA1 : 1; uint8 m_ped_flagA2 : 1; uint8 m_ped_flagA4 : 1; // stores (CTimer::GetTimeInMilliseconds() < m_lastHitTime) - uint8 m_ped_flagA8 : 1; + uint8 bIsPointingGunAt : 1; uint8 bIsLooking : 1; uint8 m_ped_flagA20 : 1; // "look" method? - probably missing in SA uint8 bIsRestoringLook : 1; @@ -177,15 +190,15 @@ public: int32 m_pEventEntity; float m_fAngleToEvent; AnimBlendFrameData *m_pFrames[PED_NODE_MAX]; - int32 m_animGroup; - int32 m_pVehicleAnim; + AssocGroupId m_animGroup; + CAnimBlendAssociation *m_pVehicleAnim; CVector2D m_vecAnimMoveDelta; CVector m_vecOffsetSeek; CPedIK m_pedIK; uint8 stuff1[8]; uint32 m_nPedStateTimer; PedState m_nPedState; - int32 m_nLastPedState; + PedState m_nLastPedState; int32 m_nMoveState; int32 m_nStoredActionState; int32 m_nPrevActionState; @@ -207,7 +220,9 @@ public: uint8 stuff2[20]; float m_fRotationCur; float m_fRotationDest; - uint8 stuff13[6]; + uint32 m_headingRate; + uint16 m_vehEnterType; + uint16 m_walkAroundType; CEntity *m_pCurrentPhysSurface; CVector m_vecOffsetFromPhysSurface; CEntity *m_pCurSurface; @@ -222,7 +237,7 @@ public: CEntity *m_pCollidingEntity; uint8 stuff6[12]; CWeapon m_weapons[NUM_PED_WEAPONTYPES]; - int32 stuff7; + eWeaponType m_storedWeapon; uint8 m_currentWeapon; // eWeaponType uint8 m_maxWeaponTypeAllowed; // eWeaponType uint8 stuff[2]; @@ -253,6 +268,7 @@ public: bool IsPlayer(void); bool UseGroundColModel(void); + bool CanSetPedState(void); void AddWeaponModel(int id); void AimGun(void); void KillPedWithCar(CVehicle *veh, float impulse); @@ -278,6 +294,13 @@ public: void Duck(void); void ClearDuck(void); void ClearPointGunAt(void); + void BeingDraggedFromCar(void); + void RestartNonPartialAnims(void); + void LineUpPedWithCar(PedLineUpPhase phase); + void SetPedPositionInCar(void); + static void GetLocalPositionToOpenCarDoor(CVector *output, CVehicle *veh, uint32 enterType, float offset); + static void GetPositionToOpenCarDoor(CVector *output, CVehicle *veh, uint32 enterType, float seatPosMult); + static void GetPositionToOpenCarDoor(CVector* output, CVehicle* veh, uint32 enterType); static RwObject *SetPedAtomicVisibilityCB(RwObject *object, void *data); static RwFrame *RecurseFrameChildrenVisibilityCB(RwFrame *frame, void *data); static void PedGetupCB(CAnimBlendAssociation *assoc, void *arg); @@ -311,6 +334,9 @@ public: CWeapon *GetWeapon(void) { return &m_weapons[m_currentWeapon]; } RwFrame *GetNodeFrame(int nodeId) { return m_pFrames[nodeId]->frame; } + static CVector &offsetToOpenRegularCarDoor; + static CVector &offsetToOpenLowCarDoor; + static CVector &offsetToOpenVanDoor; static bool &bNastyLimbsCheat; static bool &bPedCheat2; static bool &bPedCheat3; diff --git a/src/entities/Physical.cpp b/src/entities/Physical.cpp index adc53525..b35b2d11 100644 --- a/src/entities/Physical.cpp +++ b/src/entities/Physical.cpp @@ -1504,7 +1504,7 @@ CPhysical::ProcessCollisionSectorList(CPtrList *lists) else if(A->GetUp().z > 0.3f) adhesion = 0.0f; else - adhesion *= max(5.0f, 0.03f*impulseA + 1.0f); + adhesion *= min(5.0f, 0.03f*impulseA + 1.0f); } if(A->ApplyFriction(adhesion, aColPoints[i])) diff --git a/src/entities/Vehicle.cpp b/src/entities/Vehicle.cpp index bac05f7b..ced504a3 100644 --- a/src/entities/Vehicle.cpp +++ b/src/entities/Vehicle.cpp @@ -2,6 +2,8 @@ #include "patcher.h" #include "Vehicle.h" #include "Pools.h" +#include "CarCtrl.h" +#include "ModelIndices.h" bool &CVehicle::bWheelsOnlyCheat = *(bool *)0x95CD78; bool &CVehicle::bAllDodosCheat = *(bool *)0x95CD75; @@ -11,3 +13,53 @@ bool &CVehicle::bCheat5 = *(bool *)0x95CD64; void *CVehicle::operator new(size_t sz) { return CPools::GetVehiclePool()->New(); } void CVehicle::operator delete(void *p, size_t sz) { CPools::GetVehiclePool()->Delete((CVehicle*)p); } + +bool +CVehicle::IsLawEnforcementVehicle(void) +{ + switch (m_modelIndex) { + case MI_FBICAR: + case MI_POLICE: + case MI_ENFORCER: + case MI_PREDATOR: + case MI_RHINO: + case MI_BARRACKS: + return true; + default: + return false; + } +} + +void +CVehicle::ChangeLawEnforcerState(bool enable) +{ + if (enable) { + if (!bIsLawEnforcer) { + bIsLawEnforcer = true; + CCarCtrl::NumLawEnforcerCars++; + } + } else { + if (bIsLawEnforcer) { + bIsLawEnforcer = false; + CCarCtrl::NumLawEnforcerCars--; + } + } +} + +void +CVehicle::RemoveDriver(void) +{ + m_status = STATUS_ABANDONED; + pDriver = nil; +} + +bool +CVehicle::IsUpsideDown(void) +{ + return GetUp().z <= -0.9f; +} + +STARTPATCHES + InjectHook(0x552820, &CVehicle::ChangeLawEnforcerState, PATCH_JUMP); + InjectHook(0x5520A0, &CVehicle::RemoveDriver, PATCH_JUMP); +ENDPATCHES \ No newline at end of file diff --git a/src/entities/Vehicle.h b/src/entities/Vehicle.h index fea15d35..f11e9e97 100644 --- a/src/entities/Vehicle.h +++ b/src/entities/Vehicle.h @@ -3,26 +3,53 @@ #include "Physical.h" class CPed; +class CFire; +struct tHandlingData; + +enum { + GETTING_IN_OUT_FL = 1, + GETTING_IN_OUT_RL = 2, + GETTING_IN_OUT_FR = 4, + GETTING_IN_OUT_RR = 8 +}; + +enum eCarLock : uint8 { + CARLOCK_NOT_USED, + CARLOCK_UNLOCKED, + CARLOCK_LOCKED, + CARLOCK_LOCKOUT_PLAYER_ONLY, + CARLOCK_LOCKED_PLAYER_INSIDE, + CARLOCK_COP_CAR, + CARLOCK_FORCE_SHUT_DOORS, + CARLOCK_SKIP_SHUT_DOORS +}; class CVehicle : public CPhysical { public: // 0x128 - uint8 stuff1[116]; + tHandlingData *m_handling; + uint8 stuff1[112]; uint8 m_currentColour1; uint8 m_currentColour2; - uint8 m_extra1; - uint8 m_extra2; - int16 m_nAlarmState; + uint8 m_anExtras[2]; + int16 m_nAlarmState; // m_nWantedStarsOnEnter on DK22 + int16 m_nMissionValue; CPed *pDriver; CPed *pPassengers[8]; - uint8 stuff2[24]; + uint8 m_nNumPassengers; + int8 m_nNumGettingIn; + int8 m_nGettingInFlags; + int8 m_nGettingOutFlags; + uint8 m_nNumMaxPassengers; + char field_1CD[19]; CEntity *m_pCurSurface; - void* m_pFire; - float m_fWheelState; - float m_fAcceleration; - uint8 stuff4[5]; - uint8 m_veh_flagA1 : 1; + CFire *m_pCarFire; + float m_fSteerAngle; + float m_fGasPedal; + float m_fBreakPedal; + uint8 m_nCreatedBy; // eVehicleCreatedBy + uint8 bIsLawEnforcer : 1; uint8 m_veh_flagA2 : 1; uint8 m_veh_flagA4 : 1; uint8 m_veh_flagA8 : 1; @@ -30,10 +57,10 @@ public: uint8 m_veh_flagA20 : 1; uint8 m_veh_flagA40 : 1; uint8 m_veh_flagA80 : 1; - uint8 m_veh_flagB1 : 1; - uint8 m_veh_flagB2 : 1; - uint8 m_veh_flagB4 : 1; - uint8 m_veh_flagB8 : 1; + uint8 bIsVan : 1; + uint8 bIsBus : 1; + uint8 bIsBig : 1; + uint8 bIsLow : 1; uint8 m_veh_flagB10 : 1; uint8 m_veh_flagB20 : 1; uint8 m_veh_flagB40 : 1; @@ -54,10 +81,34 @@ public: uint8 m_veh_flagD20 : 1; uint8 m_veh_flagD40 : 1; uint8 m_veh_flagD80 : 1; - uint8 stuff5[7]; - float m_fHealth; - uint8 stuff6[128]; - int32 m_vehType; + int8 field_1F9; + uint8 m_nAmmoInClip[1]; // Used to make the guns on boat do a reload (20 by default) + int8 field_1FB; + int8 field_1FC[4]; + float m_fHealth; // 1000.0f = full health. 0 -> explode + uint8 m_nCurrentGear; + int8 field_205[3]; + int field_208; + uint32 m_nGunFiringTime; // last time when gun on vehicle was fired (used on boats) + uint32 m_nTimeOfDeath; + int16 field_214; + int16 m_nBombTimer; // goes down with each frame + CPed *m_pWhoDetonatedMe; + float field_21C; + float field_220; + eCarLock m_nDoorLock; + int8 m_nLastWeaponDamage; // see eWeaponType, -1 if no damage + int8 m_nRadioStation; + int8 field_22A; + int8 field_22B; + uint8 m_nCarHornTimer; + int8 field_22D; + uint8 m_nSirenOrAlarm; + int8 field_22F; + CStoredCollPoly m_frontCollPoly; // poly which is under front part of car + CStoredCollPoly m_rearCollPoly; // poly which is under rear part of car + float m_fSteerRatio; + eVehicleType m_vehType; static void *operator new(size_t); static void operator delete(void*, size_t); @@ -67,6 +118,10 @@ public: bool IsTrain(void) { return m_vehType == VEHICLE_TYPE_TRAIN; } bool IsHeli(void) { return m_vehType == VEHICLE_TYPE_HELI; } bool IsPlane(void) { return m_vehType == VEHICLE_TYPE_PLANE; } + bool IsLawEnforcementVehicle(void); + void ChangeLawEnforcerState(bool enable); + void RemoveDriver(void); + bool IsUpsideDown(void); static bool &bWheelsOnlyCheat; static bool &bAllDodosCheat; @@ -74,5 +129,7 @@ public: static bool &bCheat4; static bool &bCheat5; }; + static_assert(sizeof(CVehicle) == 0x288, "CVehicle: error"); static_assert(offsetof(CVehicle, m_pCurSurface) == 0x1E0, "CVehicle: error"); +static_assert(offsetof(CVehicle, m_nAlarmState) == 0x1A0, "CVehicle: error"); diff --git a/src/main.cpp b/src/main.cpp index f9ede02d..0dadc131 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -85,6 +85,7 @@ extern void (*DebugMenuProcess)(void); extern void (*DebugMenuRender)(void); void DebugMenuInit(void); +void PrintGameVersion(); RwRGBA gColourTop; @@ -154,6 +155,7 @@ Idle(void *arg) } RenderMenus(); + PrintGameVersion(); DoFade(); Render2dStuffAfterFade(); CCredits::Render(); @@ -186,6 +188,7 @@ FrontendIdle(void) DefinedState(); RenderMenus(); + PrintGameVersion(); DoFade(); Render2dStuffAfterFade(); CFont::DrawFonts(); @@ -772,6 +775,25 @@ AppEventHandler(RsEvent event, void *param) } } +void PrintGameVersion() +{ + CFont::SetPropOn(); + CFont::SetBackgroundOff(); + CFont::SetScale(SCREEN_SCALE_X(0.7f), SCREEN_SCALE_Y(0.5f)); + CFont::SetCentreOff(); + CFont::SetRightJustifyOff(); + CFont::SetBackGroundOnlyTextOff(); + CFont::SetFontStyle(FONT_BANK); + CFont::SetWrapx(SCREEN_WIDTH); + CFont::SetDropShadowPosition(0); + CFont::SetDropColor(CRGBA(0, 0, 0, 255)); + CFont::SetColor(CRGBA(235, 170, 50, 255)); + + strcpy(gString, "RE3"); + AsciiToUnicode(gString, gUString); + CFont::PrintString(SCREEN_SCALE_X(10.5f), SCREEN_SCALE_Y(8.0f), gUString); +} + STARTPATCHES InjectHook(0x48E480, Idle, PATCH_JUMP); InjectHook(0x48E700, FrontendIdle, PATCH_JUMP); diff --git a/src/math/Matrix.h b/src/math/Matrix.h index 2ee3863f..e2e5394e 100644 --- a/src/math/Matrix.h +++ b/src/math/Matrix.h @@ -145,6 +145,30 @@ public: m_matrix.pos.y = 0.0f; m_matrix.pos.z = 0.0f; } + void SetRotate(float xAngle, float yAngle, float zAngle) { + float cX = cos(xAngle); + float sX = sin(xAngle); + float cY = cos(yAngle); + float sY = sin(yAngle); + float cZ = cos(zAngle); + float sZ = sin(zAngle); + + m_matrix.right.x = cZ * cY - (sZ * sX) * sY; + m_matrix.right.y = (cZ * sX) * sY + sZ * cY; + m_matrix.right.z = -cX * sY; + + m_matrix.up.x = -sZ * cX; + m_matrix.up.y = cZ * cX; + m_matrix.up.z = sX; + + m_matrix.at.x = (sZ * sX) * cY + cZ * sY; + m_matrix.at.y = sZ * sY - (cZ * sX) * cY; + m_matrix.at.z = cX * cY; + + m_matrix.pos.x = 0.0f; + m_matrix.pos.y = 0.0f; + m_matrix.pos.z = 0.0f; + } void Reorthogonalise(void){ CVector &r = *GetRight(); CVector &f = *GetForward(); diff --git a/src/math/Rect.h b/src/math/Rect.h index d0824987..fd1bd05e 100644 --- a/src/math/Rect.h +++ b/src/math/Rect.h @@ -26,4 +26,20 @@ public: if(v.y < top) top = v.y; if(v.y > bottom) bottom = v.y; } + + void Translate(float x, float y){ + left += x; + right += x; + bottom += y; + top += y; + } + void Grow(float r){ + left -= r; + right += r; + top -= r; + bottom += r; + } + + float GetWidth(void) { return right - left; } + float GetHeight(void) { return bottom - top; } }; diff --git a/src/modelinfo/ModelIndices.h b/src/modelinfo/ModelIndices.h index be091e9b..feafcd40 100644 --- a/src/modelinfo/ModelIndices.h +++ b/src/modelinfo/ModelIndices.h @@ -326,6 +326,8 @@ enum MI_AIRTRAIN_VLO = 198, MI_LOPOLYGUY, + + NUM_DEFAULT_MODELS, }; void InitModelIndices(void); diff --git a/src/modelinfo/VehicleModelInfo.h b/src/modelinfo/VehicleModelInfo.h index 8068d359..2e2f1be2 100644 --- a/src/modelinfo/VehicleModelInfo.h +++ b/src/modelinfo/VehicleModelInfo.h @@ -3,7 +3,6 @@ #include "ClumpModelInfo.h" enum { - NUM_VEHICLE_POSITIONS = 10, NUM_FIRST_MATERIALS = 26, NUM_SECOND_MATERIALS = 26, NUM_VEHICLE_COLOURS = 8, @@ -25,7 +24,7 @@ enum { ATOMIC_FLAG_NOCULL = 0x800, }; -enum { +enum eVehicleType { VEHICLE_TYPE_CAR, VEHICLE_TYPE_BOAT, VEHICLE_TYPE_TRAIN, @@ -46,6 +45,13 @@ enum { NUM_VEHICLE_CLASSES }; +enum { + VEHICLE_DUMMY_BOAT_RUDDER = 0, + VEHICLE_DUMMY_FRONT_SEATS = 2, + VEHICLE_DUMMY_REAR_SEATS = 3, + NUM_VEHICLE_POSITIONS = 10 +}; + class CVehicleModelInfo : public CClumpModelInfo { public: diff --git a/src/render/Font.h b/src/render/Font.h index 2e698533..11c0f8ec 100644 --- a/src/render/Font.h +++ b/src/render/Font.h @@ -33,6 +33,12 @@ enum { FONT_HEADING, }; +enum { + ALIGN_LEFT, + ALIGN_CENTER, + ALIGN_RIGHT, +}; + class CFont { static CFontDetails &Details; @@ -56,6 +62,7 @@ public: static void DrawFonts(void); static uint16 character_code(uint8 c); + static CFontDetails GetDetails() { return Details; } static void SetScale(float x, float y) { Details.scaleX = x; Details.scaleY = y; } static void SetSlantRefPoint(float x, float y) { Details.slantRefX = x; Details.slantRefY = y; } static void SetSlant(float s) { Details.slant = s; } @@ -86,6 +93,23 @@ public: static void SetCentreOff(void) { Details.centre = false; } + static void SetAlignment(uint8 alignment) { + if (alignment == ALIGN_LEFT) { + CFont::Details.justify = true; + CFont::Details.centre = false; + CFont::Details.rightJustify = false; + } + else if (alignment == ALIGN_CENTER) { + CFont::Details.justify = false; + CFont::Details.centre = true; + CFont::Details.rightJustify = false; + } + else if (alignment == ALIGN_RIGHT) { + CFont::Details.justify = false; + CFont::Details.centre = false; + CFont::Details.rightJustify = true; + } + } static void SetWrapx(float x) { Details.wrapX = x; } static void SetCentreSize(float s) { Details.centreSize = s; } static void SetBackgroundOn(void) { Details.background = true; } diff --git a/src/render/Hud.cpp b/src/render/Hud.cpp index 23a796e6..9243bc3a 100644 --- a/src/render/Hud.cpp +++ b/src/render/Hud.cpp @@ -53,9 +53,9 @@ wchar *CHud::m_PagerMessage = (wchar*)0x878840; bool &CHud::m_Wants_To_Draw_Hud = *(bool*)0x95CD89; bool &CHud::m_Wants_To_Draw_3dMarkers = *(bool*)0x95CD62; wchar(*CHud::m_BigMessage)[128] = (wchar(*)[128])0x664CE0; -float *CHud::BigMessageInUse = (float*)0x862140; -float *CHud::BigMessageAlpha = (float*)0x862108; -float *CHud::BigMessageX = (float*)0x773248; +float CHud::BigMessageInUse[6]; +float CHud::BigMessageAlpha[6]; +float CHud::BigMessageX[6]; float &CHud::OddJob2OffTimer = *(float*)0x942FA0; int8 &CHud::CounterOnLastFrame = *(int8*)0x95CD67; @@ -231,8 +231,8 @@ void CHud::Draw() CRect rect; float fWidescreenOffset[2] = { 0.0f, 0.0f }; - - if (CMenuManager::m_PrefsUseWideScreen) { + + if (FrontEndMenuManager.m_PrefsUseWideScreen) { fWidescreenOffset[0] = 0.0f; fWidescreenOffset[1] = SCREEN_SCALE_Y(18.0f); } @@ -362,25 +362,32 @@ void CHud::Draw() /* DrawAmmo */ + int16 AmmoAmount = CWeaponInfo::GetWeaponInfo(FindPlayerPed()->GetWeapon()->m_eWeaponType)->m_nAmountofAmmunition; int32 AmmoInClip = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_nAmmoInClip; int32 TotalAmmo = CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_weapons[CWorld::Players[CWorld::PlayerInFocus].m_pPed->m_currentWeapon].m_nAmmoTotal; + int32 Ammo, Clip; - if (AmmoInClip <= 1 || AmmoInClip >= 1000) { + if (AmmoAmount <= 1 || AmmoAmount >= 1000) sprintf(sTemp, "%d", TotalAmmo); - } else { if (WeaponType == WEAPONTYPE_FLAMETHROWER) { - int tot_min_clip_div_10 = (TotalAmmo - AmmoInClip) / 10; - if (tot_min_clip_div_10 > 9999) - tot_min_clip_div_10 = 9999; + Clip = AmmoInClip / 10; - sprintf(sTemp, "%d-%d", tot_min_clip_div_10, AmmoInClip / 10); + if ((TotalAmmo - AmmoInClip) / 10 <= 9999) + Ammo = (TotalAmmo - AmmoInClip) / 10; + else + Ammo = 9999; } else { - if (AmmoInClip > 9999) - AmmoInClip = 9999; - sprintf(sTemp, "%d-%d", (TotalAmmo - AmmoInClip), AmmoInClip); + Clip = AmmoInClip; + + if (TotalAmmo - AmmoInClip > 9999) + Ammo = 9999; + else + Ammo = TotalAmmo - AmmoInClip; } + + sprintf(sTemp, "%d-%d", Ammo, Clip); } AsciiToUnicode(sTemp, sPrint); @@ -580,7 +587,7 @@ void CHud::Draw() CFont::SetPropOn(); CFont::SetBackgroundOff(); - if (CMenuManager::m_PrefsLanguage == 4) + if (FrontEndMenuManager.m_PrefsLanguage == 4) CFont::SetScale(SCREEN_SCALE_X(1.2f * 0.8f), SCREEN_SCALE_Y(1.2f)); else CFont::SetScale(SCREEN_SCALE_X(1.2f), SCREEN_SCALE_Y(1.2f)); @@ -676,7 +683,7 @@ void CHud::Draw() CFont::SetPropOn(); CFont::SetBackgroundOff(); - if (CMenuManager::m_PrefsLanguage != 3 && CMenuManager::m_PrefsLanguage != 4) + if (FrontEndMenuManager.m_PrefsLanguage != 3 && FrontEndMenuManager.m_PrefsLanguage != 4) CFont::SetScale(SCREEN_SCALE_X(1.2f), SCREEN_SCALE_Y(1.2f)); else CFont::SetScale(SCREEN_SCALE_X(1.2f * 0.85f), SCREEN_SCALE_Y(1.2f)); @@ -856,7 +863,11 @@ void CHud::Draw() */ if (CHud::m_ItemToFlash == ITEM_RADAR && CTimer::GetFrameCounter() & 8 || CHud::m_ItemToFlash != ITEM_RADAR) { CRadar::DrawMap(); - CHud::Sprites[HUD_RADARDISC].Draw(CRect(SCREEN_SCALE_X(16.0f), SCREEN_SCALE_FROM_BOTTOM(123.0f + 4.0f), SCREEN_SCALE_X(94.0f + 20.0f + 5.0f), SCREEN_SCALE_FROM_BOTTOM(-76.0f + 123.0f - 6.0f)), CRGBA(0, 0, 0, 255)); + CRect rect(0.0f, 0.0f, SCREEN_SCALE_X(RADAR_WIDTH), SCREEN_SCALE_Y(RADAR_HEIGHT)); + // FIX: game doesn't scale RADAR_LEFT here + rect.Translate(SCREEN_SCALE_X(RADAR_LEFT), SCREEN_SCALE_FROM_BOTTOM(RADAR_BOTTOM + RADAR_HEIGHT)); + rect.Grow(4.0f); + CHud::Sprites[HUD_RADARDISC].Draw(rect, CRGBA(0, 0, 0, 255)); CRadar::DrawBlips(); } } @@ -972,7 +983,7 @@ void CHud::Draw() DrawBigMessage */ // MissionCompleteFailedText - if (CHud::m_BigMessage[0][0]) { + if (m_BigMessage[0][0]) { if (BigMessageInUse[0] != 0.0f) { CFont::SetJustifyOff(); CFont::SetBackgroundOff(); @@ -1247,7 +1258,7 @@ void CHud::DrawAfterFade() CFont::SetJustifyOff(); CFont::SetBackgroundOff(); - if (CGame::frenchGame || CMenuManager::m_PrefsLanguage == 4) + if (CGame::frenchGame || FrontEndMenuManager.m_PrefsLanguage == 4) CFont::SetScale(SCREEN_SCALE_X(0.884f), SCREEN_SCALE_Y(1.36f)); else CFont::SetScale(SCREEN_SCALE_X(1.04f), SCREEN_SCALE_Y(1.6f)); diff --git a/src/render/Hud.h b/src/render/Hud.h index 8f4b6fb6..260e5312 100644 --- a/src/render/Hud.h +++ b/src/render/Hud.h @@ -62,9 +62,9 @@ public: static bool &m_Wants_To_Draw_Hud; static bool &m_Wants_To_Draw_3dMarkers; static wchar(*m_BigMessage)[128]; - static float *BigMessageInUse; - static float *BigMessageAlpha; - static float *BigMessageX; + static float BigMessageInUse[6]; + static float BigMessageAlpha[6]; + static float BigMessageX[6]; static float &OddJob2OffTimer; static int8 &CounterOnLastFrame; static float &OddJob2XOffset; diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp index a8abe1dd..814cac84 100644 --- a/src/skel/win/win.cpp +++ b/src/skel/win/win.cpp @@ -633,8 +633,10 @@ psInitialise(void) C_PcSave::SetSaveDirectory(_psGetUserFilesFolder()); - InitialiseLanguage(); - +#ifndef NASTY_GAME + InitialiseLanguage(); +#endif + FrontEndMenuManager.LoadSettings(); gGameState = GS_START_UP; diff --git a/src/skel/win/win.h b/src/skel/win/win.h index d3b0169f..371b9e44 100644 --- a/src/skel/win/win.h +++ b/src/skel/win/win.h @@ -78,6 +78,8 @@ RwBool _psSetVideoMode(RwInt32 subSystem, RwInt32 videoMode); void CenterVideo(void); void CloseClip(void); +RwChar **_psGetVideoModeList(); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/weapons/Weapon.h b/src/weapons/Weapon.h index 6009a549..aebcb2c6 100644 --- a/src/weapons/Weapon.h +++ b/src/weapons/Weapon.h @@ -18,7 +18,8 @@ enum eWeaponType WEAPONTYPE_DETONATOR, NUM_PED_WEAPONTYPES = 13, WEAPONTYPE_HELICANNON = 13, - NUM_WEAPONTYPES + NUM_WEAPONTYPES, + NO_STORED_WEAPON = 22 }; enum eWeaponFire {