From 750d3229a38cfa449ebb51e4936ca9b33e581695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?eray=20or=C3=A7unus?= Date: Thu, 27 Feb 2020 19:07:36 +0300 Subject: CPopulation 3 and fixes --- src/control/Script.cpp | 2 +- src/core/Frontend.cpp | 140 +++++++++++----------- src/core/Frontend.h | 8 -- src/objects/Object.cpp | 40 ++++++- src/objects/Object.h | 3 + src/peds/Ped.cpp | 19 ++- src/peds/Population.cpp | 305 ++++++++++++++++++++++++++++++++++++++++++++++-- src/peds/Population.h | 8 +- 8 files changed, 430 insertions(+), 95 deletions(-) diff --git a/src/control/Script.cpp b/src/control/Script.cpp index 3d8807d8..4ef3821e 100644 --- a/src/control/Script.cpp +++ b/src/control/Script.cpp @@ -3757,7 +3757,7 @@ int8 CRunningScript::ProcessCommands400To499(int32 command) CollectParameters(&m_nIp, 2); CPlayerInfo* pPlayer = &CWorld::Players[ScriptParams[0]]; if (ScriptParams[1]){ - if (CReplay::IsPlayingBack() || CTheScripts::DelayMakingPlayerUnsafeThisTime){ + if (CGame::playingIntro || CTheScripts::DelayMakingPlayerUnsafeThisTime){ CTheScripts::CountdownToMakePlayerUnsafe = 50; if (CTheScripts::DelayMakingPlayerUnsafeThisTime) CTheScripts::DelayMakingPlayerUnsafeThisTime--; diff --git a/src/core/Frontend.cpp b/src/core/Frontend.cpp index 751ca23f..d43f24d7 100644 --- a/src/core/Frontend.cpp +++ b/src/core/Frontend.cpp @@ -580,23 +580,26 @@ void CMenuManager::Draw() float usableLineHeight = lineHeight * 0.9f; // also height of biggest bar in slider float freeSpaceInLine = lineHeight * 0.1f; // also height of smallest bar in slider(weird) bool foundTheHoveringItem = false; + wchar unicodeTemp[64]; + for (int i = 0; i < NUM_MENUROWS; ++i) { if (aScreens[m_nCurrScreen].m_aEntries[i].m_Action != MENUACTION_LABEL && aScreens[m_nCurrScreen].m_aEntries[i].m_EntryName[0] != '\0') { - wchar *textToPrint[MENUCOLUMNS] = { nil, nil, nil }; + wchar *rightText = nil; + wchar *leftText; if (aScreens[m_nCurrScreen].m_aEntries[i].m_SaveSlot >= SAVESLOT_1 && aScreens[m_nCurrScreen].m_aEntries[i].m_SaveSlot <= SAVESLOT_8) { CFont::SetRightJustifyOff(); - textToPrint[MENUCOLUMN_LEFT] = GetNameOfSavedGame(i - 1); + leftText = GetNameOfSavedGame(i - 1); if (Slots[i] != SLOT_EMPTY) - textToPrint[MENUCOLUMN_RIGHT] = GetSavedGameDateAndTime(i - 1); + rightText = GetSavedGameDateAndTime(i - 1); - if (textToPrint[MENUCOLUMN_LEFT][0] == '\0') { + if (leftText[0] == '\0') { sprintf(gString, "FEM_SL%d", i); - textToPrint[MENUCOLUMN_LEFT] = TheText.Get(gString); + leftText = TheText.Get(gString); } } else { - textToPrint[MENUCOLUMN_LEFT] = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[i].m_EntryName); + leftText = TheText.Get(aScreens[m_nCurrScreen].m_aEntries[i].m_EntryName); } switch (aScreens[m_nCurrScreen].m_aEntries[i].m_Action) { @@ -605,28 +608,28 @@ void CMenuManager::Draw() case MENUPAGE_MULTIPLAYER_MAP: switch (sthWithButtons) { case 0: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEM_MA0"); + rightText = TheText.Get("FEM_MA0"); break; case 1: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEM_MA1"); + rightText = TheText.Get("FEM_MA1"); break; case 2: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEM_MA2"); + rightText = TheText.Get("FEM_MA2"); break; case 3: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEM_MA3"); + rightText = TheText.Get("FEM_MA3"); break; case 4: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEM_MA4"); + rightText = TheText.Get("FEM_MA4"); break; case 5: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEM_MA5"); + rightText = TheText.Get("FEM_MA5"); break; case 6: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEM_MA6"); + rightText = TheText.Get("FEM_MA6"); break; case 7: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEM_MA7"); + rightText = TheText.Get("FEM_MA7"); break; default: break; @@ -635,28 +638,28 @@ void CMenuManager::Draw() case MENUPAGE_MULTIPLAYER_MODE: switch (sthWithButtons2) { case 0: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEN_TY0"); + rightText = TheText.Get("FEN_TY0"); break; case 1: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEN_TY1"); + rightText = TheText.Get("FEN_TY1"); break; case 2: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEN_TY2"); + rightText = TheText.Get("FEN_TY2"); break; case 3: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEN_TY3"); + rightText = TheText.Get("FEN_TY3"); break; case 4: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEN_TY4"); + rightText = TheText.Get("FEN_TY4"); break; case 5: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEN_TY5"); + rightText = TheText.Get("FEN_TY5"); break; case 6: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEN_TY6"); + rightText = TheText.Get("FEN_TY6"); break; case 7: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEN_TY7"); + rightText = TheText.Get("FEN_TY7"); break; default: break; @@ -669,57 +672,57 @@ void CMenuManager::Draw() } case MENUACTION_CTRLVIBRATION: if (CMenuManager::m_PrefsUseVibration) - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEM_ON"); + rightText = TheText.Get("FEM_ON"); else - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEM_OFF"); + rightText = TheText.Get("FEM_OFF"); break; case MENUACTION_CTRLCONFIG: switch (CPad::GetPad(0)->Mode) { case 0: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEC_CF1"); + rightText = TheText.Get("FEC_CF1"); break; case 1: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEC_CF2"); + rightText = TheText.Get("FEC_CF2"); break; case 2: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEC_CF3"); + rightText = TheText.Get("FEC_CF3"); break; case 3: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEC_CF4"); + rightText = TheText.Get("FEC_CF4"); break; } break; case MENUACTION_CTRLDISPLAY: if (m_DisplayControllerOnFoot) - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEC_ONF"); + rightText = TheText.Get("FEC_ONF"); else - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEC_INC"); + rightText = TheText.Get("FEC_INC"); break; case MENUACTION_FRAMESYNC: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(m_PrefsVsyncDisp ? "FEM_ON" : "FEM_OFF"); + rightText = TheText.Get(m_PrefsVsyncDisp ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_FRAMELIMIT: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(m_PrefsFrameLimiter ? "FEM_ON" : "FEM_OFF"); + rightText = TheText.Get(m_PrefsFrameLimiter ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_TRAILS: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(CMBlur::BlurOn ? "FEM_ON" : "FEM_OFF"); + rightText = TheText.Get(CMBlur::BlurOn ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_SUBTITLES: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(m_PrefsShowSubtitles ? "FEM_ON" : "FEM_OFF"); + rightText = TheText.Get(m_PrefsShowSubtitles ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_WIDESCREEN: #ifndef ASPECT_RATIO_SCALE - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(m_PrefsUseWideScreen ? "FEM_ON" : "FEM_OFF"); + rightText = TheText.Get(m_PrefsUseWideScreen ? "FEM_ON" : "FEM_OFF"); #else switch (m_PrefsUseWideScreen) { case AR_AUTO: - textToPrint[MENUCOLUMN_RIGHT] = (wchar*)L"AUTO"; + rightText = (wchar*)L"AUTO"; break; case AR_4_3: - textToPrint[MENUCOLUMN_RIGHT] = (wchar*)L"4:3"; + rightText = (wchar*)L"4:3"; break; case AR_16_9: - textToPrint[MENUCOLUMN_RIGHT] = (wchar*)L"16:9"; + rightText = (wchar*)L"16:9"; break; } #endif @@ -729,42 +732,39 @@ void CMenuManager::Draw() break; sprintf(gString, "FEA_FM%d", m_PrefsRadioStation); - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(gString); + rightText = TheText.Get(gString); break; case MENUACTION_SETDBGFLAG: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(CTheScripts::DbgFlag ? "FEM_ON" : "FEM_OFF"); + rightText = TheText.Get(CTheScripts::DbgFlag ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_SWITCHBIGWHITEDEBUGLIGHT: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(gbBigWhiteDebugLightSwitchedOn ? "FEM_ON" : "FEM_OFF"); + rightText = TheText.Get(gbBigWhiteDebugLightSwitchedOn ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_PEDROADGROUPS: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(gbShowPedRoadGroups ? "FEM_ON" : "FEM_OFF"); + rightText = TheText.Get(gbShowPedRoadGroups ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_CARROADGROUPS: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(gbShowCarRoadGroups ? "FEM_ON" : "FEM_OFF"); + rightText = TheText.Get(gbShowCarRoadGroups ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_COLLISIONPOLYS: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(gbShowCollisionPolys ? "FEM_ON" : "FEM_OFF"); + rightText = TheText.Get(gbShowCollisionPolys ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_SHOWCULL: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(gbShowCullZoneDebugStuff ? "FEM_ON" : "FEM_OFF"); + rightText = TheText.Get(gbShowCullZoneDebugStuff ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_SHOWHEADBOB: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(TheCamera.m_bHeadBob ? "FEM_ON" : "FEM_OFF"); + rightText = TheText.Get(TheCamera.m_bHeadBob ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_INVVERT: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(MousePointerStateHelper.bInvertVertically ? "FEM_OFF" : "FEM_ON"); + rightText = TheText.Get(MousePointerStateHelper.bInvertVertically ? "FEM_OFF" : "FEM_ON"); break; - case MENUACTION_SCREENRES: { - RwChar* res = _psGetVideoModeList()[m_nDisplayVideoMode]; - wchar temp[32]; - AsciiToUnicode(res, temp); - textToPrint[MENUCOLUMN_RIGHT] = temp; + case MENUACTION_SCREENRES: + AsciiToUnicode(_psGetVideoModeList()[m_nDisplayVideoMode], unicodeTemp); + rightText = unicodeTemp; break; - } - case MENUACTION_AUDIOHW: { + case MENUACTION_AUDIOHW: if (m_nPrefsAudio3DProviderIndex == -1) - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEA_NAH"); + rightText = TheText.Get("FEA_NAH"); else { char *provider = DMAudio.Get3DProviderName(m_nPrefsAudio3DProviderIndex); @@ -773,25 +773,23 @@ void CMenuManager::Draw() } else if (!strcmp(strupr(provider), "DIRECTSOUND3D SOFTWARE EMULATION")) { strcpy(provider, "DSOUND3D SOFTWARE EMULATION"); } - wchar temp[64]; - AsciiToUnicode(provider, temp); - textToPrint[MENUCOLUMN_RIGHT] = temp; + AsciiToUnicode(provider, unicodeTemp); + rightText = unicodeTemp; } break; - } case MENUACTION_SPEAKERCONF: { if (m_nPrefsAudio3DProviderIndex == -1) - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEA_NAH"); + rightText = TheText.Get("FEA_NAH"); else { switch (m_PrefsSpeakers) { case 0: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEA_2SP"); + rightText = TheText.Get("FEA_2SP"); break; case 1: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEA_EAR"); + rightText = TheText.Get("FEA_EAR"); break; case 2: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get("FEA_4SP"); + rightText = TheText.Get("FEA_4SP"); break; } } @@ -800,19 +798,19 @@ void CMenuManager::Draw() case MENUACTION_CTRLMETHOD: { switch (m_ControlMethod) { case 0: - textToPrint[MENUCOLUMN_LEFT] = TheText.Get("FET_SCN"); + leftText = TheText.Get("FET_SCN"); break; case 1: - textToPrint[MENUCOLUMN_LEFT] = TheText.Get("FET_CCN"); + leftText = TheText.Get("FET_CCN"); break; } break; } case MENUACTION_DYNAMICACOUSTIC: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(m_PrefsDMA ? "FEM_ON" : "FEM_OFF"); + rightText = TheText.Get(m_PrefsDMA ? "FEM_ON" : "FEM_OFF"); break; case MENUACTION_MOUSESTEER: - textToPrint[MENUCOLUMN_RIGHT] = TheText.Get(m_bDisableMouseSteering ? "FEM_OFF" : "FEM_ON"); + rightText = TheText.Get(m_bDisableMouseSteering ? "FEM_OFF" : "FEM_ON"); break; } @@ -885,8 +883,8 @@ void CMenuManager::Draw() float itemY = MENU_Y(textLayer + nextItemY); float itemX = MENU_X_LEFT_ALIGNED(textLayer + columnWidth); - CFont::PrintString(itemX, itemY, textToPrint[MENUCOLUMN_LEFT]); - if (textToPrint[MENUCOLUMN_RIGHT]) { + CFont::PrintString(itemX, itemY, leftText); + if (rightText) { if (!CFont::Details.centre) CFont::SetRightJustifyOn(); @@ -894,7 +892,7 @@ void CMenuManager::Draw() && !m_bGameNotLoaded && textLayer == 1) { CFont::SetColor(CRGBA(155, 117, 6, FadeIn(255))); } - CFont::PrintString(MENU_X_RIGHT_ALIGNED(columnWidth - textLayer), itemY, textToPrint[MENUCOLUMN_RIGHT]); + CFont::PrintString(MENU_X_RIGHT_ALIGNED(columnWidth - textLayer), itemY, rightText); } if (i == m_nCurrOption && itemsAreSelectable) { CFont::SetColor(CRGBA(255, 217, 106, FadeIn(255))); @@ -1017,7 +1015,7 @@ void CMenuManager::Draw() } } - nextYToUse += lineHeight * CFont::GetNumberLines(menuXYpadding, nextYToUse, textToPrint[MENUCOLUMN_LEFT]); + nextYToUse += lineHeight * CFont::GetNumberLines(menuXYpadding, nextYToUse, leftText); // Radio icons. // TO-DO: This is missing/broken diff --git a/src/core/Frontend.h b/src/core/Frontend.h index 32338933..18a324ed 100644 --- a/src/core/Frontend.h +++ b/src/core/Frontend.h @@ -340,14 +340,6 @@ enum eCheckHover HOVEROPTION_42, }; -enum eMenuColumns -{ - MENUCOLUMN_LEFT, - MENUCOLUMN_CENTER, - MENUCOLUMN_RIGHT, - MENUCOLUMNS, -}; - enum { NUM_MENUROWS = 18, diff --git a/src/objects/Object.cpp b/src/objects/Object.cpp index de98a2d6..357d67d7 100644 --- a/src/objects/Object.cpp +++ b/src/objects/Object.cpp @@ -55,6 +55,21 @@ CObject::CObject(int32 mi, bool createRW) Init(); } +CObject::CObject(CDummyObject *dummy) +{ + SetModelIndexNoCreate(dummy->m_modelIndex); + + if (dummy->m_rwObject) + AttachToRwObject(dummy->m_rwObject); + else + GetMatrix() = dummy->GetMatrix(); + + m_objectMatrix = dummy->GetMatrix(); + dummy->DetachFromRwObject(); + Init(); + m_level = dummy->m_level; +} + CObject::~CObject(void) { CRadar::ClearBlipForEntity(BLIP_OBJECT, CPools::GetObjectPool()->GetIndex(this)); @@ -109,14 +124,37 @@ CObject::RefModelInfo(int32 modelId) CModelInfo::GetModelInfo(modelId)->AddRef(); } +bool +CObject::CanBeDeleted(void) +{ + switch (ObjectCreatedBy) { + case GAME_OBJECT: + return true; + case MISSION_OBJECT: + return false; + case TEMP_OBJECT: + return true; + case CUTSCENE_OBJECT: + return false; + default: + return true; + } +} + class CObject_ : public CObject { public: - void dtor(void) { this->CObject::~CObject(); } + CObject *ctor(void) { return ::new (this) CObject(); } + CObject *ctor(int32 mi, bool createRW) { return ::new (this) CObject(mi, createRW); } + CObject *ctor(CDummyObject *dummy) { return ::new (this) CObject(dummy); } + void dtor(void) { CObject::~CObject(); } void Render_(void) { CObject::Render(); } }; STARTPATCHES + InjectHook(0x4BABD0, (CObject* (CObject::*)(void)) &CObject_::ctor, PATCH_JUMP); + InjectHook(0x4BACE0, (CObject* (CObject::*)(int32, bool)) &CObject_::ctor, PATCH_JUMP); + InjectHook(0x4BAD50, (CObject* (CObject::*)(CDummyObject*)) &CObject_::ctor, PATCH_JUMP); InjectHook(0x4BAE00, &CObject_::dtor, PATCH_JUMP); InjectHook(0x4BB1E0, &CObject_::Render_, PATCH_JUMP); ENDPATCHES diff --git a/src/objects/Object.h b/src/objects/Object.h index c07bb233..1c33b07f 100644 --- a/src/objects/Object.h +++ b/src/objects/Object.h @@ -1,6 +1,7 @@ #pragma once #include "Physical.h" +#include "DummyObject.h" enum { GAME_OBJECT = 1, @@ -69,6 +70,7 @@ public: CObject(void); CObject(int32, bool); + CObject(CDummyObject*); ~CObject(void); void ProcessControl(void); @@ -80,6 +82,7 @@ public: void ObjectDamage(float amount); void RefModelInfo(int32 modelId); void Init(void); + bool CanBeDeleted(void); static void DeleteAllTempObjectInArea(CVector, float); }; diff --git a/src/peds/Ped.cpp b/src/peds/Ped.cpp index 1ea13cc2..3e9bfc51 100644 --- a/src/peds/Ped.cpp +++ b/src/peds/Ped.cpp @@ -1519,7 +1519,7 @@ CPed::BeingDraggedFromCar(void) #ifdef VC_PED_PORTS if (m_objective == OBJECTIVE_LEAVE_CAR_AND_DIE) { if (m_pMyVehicle) { - m_pMyVehicle->ProcessOpenDoor(m_vehEnterType, NUM_ANIMS, m_pVehicleAnim->currentTime); + m_pMyVehicle->ProcessOpenDoor(m_vehEnterType, NUM_ANIMS, m_pVehicleAnim->currentTime * 5.0f); } } #endif @@ -2954,6 +2954,11 @@ CPed::QuitEnteringCar(void) if (veh->m_nNumGettingIn != 0) veh->m_nNumGettingIn--; +#ifdef VC_PED_PORTS + if (m_objective == OBJECTIVE_ENTER_CAR_AS_DRIVER || m_objective == OBJECTIVE_ENTER_CAR_AS_PASSENGER) + RestorePreviousObjective(); +#endif + veh->m_nGettingInFlags &= ~GetCarDoorFlag(m_vehEnterType); } @@ -2965,7 +2970,7 @@ CPed::QuitEnteringCar(void) animAssoc = m_pVehicleAnim; if (animAssoc) { animAssoc->blendDelta = -4.0f; - animAssoc->flags |= ASSOC_FADEOUTWHENDONE; + animAssoc->flags |= ASSOC_DELETEFADEDOUT; animAssoc = m_pVehicleAnim; animAssoc->flags &= ~ASSOC_RUNNING; } @@ -4161,7 +4166,10 @@ CPed::ClearObjective(void) { if (IsPedInControl() || m_nPedState == PED_DRIVING) { m_objective = OBJECTIVE_NONE; - +#ifdef VC_PED_PORTS + m_pedInObjective = nil; + m_carInObjective = nil; +#endif if (m_nPedState == PED_DRIVING && m_pMyVehicle) { if (m_pMyVehicle->pDriver != this) { @@ -13603,7 +13611,10 @@ CPed::ProcessObjective(void) if (InVehicle()) { if (m_nPedState != PED_EXIT_CAR && m_nPedState != PED_DRAG_FROM_CAR && m_nPedState != PED_EXIT_TRAIN && (m_nPedType != PEDTYPE_COP - || m_pMyVehicle->m_vecMoveSpeed.MagnitudeSqr2D() < 0.000025f)) { +#ifdef VC_PED_PORTS + || m_pMyVehicle->IsBoat() +#endif + || m_pMyVehicle->m_vecMoveSpeed.MagnitudeSqr2D() < sq(0.005f))) { if (m_pMyVehicle->IsTrain()) SetExitTrain(m_pMyVehicle); #ifdef VC_PED_PORTS diff --git a/src/peds/Population.cpp b/src/peds/Population.cpp index a2dd5c38..6ffb65ba 100644 --- a/src/peds/Population.cpp +++ b/src/peds/Population.cpp @@ -17,6 +17,7 @@ #include "VisibilityPlugins.h" #include "PedPlacement.h" #include "DummyObject.h" +#include "Script.h" #define CREATION_DIST_MULT_TO_DIST 40.0f #define CREATION_RANGE 10.0f // Being added over the CREATION_DIST_MULT_TO_DIST. @@ -56,10 +57,8 @@ CVector &CPopulation::RegenerationPoint_a = *(CVector*)0x8E2AA4; CVector &CPopulation::RegenerationPoint_b = *(CVector*)0x8E2A98; CVector &CPopulation::RegenerationForward = *(CVector*)0x8F1AD4; -WRAPPER CPed *CPopulation::AddPedInCar(CVehicle *vehicle) { EAXJMP(0x4F5800); } WRAPPER void CPopulation::ManagePopulation(void) { EAXJMP(0x4F3B90); } -WRAPPER void CPopulation::MoveCarsAndPedsOutOfAbandonedZones(void) { EAXJMP(0x4F5BE0); } -WRAPPER void CPopulation::ConvertToRealObject(CDummyObject* obj) { EAXJMP(0x4F45A0); } +WRAPPER bool CPopulation::TestSafeForRealObject(CDummyObject*) { EAXJMP(0x4F4700); } void CPopulation::Initialise() @@ -470,13 +469,13 @@ CPopulation::PedCreationDistMultiplier() } CPed* -CPopulation::AddPed(ePedType pedType, uint32 mi, CVector const &coors) +CPopulation::AddPed(ePedType pedType, uint32 miOrCopType, CVector const &coors) { switch (pedType) { case PEDTYPE_CIVMALE: case PEDTYPE_CIVFEMALE: { - CCivilianPed *ped = new CCivilianPed(pedType, mi); + CCivilianPed *ped = new CCivilianPed(pedType, miOrCopType); ped->GetPosition() = coors; ped->SetOrientation(0.0f, 0.0f, 0.0f); CWorld::Add(ped); @@ -490,7 +489,7 @@ CPopulation::AddPed(ePedType pedType, uint32 mi, CVector const &coors) } case PEDTYPE_COP: { - CCopPed *ped = new CCopPed((eCopType)mi); + CCopPed *ped = new CCopPed((eCopType)miOrCopType); ped->GetPosition() = coors; ped->SetOrientation(0.0f, 0.0f, 0.0f); CWorld::Add(ped); @@ -506,7 +505,7 @@ CPopulation::AddPed(ePedType pedType, uint32 mi, CVector const &coors) case PEDTYPE_GANG8: case PEDTYPE_GANG9: { - CCivilianPed *ped = new CCivilianPed(pedType, mi); + CCivilianPed *ped = new CCivilianPed(pedType, miOrCopType); ped->GetPosition() = coors; ped->SetOrientation(0.0f, 0.0f, 0.0f); CWorld::Add(ped); @@ -538,7 +537,7 @@ CPopulation::AddPed(ePedType pedType, uint32 mi, CVector const &coors) case PEDTYPE_CRIMINAL: case PEDTYPE_PROSTITUTE: { - CCivilianPed *ped = new CCivilianPed(pedType, mi); + CCivilianPed *ped = new CCivilianPed(pedType, miOrCopType); ped->GetPosition() = coors; ped->SetOrientation(0.0f, 0.0f, 0.0f); CWorld::Add(ped); @@ -721,6 +720,292 @@ CPopulation::AddToPopulation(float minDist, float maxDist, float minDistOffScree } } +CPed* +CPopulation::AddPedInCar(CVehicle* car) +{ + int defaultModel = MI_MALE01; + bool imSureThatModelIsLoaded = true; + CVector coors = FindPlayerCoors(); + CZoneInfo zoneInfo; + int pedType; + + // May be eCopType, model index or non-sense(for medic), AddPed knows that by looking to ped type. + int preferredModel; + + CTheZones::GetZoneInfoForTimeOfDay(&coors, &zoneInfo); + switch (car->m_modelIndex) { + case MI_FIRETRUCK: + preferredModel = 0; + pedType = PEDTYPE_FIREMAN; + break; + case MI_AMBULAN: + preferredModel = 0; + pedType = PEDTYPE_EMERGENCY; + break; + case MI_FBICAR: + preferredModel = COP_FBI; + pedType = PEDTYPE_COP; + break; + case MI_POLICE: + preferredModel = COP_STREET; + pedType = PEDTYPE_COP; + break; + case MI_ENFORCER: + preferredModel = COP_SWAT; + pedType = PEDTYPE_COP; + break; + case MI_RHINO: + case MI_BARRACKS: + preferredModel = COP_ARMY; + pedType = PEDTYPE_COP; + break; + case MI_TAXI: + case MI_CABBIE: + case MI_BORGNINE: + if (CGeneral::GetRandomTrueFalse()) { + pedType = PEDTYPE_CIVMALE; + preferredModel = MI_TAXI_D; + break; + } + defaultModel = MI_TAXI_D; + + // fall through + default: + int gangOfPed = GANG_MAFIA; + imSureThatModelIsLoaded = false; + + while (gangOfPed < NUM_GANGS && CGangs::GetGangInfo(gangOfPed)->m_nVehicleMI != car->m_modelIndex) + gangOfPed++; + + if (gangOfPed < NUM_GANGS) { + pedType = gangOfPed + PEDTYPE_GANG1; + preferredModel = ChooseGangOccupation(gangOfPed); + } else if (gangOfPed == NUM_GANGS) { + CVehicleModelInfo *carModelInfo = ((CVehicleModelInfo*)CModelInfo::GetModelInfo(car->m_modelIndex)); + int i = 15; + for(; i >= 0; i--) { + // Should return random model each time + preferredModel = ChooseCivilianOccupation(zoneInfo.pedGroup); + if (preferredModel == -1) + preferredModel = defaultModel; + + if (((CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel))->m_carsCanDrive & (1 << carModelInfo->m_vehicleClass)) + break; + } + if (i == -1) + preferredModel = defaultModel; + + pedType = ((CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel))->m_pedType; + } + break; + } + if (!imSureThatModelIsLoaded && !((CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel))->GetRwObject()) { + preferredModel = defaultModel; + pedType = ((CPedModelInfo*)CModelInfo::GetModelInfo(defaultModel))->m_pedType; + } + + CPed *newPed = CPopulation::AddPed((ePedType)pedType, preferredModel, car->GetPosition()); + newPed->bUsesCollision = false; + + // what?? + if (pedType != PEDTYPE_COP) { + newPed->SetCurrentWeapon(WEAPONTYPE_COLT45); + newPed->RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(newPed->GetWeapon()->m_eWeaponType)->m_nModelId); + } + /* + // Miami leftover + if (car->m_vehType == VEHICLE_TYPE_BIKE) { + newPed->m_pVehicleAnim = CAnimManager::BlendAnimation(newPed->GetClump(), ASSOCGRP_STD, *((CBike*)car + 308h), 100.0f); + } else */ + + // FIX: Make peds comfortable while driving car/boat +#ifdef FIX_BUGS + if (car->IsBoat()) { + newPed->m_pVehicleAnim = CAnimManager::BlendAnimation(newPed->GetClump(), ASSOCGRP_STD, ANIM_DRIVE_BOAT, 100.0f); + } else if (car->IsVehicle() && ((CVehicle*)car)->bLowVehicle) { + newPed->m_pVehicleAnim = CAnimManager::BlendAnimation(newPed->GetClump(), ASSOCGRP_STD, ANIM_CAR_LSIT, 100.0f); + } else +#endif + { + newPed->m_pVehicleAnim = CAnimManager::BlendAnimation(newPed->GetClump(), ASSOCGRP_STD, ANIM_CAR_SIT, 100.0f); + } + + newPed->StopNonPartialAnims(); + return newPed; +} + +void +CPopulation::MoveCarsAndPedsOutOfAbandonedZones() +{ + eLevelName level; + int zone; + int frame = CTimer::GetFrameCounter() & 7; + if (frame == 1) { + int movedVehicleCount = 0; + int poolSize = CPools::GetVehiclePool()->GetSize(); + for (int poolIndex = poolSize - 1; poolIndex >= 0; poolIndex--) { + + CVehicle* veh = CPools::GetVehiclePool()->GetSlot(poolIndex); + if (veh && veh->m_nZoneLevel == LEVEL_NONE && veh->IsCar()) { + + if(veh->m_status != STATUS_ABANDONED && veh->m_status != STATUS_WRECKED && veh->m_status != STATUS_PLAYER && + veh->m_status != STATUS_PLAYER_REMOTE) { + + CVector vehPos(veh->GetPosition()); + CPopulation::FindCollisionZoneForCoors(&vehPos, &zone, &level); + + // Level 0 is transition zones, and we don't wanna touch cars on transition zones. + if (level != LEVEL_NONE && level != CCollision::ms_collisionInMemory && vehPos.z > -4.0f) { + if (veh->bIsLocked || !veh->CanBeDeleted()) { + switch (movedVehicleCount & 3) { + case 0: + veh->GetPosition() = RegenerationPoint_a; + break; + case 1: + veh->GetPosition() = RegenerationPoint_b; + break; + case 2: + veh->GetPosition() = CVector(RegenerationPoint_a.x, RegenerationPoint_b.y, RegenerationPoint_a.z); + break; + case 3: + veh->GetPosition() = CVector(RegenerationPoint_b.x, RegenerationPoint_a.y, RegenerationPoint_a.z); + break; + default: + break; + } + veh->GetPosition().z += (movedVehicleCount / 4) * 7.0f; + veh->GetForward() = RegenerationForward; + ((CAutomobile*)veh)->PlaceOnRoadProperly(); + CCarCtrl::JoinCarWithRoadSystem(veh); + CTheScripts::ClearSpaceForMissionEntity(veh->GetPosition(), veh); + ++movedVehicleCount; + } else { + CWorld::Remove(veh); + delete veh; + } + } + } + } + } + } else if (frame == 5) { + int poolSize = CPools::GetPedPool()->GetSize(); + for (int poolIndex = poolSize - 1; poolIndex >= 0; poolIndex--) { + + CPed *ped = CPools::GetPedPool()->GetSlot(poolIndex); + if (ped && ped->m_nZoneLevel == LEVEL_NONE && !ped->bInVehicle) { + + CVector pedPos(ped->GetPosition()); + CPopulation::FindCollisionZoneForCoors(&pedPos, &zone, &level); + + // Level 0 is transition zones, and we don't wanna touch peds on transition zones. + if (level != LEVEL_NONE && level != CCollision::ms_collisionInMemory && pedPos.z > -4.0f) { + if (ped->CanBeDeleted()) { + CWorld::Remove(ped); + delete ped; + } else if (ped->m_nPedType != PEDTYPE_PLAYER1 && ped->m_nPedType != PEDTYPE_PLAYER2) { + ped->GetPosition() = RegenerationPoint_a; + + bool foundGround; + float groundZ = CWorld::FindGroundZFor3DCoord(ped->GetPosition().x, ped->GetPosition().y, + ped->GetPosition().z + 2.0f, &foundGround); + + if (foundGround) { + ped->GetPosition().z = 1.0f + groundZ; + //ped->GetPosition().z += 0.0f; + CTheScripts::ClearSpaceForMissionEntity(ped->GetPosition(), ped); + } + } + } + } + } + } +} + +void +CPopulation::ConvertAllObjectsToDummyObjects() +{ + int poolSize = CPools::GetObjectPool()->GetSize(); + for (int poolIndex = poolSize - 1; poolIndex >= 0; poolIndex--) { + + CObject *obj = CPools::GetObjectPool()->GetSlot(poolIndex); + + if (obj) { + if (obj->CanBeDeleted()) + ConvertToDummyObject(obj); + } + } +} + +void +CPopulation::ConvertToRealObject(CDummyObject *dummy) +{ + if (!TestSafeForRealObject(dummy)) + return; + + CObject *obj = new CObject(dummy); + if (!obj) + return; + + bool makeInvisible; + CWorld::Remove(dummy); + delete dummy; + CWorld::Add(obj); + int16 mi = obj->m_modelIndex; + if (mi == MI_GLASS1 || mi == MI_GLASS2 || mi == MI_GLASS3 || mi == MI_GLASS4 || + mi == MI_GLASS5 || mi == MI_GLASS6 || mi == MI_GLASS7 || mi == MI_GLASS8) + makeInvisible = true; + else + makeInvisible = false; + + if (makeInvisible) { + obj->bIsVisible = false; + } else if (obj->m_modelIndex == MI_BUOY) { + obj->bIsStatic = false; + obj->m_vecMoveSpeed = CVector(0.0f, 0.0f, -0.001f); + obj->m_flagD8 = true; + obj->AddToMovingList(); + } +} + +void +CPopulation::ConvertToDummyObject(CObject *obj) +{ + CDummyObject *dummy = new CDummyObject(obj); + if (!dummy) + return; + + dummy->GetMatrix() = obj->GetMatrix(); + dummy->GetMatrix().UpdateRW(); + dummy->UpdateRwFrame(); + + bool makeInvisible; + int16 mi = obj->m_modelIndex; + if (mi == MI_GLASS1 || mi == MI_GLASS2 || mi == MI_GLASS3 || mi == MI_GLASS4 || + mi == MI_GLASS5 || mi == MI_GLASS6 || mi == MI_GLASS7 || mi == MI_GLASS8) + makeInvisible = true; + else + makeInvisible = false; + + if (makeInvisible) { + dummy->bIsVisible = false; + } + + CWorld::Remove(obj); + delete obj; + CWorld::Add(dummy); +} + +bool +CPopulation::TestRoomForDummyObject(CObject *obj) +{ + int16 collidingObjs; + CWorld::FindObjectsKindaColliding(obj->GetPosition(), + CModelInfo::GetModelInfo(obj->m_modelIndex)->GetColModel()->boundingSphere.radius, + false, &collidingObjs, 2, nil, false, true, true, false, false); + + return collidingObjs == 0; +} + STARTPATCHES InjectHook(0x4F3770, &CPopulation::Initialise, PATCH_JUMP); InjectHook(0x4F5780, &CPopulation::ChooseGangOccupation, PATCH_JUMP); @@ -728,4 +1013,8 @@ STARTPATCHES InjectHook(0x4F6010, &CPopulation::FindCollisionZoneForCoors, PATCH_JUMP); InjectHook(0x4F6410, &CPopulation::PedCreationDistMultiplier, PATCH_JUMP); InjectHook(0x4F5280, &CPopulation::AddPed, PATCH_JUMP); + InjectHook(0x4F4470, &CPopulation::ConvertToRealObject, PATCH_JUMP); + InjectHook(0x4F4690, &CPopulation::TestRoomForDummyObject, PATCH_JUMP); + InjectHook(0x4F45A0, &CPopulation::ConvertToDummyObject, PATCH_JUMP); + InjectHook(0x4F4410, &CPopulation::ConvertAllObjectsToDummyObjects, PATCH_JUMP); ENDPATCHES \ No newline at end of file diff --git a/src/peds/Population.h b/src/peds/Population.h index f22926a0..152fb7ae 100644 --- a/src/peds/Population.h +++ b/src/peds/Population.h @@ -67,7 +67,7 @@ public: static void LoadPedGroups(); static void UpdatePedCount(ePedType, bool); static void DealWithZoneChange(eLevelName oldLevel, eLevelName newLevel, bool); - static CPed *AddPedInCar(CVehicle *vehicle); + static CPed *AddPedInCar(CVehicle *car); static bool IsPointInSafeZone(CVector *coors); static void RemovePed(CPed *ent); static int32 ChooseCivilianOccupation(int32); @@ -81,5 +81,9 @@ public: static void AddToPopulation(float, float, float, float); static void ManagePopulation(void); static void MoveCarsAndPedsOutOfAbandonedZones(void); - static void ConvertToRealObject(CDummyObject* obj); + static void ConvertToRealObject(CDummyObject*); + static void ConvertToDummyObject(CObject*); + static void ConvertAllObjectsToDummyObjects(void); + static bool TestRoomForDummyObject(CObject*); + static bool TestSafeForRealObject(CDummyObject*); }; -- cgit v1.2.3 From ae523a09a7c12ff3c1fd9cb61e741cf09702b3fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?eray=20or=C3=A7unus?= Date: Sun, 1 Mar 2020 04:43:30 +0300 Subject: fixes --- src/control/Script.cpp | 8 ++++++++ src/peds/Ped.cpp | 23 +++++++++++++++++++++-- src/peds/PlayerPed.cpp | 6 +++++- src/peds/Population.cpp | 11 +++++------ src/vehicles/Vehicle.h | 4 +++- 5 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/control/Script.cpp b/src/control/Script.cpp index 4ef3821e..5daf12f5 100644 --- a/src/control/Script.cpp +++ b/src/control/Script.cpp @@ -2815,7 +2815,11 @@ int8 CRunningScript::ProcessCommands200To299(int32 command) pVehicle->AutoPilot.m_nCarMission = MISSION_CRUISE; pVehicle->bEngineOn = true; pPed->bUsesCollision = false; +#ifdef FIX_BUGS + AnimationId anim = pVehicle->GetDriverAnim(); +#else AnimationId anim = pVehicle->bLowVehicle ? ANIM_CAR_LSIT : ANIM_CAR_SIT; +#endif pPed->m_pVehicleAnim = CAnimManager::BlendAnimation(pPed->GetClump(), ASSOCGRP_STD, anim, 100.0f); pPed->StopNonPartialAnims(); pPed->m_nZoneLevel = CTheZones::GetLevelFromPosition(pPed->GetPosition()); @@ -4009,7 +4013,11 @@ int8 CRunningScript::ProcessCommands400To499(int32 command) pPed->SetPedState(PED_DRIVING); pVehicle->m_status = STATUS_PHYSICS; pPed->bUsesCollision = false; +#ifdef FIX_BUGS + AnimationId anim = pVehicle->GetDriverAnim(); +#else AnimationId anim = pVehicle->bLowVehicle ? ANIM_CAR_LSIT : ANIM_CAR_SIT; +#endif pPed->m_pVehicleAnim = CAnimManager::BlendAnimation(pPed->GetClump(), ASSOCGRP_STD, anim, 100.0f); pPed->StopNonPartialAnims(); pPed->m_nZoneLevel = CTheZones::GetLevelFromPosition(pPed->GetPosition()); diff --git a/src/peds/Ped.cpp b/src/peds/Ped.cpp index 3e9bfc51..8803b5ec 100644 --- a/src/peds/Ped.cpp +++ b/src/peds/Ped.cpp @@ -237,7 +237,8 @@ static char PersonalityTypeText[][18] = { "Geek Girl", "Old Girl", "Tough Girl", - "Tramp", + "Tramp Male", + "Tramp Female", "Tourist", "Prostitute", "Criminal", @@ -246,6 +247,8 @@ static char PersonalityTypeText[][18] = { "Psycho", "Steward", "Sports Fan", + "Shopper", + "Old Shopper" }; static char WaitStateText[][16] = { @@ -16662,6 +16665,14 @@ CPed::WarpPedIntoCar(CVehicle *car) car->bEngineOn = true; DMAudio.PlayOneShot(car->m_audioEntityId, SOUND_CAR_ENGINE_START, 1.0f); } + +#ifdef VC_PED_PORTS + RpAnimBlendClumpSetBlendDeltas(GetClump(), ASSOC_PARTIAL, -1000.0f); + + // VC uses AddInCarAnims but we don't have that + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, car->GetDriverAnim(), 100.0f); + RemoveWeaponWhenEnteringVehicle(); +#else if (car->IsBoat()) { m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_DRIVE_BOAT, 100.0f); CWeaponInfo *ourWeapon = CWeaponInfo::GetWeaponInfo(GetWeapon()->m_eWeaponType); @@ -16675,6 +16686,8 @@ CPed::WarpPedIntoCar(CVehicle *car) else m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_CAR_SIT, 100.0f); } +#endif + StopNonPartialAnims(); if (car->bIsBus) bRenderPedInCar = false; @@ -16904,11 +16917,17 @@ CPed::SetEnterCar_AllClear(CVehicle *car, uint32 doorNode, uint32 doorFlag) m_vecOffsetSeek = doorOpenPos - GetPosition(); m_nPedStateTimer = CTimer::GetTimeInMilliseconds() + 600; if (car->IsBoat()) { - m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_DRIVE_BOAT, 100.0f); #ifdef VC_PED_PORTS + // VC checks for handling flag, but we can't do that + if(car->GetModelIndex() == MI_SPEEDER) + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_CAR_SIT, 100.0f); + else + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_DRIVE_BOAT, 100.0f); + PedSetInCarCB(nil, this); m_ped_flagI4 = true; #else + m_pVehicleAnim = CAnimManager::BlendAnimation(GetClump(), ASSOCGRP_STD, ANIM_DRIVE_BOAT, 100.0f); m_pVehicleAnim->SetFinishCallback(PedSetInCarCB, this); #endif if (IsPlayer()) diff --git a/src/peds/PlayerPed.cpp b/src/peds/PlayerPed.cpp index 9d4adcef..b459ed84 100644 --- a/src/peds/PlayerPed.cpp +++ b/src/peds/PlayerPed.cpp @@ -79,7 +79,11 @@ void CPlayerPed::MakeObjectTargettable(int32 handle) { for (int i = 0; i < ARRAY_SIZE(m_nTargettableObjects); i++) { - if (CPools::GetObjectPool()->GetAt(m_nTargettableObjects[i]) == nil) { + if ( +#ifdef FIX_BUGS + m_nTargettableObjects[i] == -1 || +#endif + CPools::GetObjectPool()->GetAt(m_nTargettableObjects[i]) == nil) { m_nTargettableObjects[i] = handle; return; } diff --git a/src/peds/Population.cpp b/src/peds/Population.cpp index 6ffb65ba..6b15d8c7 100644 --- a/src/peds/Population.cpp +++ b/src/peds/Population.cpp @@ -820,15 +820,14 @@ CPopulation::AddPedInCar(CVehicle* car) // FIX: Make peds comfortable while driving car/boat #ifdef FIX_BUGS - if (car->IsBoat()) { - newPed->m_pVehicleAnim = CAnimManager::BlendAnimation(newPed->GetClump(), ASSOCGRP_STD, ANIM_DRIVE_BOAT, 100.0f); - } else if (car->IsVehicle() && ((CVehicle*)car)->bLowVehicle) { - newPed->m_pVehicleAnim = CAnimManager::BlendAnimation(newPed->GetClump(), ASSOCGRP_STD, ANIM_CAR_LSIT, 100.0f); - } else -#endif + { + newPed->m_pVehicleAnim = CAnimManager::BlendAnimation(newPed->GetClump(), ASSOCGRP_STD, car->GetDriverAnim(), 100.0f); + } +#else { newPed->m_pVehicleAnim = CAnimManager::BlendAnimation(newPed->GetClump(), ASSOCGRP_STD, ANIM_CAR_SIT, 100.0f); } +#endif newPed->StopNonPartialAnims(); return newPed; diff --git a/src/vehicles/Vehicle.h b/src/vehicles/Vehicle.h index 7e99258c..bd8df694 100644 --- a/src/vehicles/Vehicle.h +++ b/src/vehicles/Vehicle.h @@ -3,6 +3,7 @@ #include "Physical.h" #include "AutoPilot.h" #include "ModelIndices.h" +#include "AnimManager.h" class CPed; class CFire; @@ -269,7 +270,8 @@ public: bool IsAlarmOn(void) { return m_nAlarmState != 0 && m_nAlarmState != -1; } CVehicleModelInfo* GetModelInfo() { return (CVehicleModelInfo*)CModelInfo::GetModelInfo(GetModelIndex()); } bool IsTaxi(void) { return GetModelIndex() == MI_TAXI || GetModelIndex() == MI_CABBIE || GetModelIndex() == MI_BORGNINE; } - + AnimationId GetDriverAnim(void) { return IsCar() && bLowVehicle ? ANIM_CAR_LSIT : (IsBoat() && GetModelIndex() != MI_SPEEDER ? ANIM_DRIVE_BOAT : ANIM_CAR_SIT); } + static bool &bWheelsOnlyCheat; static bool &bAllDodosCheat; static bool &bCheat3; -- cgit v1.2.3