summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorNikolay Korolev <nickvnuk@gmail.com>2020-03-31 22:54:39 +0200
committerNikolay Korolev <nickvnuk@gmail.com>2020-03-31 22:54:39 +0200
commite2ed4680189e1a0ef135c4f038eb72de7a67146e (patch)
treef136c0db19a92315aebb2c91a50cf5e2e1401e2e /src
parentfix (diff)
parenttimebars (diff)
downloadre3-e2ed4680189e1a0ef135c4f038eb72de7a67146e.tar
re3-e2ed4680189e1a0ef135c4f038eb72de7a67146e.tar.gz
re3-e2ed4680189e1a0ef135c4f038eb72de7a67146e.tar.bz2
re3-e2ed4680189e1a0ef135c4f038eb72de7a67146e.tar.lz
re3-e2ed4680189e1a0ef135c4f038eb72de7a67146e.tar.xz
re3-e2ed4680189e1a0ef135c4f038eb72de7a67146e.tar.zst
re3-e2ed4680189e1a0ef135c4f038eb72de7a67146e.zip
Diffstat (limited to 'src')
-rw-r--r--src/core/Cam.cpp673
-rw-r--r--src/core/Camera.h1
-rw-r--r--src/core/Frontend.cpp36
-rw-r--r--src/core/Frontend.h13
-rw-r--r--src/core/Game.cpp3
-rw-r--r--src/core/Game.h3
-rw-r--r--src/core/MenuScreens.h3
-rw-r--r--src/core/Timer.cpp5
-rw-r--r--src/core/Timer.h1
-rw-r--r--src/core/config.h4
-rw-r--r--src/core/main.cpp75
-rw-r--r--src/core/re3.cpp6
-rw-r--r--src/core/timebars.cpp121
-rw-r--r--src/core/timebars.h6
-rw-r--r--src/peds/Ped.cpp12
-rw-r--r--src/peds/PlayerPed.cpp89
-rw-r--r--src/render/Font.cpp1151
-rw-r--r--src/render/Font.h16
-rw-r--r--src/text/Text.cpp5
-rw-r--r--src/vehicles/Automobile.cpp18
20 files changed, 1705 insertions, 536 deletions
diff --git a/src/core/Cam.cpp b/src/core/Cam.cpp
index 5844b61a..dd1b5ce2 100644
--- a/src/core/Cam.cpp
+++ b/src/core/Cam.cpp
@@ -23,12 +23,18 @@
#include "SceneEdit.h"
#include "Debug.h"
#include "Camera.h"
+#include "DMAudio.h"
const float DefaultFOV = 70.0f; // beta: 80.0f
bool PrintDebugCode = false;
int16 &DebugCamMode = *(int16*)0x95CCF2;
-bool bFreeCam = false;
+
+#ifdef FREE_CAM
+bool bFreePadCam = false;
+bool bFreeMouseCam = false;
+int nPreviousMode = -1;
+#endif
void
CCam::Init(void)
@@ -140,7 +146,7 @@ CCam::Process(void)
Process_FollowPedWithMouse(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
else
#ifdef FREE_CAM
- if(bFreeCam)
+ if(bFreePadCam)
Process_FollowPed_Rotation(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
else
#endif
@@ -180,7 +186,12 @@ CCam::Process(void)
Process_FlyBy(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
break;
case MODE_CAM_ON_A_STRING:
- Process_Cam_On_A_String(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
+#ifdef FREE_CAM
+ if(bFreeMouseCam || bFreePadCam)
+ Process_FollowCar_SA(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
+ else
+#endif
+ Process_Cam_On_A_String(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
break;
case MODE_REACTION:
Process_ReactionCam(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
@@ -192,7 +203,12 @@ CCam::Process(void)
Process_Chris_With_Binding_PlusRotation(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
break;
case MODE_BEHINDBOAT:
- Process_BehindBoat(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
+#ifdef FREE_CAM
+ if (bFreeMouseCam || bFreePadCam)
+ Process_FollowCar_SA(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
+ else
+#endif
+ Process_BehindBoat(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
break;
case MODE_PLAYER_FALLEN_WATER:
Process_Player_Fallen_Water(CameraTarget, TargetOrientation, SpeedVar, TargetSpeedVar);
@@ -244,6 +260,9 @@ CCam::Process(void)
Up = CVector(0.0f, 0.0f, 1.0f);
}
+#ifdef FREE_CAM
+ nPreviousMode = Mode;
+#endif
CVector TargetToCam = Source - m_cvecTargetCoorsForFudgeInter;
float DistOnGround = TargetToCam.Magnitude2D();
m_fTrueBeta = CGeneral::GetATanOfXY(TargetToCam.x, TargetToCam.y);
@@ -1530,7 +1549,12 @@ CCam::Process_FollowPedWithMouse(const CVector &CameraTarget, float TargetOrient
TargetCoors.z += fTranslateCamUp;
TargetCoors = DoAverageOnVector(TargetCoors);
+ // SA code
+#ifdef FREE_CAM
+ if((bFreeMouseCam && Alpha > 0.0f) || (!bFreeMouseCam && Alpha > fBaseDist))
+#else
if(Alpha > fBaseDist) // comparing an angle against a distance?
+#endif
CamDist = fBaseDist + Cos(min(Alpha*fFalloff, HALFPI))*fAngleDist;
else
CamDist = fBaseDist + Cos(Alpha)*fAngleDist;
@@ -4441,7 +4465,7 @@ CCam::Process_FollowPed_Rotation(const CVector &CameraTarget, float TargetOrient
float MouseX = CPad::GetPad(0)->GetMouseX();
float MouseY = CPad::GetPad(0)->GetMouseY();
float LookLeftRight, LookUpDown;
- if((MouseX != 0.0f || MouseY != 0.0f) && !CPad::GetPad(0)->ArePlayerControlsDisabled()){
+ if(bFreeMouseCam && (MouseX != 0.0f || MouseY != 0.0f) && !CPad::GetPad(0)->ArePlayerControlsDisabled()){
UseMouse = true;
LookLeftRight = -2.5f*MouseX;
LookUpDown = 4.0f*MouseY;
@@ -4572,6 +4596,645 @@ CCam::Process_FollowPed_Rotation(const CVector &CameraTarget, float TargetOrient
GetVectorsReadyForRW();
}
+
+// LCS cam hehe
+void
+CCam::Process_FollowCar_SA(const CVector& CameraTarget, float TargetOrientation, float, float)
+{
+ // Missing things on III CCam
+ static CVector m_aTargetHistoryPosOne;
+ static CVector m_aTargetHistoryPosTwo;
+ static CVector m_aTargetHistoryPosThree;
+ static int m_nCurrentHistoryPoints = 0;
+ static float lastBeta = -9999.0f;
+ static float lastAlpha = -9999.0f;
+ static float stepsLeftToChangeBetaByMouse;
+ static float dontCollideWithCars;
+ static bool alphaCorrected;
+ static float heightIncreaseMult;
+
+ if (!CamTargetEntity->IsVehicle())
+ return;
+
+ CVehicle* car = (CVehicle*)CamTargetEntity;
+ CVector TargetCoors = CameraTarget;
+ uint8 camSetArrPos = 0;
+
+ // We may need those later
+ bool isPlane = car->m_modelIndex == MI_DODO;
+ bool isHeli = false;
+ bool isBike = false;
+ bool isCar = car->IsCar() && !isPlane && !isHeli && !isBike;
+
+ CPad* pad = CPad::GetPad(0);
+
+ // Next direction is non-existent in III
+ uint8 nextDirectionIsForward = !(pad->GetLookBehindForCar() || pad->GetLookBehindForPed() || pad->GetLookLeft() || pad->GetLookRight()) &&
+ DirectionWasLooking == LOOKING_FORWARD;
+
+ if (car->m_modelIndex == MI_FIRETRUCK) {
+ camSetArrPos = 7;
+ } else if (car->m_modelIndex == MI_RCBANDIT) {
+ camSetArrPos = 5;
+ } else if (car->IsBoat()) {
+ camSetArrPos = 4;
+ } else if (isBike) {
+ camSetArrPos = 1;
+ } else if (isPlane) {
+ camSetArrPos = 3;
+ } else if (isHeli) {
+ camSetArrPos = 2;
+ }
+
+ // LCS one but index 1(firetruck) moved to last
+ float CARCAM_SET[][15] = {
+ {1.3f, 1.0f, 0.4f, 10.0f, 15.0f, 0.5f, 1.0f, 1.0f, 0.85f, 0.2f, 0.075f, 0.05f, 0.8f, DEGTORAD(45.0f), DEGTORAD(89.0f)}, // cars
+ {1.1f, 1.0f, 0.1f, 10.0f, 11.0f, 0.5f, 1.0f, 1.0f, 0.85f, 0.2f, 0.075f, 0.05f, 0.75f, DEGTORAD(45.0f), DEGTORAD(89.0f)}, // bike
+ {1.1f, 1.0f, 0.2f, 10.0f, 15.0f, 0.05f, 0.05f, 0.0f, 0.9f, 0.05f, 0.01f, 0.05f, 1.0f, DEGTORAD(10.0f), DEGTORAD(70.0f)}, // heli (SA values)
+ {1.1f, 3.5f, 0.2f, 10.0f, 25.0f, 0.5f, 1.0f, 1.0f, 0.75f, 0.1f, 0.005f, 0.2f, 1.0f, DEGTORAD(89.0f), DEGTORAD(89.0f)}, // plane (SA values)
+ {0.9f, 1.0f, 0.1f, 10.0f, 15.0f, 0.5f, 1.0f, 0.0f, 0.9f, 0.05f, 0.005f, 0.05f, 1.0f, -0.2f, DEGTORAD(70.0f)}, // boat
+ {1.1f, 1.0f, 0.2f, 10.0f, 5.0f, 0.5f, 1.0f, 1.0f, 0.75f, 0.1f, 0.005f, 0.2f, 1.0f, DEGTORAD(45.0f), DEGTORAD(89.0f)}, // rc cars
+ {1.1f, 1.0f, 0.2f, 10.0f, 5.0f, 0.5f, 1.0f, 1.0f, 0.75f, 0.1f, 0.005f, 0.2f, 1.0f, DEGTORAD(20.0f), DEGTORAD(70.0f)}, // rc heli/planes
+ {1.3f, 1.0f, 0.4f, 10.0f, 15.0f, 0.5f, 1.0f, 1.0f, 0.85f, 0.2f, 0.075f, 0.05f, 0.8f, -0.18f, DEGTORAD(40.0f)}, // firetruck...
+ };
+
+ // RC Heli/planes use same alpha values with heli/planes (LCS firetruck will fallback to 0)
+ uint8 alphaArrPos = (camSetArrPos > 4 ? (isPlane ? 3 : (isHeli ? 2 : 0)) : camSetArrPos);
+ float zoomModeAlphaOffset = 0.0f;
+ static float ZmOneAlphaOffsetLCS[] = { 0.12f, 0.08f, 0.15f, 0.08f, 0.08f };
+ static float ZmTwoAlphaOffsetLCS[] = { 0.1f, 0.08f, 0.3f, 0.08f, 0.08f };
+ static float ZmThreeAlphaOffsetLCS[] = { 0.065f, 0.05f, 0.15f, 0.06f, 0.08f };
+
+ if (isHeli && car->m_status == STATUS_PLAYER_REMOTE)
+ zoomModeAlphaOffset = ZmTwoAlphaOffsetLCS[alphaArrPos];
+ else {
+ switch ((int)TheCamera.CarZoomIndicator) {
+ // near
+ case 1:
+ zoomModeAlphaOffset = ZmOneAlphaOffsetLCS[alphaArrPos];
+ break;
+ // mid
+ case 2:
+ zoomModeAlphaOffset = ZmTwoAlphaOffsetLCS[alphaArrPos];
+ break;
+ // far
+ case 3:
+ zoomModeAlphaOffset = ZmThreeAlphaOffsetLCS[alphaArrPos];
+ break;
+ default:
+ break;
+ }
+ }
+
+ CColModel* carCol = (CColModel*)car->GetColModel();
+ float colMaxZ = carCol->boundingBox.max.z; // As opposed to LCS and SA, VC does this: carCol->boundingBox.max.z - carCol->boundingBox.min.z;
+ float approxCarLength = 2.0f * Abs(carCol->boundingBox.min.y); // SA taxi min.y = -2.95, max.z = 0.883502f
+
+ float newDistance = TheCamera.CarZoomValueSmooth + CARCAM_SET[camSetArrPos][1] + approxCarLength;
+
+ float minDistForThisCar = approxCarLength * CARCAM_SET[camSetArrPos][3];
+
+ if (!isHeli || car->m_status == STATUS_PLAYER_REMOTE) {
+ float radiusToStayOutside = colMaxZ * CARCAM_SET[camSetArrPos][0] - CARCAM_SET[camSetArrPos][2];
+ if (radiusToStayOutside > 0.0f) {
+ TargetCoors.z += radiusToStayOutside;
+ newDistance += radiusToStayOutside;
+ zoomModeAlphaOffset += 0.3f / newDistance * radiusToStayOutside;
+ }
+ } else {
+ // 0.6f = fTestShiftHeliCamTarget
+ TargetCoors.x += 0.6f * car->GetUp().x * colMaxZ;
+ TargetCoors.y += 0.6f * car->GetUp().y * colMaxZ;
+ TargetCoors.z += 0.6f * car->GetUp().z * colMaxZ;
+ }
+
+ float minDistForVehType = CARCAM_SET[camSetArrPos][4];
+
+ if ((int)TheCamera.CarZoomIndicator == 1 && (camSetArrPos < 2 || camSetArrPos == 7)) {
+ minDistForVehType = minDistForVehType * 0.65f;
+ }
+
+ float nextDistance = max(newDistance, minDistForVehType);
+
+ CA_MAX_DISTANCE = newDistance;
+ CA_MIN_DISTANCE = 3.5f;
+
+ if (ResetStatics) {
+ FOV = DefaultFOV;
+
+ // GTA 3 has this in veh. camera
+ if (TheCamera.m_bIdleOn)
+ TheCamera.m_uiTimeWeEnteredIdle = CTimer::GetTimeInMilliseconds();
+ } else {
+ if (isCar || isBike) {
+ // 0.4f: CAR_FOV_START_SPEED
+ if (DotProduct(car->GetForward(), car->m_vecMoveSpeed) > 0.4f)
+ FOV += (DotProduct(car->GetForward(), car->m_vecMoveSpeed) - 0.4f) * CTimer::GetTimeStep();
+ }
+
+ if (FOV > DefaultFOV)
+ // 0.98f: CAR_FOV_FADE_MULT
+ FOV = pow(0.98f, CTimer::GetTimeStep()) * (FOV - DefaultFOV) + DefaultFOV;
+
+ if (FOV <= DefaultFOV + 30.0f) {
+ if (FOV < DefaultFOV)
+ FOV = DefaultFOV;
+ } else
+ FOV = DefaultFOV + 30.0f;
+ }
+
+ // WORKAROUND: I still don't know how looking behind works (m_bCamDirectlyInFront is unused in III, they seem to use m_bUseTransitionBeta)
+ if (pad->GetLookBehindForCar())
+ if (DirectionWasLooking == LOOKING_FORWARD || !LookingBehind)
+ TheCamera.m_bCamDirectlyInFront = true;
+
+ // Taken from RotCamIfInFrontCar, because we don't call it anymore
+ if (!(pad->GetLookBehindForCar() || pad->GetLookBehindForPed() || pad->GetLookLeft() || pad->GetLookRight()))
+ if (DirectionWasLooking != LOOKING_FORWARD)
+ TheCamera.m_bCamDirectlyBehind = true;
+
+ // Called when we just entered the car, just started to look behind or returned back from looking left, right or behind
+ if (ResetStatics || TheCamera.m_bCamDirectlyBehind || TheCamera.m_bCamDirectlyInFront) {
+ ResetStatics = false;
+ Rotating = false;
+ m_bCollisionChecksOn = true;
+ // TheCamera.m_bResetOldMatrix = 1;
+
+ // Garage exit cam is not working well in III...
+ // if (!TheCamera.m_bJustCameOutOfGarage) // && !sthForScript)
+ // {
+ Alpha = 0.0f;
+ Beta = car->GetForward().Heading() - HALFPI;
+ if (TheCamera.m_bCamDirectlyInFront) {
+ Beta += PI;
+ }
+ // }
+
+ BetaSpeed = 0.0;
+ AlphaSpeed = 0.0;
+ Distance = 1000.0;
+
+ Front.x = -(cos(Beta) * cos(Alpha));
+ Front.y = -(sin(Beta) * cos(Alpha));
+ Front.z = sin(Alpha);
+
+ m_aTargetHistoryPosOne = TargetCoors - nextDistance * Front;
+
+ m_aTargetHistoryPosTwo = TargetCoors - newDistance * Front;
+
+ m_nCurrentHistoryPoints = 0;
+ if (!TheCamera.m_bJustCameOutOfGarage) // && !sthForScript)
+ Alpha = -zoomModeAlphaOffset;
+ }
+
+ Front = TargetCoors - m_aTargetHistoryPosOne;
+ Front.Normalise();
+
+ // Code that makes cam rotate around the car
+ float camRightHeading = Front.Heading() - HALFPI;
+ if (camRightHeading < -PI)
+ camRightHeading = camRightHeading + TWOPI;
+
+ float velocityRightHeading;
+ if (car->m_vecMoveSpeed.Magnitude2D() <= 0.02f)
+ velocityRightHeading = camRightHeading;
+ else
+ velocityRightHeading = car->m_vecMoveSpeed.Heading() - HALFPI;
+
+ if (velocityRightHeading < camRightHeading - PI)
+ velocityRightHeading = velocityRightHeading + TWOPI;
+ else if (velocityRightHeading > camRightHeading + PI)
+ velocityRightHeading = velocityRightHeading - TWOPI;
+
+ float betaChangeMult1 = CTimer::GetTimeStep() * CARCAM_SET[camSetArrPos][10];
+ float betaChangeLimit = CTimer::GetTimeStep() * CARCAM_SET[camSetArrPos][11];
+
+ float betaChangeMult2 = (car->m_vecMoveSpeed - DotProduct(car->m_vecMoveSpeed, Front) * Front).Magnitude();
+
+ float betaChange = min(1.0f, betaChangeMult1 * betaChangeMult2) * (velocityRightHeading - camRightHeading);
+ if (betaChange <= betaChangeLimit) {
+ if (betaChange < -betaChangeLimit)
+ betaChange = -betaChangeLimit;
+ } else {
+ betaChange = betaChangeLimit;
+ }
+ float targetBeta = camRightHeading + betaChange;
+
+ if (targetBeta < Beta - HALFPI)
+ targetBeta += TWOPI;
+ else if (targetBeta > Beta + PI)
+ targetBeta -= TWOPI;
+
+ float carPosChange = (TargetCoors - m_aTargetHistoryPosTwo).Magnitude();
+ if (carPosChange < newDistance && newDistance > minDistForThisCar) {
+ newDistance = max(minDistForThisCar, carPosChange);
+ }
+ float maxAlphaAllowed = CARCAM_SET[camSetArrPos][13];
+
+ // Originally this is to prevent camera enter into car while we're stopping, but what about moving???
+ // This is also original LCS and SA bug, or some attempt to fix lag. We'll never know
+
+ // if (car->m_vecMoveSpeed.MagnitudeSqr() < sq(0.2f))
+ if (car->m_modelIndex != MI_FIRETRUCK) {
+ // if (!isBike || GetMysteriousWheelRelatedThingBike(car) > 3)
+ // if (!isHeli && (!isPlane || car->GetWheelsOnGround())) {
+
+ CVector left = CrossProduct(car->GetForward(), CVector(0.0f, 0.0f, 1.0f));
+ left.Normalise();
+ CVector up = CrossProduct(left, car->GetForward());
+ up.Normalise();
+ float lookingUp = DotProduct(up, Front);
+ if (lookingUp > 0.0f) {
+ float v88 = Asin(Abs(Sin(Beta - (car->GetForward().Heading() - HALFPI))));
+ float v200;
+ if (v88 <= Atan2(carCol->boundingBox.max.x, -carCol->boundingBox.min.y)) {
+ v200 = (1.5f - carCol->boundingBox.min.y) / Cos(v88);
+ } else {
+ float a6g = 1.2f + carCol->boundingBox.max.x;
+ v200 = a6g / Cos(max(0.0f, HALFPI - v88));
+ }
+ maxAlphaAllowed = Cos(Beta - (car->GetForward().Heading() - HALFPI)) * Atan2(car->GetForward().z, car->GetForward().Magnitude2D())
+ + Atan2(TargetCoors.z - car->GetPosition().z + car->GetHeightAboveRoad(), v200 * 1.2f);
+
+ if (isCar && ((CAutomobile*)car)->m_nWheelsOnGround > 1 && Abs(DotProduct(car->m_vecTurnSpeed, car->GetForward())) < 0.05f) {
+ maxAlphaAllowed += Cos(Beta - (car->GetForward().Heading() - HALFPI) + HALFPI) * Atan2(car->GetRight().z, car->GetRight().Magnitude2D());
+ }
+ }
+ }
+
+ float targetAlpha = Asin(clamp(Front.z, -1.0f, 1.0f)) - zoomModeAlphaOffset;
+ if (targetAlpha <= maxAlphaAllowed) {
+ if (targetAlpha < -CARCAM_SET[camSetArrPos][14])
+ targetAlpha = -CARCAM_SET[camSetArrPos][14];
+ } else {
+ targetAlpha = maxAlphaAllowed;
+ }
+ float maxAlphaBlendAmount = CTimer::GetTimeStep() * CARCAM_SET[camSetArrPos][6];
+ float targetAlphaBlendAmount = (1.0f - pow(CARCAM_SET[camSetArrPos][5], CTimer::GetTimeStep())) * (targetAlpha - Alpha);
+ if (targetAlphaBlendAmount <= maxAlphaBlendAmount) {
+ if (targetAlphaBlendAmount < -maxAlphaBlendAmount)
+ targetAlphaBlendAmount = -maxAlphaBlendAmount;
+ } else {
+ targetAlphaBlendAmount = maxAlphaBlendAmount;
+ }
+
+ // Using GetCarGun(LR/UD) will give us same unprocessed RightStick value as SA
+ float stickX = -(pad->GetCarGunLeftRight());
+ float stickY = pad->GetCarGunUpDown();
+
+ // In SA this checks for m_bUseMouse3rdPerson so num2/num8 do not move camera when Keyboard & Mouse controls are used.
+ if (CCamera::m_bUseMouse3rdPerson)
+ stickY = 0.0f;
+
+ float xMovement = Abs(stickX) * (FOV / 80.0f * 5.f / 70.f) * stickX * 0.007f * 0.007f;
+ float yMovement = Abs(stickY) * (FOV / 80.0f * 3.f / 70.f) * stickY * 0.007f * 0.007f;
+
+ bool correctAlpha = true;
+ // if (SA checks if we aren't in work car, why?) {
+ if (!isCar || car->m_modelIndex != MI_YARDIE) {
+ correctAlpha = false;
+ }
+ else {
+ xMovement = 0.0f;
+ yMovement = 0.0f;
+ }
+ // } else
+ // yMovement = 0.0;
+
+ if (!nextDirectionIsForward) {
+ yMovement = 0.0;
+ xMovement = 0.0;
+ }
+
+ if (camSetArrPos == 0 || camSetArrPos == 7) {
+ // This is not working on cars as SA
+ // Because III/VC doesn't have any buttons tied to LeftStick if you're not in Classic Configuration, using Dodo or using GInput/Pad, so :shrug:
+ if (Abs(pad->GetSteeringUpDown()) > 120.0f) {
+ if (car->pDriver && car->pDriver->m_objective != OBJECTIVE_LEAVE_VEHICLE) {
+ yMovement += Abs(pad->GetSteeringUpDown()) * (FOV / 80.0f * 3.f / 70.f) * pad->GetSteeringUpDown() * 0.007f * 0.007f * 0.5;
+ }
+ }
+ }
+
+ if (yMovement > 0.0)
+ yMovement = yMovement * 0.5;
+
+ bool mouseChangesBeta = false;
+
+ // FIX: Disable mouse movement in drive-by, it's buggy. Original SA bug.
+ if (bFreeMouseCam && CCamera::m_bUseMouse3rdPerson && !pad->ArePlayerControlsDisabled() && nextDirectionIsForward) {
+ float mouseY = pad->GetMouseY() * 2.0f;
+ float mouseX = pad->GetMouseX() * -2.0f;
+
+ // If you want an ability to toggle free cam while steering with mouse, you can add an OR after DisableMouseSteering.
+ // There was a pad->NewState.m_bVehicleMouseLook in SA, which doesn't exists in III.
+
+ if ((mouseX != 0.0 || mouseY != 0.0) && (CVehicle::m_bDisableMouseSteering)) {
+ yMovement = mouseY * FOV / 80.0f * TheCamera.m_fMouseAccelHorzntl; // Same as SA, horizontal sensitivity.
+ BetaSpeed = 0.0;
+ AlphaSpeed = 0.0;
+ xMovement = mouseX * FOV / 80.0f * TheCamera.m_fMouseAccelHorzntl;
+ targetAlpha = Alpha;
+ stepsLeftToChangeBetaByMouse = 1.0f * 50.0f;
+ mouseChangesBeta = true;
+ } else if (stepsLeftToChangeBetaByMouse > 0.0f) {
+ // Finish rotation by decreasing speed when we stopped moving mouse
+ BetaSpeed = 0.0;
+ AlphaSpeed = 0.0;
+ yMovement = 0.0;
+ xMovement = 0.0;
+ targetAlpha = Alpha;
+ stepsLeftToChangeBetaByMouse = max(0.0f, stepsLeftToChangeBetaByMouse - CTimer::GetTimeStep());
+ mouseChangesBeta = true;
+ }
+ }
+
+ if (correctAlpha) {
+ if (nPreviousMode != MODE_CAM_ON_A_STRING)
+ alphaCorrected = false;
+
+ if (!alphaCorrected && Abs(zoomModeAlphaOffset + Alpha) > 0.05f) {
+ yMovement = (-zoomModeAlphaOffset - Alpha) * 0.05f;
+ } else
+ alphaCorrected = true;
+ }
+ float alphaSpeedFromStickY = yMovement * CARCAM_SET[camSetArrPos][12];
+ float betaSpeedFromStickX = xMovement * CARCAM_SET[camSetArrPos][12];
+
+ float newAngleSpeedMaxBlendAmount = CARCAM_SET[camSetArrPos][9];
+ float angleChangeStep = pow(CARCAM_SET[camSetArrPos][8], CTimer::GetTimeStep());
+ float targetBetaWithStickBlendAmount = betaSpeedFromStickX + (targetBeta - Beta) / max(CTimer::GetTimeStep(), 1.0f);
+
+ if (targetBetaWithStickBlendAmount < -newAngleSpeedMaxBlendAmount)
+ targetBetaWithStickBlendAmount = -newAngleSpeedMaxBlendAmount;
+ else if (targetBetaWithStickBlendAmount > newAngleSpeedMaxBlendAmount)
+ targetBetaWithStickBlendAmount = newAngleSpeedMaxBlendAmount;
+
+ float angleChangeStepLeft = 1.0f - angleChangeStep;
+ BetaSpeed = targetBetaWithStickBlendAmount * angleChangeStepLeft + angleChangeStep * BetaSpeed;
+ if (Abs(BetaSpeed) < 0.0001f)
+ BetaSpeed = 0.0f;
+
+ float betaChangePerFrame;
+ if (mouseChangesBeta)
+ betaChangePerFrame = betaSpeedFromStickX;
+ else
+ betaChangePerFrame = CTimer::GetTimeStep() * BetaSpeed;
+ Beta = betaChangePerFrame + Beta;
+
+ if (TheCamera.m_bJustCameOutOfGarage) {
+ float invHeading = Atan2(Front.y, Front.x);
+ if (invHeading < 0.0f)
+ invHeading += TWOPI;
+
+ Beta = invHeading + PI;
+ }
+
+ Beta = CGeneral::LimitRadianAngle(Beta);
+ if (Beta < 0.0f)
+ Beta += TWOPI;
+
+ if ((camSetArrPos <= 1 || camSetArrPos == 7) && targetAlpha < Alpha && carPosChange >= newDistance) {
+ if (isCar && ((CAutomobile*)car)->m_nWheelsOnGround > 1)
+ // || isBike && GetMysteriousWheelRelatedThingBike(car) > 1)
+ alphaSpeedFromStickY += (targetAlpha - Alpha) * 0.075f;
+ }
+
+ AlphaSpeed = angleChangeStepLeft * alphaSpeedFromStickY + angleChangeStep * AlphaSpeed;
+ float maxAlphaSpeed = newAngleSpeedMaxBlendAmount;
+ if (alphaSpeedFromStickY > 0.0f)
+ maxAlphaSpeed = maxAlphaSpeed * 0.5;
+
+ if (AlphaSpeed <= maxAlphaSpeed) {
+ float minAlphaSpeed = -maxAlphaSpeed;
+ if (AlphaSpeed < minAlphaSpeed)
+ AlphaSpeed = minAlphaSpeed;
+ } else {
+ AlphaSpeed = maxAlphaSpeed;
+ }
+
+ if (Abs(AlphaSpeed) < 0.0001f)
+ AlphaSpeed = 0.0f;
+
+ float alphaWithSpeedAccounted;
+ if (mouseChangesBeta) {
+ alphaWithSpeedAccounted = alphaSpeedFromStickY + targetAlpha;
+ Alpha += alphaSpeedFromStickY;
+ } else {
+ alphaWithSpeedAccounted = CTimer::GetTimeStep() * AlphaSpeed + targetAlpha;
+ Alpha += targetAlphaBlendAmount;
+ }
+
+ if (Alpha <= maxAlphaAllowed) {
+ float minAlphaAllowed = -CARCAM_SET[camSetArrPos][14];
+ if (minAlphaAllowed > Alpha) {
+ Alpha = minAlphaAllowed;
+ AlphaSpeed = 0.0f;
+ }
+ } else {
+ Alpha = maxAlphaAllowed;
+ AlphaSpeed = 0.0f;
+ }
+
+ // Prevent unsignificant angle changes
+ if (Abs(lastAlpha - Alpha) < 0.0001f)
+ Alpha = lastAlpha;
+
+ lastAlpha = Alpha;
+
+ if (Abs(lastBeta - Beta) < 0.0001f)
+ Beta = lastBeta;
+
+ lastBeta = Beta;
+
+ Front.x = -(cos(Beta) * cos(Alpha));
+ Front.y = -(sin(Beta) * cos(Alpha));
+ Front.z = sin(Alpha);
+ GetVectorsReadyForRW();
+ TheCamera.m_bCamDirectlyBehind = false;
+ TheCamera.m_bCamDirectlyInFront = false;
+
+ Source = TargetCoors - newDistance * Front;
+
+ m_cvecTargetCoorsForFudgeInter = TargetCoors;
+ m_aTargetHistoryPosThree = m_aTargetHistoryPosOne;
+ float nextAlpha = alphaWithSpeedAccounted + zoomModeAlphaOffset;
+ float nextFrontX = -(cos(Beta) * cos(nextAlpha));
+ float nextFrontY = -(sin(Beta) * cos(nextAlpha));
+ float nextFrontZ = sin(nextAlpha);
+
+ m_aTargetHistoryPosOne.x = TargetCoors.x - nextFrontX * nextDistance;
+ m_aTargetHistoryPosOne.y = TargetCoors.y - nextFrontY * nextDistance;
+ m_aTargetHistoryPosOne.z = TargetCoors.z - nextFrontZ * nextDistance;
+
+ m_aTargetHistoryPosTwo.x = TargetCoors.x - nextFrontX * newDistance;
+ m_aTargetHistoryPosTwo.y = TargetCoors.y - nextFrontY * newDistance;
+ m_aTargetHistoryPosTwo.z = TargetCoors.z - nextFrontZ * newDistance;
+
+ // SA calls SetColVarsVehicle in here
+ if (nextDirectionIsForward) {
+
+ // This is new in LCS!
+ float timestepFactor = Pow(0.99f, CTimer::GetTimeStep());
+ dontCollideWithCars = (timestepFactor * dontCollideWithCars) + ((1.0f - timestepFactor) * car->m_vecMoveSpeed.Magnitude());
+
+ // Move cam if on collision
+ CColPoint foundCol;
+ CEntity* foundEnt;
+ CWorld::pIgnoreEntity = CamTargetEntity;
+ if (CWorld::ProcessLineOfSight(TargetCoors, Source, foundCol, foundEnt, true, dontCollideWithCars < 0.1f, false, true, false, true, false)) {
+ float obstacleTargetDist = (TargetCoors - foundCol.point).Magnitude();
+ float obstacleCamDist = newDistance - obstacleTargetDist;
+ if (!foundEnt->IsPed() || obstacleCamDist <= 1.0f) {
+ Source = foundCol.point;
+ if (obstacleTargetDist < 1.2f) {
+ RwCameraSetNearClipPlane(Scene.camera, max(0.05f, obstacleTargetDist - 0.3f));
+ }
+ } else {
+ if (!CWorld::ProcessLineOfSight(foundCol.point, Source, foundCol, foundEnt, true, dontCollideWithCars < 0.1f, false, true, false, true, false)) {
+ float lessClip = obstacleCamDist - 0.35f;
+ if (lessClip <= 0.9f)
+ RwCameraSetNearClipPlane(Scene.camera, lessClip);
+ else
+ RwCameraSetNearClipPlane(Scene.camera, 0.9f);
+ } else {
+ obstacleTargetDist = (TargetCoors - foundCol.point).Magnitude();
+ Source = foundCol.point;
+ if (obstacleTargetDist < 1.2f) {
+ float lessClip = obstacleTargetDist - 0.3f;
+ if (lessClip >= 0.05f)
+ RwCameraSetNearClipPlane(Scene.camera, lessClip);
+ else
+ RwCameraSetNearClipPlane(Scene.camera, 0.05f);
+ }
+ }
+ }
+ }
+ CWorld::pIgnoreEntity = nil;
+ float nearClip = RwCameraGetNearClipPlane(Scene.camera);
+ float radius = Tan(DEGTORAD(FOV * 0.5f)) * CDraw::GetAspectRatio() * 1.1f;
+
+ // If we're seeing blue hell due to camera intersects some surface, fix it.
+ // SA and LCS have this unrolled.
+ for (int i = 0;
+ i <= 5 && CWorld::TestSphereAgainstWorld((nearClip * Front) + Source, radius * nearClip, nil, true, true, false, true, false, false);
+ i++) {
+
+ CVector surfaceCamDist = gaTempSphereColPoints->point - Source;
+ CVector frontButInvertedIfTouchesSurface = DotProduct(surfaceCamDist, Front) * Front;
+ float newNearClip = (surfaceCamDist - frontButInvertedIfTouchesSurface).Magnitude() / radius;
+
+ if (newNearClip > nearClip)
+ newNearClip = nearClip;
+ if (newNearClip < 0.1f)
+ newNearClip = 0.1f;
+ if (nearClip > newNearClip)
+ RwCameraSetNearClipPlane(Scene.camera, newNearClip);
+
+ if (newNearClip == 0.1f)
+ Source += (TargetCoors - Source) * 0.3f;
+
+ nearClip = RwCameraGetNearClipPlane(Scene.camera);
+ radius = Tan(DEGTORAD(FOV * 0.5f)) * CDraw::GetAspectRatio() * 1.1f;
+ }
+ }
+ TheCamera.m_bCamDirectlyBehind = false;
+ TheCamera.m_bCamDirectlyInFront = false;
+
+ // ------- LCS specific part starts
+
+ if (camSetArrPos == 5 && Source.z < 1.0f) // RC Bandit and Baron
+ Source.z = 1.0f;
+
+ // Obviously some specific place in LC
+ if (Source.x > 11.0f && Source.x < 91.0f) {
+ if (Source.y > -680.0f && Source.y < -600.0f && Source.z < 24.4f)
+ Source.z = 24.4f;
+ }
+
+ // CCam::FixSourceAboveWaterLevel
+ if (CameraTarget.z >= -2.0f) {
+ float level = -6000.0;
+ // +0.5f is needed for III
+ if (CWaterLevel::GetWaterLevelNoWaves(Source.x, Source.y, Source.z, &level)) {
+ if (Source.z < level + 0.5f)
+ Source.z = level + 0.5f;
+ }
+ }
+ Front = TargetCoors - Source;
+
+ // -------- LCS specific part ends
+
+ GetVectorsReadyForRW();
+ // SA
+ // gTargetCoordsForLookingBehind = TargetCoors;
+
+ // SA code from CAutomobile::TankControl/FireTruckControl.
+ if (car->m_modelIndex == MI_RHINO || car->m_modelIndex == MI_FIRETRUCK) {
+
+ float &carGunLR = ((CAutomobile*)car)->m_fCarGunLR;
+ CVector hi = Multiply3x3(Front, car->GetMatrix());
+
+ // III/VC's firetruck turret angle is reversed
+ float angleToFace = (car->m_modelIndex == MI_FIRETRUCK ? -hi.Heading() : hi.Heading());
+
+ if (angleToFace <= carGunLR + PI) {
+ if (angleToFace < carGunLR - PI)
+ angleToFace = angleToFace + TWOPI;
+ } else {
+ angleToFace = angleToFace - TWOPI;
+ }
+
+ float neededTurn = angleToFace - carGunLR;
+ float turnPerFrame = CTimer::GetTimeStep() * (car->m_modelIndex == MI_FIRETRUCK ? 0.05f : 0.015f);
+ if (neededTurn <= turnPerFrame) {
+ if (neededTurn < -turnPerFrame)
+ angleToFace = carGunLR - turnPerFrame;
+ } else {
+ angleToFace = turnPerFrame + carGunLR;
+ }
+
+ if (car->m_modelIndex == MI_RHINO && carGunLR != angleToFace) {
+ DMAudio.PlayOneShot(car->m_audioEntityId, SOUND_CAR_TANK_TURRET_ROTATE, Abs(angleToFace - carGunLR));
+ }
+ carGunLR = angleToFace;
+
+ if (carGunLR < -PI) {
+ carGunLR += TWOPI;
+ } else if (carGunLR > PI) {
+ carGunLR -= TWOPI;
+ }
+
+ // Because firetruk turret also has Y movement
+ if (car->m_modelIndex == MI_FIRETRUCK) {
+ float &carGunUD = ((CAutomobile*)car)->m_fCarGunUD;
+
+ float alphaToFace = Atan2(hi.z, hi.Magnitude2D()) + DEGTORAD(15.0f);
+ float neededAlphaTurn = alphaToFace - carGunUD;
+ float alphaTurnPerFrame = CTimer::GetTimeStep() * 0.02f;
+
+ if (neededAlphaTurn > alphaTurnPerFrame) {
+ neededTurn = alphaTurnPerFrame;
+ carGunUD = neededTurn + carGunUD;
+ } else {
+ if (neededAlphaTurn >= -alphaTurnPerFrame) {
+ carGunUD = alphaToFace;
+ } else {
+ carGunUD = carGunUD - alphaTurnPerFrame;
+ }
+ }
+
+ float turretMinY = -DEGTORAD(20.0f);
+ float turretMaxY = DEGTORAD(20.0f);
+ if (turretMinY <= carGunUD) {
+ if (carGunUD > turretMaxY)
+ carGunUD = turretMaxY;
+ } else {
+ carGunUD = turretMinY;
+ }
+ }
+ }
+}
#endif
STARTPATCHES
diff --git a/src/core/Camera.h b/src/core/Camera.h
index 3dc74fe7..f3e3e661 100644
--- a/src/core/Camera.h
+++ b/src/core/Camera.h
@@ -224,6 +224,7 @@ struct CCam
// custom stuff
void Process_FollowPed_Rotation(const CVector &CameraTarget, float TargetOrientation, float, float);
+ void Process_FollowCar_SA(const CVector &CameraTarget, float TargetOrientation, float, float);
};
static_assert(sizeof(CCam) == 0x1A4, "CCam: wrong size");
static_assert(offsetof(CCam, Alpha) == 0xA8, "CCam: error");
diff --git a/src/core/Frontend.cpp b/src/core/Frontend.cpp
index a469a215..4fefe9a9 100644
--- a/src/core/Frontend.cpp
+++ b/src/core/Frontend.cpp
@@ -64,7 +64,6 @@ bool &CMenuManager::m_bShutDownFrontEndRequested = *(bool*)0x95CD6A;
int8 &CMenuManager::m_PrefsUseWideScreen = *(int8*)0x95CD23;
int8 &CMenuManager::m_PrefsRadioStation = *(int8*)0x95CDA4;
-int8 &CMenuManager::m_bDisableMouseSteering = *(int8*)0x60252C; // 1
int32 &CMenuManager::m_PrefsBrightness = *(int32*)0x5F2E50; // 256
float &CMenuManager::m_PrefsLOD = *(float*)0x8F42C4;
int8 &CMenuManager::m_bFrontEnd_ReloadObrTxtGxt = *(int8*)0x628CFC;
@@ -97,6 +96,11 @@ int32 &JoyButtonJustClicked = *(int32*)0x628D10;
bool &holdingScrollBar = *(bool*)0x628D59;
//int32 *pControlTemp = 0;
+#ifndef MASTER
+bool CMenuManager::m_PrefsMarketing = false;
+bool CMenuManager::m_PrefsDisableTutorials = false;
+#endif // !MASTER
+
// 0x5F311C
const char* FrontendFilenames[][2] = {
{"fe2_mainpanel_ul", "" },
@@ -968,7 +972,7 @@ void CMenuManager::Draw()
rightText = TheText.Get(m_PrefsDMA ? "FEM_ON" : "FEM_OFF");
break;
case MENUACTION_MOUSESTEER:
- rightText = TheText.Get(m_bDisableMouseSteering ? "FEM_OFF" : "FEM_ON");
+ rightText = TheText.Get(CVehicle::m_bDisableMouseSteering ? "FEM_OFF" : "FEM_ON");
break;
}
@@ -1727,6 +1731,17 @@ void CMenuManager::InitialiseChangedLanguageSettings()
CTimer::Update();
CGame::frenchGame = false;
CGame::germanGame = false;
+#ifdef MORE_LANGUAGES
+ switch (CMenuManager::m_PrefsLanguage) {
+ case LANGUAGE_RUSSIAN:
+ CFont::ReloadFonts(FONT_LANGSET_RUSSIAN);
+ break;
+ default:
+ CFont::ReloadFonts(FONT_LANGSET_EFIGS);
+ break;
+ }
+#endif
+
switch (CMenuManager::m_PrefsLanguage) {
case LANGUAGE_FRENCH:
CGame::frenchGame = true;
@@ -1734,6 +1749,11 @@ void CMenuManager::InitialiseChangedLanguageSettings()
case LANGUAGE_GERMAN:
CGame::germanGame = true;
break;
+#ifdef MORE_LANGUAGES
+ case LANGUAGE_RUSSIAN:
+ CGame::russianGame = true;
+ break;
+#endif
default:
break;
}
@@ -2935,6 +2955,14 @@ CMenuManager::ProcessButtonPresses(void)
CMenuManager::InitialiseChangedLanguageSettings();
SaveSettings();
break;
+#ifdef MORE_LANGUAGES
+ case MENUACTION_LANG_RUS:
+ m_PrefsLanguage = LANGUAGE_RUSSIAN;
+ m_bFrontEnd_ReloadObrTxtGxt = true;
+ CMenuManager::InitialiseChangedLanguageSettings();
+ SaveSettings();
+ break;
+#endif
case MENUACTION_POPULATESLOTS_CHANGEMENU:
PcSaveHelper.PopulateSlotInfo();
@@ -3141,7 +3169,7 @@ CMenuManager::ProcessButtonPresses(void)
CMenuManager::m_ControlMethod = CONTROL_STANDART;
MousePointerStateHelper.bInvertVertically = false;
TheCamera.m_fMouseAccelHorzntl = 0.0025f;
- m_bDisableMouseSteering = true;
+ CVehicle::m_bDisableMouseSteering = true;
TheCamera.m_bHeadBob = false;
SaveSettings();
}
@@ -3460,7 +3488,7 @@ void CMenuManager::ProcessOnOffMenuOptions()
SaveSettings();
break;
case MENUACTION_MOUSESTEER:
- m_bDisableMouseSteering = !m_bDisableMouseSteering;
+ CVehicle::m_bDisableMouseSteering = !CVehicle::m_bDisableMouseSteering;
DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SUCCESS, 0);
SaveSettings();
break;
diff --git a/src/core/Frontend.h b/src/core/Frontend.h
index 3dbed164..30e4f652 100644
--- a/src/core/Frontend.h
+++ b/src/core/Frontend.h
@@ -51,6 +51,9 @@ enum eLanguages
LANGUAGE_GERMAN,
LANGUAGE_ITALIAN,
LANGUAGE_SPANISH,
+#ifdef MORE_LANGUAGES
+ LANGUAGE_RUSSIAN,
+#endif
};
enum eFrontendSprites
@@ -301,6 +304,9 @@ enum eMenuAction
MENUACTION_UNK108,
MENUACTION_UNK109,
MENUACTION_UNK110,
+#ifdef MORE_LANGUAGES
+ MENUACTION_LANG_RUS,
+#endif
};
enum eCheckHover
@@ -473,7 +479,6 @@ public:
static int32 &m_ControlMethod;
static int8 &m_PrefsDMA;
static int32 &m_PrefsLanguage;
- static int8 &m_bDisableMouseSteering;
static int32 &m_PrefsBrightness;
static float &m_PrefsLOD;
static int8 &m_bFrontEnd_ReloadObrTxtGxt;
@@ -492,6 +497,12 @@ public:
static int32 &sthWithButtons;
static int32 &sthWithButtons2;
+#ifndef MASTER
+ static bool m_PrefsMarketing;
+ static bool m_PrefsDisableTutorials;
+#endif // !MASTER
+
+
public:
static void BuildStatLine(char *text, void *stat, uint8 aFloat, void *stat2);
static void CentreMousePointer();
diff --git a/src/core/Game.cpp b/src/core/Game.cpp
index 3306277c..8571e93e 100644
--- a/src/core/Game.cpp
+++ b/src/core/Game.cpp
@@ -99,6 +99,9 @@ bool &CGame::germanGame = *(bool*)0x95CD1E;
bool &CGame::noProstitutes = *(bool*)0x95CDCF;
bool &CGame::playingIntro = *(bool*)0x95CDC2;
char *CGame::aDatFile = (char*)0x773A48;
+#ifdef MORE_LANGUAGES
+bool CGame::russianGame = false;
+#endif
int &gameTxdSlot = *(int*)0x628D88;
diff --git a/src/core/Game.h b/src/core/Game.h
index b6728a2f..318ff54b 100644
--- a/src/core/Game.h
+++ b/src/core/Game.h
@@ -16,6 +16,9 @@ public:
static bool &nastyGame;
static bool &frenchGame;
static bool &germanGame;
+#ifdef MORE_LANGUAGES
+ static bool russianGame;
+#endif
static bool &noProstitutes;
static bool &playingIntro;
static char *aDatFile; //[32];
diff --git a/src/core/MenuScreens.h b/src/core/MenuScreens.h
index 427d01bf..ace6a719 100644
--- a/src/core/MenuScreens.h
+++ b/src/core/MenuScreens.h
@@ -65,6 +65,9 @@ const CMenuScreen aScreens[] = {
MENUACTION_LANG_GER, "FEL_GER", SAVESLOT_NONE, MENUPAGE_NONE,
MENUACTION_LANG_ITA, "FEL_ITA", SAVESLOT_NONE, MENUPAGE_NONE,
MENUACTION_LANG_SPA, "FEL_SPA", SAVESLOT_NONE, MENUPAGE_NONE,
+#ifdef MORE_LANGUAGES
+ MENUACTION_LANG_RUS, "FEL_RUS", SAVESLOT_NONE, MENUPAGE_NONE,
+#endif
MENUACTION_CHANGEMENU, "FEDS_TB", SAVESLOT_NONE, MENUPAGE_NONE,
},
diff --git a/src/core/Timer.cpp b/src/core/Timer.cpp
index a46e1d8b..18d6b6a3 100644
--- a/src/core/Timer.cpp
+++ b/src/core/Timer.cpp
@@ -214,6 +214,11 @@ void CTimer::EndUserPause(void)
m_UserPause = false;
}
+uint32 CTimer::GetCyclesPerFrame()
+{
+ return 20;
+}
+
#if 1
STARTPATCHES
InjectHook(0x4ACE60, CTimer::Initialise, PATCH_JUMP);
diff --git a/src/core/Timer.h b/src/core/Timer.h
index ef525be7..2498ec8a 100644
--- a/src/core/Timer.h
+++ b/src/core/Timer.h
@@ -34,6 +34,7 @@ public:
static void SetPreviousTimeInMilliseconds(uint32 t) { m_snPreviousTimeInMilliseconds = t; }
static const float &GetTimeScale(void) { return ms_fTimeScale; }
static void SetTimeScale(float ts) { ms_fTimeScale = ts; }
+ static uint32 GetCyclesPerFrame();
static bool GetIsPaused() { return m_UserPause || m_CodePause; }
static bool GetIsUserPaused() { return m_UserPause; }
diff --git a/src/core/config.h b/src/core/config.h
index 0d39550a..58885e57 100644
--- a/src/core/config.h
+++ b/src/core/config.h
@@ -169,12 +169,14 @@ enum Config {
// not in any game
# define NASTY_GAME // nasty game for all languages
# define NO_MOVIES // disable intro videos
-# define NO_CDCHECK
+# define NO_CDCHECK
# define CHATTYSPLASH // print what the game is loading
+//# define TIMEBARS // print debug timers
#endif
#define FIX_BUGS // fixes bugs that we've came across during reversing, TODO: use this more
#define TOGGLEABLE_BETA_FEATURES // toggleable from debug menu. not too many things
+#define MORE_LANGUAGES // Add more translations to the game
// Pad
#define XINPUT
diff --git a/src/core/main.cpp b/src/core/main.cpp
index 50543b1e..674527f5 100644
--- a/src/core/main.cpp
+++ b/src/core/main.cpp
@@ -51,6 +51,7 @@
#include "Script.h"
#include "Debug.h"
#include "Console.h"
+#include "timebars.h"
#define DEFAULT_VIEWWINDOW (Tan(DEGTORAD(CDraw::GetFOV() * 0.5f)))
@@ -141,6 +142,11 @@ Idle(void *arg)
#endif
CTimer::Update();
+
+#ifdef TIMEBARS
+ tbInit();
+#endif
+
CSprite2d::InitPerFrame();
CFont::InitPerFrame();
@@ -155,16 +161,39 @@ Idle(void *arg)
FrontEndMenuManager.Process();
} else {
CPointLights::InitPerFrame();
+#ifdef TIMEBARS
+ tbStartTimer(0, "CGame::Process");
+#endif
CGame::Process();
+#ifdef TIMEBARS
+ tbEndTimer("CGame::Process");
+ tbStartTimer(0, "DMAudio.Service");
+#endif
DMAudio.Service();
+
+#ifdef TIMEBARS
+ tbEndTimer("DMAudio.Service");
+#endif
}
if (RsGlobal.quit)
return;
#else
CPointLights::InitPerFrame();
+#ifdef TIMEBARS
+ tbStartTimer(0, "CGame::Process");
+#endif
CGame::Process();
+#ifdef TIMEBARS
+ tbEndTimer("CGame::Process");
+ tbStartTimer(0, "DMAudio.Service");
+#endif
+
DMAudio.Service();
+
+#ifdef TIMEBARS
+ tbEndTimer("DMAudio.Service");
+#endif
#endif
if(CGame::bDemoMode && CTimer::GetTimeInMilliseconds() > (3*60 + 30)*1000 && !CCutsceneMgr::IsCutsceneProcessing()){
@@ -192,8 +221,18 @@ Idle(void *arg)
RsMouseSetPos(&pos);
}
#endif
+#ifdef TIMEBARS
+ tbStartTimer(0, "CnstrRenderList");
+#endif
CRenderer::ConstructRenderList();
+#ifdef TIMEBARS
+ tbEndTimer("CnstrRenderList");
+ tbStartTimer(0, "PreRender");
+#endif
CRenderer::PreRender();
+#ifdef TIMEBARS
+ tbEndTimer("PreRender");
+#endif
if(CWeather::LightningFlash && !CCullZones::CamNoRain()){
if(!DoRWStuffStartOfFrame_Horizon(255, 255, 255, 255, 255, 255, 255))
@@ -211,16 +250,31 @@ Idle(void *arg)
RwCameraSetFarClipPlane(Scene.camera, CTimeCycle::GetFarClip());
RwCameraSetFogDistance(Scene.camera, CTimeCycle::GetFogStart());
+#ifdef TIMEBARS
+ tbStartTimer(0, "RenderScene");
+#endif
RenderScene();
+#ifdef TIMEBARS
+ tbEndTimer("RenderScene");
+#endif
RenderDebugShit();
RenderEffects();
+#ifdef TIMEBARS
+ tbStartTimer(0, "RenderMotionBlur");
+#endif
if((TheCamera.m_BlurType == MBLUR_NONE || TheCamera.m_BlurType == MBLUR_NORMAL) &&
TheCamera.m_ScreenReductionPercentage > 0.0f)
TheCamera.SetMotionBlurAlpha(150);
TheCamera.RenderMotionBlur();
-
+#ifdef TIMEBARS
+ tbEndTimer("RenderMotionBlur");
+ tbStartTimer(0, "Render2dStuff");
+#endif
Render2dStuff();
+#ifdef TIMEBARS
+ tbEndTimer("Render2dStuff");
+#endif
}else{
float viewWindow = DEFAULT_VIEWWINDOW;
#ifdef ASPECT_RATIO_SCALE
@@ -238,10 +292,29 @@ Idle(void *arg)
if (FrontEndMenuManager.m_bMenuActive)
DefinedState();
#endif
+#ifdef TIMEBARS
+ tbStartTimer(0, "RenderMenus");
+#endif
RenderMenus();
+#ifdef TIMEBARS
+ tbEndTimer("RenderMenus");
+ tbStartTimer(0, "DoFade");
+#endif
DoFade();
+#ifdef TIMEBARS
+ tbEndTimer("DoFade");
+ tbStartTimer(0, "Render2dStuff-Fade");
+#endif
Render2dStuffAfterFade();
+#ifdef TIMEBARS
+ tbEndTimer("Render2dStuff-Fade");
+#endif
CCredits::Render();
+
+#ifdef TIMEBARS
+ tbDisplay();
+#endif
+
DoRWStuffEndOfFrame();
// if(g_SlowMode)
diff --git a/src/core/re3.cpp b/src/core/re3.cpp
index 500bf230..05d28167 100644
--- a/src/core/re3.cpp
+++ b/src/core/re3.cpp
@@ -373,8 +373,10 @@ DebugMenuPopulate(void)
extern bool PrintDebugCode;
extern int16 &DebugCamMode;
#ifdef FREE_CAM
- extern bool bFreeCam;
- DebugMenuAddVarBool8("Cam", "Free Cam", (int8*)&bFreeCam, nil);
+ extern bool bFreePadCam;
+ extern bool bFreeMouseCam;
+ DebugMenuAddVarBool8("Cam", "Free Gamepad Cam", (int8*)&bFreePadCam, nil);
+ DebugMenuAddVarBool8("Cam", "Free Mouse Cam", (int8*)&bFreeMouseCam, nil);
#endif
DebugMenuAddVarBool8("Cam", "Print Debug Code", (int8*)&PrintDebugCode, nil);
DebugMenuAddVar("Cam", "Cam Mode", &DebugCamMode, nil, 1, 0, CCam::MODE_EDITOR, nil);
diff --git a/src/core/timebars.cpp b/src/core/timebars.cpp
new file mode 100644
index 00000000..30421731
--- /dev/null
+++ b/src/core/timebars.cpp
@@ -0,0 +1,121 @@
+#ifndef MASTER
+#include "common.h"
+#include "Font.h"
+#include "Frontend.h"
+#include "Timer.h"
+#include "Text.h"
+
+#define MAX_TIMERS (50)
+#define MAX_MS_COLLECTED (40)
+
+// enables frame time output
+#define FRAMETIME
+
+struct sTimeBar
+{
+ char name[20];
+ float startTime;
+ float endTime;
+ int32 unk;
+};
+
+struct
+{
+ sTimeBar Timers[MAX_TIMERS];
+ uint32 count;
+} TimerBar;
+float MaxTimes[MAX_TIMERS];
+float MaxFrameTime;
+
+uint32 curMS;
+uint32 msCollected[MAX_MS_COLLECTED];
+#ifdef FRAMETIME
+float FrameInitTime;
+#endif
+
+void tbInit()
+{
+ TimerBar.count = 0;
+ uint32 i = CTimer::GetFrameCounter() & 0x7F;
+ if (i == 0) {
+ do
+ MaxTimes[i++] = 0.0f;
+ while (i != MAX_TIMERS);
+#ifdef FRAMETIME
+ MaxFrameTime = 0.0f;
+#endif
+ }
+#ifdef FRAMETIME
+ FrameInitTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame();
+#endif
+}
+
+void tbStartTimer(int32 unk, char *name)
+{
+ strcpy(TimerBar.Timers[TimerBar.count].name, name);
+ TimerBar.Timers[TimerBar.count].unk = unk;
+ TimerBar.Timers[TimerBar.count].startTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame();
+ TimerBar.count++;
+}
+
+void tbEndTimer(char* name)
+{
+ uint32 n = 1500;
+ for (uint32 i = 0; i < TimerBar.count; i++) {
+ if (strcmp(name, TimerBar.Timers[i].name) == 0)
+ n = i;
+ }
+ assert(n != 1500);
+ TimerBar.Timers[n].endTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame();
+}
+
+float Diag_GetFPS()
+{
+ return 39000.0f / (msCollected[(curMS - 1) % MAX_MS_COLLECTED] - msCollected[curMS % MAX_MS_COLLECTED]);
+}
+
+void tbDisplay()
+{
+ char temp[200];
+ wchar wtemp[200];
+
+#ifdef FRAMETIME
+ float FrameEndTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerFrame();
+#endif
+
+ msCollected[(curMS++) % MAX_MS_COLLECTED] = RsTimer();
+ CFont::SetBackgroundOff();
+ CFont::SetBackgroundColor(CRGBA(0, 0, 0, 128));
+ CFont::SetScale(0.48f, 1.12f);
+ CFont::SetCentreOff();
+ CFont::SetJustifyOff();
+ CFont::SetWrapx(640.0f);
+ CFont::SetRightJustifyOff();
+ CFont::SetPropOn();
+ CFont::SetFontStyle(FONT_BANK);
+ sprintf(temp, "FPS: %.2f", Diag_GetFPS());
+ AsciiToUnicode(temp, wtemp);
+ CFont::SetColor(CRGBA(255, 255, 255, 255));
+ if (!CMenuManager::m_PrefsMarketing || !CMenuManager::m_PrefsDisableTutorials) {
+ CFont::PrintString(RsGlobal.maximumWidth * (4.0f / DEFAULT_SCREEN_WIDTH), RsGlobal.maximumHeight * (4.0f / DEFAULT_SCREEN_HEIGHT), wtemp);
+
+#ifndef FINAL
+ // Timers output (my own implementation)
+ for (uint32 i = 0; i < TimerBar.count; i++) {
+ MaxTimes[i] = max(MaxTimes[i], TimerBar.Timers[i].endTime - TimerBar.Timers[i].startTime);
+ sprintf(temp, "%s: %.2f", &TimerBar.Timers[i].name[0], MaxTimes[i]);
+ AsciiToUnicode(temp, wtemp);
+ CFont::PrintString(RsGlobal.maximumWidth * (4.0f / DEFAULT_SCREEN_WIDTH), RsGlobal.maximumHeight * ((8.0f * (i + 2)) / DEFAULT_SCREEN_HEIGHT), wtemp);
+ }
+
+#ifdef FRAMETIME
+ MaxFrameTime = max(MaxFrameTime, FrameEndTime - FrameInitTime);
+ sprintf(temp, "Frame Time: %.2f", MaxFrameTime);
+ AsciiToUnicode(temp, wtemp);
+
+ CFont::PrintString(RsGlobal.maximumWidth * (4.0f / DEFAULT_SCREEN_WIDTH), RsGlobal.maximumHeight * ((8.0f * (TimerBar.count + 4)) / DEFAULT_SCREEN_HEIGHT), wtemp);
+#endif // FRAMETIME
+#endif // !FINAL
+ }
+}
+#endif // !MASTER \ No newline at end of file
diff --git a/src/core/timebars.h b/src/core/timebars.h
new file mode 100644
index 00000000..8ffccd8e
--- /dev/null
+++ b/src/core/timebars.h
@@ -0,0 +1,6 @@
+#pragma once
+
+void tbInit();
+void tbStartTimer(int32, char*);
+void tbEndTimer(char*);
+void tbDisplay(); \ No newline at end of file
diff --git a/src/peds/Ped.cpp b/src/peds/Ped.cpp
index f43feae5..264fa669 100644
--- a/src/peds/Ped.cpp
+++ b/src/peds/Ped.cpp
@@ -59,6 +59,10 @@
#define CAN_SEE_ENTITY_ANGLE_THRESHOLD DEGTORAD(60.0f)
+#ifdef FREE_CAM
+extern bool bFreeMouseCam;
+#endif
+
CPed *gapTempPedList[50];
uint16 gnNumTempPedList;
@@ -807,6 +811,10 @@ CPed::IsPedInControl(void)
bool
CPed::CanStrafeOrMouseControl(void)
{
+#ifdef FREE_CAM
+ if (bFreeMouseCam)
+ return false;
+#endif
return m_nPedState == PED_NONE || m_nPedState == PED_IDLE || m_nPedState == PED_FLEE_POS || m_nPedState == PED_FLEE_ENTITY ||
m_nPedState == PED_ATTACK || m_nPedState == PED_FIGHT || m_nPedState == PED_AIM_GUN || m_nPedState == PED_JUMP;
}
@@ -6984,7 +6992,11 @@ CPed::FinishLaunchCB(CAnimBlendAssociation *animAssoc, void *arg)
#endif
) {
+#ifdef FREE_CAM
+ if (TheCamera.Cams[0].Using3rdPersonMouseCam() && !bFreeMouseCam) {
+#else
if (TheCamera.Cams[0].Using3rdPersonMouseCam()) {
+#endif
float fpsAngle = ped->WorkOutHeadingForMovingFirstPerson(ped->m_fRotationCur);
ped->m_vecMoveSpeed.x = -velocityFromAnim * Sin(fpsAngle);
ped->m_vecMoveSpeed.y = velocityFromAnim * Cos(fpsAngle);
diff --git a/src/peds/PlayerPed.cpp b/src/peds/PlayerPed.cpp
index 5275f716..cd2cac23 100644
--- a/src/peds/PlayerPed.cpp
+++ b/src/peds/PlayerPed.cpp
@@ -18,6 +18,10 @@
#define PAD_MOVE_TO_GAME_WORLD_MOVE 60.0f
+#ifdef FREE_CAM
+extern bool bFreeMouseCam;
+#endif
+
CPlayerPed::~CPlayerPed()
{
delete m_pWanted;
@@ -65,7 +69,7 @@ void CPlayerPed::ClearWeaponTarget()
TheCamera.ClearPlayerWeaponMode();
CWeaponEffects::ClearCrossHair();
}
- ClearPointGunAt();
+ ClearPointGunAt();
}
void
@@ -688,7 +692,14 @@ CPlayerPed::PlayerControl1stPersonRunAround(CPad *padUsed)
float padMove = CVector2D(leftRight, upDown).Magnitude();
float padMoveInGameUnit = padMove / PAD_MOVE_TO_GAME_WORLD_MOVE;
if (padMoveInGameUnit > 0.0f) {
+#ifdef FREE_CAM
+ if (!bFreeMouseCam)
+ m_fRotationDest = CGeneral::LimitRadianAngle(TheCamera.Orientation);
+ else
+ m_fRotationDest = CGeneral::GetRadianAngleBetweenPoints(0.0f, 0.0f, -leftRight, upDown) - TheCamera.Orientation;
+#else
m_fRotationDest = CGeneral::LimitRadianAngle(TheCamera.Orientation);
+#endif
m_fMoveSpeed = min(padMoveInGameUnit, 0.07f * CTimer::GetTimeStep() + m_fMoveSpeed);
} else {
m_fMoveSpeed = 0.0f;
@@ -981,6 +992,12 @@ CPlayerPed::ProcessPlayerWeapon(CPad *padUsed)
if (padUsed->TargetJustDown()) {
SetStoredState();
m_nPedState = PED_SNIPER_MODE;
+#ifdef FREE_CAM
+ if (bFreeMouseCam && TheCamera.Cams[0].Using3rdPersonMouseCam()) {
+ m_fRotationCur = CGeneral::LimitRadianAngle(-TheCamera.Orientation);
+ SetHeading(m_fRotationCur);
+ }
+#endif
if (GetWeapon()->m_eWeaponType == WEAPONTYPE_ROCKETLAUNCHER)
TheCamera.SetNewPlayerWeaponMode(CCam::MODE_ROCKETLAUNCHER, 0, 0);
else if (GetWeapon()->m_eWeaponType == WEAPONTYPE_SNIPERRIFLE)
@@ -1000,7 +1017,12 @@ CPlayerPed::ProcessPlayerWeapon(CPad *padUsed)
if (padUsed->GetWeapon() && m_nMoveState != PEDMOVE_SPRINT) {
if (m_nSelectedWepSlot == m_currentWeapon) {
if (m_pPointGunAt) {
- SetAttack(m_pPointGunAt);
+#ifdef FREE_CAM
+ if (bFreeMouseCam && weaponInfo->m_eWeaponFire == WEAPON_FIRE_MELEE && m_fMoveSpeed < 1.0f)
+ StartFightAttack(padUsed->GetWeapon());
+ else
+#endif
+ SetAttack(m_pPointGunAt);
} else if (m_currentWeapon != WEAPONTYPE_UNARMED) {
if (m_nPedState == PED_ATTACK) {
if (padUsed->WeaponJustDown()) {
@@ -1027,11 +1049,65 @@ CPlayerPed::ProcessPlayerWeapon(CPad *padUsed)
bIsAttacking = false;
}
}
+
+#ifdef FREE_CAM
+ // Rotate player/arm when shooting. We don't have auto-rotation anymore
+ if (CCamera::m_bUseMouse3rdPerson && bFreeMouseCam &&
+ m_nSelectedWepSlot == m_currentWeapon && m_nMoveState != PEDMOVE_SPRINT) {
+
+ // Weapons except throwable and melee ones
+ if (weaponInfo->m_bCanAim || weaponInfo->m_b1stPerson || weaponInfo->m_bExpands) {
+ if ((padUsed->GetTarget() && weaponInfo->m_bCanAimWithArm) || padUsed->GetWeapon()) {
+ float limitedCam = CGeneral::LimitRadianAngle(-TheCamera.Orientation);
+
+ // On this one we can rotate arm.
+ if (weaponInfo->m_bCanAimWithArm) {
+ if (!padUsed->GetWeapon()) { // making this State != ATTACK still stops it after attack. Re-start it immediately!
+ SetPointGunAt(nil);
+ bIsPointingGunAt = false; // to not stop after attack
+ }
+
+ SetLookFlag(limitedCam, true);
+ SetAimFlag(limitedCam);
+#ifdef VC_PED_PORTS
+ SetLookTimer(INT_MAX); // removing this makes head move for real, but I experinced some bugs.
+#endif
+ } else {
+ m_fRotationDest = limitedCam;
+ m_headingRate = 50.0f;
+
+ // Anim. fix for shotgun, ak47 and m16 (we must finish rot. it quickly)
+ if (weaponInfo->m_bCanAim && padUsed->WeaponJustDown()) {
+ m_fRotationCur = CGeneral::LimitRadianAngle(m_fRotationCur);
+ float limitedRotDest = m_fRotationDest;
+
+ if (m_fRotationCur - PI > m_fRotationDest) {
+ limitedRotDest += 2 * PI;
+ } else if (PI + m_fRotationCur < m_fRotationDest) {
+ limitedRotDest -= 2 * PI;
+ }
+
+ m_fRotationCur += (limitedRotDest - m_fRotationCur) / 2;
+ }
+ }
+ } else if (weaponInfo->m_bCanAimWithArm)
+ ClearPointGunAt();
+ else
+ RestoreHeadingRate();
+ }
+ }
+#endif
+
if (padUsed->GetTarget() && m_nSelectedWepSlot == m_currentWeapon && m_nMoveState != PEDMOVE_SPRINT) {
if (m_pPointGunAt) {
// what??
if (!m_pPointGunAt
- || CCamera::m_bUseMouse3rdPerson || m_pPointGunAt->IsPed() && ((CPed*)m_pPointGunAt)->bInVehicle) {
+#ifdef FREE_CAM
+ || (!bFreeMouseCam && CCamera::m_bUseMouse3rdPerson)
+#else
+ || CCamera::m_bUseMouse3rdPerson
+#endif
+ || m_pPointGunAt->IsPed() && ((CPed*)m_pPointGunAt)->bInVehicle) {
ClearWeaponTarget();
return;
}
@@ -1047,7 +1123,12 @@ CPlayerPed::ProcessPlayerWeapon(CPad *padUsed)
}
TheCamera.SetNewPlayerWeaponMode(CCam::MODE_SYPHON, 0, 0);
TheCamera.UpdateAimingCoors(m_pPointGunAt->GetPosition());
- } else if (weaponInfo->m_bCanAim && !CCamera::m_bUseMouse3rdPerson) {
+ }
+#ifdef FREE_CAM
+ else if ((bFreeMouseCam && weaponInfo->m_eWeaponFire == WEAPON_FIRE_MELEE) || (weaponInfo->m_bCanAim && !CCamera::m_bUseMouse3rdPerson)) {
+#else
+ else if (weaponInfo->m_bCanAim && !CCamera::m_bUseMouse3rdPerson) {
+#endif
if (padUsed->TargetJustDown())
FindWeaponLockOnTarget();
}
diff --git a/src/render/Font.cpp b/src/render/Font.cpp
index d7b4b5d8..0d79eee3 100644
--- a/src/render/Font.cpp
+++ b/src/render/Font.cpp
@@ -1,515 +1,636 @@
-#include "common.h"
-#include "patcher.h"
-#include "Sprite2d.h"
-#include "TxdStore.h"
-#include "Font.h"
-
-CFontDetails &CFont::Details = *(CFontDetails*)0x8F317C;
-int16 &CFont::NewLine = *(int16*)0x95CC94;
-CSprite2d *CFont::Sprite = (CSprite2d*)0x95CC04;
-
-int16 CFont::Size[3][193] = {
- {
- 13, 12, 31, 35, 23, 35, 31, 9, 14, 15, 25, 30, 11, 17, 13, 31,
- 23, 16, 22, 21, 24, 23, 23, 20, 23, 22, 10, 35, 26, 26, 26, 26,
- 30, 26, 24, 23, 24, 22, 21, 24, 26, 10, 20, 26, 22, 29, 26, 25,
- 23, 25, 24, 24, 22, 25, 24, 29, 29, 23, 25, 37, 22, 37, 35, 37,
- 35, 21, 22, 21, 21, 22, 13, 22, 21, 10, 16, 22, 11, 32, 21, 21,
- 23, 22, 16, 20, 14, 21, 20, 30, 25, 21, 21, 33, 33, 33, 33, 35,
- 27, 27, 27, 27, 32, 24, 23, 23, 23, 23, 11, 11, 11, 11, 26, 26,
- 26, 26, 26, 26, 26, 25, 26, 21, 21, 21, 21, 32, 23, 22, 22, 22,
- 22, 11, 11, 11, 11, 22, 22, 22, 22, 22, 22, 22, 22, 26, 21, 24,
- 12, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
- 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 18, 26, 26,
- 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
- 20
- },
-
- {
- 13, 9, 21, 35, 23, 35, 35, 11, 35, 35, 25, 35, 11, 17, 13, 33,
- 28, 14, 22, 21, 24, 23, 23, 21, 23, 22, 10, 35, 13, 35, 13, 33,
- 5, 25, 22, 23, 24, 21, 21, 24, 24, 9, 20, 24, 21, 27, 25, 25,
- 22, 25, 23, 20, 23, 23, 23, 31, 23, 23, 23, 37, 33, 37, 35, 37,
- 35, 21, 19, 19, 21, 19, 17, 21, 21, 8, 17, 18, 14, 24, 21, 21,
- 20, 22, 19, 20, 20, 19, 20, 26, 21, 20, 21, 33, 33, 33, 33, 35,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 16
- },
-
- {
- 15, 14, 16, 25, 19, 26, 22, 11, 18, 18, 27, 26, 13, 19, 9, 27,
- 19, 18, 19, 19, 22, 19, 20, 18, 19, 20, 12, 32, 15, 32, 15, 35,
- 15, 19, 19, 19, 19, 19, 16, 19, 20, 9, 19, 20, 14, 29, 19, 20,
- 19, 19, 19, 19, 21, 19, 20, 32, 20, 19, 19, 33, 31, 39, 37, 39,
- 37, 21, 21, 21, 23, 21, 19, 23, 23, 10, 19, 20, 16, 26, 23, 23,
- 20, 20, 20, 22, 21, 22, 22, 26, 22, 22, 23, 35, 35, 35, 35, 37,
- 19, 19, 19, 19, 29, 19, 19, 19, 19, 19, 9, 9, 9, 9, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 30, 19, 19, 19, 19,
- 19, 10, 10, 10, 10, 19, 19, 19, 19, 19, 19, 19, 19, 19, 23, 35,
- 12, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 11, 19, 19,
- 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19
- }
-};
-
-uint16 foreign_table[128] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 177, 0, 0, 0, 0, 0, 95, 0, 0, 0, 0, 175,
- 128, 129, 130, 0, 131, 0, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
- 0, 173, 142, 143, 144, 0, 145, 0, 0, 146, 147, 148, 149, 0, 0, 150,
- 151, 152, 153, 0, 154, 0, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
- 0, 174, 165, 166, 167, 0, 168, 0, 0, 169, 170, 171, 172, 0, 0, 0,
-};
-
-void
-CFont::Initialise(void)
-{
- int slot;
-
- slot = CTxdStore::AddTxdSlot("fonts");
- CTxdStore::LoadTxd(slot, "MODELS/FONTS.TXD");
- CTxdStore::AddRef(slot);
- CTxdStore::PushCurrentTxd();
- CTxdStore::SetCurrentTxd(slot);
- Sprite[0].SetTexture("font2", "font2_mask");
- Sprite[1].SetTexture("pager", "pager_mask");
- Sprite[2].SetTexture("font1", "font1_mask");
- SetScale(1.0f, 1.0f);
- SetSlantRefPoint(SCREEN_WIDTH, 0.0f);
- SetSlant(0.0f);
- SetColor(CRGBA(0xFF, 0xFF, 0xFF, 0));
- SetJustifyOff();
- SetCentreOff();
- SetWrapx(640.0f);
- SetCentreSize(640.0f);
- SetBackgroundOff();
- SetBackgroundColor(CRGBA(0x80, 0x80, 0x80, 0x80));
- SetBackGroundOnlyTextOff();
- SetPropOn();
- SetFontStyle(FONT_BANK);
- SetRightJustifyWrap(0.0f);
- SetAlphaFade(255.0f);
- SetDropShadowPosition(0);
- CTxdStore::PopCurrentTxd();
-}
-
-void
-CFont::Shutdown(void)
-{
- Sprite[0].Delete();
- Sprite[1].Delete();
- Sprite[2].Delete();
- CTxdStore::RemoveTxdSlot(CTxdStore::FindTxdSlot("fonts"));
-}
-
-void
-CFont::InitPerFrame(void)
-{
- Details.bank = CSprite2d::GetBank(30, Sprite[0].m_pTexture);
- CSprite2d::GetBank(15, Sprite[1].m_pTexture);
- CSprite2d::GetBank(15, Sprite[2].m_pTexture);
- SetDropShadowPosition(0);
- NewLine = 0;
-}
-
-void
-CFont::PrintChar(float x, float y, uint16 c)
-{
- if(x <= 0.0f || x > SCREEN_WIDTH ||
- y <= 0.0f || y > SCREEN_HEIGHT) // BUG: game uses SCREENW again
- return;
-
- float w = GetCharacterWidth(c) / 32.0f;
- float xoff = c & 0xF;
- float yoff = c >> 4;
-
- if(Details.style == FONT_BANK || Details.style == FONT_HEADING){
- if(Details.dropShadowPosition != 0){
- CSprite2d::AddSpriteToBank(Details.bank + Details.style, // BUG: game doesn't add bank
- CRect(x + SCREEN_SCALE_X(Details.dropShadowPosition),
- y + SCREEN_SCALE_Y(Details.dropShadowPosition),
- x + SCREEN_SCALE_X(Details.dropShadowPosition) + 32.0f * Details.scaleX * 1.0f,
- y + SCREEN_SCALE_Y(Details.dropShadowPosition) + 40.0f * Details.scaleY * 0.5f),
- Details.dropColor,
- xoff/16.0f, yoff/12.8f,
- (xoff+1.0f)/16.0f - 0.001f, yoff/12.8f,
- xoff/16.0f, (yoff+1.0f)/12.8f,
- (xoff+1.0f)/16.0f - 0.001f, (yoff+1.0f)/12.8f - 0.0001f);
- }
- CSprite2d::AddSpriteToBank(Details.bank + Details.style, // BUG: game doesn't add bank
- CRect(x, y,
- x + 32.0f * Details.scaleX * 1.0f,
- y + 40.0f * Details.scaleY * 0.5f),
- Details.color,
- xoff/16.0f, yoff/12.8f,
- (xoff+1.0f)/16.0f - 0.001f, yoff/12.8f,
- xoff/16.0f, (yoff+1.0f)/12.8f - 0.002f,
- (xoff+1.0f)/16.0f - 0.001f, (yoff+1.0f)/12.8f - 0.002f);
- }else{
- CSprite2d::AddSpriteToBank(Details.bank + Details.style, // BUG: game doesn't add bank
- CRect(x, y,
- x + 32.0f * Details.scaleX * w,
- y + 32.0f * Details.scaleY * 0.5f),
- Details.color,
- xoff/16.0f, yoff/16.0f,
- (xoff+w)/16.0f, yoff/16.0f,
- xoff/16.0f, (yoff+1.0f)/16.0f,
- (xoff+w)/16.0f - 0.0001f, (yoff+1.0f)/16.0f - 0.0001f);
- }
-}
-
-void
-CFont::PrintString(float xstart, float ystart, uint16 *s)
-{
- CRect rect;
- int numSpaces;
- float lineLength;
- float x, y;
- bool first;
- uint16 *start, *t;
-
- if(*s == '*')
- return;
-
- if(Details.background){
- GetNumberLines(xstart, ystart, s); // BUG: result not used
- GetTextRect(&rect, xstart, ystart, s);
- CSprite2d::DrawRect(rect, Details.backgroundColor);
- }
-
- lineLength = 0.0f;
- numSpaces = 0;
- first = true;
- if(Details.centre || Details.rightJustify)
- x = 0.0f;
- else
- x = xstart;
- y = ystart;
- start = s;
-
- // This is super ugly, I blame R*
- for(;;){
- for(;;){
- for(;;){
- if(*s == '\0')
- return;
- int xend = Details.centre ? Details.centreSize :
- Details.rightJustify ? xstart - Details.rightJustifyWrap :
- Details.wrapX;
- if(x + GetStringWidth(s) > xend && !first){
- // flush line
- float spaceWidth = !Details.justify || Details.centre ? 0.0f :
- (Details.wrapX - lineLength) / numSpaces;
- float xleft = Details.centre ? xstart - x/2 :
- Details.rightJustify ? xstart - x :
- xstart;
- PrintString(xleft, y, start, s, spaceWidth);
- // reset things
- lineLength = 0.0f;
- numSpaces = 0;
- first = true;
- if(Details.centre || Details.rightJustify)
- x = 0.0f;
- else
- x = xstart;
- y += 32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY;
- start = s;
- }else
- break;
- }
- // advance by one word
- t = GetNextSpace(s);
- if(t[0] == '\0' ||
- t[0] == ' ' && t[1] == '\0')
- break;
- if(!first)
- numSpaces++;
- first = false;
- x += GetStringWidth(s) + GetCharacterSize(*t - ' ');
- lineLength = x;
- s = t+1;
- }
- // print rest
- if(t[0] == ' ' && t[1] == '\0')
- t[0] = '\0';
- x += GetStringWidth(s);
- s = t;
- float xleft = Details.centre ? xstart - x/2 :
- Details.rightJustify ? xstart - x :
- xstart;
- PrintString(xleft, y, start, s, 0.0f);
- }
-}
-
-int
-CFont::GetNumberLines(float xstart, float ystart, uint16 *s)
-{
- int n;
- float x, y;
- uint16 *t;
-
- n = 0;
- if(Details.centre || Details.rightJustify)
- x = 0.0f;
- else
- x = xstart;
- y = ystart;
-
- while(*s){
- if(x + GetStringWidth(s) > (Details.centre ? Details.centreSize : Details.wrapX)){
- // reached end of line
- if(Details.centre || Details.rightJustify)
- x = 0.0f;
- else
- x = xstart;
- n++;
- // Why even?
- y += 32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY;
- }else{
- // still space in current line
- t = GetNextSpace(s);
- if(*t == '\0'){
- // end of string
- x += GetStringWidth(s);
- n++;
- s = t;
- }else{
- x += GetStringWidth(s);
- x += GetCharacterSize(*t - ' ');
- s = t+1;
- }
- }
- }
-
- return n;
-}
-
-void
-CFont::GetTextRect(CRect *rect, float xstart, float ystart, uint16 *s)
-{
- int numLines;
- float x, y;
- int16 maxlength;
- uint16 *t;
-
- maxlength = 0;
- numLines = 0;
- if(Details.centre || Details.rightJustify)
- x = 0.0f;
- else
- x = xstart;
- y = ystart;
-
- while(*s){
- if(x + GetStringWidth(s) > (Details.centre ? Details.centreSize : Details.wrapX)){
- // reached end of line
- if(x > maxlength)
- maxlength = x;
- if(Details.centre || Details.rightJustify)
- x = 0.0f;
- else
- x = xstart;
- numLines++;
- y += 32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY;
- }else{
- // still space in current line
- t = GetNextSpace(s);
- if(*t == '\0'){
- // end of string
- x += GetStringWidth(s);
- if(x > maxlength)
- maxlength = x;
- numLines++;
- s = t;
- }else{
- x += GetStringWidth(s);
- x += GetCharacterSize(*t - ' ');
- s = t+1;
- }
- }
- }
-
- if(Details.centre){
- if(Details.backgroundOnlyText){
- rect->left = xstart - maxlength/2 - 4.0f;
- rect->right = xstart + maxlength/2 + 4.0f;
- rect->bottom = (32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY) * numLines +
- ystart + 2.0f;
- rect->top = ystart - 2.0f;
- }else{
- rect->left = xstart - Details.centreSize*0.5f - 4.0f;
- rect->right = xstart + Details.centreSize*0.5f + 4.0f;
- rect->bottom = (32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY) * numLines +
- ystart + 2.0f;
- rect->top = ystart - 2.0f;
- }
- }else{
- rect->left = xstart - 4.0f;
- rect->right = Details.wrapX;
- // WTF?
- rect->bottom = ystart - 4.0f + 4.0f;
- rect->top = (32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY) * numLines +
- ystart + 2.0f + 2.0f;
- }
-}
-
-void
-CFont::PrintString(float x, float y, uint16 *start, uint16 *end, float spwidth)
-{
- uint16 *s, c, unused;
-
- for(s = start; s < end; s++){
- if(*s == '~')
- s = ParseToken(s, &unused);
- c = *s - ' ';
- if(Details.slant != 0.0f)
- y = (Details.slantRefX - x)*Details.slant + Details.slantRefY;
- PrintChar(x, y, c);
- x += GetCharacterSize(c);
- if(c == 0) // space
- x += spwidth;
- }
-}
-
-float
-CFont::GetCharacterWidth(uint16 c)
-{
- if(Details.proportional)
- return Size[Details.style][c];
- else
- return Size[Details.style][192];
-}
-
-float
-CFont::GetCharacterSize(uint16 c)
-{
- if(Details.proportional)
- return Size[Details.style][c] * Details.scaleX;
- else
- return Size[Details.style][192] * Details.scaleX;
-}
-
-float
-CFont::GetStringWidth(uint16 *s, bool spaces)
-{
- float w;
-
- w = 0.0f;
- for(; (*s != ' ' || spaces) && *s != '\0'; s++){
- if(*s == '~'){
- s++;
- while(*s != '~') s++;
- s++;
- if(*s == ' ' && !spaces)
- break;
- }
- w += GetCharacterSize(*s - ' ');
- }
- return w;
-}
-
-uint16*
-CFont::GetNextSpace(uint16 *s)
-{
- for(; *s != ' ' && *s != '\0'; s++)
- if(*s == '~'){
- s++;
- while(*s != '~') s++;
- s++;
- if(*s == ' ')
- break;
- }
- return s;
-}
-
-uint16*
-CFont::ParseToken(uint16 *s, uint16*)
-{
- s++;
- if(Details.color.r || Details.color.g || Details.color.b)
- switch(*s){
- case 'N':
- case 'n':
- NewLine = 1;
- break;
- case 'b': SetColor(CRGBA(0x80, 0xA7, 0xF3, 0xFF)); break;
- case 'g': SetColor(CRGBA(0x5F, 0xA0, 0x6A, 0xFF)); break;
- case 'h': SetColor(CRGBA(0xE1, 0xE1, 0xE1, 0xFF)); break;
- case 'l': SetColor(CRGBA(0x00, 0x00, 0x00, 0xFF)); break;
- case 'p': SetColor(CRGBA(0xA8, 0x6E, 0xFC, 0xFF)); break;
- case 'r': SetColor(CRGBA(0x71, 0x2B, 0x49, 0xFF)); break;
- case 'w': SetColor(CRGBA(0xAF, 0xAF, 0xAF, 0xFF)); break;
- case 'y': SetColor(CRGBA(0xD2, 0xC4, 0x6A, 0xFF)); break;
- }
- while(*s != '~') s++;
- return s+1;
-}
-
-void
-CFont::DrawFonts(void)
-{
- CSprite2d::DrawBank(Details.bank);
- CSprite2d::DrawBank(Details.bank+1);
- CSprite2d::DrawBank(Details.bank+2);
-}
-
-uint16
-CFont::character_code(uint8 c)
-{
- if(c < 128)
- return c;
- return foreign_table[c-128];
-}
-
-STARTPATCHES
-
- InjectHook(0x500A40, CFont::Initialise, PATCH_JUMP);
- InjectHook(0x500BA0, CFont::Shutdown, PATCH_JUMP);
- InjectHook(0x500BE0, CFont::InitPerFrame, PATCH_JUMP);
- InjectHook(0x500C30, CFont::PrintChar, PATCH_JUMP);
- InjectHook(0x500F50, (void (*)(float, float, uint16*))CFont::PrintString, PATCH_JUMP);
- InjectHook(0x501260, CFont::GetNumberLines, PATCH_JUMP);
- InjectHook(0x5013B0, CFont::GetTextRect, PATCH_JUMP);
- InjectHook(0x501730, (void (*)(float, float, uint16*, uint16*, float))CFont::PrintString, PATCH_JUMP);
- InjectHook(0x5017E0, CFont::GetCharacterWidth, PATCH_JUMP);
- InjectHook(0x501840, CFont::GetCharacterSize, PATCH_JUMP);
- InjectHook(0x5018A0, CFont::GetStringWidth, PATCH_JUMP);
- InjectHook(0x501960, CFont::GetNextSpace, PATCH_JUMP);
- InjectHook(0x5019A0, CFont::ParseToken, PATCH_JUMP);
- InjectHook(0x501B50, CFont::DrawFonts, PATCH_JUMP);
- InjectHook(0x501E80, CFont::character_code, PATCH_JUMP);
-
- InjectHook(0x501B80, CFont::SetScale, PATCH_JUMP);
- InjectHook(0x501BA0, CFont::SetSlantRefPoint, PATCH_JUMP);
- InjectHook(0x501BC0, CFont::SetSlant, PATCH_JUMP);
- InjectHook(0x501BD0, CFont::SetColor, PATCH_JUMP);
- InjectHook(0x501C60, CFont::SetJustifyOn, PATCH_JUMP);
- InjectHook(0x501C80, CFont::SetJustifyOff, PATCH_JUMP);
- InjectHook(0x501C90, CFont::SetCentreOn, PATCH_JUMP);
- InjectHook(0x501CB0, CFont::SetCentreOff, PATCH_JUMP);
- InjectHook(0x501CC0, CFont::SetWrapx, PATCH_JUMP);
- InjectHook(0x501CD0, CFont::SetCentreSize, PATCH_JUMP);
- InjectHook(0x501CE0, CFont::SetBackgroundOn, PATCH_JUMP);
- InjectHook(0x501CF0, CFont::SetBackgroundOff, PATCH_JUMP);
- InjectHook(0x501D00, CFont::SetBackgroundColor, PATCH_JUMP);
- InjectHook(0x501D30, CFont::SetBackGroundOnlyTextOn, PATCH_JUMP);
- InjectHook(0x501D40, CFont::SetBackGroundOnlyTextOff, PATCH_JUMP);
- InjectHook(0x501D50, CFont::SetRightJustifyOn, PATCH_JUMP);
- InjectHook(0x501D70, CFont::SetRightJustifyOff, PATCH_JUMP);
- InjectHook(0x501D90, CFont::SetPropOff, PATCH_JUMP);
- InjectHook(0x501DA0, CFont::SetPropOn, PATCH_JUMP);
- InjectHook(0x501DB0, CFont::SetFontStyle, PATCH_JUMP);
- InjectHook(0x501DC0, CFont::SetRightJustifyWrap, PATCH_JUMP);
- InjectHook(0x501DD0, CFont::SetAlphaFade, PATCH_JUMP);
- InjectHook(0x501DE0, CFont::SetDropColor, PATCH_JUMP);
- InjectHook(0x501E70, CFont::SetDropShadowPosition, PATCH_JUMP);
-
-ENDPATCHES
+#include "common.h"
+#include "patcher.h"
+#include "Sprite2d.h"
+#include "TxdStore.h"
+#include "Font.h"
+
+CFontDetails &CFont::Details = *(CFontDetails*)0x8F317C;
+int16 &CFont::NewLine = *(int16*)0x95CC94;
+CSprite2d *CFont::Sprite = (CSprite2d*)0x95CC04;
+
+#ifdef MORE_LANGUAGES
+uint8 CFont::LanguageSet = FONT_LANGSET_EFIGS;
+int32 CFont::Slot = -1;
+
+int16 CFont::Size[2][3][193] = {
+ {
+#else
+int16 CFont::Size[3][193] = {
+#endif
+ {
+ 13, 12, 31, 35, 23, 35, 31, 9, 14, 15, 25, 30, 11, 17, 13, 31,
+ 23, 16, 22, 21, 24, 23, 23, 20, 23, 22, 10, 35, 26, 26, 26, 26,
+ 30, 26, 24, 23, 24, 22, 21, 24, 26, 10, 20, 26, 22, 29, 26, 25,
+ 23, 25, 24, 24, 22, 25, 24, 29, 29, 23, 25, 37, 22, 37, 35, 37,
+ 35, 21, 22, 21, 21, 22, 13, 22, 21, 10, 16, 22, 11, 32, 21, 21,
+ 23, 22, 16, 20, 14, 21, 20, 30, 25, 21, 21, 33, 33, 33, 33, 35,
+ 27, 27, 27, 27, 32, 24, 23, 23, 23, 23, 11, 11, 11, 11, 26, 26,
+ 26, 26, 26, 26, 26, 25, 26, 21, 21, 21, 21, 32, 23, 22, 22, 22,
+ 22, 11, 11, 11, 11, 22, 22, 22, 22, 22, 22, 22, 22, 26, 21, 24,
+ 12, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 18, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 20
+ },
+
+ {
+ 13, 9, 21, 35, 23, 35, 35, 11, 35, 35, 25, 35, 11, 17, 13, 33,
+ 28, 14, 22, 21, 24, 23, 23, 21, 23, 22, 10, 35, 13, 35, 13, 33,
+ 5, 25, 22, 23, 24, 21, 21, 24, 24, 9, 20, 24, 21, 27, 25, 25,
+ 22, 25, 23, 20, 23, 23, 23, 31, 23, 23, 23, 37, 33, 37, 35, 37,
+ 35, 21, 19, 19, 21, 19, 17, 21, 21, 8, 17, 18, 14, 24, 21, 21,
+ 20, 22, 19, 20, 20, 19, 20, 26, 21, 20, 21, 33, 33, 33, 33, 35,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 16
+ },
+
+ {
+ 15, 14, 16, 25, 19, 26, 22, 11, 18, 18, 27, 26, 13, 19, 9, 27,
+ 19, 18, 19, 19, 22, 19, 20, 18, 19, 20, 12, 32, 15, 32, 15, 35,
+ 15, 19, 19, 19, 19, 19, 16, 19, 20, 9, 19, 20, 14, 29, 19, 20,
+ 19, 19, 19, 19, 21, 19, 20, 32, 20, 19, 19, 33, 31, 39, 37, 39,
+ 37, 21, 21, 21, 23, 21, 19, 23, 23, 10, 19, 20, 16, 26, 23, 23,
+ 20, 20, 20, 22, 21, 22, 22, 26, 22, 22, 23, 35, 35, 35, 35, 37,
+ 19, 19, 19, 19, 29, 19, 19, 19, 19, 19, 9, 9, 9, 9, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 30, 19, 19, 19, 19,
+ 19, 10, 10, 10, 10, 19, 19, 19, 19, 19, 19, 19, 19, 19, 23, 35,
+ 12, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 11, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19
+ }
+#ifdef MORE_LANGUAGES
+ },
+ {
+ { 13, 12, 31, 35, 23, 35, 31, 9, 14, 15, 25, 30, 11, 17,
+ 13, 31, 23, 16, 22, 21, 24, 23, 23, 20, 23, 22, 10,
+ 35, 26, 26, 26, 26, 30, 26, 24, 23, 24, 22, 21, 24,
+ 26, 10, 20, 26, 22, 29, 26, 25, 23, 25, 24, 24, 22,
+ 25, 24, 29, 29, 23, 25, 37, 22, 37, 35, 37, 35, 21,
+ 22, 21, 21, 22, 13, 22, 21, 10, 16, 22, 11, 32, 21,
+ 21, 23, 22, 16, 20, 14, 21, 20, 30, 25, 21, 21, 13,
+ 33, 13, 13, 13, 24, 22, 22, 19, 26, 21, 30, 20, 23,
+ 23, 21, 24, 26, 23, 22, 23, 21, 22, 20, 20, 26, 25,
+ 24, 22, 31, 32, 23, 30, 22, 22, 32, 23, 19, 18, 18,
+ 15, 22, 19, 27, 19, 20, 20, 18, 22, 24, 20, 19, 19,
+ 20, 19, 16, 19, 28, 20, 20, 18, 26, 27, 19, 26, 18,
+ 19, 27, 19, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 18, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 20 },
+ { 13, 9, 21, 35, 23, 35, 35, 11, 35, 35, 25, 35, 11,
+ 17, 13, 33, 28, 14, 22, 21, 24, 23, 23, 21, 23, 22,
+ 10, 35, 13, 35, 13, 33, 5, 25, 22, 23, 24, 21, 21, 24,
+ 24, 9, 20, 24, 21, 27, 25, 25, 22, 25, 23, 20, 23, 23,
+ 23, 31, 23, 23, 23, 37, 33, 37, 35, 37, 35, 21, 19,
+ 19, 21, 19, 17, 21, 21, 8, 17, 18, 14, 24, 21, 21, 20,
+ 22, 19, 20, 20, 19, 20, 26, 21, 20, 21, 33, 33, 33,
+ 33, 35, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 16, },
+ { 15, 14, 16, 25, 19,
+ 26, 22, 11, 18, 18, 27, 26, 13, 19, 9, 27, 19, 18, 19,
+ 19, 22, 19, 20, 18, 19, 20, 12, 32, 15, 32, 15, 35,
+ 15, 19, 19, 19, 19, 19, 16, 19, 20, 9, 19, 20, 14, 29,
+ 19, 20, 19, 19, 19, 19, 21, 19, 20, 32, 20, 19, 19,
+ 33, 31, 39, 37, 39, 37, 21, 21, 21, 23, 21, 19, 23, 23, 10, 19, 20, 16, 26, 23,
+ 21, 21, 20, 20, 22, 21, 22, 22, 26, 22, 22, 23, 35,
+ 35, 35, 35, 37, 19, 19, 19, 19, 19, 19, 29, 19, 19,
+ 19, 20, 22, 31, 19, 19, 19, 19, 19, 29, 19, 29, 19,
+ 21, 19, 30, 31, 21, 29, 19, 19, 29, 19, 21, 23, 32,
+ 21, 21, 30, 31, 22, 21, 32, 33, 23, 32, 21, 21, 32,
+ 21, 19, 19, 30, 31, 22, 22, 21, 32, 33, 23, 32, 21,
+ 21, 32, 21, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 11, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19 },
+ }
+#endif
+};
+
+uint16 foreign_table[128] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 177, 0, 0, 0, 0, 0, 95, 0, 0, 0, 0, 175,
+ 128, 129, 130, 0, 131, 0, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
+ 0, 173, 142, 143, 144, 0, 145, 0, 0, 146, 147, 148, 149, 0, 0, 150,
+ 151, 152, 153, 0, 154, 0, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+ 0, 174, 165, 166, 167, 0, 168, 0, 0, 169, 170, 171, 172, 0, 0, 0,
+};
+
+void
+CFont::Initialise(void)
+{
+ int slot;
+
+ slot = CTxdStore::AddTxdSlot("fonts");
+#ifdef MORE_LANGUAGES
+ Slot = slot;
+ switch (LanguageSet)
+ {
+ case FONT_LANGSET_EFIGS:
+ default:
+ CTxdStore::LoadTxd(slot, "MODELS/FONTS.TXD");
+ break;
+ case FONT_LANGSET_RUSSIAN:
+ CTxdStore::LoadTxd(slot, "MODELS/FONTS_R.TXD");
+ break;
+ }
+#else
+ CTxdStore::LoadTxd(slot, "MODELS/FONTS.TXD");
+#endif
+ CTxdStore::AddRef(slot);
+ CTxdStore::PushCurrentTxd();
+ CTxdStore::SetCurrentTxd(slot);
+ Sprite[0].SetTexture("font2", "font2_mask");
+ Sprite[1].SetTexture("pager", "pager_mask");
+ Sprite[2].SetTexture("font1", "font1_mask");
+ SetScale(1.0f, 1.0f);
+ SetSlantRefPoint(SCREEN_WIDTH, 0.0f);
+ SetSlant(0.0f);
+ SetColor(CRGBA(0xFF, 0xFF, 0xFF, 0));
+ SetJustifyOff();
+ SetCentreOff();
+ SetWrapx(640.0f);
+ SetCentreSize(640.0f);
+ SetBackgroundOff();
+ SetBackgroundColor(CRGBA(0x80, 0x80, 0x80, 0x80));
+ SetBackGroundOnlyTextOff();
+ SetPropOn();
+ SetFontStyle(FONT_BANK);
+ SetRightJustifyWrap(0.0f);
+ SetAlphaFade(255.0f);
+ SetDropShadowPosition(0);
+ CTxdStore::PopCurrentTxd();
+}
+
+#ifdef MORE_LANGUAGES
+void
+CFont::ReloadFonts(uint8 set)
+{
+ if (Slot != -1 && LanguageSet != set) {
+ Sprite[0].Delete();
+ Sprite[1].Delete();
+ Sprite[2].Delete();
+ CTxdStore::PushCurrentTxd();
+ CTxdStore::RemoveTxd(Slot);
+ switch (set)
+ {
+ case FONT_LANGSET_EFIGS:
+ default:
+ CTxdStore::LoadTxd(Slot, "MODELS/FONTS.TXD");
+ break;
+ case FONT_LANGSET_RUSSIAN:
+ CTxdStore::LoadTxd(Slot, "MODELS/FONTS_R.TXD");
+ break;
+ }
+ CTxdStore::SetCurrentTxd(Slot);
+ Sprite[0].SetTexture("font2", "font2_mask");
+ Sprite[1].SetTexture("pager", "pager_mask");
+ Sprite[2].SetTexture("font1", "font1_mask");
+ CTxdStore::PopCurrentTxd();
+ }
+ LanguageSet = set;
+}
+#endif
+
+void
+CFont::Shutdown(void)
+{
+ Sprite[0].Delete();
+ Sprite[1].Delete();
+ Sprite[2].Delete();
+#ifdef MORE_LANGUAGES
+ CTxdStore::RemoveTxdSlot(Slot);
+ Slot = -1;
+#else
+ CTxdStore::RemoveTxdSlot(CTxdStore::FindTxdSlot("fonts"));
+#endif
+}
+
+void
+CFont::InitPerFrame(void)
+{
+ Details.bank = CSprite2d::GetBank(30, Sprite[0].m_pTexture);
+ CSprite2d::GetBank(15, Sprite[1].m_pTexture);
+ CSprite2d::GetBank(15, Sprite[2].m_pTexture);
+ SetDropShadowPosition(0);
+ NewLine = 0;
+}
+
+void
+CFont::PrintChar(float x, float y, uint16 c)
+{
+ if(x <= 0.0f || x > SCREEN_WIDTH ||
+ y <= 0.0f || y > SCREEN_HEIGHT) // BUG: game uses SCREENW again
+ return;
+
+ float w = GetCharacterWidth(c) / 32.0f;
+ float xoff = c & 0xF;
+ float yoff = c >> 4;
+
+ if(Details.style == FONT_BANK || Details.style == FONT_HEADING){
+ if(Details.dropShadowPosition != 0){
+ CSprite2d::AddSpriteToBank(Details.bank + Details.style, // BUG: game doesn't add bank
+ CRect(x + SCREEN_SCALE_X(Details.dropShadowPosition),
+ y + SCREEN_SCALE_Y(Details.dropShadowPosition),
+ x + SCREEN_SCALE_X(Details.dropShadowPosition) + 32.0f * Details.scaleX * 1.0f,
+ y + SCREEN_SCALE_Y(Details.dropShadowPosition) + 40.0f * Details.scaleY * 0.5f),
+ Details.dropColor,
+ xoff/16.0f, yoff/12.8f,
+ (xoff+1.0f)/16.0f - 0.001f, yoff/12.8f,
+ xoff/16.0f, (yoff+1.0f)/12.8f,
+ (xoff+1.0f)/16.0f - 0.001f, (yoff+1.0f)/12.8f - 0.0001f);
+ }
+ CSprite2d::AddSpriteToBank(Details.bank + Details.style, // BUG: game doesn't add bank
+ CRect(x, y,
+ x + 32.0f * Details.scaleX * 1.0f,
+ y + 40.0f * Details.scaleY * 0.5f),
+ Details.color,
+ xoff/16.0f, yoff/12.8f,
+ (xoff+1.0f)/16.0f - 0.001f, yoff/12.8f,
+ xoff/16.0f, (yoff+1.0f)/12.8f - 0.002f,
+ (xoff+1.0f)/16.0f - 0.001f, (yoff+1.0f)/12.8f - 0.002f);
+ }else{
+ CSprite2d::AddSpriteToBank(Details.bank + Details.style, // BUG: game doesn't add bank
+ CRect(x, y,
+ x + 32.0f * Details.scaleX * w,
+ y + 32.0f * Details.scaleY * 0.5f),
+ Details.color,
+ xoff/16.0f, yoff/16.0f,
+ (xoff+w)/16.0f, yoff/16.0f,
+ xoff/16.0f, (yoff+1.0f)/16.0f,
+ (xoff+w)/16.0f - 0.0001f, (yoff+1.0f)/16.0f - 0.0001f);
+ }
+}
+
+void
+CFont::PrintString(float xstart, float ystart, uint16 *s)
+{
+ CRect rect;
+ int numSpaces;
+ float lineLength;
+ float x, y;
+ bool first;
+ uint16 *start, *t;
+
+ if(*s == '*')
+ return;
+
+ if(Details.background){
+ GetNumberLines(xstart, ystart, s); // BUG: result not used
+ GetTextRect(&rect, xstart, ystart, s);
+ CSprite2d::DrawRect(rect, Details.backgroundColor);
+ }
+
+ lineLength = 0.0f;
+ numSpaces = 0;
+ first = true;
+ if(Details.centre || Details.rightJustify)
+ x = 0.0f;
+ else
+ x = xstart;
+ y = ystart;
+ start = s;
+
+ // This is super ugly, I blame R*
+ for(;;){
+ for(;;){
+ for(;;){
+ if(*s == '\0')
+ return;
+ int xend = Details.centre ? Details.centreSize :
+ Details.rightJustify ? xstart - Details.rightJustifyWrap :
+ Details.wrapX;
+ if(x + GetStringWidth(s) > xend && !first){
+ // flush line
+ float spaceWidth = !Details.justify || Details.centre ? 0.0f :
+ (Details.wrapX - lineLength) / numSpaces;
+ float xleft = Details.centre ? xstart - x/2 :
+ Details.rightJustify ? xstart - x :
+ xstart;
+ PrintString(xleft, y, start, s, spaceWidth);
+ // reset things
+ lineLength = 0.0f;
+ numSpaces = 0;
+ first = true;
+ if(Details.centre || Details.rightJustify)
+ x = 0.0f;
+ else
+ x = xstart;
+ y += 32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY;
+ start = s;
+ }else
+ break;
+ }
+ // advance by one word
+ t = GetNextSpace(s);
+ if(t[0] == '\0' ||
+ t[0] == ' ' && t[1] == '\0')
+ break;
+ if(!first)
+ numSpaces++;
+ first = false;
+ x += GetStringWidth(s) + GetCharacterSize(*t - ' ');
+ lineLength = x;
+ s = t+1;
+ }
+ // print rest
+ if(t[0] == ' ' && t[1] == '\0')
+ t[0] = '\0';
+ x += GetStringWidth(s);
+ s = t;
+ float xleft = Details.centre ? xstart - x/2 :
+ Details.rightJustify ? xstart - x :
+ xstart;
+ PrintString(xleft, y, start, s, 0.0f);
+ }
+}
+
+int
+CFont::GetNumberLines(float xstart, float ystart, uint16 *s)
+{
+ int n;
+ float x, y;
+ uint16 *t;
+
+ n = 0;
+ if(Details.centre || Details.rightJustify)
+ x = 0.0f;
+ else
+ x = xstart;
+ y = ystart;
+
+ while(*s){
+ if(x + GetStringWidth(s) > (Details.centre ? Details.centreSize : Details.wrapX)){
+ // reached end of line
+ if(Details.centre || Details.rightJustify)
+ x = 0.0f;
+ else
+ x = xstart;
+ n++;
+ // Why even?
+ y += 32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY;
+ }else{
+ // still space in current line
+ t = GetNextSpace(s);
+ if(*t == '\0'){
+ // end of string
+ x += GetStringWidth(s);
+ n++;
+ s = t;
+ }else{
+ x += GetStringWidth(s);
+ x += GetCharacterSize(*t - ' ');
+ s = t+1;
+ }
+ }
+ }
+
+ return n;
+}
+
+void
+CFont::GetTextRect(CRect *rect, float xstart, float ystart, uint16 *s)
+{
+ int numLines;
+ float x, y;
+ int16 maxlength;
+ uint16 *t;
+
+ maxlength = 0;
+ numLines = 0;
+ if(Details.centre || Details.rightJustify)
+ x = 0.0f;
+ else
+ x = xstart;
+ y = ystart;
+
+ while(*s){
+ if(x + GetStringWidth(s) > (Details.centre ? Details.centreSize : Details.wrapX)){
+ // reached end of line
+ if(x > maxlength)
+ maxlength = x;
+ if(Details.centre || Details.rightJustify)
+ x = 0.0f;
+ else
+ x = xstart;
+ numLines++;
+ y += 32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY;
+ }else{
+ // still space in current line
+ t = GetNextSpace(s);
+ if(*t == '\0'){
+ // end of string
+ x += GetStringWidth(s);
+ if(x > maxlength)
+ maxlength = x;
+ numLines++;
+ s = t;
+ }else{
+ x += GetStringWidth(s);
+ x += GetCharacterSize(*t - ' ');
+ s = t+1;
+ }
+ }
+ }
+
+ if(Details.centre){
+ if(Details.backgroundOnlyText){
+ rect->left = xstart - maxlength/2 - 4.0f;
+ rect->right = xstart + maxlength/2 + 4.0f;
+ rect->bottom = (32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY) * numLines +
+ ystart + 2.0f;
+ rect->top = ystart - 2.0f;
+ }else{
+ rect->left = xstart - Details.centreSize*0.5f - 4.0f;
+ rect->right = xstart + Details.centreSize*0.5f + 4.0f;
+ rect->bottom = (32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY) * numLines +
+ ystart + 2.0f;
+ rect->top = ystart - 2.0f;
+ }
+ }else{
+ rect->left = xstart - 4.0f;
+ rect->right = Details.wrapX;
+ // WTF?
+ rect->bottom = ystart - 4.0f + 4.0f;
+ rect->top = (32.0f * CFont::Details.scaleY * 0.5f + 2.0f * CFont::Details.scaleY) * numLines +
+ ystart + 2.0f + 2.0f;
+ }
+}
+
+void
+CFont::PrintString(float x, float y, uint16 *start, uint16 *end, float spwidth)
+{
+ uint16 *s, c, unused;
+
+ for(s = start; s < end; s++){
+ if(*s == '~')
+ s = ParseToken(s, &unused);
+ c = *s - ' ';
+ if(Details.slant != 0.0f)
+ y = (Details.slantRefX - x)*Details.slant + Details.slantRefY;
+ PrintChar(x, y, c);
+ x += GetCharacterSize(c);
+ if(c == 0) // space
+ x += spwidth;
+ }
+}
+
+float
+CFont::GetCharacterWidth(uint16 c)
+{
+#ifdef MORE_LANGUAGES
+ if (Details.proportional)
+ return Size[LanguageSet][Details.style][c];
+ else
+ return Size[LanguageSet][Details.style][192];
+#else
+ if (Details.proportional)
+ return Size[Details.style][c];
+ else
+ return Size[Details.style][192];
+#endif // MORE_LANGUAGES
+}
+
+float
+CFont::GetCharacterSize(uint16 c)
+{
+#ifdef MORE_LANGUAGES
+ if(Details.proportional)
+ return Size[LanguageSet][Details.style][c] * Details.scaleX;
+ else
+ return Size[LanguageSet][Details.style][192] * Details.scaleX;
+#else
+ if (Details.proportional)
+ return Size[Details.style][c] * Details.scaleX;
+ else
+ return Size[Details.style][192] * Details.scaleX;
+#endif // MORE_LANGUAGES
+}
+
+float
+CFont::GetStringWidth(uint16 *s, bool spaces)
+{
+ float w;
+
+ w = 0.0f;
+ for(; (*s != ' ' || spaces) && *s != '\0'; s++){
+ if(*s == '~'){
+ s++;
+ while(*s != '~') s++;
+ s++;
+ if(*s == ' ' && !spaces)
+ break;
+ }
+ w += GetCharacterSize(*s - ' ');
+ }
+ return w;
+}
+
+uint16*
+CFont::GetNextSpace(uint16 *s)
+{
+ for(; *s != ' ' && *s != '\0'; s++)
+ if(*s == '~'){
+ s++;
+ while(*s != '~') s++;
+ s++;
+ if(*s == ' ')
+ break;
+ }
+ return s;
+}
+
+uint16*
+CFont::ParseToken(uint16 *s, uint16*)
+{
+ s++;
+ if(Details.color.r || Details.color.g || Details.color.b)
+ switch(*s){
+ case 'N':
+ case 'n':
+ NewLine = 1;
+ break;
+ case 'b': SetColor(CRGBA(0x80, 0xA7, 0xF3, 0xFF)); break;
+ case 'g': SetColor(CRGBA(0x5F, 0xA0, 0x6A, 0xFF)); break;
+ case 'h': SetColor(CRGBA(0xE1, 0xE1, 0xE1, 0xFF)); break;
+ case 'l': SetColor(CRGBA(0x00, 0x00, 0x00, 0xFF)); break;
+ case 'p': SetColor(CRGBA(0xA8, 0x6E, 0xFC, 0xFF)); break;
+ case 'r': SetColor(CRGBA(0x71, 0x2B, 0x49, 0xFF)); break;
+ case 'w': SetColor(CRGBA(0xAF, 0xAF, 0xAF, 0xFF)); break;
+ case 'y': SetColor(CRGBA(0xD2, 0xC4, 0x6A, 0xFF)); break;
+ }
+ while(*s != '~') s++;
+ return s+1;
+}
+
+void
+CFont::DrawFonts(void)
+{
+ CSprite2d::DrawBank(Details.bank);
+ CSprite2d::DrawBank(Details.bank+1);
+ CSprite2d::DrawBank(Details.bank+2);
+}
+
+uint16
+CFont::character_code(uint8 c)
+{
+ if(c < 128)
+ return c;
+ return foreign_table[c-128];
+}
+
+STARTPATCHES
+
+ InjectHook(0x500A40, CFont::Initialise, PATCH_JUMP);
+ InjectHook(0x500BA0, CFont::Shutdown, PATCH_JUMP);
+ InjectHook(0x500BE0, CFont::InitPerFrame, PATCH_JUMP);
+ InjectHook(0x500C30, CFont::PrintChar, PATCH_JUMP);
+ InjectHook(0x500F50, (void (*)(float, float, uint16*))CFont::PrintString, PATCH_JUMP);
+ InjectHook(0x501260, CFont::GetNumberLines, PATCH_JUMP);
+ InjectHook(0x5013B0, CFont::GetTextRect, PATCH_JUMP);
+ InjectHook(0x501730, (void (*)(float, float, uint16*, uint16*, float))CFont::PrintString, PATCH_JUMP);
+ InjectHook(0x5017E0, CFont::GetCharacterWidth, PATCH_JUMP);
+ InjectHook(0x501840, CFont::GetCharacterSize, PATCH_JUMP);
+ InjectHook(0x5018A0, CFont::GetStringWidth, PATCH_JUMP);
+ InjectHook(0x501960, CFont::GetNextSpace, PATCH_JUMP);
+ InjectHook(0x5019A0, CFont::ParseToken, PATCH_JUMP);
+ InjectHook(0x501B50, CFont::DrawFonts, PATCH_JUMP);
+ InjectHook(0x501E80, CFont::character_code, PATCH_JUMP);
+
+ InjectHook(0x501B80, CFont::SetScale, PATCH_JUMP);
+ InjectHook(0x501BA0, CFont::SetSlantRefPoint, PATCH_JUMP);
+ InjectHook(0x501BC0, CFont::SetSlant, PATCH_JUMP);
+ InjectHook(0x501BD0, CFont::SetColor, PATCH_JUMP);
+ InjectHook(0x501C60, CFont::SetJustifyOn, PATCH_JUMP);
+ InjectHook(0x501C80, CFont::SetJustifyOff, PATCH_JUMP);
+ InjectHook(0x501C90, CFont::SetCentreOn, PATCH_JUMP);
+ InjectHook(0x501CB0, CFont::SetCentreOff, PATCH_JUMP);
+ InjectHook(0x501CC0, CFont::SetWrapx, PATCH_JUMP);
+ InjectHook(0x501CD0, CFont::SetCentreSize, PATCH_JUMP);
+ InjectHook(0x501CE0, CFont::SetBackgroundOn, PATCH_JUMP);
+ InjectHook(0x501CF0, CFont::SetBackgroundOff, PATCH_JUMP);
+ InjectHook(0x501D00, CFont::SetBackgroundColor, PATCH_JUMP);
+ InjectHook(0x501D30, CFont::SetBackGroundOnlyTextOn, PATCH_JUMP);
+ InjectHook(0x501D40, CFont::SetBackGroundOnlyTextOff, PATCH_JUMP);
+ InjectHook(0x501D50, CFont::SetRightJustifyOn, PATCH_JUMP);
+ InjectHook(0x501D70, CFont::SetRightJustifyOff, PATCH_JUMP);
+ InjectHook(0x501D90, CFont::SetPropOff, PATCH_JUMP);
+ InjectHook(0x501DA0, CFont::SetPropOn, PATCH_JUMP);
+ InjectHook(0x501DB0, CFont::SetFontStyle, PATCH_JUMP);
+ InjectHook(0x501DC0, CFont::SetRightJustifyWrap, PATCH_JUMP);
+ InjectHook(0x501DD0, CFont::SetAlphaFade, PATCH_JUMP);
+ InjectHook(0x501DE0, CFont::SetDropColor, PATCH_JUMP);
+ InjectHook(0x501E70, CFont::SetDropShadowPosition, PATCH_JUMP);
+
+ENDPATCHES
diff --git a/src/render/Font.h b/src/render/Font.h
index 132ad168..26309377 100644
--- a/src/render/Font.h
+++ b/src/render/Font.h
@@ -39,9 +39,23 @@ enum {
ALIGN_RIGHT,
};
+#ifdef MORE_LANGUAGES
+enum
+{
+ FONT_LANGSET_EFIGS,
+ FONT_LANGSET_RUSSIAN
+};
+#endif
+
class CFont
{
+#ifdef MORE_LANGUAGES
+ static int16 Size[2][3][193];
+ static uint8 LanguageSet;
+ static int32 Slot;
+#else
static int16 Size[3][193];
+#endif
static int16 &NewLine;
static CSprite2d *Sprite; //[3]
public:
@@ -136,4 +150,6 @@ public:
if(Details.alphaFade < 255.0f)
Details.dropColor.a *= Details.alphaFade/255.0f;
}
+
+ static void ReloadFonts(uint8 set);
};
diff --git a/src/text/Text.cpp b/src/text/Text.cpp
index 8bffa7e1..d0cdb310 100644
--- a/src/text/Text.cpp
+++ b/src/text/Text.cpp
@@ -43,6 +43,11 @@ CText::Load(void)
case LANGUAGE_SPANISH:
sprintf(filename, "SPANISH.GXT");
break;
+#ifdef MORE_LANGUAGES
+ case LANGUAGE_RUSSIAN:
+ sprintf(filename, "RUSSIAN.GXT");
+ break;
+#endif
}
length = CFileMgr::LoadFile(filename, filedata, 0x40000, "rb");
diff --git a/src/vehicles/Automobile.cpp b/src/vehicles/Automobile.cpp
index e709a87f..aca96aa3 100644
--- a/src/vehicles/Automobile.cpp
+++ b/src/vehicles/Automobile.cpp
@@ -2339,10 +2339,17 @@ CAutomobile::FireTruckControl(void)
if(this == FindPlayerVehicle()){
if(!CPad::GetPad(0)->GetWeapon())
return;
- m_fCarGunLR += CPad::GetPad(0)->GetCarGunLeftRight()*0.00025f*CTimer::GetTimeStep();
- m_fCarGunUD += CPad::GetPad(0)->GetCarGunUpDown()*0.0001f*CTimer::GetTimeStep();
+#ifdef FREE_CAM
+ extern bool bFreeMouseCam;
+ if (!bFreeMouseCam)
+#endif
+ {
+ m_fCarGunLR += CPad::GetPad(0)->GetCarGunLeftRight() * 0.00025f * CTimer::GetTimeStep();
+ m_fCarGunUD += CPad::GetPad(0)->GetCarGunUpDown() * 0.0001f * CTimer::GetTimeStep();
+ }
m_fCarGunUD = clamp(m_fCarGunUD, 0.05f, 0.3f);
+
CVector cannonPos(0.0f, 1.5f, 1.9f);
cannonPos = GetMatrix() * cannonPos;
CVector cannonDir(
@@ -2408,7 +2415,12 @@ CAutomobile::TankControl(void)
// Rotate turret
float prevAngle = m_fCarGunLR;
- m_fCarGunLR -= CPad::GetPad(0)->GetCarGunLeftRight() * 0.00015f * CTimer::GetTimeStep();
+#ifdef FREE_CAM
+ extern bool bFreeMouseCam;
+ if(!bFreeMouseCam)
+#endif
+ m_fCarGunLR -= CPad::GetPad(0)->GetCarGunLeftRight() * 0.00015f * CTimer::GetTimeStep();
+
if(m_fCarGunLR < 0.0f)
m_fCarGunLR += TWOPI;
if(m_fCarGunLR > TWOPI)