diff options
author | Nikolay Korolev <nickvnuk@gmail.com> | 2020-02-15 14:53:42 +0300 |
---|---|---|
committer | Nikolay Korolev <nickvnuk@gmail.com> | 2020-02-15 14:53:42 +0300 |
commit | 043efaf082a8ce5010e6ff974afea05d8d66e9d1 (patch) | |
tree | 40115f81c6a4b4756743a36485eacd4b7a5607b9 /src | |
parent | 7547f7ae973a435b05b09155d3800c52e927bc44 (diff) |
script 900-999
Diffstat (limited to 'src')
-rw-r--r-- | src/control/Garages.cpp | 33 | ||||
-rw-r--r-- | src/control/Garages.h | 21 | ||||
-rw-r--r-- | src/control/Script.cpp | 559 | ||||
-rw-r--r-- | src/control/Script.h | 6 | ||||
-rw-r--r-- | src/core/Stats.cpp | 6 | ||||
-rw-r--r-- | src/core/Stats.h | 2 | ||||
-rw-r--r-- | src/core/World.cpp | 1 | ||||
-rw-r--r-- | src/core/World.h | 1 | ||||
-rw-r--r-- | src/objects/ParticleObject.h | 2 | ||||
-rw-r--r-- | src/peds/PedRoutes.cpp | 25 | ||||
-rw-r--r-- | src/peds/PedRoutes.h | 1 | ||||
-rw-r--r-- | src/render/Rubbish.cpp | 1 | ||||
-rw-r--r-- | src/render/Rubbish.h | 1 |
13 files changed, 641 insertions, 18 deletions
diff --git a/src/control/Garages.cpp b/src/control/Garages.cpp index dc77a154..a1d5f94b 100644 --- a/src/control/Garages.cpp +++ b/src/control/Garages.cpp @@ -14,7 +14,7 @@ int32 &CGarages::BankVansCollected = *(int32 *)0x8F1B34; bool &CGarages::BombsAreFree = *(bool *)0x95CD7A; bool &CGarages::RespraysAreFree = *(bool *)0x95CD1D; int32 &CGarages::CarsCollected = *(int32 *)0x880E18; -int32 &CGarages::CarTypesCollected = *(int32 *)0x8E286C; +int32 (&CGarages::CarTypesCollected)[TOTAL_COLLECTCARS_GARAGES] = *(int32 (*)[TOTAL_COLLECTCARS_GARAGES])(uintptr*)0x8E286C; int32 &CGarages::CrushedCarId = *(int32 *)0x943060; uint32 &CGarages::LastTimeHelpMessage = *(uint32 *)0x8F1B58; int32 &CGarages::MessageNumberInString = *(int32 *)0x885BA8; @@ -97,7 +97,7 @@ void CGarages::GivePlayerDetonator() } WRAPPER bool CGarages::HasThisCarBeenCollected(int16 garage, uint8 id) { EAXJMP(0x426D50); } -WRAPPER void CGarages::ChangeGarageType(int16 garage, eGarageType type) { EAXJMP(0x4222A0); } +WRAPPER void CGarages::ChangeGarageType(int16 garage, eGarageType type, int32 mi) { EAXJMP(0x4222A0); } WRAPPER bool CGarages::HasResprayHappened(int16 garage) { EAXJMP(0x4274F0); } void CGarage::OpenThisGarage() @@ -106,12 +106,41 @@ void CGarage::OpenThisGarage() m_eGarageState = GS_OPENING; } +bool CGarages::IsGarageOpen(int16 garage) +{ + return Garages[garage].IsOpen(); +} + +bool CGarages::IsGarageClosed(int16 garage) +{ + return Garages[garage].IsClosed(); +} + void CGarage::CloseThisGarage() { if (m_eGarageState == GS_OPENED || m_eGarageState == GS_OPENING) m_eGarageState = GS_CLOSING; } +void CGarages::SetGarageDoorToRotate(int16 garage) +{ + if (Garages[garage].m_bRotatedDoor) + return; + Garages[garage].m_bRotatedDoor = true; + Garages[garage].m_fDoorHeight /= 2.0f; + Garages[garage].m_fDoorHeight -= 0.1f; +} + +bool CGarages::HasImportExportGarageCollectedThisCar(int16 garage, int8 car) +{ + return CarTypesCollected[GetCarsCollectedIndexForGarageType(Garages[garage].m_eGarageType)] & (1 << car); +} + +void CGarages::SetLeaveCameraForThisGarage(int16 garage) +{ + Garages[garage].m_bCameraFollowsPlayer = true; +} + #if 0 WRAPPER void CGarages::PrintMessages(void) { EAXJMP(0x426310); } #else diff --git a/src/control/Garages.h b/src/control/Garages.h index 945922fe..8945e311 100644 --- a/src/control/Garages.h +++ b/src/control/Garages.h @@ -42,6 +42,11 @@ enum eGarageType : int8 GARAGE_MISSION_KEEPCAR_REMAINCLOSED, }; +enum +{ + TOTAL_COLLECTCARS_GARAGES = GARAGE_COLLECTCARS_3 - GARAGE_COLLECTCARS_1 + 1 +}; + class CStoredCar { int32 m_nModelIndex; @@ -64,6 +69,7 @@ static_assert(sizeof(CStoredCar) == 0x28, "CStoredCar"); class CGarage { +public: eGarageType m_eGarageType; eGarageState m_eGarageState; char field_2; @@ -101,9 +107,11 @@ class CGarage CVehicle *m_pTarget; int field_96; CStoredCar m_sStoredCar; -public: + void OpenThisGarage(); void CloseThisGarage(); + bool IsOpen() { return m_eGarageState == GS_OPENED || m_eGarageState == GS_OPENEDCONTAINSCAR; } + bool IsClosed() { return m_eGarageState == GS_FULLYCLOSED; } }; static_assert(sizeof(CGarage) == 140, "CGarage"); @@ -115,7 +123,7 @@ public: static bool &BombsAreFree; static bool &RespraysAreFree; static int32 &CarsCollected; - static int32 &CarTypesCollected; + static int32 (&CarTypesCollected)[TOTAL_COLLECTCARS_GARAGES]; static int32 &CrushedCarId; static uint32 &LastTimeHelpMessage; static int32 &MessageNumberInString; @@ -146,7 +154,14 @@ public: static void DeActivateGarage(int16); static int32 QueryCarsCollected(int16); static bool HasThisCarBeenCollected(int16, uint8); - static void ChangeGarageType(int16, eGarageType); + static void ChangeGarageType(int16, eGarageType, int32); static bool HasResprayHappened(int16); static void GivePlayerDetonator(); + static bool IsGarageOpen(int16); + static bool IsGarageClosed(int16); + static void SetGarageDoorToRotate(int16); + static bool HasImportExportGarageCollectedThisCar(int16, int8); + static void SetLeaveCameraForThisGarage(int16); + + static int GetCarsCollectedIndexForGarageType(eGarageType type) { return type - GARAGE_COLLECTCARS_1; } }; diff --git a/src/control/Script.cpp b/src/control/Script.cpp index e5d4ba95..dfb16b0f 100644 --- a/src/control/Script.cpp +++ b/src/control/Script.cpp @@ -21,6 +21,7 @@ #include "EmergencyPed.h" #include "Explosion.h" #include "FileMgr.h" +#include "Frontend.h" #include "Gangs.h" #include "Garages.h" #include "General.h" @@ -47,7 +48,9 @@ #include "Restart.h" #include "Replay.h" #include "RpAnimBlend.h" +#include "Rubbish.h" #include "Shadows.h" +#include "SpecialFX.h" #include "Stats.h" #include "Streaming.h" #include "Text.h" @@ -61,6 +64,13 @@ #define PICKUP_PLACEMENT_OFFSET 0.5f #define PED_FIND_Z_OFFSET 5.0f +#define SPHERE_MARKER_R 0 +#define SPHERE_MARKER_G 128 +#define SPHERE_MARKER_B 255 +#define SPHERE_MARKER_A 128 +#define SPHERE_MARKER_PULSE_PERIOD 2048 +#define SPHERE_MARKER_PULSE_FRACTION 0.1f + uint8 (&CTheScripts::ScriptSpace)[SIZE_SCRIPT_SPACE] = *(uint8(*)[SIZE_SCRIPT_SPACE])*(uintptr*)0x74B248; CRunningScript(&CTheScripts::ScriptsArray)[MAX_NUM_SCRIPTS] = *(CRunningScript(*)[MAX_NUM_SCRIPTS])*(uintptr*)0x6F5C08; int32(&CTheScripts::BaseBriefIdForContact)[MAX_NUM_CONTACTS] = *(int32(*)[MAX_NUM_CONTACTS])*(uintptr*)0x880200; @@ -6178,7 +6188,7 @@ int8 CRunningScript::ProcessCommands700To799(int32 command) } case COMMAND_CHANGE_GARAGE_TYPE: CollectParameters(&m_nIp, 2); - CGarages::ChangeGarageType(ScriptParams[0], (eGarageType)ScriptParams[1]); + CGarages::ChangeGarageType(ScriptParams[0], (eGarageType)ScriptParams[1], 0); return 0; case COMMAND_ACTIVATE_CRUSHER_CRANE: { @@ -7508,7 +7518,7 @@ int8 CRunningScript::ProcessCommands800To899(int32 command) assert(pVehicle); // Adding this check to correspond to command name. // All original game scripts always assume that the vehicle is actually Mr. Whoopee, - // but maybe there are mods that use it as "is horn activated"? + // but maybe there are mods that use it as "is alarm activated"? assert(pVehicle->GetModelIndex() == MI_MRWHOOP); UpdateCompareFlag(pVehicle->m_bSirenOrAlarm); return 0; @@ -7520,12 +7530,13 @@ int8 CRunningScript::ProcessCommands800To899(int32 command) } #endif -#if 1 +#if 0 WRAPPER int8 CRunningScript::ProcessCommands900To999(int32 command) { EAXJMP(0x44CB80); } #else int8 CRunningScript::ProcessCommands900To999(int32 command) { - char txd[52]; + char str[52]; + char onscreen_str[8]; switch (command) { case COMMAND_PRINT_STRING_IN_STRING_NOW: { @@ -7614,7 +7625,7 @@ int8 CRunningScript::ProcessCommands900To999(int32 command) case COMMAND_LOAD_ALL_MODELS_NOW: CTimer::Stop(); CStreaming::LoadAllRequestedModels(false); - CTimer::Resume(); + CTimer::Update(); return 0; case COMMAND_ADD_TO_OBJECT_VELOCITY: { @@ -7649,27 +7660,27 @@ int8 CRunningScript::ProcessCommands900To999(int32 command) case COMMAND_LOAD_SPRITE: { CollectParameters(&m_nIp, 1); - strncpy(txd, (char*)&CTheScripts::ScriptSpace[m_nIp], 8); + strncpy(str, (char*)&CTheScripts::ScriptSpace[m_nIp], 8); for (int i = 0; i < 8; i++) - txd[i] = tolower(txd[i]); + str[i] = tolower(str[i]); m_nIp += 8; int slot = CTxdStore::FindTxdSlot("script"); CTxdStore::PushCurrentTxd(); CTxdStore::SetCurrentTxd(slot); - CTheScripts::ScriptSprites[ScriptParams[0] - 1].SetTexture(txd); + CTheScripts::ScriptSprites[ScriptParams[0] - 1].SetTexture(str); CTxdStore::PopCurrentTxd(); return 0; } case COMMAND_LOAD_TEXTURE_DICTIONARY: { - strcpy(txd, "models\\"); - strncpy(txd + sizeof("models\\"), (char*)&CTheScripts::ScriptSpace[m_nIp], 8); - strcat(txd, ".txd"); + strcpy(str, "models\\"); + strncpy(str + sizeof("models\\"), (char*)&CTheScripts::ScriptSpace[m_nIp], 8); + strcat(str, ".txd"); m_nIp += 8; int slot = CTxdStore::FindTxdSlot("script"); if (slot == -1) slot = CTxdStore::AddTxdSlot("script"); - CTxdStore::LoadTxd(slot, txd); + CTxdStore::LoadTxd(slot, str); CTxdStore::AddRef(slot); return 0; } @@ -7818,146 +7829,590 @@ int8 CRunningScript::ProcessCommands900To999(int32 command) return 0; } case COMMAND_DRAW_SPHERE: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + C3dMarkers::PlaceMarkerSet((uint32)this + m_nIp, 4, pos, *(float*)&ScriptParams[3], + SPHERE_MARKER_R, SPHERE_MARKER_G, SPHERE_MARKER_B, SPHERE_MARKER_A, + SPHERE_MARKER_PULSE_PERIOD, SPHERE_MARKER_PULSE_FRACTION, 0); return 0; + } case COMMAND_SET_CAR_STATUS: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + assert(pVehicle); + pVehicle->m_status = ScriptParams[1]; return 0; + } case COMMAND_IS_CHAR_MALE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + assert(pPed); + UpdateCompareFlag(pPed->m_nPedType != PEDTYPE_CIVFEMALE && pPed->m_nPedType != PEDTYPE_PROSTITUTE); return 0; + } case COMMAND_SCRIPT_NAME: + { + strncpy(str, (char*)&CTheScripts::ScriptSpace[m_nIp], 8); + for (int i = 0; i < 8; i++) + str[i] = tolower(str[i]); + m_nIp += 8; + strncpy(m_abScriptName, str, 8); return 0; + } case COMMAND_CHANGE_GARAGE_TYPE_WITH_CAR_MODEL: + { + CollectParameters(&m_nIp, 3); + CGarages::ChangeGarageType(ScriptParams[0], (eGarageType)ScriptParams[1], ScriptParams[2]); return 0; + } case COMMAND_FIND_DRUG_PLANE_COORDINATES: + *(CVector*)&ScriptParams[0] = CPlane::FindDrugPlaneCoordinates(); + StoreParameters(&m_nIp, 3); return 0; case COMMAND_SAVE_INT_TO_DEBUG_FILE: + // TODO: implement something here + CollectParameters(&m_nIp, 1); return 0; case COMMAND_SAVE_FLOAT_TO_DEBUG_FILE: + CollectParameters(&m_nIp, 1); return 0; case COMMAND_SAVE_NEWLINE_TO_DEBUG_FILE: return 0; case COMMAND_POLICE_RADIO_MESSAGE: + CollectParameters(&m_nIp, 3); + DMAudio.PlaySuspectLastSeen(*(float*)&ScriptParams[0], *(float*)&ScriptParams[1], *(float*)&ScriptParams[2]); return 0; case COMMAND_SET_CAR_STRONG: + { + CollectParameters(&m_nIp, 2); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + assert(pVehicle); + pVehicle->bTakeLessDamage = ScriptParams[1] != 0; return 0; + } case COMMAND_REMOVE_ROUTE: + CollectParameters(&m_nIp, 1); + CRouteNode::RemoveRoute(ScriptParams[0]); return 0; case COMMAND_SWITCH_RUBBISH: + CollectParameters(&m_nIp, 1); + CRubbish::SetVisibility(ScriptParams[0] != 0);; return 0; case COMMAND_REMOVE_PARTICLE_EFFECTS_IN_AREA: + { + CollectParameters(&m_nIp, 6); + float x1 = *(float*)&ScriptParams[1]; + float y1 = *(float*)&ScriptParams[2]; + float z1 = *(float*)&ScriptParams[3]; + float x2 = *(float*)&ScriptParams[4]; + float y2 = *(float*)&ScriptParams[5]; + float z2 = *(float*)&ScriptParams[6]; + CParticleObject* tmp = CParticleObject::pCloseListHead; + while (tmp) { + CParticleObject* next = tmp->m_pNext; + if (tmp->IsWithinArea(x1, y1, z1, x2, y2, z2)) + tmp->RemoveObject(); + tmp = next; + } + tmp = CParticleObject::pFarListHead; + while (tmp) { + CParticleObject* next = tmp->m_pNext; + if (tmp->IsWithinArea(x1, y1, z1, x2, y2, z2)) + tmp->RemoveObject(); + tmp = next; + } return 0; + } case COMMAND_SWITCH_STREAMING: + CollectParameters(&m_nIp, 1); + CStreaming::ms_disableStreaming = ScriptParams[0] == 0; return 0; case COMMAND_IS_GARAGE_OPEN: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CGarages::IsGarageOpen(ScriptParams[0])); return 0; case COMMAND_IS_GARAGE_CLOSED: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CGarages::IsGarageClosed(ScriptParams[0])); return 0; case COMMAND_START_CATALINA_HELI: + CHeli::StartCatalinaFlyBy(); return 0; case COMMAND_CATALINA_HELI_TAKE_OFF: + CHeli::CatalinaTakeOff(); return 0; case COMMAND_REMOVE_CATALINA_HELI: + CHeli::RemoveCatalinaHeli(); return 0; case COMMAND_HAS_CATALINA_HELI_BEEN_SHOT_DOWN: + UpdateCompareFlag(CHeli::HasCatalinaBeenShotDown()); return 0; case COMMAND_SWAP_NEAREST_BUILDING_MODEL: + { + CollectParameters(&m_nIp, 6); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + float radius = *(float*)&ScriptParams[3]; + int mi1 = ScriptParams[4] >= 0 ? ScriptParams[4] : CTheScripts::UsedObjectArray[-ScriptParams[4]].index; + int mi2 = ScriptParams[5] >= 0 ? ScriptParams[5] : CTheScripts::UsedObjectArray[-ScriptParams[5]].index; + int16 total; + CEntity* apEntities[16]; + CWorld::FindObjectsOfTypeInRange(mi1, pos, radius, true, &total, 16, apEntities, true, false, false, false, false); + if (total == 0) + CWorld::FindObjectsOfTypeInRangeSectorList(mi1, CWorld::GetBigBuildingList(LEVEL_NONE), pos, radius, true, &total, 16, apEntities); + if (total == 0) + CWorld::FindObjectsOfTypeInRangeSectorList(mi1, CWorld::GetBigBuildingList(CTheZones::FindZoneForPoint(pos)), pos, radius, true, &total, 16, apEntities); + CEntity* pClosestEntity = nil; + float min_dist = 2.0f * radius; + for (int i = 0; i < total; i++) { + float dist = (apEntities[i]->GetPosition() - pos).Magnitude(); + if (dist < min_dist) { + min_dist = dist; + pClosestEntity = apEntities[i]; + } + } + if (!pClosestEntity) { + printf("Failed to find building\n"); + return 0; + } + CBuilding* pReplacedBuilding = ((CBuilding*)pClosestEntity); + pReplacedBuilding->ReplaceWithNewModel(mi2); + CTheScripts::AddToBuildingSwapArray(pReplacedBuilding, mi1, mi2); return 0; + } case COMMAND_SWITCH_WORLD_PROCESSING: + CollectParameters(&m_nIp, 1); + CWorld::bProcessCutsceneOnly = ScriptParams[0] == 0; return 0; case COMMAND_REMOVE_ALL_PLAYER_WEAPONS: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + assert(pPed); + pPed->ClearWeapons(); return 0; + } case COMMAND_GRAB_CATALINA_HELI: + CHeli* pHeli = CHeli::FindPointerToCatalinasHeli(); + ScriptParams[0] = pHeli ? CPools::GetVehiclePool()->GetIndex(pHeli) : -1; + StoreParameters(&m_nIp, 1); return 0; case COMMAND_CLEAR_AREA_OF_CARS: + { + CollectParameters(&m_nIp, 6); + float infX = *(float*)&ScriptParams[0]; + float infY = *(float*)&ScriptParams[1]; + float infZ = *(float*)&ScriptParams[2]; + float supX = *(float*)&ScriptParams[3]; + float supY = *(float*)&ScriptParams[4]; + float supZ = *(float*)&ScriptParams[5]; + if (infX > supX) { + infX = *(float*)&ScriptParams[3]; + supX = *(float*)&ScriptParams[0]; + } + if (infY > supY) { + infY = *(float*)&ScriptParams[4]; + supY = *(float*)&ScriptParams[1]; + } + if (infZ > supZ) { + infZ = *(float*)&ScriptParams[5]; + supZ = *(float*)&ScriptParams[2]; + } + CWorld::ClearCarsFromArea(infX, infY, infZ, supX, supY, supZ); return 0; + } case COMMAND_SET_ROTATING_GARAGE_DOOR: + CollectParameters(&m_nIp, 1); + CGarages::SetGarageDoorToRotate(ScriptParams[0]); return 0; case COMMAND_ADD_SPHERE: + { + CollectParameters(&m_nIp, 4); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + float radius = *(float*)&ScriptParams[3]; + CTheScripts::GetActualScriptSphereIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + ScriptParams[0] = CTheScripts::AddScriptSphere((uint32)this + m_nIp, pos, radius); + StoreParameters(&m_nIp, 1); return 0; + } case COMMAND_REMOVE_SPHERE: + CollectParameters(&m_nIp, 1); + CTheScripts::RemoveScriptSphere(ScriptParams[0]); return 0; case COMMAND_CATALINA_HELI_FLY_AWAY: + CHeli::MakeCatalinaHeliFlyAway(); return 0; case COMMAND_SET_EVERYONE_IGNORE_PLAYER: + { + CollectParameters(&m_nIp, 2); + CPlayerPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + assert(pPed); + if (ScriptParams[1]) { + pPed->m_pWanted->m_bIgnoredByEveryone = true; + CWorld::StopAllLawEnforcersInTheirTracks(); + } + else { + pPed->m_pWanted->m_bIgnoredByEveryone = false; + } return 0; + } case COMMAND_STORE_CAR_CHAR_IS_IN_NO_SAVE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CPools::GetPedPool()->GetAt(ScriptParams[0]); + assert(pPed); + CVehicle* pVehicle = pPed->bInVehicle ? pPed->m_pMyVehicle : nil; + ScriptParams[0] = CPools::GetVehiclePool()->GetIndex(pVehicle); + StoreParameters(&m_nIp, 1); return 0; + } case COMMAND_STORE_CAR_PLAYER_IS_IN_NO_SAVE: + { + CollectParameters(&m_nIp, 1); + CPed* pPed = CWorld::Players[ScriptParams[0]].m_pPed; + assert(pPed); + CVehicle* pVehicle = pPed->bInVehicle ? pPed->m_pMyVehicle : nil; + ScriptParams[0] = CPools::GetVehiclePool()->GetIndex(pVehicle); + StoreParameters(&m_nIp, 1); return 0; + } case COMMAND_IS_PHONE_DISPLAYING_MESSAGE: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(gPhoneInfo.IsMessageBeingDisplayed(ScriptParams[0])); return 0; case COMMAND_DISPLAY_ONSCREEN_TIMER_WITH_STRING: + assert(CTheScripts::ScriptSpace[m_nIp++] == ARGUMENT_GLOBALVAR); + int16 var = CTheScripts::Read2BytesFromScript(&m_nIp); + wchar* text = TheText.Get((char*)&CTheScripts::ScriptSpace[m_nIp]); // ??? + strncpy(onscreen_str, (char*)&CTheScripts::ScriptSpace[m_nIp], 8); + m_nIp += 8; + CUserDisplay::OnscnTimer.AddClock(var, onscreen_str); return 0; case COMMAND_DISPLAY_ONSCREEN_COUNTER_WITH_STRING: + assert(CTheScripts::ScriptSpace[m_nIp++] == ARGUMENT_GLOBALVAR); + int16 var = CTheScripts::Read2BytesFromScript(&m_nIp); + CollectParameters(&m_nIp, 1); + wchar* text = TheText.Get((char*)&CTheScripts::ScriptSpace[m_nIp]); // ??? + strncpy(onscreen_str, (char*)&CTheScripts::ScriptSpace[m_nIp], 8); + m_nIp += 8; + CUserDisplay::OnscnTimer.AddCounter(var, ScriptParams[0], onscreen_str); return 0; case COMMAND_CREATE_RANDOM_CAR_FOR_CAR_PARK: + { + CollectParameters(&m_nIp, 4); + if (CCarCtrl::NumRandomCars >= 30) + return 0; + int attempts; + int model = -1; + int index = CGeneral::GetRandomNumberInRange(0, 50); + for (attempts = 0; attempts < 50; attempts++) { + if (model != -1) + break; + model = CStreaming::ms_vehiclesLoaded[index]; + // desperatly want to believe this was inlined :| + CBaseModelInfo* pInfo = CModelInfo::GetModelInfo(model); + assert(pInfo->m_type == MITYPE_VEHICLE); + CVehicleModelInfo* pVehicleInfo = (CVehicleModelInfo*)pInfo; + if (pVehicleInfo->m_vehicleType != VEHICLE_TYPE_CAR) { + switch (model) { + case MI_LANDSTAL: + case MI_LINERUN: + case MI_FIRETRUCK: + case MI_TRASH: + case MI_STRETCH: + case MI_MULE: + case MI_AMBULAN: + case MI_FBICAR: + case MI_MRWHOOP: + case MI_BFINJECT: + case MI_CORPSE: + case MI_POLICE: + case MI_ENFORCER: + case MI_SECURICA: + case MI_PREDATOR: + case MI_BUS: + case MI_RHINO: + case MI_BARRACKS: + case MI_TRAIN: + case MI_CHOPPER: + case MI_DODO: + case MI_COACH: + case MI_RCBANDIT: + case MI_BELLYUP: + case MI_MRWONGS: + case MI_MAFIA: + case MI_YARDIE: + case MI_YAKUZA: + case MI_DIABLOS: + case MI_COLUMB: + case MI_HOODS: + case MI_AIRTRAIN: + case MI_DEADDODO: + case MI_SPEEDER: + case MI_REEFER: + case MI_PANLANT: + case MI_FLATBED: + case MI_YANKEE: + case MI_ESCAPE: + case MI_BORGNINE: + case MI_TOYZ: + case MI_GHOST: + case MI_MIAMI_RCBARON: + case MI_MIAMI_RCRAIDER: + model = -1; + break; + case MI_IDAHO: + case MI_STINGER: + case MI_PEREN: + case MI_SENTINEL: + case MI_PATRIOT: + case MI_MANANA: + case MI_INFERNUS: + case MI_BLISTA: + case MI_PONY: + case MI_CHEETAH: + case MI_MOONBEAM: + case MI_ESPERANT: + case MI_TAXI: + case MI_KURUMA: + case MI_BOBCAT: + case MI_BANSHEE: + case MI_CABBIE: + case MI_STALLION: + case MI_RUMPO: + case 151: + case 152: + case 153: + break; + default: + printf("CREATE_RANDOM_CAR_FOR_CAR_PARK - Unknown car model %d\n", CStreaming::ms_vehiclesLoaded[index]); + model = -1; + break; + } + } + else + model = -1; + if (++index >= 50) + index = 0; + } + if (model == -1) + return 0; + CVehicle* car; + if (!CModelInfo::IsBikeModel(model)) + car = new CAutomobile(model, MISSION_VEHICLE); + CVector pos = *(CVector*)&ScriptParams[0]; + pos.z += car->GetDistanceFromCentreOfMassToBaseOfModel(); + car->GetPosition() = pos; + car->SetHeading(DEGTORAD(*(float*)&ScriptParams[3])); + CTheScripts::ClearSpaceForMissionEntity(pos, car); + car->m_status = STATUS_ABANDONED; + car->bIsLocked = true; + car->bIsCarParkVehicle = true; + CCarCtrl::JoinCarWithRoadSystem(car); + car->AutoPilot.m_nCarMission = MISSION_NONE; + car->AutoPilot.m_nTempAction = TEMPACT_NONE; + car->AutoPilot.m_nDrivingStyle = DRIVINGSTYLE_STOP_FOR_CARS; + car->AutoPilot.m_nCruiseSpeed = car->AutoPilot.m_fMaxTrafficSpeed = 9.0f; + car->AutoPilot.m_nCurrentLane = car->AutoPilot.m_nNextLane = 0; + car->bEngineOn = false; + car->m_nZoneLevel = CTheZones::GetLevelFromPosition(pos); + CWorld::Add(car); return 0; + } case COMMAND_IS_COLLISION_IN_MEMORY: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CCollision::ms_collisionInMemory == ScriptParams[0]); return 0; case COMMAND_SET_WANTED_MULTIPLIER: + CollectParameters(&m_nIp, 1); + FindPlayerPed()->m_pWanted->m_fCrimeSensitivity = *(float*)&ScriptParams[0]; return 0; case COMMAND_SET_CAMERA_IN_FRONT_OF_PLAYER: + TheCamera.SetCameraDirectlyInFrontForFollowPed_CamOnAString(); return 0; case COMMAND_IS_CAR_VISIBLY_DAMAGED: + { + CollectParameters(&m_nIp, 1); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + assert(pVehicle); + UpdateCompareFlag(pVehicle->bIsDamaged); return 0; + } case COMMAND_DOES_OBJECT_EXIST: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CPools::GetObjectPool()->GetAt(ScriptParams[0])); return 0; case COMMAND_LOAD_SCENE: + { + CollectParameters(&m_nIp, 3); + CVector pos = *(CVector*)&ScriptParams[0]; + CTimer::Stop(); + CStreaming::LoadScene(pos); + CTimer::Update(); return 0; + } case COMMAND_ADD_STUCK_CAR_CHECK: + { + CollectParameters(&m_nIp, 3); + CVehicle* pVehicle = CPools::GetVehiclePool()->GetAt(ScriptParams[0]); + assert(pVehicle); + CTheScripts::StuckCars.AddCarToCheck(ScriptParams[0], *(float*)&ScriptParams[1], ScriptParams[2]); return 0; + } case COMMAND_REMOVE_STUCK_CAR_CHECK: + { + CollectParameters(&m_nIp, 1); + CTheScripts::StuckCars.RemoveCarFromCheck(ScriptParams[0]); return 0; + } case COMMAND_IS_CAR_STUCK: + CollectParameters(&m_nIp, 1); + UpdateCompareFlag(CTheScripts::StuckCars.HasCarBeenStuckForAWhile(ScriptParams[0])); return 0; case COMMAND_LOAD_MISSION_AUDIO: + strncpy(str, (char*)&CTheScripts::ScriptSpace[m_nIp], 8); + for (int i = 0; i < 8; i++) + str[i] = tolower(str[i]); + m_nIp += 8; + DMAudio.PreloadMissionAudio(str); return 0; case COMMAND_HAS_MISSION_AUDIO_LOADED: + UpdateCompareFlag(DMAudio.GetMissionAudioLoadingStatus() == 1); return 0; case COMMAND_PLAY_MISSION_AUDIO: + DMAudio.PlayLoadedMissionAudio(); return 0; case COMMAND_HAS_MISSION_AUDIO_FINISHED: + UpdateCompareFlag(DMAudio.IsMissionAudioSampleFinished()); return 0; case COMMAND_GET_CLOSEST_CAR_NODE_WITH_HEADING: + { + CollectParameters(&m_nIp, 3); + CVector pos = *(CVector*)&ScriptParams[0]; + if (pos.z <= MAP_Z_LOW_LIMIT) + pos.z = CWorld::FindGroundZForCoord(pos.x, pos.y); + int node = ThePaths.FindNodeClosestToCoors(pos, 0, 999999.9f, true, true); + *(CVector*)&ScriptParams[0] = ThePaths.m_pathNodes[node].pos; + *(float*)&ScriptParams[3] = ThePaths.FindNodeOrientationForCarPlacement(node); + StoreParameters(&m_nIp, 4); return 0; + } case COMMAND_HAS_IMPORT_GARAGE_SLOT_BEEN_FILLED: + { + CollectParameters(&m_nIp, 2); + UpdateCompareFlag(CGarages::HasImportExportGarageCollectedThisCar(ScriptParams[0], ScriptParams[1] - 1)); return 0; + } case COMMAND_CLEAR_THIS_PRINT: + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CMessages::ClearThisPrint(text); return 0; case COMMAND_CLEAR_THIS_BIG_PRINT: + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CMessages::ClearThisBigPrint(text); return 0; case COMMAND_SET_MISSION_AUDIO_POSITION: + { + CollectParameters(&m_nIp, 3); + CVector pos = *(CVector*)&ScriptParams[0]; + DMAudio.SetMissionAudioLocation(pos.x, pos.y, pos.z); return 0; + } case COMMAND_ACTIVATE_SAVE_MENU: + FrontEndMenuManager.m_bSaveMenuActive = true; return 0; case COMMAND_HAS_SAVE_GAME_FINISHED: + UpdateCompareFlag(!FrontEndMenuManager.m_bMenuActive); return 0; case COMMAND_NO_SPECIAL_CAMERA_FOR_THIS_GARAGE: + CollectParameters(&m_nIp, 1); + CGarages::SetLeaveCameraForThisGarage(ScriptParams[0]); return 0; case COMMAND_ADD_BLIP_FOR_PICKUP_OLD: + { + CollectParameters(&m_nIp, 3); + CObject* pObject = CPickups::aPickUps[CPickups::GetActualPickupIndex(ScriptParams[0])].m_pObject; + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + ScriptParams[0] = CRadar::SetEntityBlip(BLIP_OBJECT, CPools::GetObjectPool()->GetIndex(pObject), ScriptParams[1], (eBlipDisplay)ScriptParams[2]); + StoreParameters(&m_nIp, 1); return 0; + } case COMMAND_ADD_BLIP_FOR_PICKUP: + { + CollectParameters(&m_nIp, 1); + CObject* pObject = CPickups::aPickUps[CPickups::GetActualPickupIndex(ScriptParams[0])].m_pObject; + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + int handle = CRadar::SetEntityBlip(BLIP_OBJECT, CPools::GetObjectPool()->GetIndex(pObject), 6, BLIP_DISPLAY_BOTH); + CRadar::ChangeBlipScale(handle, 3); + ScriptParams[0] = handle; + StoreParameters(&m_nIp, 1); return 0; + } case COMMAND_ADD_SPRITE_BLIP_FOR_PICKUP: + { + CollectParameters(&m_nIp, 2); + CObject* pObject = CPickups::aPickUps[CPickups::GetActualPickupIndex(ScriptParams[0])].m_pObject; + CRadar::GetActualBlipArrayIndex(CollectNextParameterWithoutIncreasingPC(m_nIp)); + int handle = CRadar::SetEntityBlip(BLIP_OBJECT, CPools::GetObjectPool()->GetIndex(pObject), 6, BLIP_DISPLAY_BOTH); + CRadar::SetBlipSprite(handle, ScriptParams[1]); + ScriptParams[0] = handle; + StoreParameters(&m_nIp, 1); return 0; + } case COMMAND_SET_PED_DENSITY_MULTIPLIER: + CollectParameters(&m_nIp, 1); + CPopulation::PedDensityMultiplier = *(float*)&ScriptParams[0]; return 0; case COMMAND_FORCE_RANDOM_PED_TYPE: + CollectParameters(&m_nIp, 1); + CPopulation::m_AllRandomPedsThisType = ScriptParams[0]; return 0; case COMMAND_SET_TEXT_DRAW_BEFORE_FADE: + CollectParameters(&m_nIp, 1); + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_bTextBeforeFade = ScriptParams[0] != 0; return 0; case COMMAND_GET_COLLECTABLE1S_COLLECTED: + ScriptParams[0] = CWorld::Players[CWorld::PlayerInFocus].m_nCollectedPackages; + StoreParameters(&m_nIp, 1); return 0; case COMMAND_REGISTER_EL_BURRO_TIME: + CollectParameters(&m_nIp, 1); + CStats::RegisterElBurroTime(ScriptParams[0]); return 0; case COMMAND_SET_SPRITES_DRAW_BEFORE_FADE: + CollectParameters(&m_nIp, 1); + CTheScripts::IntroRectangles[CTheScripts::NumberOfIntroRectanglesThisFrame].m_bBeforeFade = ScriptParams[0] != 0; return 0; case COMMAND_SET_TEXT_RIGHT_JUSTIFY: + CollectParameters(&m_nIp, 1); + CTheScripts::IntroTextLines[CTheScripts::NumberOfIntroTextLinesThisFrame].m_bRightJustify = ScriptParams[0] != 0; return 0; case COMMAND_PRINT_HELP: + if (CCamera::m_bUseMouse3rdPerson && ( + strncmp((char*)&CTheScripts::ScriptSpace[m_nIp], "HELP15", 7) == 0 || + strncmp((char*)&CTheScripts::ScriptSpace[m_nIp], "GUN_2A", 7) == 0 || + strncmp((char*)&CTheScripts::ScriptSpace[m_nIp], "GUN_3A", 7) == 0 || + strncmp((char*)&CTheScripts::ScriptSpace[m_nIp], "GUN_4A", 7) == 0)){ + m_nIp += 8; + return 0; + } + wchar* text = CTheScripts::GetTextByKeyFromScript(&m_nIp); + CHud::SetHelpMessage(text, false); return 0; case COMMAND_CLEAR_HELP: + CHud::SetHelpMessage(nil, false); return 0; case COMMAND_FLASH_HUD_OBJECT: + CollectParameters(&m_nIp, 1); + CHud::m_ItemToFlash = ScriptParams[0]; return 0; default: assert(0); @@ -8329,6 +8784,53 @@ int16 CRunningScript::GetPadState(uint16 pad, uint16 button) return 0; } +int32 CTheScripts::GetActualScriptSphereIndex(int32 index) +{ + if (index == -1) + return -1; + uint16 check = (uint32)index >> 16; + uint16 array_idx = (uint32)index & (0xFFFF); + if (check != ScriptSphereArray[array_idx].m_Index) + return -1; + return index; +} + +int32 CTheScripts::AddScriptSphere(int32 id, CVector pos, float radius) +{ + int16 i = 0; + for (i = 0; i < MAX_NUM_SCRIPT_SPHERES; i++) { + if (!ScriptSphereArray[i].m_bInUse) + break; + } +#ifdef FIX_BUGS + if (i == MAX_NUM_SCRIPT_SPHERES) + return -1; +#endif + ScriptSphereArray[i].m_bInUse = true; + ScriptSphereArray[i].m_Id = id; + ScriptSphereArray[i].m_vecCenter = pos; + ScriptSphereArray[i].m_fRadius = radius; + return GetNewUniqueScriptSphereIndex(i); +} + +int32 CTheScripts::GetNewUniqueScriptSphereIndex(int32 index) +{ + if (ScriptSphereArray[index].m_Index >= 0xFFFE) + ScriptSphereArray[index].m_Index = 1; + else + ScriptSphereArray[index].m_Index = 1; + return index | ScriptSphereArray[index].m_Index << 16; +} + +void CTheScripts::RemoveScriptSphere(int32 index) +{ + index = GetActualScriptSphereIndex(index); + if (index == -1) + return; + ScriptSphereArray[index].m_bInUse = false; + ScriptSphereArray[index].m_Id = 0; +} + bool CTheScripts::IsVehicleStopped(CVehicle* pVehicle) { return 0.01f * CTimer::GetTimeStep() >= pVehicle->m_fDistanceTravelled; @@ -8359,7 +8861,40 @@ void CTheScripts::AddToInvisibilitySwapArray(CEntity* pEntity, bool remove) if (found) InvisibilitySettingArray[i] = pEntity; } +} +void CTheScripts::AddToBuildingSwapArray(CBuilding* pBuilding, int32 old_model, int32 new_model) +{ + int i = 0; + bool found = false; + while (i < MAX_NUM_BUILDING_SWAPS && !found) { + if (BuildingSwapArray[i].m_pBuilding == pBuilding) + found = true; + else + i++; + } + if (found) { + if (BuildingSwapArray[i].m_nOldModel == new_model) { + BuildingSwapArray[i].m_pBuilding = nil; + BuildingSwapArray[i].m_nOldModel = BuildingSwapArray[i].m_nNewModel = -1; + }else{ + BuildingSwapArray[i].m_nNewModel = new_model; + } + } + else { + i = 0; + while (i < MAX_NUM_BUILDING_SWAPS && !found) { + if (BuildingSwapArray[i].m_pBuilding == nil) + found = true; + else + i++; + } + if (found) { + BuildingSwapArray[i].m_pBuilding = pBuilding; + BuildingSwapArray[i].m_nNewModel = new_model; + BuildingSwapArray[i].m_nOldModel = old_model; + } + } } WRAPPER void CRunningScript::LocatePlayerCommand(int32, uint32*) { EAXJMP(0x44FE10); } diff --git a/src/control/Script.h b/src/control/Script.h index 9a39dd10..426a0f50 100644 --- a/src/control/Script.h +++ b/src/control/Script.h @@ -377,6 +377,12 @@ public: static void DrawDebugSquare(float, float, float, float); static void DrawDebugCube(float, float, float, float, float, float); static void AddToInvisibilitySwapArray(CEntity*, bool); + static void AddToBuildingSwapArray(CBuilding*, int32, int32); + + static int32 GetActualScriptSphereIndex(int32 index); + static int32 AddScriptSphere(int32 id, CVector pos, float radius); + static int32 GetNewUniqueScriptSphereIndex(int32 index); + static void RemoveScriptSphere(int32 index); static int32 Read4BytesFromScript(uint32* pIp){ int32 retval = 0; diff --git a/src/core/Stats.cpp b/src/core/Stats.cpp index c1259f82..cd5bee06 100644 --- a/src/core/Stats.cpp +++ b/src/core/Stats.cpp @@ -32,8 +32,14 @@ int32 &CStats::MissionsGiven = *(int32*)0x9430E8; int32 &CStats::MissionsPassed = *(int32*)0x940768; char(&CStats::LastMissionPassedName)[8] = *(char(*)[8])*(uintptr*)0x70D828; int32 &CStats::TotalLegitimateKills = *(int32*)0x8F6004; +int32 &CStats::ElBurroTime = *(int32*)0x8E2A6C; void CStats::AnotherKillFrenzyPassed() { ++NumberKillFrenziesPassed; } + +void CStats::RegisterElBurroTime(int32 time) +{ + ElBurroTime = (ElBurroTime && ElBurroTime < time) ? ElBurroTime : time; +} diff --git a/src/core/Stats.h b/src/core/Stats.h index 3e7973ea..e1ef3749 100644 --- a/src/core/Stats.h +++ b/src/core/Stats.h @@ -34,9 +34,11 @@ public: static int32 &MissionsPassed; static char (&LastMissionPassedName)[8]; static int32 &TotalLegitimateKills; + static int32 &ElBurroTime; public: static void AnotherKillFrenzyPassed(); static void CheckPointReachedUnsuccessfully() { KillsSinceLastCheckpoint = 0; }; static void CheckPointReachedSuccessfully() { TotalLegitimateKills += KillsSinceLastCheckpoint; KillsSinceLastCheckpoint = 0; }; + static void RegisterElBurroTime(int32); };
\ No newline at end of file diff --git a/src/core/World.cpp b/src/core/World.cpp index 950943ad..d026e971 100644 --- a/src/core/World.cpp +++ b/src/core/World.cpp @@ -49,6 +49,7 @@ WRAPPER void CWorld::FindObjectsIntersectingAngledCollisionBox(const CColBox &, WRAPPER void CWorld::FindObjectsOfTypeInRange(uint32, CVector&, float, bool, short*, short, CEntity**, bool, bool, bool, bool, bool) { EAXJMP(0x4B2600); } WRAPPER void CWorld::FindObjectsOfTypeInRangeSectorList(uint32, CPtrList&, CVector&, float, bool, short*, short, CEntity**) { EAXJMP(0x4B2960); } WRAPPER void CWorld::FindMissionEntitiesIntersectingCube(const CVector&, const CVector&, int16*, int16, CEntity**, bool, bool, bool) { EAXJMP(0x4B3680); } +WRAPPER void CWorld::ClearCarsFromArea(float, float, float, float, float, float) { EAXJMP(0x4B50E0); } void CWorld::Initialise() diff --git a/src/core/World.h b/src/core/World.h index 8111746e..68af156c 100644 --- a/src/core/World.h +++ b/src/core/World.h @@ -115,6 +115,7 @@ public: static void FindObjectsIntersectingCube(const CVector &, const CVector &, int16*, int16, CEntity **, bool, bool, bool, bool, bool); static void FindObjectsIntersectingAngledCollisionBox(const CColBox &, const CMatrix &, const CVector &, float, float, float, float, int16*, int16, CEntity **, bool, bool, bool, bool, bool); static void FindMissionEntitiesIntersectingCube(const CVector&, const CVector&, int16*, int16, CEntity**, bool, bool, bool); + static void ClearCarsFromArea(float, float, float, float, float, float); static float GetSectorX(float f) { return ((f - WORLD_MIN_X)/SECTOR_SIZE_X); } static float GetSectorY(float f) { return ((f - WORLD_MIN_Y)/SECTOR_SIZE_Y); } diff --git a/src/objects/ParticleObject.h b/src/objects/ParticleObject.h index a0d99b69..45a3fa30 100644 --- a/src/objects/ParticleObject.h +++ b/src/objects/ParticleObject.h @@ -40,7 +40,7 @@ enum eParticleObjectState enum tParticleType; class CParticle; -class CParticleObject : CPlaceable +class CParticleObject : public CPlaceable { public: CParticleObject *m_pNext; diff --git a/src/peds/PedRoutes.cpp b/src/peds/PedRoutes.cpp index c2ec51e2..9b7dafd4 100644 --- a/src/peds/PedRoutes.cpp +++ b/src/peds/PedRoutes.cpp @@ -43,3 +43,28 @@ CRouteNode::AddRoutePoint(int16 route, CVector pos) gaRoutes[point].m_route = route; gaRoutes[point].m_pos = pos; } + +void +CRouteNode::RemoveRoute(int16 route) +{ + uint16 first_point, last_point, i; + for (first_point = 0; first_point < NUMPEDROUTES; first_point++) { + if (gaRoutes[first_point].m_route == route) + break; + } + if (first_point == NUMPEDROUTES) + return; + for (last_point = first_point; last_point < NUMPEDROUTES; last_point++) + if (gaRoutes[last_point].m_route != route) + break; + uint16 diff = last_point - first_point; +#ifdef FIX_BUGS + for (i = first_point; i < NUMPEDROUTES - diff; i++) + gaRoutes[i] = gaRoutes[i + diff]; +#else + for (i = 0; i < diff; i++) + gaRoutes[first_point + i] = gaRoutes[last_point + i]; +#endif + for (i = NUMPEDROUTES - diff; i < NUMPEDROUTES; i++) + gaRoutes[i].m_route = -1; +} diff --git a/src/peds/PedRoutes.h b/src/peds/PedRoutes.h index ff4e6c89..d313938a 100644 --- a/src/peds/PedRoutes.h +++ b/src/peds/PedRoutes.h @@ -10,4 +10,5 @@ public: static CVector GetPointPosition(int16); static int16 GetRouteStart(int16); static void AddRoutePoint(int16, CVector); + static void RemoveRoute(int16); };
\ No newline at end of file diff --git a/src/render/Rubbish.cpp b/src/render/Rubbish.cpp index 05d6b544..5fa695ea 100644 --- a/src/render/Rubbish.cpp +++ b/src/render/Rubbish.cpp @@ -5,3 +5,4 @@ WRAPPER void CRubbish::Render(void) { EAXJMP(0x512190); } WRAPPER void CRubbish::StirUp(CVehicle *veh) { EAXJMP(0x512690); } WRAPPER void CRubbish::Update(void) { EAXJMP(0x511B90); } +WRAPPER void CRubbish::SetVisibility(bool) { EAXJMP(0x512AA0); } diff --git a/src/render/Rubbish.h b/src/render/Rubbish.h index 0bc7c397..7ed0978b 100644 --- a/src/render/Rubbish.h +++ b/src/render/Rubbish.h @@ -8,4 +8,5 @@ public: static void Render(void); static void StirUp(CVehicle *veh); // CAutomobile on PS2 static void Update(void); + static void SetVisibility(bool); }; |