From 9b9ffc4b13a47beff6ee2d400466d764360a0edf Mon Sep 17 00:00:00 2001 From: Nikolay Korolev Date: Sun, 7 Feb 2021 23:29:49 +0300 Subject: ferries --- src/control/Script.cpp | 2 +- src/control/Script9.cpp | 74 ++-- src/control/ScriptCommands.h | 2 +- src/core/Game.cpp | 5 + src/core/config.h | 4 +- src/entities/Entity.h | 6 +- src/render/Renderer.cpp | 3 + src/vehicles/Ferry.cpp | 822 +++++++++++++++++++++++++++++++++++++++++++ src/vehicles/Ferry.h | 130 +++++++ src/vehicles/Train.cpp | 30 +- 10 files changed, 1024 insertions(+), 54 deletions(-) create mode 100644 src/vehicles/Ferry.cpp (limited to 'src') diff --git a/src/control/Script.cpp b/src/control/Script.cpp index 8ce2d84d..609827ca 100644 --- a/src/control/Script.cpp +++ b/src/control/Script.cpp @@ -1662,7 +1662,7 @@ const tScriptCommandData commands[] = { REGISTER_COMMAND(COMMAND_IS_CHAR_CROUCHING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), true, -1, ""), REGISTER_COMMAND(COMMAND_GET_FERRY_BOARDING_SPACE, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ARGTYPE_FLOAT, ), false, -1, ""), REGISTER_COMMAND(COMMAND_GET_FERRY_HEADING, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(ARGTYPE_FLOAT, ), false, -1, ""), - REGISTER_COMMAND(COMMAND_SET_FERRIES_ENABLED, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), + REGISTER_COMMAND(COMMAND_SET_FERRIES_DISABLED, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_COMPLETE_FERRY_DOOR_MOVEMENT, INPUT_ARGUMENTS(ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_OVERRIDE_CAR_REMOTE_CONTROL, INPUT_ARGUMENTS(ARGTYPE_INT, ARGTYPE_INT, ), OUTPUT_ARGUMENTS(), false, -1, ""), REGISTER_COMMAND(COMMAND_CANCEL_REMOTE_MODE, INPUT_ARGUMENTS(), OUTPUT_ARGUMENTS(), false, -1, ""), diff --git a/src/control/Script9.cpp b/src/control/Script9.cpp index 3a68e239..7bf7bcc6 100644 --- a/src/control/Script9.cpp +++ b/src/control/Script9.cpp @@ -7,6 +7,7 @@ #include "CarCtrl.h" #include "Camera.h" #include "CutsceneMgr.h" +#include "Ferry.h" #include "Garages.h" #include "GameLogic.h" #include "Hud.h" @@ -29,22 +30,22 @@ int8 CRunningScript::ProcessCommands1500To1599(int32 command) case COMMAND_DISABLE_FERRY_PATH: { CollectParameters(&m_nIp, 1); - // CFerry:DissableFerryPath(GET_INTEGER_PARAM(0)); TODO + CFerry::DissableFerryPath(GET_INTEGER_PARAM(0)); return 0; } case COMMAND_ENABLE_FERRY_PATH: { CollectParameters(&m_nIp, 1); - // CFerry::EnableFerryPath(GET_INTEGER_PARAM(0)); + CFerry::EnableFerryPath(GET_INTEGER_PARAM(0)); return 0; } case COMMAND_GET_CLOSEST_DOCKED_FERRY: { CollectParameters(&m_nIp, 2); - // CFerry* pFerry = CFerry::GetClosestFerry(GET_FLOAT_PARAM(0), GET_FLOAT_PARAM(1)); + CFerry* pFerry = CFerry::GetClosestFerry(GET_FLOAT_PARAM(0), GET_FLOAT_PARAM(1)); int id = -1; - // if (pFerry && pFerry->IsDocked() - // id = pFerry->GetId(); + if (pFerry && pFerry->IsDocked()) + id = pFerry->m_nFerryId; SET_INTEGER_PARAM(0, id); StoreParameters(&m_nIp, 1); return 0; @@ -52,43 +53,43 @@ int8 CRunningScript::ProcessCommands1500To1599(int32 command) case COMMAND_OPEN_FERRY_DOOR: { CollectParameters(&m_nIp, 1); - // CFerry* pFerry = CFerry::GetFerry(GET_INTEGER_PARAM(0)); - // script_assert(pFerry); - // pFerry->OpenDoor(); + CFerry* pFerry = CFerry::GetFerry(GET_INTEGER_PARAM(0)); + script_assert(pFerry); + pFerry->OpenDoor(); return 0; } case COMMAND_CLOSE_FERRY_DOOR: { CollectParameters(&m_nIp, 1); - // CFerry* pFerry = CFerry::GetFerry(GET_INTEGER_PARAM(0)); - // script_assert(pFerry); - // pFerry->CloseDoor(); + CFerry* pFerry = CFerry::GetFerry(GET_INTEGER_PARAM(0)); + script_assert(pFerry); + pFerry->CloseDoor(); return 0; } case COMMAND_IS_FERRY_DOOR_OPEN: { CollectParameters(&m_nIp, 1); - // CFerry* pFerry = CFerry::GetFerry(GET_INTEGER_PARAM(0)); - // script_assert(pFerry); - // UpdateCompareFlag(pFerry->IsDoorOpen()); + CFerry* pFerry = CFerry::GetFerry(GET_INTEGER_PARAM(0)); + script_assert(pFerry); + UpdateCompareFlag(pFerry->IsDoorOpen()); UpdateCompareFlag(false); return 0; } case COMMAND_IS_FERRY_DOOR_CLOSED: { CollectParameters(&m_nIp, 1); - // CFerry* pFerry = CFerry::GetFerry(GET_INTEGER_PARAM(0)); - // script_assert(pFerry); - // UpdateCompareFlag(pFerry->IsDoorClosed()); + CFerry* pFerry = CFerry::GetFerry(GET_INTEGER_PARAM(0)); + script_assert(pFerry); + UpdateCompareFlag(pFerry->IsDoorClosed()); UpdateCompareFlag(true); return 0; } case COMMAND_SKIP_FERRY_TO_NEXT_DOCK: { CollectParameters(&m_nIp, 1); - // CFerry* pFerry = CFerry::GetFerry(GET_INTEGER_PARAM(0)); - // script_assert(pFerry); - // pFerry->SkipFerryToNextDock(); + CFerry* pFerry = CFerry::GetFerry(GET_INTEGER_PARAM(0)); + script_assert(pFerry); + pFerry->SkipFerryToNextDock(); return 0; } case COMMAND_SET_CHAR_DROPS_WEAPONS_ON_DEATH: @@ -110,35 +111,36 @@ int8 CRunningScript::ProcessCommands1500To1599(int32 command) case COMMAND_GET_FERRY_BOARDING_SPACE: { CollectParameters(&m_nIp, 4); - // CFerry* pFerry = CFerry::GetFerry(GET_INTEGER_PARAM(0)); - // script_assert(pFerry); - // ? = pFerry->GetBoardingSpace((CFerry::eSpaceUse)GET_INTEGER_PARAMS(1), (CFerry::eSpaceStyle)GET_INTEGER_PARAMS(2), GET_INTEGER_PARAMS(3)); - SET_FLOAT_PARAM(0, 0.0f); - SET_FLOAT_PARAM(1, 0.0f); // TODO + CFerry* pFerry = CFerry::GetFerry(GET_INTEGER_PARAM(0)); + script_assert(pFerry); + CVector space = pFerry->GetBoardingSpace((CFerry::eSpaceUse)GET_INTEGER_PARAM(1), (CFerry::eSpaceStyle)GET_INTEGER_PARAM(2), GET_INTEGER_PARAM(3)); + SET_FLOAT_PARAM(0, space.x); + SET_FLOAT_PARAM(1, space.y); StoreParameters(&m_nIp, 2); return 0; } case COMMAND_GET_FERRY_HEADING: { CollectParameters(&m_nIp, 1); - // CFerry* pFerry = CFerry::GetFerry(GET_INTEGER_PARAM(0)); - // script_assert(pFerry); - // float fHeading = CGeneral::GetATanOfXY(pFerry->GetForward().x, pFerry->GetForward().y); - // SET_FLOAT_PARAM(0, fHeading); - SET_FLOAT_PARAM(0, 0.0f); + CFerry* pFerry = CFerry::GetFerry(GET_INTEGER_PARAM(0)); + script_assert(pFerry); + float fHeading = Atan2(-pFerry->GetForward().x, pFerry->GetForward().y); + SET_FLOAT_PARAM(0, fHeading); StoreParameters(&m_nIp, 1); return 0; } - case COMMAND_SET_FERRIES_ENABLED: + case COMMAND_SET_FERRIES_DISABLED: { - CollectParameters(&m_nIp, 1); - // CFerry::SetFerriesEnabled(GET_INTEGER_PARAM(0)); + CollectParameters(&m_nIp, 2); + CFerry::SetFerriesDisabled(GET_INTEGER_PARAM(1)); return 0; } case COMMAND_COMPLETE_FERRY_DOOR_MOVEMENT: { CollectParameters(&m_nIp, 1); - // CFerry::CompleteDorrMovement(GET_INTEGER_PARAM(0)); + CFerry* pFerry = CFerry::GetFerry(GET_INTEGER_PARAM(0)); + script_assert(pFerry); + pFerry->CompleteDorrMovement(); return 0; } case COMMAND_OVERRIDE_CAR_REMOTE_CONTROL: @@ -157,7 +159,7 @@ int8 CRunningScript::ProcessCommands1500To1599(int32 command) else { TheCamera.TakeControl(pVehicle, CCam::MODE_1STPERSON, GET_INTEGER_PARAM(1) ? INTERPOLATION : JUMP_CUT, CAMCONTROL_SCRIPT); script_assert(pVehicle->IsCar()); - //((CAutomobile*)pVehicle)->Damage.m_bSmashedDoorDoesntClose = true; + ((CAutomobile*)pVehicle)->Damage.m_bSmashedDoorDoesntClose = true; } if (m_bIsMissionScript) CTheScripts::MissionCleanUp.RemoveEntityFromList(GET_INTEGER_PARAM(0), CLEANUP_CAR); @@ -670,7 +672,7 @@ int8 CRunningScript::ProcessCommands1500To1599(int32 command) } case COMMAND_SWITCH_FERRY_COLLISION: CollectParameters(&m_nIp, 1); - // CFerry::SwitchFerryCollision(GET_INTEGER_PARAM(0)); + CFerry::SwitchFerryCollision(GET_INTEGER_PARAM(0)); return 0; case COMMAND_SET_CHAR_MAX_HEALTH: { diff --git a/src/control/ScriptCommands.h b/src/control/ScriptCommands.h index 000f561c..93f670a9 100644 --- a/src/control/ScriptCommands.h +++ b/src/control/ScriptCommands.h @@ -1510,7 +1510,7 @@ enum { COMMAND_IS_CHAR_CROUCHING, COMMAND_GET_FERRY_BOARDING_SPACE, COMMAND_GET_FERRY_HEADING, - COMMAND_SET_FERRIES_ENABLED, + COMMAND_SET_FERRIES_DISABLED, COMMAND_COMPLETE_FERRY_DOOR_MOVEMENT, COMMAND_OVERRIDE_CAR_REMOTE_CONTROL, COMMAND_CANCEL_REMOTE_MODE, diff --git a/src/core/Game.cpp b/src/core/Game.cpp index b5b4b90d..c97a63a7 100644 --- a/src/core/Game.cpp +++ b/src/core/Game.cpp @@ -22,6 +22,7 @@ #include "Darkel.h" #include "Debug.h" #include "EventList.h" +#include "Ferry.h" #include "FileLoader.h" #include "FileMgr.h" #include "Fire.h" @@ -539,6 +540,7 @@ bool CGame::Initialise(const char* datFile) LoadingScreen("Loading the Game", "Position dynamic objects", nil); LoadingScreen("Loading the Game", "Initialise vehicle paths", nil); + CFerry::InitFerrys(); CTrain::InitTrains(); CPlane::InitPlanes(); CCredits::Init(); @@ -713,6 +715,7 @@ void CGame::ReInitGameObjectVariables(void) CTheScripts::StartTestScript(); CTheScripts::Process(); TheCamera.Process(); + CFerry::InitFerrys(); CTrain::InitTrains(); CPlane::InitPlanes(); } @@ -798,6 +801,7 @@ void CGame::InitialiseWhenRestarting(void) if ( GenericLoad() == true ) { DMAudio.ResetTimers(CTimer::GetTimeInMilliseconds()); + CFerry::InitFerrys(); CTrain::InitTrains(); CPlane::InitPlanes(); } @@ -881,6 +885,7 @@ void CGame::Process(void) CCollision::Update(); CScriptPaths::Update(); + CFerry::UpdateFerrys(); CTrain::UpdateTrains(); CPlane::UpdatePlanes(); CHeli::UpdateHelis(); diff --git a/src/core/config.h b/src/core/config.h index 94b2eb60..ac90c9bd 100644 --- a/src/core/config.h +++ b/src/core/config.h @@ -146,7 +146,9 @@ enum Config { NUM_EXPLOSIONS = 48, NUM_SETPIECES = 96, - NUM_SHORTCUT_START_POINTS = 16 + NUM_SHORTCUT_START_POINTS = 16, + + NUM_FERRY_PATHS = 1 }; // We don't expect to compile for PS2 or Xbox diff --git a/src/entities/Entity.h b/src/entities/Entity.h index c7a6f881..5d26f85c 100644 --- a/src/entities/Entity.h +++ b/src/entities/Entity.h @@ -24,11 +24,13 @@ enum eEntityStatus STATUS_PHYSICS, STATUS_ABANDONED, STATUS_WRECKED, - STATUS_TRAIN_MOVING, // these probably copied for FERRY + STATUS_TRAIN_MOVING, STATUS_TRAIN_NOT_MOVING, + STATUS_FERRY_MOVING, + STATUS_FERRY_NOT_MOVING, STATUS_HELI, STATUS_PLANE, - STATUS_PLAYER_REMOTE, // 12 in LCS + STATUS_PLAYER_REMOTE, STATUS_PLAYER_DISABLED, STATUS_GHOST }; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 90b54ab7..65fec400 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -27,6 +27,7 @@ #include "Renderer.h" #include "custompipes.h" #include "Frontend.h" +#include "Ferry.h" bool gbShowPedRoadGroups; bool gbShowCarRoadGroups; @@ -522,6 +523,8 @@ CRenderer::RenderVehicles(void) node != &gSortedVehiclesAndPeds.head; node = node->prev) RenderOneNonRoad(node->item.ent); + + CFerry::RenderAllRemaning(); } void diff --git a/src/vehicles/Ferry.cpp b/src/vehicles/Ferry.cpp new file mode 100644 index 00000000..32a8ac4f --- /dev/null +++ b/src/vehicles/Ferry.cpp @@ -0,0 +1,822 @@ +#include "common.h" +#include "main.h" +#include "Ferry.h" + +#include "AudioManager.h" +#include "Camera.h" +#include "Coronas.h" +#include "FileMgr.h" +#include "General.h" +#include "Leeds.h" +#include "Particle.h" +#include "PlayerPed.h" +#include "Streaming.h" +#include "TempColModels.h" +#include "WaterLevel.h" +#include "World.h" + +CFerryInst* CFerry::mspInst; + +#define FERRY_SPEED (0.1f) +#define FERRY_SLOWDOWN_DISTANCE (50.0f) +#define FERRY_TIME_STOPPED_AT_STATION (10.0f) + +CFerry::CFerry(int32 id, uint8 owner) : CVehicle(owner) +{ + m_bPlayerArrivedHorn = false; + m_nTimeAlongPath = 0; + m_vehType = VEHICLE_TYPE_FERRY; + CVehicleModelInfo* mi = (CVehicleModelInfo*)CModelInfo::GetModelInfo(id); + pHandling = mod_HandlingManager.GetHandlingData((tVehicleType)mi->m_handlingId); + SetModelIndex(id); + m_doors[0].Init(DEGTORAD(90.0f), 0.0f, 1, 0); + m_doors[1].Init(DEGTORAD(-95.0f), 0.0f, 1, 0); + m_doors[2].Init(DEGTORAD(-90.0f), 0.0f, 1, 0); + m_doors[3].Init(DEGTORAD(95.0f), 0.0f, 1, 0); + m_fTurnMass = 100000000.0f; + m_fAirResistance = 0.9994f; + m_fElasticity = 0.05f; + m_nNumMaxPassengers = 1; + m_fMass = 100000000.0f; + bInfiniteMass = true; + m_phy_flagA08 = true; + m_bFerryDocked = false; + SetStatus(STATUS_FERRY_MOVING); + bUsesCollision = true; + m_nDoorTimer = CTimer::GetTimeInMilliseconds(); + m_nDoorState = FERRY_DOOR_CLOSED; + m_bApproachingDock = false; + m_nSkipFerryStatus = 0; + m_nCollision = 0; + m_pDefaultColModel = mi->GetColModel(); + m_level = LEVEL_GENERIC; +} + +void CFerry::Init(void* pInstancePtr) +{ + mspInst = (CFerryInst*)pInstancePtr; + if (mspInst) + return; + // the following code should be wrapped in a define + mspInst = new CFerryInst(); + memset(mspInst, 0, sizeof(CFerryInst)); + for (int k = 0; k < NUM_FERRY_PATHS; k++) { + mspInst->pPathData[k] = new CFerryPath(); + mspInst->pPathData[k]->aLineBits = new CFerryInterpolationLine[NUM_FERRY_STATIONS * 4 + 2]; + + const char* filename = "Data\\PATHS\\FERRY1.DAT"; // actually base::cStringT filename; filename += k+1; filename += ".DAT" + + bool readingFile = false; + int bp, lp; + int i, tmp; + CFerryPath* pPath = mspInst->pPathData[k]; + + if (pPath->aTrackNodes == nil) { + readingFile = true; + + CFileMgr::LoadFile(filename, work_buff, sizeof(work_buff), "r"); + *gString = '\0'; + for (bp = 0, lp = 0; work_buff[bp] != '\n'; bp++, lp++) + gString[lp] = work_buff[bp]; + bp++; +#ifdef FIX_BUGS + gString[lp] = '\0'; +#endif + sscanf(gString, "%d", &tmp); + pPath->NumTrackNodes = tmp; + pPath->aTrackNodes = new CFerryNode[tmp]; + + for (i = 0; i < pPath->NumTrackNodes; i++) { + *gString = '\0'; + for (lp = 0; work_buff[bp] != '\n'; bp++, lp++) + gString[lp] = work_buff[bp]; + bp++; +#ifdef FIX_BUGS + gString[lp] = '\0'; +#endif + sscanf(gString, "%f %f %f", &pPath->aTrackNodes[i].x, &pPath->aTrackNodes[i].y, &pPath->aTrackNodes[i].z); + pPath->aTrackNodes[i].z = 0.0f; + } + } + + // Calculate length of segments and track + float t = 0.0f; + for (i = 0; i < pPath->NumTrackNodes; i++) { + pPath->aTrackNodes[i].t = t; + t += Sqrt(SQR(pPath->aTrackNodes[(i + 1) % pPath->NumTrackNodes].x - pPath->aTrackNodes[i].x)) + + (SQR(pPath->aTrackNodes[(i + 1) % pPath->NumTrackNodes].y - pPath->aTrackNodes[i].y)); + } + pPath->TotalLengthOfTrack = t; + + // Find correct z values + if (readingFile) { + CColPoint colpoint; + CEntity* entity; + for (i = 0; i < pPath->NumTrackNodes; i++) { + CVector p(pPath->aTrackNodes[i].x, pPath->aTrackNodes[i].y, pPath->aTrackNodes[i].z + 1.0f); + if (CWorld::ProcessVerticalLine(p, p.z - 0.5f, colpoint, entity, true, false, false, false, true, false, nil)) + pPath->aTrackNodes[i].z = colpoint.point.z; + pPath->aTrackNodes[i].z += 0.2f; + } + } + + int nStationIndices[NUM_FERRY_STATIONS]; + for (int i = 0; i < NUM_FERRY_STATIONS; i++) + nStationIndices[i] = 0; + int nCurrentStation = 0; + + for (i = 0; i < pPath->NumTrackNodes; i++) { + CFerryNode* pCurNode = &pPath->aTrackNodes[i]; + CFerryNode* pNextNode = (i + 1 < pPath->NumTrackNodes) ? &pPath->aTrackNodes[i + 1] : &pPath->aTrackNodes[0]; + CFerryNode* pPrevNode = (i - 1 >= 0) ? &pPath->aTrackNodes[i - 1] : &pPath->aTrackNodes[pPath->NumTrackNodes - 1]; + if (pCurNode->x - pNextNode->x > 0.0f && pPrevNode->x - pCurNode->x < 0.0f) + nStationIndices[nCurrentStation++] = i; + if (pCurNode->x - pNextNode->x < 0.0f && pPrevNode->x - pCurNode->x > 0.0f) + nStationIndices[nCurrentStation++] = i; + } + + float stationDists[NUM_FERRY_STATIONS]; + for (i = 0; i < NUM_FERRY_STATIONS; i++) + stationDists[i] = pPath->aTrackNodes[nStationIndices[i]].t; + + // Create animation for stopping at stations + float position = 0.0f; + float time = 0.0f; + int j = 0; + for (i = 0; i < NUM_FERRY_STATIONS; i++) { + // Start at full speed + pPath->aLineBits[j].type = FERRY_CRUISING; + pPath->aLineBits[j].time = time; + pPath->aLineBits[j].position = position; + pPath->aLineBits[j].speed = FERRY_SPEED; + pPath->aLineBits[j].acceleration = 0.0f; + j++; + // distance to next keyframe + float dist = (stationDists[i] - FERRY_SLOWDOWN_DISTANCE) - position; + time += dist / FERRY_SPEED; + position += dist; + + // Now slow down 50 units before stop + pPath->aLineBits[j].type = FERRY_SLOWING; + pPath->aLineBits[j].time = time; + pPath->aLineBits[j].position = position; + pPath->aLineBits[j].speed = FERRY_SPEED; + pPath->aLineBits[j].acceleration = -(FERRY_SPEED * FERRY_SPEED) / (4 * FERRY_SLOWDOWN_DISTANCE); + j++; + time += 2 * FERRY_SLOWDOWN_DISTANCE / FERRY_SPEED; + position += FERRY_SLOWDOWN_DISTANCE; // at station + + // stopping + pPath->aLineBits[j].type = FERRY_STOPPED; + pPath->aLineBits[j].time = time; + pPath->aLineBits[j].position = position; + pPath->aLineBits[j].speed = 0.0f; + pPath->aLineBits[j].acceleration = 0.0f; + j++; + time += FERRY_TIME_STOPPED_AT_STATION; + + // accelerate again + pPath->aLineBits[j].type = FERRY_ACCELERATING; + pPath->aLineBits[j].time = time; + pPath->aLineBits[j].position = position; + pPath->aLineBits[j].speed = 0.0f; + pPath->aLineBits[j].acceleration = (FERRY_SPEED * FERRY_SPEED) / (4 * FERRY_SLOWDOWN_DISTANCE); + j++; + time += 2 * FERRY_SLOWDOWN_DISTANCE / FERRY_SPEED; + position += FERRY_SLOWDOWN_DISTANCE; // after station + } + // last keyframe + pPath->aLineBits[j].type = FERRY_CRUISING; + pPath->aLineBits[j].time = time; + pPath->aLineBits[j].position = position; + pPath->aLineBits[j].speed = FERRY_SPEED; + pPath->aLineBits[j].acceleration = 0.0f; + j++; + pPath->TotalDurationOfTrack = time + (pPath->TotalLengthOfTrack - position) / FERRY_SPEED; + + // end + pPath->aLineBits[j].time = pPath->TotalDurationOfTrack; + } +} + +void CFerry::InitFerrys(void) +{ + if (!mspInst) + Init(nil); + for (int i = 0; i < NUM_FERRIES; i++) + mspInst->m_apFerries[i] = nil; + CStreaming::LoadAllRequestedModels(false); + CStreaming::RequestModel(MI_FERRY, 0); + CStreaming::LoadAllRequestedModels(false); +} + +void CFerry::SwitchFerryCollision(int type) +{ + for (int i = 0; i < NUM_FERRIES; i++) { + CFerry* pFerry = GetFerry(i); + if (pFerry && pFerry->m_nCollision != type) { + pFerry->m_nCollision = type; + CVehicleModelInfo* mi = pFerry->GetModelInfo(); + if (type == 1) + mi->SetColModel(&CTempColModels::ms_colModelFerryDocked); + else + mi->SetColModel(pFerry->m_pDefaultColModel, true); + } + } +} + +void CFerry::UpdateFerrys(void) +{ + int i, j; + float t, deltaT; + if (mspInst->m_bFerriesDisabled) + return; + for (i = 0; i < NUM_FERRIES; i++) { + CFerry* pFerry = GetFerry(i); + if (pFerry) { + pFerry->m_nTimeAlongPath += CTimer::GetTimeStepInMilliseconds(); + t = mspInst->pPathData[i/2]->TotalDurationOfTrack * (float)((pFerry->m_nTimeAlongPath + (i & 1) * 0x20000) & 0x3FFFF) / 0x40000; + // find current frame + for (j = 0; t > mspInst->pPathData[i / 2]->aLineBits[j + 1].time; j++); + + deltaT = t - mspInst->pPathData[i / 2]->aLineBits[j].time; + if (pFerry->m_bFerryDocked) { + if (mspInst->pPathData[i / 2]->aLineBits[j].type == FERRY_SLOWING) { + pFerry->m_nTimeAlongPath += (mspInst->pPathData[i / 2]->aLineBits[j + 1].time - mspInst->pPathData[i / 2]->aLineBits[j].time); + j++; + if (j > NUM_FERRY_STATIONS * 4 + 1) + j = 0; + pFerry->m_bApproachingDock = true; + if ((i & 1) == 0) { + GetFerry(i + 1)->m_bApproachingDock = true; + GetFerry(i + 1)->m_nTimeAlongPath += (mspInst->pPathData[i / 2]->aLineBits[j + 1].time - mspInst->pPathData[i / 2]->aLineBits[j].time); + } + else { + GetFerry(i - 1)->m_bApproachingDock = true; + GetFerry(i - 1)->m_nTimeAlongPath += (mspInst->pPathData[i / 2]->aLineBits[j + 1].time - mspInst->pPathData[i / 2]->aLineBits[j].time); + } + } + } + if (pFerry->m_nSkipFerryStatus == 1) { + float fDelta = 0.0f; + pFerry->m_nSkipFerryStatus = 2; + while (mspInst->pPathData[i / 2]->aLineBits[j].type != FERRY_STOPPED) { + fDelta += (mspInst->pPathData[i / 2]->aLineBits[j + 1].time - mspInst->pPathData[i / 2]->aLineBits[j].time); + j++; + if (j > NUM_FERRY_STATIONS * 4) + j = 0; + } + pFerry->m_nTimeAlongPath += fDelta; + pFerry->m_bApproachingDock = true; + if ((i & 1) == 0) { + GetFerry(i + 1)->m_bApproachingDock = true; + GetFerry(i + 1)->m_nTimeAlongPath += fDelta; + } + else { + GetFerry(i - 1)->m_bApproachingDock = true; + GetFerry(i - 1)->m_nTimeAlongPath += fDelta; + } + } + switch (mspInst->pPathData[i / 2]->aLineBits[j].type) { + case FERRY_STOPPED: + mspInst->m_afPositions[i] = mspInst->pPathData[i / 2]->aLineBits[j].position; + mspInst->m_afSpeeds[i] = 0.0f; + break; + case FERRY_CRUISING: + mspInst->m_afPositions[i] = mspInst->pPathData[i / 2]->aLineBits[j].position + mspInst->pPathData[i / 2]->aLineBits[j].speed * deltaT; + mspInst->m_afSpeeds[i] = (mspInst->pPathData[i / 2]->TotalDurationOfTrack * 1000.0f / 0x40000) * mspInst->pPathData[i / 2]->aLineBits[j].speed; + break; + case FERRY_SLOWING: + case FERRY_ACCELERATING: + pFerry->m_bApproachingDock = (mspInst->pPathData[i / 2]->aLineBits[j].type == FERRY_SLOWING); + mspInst->m_afPositions[i] = mspInst->pPathData[i / 2]->aLineBits[j].position + (mspInst->pPathData[i / 2]->aLineBits[j].speed + mspInst->pPathData[i / 2]->aLineBits[j].acceleration * deltaT) * deltaT; + mspInst->m_afSpeeds[i] = (mspInst->pPathData[i / 2]->TotalDurationOfTrack * 1000.0f / 0x40000) * (mspInst->pPathData[i / 2]->aLineBits[j].speed + 2 * mspInst->pPathData[i / 2]->aLineBits[j].acceleration * deltaT); + break; + } + } + } +} + +void CFerry::SetModelIndex(uint32 mi) +{ + CVehicle::SetModelIndex(mi); + for (int i = 0; i < NUM_FERRY_NODES; i++) + m_aFerryNodes[i] = nil; + CClumpModelInfo::FillFrameArray(GetClump(), m_aFerryNodes); +} + +void CFerry::PreRender(void) +{ + CVehicleModelInfo* mi = GetModelInfo(); + if (CGeneral::GetRandomTrueFalse()) + CParticle::AddParticle(PARTICLE_FERRY_CHIM_SMOKE, GetMatrix() * mi->m_positions[FERRY_POS_CHIM_LEFT], CVector(0.0f, 0.0f, 0.2f)); + CVector vVectorToCamera = GetPosition() - TheCamera.GetPosition(); + CVector vDirectionToCamera; + float fDistanceToCamera = vVectorToCamera.Magnitude(); + if (fDistanceToCamera == 0.0f) + vVectorToCamera = vDirectionToCamera = CVector(1.0f, 0.0f, 0.0f); + else + vDirectionToCamera = vVectorToCamera / fDistanceToCamera; + float dp = DotProduct(GetForward(), vDirectionToCamera); + if (dp < 0.0f) { + CVector vFrontLightPosition = GetMatrix() * mi->m_positions[FERRY_POS_LIGHT_FRONT]; + CVector vFrontLightPosition2 = vFrontLightPosition - 2 * mi->m_positions[FERRY_POS_LIGHT_FRONT].x * GetRight(); + float size = -dp + 1.0f; + float fIntensity = -dp * 0.4f + 0.2f; + if (dp < -0.9f && fDistanceToCamera < 35.0f) { + uint8 intensity = fIntensity * 255.0f; + CCoronas::RegisterCorona((uint32)(uintptr)this + 26, intensity, intensity, intensity, 255, vFrontLightPosition2, size, 80.0f, CCoronas::TYPE_NORMAL, CCoronas::FLARE_HEADLIGHTS, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + CCoronas::RegisterCorona((uint32)(uintptr)this + 27, intensity, intensity, intensity, 255, vFrontLightPosition, size, 80.0f, CCoronas::TYPE_NORMAL, CCoronas::FLARE_HEADLIGHTS, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + } + else { + uint8 intensity = fIntensity * 255.0f; + CCoronas::RegisterCorona((uint32)(uintptr)this + 26, intensity, intensity, intensity, 255, vFrontLightPosition2, size, 80.0f, CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + CCoronas::RegisterCorona((uint32)(uintptr)this + 27, intensity, intensity, intensity, 255, vFrontLightPosition, size, 80.0f, CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + } + } + CVector vRearLightPosition = GetMatrix() * mi->m_positions[FERRY_POS_LIGHT_REAR]; + CVector vRearLightPosition2 = vRearLightPosition - 2 * mi->m_positions[FERRY_POS_LIGHT_REAR].x * GetRight(); + CCoronas::RegisterCorona((uint32)(uintptr)this + 28, 255, 0, 0, 255, vRearLightPosition2, 1.0f, 80.0f, CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); + CCoronas::RegisterCorona((uint32)(uintptr)this + 29, 255, 0, 0, 255, vRearLightPosition, 1.0f, 80.0f, CCoronas::TYPE_NORMAL, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON, CCoronas::LOSCHECK_OFF, CCoronas::STREAK_ON, 0.0f); +} + +void CFerry::Render(void) +{ + m_bAlreadyRendered = true; + CEntity::Render(); +} + +void CFerry::RenderAllRemaning(void) +{ + for (int i = 0; i < NUM_FERRIES; i++) { + CFerry* pFerry = GetFerry(i); + if (pFerry) { + if (!pFerry->m_bAlreadyRendered) + pFerry->Render(); + pFerry->m_bAlreadyRendered = false; + } + } +} + +void CFerry::FerryHitStuff(CPtrList& lst) +{ + for (CPtrNode* pNode = lst.first; pNode != nil; pNode = pNode->next) { + CPhysical* pEntity = (CPhysical*)pNode->item; + if (pEntity != this && Abs(GetPosition().x - pEntity->GetPosition().z) < 1.5f) + pEntity->bHitByTrain = true; + } +} + +void CFerry::ProcessControl(void) +{ + if (gbModelViewer) + return; + PruneWakeTrail(); + if (m_isFarAway && (m_nFerryId + CTimer::GetFrameCounter() & 0xF) != 0) + return; + CFerryPath* pPath = mspInst->pPathData[m_nFerryId / 2]; + float fPosition = mspInst->m_afPositions[m_nFerryId]; + float fSpeed = mspInst->m_afSpeeds[m_nFerryId]; + if (fPosition < 0.0f) + fPosition += pPath->TotalLengthOfTrack; + + CFerryNode* trackNodes = mspInst->pPathData[m_nFerryId / 2]->aTrackNodes; + int16 numTrackNodes = mspInst->pPathData[m_nFerryId / 2]->NumTrackNodes; + float totalLengthOfTrack = mspInst->pPathData[m_nFerryId / 2]->TotalLengthOfTrack; + float trackPosition = mspInst->m_afPositions[m_nFerryId]; + float trackSpeed = mspInst->m_afSpeeds[m_nFerryId]; + + float trackPositionRear = trackPosition; + if (trackPositionRear < 0.0f) + trackPositionRear += totalLengthOfTrack; + + // Advance current node to appropriate position + float pos1, pos2; + int nextTrackNode = m_nCurTrackNode + 1; + pos1 = trackNodes[m_nCurTrackNode].t; + if (nextTrackNode < numTrackNodes) + pos2 = trackNodes[nextTrackNode].t; + else { + nextTrackNode = 0; + pos2 = totalLengthOfTrack; + } + while (trackPositionRear < pos1 || trackPositionRear > pos2) { + m_nCurTrackNode = (m_nCurTrackNode + 1) % numTrackNodes; + nextTrackNode = m_nCurTrackNode + 1; + pos1 = trackNodes[m_nCurTrackNode].t; + if (nextTrackNode < numTrackNodes) + pos2 = trackNodes[nextTrackNode].t; + else { + nextTrackNode = 0; + pos2 = totalLengthOfTrack; + } + } + float dist = trackNodes[nextTrackNode].t - trackNodes[m_nCurTrackNode].t; + if (dist < 0.0f) + dist += totalLengthOfTrack; + float f = (trackPositionRear - trackNodes[m_nCurTrackNode].t) / dist; + CVector posRear = (1.0f - f) * CVector(trackNodes[m_nCurTrackNode].x, trackNodes[m_nCurTrackNode].y, trackNodes[m_nCurTrackNode].z) + + f * CVector(trackNodes[nextTrackNode].x, trackNodes[nextTrackNode].y, trackNodes[nextTrackNode].z); + + // Now same again for the front + float trackPositionFront = trackPositionRear + 20.0f; + if (trackPositionFront > totalLengthOfTrack) + trackPositionFront -= totalLengthOfTrack; + int curTrackNodeFront = m_nCurTrackNode; + int nextTrackNodeFront = curTrackNodeFront + 1; + pos1 = trackNodes[curTrackNodeFront].t; + if (nextTrackNodeFront < numTrackNodes) + pos2 = trackNodes[nextTrackNodeFront].t; + else { + nextTrackNodeFront = 0; + pos2 = totalLengthOfTrack; + } + while (trackPositionFront < pos1 || trackPositionFront > pos2) { + curTrackNodeFront = (curTrackNodeFront + 1) % numTrackNodes; + nextTrackNodeFront = curTrackNodeFront + 1; + pos1 = trackNodes[curTrackNodeFront].t; + if (nextTrackNodeFront < numTrackNodes) + pos2 = trackNodes[nextTrackNodeFront].t; + else { + nextTrackNodeFront = 0; + pos2 = totalLengthOfTrack; + } + } + dist = trackNodes[nextTrackNodeFront].t - trackNodes[curTrackNodeFront].t; + if (dist < 0.0f) + dist += totalLengthOfTrack; + f = (trackPositionFront - trackNodes[curTrackNodeFront].t) / dist; + CVector posFront = (1.0f - f) * CVector(trackNodes[curTrackNodeFront].x, trackNodes[curTrackNodeFront].y, trackNodes[curTrackNodeFront].z) + + f * CVector(trackNodes[nextTrackNodeFront].x, trackNodes[nextTrackNodeFront].y, trackNodes[nextTrackNodeFront].z); + + // Now set matrix + SetPosition((posRear + posFront) / 2.0f); + CVector fwd = posFront - posRear; + m_vecForwardSpeed = fwd; + fwd.Normalise(); + float dp = DotProduct(fwd, GetForward()); + if (Abs(dp) > 1.001f || Abs(dp) < 0.999f) { + CVector df = CrossProduct(fwd, GetForward()); + CMatrix tmp; + float angle; + if (m_nSkipFerryStatus == 2) { + m_nSkipFerryStatus = 0; + angle = (fwd.x < 0.0f && GetForward().x < 0.0f) ? PI - Acos(dp) : Acos(dp); + } + else { + angle = 0.001f; + } + if (dp > 0.0f && df.z < 0.0f || dp <= 0.0f && df.z > 0.0f) + angle = -angle; + tmp.SetRotateZ(angle); + fwd = Multiply3x3(GetForward(), tmp); + } + else + fwd = GetForward(); + CVector right = CrossProduct(fwd, CVector(0.0f, 0.0f, 1.0f)); + right.Normalise(); + CVector up = CrossProduct(right, fwd); + GetRight() = right; + GetUp() = up; + GetForward() = fwd; + + // Set speed + m_vecMoveSpeed = fwd * trackSpeed / 60.0f; + m_fSpeed = trackSpeed / 60.0f; + m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f); + + if (m_vecMoveSpeed.MagnitudeSqr() > 0.001f || !m_bApproachingDock) { + SetStatus(STATUS_FERRY_MOVING); + m_bFerryDocked = false; + m_bPlayerArrivedHorn = false; + m_nTimeLeftStation = CTimer::GetTimeInMilliseconds(); + } + else { + SetStatus(STATUS_FERRY_NOT_MOVING); + PlayArrivedHorn(); + m_bFerryDocked = true; + } + + m_isFarAway = !((posFront - TheCamera.GetPosition()).Magnitude2D() < sq(1000.0f)); + + switch (m_nDoorState) { + case FERRY_DOOR_CLOSED: + break; + case FERRY_DOOR_OPENING: + if (CTimer::GetTimeInMilliseconds() < m_nDoorTimer) { + OpenFerryDoor(1.0f - (m_nDoorTimer - CTimer::GetTimeInMilliseconds()) / 1000.0f); + } + else { + OpenFerryDoor(1.0f); + m_nDoorState = FERRY_DOOR_OPEN; + } + break; + + case FERRY_DOOR_OPEN: + break; + + case FERRY_DOOR_CLOSING: + if (CTimer::GetTimeInMilliseconds() < m_nDoorTimer) { + OpenFerryDoor((m_nDoorTimer - CTimer::GetTimeInMilliseconds()) / 1000.0f); + } + else { + OpenFerryDoor(0.0f); + m_nDoorState = FERRY_DOOR_CLOSED; + } + break; + } + + CVector wn = CWaterLevel::GetWaterNormal(GetPosition().x, GetPosition().y); + float fRollAngle = wn.x - GetUp().x; + if (fRollAngle < 0.0f) + fRollAngle = Max(-0.05f, fRollAngle); + else + fRollAngle = Min(0.05f, fRollAngle); + + CVector oldPos = GetPosition(); + SetPosition(-GetPosition()); + CMatrix tmp2; + tmp2.SetRotateX(fRollAngle); + tmp2 = tmp2 * GetMatrix(); + tmp2.SetTranslateOnly(oldPos); + GetMatrix() = tmp2; + + GetMatrix().UpdateRW(); + UpdateRwFrame(); + RemoveAndAdd(); + + bIsStuck = false; + bIsInSafePosition = true; + bWasPostponed = false; + + // request/remove model + if (m_isFarAway) { + if (m_rwObject) + DeleteRwObject(); + } + else if (CStreaming::HasModelLoaded(MI_FERRY)) { + if (m_rwObject == nil) { + m_modelIndex = -1; + SetModelIndex(MI_FERRY); + } + } + else { + if (FindPlayerCoors().z * GetPosition().z >= 0.0f) + CStreaming::RequestModel(MI_FERRY, STREAMFLAGS_DEPENDENCY); + } + + // Hit stuff + if (GetStatus() == STATUS_TRAIN_MOVING) { + CVector nfwd = GetForward() * GetColModel()->boundingBox.max.y; + if (m_vecForwardSpeed.x > 0.0f) + nfwd = -nfwd; + if ((m_nFerryId & 1) == 0) + nfwd = -nfwd; + + int x, xmin, xmax; + int y, ymin, ymax; + + CVector front = GetPosition() + nfwd + m_vecMoveSpeed * CTimer::GetTimeStep(); + if (!m_isFarAway && m_vecMoveSpeed.Magnitude2D() > 0.05f) + AddWakePoint(nfwd * 0.85f + GetPosition()); + + xmin = CWorld::GetSectorIndexX(front.x - 3.0f); + if (xmin < 0) xmin = 0; + xmax = CWorld::GetSectorIndexX(front.x + 3.0f); + if (xmax > NUMSECTORS_X - 1) xmax = NUMSECTORS_X - 1; + ymin = CWorld::GetSectorIndexY(front.y - 3.0f); + if (ymin < 0) ymin = 0; + ymax = CWorld::GetSectorIndexY(front.y + 3.0f); + if (ymax > NUMSECTORS_Y - 1) ymax = NUMSECTORS_X - 1; + + CWorld::AdvanceCurrentScanCode(); + + for (y = ymin; y <= ymax; y++) + for (x = xmin; x <= xmax; x++) { + CSector* s = CWorld::GetSector(x, y); + FerryHitStuff(s->m_lists[ENTITYLIST_VEHICLES]); + FerryHitStuff(s->m_lists[ENTITYLIST_VEHICLES_OVERLAP]); + FerryHitStuff(s->m_lists[ENTITYLIST_PEDS]); + FerryHitStuff(s->m_lists[ENTITYLIST_PEDS_OVERLAP]); + } + } +} + +void CFerry::OpenFerryDoor(float ratio) +{ + if (!m_rwObject) + return; + + int door1 = 0; + int door2 = 1; + if (!m_bUseFrontDoor) { + door1 = 2; + door2 = 3; + } + int node1 = m_bUseFrontDoor ? FERRY_DOOR_FRONT : FERRY_DOOR_BACK; + int node2 = m_bUseFrontDoor ? FERRY_RAMP_FRONT : FERRY_RAMP_BACK; + CMatrix doorL(RwFrameGetMatrix(m_aFerryNodes[node1])); + CMatrix doorR(RwFrameGetMatrix(m_aFerryNodes[node2])); + CVector posL = doorL.GetPosition(); + CVector posR = doorR.GetPosition(); + + bool isClosed = m_doors[0].IsClosed(); // useless + + m_doors[door1].Open(ratio); + m_doors[door2].Open(ratio); + + doorL.SetRotateXOnly(m_doors[door1].m_fAngle); + doorR.SetRotateXOnly(m_doors[door2].m_fAngle); + + doorL.UpdateRW(); + doorR.UpdateRW(); +} + +CVector CFerry::GetBoardingSpace(CFerry::eSpaceUse use, CFerry::eSpaceStyle style, uint8 position) +{ + CVehicleModelInfo* pModelInfo = GetModelInfo(); + CVector space; + if (m_nFerryId & 1) { + if (style == FERRY_SPACE_STYLE_0) + style = FERRY_SPACE_STYLE_1; + else + style = FERRY_SPACE_STYLE_0; + } + switch (use) { + case FERRY_SPACE_PED: + space = pModelInfo->m_positions[FERRY_POS_PED_POINT]; + break; + case FERRY_SPACE_CAR: + space = pModelInfo->m_positions[FERRY_POS_CAR1 + position]; + break; + } + switch (style) { + case FERRY_SPACE_STYLE_0: + space = GetMatrix() * space; + break; + case FERRY_SPACE_STYLE_1: + space = GetMatrix() * space - (2 * space.x) * GetRight() - (2 * space.y) * GetForward(); + break; + } + return space; +} + +CFerry* CFerry::GetClosestFerry(float x, float y) +{ + int closest = -1; + float mindist = 9999.9f; + for (int i = 0; i < NUM_FERRIES; i++) { + CFerry* pFerry = GetFerry(i); + if (pFerry) { + float dist = ((CVector2D)pFerry->GetPosition() - CVector2D(x, y)).Magnitude(); + if (dist < 300.0f && dist < mindist) { + mindist = dist; + closest = i; + } + } + } + if (closest == -1) + return nil; + return GetFerry(closest); +} + +bool CFerry::IsDocked(void) +{ + return m_bFerryDocked; +} + +void CFerry::OpenDoor(void) +{ + printf("opening the ferry door\n"); + m_nDoorState = FERRY_DOOR_OPENING; + m_nDoorTimer = CTimer::GetTimeInMilliseconds() + 10000; + CVehicleModelInfo* pModelInfo = GetModelInfo(); + CVector2D vPlayerPos = FindPlayerPed()->GetPosition(); + float fDistToCar4 = (vPlayerPos - GetMatrix() * pModelInfo->m_positions[FERRY_POS_CAR4]).Magnitude(); + float fDistToCar1 = (vPlayerPos - GetMatrix() * pModelInfo->m_positions[FERRY_POS_CAR1]).Magnitude(); + m_bUseFrontDoor = true; + if (fDistToCar4 < fDistToCar1) + m_bUseFrontDoor = false; + // AudioManager.DirectlyEnqueueSample(0xb8,0,0,1,0x5622,0x7f,0x14,0); // TODO +} + +void CFerry::CloseDoor(void) +{ + printf("closing the ferry door\n"); + m_nDoorState = FERRY_DOOR_CLOSING; + m_nDoorTimer = CTimer::GetTimeInMilliseconds() + 10000; + // AudioManager.DirectlyEnqueueSample(0xb8, 0, 0, 1, 0x5622, 0x7f, 0x14, 0); // TODO +} + +bool CFerry::IsDoorOpen(void) +{ + return m_nDoorState == FERRY_DOOR_OPEN; +} + +bool CFerry::IsDoorClosed(void) +{ + return m_nDoorState == FERRY_DOOR_CLOSED; +} + +void CFerry::CompleteDorrMovement(void) +{ + m_nDoorTimer = 0; +} + +void CFerry::DissableFerryPath(int) +{ +} + +void CFerry::EnableFerryPath(int path) +{ + CStreaming::LoadAllRequestedModels(false); + CStreaming::RequestModel(MI_FERRY, 0); + CStreaming::LoadAllRequestedModels(false); + for (int i = path * 2; i < path * 2 + 2; i++) { + CFerry* pFerry = new CFerry(MI_FERRY, PERMANENT_VEHICLE); + bool bDirect = i & 1; + mspInst->m_apFerries[i] = pFerry; + pFerry->SetPosition(0.0f, 0.0f, 0.0f); + pFerry->SetStatus(STATUS_ABANDONED); + pFerry->bIsLocked = true; + pFerry->SetHeading(bDirect ? HALFPI : 3 * HALFPI); + pFerry->m_nFerryId = i; + pFerry->m_nCurTrackNode = 0; + CWorld::Add(pFerry); + } +} + +void CFerry::SkipFerryToNextDock(void) +{ + m_nSkipFerryStatus = 1; +} + +void CFerry::PruneWakeTrail(void) +{ + int16 num_remaining = 0; + for (int i = 0; i < NUM_WAKE_POINTS; i++) { + if (mspInst->m_afWakePointTimer[m_nFerryId][i] <= 0.0f) + break; + if (mspInst->m_afWakePointTimer[m_nFerryId][i] <= CTimer::GetTimeStep()) { + mspInst->m_afWakePointTimer[m_nFerryId][i] = 0.0f; + break; + } + mspInst->m_afWakePointTimer[m_nFerryId][i] -= CTimer::GetTimeStep(); + num_remaining++; + } + mspInst->m_anNumWakePoints[m_nFerryId] = num_remaining; +} + +void CFerry::AddWakePoint(CVector point) +{ + if (mspInst->m_afWakePointTimer[m_nFerryId][0] > 0.0f) { + int nNewWakePoints = Min(NUM_WAKE_POINTS - 1, mspInst->m_anNumWakePoints[m_nFerryId]); + for (int i = 0; i < nNewWakePoints; i++) { + mspInst->m_avWakePoints[m_nFerryId][i + 1] = mspInst->m_avWakePoints[m_nFerryId][i]; + mspInst->m_afWakePointTimer[m_nFerryId][i + 1] = mspInst->m_afWakePointTimer[m_nFerryId][i]; + } + } + mspInst->m_avWakePoints[m_nFerryId][0] = point; + mspInst->m_afWakePointTimer[m_nFerryId][0] = 900.0f; // TODO: define + mspInst->m_anNumWakePoints[m_nFerryId] += 1; +} + +void CFerry::PlayArrivedHorn(void) +{ + if (m_bPlayerArrivedHorn) + return; + m_bPlayerArrivedHorn = true; + float fDistToCamera = (GetPosition() - TheCamera.GetPosition()).Magnitude(); + if (fDistToCamera < 200.0f) { + uint8 volume = (200.0f - fDistToCamera) / 200.0f * 127; + // AudioManager.DirectlyEnqueueSample(0x32, 0, 0, 1, 18000, volume, 0x32, 0); // TODO + } +} + +CVector CFerry::GetNearestDoor(CVector) +{ + return CVector(0.0f, 0.0f, 1.0f); +} + +void CFerry::SetupForMultiplayer(void) +{ + for (int i = 0; i < NUM_FERRIES; i++) + mspInst->m_apFerries[i] = nil; + CStreaming::SetModelIsDeletable(MI_FERRY); +} + +void CFerry::Write(base::cRelocatableChunkWriter& writer) +{ + writer.AllocateRaw(mspInst, sizeof(*mspInst), 4); + if (!mspInst) + return; + for (int i = 0; i < NUM_FERRY_PATHS; i++) { + writer.AllocateRaw(mspInst->pPathData[i], sizeof(*mspInst->pPathData[i]), 4); + writer.AddPatch(&mspInst->pPathData[i]); + writer.AllocateRaw(mspInst->pPathData[i]->aTrackNodes, mspInst->pPathData[i]->NumTrackNodes * sizeof(CFerryNode), 4); + writer.AddPatch(&mspInst->pPathData[i]->aTrackNodes); + writer.AllocateRaw(mspInst->pPathData[i]->aLineBits, (NUM_FERRY_STATIONS * 4 + 2) * sizeof(CFerryInterpolationLine), 4); + writer.AddPatch(&mspInst->pPathData[i]->aTrackNodes); + } +} + diff --git a/src/vehicles/Ferry.h b/src/vehicles/Ferry.h index 375dfce1..2615a629 100644 --- a/src/vehicles/Ferry.h +++ b/src/vehicles/Ferry.h @@ -11,3 +11,133 @@ enum eFerryNodes FERRY_RAMP_BACK, NUM_FERRY_NODES }; + +enum { + NUM_FERRY_STATIONS = 2, + NUM_FERRIES = NUM_FERRY_PATHS * 2, + NUM_WAKE_POINTS = 64 +}; + +enum { + FERRY_STOPPED = 0, + FERRY_CRUISING, + FERRY_SLOWING, + FERRY_ACCELERATING +}; + +enum +{ + FERRY_DOOR_CLOSED = 0, + FERRY_DOOR_OPENING, + FERRY_DOOR_OPEN, + FERRY_DOOR_CLOSING +}; + +struct CFerryNode +{ + float x; + float y; + float z; + float t; +}; + +struct CFerryInterpolationLine +{ + uint8 type; + float time; // when does this keyframe start + // initial values at start of frame + float position; + float speed; + float acceleration; +}; + +struct CFerryPath +{ + float TotalLengthOfTrack; + float TotalDurationOfTrack; + int16 NumTrackNodes; + CFerryNode* aTrackNodes; + CFerryInterpolationLine* aLineBits; +}; + +class CFerry; + +class CFerryInst +{ +public: + CFerryPath* pPathData[NUM_FERRY_PATHS]; + float m_afPositions[NUM_FERRIES]; + float m_afSpeeds[NUM_FERRIES]; + CFerry* m_apFerries[NUM_FERRIES]; + bool m_bFerriesDisabled; + uint16 m_anNumWakePoints[NUM_FERRIES]; + CVector2D m_avWakePoints[NUM_FERRIES][NUM_WAKE_POINTS]; + float m_afWakePointTimer[NUM_FERRIES][NUM_WAKE_POINTS]; +}; + +class CFerry : public CVehicle +{ +public: + uint16 m_nFerryId; + uint16 m_isFarAway; + uint16 m_nCurTrackNode; + float m_fSpeed; + bool m_bFerryDocked; + uint32 m_nDoorTimer; + uint32 m_nTimeLeftStation; + int16 m_nDoorState; + bool m_bApproachingDock; + uint8 m_nSkipFerryStatus; + uint32 m_nTimeAlongPath; + bool m_bUseFrontDoor; + CVector m_vecForwardSpeed; + CColModel* m_pDefaultColModel; + uint8 m_nCollision; + CDoor m_doors[4]; + RwFrame* m_aFerryNodes[NUM_FERRY_NODES]; + bool m_bAlreadyRendered; + bool m_bPlayerArrivedHorn; + + static CFerryInst* mspInst; + + enum eSpaceUse { + FERRY_SPACE_PED = 0, + FERRY_SPACE_CAR + }; + enum eSpaceStyle { + FERRY_SPACE_STYLE_0 = 0, + FERRY_SPACE_STYLE_1 + }; + void Render(void); + static void EnableFerryPath(int); + CFerry(int32, uint8); + void SetModelIndex(uint32); + static void InitFerrys(void); + static void Init(void*); + void ProcessControl(void); + void PlayArrivedHorn(void); + void AddWakePoint(CVector); + void PruneWakeTrail(void); + void SkipFerryToNextDock(void); + static void DissableFerryPath(int); + void CompleteDorrMovement(void); + bool IsDoorOpen(void); + void CloseDoor(void); + bool IsDocked(void); + static CFerry* GetClosestFerry(float, float); + CVector GetBoardingSpace(CFerry::eSpaceUse, CFerry::eSpaceStyle, uint8); + CVector GetNearestDoor(CVector); + void OpenFerryDoor(float); + void FerryHitStuff(CPtrList&); + static void RenderAllRemaning(void); + static void UpdateFerrys(void); + static void SwitchFerryCollision(int); + void SetupForMultiplayer(void); + void Write(base::cRelocatableChunkWriter&); + virtual void OpenDoor(void); + void PreRender(void); + virtual bool IsDoorClosed(void); + + static CFerry* GetFerry(int i) { return mspInst ? mspInst->m_apFerries[i] : nil; } + static void SetFerriesDisabled(bool disabled) { mspInst->m_bFerriesDisabled = disabled; } +}; diff --git a/src/vehicles/Train.cpp b/src/vehicles/Train.cpp index 55be4a20..3c3d45d7 100644 --- a/src/vehicles/Train.cpp +++ b/src/vehicles/Train.cpp @@ -15,6 +15,10 @@ #include "Train.h" #include "AudioScriptObject.h" +#define TRAIN_SPEED (15.0f) +#define TRAIN_SLOWDOWN_DISTANCE (40.0f) +#define TRAIN_TIME_STOPPED_AT_STATION (25.0f) + static CTrainNode* pTrackNodes; static int16 NumTrackNodes; static float StationDist[3] = { 873.0f, 1522.0f, 2481.0f }; @@ -598,23 +602,23 @@ CTrain::ReadAndInterpretTrackFile(Const char *filename, CTrainNode **nodes, int1 interpLines[j].type = 1; interpLines[j].time = time; interpLines[j].position = position; - interpLines[j].speed = 15.0f; + interpLines[j].speed = TRAIN_SPEED; interpLines[j].acceleration = 0.0f; j++; // distance to next keyframe - float dist = (stationDists[i]-40.0f) - position; - time += dist/15.0f; + float dist = (stationDists[i]-TRAIN_SLOWDOWN_DISTANCE) - position; + time += dist/TRAIN_SPEED; position += dist; // Now slow down 40 units before stop interpLines[j].type = 2; interpLines[j].time = time; interpLines[j].position = position; - interpLines[j].speed = 15.0f; - interpLines[j].acceleration = -45.0f/32.0f; + interpLines[j].speed = TRAIN_SPEED; + interpLines[j].acceleration = -(TRAIN_SPEED * TRAIN_SPEED) / (4 * TRAIN_SLOWDOWN_DISTANCE); j++; - time += 80.0f/15.0f; - position += 40.0f; // at station + time += 2*TRAIN_SLOWDOWN_DISTANCE/TRAIN_SPEED; + position += TRAIN_SLOWDOWN_DISTANCE; // at station // stopping interpLines[j].type = 0; @@ -623,26 +627,26 @@ CTrain::ReadAndInterpretTrackFile(Const char *filename, CTrainNode **nodes, int1 interpLines[j].speed = 0.0f; interpLines[j].acceleration = 0.0f; j++; - time += 25.0f; + time += TRAIN_TIME_STOPPED_AT_STATION; // accelerate again interpLines[j].type = 2; interpLines[j].time = time; interpLines[j].position = position; interpLines[j].speed = 0.0f; - interpLines[j].acceleration = 45.0f/32.0f; + interpLines[j].acceleration = (TRAIN_SPEED * TRAIN_SPEED) / (4 * TRAIN_SLOWDOWN_DISTANCE); j++; - time += 80.0f/15.0f; - position += 40.0f; // after station + time += 2*TRAIN_SLOWDOWN_DISTANCE /TRAIN_SPEED; + position += TRAIN_SLOWDOWN_DISTANCE; // after station } // last keyframe interpLines[j].type = 1; interpLines[j].time = time; interpLines[j].position = position; - interpLines[j].speed = 15.0f; + interpLines[j].speed = TRAIN_SPEED; interpLines[j].acceleration = 0.0f; j++; - *totalDuration = time + (*totalLength - position)/15.0f; + *totalDuration = time + (*totalLength - position)/ TRAIN_SPEED; // end interpLines[j].time = *totalDuration; -- cgit v1.2.3