1
0
Fork 0
mirror of https://git.rip/DMCA_FUCKER/re3.git synced 2024-12-23 00:40:00 +00:00

first commit

This commit is contained in:
aap 2019-05-15 16:52:37 +02:00
commit 600bf03514
116 changed files with 15132 additions and 0 deletions

101
README.md Normal file
View file

@ -0,0 +1,101 @@
# Intro
The aim of this project is to reverse GTA III for PC by replacing
parts of the game [one by one](https://en.wikipedia.org/wiki/Ship_of_Theseus)
such that we have a working game at all times.
# Strategy
A good approach is to start at the fringes of the code base,
i.e. classes that don't depend on code that we don't have reversed yet.
If a function uses only few unreversed functions that would be inconvenient
to reverse at the time, calling the original functions is acceptable.
# Progress
This is a list of some things that have been reversed to some non-trivial extent.
Not everything is listed, check the code.
```
CPool
CTxdStore
CVector
CVector2D
CMatrix
CModelInfo
CBaseModelInfo
CSimpleModelInfo
CTimeModelInfo
CClumpModelInfo
CPedModelInfo
CVehicleModelInfo
CVisibilityPlugins
CRenderer
CEntity
CPhysical
CCollision
CCullZones
CTheZones
CPathFind
```
# Low hanging fruit
There are a couple of things that have been reversed for other projects
already that could probably be put into this project without too much effort.
Again, the list is not complete:
* Animation (https://github.com/aap/iii_anim)
* File Loader (https://github.com/aap/librwgta/tree/master/tools/IIItest)
* ...
# Coding style
I started writing in [Plan 9 style](http://man.cat-v.org/plan_9/6/style),
but realize that this is not the most popular style, so I'm willing to compromise.
Try not to deviate too much so the code will look similar across the whole project.
To give examples, these two styles (or anything in between) are fine:
```
type
functionname(args)
{
if(a == b){
s1;
s2;
}
if(x != y)
s3;
}
type functionname(args)
{
if (a == b) {
s1;
s2;
}
if (x != y)
s3;
}
```
This one (or anything more extreme) is heavily discouraged:
```
type functionname ( args )
{
if ( a == b )
{
s1;
s2;
}
if ( x != y )
{
s3;
}
}
```
Indentation is done with TABS.

1
premake5.cmd Normal file
View file

@ -0,0 +1 @@
premake5 vs2015

BIN
premake5.exe Normal file

Binary file not shown.

38
premake5.lua Normal file
View file

@ -0,0 +1,38 @@
workspace "re3"
configurations { "Release","Debug" }
location "build"
files { "src/*.*" }
files { "src/math/*.*" }
files { "src/modelinfo/*.*" }
files { "src/entities/*.*" }
files { "src/render/*.*" }
includedirs { "src", "src/modelinfo" }
includedirs { "src", "src/entities" }
includedirs { "src", "src/render" }
includedirs { os.getenv("RWSDK33") }
project "re3"
kind "SharedLib"
language "C++"
targetname "re3"
targetdir "bin/%{cfg.buildcfg}"
targetextension ".dll"
characterset ("MBCS")
filter "configurations:Debug"
defines { "DEBUG" }
flags { "StaticRuntime" }
symbols "On"
debugdir "C:/Users/aap/games/gta3_re"
debugcommand "C:/Users/aap/games/gta3_re/gta3.exe"
postbuildcommands "copy /y \"$(TargetPath)\" \"C:\\Users\\aap\\games\\gta3_re\\plugins\\re3.dll\""
filter "configurations:Release"
defines { "NDEBUG" }
optimize "On"
flags { "StaticRuntime" }
debugdir "C:/Users/aap/games/gta3_re"
debugcommand "C:/Users/aap/games/gta3_re/gta3.exe"
postbuildcommands "copy /y \"$(TargetPath)\" \"C:\\Users\\aap\\games\\gta3_re\\plugins\\re3.dll\""

63
src/Camera.cpp Normal file
View file

@ -0,0 +1,63 @@
#include "common.h"
#include "patcher.h"
#include "Draw.h"
#include "Camera.h"
CCamera &TheCamera = *(CCamera*)0x6FACF8;
bool
CCamera::IsSphereVisible(const CVector &center, float radius, const CMatrix *mat)
{
RwV3d c;
c = *(RwV3d*)&center;
RwV3dTransformPoints(&c, &c, 1, &mat->m_matrix);
if(c.y + radius < CDraw::GetNearClipZ()) return false;
if(c.y - radius > CDraw::GetFarClipZ()) return false;
if(c.x*m_vecFrustumNormals[0].x + c.y*m_vecFrustumNormals[0].y > radius) return false;
if(c.x*m_vecFrustumNormals[1].x + c.y*m_vecFrustumNormals[1].y > radius) return false;
if(c.y*m_vecFrustumNormals[2].y + c.z*m_vecFrustumNormals[2].z > radius) return false;
if(c.y*m_vecFrustumNormals[3].y + c.z*m_vecFrustumNormals[3].z > radius) return false;
return true;
}
bool
CCamera::IsPointVisible(const CVector &center, const CMatrix *mat)
{
RwV3d c;
c = *(RwV3d*)&center;
RwV3dTransformPoints(&c, &c, 1, &mat->m_matrix);
if(c.y < CDraw::GetNearClipZ()) return false;
if(c.y > CDraw::GetFarClipZ()) return false;
if(c.x*m_vecFrustumNormals[0].x + c.y*m_vecFrustumNormals[0].y > 0.0f) return false;
if(c.x*m_vecFrustumNormals[1].x + c.y*m_vecFrustumNormals[1].y > 0.0f) return false;
if(c.y*m_vecFrustumNormals[2].y + c.z*m_vecFrustumNormals[2].z > 0.0f) return false;
if(c.y*m_vecFrustumNormals[3].y + c.z*m_vecFrustumNormals[3].z > 0.0f) return false;
return true;
}
bool
CCamera::IsBoxVisible(RwV3d *box, const CMatrix *mat)
{
int i;
int frustumTests[6] = { 0 };
RwV3dTransformPoints(box, box, 8, &mat->m_matrix);
for(i = 0; i < 8; i++){
if(box[i].y < CDraw::GetNearClipZ()) frustumTests[0]++;
if(box[i].y > CDraw::GetFarClipZ()) frustumTests[1]++;
if(box[i].x*m_vecFrustumNormals[0].x + box[i].y*m_vecFrustumNormals[0].y > 0.0f) frustumTests[2]++;
if(box[i].x*m_vecFrustumNormals[1].x + box[i].y*m_vecFrustumNormals[1].y > 0.0f) frustumTests[3]++;
// Why not test z?
// if(box[i].y*m_vecFrustumNormals[2].y + box[i].z*m_vecFrustumNormals[2].z > 0.0f) frustumTests[4]++;
// if(box[i].y*m_vecFrustumNormals[3].y + box[i].z*m_vecFrustumNormals[3].z > 0.0f) frustumTests[5]++;
}
for(i = 0; i < 6; i++)
if(frustumTests[i] == 8)
return false; // Box is completely outside of one plane
return true;
}
STARTPATCHES
InjectHook(0x42C760, &CCamera::IsSphereVisible, PATCH_JUMP);
ENDPATCHES

420
src/Camera.h Normal file
View file

@ -0,0 +1,420 @@
#pragma once
#include "Placeable.h"
class CEntity;
class CPed;
class CAutomobile;
#define NUMBER_OF_VECTORS_FOR_AVERAGE 2
struct CCam
{
enum CamMode
{
MODE_TOPDOWN1 = 1,
MODE_TOPDOWN2,
MODE_BEHINDCAR,
MODE_FOLLOWPED,
MODE_AIMING,
MODE_DEBUG,
MODE_SNIPER,
MODE_ROCKET,
MODE_MODELVIEW,
MODE_BILL,
MODE_SYPHON,
MODE_CIRCLE,
MODE_CHEESYZOOM,
MODE_WHEELCAM,
MODE_FIXED,
MODE_FIRSTPERSON,
MODE_FLYBY,
MODE_CAMONASTRING,
MODE_REACTIONCAM,
MODE_FOLLOWPEDWITHBINDING,
MODE_CHRISWITHBINDINGPLUSROTATION,
MODE_BEHINDBOAT,
MODE_PLAYERFALLENWATER,
MODE_CAMONTRAINROOF,
MODE_CAMRUNNINGSIDETRAIN,
MODE_BLOODONTHETRACKS,
MODE_IMTHEPASSENGERWOOWOO,
MODE_SYPHONCRIMINFRONT,
MODE_PEDSDEADBABY,
MODE_CUSHYPILLOWSARSE,
MODE_LOOKATCARS,
MODE_ARRESTCAMONE,
MODE_ARRESTCAMTWO,
MODE_M16FIRSTPERSON_34,
MODE_SPECIALFIXEDFORSYPHON,
MODE_FIGHT,
MODE_TOPDOWNPED,
MODE_FIRSTPERSONPEDONPC_38,
MODE_FIRSTPERSONPEDONPC_39,
MODE_FIRSTPERSONPEDONPC_40,
MODE_FIRSTPERSONPEDONPC_41,
MODE_FIRSTPERSONPEDONPC_42,
MODE_EDITOR,
MODE_M16FIRSTPERSON_44
};
bool bBelowMinDist; //used for follow ped mode
bool bBehindPlayerDesired; //used for follow ped mode
bool m_bCamLookingAtVector;
bool m_bCollisionChecksOn;
bool m_bFixingBeta; //used for camera on a string
bool m_bTheHeightFixerVehicleIsATrain;
bool LookBehindCamWasInFront;
bool LookingBehind;
bool LookingLeft; // 32
bool LookingRight;
bool ResetStatics; //for interpolation type stuff to work
bool Rotating;
int16 Mode; // CameraMode
uint32 m_uiFinishTime; // 52
int m_iDoCollisionChecksOnFrameNum;
int m_iDoCollisionCheckEveryNumOfFrames;
int m_iFrameNumWereAt; // 64
int m_iRunningVectorArrayPos;
int m_iRunningVectorCounter;
int DirectionWasLooking;
float f_max_role_angle; //=DEGTORAD(5.0f);
float f_Roll; //used for adding a slight roll to the camera in the
float f_rollSpeed;
float m_fSyphonModeTargetZOffSet;
float m_unknownZOffset;
float m_fAmountFractionObscured;
float m_fAlphaSpeedOverOneFrame; // 100
float m_fBetaSpeedOverOneFrame;
float m_fBufferedTargetBeta;
float m_fBufferedTargetOrientation;
float m_fBufferedTargetOrientationSpeed;
float m_fCamBufferedHeight;
float m_fCamBufferedHeightSpeed;
float m_fCloseInPedHeightOffset;
float m_fCloseInPedHeightOffsetSpeed; // 132
float m_fCloseInCarHeightOffset;
float m_fCloseInCarHeightOffsetSpeed;
float m_fDimensionOfHighestNearCar;
float m_fDistanceBeforeChanges;
float m_fFovSpeedOverOneFrame;
float m_fMinDistAwayFromCamWhenInterPolating;
float m_fPedBetweenCameraHeightOffset;
float m_fPlayerInFrontSyphonAngleOffSet; // 164
float m_fRadiusForDead;
float m_fRealGroundDist; //used for follow ped mode
float m_fTargetBeta;
float m_fTimeElapsedFloat;
float m_fTransitionBeta;
float m_fTrueBeta;
float m_fTrueAlpha; // 200
float m_fInitialPlayerOrientation; //used for first person
float Alpha;
float AlphaSpeed;
float FOV;
float FOVSpeed;
float Beta;
float BetaSpeed;
float Distance; // 232
float DistanceSpeed;
float CA_MIN_DISTANCE;
float CA_MAX_DISTANCE;
float SpeedVar;
// ped onfoot zoom distance
float m_fTargetZoomGroundOne;
float m_fTargetZoomGroundTwo; // 256
float m_fTargetZoomGroundThree;
// ped onfoot alpha angle offset
float m_fTargetZoomOneZExtra;
float m_fTargetZoomTwoZExtra;
float m_fTargetZoomThreeZExtra;
float m_fTargetZoomZCloseIn;
float m_fMinRealGroundDist;
float m_fTargetCloseInDist;
CVector m_cvecTargetCoorsForFudgeInter; // 360
CVector m_cvecCamFixedModeVector; // 372
CVector m_cvecCamFixedModeSource; // 384
CVector m_cvecCamFixedModeUpOffSet; // 396
CVector m_vecLastAboveWaterCamPosition; //408 //helper for when the player has gone under the water
CVector m_vecBufferedPlayerBodyOffset; // 420
// The three vectors that determine this camera for this frame
CVector Front; // 432 // Direction of looking in
CVector Source; // Coors in world space
CVector SourceBeforeLookBehind;
CVector Up; // Just that
CVector m_arrPreviousVectors[NUMBER_OF_VECTORS_FOR_AVERAGE]; // used to average stuff
CEntity *CamTargetEntity;
float m_fCameraDistance;
float m_fIdealAlpha;
float m_fPlayerVelocity;
CAutomobile *m_pLastCarEntered; // So interpolation works
CPed *m_pLastPedLookedAt;// So interpolation works
bool m_bFirstPersonRunAboutActive;
void Process_Debug(float *vec, float a, float b, float c);
void Process_Kalvin(float*, float, float, float);
void GetVectorsReadyForRW(void);
};
static_assert(sizeof(CCam) == 0x1A4, "CCam: wrong size");
static_assert(offsetof(CCam, Alpha) == 0xA8, "CCam: error");
static_assert(offsetof(CCam, Front) == 0x140, "CCam: error");
struct CCamPathSplines
{
float m_arr_PathData[800];
};
struct CTrainCamNode
{
CVector m_cvecCamPosition;
CVector m_cvecPointToLookAt;
CVector m_cvecMinPointInRange;
CVector m_cvecMaxPointInRange;
float m_fDesiredFOV;
float m_fNearClip;
};
struct CQueuedMode
{
int16 Mode;
float Duration;
int16 MinZoom;
int16 MaxZoom;
};
enum
{
LOOKING_BEHIND,
LOOKING_LEFT,
LOOKING_RIGHT,
LOOKING_FORWARD,
};
struct CCamera : public CPlaceable
{
bool m_bAboveGroundTrainNodesLoaded;
bool m_bBelowGroundTrainNodesLoaded;
bool m_bCamDirectlyBehind;
bool m_bCamDirectlyInFront;
bool m_bCameraJustRestored;
bool m_bcutsceneFinished;
bool m_bCullZoneChecksOn;
bool m_bFirstPersonBeingUsed;
bool m_bJustJumpedOutOf1stPersonBecauseOfTarget;
bool m_bIdleOn;
bool m_bInATunnelAndABigVehicle;
bool m_bInitialNodeFound;
bool m_bInitialNoNodeStaticsSet;
bool m_bIgnoreFadingStuffForMusic;
bool m_bPlayerIsInGarage;
bool m_bJustCameOutOfGarage;
bool m_bJustInitalised;
bool m_bJust_Switched;
bool m_bLookingAtPlayer;
bool m_bLookingAtVector;
bool m_bMoveCamToAvoidGeom;
bool m_bObbeCinematicPedCamOn;
bool m_bObbeCinematicCarCamOn;
bool m_bRestoreByJumpCut;
bool m_bUseNearClipScript;
bool m_bStartInterScript;
bool m_bStartingSpline;
bool m_bTargetJustBeenOnTrain;
bool m_bTargetJustCameOffTrain;
bool m_bUseSpecialFovTrain;
bool m_bUseTransitionBeta;
bool m_bUseScriptZoomValuePed;
bool m_bUseScriptZoomValueCar;
bool m_bWaitForInterpolToFinish;
bool m_bItsOkToLookJustAtThePlayer;
bool m_bWantsToSwitchWidescreenOff;
bool m_WideScreenOn;
bool m_1rstPersonRunCloseToAWall;
bool m_bHeadBob;
bool m_bFailedCullZoneTestPreviously;
bool m_FadeTargetIsSplashScreen;
bool WorldViewerBeingUsed;
uint8 ActiveCam;
uint32 m_uiCamShakeStart;
uint32 m_uiFirstPersonCamLastInputTime;
// where are those?
//bool m_bVehicleSuspenHigh;
//bool m_bEnable1rstPersonCamCntrlsScript;
//bool m_bAllow1rstPersonWeaponsCamera;
uint32 m_uiLongestTimeInMill;
uint32 m_uiNumberOfTrainCamNodes;
uint8 m_uiTransitionJUSTStarted;
uint8 m_uiTransitionState; // 0:one mode 1:transition
uint32 m_uiTimeLastChange;
uint32 m_uiTimeWeEnteredIdle;
uint32 m_uiTimeTransitionStart;
uint32 m_uiTransitionDuration;
int m_BlurBlue;
int m_BlurGreen;
int m_BlurRed;
int m_BlurType;
uint32 unknown;
int m_iWorkOutSpeedThisNumFrames;
int m_iNumFramesSoFar;
int m_iCurrentTrainCamNode;
int m_motionBlur;
int m_imotionBlurAddAlpha;
int m_iCheckCullZoneThisNumFrames;
int m_iZoneCullFrameNumWereAt;
int WhoIsInControlOfTheCamera;
float CamFrontXNorm;
float CamFrontYNorm;
float CarZoomIndicator;
float CarZoomValue;
float CarZoomValueSmooth;
float DistanceToWater;
float FOVDuringInter;
float LODDistMultiplier;
float GenerationDistMultiplier;
float m_fAlphaSpeedAtStartInter;
float m_fAlphaWhenInterPol;
float m_fAlphaDuringInterPol;
float m_fBetaDuringInterPol;
float m_fBetaSpeedAtStartInter;
float m_fBetaWhenInterPol;
float m_fFOVWhenInterPol;
float m_fFOVSpeedAtStartInter;
float m_fStartingBetaForInterPol;
float m_fStartingAlphaForInterPol;
float m_PedOrientForBehindOrInFront;
float m_CameraAverageSpeed;
float m_CameraSpeedSoFar;
float m_fCamShakeForce;
float m_fCarZoomValueScript;
float m_fFovForTrain;
float m_fFOV_Wide_Screen;
float m_fNearClipScript;
float m_fOldBetaDiff;
float m_fPedZoomValue;
float m_fPedZoomValueScript;
float m_fPedZoomValueSmooth;
float m_fPositionAlongSpline;
float m_ScreenReductionPercentage;
float m_ScreenReductionSpeed;
float m_AlphaForPlayerAnim1rstPerson;
float Orientation;
float PedZoomIndicator;
float PlayerExhaustion;
float SoundDistUp, SoundDistLeft, SoundDistRight;
float SoundDistUpAsRead, SoundDistLeftAsRead, SoundDistRightAsRead;
float SoundDistUpAsReadOld, SoundDistLeftAsReadOld, SoundDistRightAsReadOld;
float m_fWideScreenReductionAmount;
float m_fStartingFOVForInterPol;
// not static yet
float m_fMouseAccelHorzntl;// acceleration multiplier for 1st person controls
float m_fMouseAccelVertical;// acceleration multiplier for 1st person controls
float m_f3rdPersonCHairMultX;
float m_f3rdPersonCHairMultY;
CCam Cams[3];
void *pToGarageWeAreIn;
void *pToGarageWeAreInForHackAvoidFirstPerson;
CQueuedMode m_PlayerMode;
CQueuedMode PlayerWeaponMode;
CVector m_PreviousCameraPosition;
CVector m_RealPreviousCameraPosition;
CVector m_cvecAimingTargetCoors;
CVector m_vecFixedModeVector;
// one of those has to go
CVector m_vecFixedModeSource;
CVector m_vecFixedModeUpOffSet;
// CVector m_vecCutSceneOffset;
CVector m_cvecStartingSourceForInterPol;
CVector m_cvecStartingTargetForInterPol;
CVector m_cvecStartingUpForInterPol;
CVector m_cvecSourceSpeedAtStartInter;
CVector m_cvecTargetSpeedAtStartInter;
CVector m_cvecUpSpeedAtStartInter;
CVector m_vecSourceWhenInterPol;
CVector m_vecTargetWhenInterPol;
CVector m_vecUpWhenInterPol;
CVector m_vecClearGeometryVec;
CVector m_vecGameCamPos;
CVector SourceDuringInter;
CVector TargetDuringInter;
CVector UpDuringInter;
RwCamera *m_pRwCamera;
CEntity *pTargetEntity;
CCamPathSplines m_arrPathArray[4];
CTrainCamNode m_arrTrainCamNode[800];
CMatrix m_cameraMatrix;
bool m_bGarageFixedCamPositionSet;
bool m_vecDoingSpecialInterPolation;
bool m_bScriptParametersSetForInterPol;
bool m_bFading;
bool m_bMusicFading;
CMatrix m_viewMatrix;
CVector m_vecFrustumNormals[4];
CVector m_vecOldSourceForInter;
CVector m_vecOldFrontForInter;
CVector m_vecOldUpForInter;
float m_vecOldFOVForInter;
float m_fFLOATingFade;
float m_fFLOATingFadeMusic;
float m_fTimeToFadeOut;
float m_fTimeToFadeMusic;
float m_fFractionInterToStopMovingTarget;
float m_fFractionInterToStopCatchUpTarget;
float m_fGaitSwayBuffer;
float m_fScriptPercentageInterToStopMoving;
float m_fScriptPercentageInterToCatchUp;
uint32 m_fScriptTimeForInterPolation;
int16 m_iFadingDirection;
int m_iModeObbeCamIsInForCar;
int16 m_iModeToGoTo;
int16 m_iMusicFadingDirection;
int16 m_iTypeOfSwitch;
uint32 m_uiFadeTimeStarted;
uint32 m_uiFadeTimeStartedMusic;
CMatrix &GetCameraMatrix(void) { return m_cameraMatrix; }
CVector &GetGameCamPosition(void) { return m_vecGameCamPos; }
bool IsPointVisible(const CVector &center, const CMatrix *mat);
bool IsSphereVisible(const CVector &center, float radius, const CMatrix *mat);
bool IsBoxVisible(RwV3d *box, const CMatrix *mat);
};
static_assert(offsetof(CCamera, m_WideScreenOn) == 0x70, "CCamera: error");
static_assert(offsetof(CCamera, WorldViewerBeingUsed) == 0x75, "CCamera: error");
static_assert(offsetof(CCamera, m_uiNumberOfTrainCamNodes) == 0x84, "CCamera: error");
static_assert(offsetof(CCamera, m_uiTransitionState) == 0x89, "CCamera: error");
static_assert(offsetof(CCamera, m_uiTimeTransitionStart) == 0x94, "CCamera: error");
static_assert(offsetof(CCamera, m_BlurBlue) == 0x9C, "CCamera: error");
static_assert(offsetof(CCamera, Cams) == 0x1A4, "CCamera: error");
static_assert(sizeof(CCamera) == 0xE9D8, "CCamera: wrong size");
extern CCamera &TheCamera;

116
src/Clock.cpp Normal file
View file

@ -0,0 +1,116 @@
#include "common.h"
#include "patcher.h"
#include "Timer.h"
#include "Pad.h"
#include "Clock.h"
uint8 &CClock::ms_nGameClockHours = *(uint8*)0x95CDA6;
uint8 &CClock::ms_nGameClockMinutes = *(uint8*)0x95CDC8;
uint16 &CClock::ms_nGameClockSeconds = *(uint16*)0x95CC7C;
uint8 &CClock::ms_Stored_nGameClockHours = *(uint8*)0x95CD7B;
uint8 &CClock::ms_Stored_nGameClockMinutes = *(uint8*)0x95CD9B;
uint16 &CClock::ms_Stored_nGameClockSeconds = *(uint16*)0x95CC9C;
uint32 &CClock::ms_nMillisecondsPerGameMinute = *(uint32*)0x8F2C64;
int32 &CClock::ms_nLastClockTick = *(int32*)0x9430E4;
bool &CClock::ms_bClockHasBeenStored = *(bool*)0x95CD82;
void
CClock::Initialise(uint32 scale)
{
debug("Initialising CClock...\n");
ms_nGameClockHours = 12;
ms_nGameClockMinutes = 0;
ms_nGameClockSeconds = 0;
ms_nMillisecondsPerGameMinute = scale;
ms_nLastClockTick = CTimer::GetTimeInMilliseconds();
ms_bClockHasBeenStored = false;
debug("CClock ready\n");
}
void
CClock::Update(void)
{
if(CPad::GetPad(1)->NewState.r1){
ms_nGameClockMinutes += 8;
ms_nLastClockTick = CTimer::GetTimeInMilliseconds();
if(ms_nGameClockMinutes >= 60){
ms_nGameClockHours++;
ms_nGameClockMinutes = 0;
if(ms_nGameClockHours >= 24)
ms_nGameClockHours = 0;
}
}else
if(CTimer::GetTimeInMilliseconds() - ms_nLastClockTick >
ms_nMillisecondsPerGameMinute){
ms_nGameClockMinutes++;
ms_nLastClockTick += ms_nMillisecondsPerGameMinute;
if(ms_nGameClockMinutes >= 60){
ms_nGameClockHours++;
ms_nGameClockMinutes = 0;
if(ms_nGameClockHours >= 24)
ms_nGameClockHours = 0;
// TODO: stats days passed
}
}
ms_nGameClockSeconds +=
60
* (CTimer::GetTimeInMilliseconds() - ms_nLastClockTick)
/ ms_nMillisecondsPerGameMinute;
}
void
CClock::SetGameClock(uint8 h, uint8 m)
{
ms_nGameClockHours = h;
ms_nGameClockMinutes = m;
ms_nGameClockSeconds = 0;
ms_nLastClockTick = CTimer::GetTimeInMilliseconds();
}
int32
CClock::GetGameClockMinutesUntil(uint8 h, uint8 m)
{
int32 now, then;
now = ms_nGameClockHours*60 + ms_nGameClockMinutes;
then = h*60 + m;
if(then < now)
then += 24*60;
return then-now;
}
bool
CClock::GetIsTimeInRange(uint8 h1, uint8 h2)
{
if(h1 > h2)
return ms_nGameClockHours >= h1 || ms_nGameClockHours < h2;
else
return ms_nGameClockHours >= h1 && ms_nGameClockHours < h2;
}
void
CClock::StoreClock(void)
{
ms_Stored_nGameClockHours = ms_nGameClockHours;
ms_Stored_nGameClockMinutes = ms_nGameClockMinutes;
ms_Stored_nGameClockSeconds = ms_nGameClockSeconds;
ms_bClockHasBeenStored = true;
}
void
CClock::RestoreClock(void)
{
ms_nGameClockHours = ms_Stored_nGameClockHours;
ms_nGameClockMinutes = ms_Stored_nGameClockMinutes;
ms_nGameClockSeconds = ms_Stored_nGameClockSeconds;
}
STARTPATCHES
InjectHook(0x473370, CClock::Initialise, PATCH_JUMP);
InjectHook(0x473460, CClock::Update, PATCH_JUMP);
InjectHook(0x4733C0, CClock::SetGameClock, PATCH_JUMP);
InjectHook(0x4733F0, CClock::GetGameClockMinutesUntil, PATCH_JUMP);
InjectHook(0x473420, CClock::GetIsTimeInRange, PATCH_JUMP);
InjectHook(0x473540, CClock::StoreClock, PATCH_JUMP);
InjectHook(0x473570, CClock::RestoreClock, PATCH_JUMP);
ENDPATCHES

27
src/Clock.h Normal file
View file

@ -0,0 +1,27 @@
#pragma once
class CClock
{
static uint8 &ms_nGameClockHours;
static uint8 &ms_nGameClockMinutes;
static uint16 &ms_nGameClockSeconds;
static uint8 &ms_Stored_nGameClockHours;
static uint8 &ms_Stored_nGameClockMinutes;
static uint16 &ms_Stored_nGameClockSeconds;
static uint32 &ms_nMillisecondsPerGameMinute;
static int32 &ms_nLastClockTick;
static bool &ms_bClockHasBeenStored;
public:
static void Initialise(uint32 scale);
static void Update(void);
static void SetGameClock(uint8 h, uint8 m);
static int32 GetGameClockMinutesUntil(uint8 h, uint8 m);
static bool GetIsTimeInRange(uint8 h1, uint8 h2);
static void StoreClock(void);
static void RestoreClock(void);
static int8 GetHours(void) { return ms_nGameClockHours; }
static int8 GetMinutes(void) { return ms_nGameClockMinutes; }
static int16 GetSeconds(void) { return ms_nGameClockSeconds; }
};

1629
src/Collision.cpp Normal file

File diff suppressed because it is too large Load diff

152
src/Collision.h Normal file
View file

@ -0,0 +1,152 @@
#pragma once
#include "templates.h"
#include "Game.h" // for eLevelName
struct CColSphere
{
CVector center;
float radius;
uint8 surface;
uint8 piece;
void Set(float radius, const CVector &center, uint8 surf, uint8 piece);
void Set(float radius, const CVector &center) { this->center = center; this->radius = radius; }
};
struct CColBox
{
CVector min;
CVector max;
uint8 surface;
uint8 piece;
void Set(const CVector &min, const CVector &max, uint8 surf, uint8 piece);
};
struct CColLine
{
CVector p0;
int pad0;
CVector p1;
int pad1;
CColLine(void) { };
CColLine(const CVector &p0, const CVector &p1) { this->p0 = p0; this->p1 = p1; };
void Set(const CVector &p0, const CVector &p1);
CColLine *ctor(CVector *p0, CVector *p1) { return ::new (this) CColLine(*p0, *p1); }
};
struct CColTriangle
{
uint16 a;
uint16 b;
uint16 c;
uint8 surface;
void Set(const CVector *v, int a, int b, int c, uint8 surf, uint8 piece);
};
struct CColTrianglePlane
{
CVector normal;
float dist;
uint8 dir;
void Set(const CVector *v, CColTriangle &tri);
void GetNormal(CVector &n) const { n = normal; }
float CalcPoint(const CVector &v) const { return DotProduct(normal, v) - dist; };
};
struct CColPoint
{
CVector point;
int pad1;
// the surface normal on the surface of point
CVector normal;
int pad2;
uint8 surfaceA;
uint8 pieceA;
uint8 surfaceB;
uint8 pieceB;
float depth;
};
struct CStoredCollPoly
{
CVector verts[3];
bool valid;
};
struct CColModel
{
CColSphere boundingSphere;
CColBox boundingBox;
short numSpheres;
short numLines;
short numBoxes;
short numTriangles;
int level;
bool ownsCollisionVolumes;
CColSphere *spheres;
CColLine *lines;
CColBox *boxes;
CVector *vertices;
CColTriangle *triangles;
CColTrianglePlane *trianglePlanes;
CColModel(void);
~CColModel(void);
void RemoveCollisionVolumes(void);
void CalculateTrianglePlanes(void);
void RemoveTrianglePlanes(void);
CLink<CColModel*> *GetLinkPtr(void);
void SetLinkPtr(CLink<CColModel*>*);
void GetTrianglePoint(CVector &v, int i) const;
CColModel *ctor(void) { return ::new (this) CColModel(); }
void dtor(void) { this->CColModel::~CColModel(); }
};
class CCollision
{
public:
static eLevelName &ms_collisionInMemory;
static CLinkList<CColModel*> &ms_colModelCache;
static void Init(void);
static void Update(void);
static void LoadCollisionWhenINeedIt(bool changeLevel);
static void DrawColModel(const CMatrix &mat, const CColModel &colModel);
static void DrawColModel_Coloured(const CMatrix &mat, const CColModel &colModel, int32 id);
static void CalculateTrianglePlanes(CColModel *model);
// all these return true if there's a collision
static bool TestSphereSphere(const CColSphere &s1, const CColSphere &s2);
static bool TestSphereBox(const CColSphere &sph, const CColBox &box);
static bool TestLineBox(const CColLine &line, const CColBox &box);
static bool TestVerticalLineBox(const CColLine &line, const CColBox &box);
static bool TestLineTriangle(const CColLine &line, const CVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane);
static bool TestLineSphere(const CColLine &line, const CColSphere &sph);
static bool TestSphereTriangle(const CColSphere &sphere, const CVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane);
static bool TestLineOfSight(CColLine &line, const CMatrix &matrix, CColModel &model, bool ignoreSurf78);
static bool ProcessSphereSphere(const CColSphere &s1, const CColSphere &s2, CColPoint &point, float &mindistsq);
static bool ProcessSphereBox(const CColSphere &sph, const CColBox &box, CColPoint &point, float &mindistsq);
static bool ProcessLineBox(const CColLine &line, const CColBox &box, CColPoint &point, float &mindist);
static bool ProcessVerticalLineTriangle(const CColLine &line, const CVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane, CColPoint &point, float &mindist, CStoredCollPoly *poly);
static bool ProcessLineTriangle(const CColLine &line , const CVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane, CColPoint &point, float &mindist);
static bool ProcessLineSphere(const CColLine &line, const CColSphere &sphere, CColPoint &point, float &mindist);
static bool ProcessSphereTriangle(const CColSphere &sph, const CVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane, CColPoint &point, float &mindistsq);
static bool ProcessLineOfSight(const CColLine &line, const CMatrix &matrix, CColModel &model, CColPoint &point, float &mindist, bool ignoreSurf78);
static bool ProcessVerticalLine(const CColLine &line, const CMatrix &matrix, CColModel &model, CColPoint &point, float &mindist, bool ignoreSurf78, CStoredCollPoly *poly);
static int32 ProcessColModels(const CMatrix &matrix1, CColModel &model1, const CMatrix &matrix2, CColModel &model2, CColPoint *point1, CColPoint *point2, float *linedists);
// TODO:
// CCollision::IsStoredPolyStillValidVerticalLine
static float DistToLine(const CVector *l0, const CVector *l1, const CVector *point);
static float DistToLine(const CVector *l0, const CVector *l1, const CVector *point, CVector &closest);
};

323
src/CullZones.cpp Normal file
View file

@ -0,0 +1,323 @@
#include "common.h"
#include "patcher.h"
#include "Building.h"
#include "Treadable.h"
#include "Pools.h"
#include "Timer.h"
#include "Camera.h"
#include "World.h"
#include "CullZones.h"
int32 &CCullZones::NumCullZones = *(int*)0x8F2564;
CCullZone *CCullZones::aZones = (CCullZone*)0x864750; // [NUMCULLZONES];
int32 &CCullZones::NumAttributeZones = *(int*)0x8E29D0;
CAttributeZone *CCullZones::aAttributeZones = (CAttributeZone*)0x709C60; // [NUMATTRIBZONES];
uint16 *CCullZones::aIndices = (uint16*)0x847330; // [NUMZONEINDICES];
int16 *CCullZones::aPointersToBigBuildingsForBuildings = (int16*)0x86C9D0; // [NUMBUILDINGS];
int16 *CCullZones::aPointersToBigBuildingsForTreadables = (int16*)0x8F1B8C; // [NUMTREADABLES];
int32 &CCullZones::CurrentWantedLevelDrop_Player = *(int32*)0x880DA8;
int32 &CCullZones::CurrentFlags_Camera = *(int32*)0x940718;
int32 &CCullZones::CurrentFlags_Player = *(int32*)0x9415F0;
int32 &CCullZones::OldCullZone = *(int32*)0x8E2C90;
int32 &CCullZones::EntityIndicesUsed = *(int32*)0x8F2508;
bool &CCullZones::bCurrentSubwayIsInvisible = *(bool*)0x95CDA5;
bool &CCullZones::bCullZonesDisabled = *(bool*)0x95CD4A;
void
CCullZones::Init(void)
{
int i;
NumAttributeZones = 0;
NumCullZones = 0;
CurrentWantedLevelDrop_Player = 0;
CurrentFlags_Camera = 0;
CurrentFlags_Player = 0;
OldCullZone = -1;
EntityIndicesUsed = 0;
bCurrentSubwayIsInvisible = false;
for(i = 0; i < NUMBUILDINGS; i++)
aPointersToBigBuildingsForBuildings[i] = -1;
for(i = 0; i < NUMTREADABLES; i++)
aPointersToBigBuildingsForTreadables[i] = -1;
}
void
CCullZones::Update(void)
{
bool invisible;
CVector v;
if(bCullZonesDisabled)
return;
switch(CTimer::GetFrameCounter() & 7){
case 0:
case 4:
/* Update Cull zone */
ForceCullZoneCoors(TheCamera.GetGameCamPosition());
break;
case 2:
/* Update camera attributes */
CurrentFlags_Camera = FindAttributesForCoors(TheCamera.GetGameCamPosition(), nil);
invisible = (CurrentFlags_Camera & ATTRZONE_SUBWAYVISIBLE) == 0;
if(invisible != bCurrentSubwayIsInvisible){
MarkSubwayAsInvisible(!invisible);
bCurrentSubwayIsInvisible = invisible;
}
break;
case 6:
/* Update player attributes */
CurrentFlags_Player = FindAttributesForCoors(FindPlayerCoors(v),
&CurrentWantedLevelDrop_Player);
break;
}
}
void
CCullZones::ForceCullZoneCoors(CVector coors)
{
int32 z;
z = FindCullZoneForCoors(coors);
if(z != OldCullZone){
if(OldCullZone >= 0)
aZones[OldCullZone].DoStuffLeavingZone();
if(z >= 0)
aZones[z].DoStuffEnteringZone();
OldCullZone = z;
}
}
int32
CCullZones::FindCullZoneForCoors(CVector coors)
{
int i;
for(i = 0; i < NumCullZones; i++)
if(coors.x >= aZones[i].minx && coors.x <= aZones[i].maxx &&
coors.y >= aZones[i].miny && coors.y <= aZones[i].maxy &&
coors.z >= aZones[i].minz && coors.z <= aZones[i].maxz)
return i;
return -1;
}
int32
CCullZones::FindAttributesForCoors(CVector coors, int32 *wantedLevel)
{
int i;
int32 attribs;
attribs = 0;
for(i = 0; i < NumAttributeZones; i++)
if(coors.x >= aAttributeZones[i].minx && coors.x <= aAttributeZones[i].maxx &&
coors.y >= aAttributeZones[i].miny && coors.y <= aAttributeZones[i].maxy &&
coors.z >= aAttributeZones[i].minz && coors.z <= aAttributeZones[i].maxz){
attribs |= aAttributeZones[i].attributes;
if(wantedLevel && *wantedLevel <= aAttributeZones[i].wantedLevel)
*wantedLevel = aAttributeZones[i].wantedLevel;
}
return attribs;
}
CAttributeZone*
CCullZones::FindZoneWithStairsAttributeForPlayer(void)
{
int i;
CVector coors;
FindPlayerCoors(coors);
for(i = 0; i < NumAttributeZones; i++)
if(aAttributeZones[i].attributes & ATTRZONE_STAIRS &&
coors.x >= aAttributeZones[i].minx && coors.x <= aAttributeZones[i].maxx &&
coors.y >= aAttributeZones[i].miny && coors.y <= aAttributeZones[i].maxy &&
coors.z >= aAttributeZones[i].minz && coors.z <= aAttributeZones[i].maxz)
return &aAttributeZones[i];
return nil;
}
WRAPPER void
CCullZones::MarkSubwayAsInvisible(bool visible)
{ EAXJMP(0x525AF0);
}
void
CCullZones::AddCullZone(CVector const &position,
float minx, float maxx,
float miny, float maxy,
float minz, float maxz,
uint16 flag, int16 wantedLevel)
{
CCullZone *cull;
CAttributeZone *attrib;
CVector v;
if((flag & ATTRZONE_NOTCULLZONE) == 0){
cull = &aZones[NumCullZones++];
v = position;
// WTF is this?
if((v-CVector(1032.14f, -624.255f, 24.93f)).Magnitude() < 1.0f)
v = CVector(1061.7f, -613.0f, 19.0f);
if((v-CVector(1029.48f, -495.757f, 21.98f)).Magnitude() < 1.0f)
v = CVector(1061.4f, -506.0f, 18.5f);
cull->position.x = clamp(v.x, minx, maxx);
cull->position.y = clamp(v.y, miny, maxy);
cull->position.z = clamp(v.z, minz, maxz);
cull->minx = minx;
cull->maxx = maxx;
cull->miny = miny;
cull->maxy = maxy;
cull->minz = minz;
cull->maxz = maxz;
cull->unk2 = 0;
cull->unk3 = 0;
cull->unk4 = 0;
cull->m_indexStart = 0;
}
if(flag & ~ATTRZONE_NOTCULLZONE){
attrib = &aAttributeZones[NumAttributeZones++];
attrib->minx = minx;
attrib->maxx = maxx;
attrib->miny = miny;
attrib->maxy = maxy;
attrib->minz = minz;
attrib->maxz = maxz;
attrib->attributes = flag;
attrib->wantedLevel = wantedLevel;
}
}
void
CCullZone::DoStuffLeavingZone(void)
{
int i;
for(i = 0; i < m_numBuildings; i++)
DoStuffLeavingZone_OneBuilding(CCullZones::aIndices[m_indexStart + i]);
for(; i < m_numBuildings + m_numTreadablesPlus10m + m_numTreadables ; i++)
DoStuffLeavingZone_OneTreadableBoth(CCullZones::aIndices[m_indexStart + i]);
}
void
CCullZone::DoStuffLeavingZone_OneBuilding(uint16 i)
{
int16 bb;
int j;
if(i < 6000){
CPools::GetBuildingPool()->GetSlot(i)->m_bZoneCulled = false;
bb = CCullZones::aPointersToBigBuildingsForBuildings[i];
if(bb != -1)
CPools::GetBuildingPool()->GetSlot(bb)->m_bZoneCulled = false;
}else{
i -= 6000;
for(j = 0; j < 3; j++)
DoStuffLeavingZone_OneBuilding(CCullZones::aIndices[i+j]);
}
}
void
CCullZone::DoStuffLeavingZone_OneTreadableBoth(uint16 i)
{
int16 bb;
int j;
if(i < 6000){
CPools::GetTreadablePool()->GetSlot(i)->m_bZoneCulled = false;
CPools::GetTreadablePool()->GetSlot(i)->m_bZoneCulled2 = false;
bb = CCullZones::aPointersToBigBuildingsForTreadables[i];
if(bb != -1)
CPools::GetBuildingPool()->GetSlot(bb)->m_bZoneCulled = false;
}else{
i -= 6000;
for(j = 0; j < 3; j++)
DoStuffLeavingZone_OneTreadableBoth(CCullZones::aIndices[i+j]);
}
}
void
CCullZone::DoStuffEnteringZone(void)
{
int i;
for(i = 0; i < m_numBuildings; i++)
DoStuffEnteringZone_OneBuilding(CCullZones::aIndices[m_indexStart + i]);
for(; i < m_numBuildings + m_numTreadablesPlus10m; i++)
DoStuffEnteringZone_OneTreadablePlus10m(CCullZones::aIndices[m_indexStart + i]);
for(; i < m_numBuildings + m_numTreadablesPlus10m + m_numTreadables; i++)
DoStuffEnteringZone_OneTreadable(CCullZones::aIndices[m_indexStart + i]);
}
void
CCullZone::DoStuffEnteringZone_OneBuilding(uint16 i)
{
int16 bb;
int j;
if(i < 6000){
CPools::GetBuildingPool()->GetSlot(i)->m_bZoneCulled = true;
bb = CCullZones::aPointersToBigBuildingsForBuildings[i];
if(bb != -1)
CPools::GetBuildingPool()->GetSlot(bb)->m_bZoneCulled = true;
}else{
i -= 6000;
for(j = 0; j < 3; j++)
DoStuffLeavingZone_OneBuilding(CCullZones::aIndices[i+j]);
}
}
void
CCullZone::DoStuffEnteringZone_OneTreadablePlus10m(uint16 i)
{
int16 bb;
int j;
if(i < 6000){
CPools::GetTreadablePool()->GetSlot(i)->m_bZoneCulled = true;;
CPools::GetTreadablePool()->GetSlot(i)->m_bZoneCulled2 = true;;
bb = CCullZones::aPointersToBigBuildingsForTreadables[i];
if(bb != -1)
CPools::GetBuildingPool()->GetSlot(bb)->m_bZoneCulled = true;
}else{
i -= 6000;
for(j = 0; j < 3; j++)
DoStuffLeavingZone_OneBuilding(CCullZones::aIndices[i+j]);
}
}
void
CCullZone::DoStuffEnteringZone_OneTreadable(uint16 i)
{
int16 bb;
int j;
if(i < 6000){
CPools::GetTreadablePool()->GetSlot(i)->m_bZoneCulled = true;;
bb = CCullZones::aPointersToBigBuildingsForTreadables[i];
if(bb != -1)
CPools::GetBuildingPool()->GetSlot(bb)->m_bZoneCulled = true;
}else{
i -= 6000;
for(j = 0; j < 3; j++)
DoStuffLeavingZone_OneBuilding(CCullZones::aIndices[i+j]);
}
}
STARTPATCHES
InjectHook(0x524BC0, &CCullZones::Init, PATCH_JUMP);
InjectHook(0x524F80, &CCullZones::Update, PATCH_JUMP);
InjectHook(0x525370, &CCullZones::AddCullZone, PATCH_JUMP);
InjectHook(0x5250D0, &CCullZones::ForceCullZoneCoors, PATCH_JUMP);
InjectHook(0x525130, &CCullZones::FindCullZoneForCoors, PATCH_JUMP);
InjectHook(0x5251C0, &CCullZones::FindAttributesForCoors, PATCH_JUMP);
InjectHook(0x525290, &CCullZones::FindZoneWithStairsAttributeForPlayer, PATCH_JUMP);
InjectHook(0x525610, &CCullZone::DoStuffLeavingZone, PATCH_JUMP);
InjectHook(0x525810, &CCullZone::DoStuffEnteringZone, PATCH_JUMP);
ENDPATCHES

93
src/CullZones.h Normal file
View file

@ -0,0 +1,93 @@
class CCullZone
{
public:
CVector position;
float minx;
float maxx;
float miny;
float maxy;
float minz;
float maxz;
// TODO: figure these out:
int32 m_indexStart;
int16 unk2;
int16 unk3;
int16 unk4;
int16 m_numBuildings;
int16 m_numTreadablesPlus10m;
int16 m_numTreadables;
void DoStuffLeavingZone(void);
static void DoStuffLeavingZone_OneBuilding(uint16 i);
static void DoStuffLeavingZone_OneTreadableBoth(uint16 i);
void DoStuffEnteringZone(void);
static void DoStuffEnteringZone_OneBuilding(uint16 i);
static void DoStuffEnteringZone_OneTreadablePlus10m(uint16 i);
static void DoStuffEnteringZone_OneTreadable(uint16 i);
};
enum eZoneAttribs
{
ATTRZONE_CAMCLOSEIN = 1,
ATTRZONE_STAIRS = 2,
ATTRZONE_1STPERSON = 4,
ATTRZONE_NORAIN = 8,
ATTRZONE_NOPOLICE = 0x10,
ATTRZONE_NOTCULLZONE = 0x20,
ATTRZONE_DOINEEDCOLLISION = 0x40,
ATTRZONE_SUBWAYVISIBLE = 0x80,
};
struct CAttributeZone
{
float minx;
float maxx;
float miny;
float maxy;
float minz;
float maxz;
int16 attributes;
int16 wantedLevel;
};
class CCullZones
{
public:
static int32 &NumCullZones;
static CCullZone *aZones; // [NUMCULLZONES];
static int32 &NumAttributeZones;
static CAttributeZone *aAttributeZones; // [NUMATTRIBZONES];
static uint16 *aIndices; // [NUMZONEINDICES];
static int16 *aPointersToBigBuildingsForBuildings; // [NUMBUILDINGS];
static int16 *aPointersToBigBuildingsForTreadables; // [NUMTREADABLES];
static int32 &CurrentWantedLevelDrop_Player;
static int32 &CurrentFlags_Camera;
static int32 &CurrentFlags_Player;
static int32 &OldCullZone;
static int32 &EntityIndicesUsed;
static bool &bCurrentSubwayIsInvisible;
static bool &bCullZonesDisabled;
static void Init(void);
static void Update(void);
static void ForceCullZoneCoors(CVector coors);
static int32 FindCullZoneForCoors(CVector coors);
static int32 FindAttributesForCoors(CVector coors, int32 *wantedLevel);
static CAttributeZone *FindZoneWithStairsAttributeForPlayer(void);
static void MarkSubwayAsInvisible(bool visible);
static void AddCullZone(CVector const &position,
float minx, float maxx,
float miny, float maxy,
float minz, float maxz,
uint16 flag, int16 wantedLevel);
static bool CamCloseInForPlayer(void) { return (CurrentFlags_Player & ATTRZONE_CAMCLOSEIN) != 0; }
static bool CamStairsForPlayer(void) { return (CurrentFlags_Player & ATTRZONE_STAIRS) != 0; }
static bool Cam1stPersonForPlayer(void) { return (CurrentFlags_Player & ATTRZONE_1STPERSON) != 0; }
static bool NoPolice(void) { return (CurrentFlags_Player & ATTRZONE_NOPOLICE) != 0; }
static bool DoINeedToLoadCollision(void) { return (CurrentFlags_Player & ATTRZONE_DOINEEDCOLLISION) != 0; }
static bool PlayerNoRain(void) { return (CurrentFlags_Player & ATTRZONE_NORAIN) != 0; }
static bool CamNoRain(void) { return (CurrentFlags_Camera & ATTRZONE_NORAIN) != 0; }
static int32 GetWantedLevelDrop(void) { return CurrentWantedLevelDrop_Player; }
};

5
src/Game.cpp Normal file
View file

@ -0,0 +1,5 @@
#include "common.h"
#include "patcher.h"
#include "Game.h"
int &CGame::currLevel = *(int*)0x941514;

15
src/Game.h Normal file
View file

@ -0,0 +1,15 @@
#pragma once
enum eLevelName
{
LEVEL_NONE = 0,
LEVEL_INDUSTRIAL,
LEVEL_COMMERCIAL,
LEVEL_SUBURBAN
};
class CGame
{
public:
static int &currLevel;
};

15
src/General.h Normal file
View file

@ -0,0 +1,15 @@
class CGeneral
{
public:
static float GetATanOfXY(float x, float y){
if(y >= 0.0f) return atan2(x, y);
return atan2(x, y) + 2*M_PI;
}
// not too sure about all these...
static uint16 GetRandomNumber(void)
{ return myrand() & 0xFFFF; }
// Probably don't want to ever reach high
static float GetRandomNumberInRange(float low, float high)
{ return low + (high - low)*(GetRandomNumber()/65536.0f); }
};

15
src/Glass.cpp Normal file
View file

@ -0,0 +1,15 @@
#include "common.h"
#include "patcher.h"
#include "Glass.h"
WRAPPER void
CGlass::WindowRespondsToCollision(CEntity *ent, float amount, CVector speed, CVector point, bool foo)
{
EAXJMP(0x503F10);
}
WRAPPER void
CGlass::WindowRespondsToSoftCollision(CEntity *ent, float amount)
{
EAXJMP(0x504630);
}

10
src/Glass.h Normal file
View file

@ -0,0 +1,10 @@
#pragma once
class CEntity;
class CGlass
{
public:
static void WindowRespondsToCollision(CEntity *ent, float amount, CVector speed, CVector point, bool foo);
static void WindowRespondsToSoftCollision(CEntity *ent, float amount);
};

26
src/Lists.cpp Normal file
View file

@ -0,0 +1,26 @@
#include "common.h"
#include "Pools.h"
#include "Lists.h"
void*
CPtrNode::operator new(size_t){
CPtrNode *node = CPools::GetPtrNodePool()->New();
assert(node);
return node;
}
void
CPtrNode::operator delete(void *p, size_t){
CPools::GetPtrNodePool()->Delete((CPtrNode*)p);
}
void*
CEntryInfoNode::operator new(size_t){
CEntryInfoNode *node = CPools::GetEntryInfoNodePool()->New();
assert(node);
return node;
}
void
CEntryInfoNode::operator delete(void *p, size_t){
CPools::GetEntryInfoNodePool()->Delete((CEntryInfoNode*)p);
}

130
src/Lists.h Normal file
View file

@ -0,0 +1,130 @@
#pragma once
class CPtrNode
{
public:
void *item;
CPtrNode *prev;
CPtrNode *next;
void *operator new(size_t);
void operator delete(void *p, size_t);
};
class CPtrList
{
public:
CPtrNode *first;
CPtrList(void) { first = nil; }
~CPtrList(void) { Flush(); }
CPtrNode *FindItem(void *item){
CPtrNode *node;
for(node = first; node; node = node->next)
if(node->item == item)
return node;
return nil;
}
CPtrNode *InsertNode(CPtrNode *node){
node->prev = nil;
node->next = first;
if(first)
first->prev = node;
first = node;
return node;
}
CPtrNode *InsertItem(void *item){
CPtrNode *node = new CPtrNode;
node->item = item;
InsertNode(node);
return node;
}
void RemoveNode(CPtrNode *node){
if(node == first)
first = node->next;
if(node->prev)
node->prev->next = node->next;
if(node->next)
node->next->prev = node->prev;
}
void DeleteNode(CPtrNode *node){
RemoveNode(node);
delete node;
}
void RemoveItem(void *item){
CPtrNode *node, *next;
for(node = first; node; node = next){
next = node->next;
if(node->item == item)
DeleteNode(node);
}
}
void Flush(void){
CPtrNode *node, *next;
for(node = first; node; node = next){
next = node->next;
DeleteNode(node);
}
}
};
class CSector;
// This records in which sector list a Physical is
class CEntryInfoNode
{
public:
CPtrList *list; // list in sector
CPtrNode *listnode; // node in list
CSector *sector;
CEntryInfoNode *prev;
CEntryInfoNode *next;
void *operator new(size_t);
void operator delete(void *p, size_t);
};
class CEntryInfoList
{
public:
CEntryInfoNode *first;
CEntryInfoList(void) { first = nil; }
~CEntryInfoList(void) { Flush(); }
CEntryInfoNode *InsertNode(CEntryInfoNode *node){
node->prev = nil;
node->next = first;
if(first)
first->prev = node;
first = node;
return node;
}
CEntryInfoNode *InsertItem(CPtrList *list, CPtrNode *listnode, CSector *sect){
CEntryInfoNode *node = new CEntryInfoNode;
node->list = list;
node->listnode = listnode;
node->sector = sect;
InsertNode(node);
return node;
}
void RemoveNode(CEntryInfoNode *node){
if(node == first)
first = node->next;
if(node->prev)
node->prev->next = node->next;
if(node->next)
node->next->prev = node->prev;
}
void DeleteNode(CEntryInfoNode *node){
RemoveNode(node);
delete node;
}
void Flush(void){
CEntryInfoNode *node, *next;
for(node = first; node; node = next){
next = node->next;
DeleteNode(node);
}
}
};

4
src/MenuManager.cpp Normal file
View file

@ -0,0 +1,4 @@
#include "common.h"
#include "MenuManager.h"
int &CMenuManager::m_PrefsBrightness = *(int*)0x5F2E50;

5
src/MenuManager.h Normal file
View file

@ -0,0 +1,5 @@
class CMenuManager
{
public:
static int &m_PrefsBrightness;
};

15
src/NodeName.cpp Normal file
View file

@ -0,0 +1,15 @@
#include "common.h"
#include "patcher.h"
#include "NodeName.h"
int &gPluginOffset = *(int*)0x64C610;
#define NODENAMEEXT(o) (RWPLUGINOFFSET(char, o, gPluginOffset))
char*
GetFrameNodeName(RwFrame *frame)
{
if(gPluginOffset < 0)
return nil;
return NODENAMEEXT(frame);
}

3
src/NodeName.h Normal file
View file

@ -0,0 +1,3 @@
#pragma once
char *GetFrameNodeName(RwFrame *frame);

37
src/Pad.cpp Normal file
View file

@ -0,0 +1,37 @@
#include "common.h"
#include "Pad.h"
CPad *CPad::Pads = (CPad*)0x6F0360;
uint16 *CPad::OldKeyState = (uint16*)0x6F1E70;
uint16 *CPad::NewKeyState = (uint16*)0x6E60D0;
uint16 *CPad::TempKeyState = (uint16*)0x774DE8;
CMouseControllerState &CPad::OldMouseControllerState = *(CMouseControllerState*)0x8472A0;
CMouseControllerState &CPad::NewMouseControllerState = *(CMouseControllerState*)0x8809F0;
CMouseControllerState &CPad::PCTempMouseControllerState = *(CMouseControllerState*)0x6F1E60;
void
CControllerState::Clear(void)
{
leftX = 0;
leftY = 0;
rightX = 0;
rightY = 0;
l1 = 0;
l2 = 0;
r1 = 0;
r2 = 0;
up = 0;
down = 0;
left = 0;
right = 0;
start = 0;
select = 0;
square = 0;
triangle = 0;
cross = 0;
circle = 0;
leftshock = 0;
rightshock = 0;
}

116
src/Pad.h Normal file
View file

@ -0,0 +1,116 @@
#pragma once
// same as RW skeleton
enum Key
{
// ascii...
KEY_ESC = 128,
KEY_F1 = 129,
KEY_F2 = 130,
KEY_F3 = 131,
KEY_F4 = 132,
KEY_F5 = 133,
KEY_F6 = 134,
KEY_F7 = 135,
KEY_F8 = 136,
KEY_F9 = 137,
KEY_F10 = 138,
KEY_F11 = 139,
KEY_F12 = 140,
KEY_INS = 141,
KEY_DEL = 142,
KEY_HOME = 143,
KEY_END = 144,
KEY_PGUP = 145,
KEY_PGDN = 146,
KEY_UP = 147,
KEY_DOWN = 148,
KEY_LEFT = 149,
KEY_RIGHT = 150,
// some stuff ommitted
KEY_BACKSP = 168,
KEY_TAB = 169,
KEY_CAPSLK = 170,
KEY_ENTER = 171,
KEY_LSHIFT = 172,
KEY_RSHIFT = 173,
KEY_LCTRL = 174,
KEY_RCTRL = 175,
KEY_LALT = 176,
KEY_RALT = 177,
KEY_NULL, // unused
KEY_NUMKEYS,
};
class CControllerState
{
public:
int16 leftX, leftY;
int16 rightX, rightY;
int16 l1, l2;
int16 r1, r2;
int16 up, down, left, right;
int16 start, select;
int16 square, triangle, cross, circle;
int16 leftshock, rightshock;
int16 networktalk;
float getLeftX(void) { return leftX/32767.0f; };
float getLeftY(void) { return leftY/32767.0f; };
float getRightX(void) { return rightX/32767.0f; };
float getRightY(void) { return rightY/32767.0f; };
void Clear(void);
};
static_assert(sizeof(CControllerState) == 0x2A, "CControllerState: error");
struct CMouseControllerState
{
uint32 btns; // bit 0-2 button 1-3
int x, y;
};
class CPad
{
public:
CControllerState NewState;
CControllerState OldState;
CControllerState PCTempKeyState;
CControllerState PCTempJoyState;
CControllerState PCTempMouseState;
// straight out of my IDB
int16 Phase;
int16 Mode;
int16 ShakeDur;
int8 ShakeFreq;
int8 bHornHistory[5];
int8 iCurrHornHistory;
int8 DisablePlayerControls;
int8 JustOutOfFrontEnd;
int8 bApplyBrakes;
int32 unk[3];
int32 LastTimeTouched;
int32 AverageWeapon;
int32 AverageEntries;
static CPad *Pads; //[2];
static uint16 *OldKeyState; //[KEY_NUMKEYS];
static uint16 *NewKeyState; //[KEY_NUMKEYS];
static uint16 *TempKeyState; //[KEY_NUMKEYS];
static CMouseControllerState &OldMouseControllerState;
static CMouseControllerState &NewMouseControllerState;
static CMouseControllerState &PCTempMouseControllerState;
static CPad *GetPad(int n) { return &Pads[n]; }
};
static_assert(sizeof(CPad) == 0xFC, "CPad: error");
#define IsButtonJustDown(pad, btn) \
(!(pad)->OldState.btn && (pad)->NewState.btn)

5
src/ParticleObject.cpp Normal file
View file

@ -0,0 +1,5 @@
#include "common.h"
#include "patcher.h"
#include "ParticleObject.h"
void CParticleObject::AddObject(uint16, const CVector &pos, bool remove) { EAXJMP(0x4BC4D0); }

31
src/ParticleObject.h Normal file
View file

@ -0,0 +1,31 @@
#pragma once
enum eParticleObjectType
{
POBJECT_PAVEMENT_STEAM,
POBJECT_PAVEMENT_STEAM_SLOWMOTION,
POBJECT_WALL_STEAM,
POBJECT_WALL_STEAM_SLOWMOTION,
POBJECT_DARK_SMOKE,
POBJECT_FIRE_HYDRANT,
POBJECT_CAR_WATER_SPLASH,
POBJECT_PED_WATER_SPLASH,
POBJECT_SPLASHES_AROUND,
POBJECT_SMALL_FIRE,
POBJECT_BIG_FIRE,
POBJECT_DRY_ICE,
POBJECT_DRY_ICE_SLOWMOTION,
POBJECT_FIRE_TRAIL,
POBJECT_SMOKE_TRAIL,
POBJECT_FIREBALL_AND_SMOKE,
POBJECT_ROCKET_TRAIL,
POBJECT_EXPLOSION_ONCE,
POBJECT_CATALINAS_GUNFLASH,
POBJECT_CATALINAS_SHOTGUNFLASH,
};
class CParticleObject
{
public:
static void AddObject(uint16, const CVector &pos, bool remove);
};

591
src/PathFind.cpp Normal file
View file

@ -0,0 +1,591 @@
#include "common.h"
#include "patcher.h"
#include "PathFind.h"
CPathFind &ThePaths = *(CPathFind*)0x8F6754;
int TempListLength;
enum
{
NodeTypeExtern = 1,
NodeTypeIntern = 2,
PathTypeCar = 0,
PathTypePed = 1,
PathNodeFlag1 = 1, // used?
PathNodeFlag2 = 2,
PathNodeDeadEnd = 4,
PathNodeDisabled = 8,
PathNodeBetweenLevels = 0x10,
};
// link flags:
// 1: crosses road
// 2: ped traffic light
// pathnode flags:
// 1:
// 2:
// 4: dead end
// 8: switched off
// 10: road between levels??
// navi node flags:
// 1: bridge light
// object flags:
// 1
// 2 east/west road(?)
CPathInfoForObject *&InfoForTileCars = *(CPathInfoForObject**)0x8F1A8C;
CPathInfoForObject *&InfoForTilePeds = *(CPathInfoForObject**)0x8F1AE4;
// unused
CTempDetachedNode *&DetachedNodesCars = *(CTempDetachedNode**)0x8E2824;
CTempDetachedNode *&DetachedNodesPeds = *(CTempDetachedNode**)0x8E28A0;
void
CPathFind::PreparePathData(void)
{
int i, j, k;
int numExtern, numIntern, numLanes;
float maxX, maxY;
CTempNode *tempNodes;
printf("PreparePathData\n");
// UNUSED: CPathFind::LoadPathFindData
if(InfoForTileCars && InfoForTilePeds &&
DetachedNodesCars && DetachedNodesPeds){
tempNodes = new CTempNode[4000];
m_numLinks = 0;
for(i = 0; i < PATHNODESIZE; i++)
m_pathNodes[i].flags &= ~(PathNodeFlag1 | PathNodeFlag2);
for(i = 0; i < PATHNODESIZE; i++){
numExtern = 0;
numIntern = 0;
for(j = 0; j < 12; j++){
if(InfoForTileCars[i*12 + j].type == NodeTypeExtern)
numExtern++;
if(InfoForTileCars[i*12 + j].type == NodeTypeIntern)
numIntern++;
}
if(numIntern > 1 && numExtern != 2)
printf("ILLEGAL BLOCK. MORE THAN 1 INTERNALS AND NOT 2 EXTERNALS (Modelindex:%d)\n", i);
}
for(i = 0; i < PATHNODESIZE; i++)
for(j = 0; j < 12; j++)
if(InfoForTileCars[i*12 + j].type == NodeTypeExtern){
if(InfoForTileCars[i*12 + j].numLeftLanes < 0)
printf("ILLEGAL BLOCK. NEGATIVE NUMBER OF LANES (Obj:%d)\n", i);
if(InfoForTileCars[i*12 + j].numRightLanes < 0)
printf("ILLEGAL BLOCK. NEGATIVE NUMBER OF LANES (Obj:%d)\n", i);
if(InfoForTileCars[i*12 + j].numLeftLanes + InfoForTileCars[i*12 + j].numRightLanes <= 0)
printf("ILLEGAL BLOCK. NO LANES IN NODE (Obj:%d)\n", i);
}
m_numPathNodes = 0;
PreparePathDataForType(PathTypeCar, tempNodes, InfoForTileCars, 1.0f, DetachedNodesCars, 100);
m_numCarPathNodes = m_numPathNodes;
PreparePathDataForType(PathTypePed, tempNodes, InfoForTilePeds, 1.0f, DetachedNodesPeds, 50);
m_numPedPathNodes = m_numPathNodes - m_numCarPathNodes;
// TODO: figure out what exactly is going on here
// Some roads seem to get a west/east flag
for(i = 0; i < m_numMapObjects; i++){
numExtern = 0;
numIntern = 0;
numLanes = 0;
maxX = 0.0f;
maxY = 0.0f;
for(j = 0; j < 12; j++){
k = i*12 + j;
if(InfoForTileCars[k].type == NodeTypeExtern){
numExtern++;
if(InfoForTileCars[k].numLeftLanes + InfoForTileCars[k].numRightLanes > numLanes)
numLanes = InfoForTileCars[k].numLeftLanes + InfoForTileCars[k].numRightLanes;
maxX = max(maxX, fabs(InfoForTileCars[k].x));
maxY = max(maxY, fabs(InfoForTileCars[k].y));
}else if(InfoForTileCars[k].type == NodeTypeIntern)
numIntern++;
}
if(numIntern == 1 && numExtern == 2){
if(numLanes < 4){
if((i & 7) == 4){ // WHAT?
m_objectFlags[i] |= 1;
if(maxX > maxY)
m_objectFlags[i] |= 2;
else
m_objectFlags[i] &= ~2;
}
}else{
m_objectFlags[i] |= 1;
if(maxX > maxY)
m_objectFlags[i] |= 2;
else
m_objectFlags[i] &= ~2;
}
}
}
delete[] tempNodes;
CountFloodFillGroups(PathTypeCar);
CountFloodFillGroups(PathTypePed);
delete[] InfoForTileCars;
InfoForTileCars = nil;
delete[] InfoForTilePeds;
InfoForTilePeds = nil;
delete[] DetachedNodesCars;
DetachedNodesCars = nil;
delete[] DetachedNodesPeds;
DetachedNodesPeds = nil;
}
printf("Done with PreparePathData\n");
}
/* String together connected nodes in a list by a flood fill algorithm */
void
CPathFind::CountFloodFillGroups(uint8 type)
{
int start, end;
int i, l;
uint16 n;
CPathNode *node, *prev;
switch(type){
case PathTypeCar:
start = 0;
end = m_numCarPathNodes;
break;
case PathTypePed:
start = m_numCarPathNodes;
end = start + m_numPedPathNodes;
break;
}
for(i = start; i < end; i++)
m_pathNodes[i].group = 0;
n = 0;
for(;;){
n++;
if(n > 1500){
for(i = start; m_pathNodes[i].group && i < end; i++);
printf("NumNodes:%d Accounted for:%d\n", end - start, i - start);
}
// Look for unvisited node
for(i = start; m_pathNodes[i].group && i < end; i++);
if(i == end)
break;
node = &m_pathNodes[i];
node->next = nil;
node->group = n;
if(node->numLinks == 0){
if(type == PathTypeCar)
printf("Single car node: %f %f %f (%d)\n",
node->pos.x, node->pos.y, node->pos.z,
m_mapObjects[node->objectIndex]->m_modelIndex);
else
printf("Single ped node: %f %f %f\n",
node->pos.x, node->pos.y, node->pos.z);
}
while(node){
prev = node;
node = node->next;
for(i = 0; i < prev->numLinks; i++){
l = m_linkTo[prev->firstLink + i];
if(m_pathNodes[l].group == 0){
m_pathNodes[l].group = n;
if(m_pathNodes[l].group == 0)
m_pathNodes[l].group = 0x80; // ???
m_pathNodes[l].next = node;
node = &m_pathNodes[l];
}
}
}
}
m_numGroups[type] = n-1;
printf("GraphType:%d. FloodFill groups:%d\n", type, n);
}
void
CPathFind::PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoForObject *objectpathinfo,
float maxdist, CTempDetachedNode *detachednodes, int unused)
{
static CVector CoorsXFormed;
int i, j, k, l;
int l1, l2;
int start, typeoff;
float posx, posy;
float dx, dy, mag;
float nearestDist;
int nearestId;
int next;
int oldNumPathNodes, oldNumLinks;
CVector dist;
int iseg, jseg;
int istart, jstart;
int done, cont;
typeoff = 12*type;
oldNumPathNodes = m_numPathNodes;
oldNumLinks = m_numLinks;
// Initialize map objects
for(i = 0; i < m_numMapObjects; i++)
for(j = 0; j < 12; j++)
m_mapObjects[i]->m_nodeIndicesCars[typeoff + j] = -1;
// Calculate internal nodes, store them and connect them to defining object
for(i = 0; i < m_numMapObjects; i++){
start = 12*m_mapObjects[i]->m_modelIndex;
for(j = 0; j < 12; j++){
if(objectpathinfo[start + j].type != NodeTypeIntern)
continue;
CalcNodeCoors(
objectpathinfo[start + j].x,
objectpathinfo[start + j].y,
objectpathinfo[start + j].z,
i,
&CoorsXFormed);
m_pathNodes[m_numPathNodes].pos = CoorsXFormed;
m_pathNodes[m_numPathNodes].objectIndex = i;
m_pathNodes[m_numPathNodes].flags |= 1;
m_mapObjects[i]->m_nodeIndicesCars[typeoff + j] = m_numPathNodes++;
}
}
// Insert external nodes into TempList
TempListLength = 0;
for(i = 0; i < m_numMapObjects; i++){
start = 12*m_mapObjects[i]->m_modelIndex;
for(j = 0; j < 12; j++){
if(objectpathinfo[start + j].type != NodeTypeExtern)
continue;
CalcNodeCoors(
objectpathinfo[start + j].x,
objectpathinfo[start + j].y,
objectpathinfo[start + j].z,
i,
&CoorsXFormed);
// find closest unconnected node
nearestId = -1;
nearestDist = maxdist;
for(k = 0; k < TempListLength; k++){
if(tempnodes[k].linkState != 1)
continue;
dx = tempnodes[k].pos.x - CoorsXFormed.x;
if(fabs(dx) < nearestDist){
dy = tempnodes[k].pos.y - CoorsXFormed.y;
if(fabs(dy) < nearestDist){
nearestDist = max(fabs(dx), fabs(dy));
nearestId = k;
}
}
}
if(nearestId < 0){
// None found, add this one to temp list
tempnodes[TempListLength].pos = CoorsXFormed;
next = objectpathinfo[start + j].next;
if(next < 0){
// no link from this node, find link to this node
next = 0;
for(k = start; j != objectpathinfo[k].next; k++)
next++;
}
// link to connecting internal node
tempnodes[TempListLength].link1 = m_mapObjects[i]->m_nodeIndicesCars[typeoff + next];
if(type == PathTypeCar){
tempnodes[TempListLength].numLeftLanes = objectpathinfo[start + j].numLeftLanes;
tempnodes[TempListLength].numRightLanes = objectpathinfo[start + j].numRightLanes;
}
tempnodes[TempListLength++].linkState = 1;
}else{
// Found nearest, connect it to our neighbour
next = objectpathinfo[start + j].next;
if(next < 0){
// no link from this node, find link to this node
next = 0;
for(k = start; j != objectpathinfo[k].next; k++)
next++;
}
tempnodes[nearestId].link2 = m_mapObjects[i]->m_nodeIndicesCars[typeoff + next];
tempnodes[nearestId].linkState = 2;
// collapse this node with nearest we found
dx = m_pathNodes[tempnodes[nearestId].link1].pos.x - m_pathNodes[tempnodes[nearestId].link2].pos.x;
dy = m_pathNodes[tempnodes[nearestId].link1].pos.y - m_pathNodes[tempnodes[nearestId].link2].pos.y;
tempnodes[nearestId].pos.x = (tempnodes[nearestId].pos.x + CoorsXFormed.x)*0.5f;
tempnodes[nearestId].pos.y = (tempnodes[nearestId].pos.y + CoorsXFormed.y)*0.5f;
tempnodes[nearestId].pos.z = (tempnodes[nearestId].pos.z + CoorsXFormed.z)*0.5f;
mag = sqrt(dx*dx + dy*dy);
tempnodes[nearestId].dirX = dx/mag;
tempnodes[nearestId].dirY = dy/mag;
// do something when number of lanes doesn't agree
if(type == PathTypeCar)
if(tempnodes[nearestId].numLeftLanes != 0 && tempnodes[nearestId].numRightLanes != 0 &&
(objectpathinfo[start + j].numLeftLanes == 0 || objectpathinfo[start + j].numRightLanes == 0)){
tempnodes[nearestId].numLeftLanes = objectpathinfo[start + j].numLeftLanes;
tempnodes[nearestId].numRightLanes = objectpathinfo[start + j].numRightLanes;
}
}
}
}
// Loop through previously added internal nodes and link them
for(i = oldNumPathNodes; i < m_numPathNodes; i++){
// Init link
m_pathNodes[i].numLinks = 0;
m_pathNodes[i].firstLink = m_numLinks;
// See if node connects to external nodes
for(j = 0; j < TempListLength; j++){
if(tempnodes[j].linkState != 2)
continue;
// Add link to other side of the external
if(tempnodes[j].link1 == i)
m_linkTo[m_numLinks] = tempnodes[j].link2;
else if(tempnodes[j].link2 == i)
m_linkTo[m_numLinks] = tempnodes[j].link1;
else
continue;
dist = m_pathNodes[i].pos - m_pathNodes[m_linkTo[m_numLinks]].pos;
m_distTo[m_numLinks] = dist.Magnitude();
m_linkFlags[m_numLinks] = 0;
if(type == PathTypeCar){
// IMPROVE: use a goto here
// Find existing navi node
for(k = 0; k < m_numNaviNodes; k++){
if(m_naviNodes[k].dirX == tempnodes[j].dirX &&
m_naviNodes[k].dirY == tempnodes[j].dirY &&
m_naviNodes[k].posX == tempnodes[j].pos.x &&
m_naviNodes[k].posY == tempnodes[j].pos.y){
m_naviNodeLinks[m_numLinks] = k;
k = m_numNaviNodes;
}
}
// k is m_numNaviNodes+1 if we found one
if(k == m_numNaviNodes){
m_naviNodes[m_numNaviNodes].dirX = tempnodes[j].dirX;
m_naviNodes[m_numNaviNodes].dirY = tempnodes[j].dirY;
m_naviNodes[m_numNaviNodes].posX = tempnodes[j].pos.x;
m_naviNodes[m_numNaviNodes].posY = tempnodes[j].pos.y;
m_naviNodes[m_numNaviNodes].pathNodeIndex = i;
m_naviNodes[m_numNaviNodes].numLeftLanes = tempnodes[j].numLeftLanes;
m_naviNodes[m_numNaviNodes].numRightLanes = tempnodes[j].numRightLanes;
m_naviNodes[m_numNaviNodes].trafficLightType = 0;
m_naviNodeLinks[m_numLinks] = m_numNaviNodes++;
}
}
m_pathNodes[i].numLinks++;
m_numLinks++;
}
// Find i inside path segment
iseg = 0;
for(j = max(oldNumPathNodes, i-12); j < i; j++)
if(m_pathNodes[j].objectIndex == m_pathNodes[i].objectIndex)
iseg++;
istart = 12*m_mapObjects[m_pathNodes[i].objectIndex]->m_modelIndex;
// Add links to other internal nodes
for(j = max(oldNumPathNodes, i-12); j < min(m_numPathNodes, i+12); j++){
if(m_pathNodes[i].objectIndex != m_pathNodes[j].objectIndex || i == j)
continue;
// N.B.: in every path segment, the externals have to be at the end
jseg = j-i + iseg;
jstart = 12*m_mapObjects[m_pathNodes[j].objectIndex]->m_modelIndex;
if(objectpathinfo[istart + iseg].next == jseg ||
objectpathinfo[jstart + jseg].next == iseg){
// Found a link between i and j
m_linkTo[m_numLinks] = j;
dist = m_pathNodes[i].pos - m_pathNodes[j].pos;
m_distTo[m_numLinks] = dist.Magnitude();
if(type == PathTypeCar){
posx = (m_pathNodes[i].pos.x + m_pathNodes[j].pos.x)*0.5f;
posy = (m_pathNodes[i].pos.y + m_pathNodes[j].pos.y)*0.5f;
dx = m_pathNodes[j].pos.x - m_pathNodes[i].pos.x;
dy = m_pathNodes[j].pos.y - m_pathNodes[i].pos.y;
mag = sqrt(dx*dx + dy*dy);
dx /= mag;
dy /= mag;
if(i < j){
dx = -dx;
dy = -dy;
}
// IMPROVE: use a goto here
// Find existing navi node
for(k = 0; k < m_numNaviNodes; k++){
if(m_naviNodes[k].dirX == dx &&
m_naviNodes[k].dirY == dy &&
m_naviNodes[k].posX == posx &&
m_naviNodes[k].posY == posy){
m_naviNodeLinks[m_numLinks] = k;
k = m_numNaviNodes;
}
}
// k is m_numNaviNodes+1 if we found one
if(k == m_numNaviNodes){
m_naviNodes[m_numNaviNodes].dirX = dx;
m_naviNodes[m_numNaviNodes].dirY = dy;
m_naviNodes[m_numNaviNodes].posX = posx;
m_naviNodes[m_numNaviNodes].posY = posy;
m_naviNodes[m_numNaviNodes].pathNodeIndex = i;
m_naviNodes[m_numNaviNodes].numLeftLanes = -1;
m_naviNodes[m_numNaviNodes].numRightLanes = -1;
m_naviNodes[m_numNaviNodes].trafficLightType = 0;
m_naviNodeLinks[m_numLinks] = m_numNaviNodes++;
}
}else{
// Crosses road
if(objectpathinfo[istart + iseg].next == jseg && objectpathinfo[istart + iseg].flag & 1 ||
objectpathinfo[jstart + jseg].next == iseg && objectpathinfo[jstart + jseg].flag & 1)
m_linkFlags[m_numLinks] |= 1;
else
m_linkFlags[m_numLinks] &= ~1;
}
m_pathNodes[i].numLinks++;
m_numLinks++;
}
}
}
if(type == PathTypeCar){
done = 0;
// Set number of lanes for all nodes somehow
// very strange code
for(k = 0; !done && k < 10; k++){
done = 1;
for(i = 0; i < m_numPathNodes; i++){
if(m_pathNodes[i].numLinks != 2)
continue;
l1 = m_naviNodeLinks[m_pathNodes[i].firstLink];
l2 = m_naviNodeLinks[m_pathNodes[i].firstLink+1];
if(m_naviNodes[l1].numLeftLanes == -1 &&
m_naviNodes[l2].numLeftLanes != -1){
done = 0;
if(m_naviNodes[l2].pathNodeIndex == i){
// why switch left and right here?
m_naviNodes[l1].numLeftLanes = m_naviNodes[l2].numRightLanes;
m_naviNodes[l1].numRightLanes = m_naviNodes[l2].numLeftLanes;
}else{
m_naviNodes[l1].numLeftLanes = m_naviNodes[l2].numLeftLanes;
m_naviNodes[l1].numRightLanes = m_naviNodes[l2].numRightLanes;
}
m_naviNodes[l1].pathNodeIndex = i;
}else if(m_naviNodes[l1].numLeftLanes != -1 &&
m_naviNodes[l2].numLeftLanes == -1){
done = 0;
if(m_naviNodes[l1].pathNodeIndex == i){
// why switch left and right here?
m_naviNodes[l2].numLeftLanes = m_naviNodes[l1].numRightLanes;
m_naviNodes[l2].numRightLanes = m_naviNodes[l1].numLeftLanes;
}else{
m_naviNodes[l2].numLeftLanes = m_naviNodes[l1].numLeftLanes;
m_naviNodes[l2].numRightLanes = m_naviNodes[l1].numRightLanes;
}
m_naviNodes[l2].pathNodeIndex = i;
}else if(m_naviNodes[l1].numLeftLanes == -1 &&
m_naviNodes[l2].numLeftLanes == -1)
done = 0;
}
}
// Fall back to default values for number of lanes
for(i = 0; i < m_numPathNodes; i++)
for(j = 0; j < m_pathNodes[i].numLinks; j++){
k = m_naviNodeLinks[m_pathNodes[i].firstLink + j];
if(m_naviNodes[k].numLeftLanes < 0)
m_naviNodes[k].numLeftLanes = 1;
if(m_naviNodes[k].numRightLanes < 0)
m_naviNodes[k].numRightLanes = 1;
}
}
// Set flags for car nodes
if(type == PathTypeCar){
do{
cont = 0;
for(i = 0; i < m_numPathNodes; i++){
m_pathNodes[i].flags &= ~PathNodeDisabled;
m_pathNodes[i].flags &= ~PathNodeBetweenLevels;
// See if node is a dead end, if so, we're not done yet
if((m_pathNodes[i].flags & PathNodeDeadEnd) == 0){
k = 0;
for(j = 0; j < m_pathNodes[i].numLinks; j++)
if((m_pathNodes[m_linkTo[m_pathNodes[i].firstLink + j]].flags & PathNodeDeadEnd) == 0)
k++;
if(k < 2){
m_pathNodes[i].flags |= PathNodeDeadEnd;
cont = 1;
}
}
}
}while(cont);
}
// Remove isolated ped nodes
if(type == PathTypePed)
for(i = oldNumPathNodes; i < m_numPathNodes; i++){
if(m_pathNodes[i].numLinks != 0)
continue;
// Remove node
for(j = i; j < m_numPathNodes-1; j++)
m_pathNodes[j] = m_pathNodes[j+1];
// Fix links
for(j = oldNumLinks; j < m_numLinks; j++)
if(m_linkTo[j] >= i)
m_linkTo[j]--;
// Also in treadables
for(j = 0; j < m_numMapObjects; j++)
for(k = 0; k < 12; k++){
if(m_mapObjects[j]->m_nodeIndicesPeds[k] == i){
// remove this one
for(l = k; l < 12-1; l++)
m_mapObjects[j]->m_nodeIndicesPeds[l] = m_mapObjects[j]->m_nodeIndicesPeds[l+1];
m_mapObjects[j]->m_nodeIndicesPeds[11] = -1;
}else if(m_mapObjects[j]->m_nodeIndicesPeds[k] > i)
m_mapObjects[j]->m_nodeIndicesPeds[k]--;
}
i--;
m_numPathNodes--;
}
}
void
CPathFind::CalcNodeCoors(int16 x, int16 y, int16 z, int id, CVector *out)
{
CVector pos;
pos.x = x / 16.0f;
pos.y = y / 16.0f;
pos.z = z / 16.0f;
*out = m_mapObjects[id]->GetMatrix() * pos;
}
STARTPATCHES
InjectHook(0x429610, &CPathFind::PreparePathData, PATCH_JUMP);
InjectHook(0x429C20, &CPathFind::PreparePathDataForType, PATCH_JUMP);
ENDPATCHES

130
src/PathFind.h Normal file
View file

@ -0,0 +1,130 @@
#pragma once
#include "Treadable.h"
struct CPathNode
{
CVector pos;
CPathNode *prev; //?
CPathNode *next;
int16 unknown;
int16 objectIndex;
int16 firstLink;
uint8 numLinks;
uint8 flags;
uint8 group;
/* VC:
int16 unk1;
int16 nextIndex;
int16 x;
int16 y;
int16 z;
int16 unknown;
int16 firstLink;
int8 width;
int8 group;
int8 numLinks : 4;
int8 bDeadEnd : 1;
int8 bTurnedOff : 1; // flag 8 in node info
int8 flagA40 : 1; // flag 20 in node info
int8 flagA80 : 1; // flag 4 in node info
int8 flagB1 : 1; // flag 10 in node info
int8 flagB2 : 1; // flag 2 in node info
int8 flagB4 : 1;
int8 speedLimit : 2; // speed limit
int8 flagB20 : 1;
int8 flagB40 : 1;
int8 flagB80 : 1;
int8 spawnRate : 4;
int8 flagsC : 4;
*/
};
// TODO: name?
struct NaviNode
{
float posX;
float posY;
float dirX;
float dirY;
int16 pathNodeIndex;
int8 numLeftLanes;
int8 numRightLanes;
int8 trafficLightType;
// probably only padding
int8 field15;
int8 field16;
int8 field17;
};
struct CPathInfoForObject
{
int16 x;
int16 y;
int16 z;
int8 type;
int8 next;
int8 numLeftLanes;
int8 numRightLanes;
uint8 flag;
};
struct CTempNode
{
CVector pos;
float dirX;
float dirY;
int16 link1;
int16 link2;
int8 numLeftLanes;
int8 numRightLanes;
int8 linkState;
// probably padding
int8 field1B;
};
struct CTempDetachedNode // unused
{
uint8 foo[20];
};
class CPathFind
{
public:
/* For reference VC:
CPathNode pathNodes[9650];
NaviNode naviNodes[3500];
CBuilding *mapObjects[1250];
// 0x8000 is cross road flag
// 0x4000 is traffic light flag
uint16 linkTo[20400];
uint8 distTo[20400];
int16 naviNodeLinks[20400];
*/
CPathNode m_pathNodes[4930];
NaviNode m_naviNodes[2076];
CTreadable *m_mapObjects[1250];
uint8 m_objectFlags[1250];
int16 m_linkTo[10260];
int16 m_distTo[10260];
uint8 m_linkFlags[10260];
int16 m_naviNodeLinks[10260];
int32 m_numPathNodes;
int32 m_numCarPathNodes;
int32 m_numPedPathNodes;
int16 m_numMapObjects;
int16 m_numLinks;
int32 m_numNaviNodes;
int32 h;
uint8 m_numGroups[2];
CPathNode m_aExtraPaths[872];
void PreparePathData(void);
void CountFloodFillGroups(uint8 type);
void PreparePathDataForType(uint8 type, CTempNode *tempnodes, CPathInfoForObject *objectpathinfo,
float unk, CTempDetachedNode *detachednodes, int unused);
void CalcNodeCoors(int16 x, int16 y, int16 z, int32 id, CVector *out);
};
static_assert(sizeof(CPathFind) == 0x4c8f4, "CPathFind: error");
extern CPathFind &ThePaths;

81
src/Placeable.cpp Normal file
View file

@ -0,0 +1,81 @@
#include "common.h"
#include "Placeable.h"
#include "patcher.h"
CPlaceable::CPlaceable(void)
{
m_matrix.SetScale(1.0f);
}
CPlaceable::~CPlaceable(void) { }
void
CPlaceable::SetHeading(float angle)
{
CVector pos = GetPosition();
m_matrix.SetRotateZ(angle);
GetPosition() += pos;
}
bool
CPlaceable::IsWithinArea(float x1, float y1, float x2, float y2)
{
float x, xmin, xmax;
float y, ymin, ymax;
xmin = x1;
xmax = x2;
ymin = y1;
ymax = y2;
if(x2 > x1){
xmin = x2;
xmax = x1;
}
if(y2 > y1){
ymin = y2;
ymax = y1;
}
x = GetPosition().x;
y = GetPosition().y;
return xmin <= x && x <= xmax &&
ymin <= y && y <= ymax;
}
bool
CPlaceable::IsWithinArea(float x1, float y1, float z1, float x2, float y2, float z2)
{
float x, xmin, xmax;
float y, ymin, ymax;
float z, zmin, zmax;
xmin = x1;
xmax = x2;
ymin = y1;
ymax = y2;
zmin = z1;
zmax = z2;
if(x2 > x1){
xmin = x2;
xmax = x1;
}
if(y2 > y1){
ymin = y2;
ymax = y1;
}
if(z2 > z1){
zmin = z2;
zmax = z1;
}
x = GetPosition().x;
y = GetPosition().y;
z = GetPosition().z;
return xmin <= x && x <= xmax &&
ymin <= y && y <= ymax &&
zmin <= z && z <= zmax;
}
STARTPATCHES
InjectHook(0x49F9A0, &CPlaceable::ctor, PATCH_JUMP);
InjectHook(0x49F9E0, &CPlaceable::dtor, PATCH_JUMP);
InjectHook(0x49FA00, &CPlaceable::SetHeading, PATCH_JUMP);
InjectHook(0x49FA50, (bool (CPlaceable::*)(float, float, float, float))&CPlaceable::IsWithinArea, PATCH_JUMP);
InjectHook(0x49FAF0, (bool (CPlaceable::*)(float, float, float, float, float, float))&CPlaceable::IsWithinArea, PATCH_JUMP);
ENDPATCHES

26
src/Placeable.h Normal file
View file

@ -0,0 +1,26 @@
#pragma once
class CPlaceable
{
// disable allocation
static void *operator new(size_t) { assert(0); return nil; }
static void operator delete(void*, size_t) { assert(0); }
public:
CMatrix m_matrix;
CPlaceable(void);
virtual ~CPlaceable(void);
CVector &GetPosition(void) { return *m_matrix.GetPosition(); }
CVector &GetRight(void) { return *m_matrix.GetRight(); }
CVector &GetForward(void) { return *m_matrix.GetForward(); }
CVector &GetUp(void) { return *m_matrix.GetUp(); }
CMatrix &GetMatrix(void) { return m_matrix; }
void SetTransform(RwMatrix *m) { m_matrix = CMatrix(m, false); }
void SetHeading(float angle);
bool IsWithinArea(float x1, float y1, float x2, float y2);
bool IsWithinArea(float x1, float y1, float z1, float x2, float y2, float z2);
CPlaceable *ctor(void) { return ::new (this) CPlaceable(); }
void dtor(void) { this->CPlaceable::~CPlaceable(); }
};
static_assert(sizeof(CPlaceable) == 0x4C, "CPlaceable: error");

19
src/Pools.cpp Normal file
View file

@ -0,0 +1,19 @@
#include "common.h"
#include "Pools.h"
CCPtrNodePool *&CPools::ms_pPtrNodePool = *(CCPtrNodePool**)0x943044;
CEntryInfoNodePool *&CPools::ms_pEntryInfoNodePool = *(CEntryInfoNodePool**)0x941448;
CBuildingPool *&CPools::ms_pBuildingPool = *(CBuildingPool**)0x8F2C04;
CTreadablePool *&CPools::ms_pTreadablePool = *(CTreadablePool**)0x8F2568;
CObjectPool *&CPools::ms_pObjectPool = *(CObjectPool**)0x880E28;
void
CPools::Initialise(void)
{
// TODO: unused right now
ms_pPtrNodePool = new CCPtrNodePool(NUMPTRNODES);
ms_pEntryInfoNodePool = new CEntryInfoNodePool(NUMENTRYINFOS);
ms_pBuildingPool = new CBuildingPool(NUMBUILDINGS);
ms_pTreadablePool = new CTreadablePool(NUMTREADABLES);
ms_pObjectPool = new CObjectPool(NUMOBJECTS);
}

34
src/Pools.h Normal file
View file

@ -0,0 +1,34 @@
#pragma once
#include "templates.h"
#include "Lists.h"
#include "Treadable.h"
#include "Object.h"
#include "CutsceneHead.h"
typedef CPool<CPtrNode> CCPtrNodePool;
typedef CPool<CEntryInfoNode> CEntryInfoNodePool;
typedef CPool<CBuilding> CBuildingPool;
typedef CPool<CTreadable> CTreadablePool;
typedef CPool<CObject, CCutsceneHead> CObjectPool;
class CPools
{
static CCPtrNodePool *&ms_pPtrNodePool;
static CEntryInfoNodePool *&ms_pEntryInfoNodePool;
// ms_pPedPool
// ms_pVehiclePool
static CBuildingPool *&ms_pBuildingPool;
static CTreadablePool *&ms_pTreadablePool;
static CObjectPool *&ms_pObjectPool;
// ms_pDummyPool
// ms_pAudioScriptObjectPool
public:
static CCPtrNodePool *GetPtrNodePool(void) { return ms_pPtrNodePool; }
static CEntryInfoNodePool *GetEntryInfoNodePool(void) { return ms_pEntryInfoNodePool; }
static CBuildingPool *GetBuildingPool(void) { return ms_pBuildingPool; }
static CTreadablePool *GetTreadablePool(void) { return ms_pTreadablePool; }
static CObjectPool *GetObjectPool(void) { return ms_pObjectPool; }
static void Initialise(void);
};

22
src/References.cpp Normal file
View file

@ -0,0 +1,22 @@
#include "common.h"
#include "patcher.h"
#include "References.h"
CReference *CReferences::aRefs = (CReference*)0x70BBE0; //[NUMREFERENCES];
CReference *&CReferences::pEmptyList = *(CReference**)0x8F1AF8;
void
CReferences::Init(void)
{
int i;
pEmptyList = &aRefs[0];
for(i = 0; i < NUMREFERENCES; i++){
aRefs[i].pentity = nil;
aRefs[i].next = &aRefs[i+1];
}
aRefs[NUMREFERENCES-1].next = nil;
}
STARTPATCHES
InjectHook(0x4A7350, CReferences::Init, PATCH_JUMP);
ENDPATCHES

17
src/References.h Normal file
View file

@ -0,0 +1,17 @@
#pragma once
class CEntity;
struct CReference
{
CReference *next;
CEntity **pentity;
};
class CReferences
{
static CReference *aRefs; //[NUMREFERENCES];
public:
static CReference *&pEmptyList;
static void Init(void);
};

19
src/RwHelper.cpp Normal file
View file

@ -0,0 +1,19 @@
#include "common.h"
RwObject*
GetFirstObjectCallback(RwObject *object, void *data)
{
*(RwObject**)data = object;
return nil;
}
RwObject*
GetFirstObject(RwFrame *frame)
{
RwObject *obj;
obj = nil;
RwFrameForAllObjects(frame, GetFirstObjectCallback, &obj);
return obj;
}

3
src/RwHelper.h Normal file
View file

@ -0,0 +1,3 @@
#pragma once
RwObject *GetFirstObject(RwFrame *frame);

10
src/Streaming.cpp Normal file
View file

@ -0,0 +1,10 @@
#include "common.h"
#include "patcher.h"
#include "Streaming.h"
bool &CStreaming::ms_disableStreaming = *(bool*)0x95CD6E;
int32 &CStreaming::ms_numModelsRequested = *(int32*)0x8E2C10;
CStreamingInfo *CStreaming::ms_aInfoForModel = (CStreamingInfo*)0x6C7088;
WRAPPER void CStreaming::RemoveModel(int32 id) { EAXJMP(0x408830); }
WRAPPER void CStreaming::RequestModel(int32 model, int32 flags) { EAXJMP(0x407EA0); }

54
src/Streaming.h Normal file
View file

@ -0,0 +1,54 @@
#pragma once
enum {
STREAM_OFFSET_MODEL = 0,
STREAM_OFFSET_TXD = STREAM_OFFSET_MODEL+MODELINFOSIZE,
NUMSTREAMINFO = STREAM_OFFSET_TXD+TXDSTORESIZE
};
enum StreamFlags
{
STREAM_DONT_REMOVE = 0x01,
STREAM_SCRIPTOWNED = 0x02,
STREAM_DEPENDENCY = 0x04,
STREAM_PRIORITY = 0x08,
STREAM_NOFADE = 0x10,
};
enum StreamLoadState
{
STREAM_NOTLOADED = 0,
STREAM_LOADED = 1,
STREAM_INQUEUE = 2,
STREAM_READING = 3, // what is this?
STREAM_BIGFILE = 4,
};
class CStreamingInfo
{
public:
CStreamingInfo *m_next;
CStreamingInfo *m_prev;
uint8 m_loadState;
uint8 m_flags;
int16 m_nextID;
uint32 m_position;
uint32 m_size;
// bool GetCdPosnAndSize(uint32 *pos, uint32 *size);
// void SetCdPosnAndSize(uint32 pos, uint32 size);
// void AddToList(CStreamingInfo *link);
// void RemoveFromList(void);
};
class CStreaming
{
public:
static bool &ms_disableStreaming;
static int32 &ms_numModelsRequested;
static CStreamingInfo *ms_aInfoForModel; //[NUMSTREAMINFO]
static void RemoveModel(int32 id);
static void RequestModel(int32 model, int32 flags);
};

44
src/SurfaceTable.cpp Normal file
View file

@ -0,0 +1,44 @@
#include "common.h"
#include "patcher.h"
#include "SurfaceTable.h"
int
CSurfaceTable::GetAdhesionGroup(uint8 surfaceType)
{
switch(surfaceType){
case SURFACE_0: return ADHESIVE_ROAD;
case SURFACE_1: return ADHESIVE_ROAD;
case SURFACE_2: return ADHESIVE_LOOSE;
case SURFACE_3: return ADHESIVE_LOOSE;
case SURFACE_4: return ADHESIVE_HARD;
case SURFACE_5: return ADHESIVE_ROAD;
case SURFACE_6: return ADHESIVE_HARD;
case SURFACE_7: return ADHESIVE_HARD;
case SURFACE_8: return ADHESIVE_HARD;
case SURFACE_9: return ADHESIVE_HARD;
case SURFACE_10: return ADHESIVE_HARD;
case SURFACE_11: return ADHESIVE_HARD;
case SURFACE_12: return ADHESIVE_HARD;
case SURFACE_13: return ADHESIVE_HARD;
case SURFACE_14: return ADHESIVE_HARD;
case SURFACE_15: return ADHESIVE_HARD;
case SURFACE_16: return ADHESIVE_HARD;
case SURFACE_17: return ADHESIVE_RUBBER;
case SURFACE_18: return ADHESIVE_LOOSE;
case SURFACE_19: return ADHESIVE_WET;
case SURFACE_20: return ADHESIVE_ROAD;
case SURFACE_21: return ADHESIVE_ROAD;
case SURFACE_22: return ADHESIVE_ROAD;
case SURFACE_23: return ADHESIVE_RUBBER;
case SURFACE_24: return ADHESIVE_HARD;
case SURFACE_25: return ADHESIVE_LOOSE;
case SURFACE_26: return ADHESIVE_LOOSE;
case SURFACE_27: return ADHESIVE_HARD;
case SURFACE_28: return ADHESIVE_HARD;
case SURFACE_29: return ADHESIVE_RUBBER;
case SURFACE_30: return ADHESIVE_LOOSE;
case SURFACE_31: return ADHESIVE_HARD;
case SURFACE_32: return ADHESIVE_HARD;
default: return ADHESIVE_ROAD;
}
}

99
src/SurfaceTable.h Normal file
View file

@ -0,0 +1,99 @@
#pragma once
enum
{
SURFACE_0,
SURFACE_1,
SURFACE_2,
SURFACE_3,
SURFACE_4,
SURFACE_5,
SURFACE_6,
SURFACE_7,
SURFACE_8,
SURFACE_9,
SURFACE_10,
SURFACE_11,
SURFACE_12,
SURFACE_13,
SURFACE_14,
SURFACE_15,
SURFACE_16,
SURFACE_17,
SURFACE_18,
SURFACE_19,
SURFACE_20,
SURFACE_21,
SURFACE_22,
SURFACE_23,
SURFACE_24,
SURFACE_25,
SURFACE_26,
SURFACE_27,
SURFACE_28,
SURFACE_29,
SURFACE_30,
SURFACE_31,
SURFACE_32,
NUMSURFACETYPES
};
// From nick
// TODO: check and use this
enum eSurfaceType
{
SURFACE_ROAD0,
SURFACE_ROAD1,
SURFACE_GRASS,
SURFACE_DIRT,
SURFACE_MUD,
SURFACE_PAVEMENT,
SURFACE_METAL6,
SURFACE_GLASS,
SURFACE_HARD8,
SURFACE_METAL_DOOR,
SURFACE_METAL10,
SURFACE_METAL11,
SURFACE_METAL12,
SURFACE_METAL13,
SURFACE_METAL14,
SURFACE_METAL15,
SURFACE_METAL_FENCE,
SURFACE_FLESH,
SURFACE_SAND18,
SURFACE_WATER,
SURFACE_WOOD,
SURFACE_WOOD_BOX,
SURFACE_WOOD_PLANK,
SURFACE_TIRE,
SURFACE_HARD24,
SURFACE_HEDGE,
SURFACE_STONE,
SURFACE_METAL27,
SURFACE_METAL28,
SURFACE_RUBBER29,
SURFACE_LOOSE30,
SURFACE_BOLLARD,
SURFACE_GATE,
SURFACE_SAND33,
SURFACE_ROAD34,
};
enum
{
ADHESIVE_RUBBER,
ADHESIVE_HARD,
ADHESIVE_ROAD,
ADHESIVE_LOOSE,
ADHESIVE_WET,
NUMADHESIVEGROUPS
};
class CSurfaceTable
{
public:
static int GetAdhesionGroup(uint8 surfaceType);
};

92
src/Timecycle.cpp Normal file
View file

@ -0,0 +1,92 @@
#include "common.h"
#include "Timecycle.h"
int (*CTimeCycle::m_nAmbientRed)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x86AF78;
int (*CTimeCycle::m_nAmbientGreen)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x665308;
int (*CTimeCycle::m_nAmbientBlue)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x72CF88;
int (*CTimeCycle::m_nDirectionalRed)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x6FAB78;
int (*CTimeCycle::m_nDirectionalGreen)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x6F4528;
int (*CTimeCycle::m_nDirectionalBlue)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x83CE58;
int (*CTimeCycle::m_nSkyTopRed)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x87FB90;
int (*CTimeCycle::m_nSkyTopGreen)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x8460A8;
int (*CTimeCycle::m_nSkyTopBlue)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x87B158;
int (*CTimeCycle::m_nSkyBottomRed)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x6FA960;
int (*CTimeCycle::m_nSkyBottomGreen)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x70D6A8;
int (*CTimeCycle::m_nSkyBottomBlue)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x83D288;
int (*CTimeCycle::m_nSunCoreRed)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x878360;
int (*CTimeCycle::m_nSunCoreGreen)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x6EE088;
int (*CTimeCycle::m_nSunCoreBlue)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x773A68;
int (*CTimeCycle::m_nSunCoronaRed)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x664B60;
int (*CTimeCycle::m_nSunCoronaGreen)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x6F01E0;
int (*CTimeCycle::m_nSunCoronaBlue)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x6E6340;
float (*CTimeCycle::m_fSunSize)[NUMWEATHERS] = (float(*)[NUMWEATHERS])0x733510;
float (*CTimeCycle::m_fSpriteSize)[NUMWEATHERS] = (float(*)[NUMWEATHERS])0x87F820;
float (*CTimeCycle::m_fSpriteBrightness)[NUMWEATHERS] = (float(*)[NUMWEATHERS])0x6E96F0;
short (*CTimeCycle::m_nShadowStrength)[NUMWEATHERS] = (short(*)[NUMWEATHERS])0x83CFD8;
short (*CTimeCycle::m_nLightShadowStrength)[NUMWEATHERS] = (short(*)[NUMWEATHERS])0x72B0F8;
short (*CTimeCycle::m_nTreeShadowStrength)[NUMWEATHERS] = (short(*)[NUMWEATHERS])0x733450;
float (*CTimeCycle::m_fFogStart)[NUMWEATHERS] = (float(*)[NUMWEATHERS])0x8806C8;
float (*CTimeCycle::m_fFarClip)[NUMWEATHERS] = (float(*)[NUMWEATHERS])0x8804E0;
float (*CTimeCycle::m_fLightsOnGroundBrightness)[NUMWEATHERS] = (float(*)[NUMWEATHERS])0x83D108;
int (*CTimeCycle::m_nLowCloudsRed)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x726770;
int (*CTimeCycle::m_nLowCloudsGreen)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x87BF08;
int (*CTimeCycle::m_nLowCloudsBlue)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x87FA10;
int (*CTimeCycle::m_nFluffyCloudsTopRed)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x70F2B0;
int (*CTimeCycle::m_nFluffyCloudsTopGreen)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x72D288;
int (*CTimeCycle::m_nFluffyCloudsTopBlue)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x86B108;
int (*CTimeCycle::m_nFluffyCloudsBottomRed)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x6E8DA8;
int (*CTimeCycle::m_nFluffyCloudsBottomGreen)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x715AA8;
int (*CTimeCycle::m_nFluffyCloudsBottomBlue)[NUMWEATHERS] = (int(*)[NUMWEATHERS])0x6EE2D0;
float (*CTimeCycle::m_fBlurRed)[NUMWEATHERS] = (float(*)[NUMWEATHERS])0x87C7E0;
float (*CTimeCycle::m_fBlurGreen)[NUMWEATHERS] = (float(*)[NUMWEATHERS])0x774C10;
float (*CTimeCycle::m_fBlurBlue)[NUMWEATHERS] = (float(*)[NUMWEATHERS])0x8784E0;
float (*CTimeCycle::m_fBlurAlpha)[NUMWEATHERS] = (float(*)[NUMWEATHERS])0x733690;
float &CTimeCycle::m_fCurrentAmbientRed = *(float*)0x8F29B4;
float &CTimeCycle::m_fCurrentAmbientGreen = *(float*)0x94144C;
float &CTimeCycle::m_fCurrentAmbientBlue = *(float*)0x942FC0;
float &CTimeCycle::m_fCurrentDirectionalRed = *(float*)0x8F29D8;
float &CTimeCycle::m_fCurrentDirectionalGreen = *(float*)0x940594;
float &CTimeCycle::m_fCurrentDirectionalBlue = *(float*)0x942FAC;
int &CTimeCycle::m_nCurrentSkyTopRed = *(int*)0x9403C0;
int &CTimeCycle::m_nCurrentSkyTopGreen = *(int*)0x943074;
int &CTimeCycle::m_nCurrentSkyTopBlue = *(int*)0x8F29B8;
int &CTimeCycle::m_nCurrentSkyBottomRed = *(int*)0x9414D0;
int &CTimeCycle::m_nCurrentSkyBottomGreen = *(int*)0x8F2BD0;
int &CTimeCycle::m_nCurrentSkyBottomBlue = *(int*)0x8F625C;
int &CTimeCycle::m_nCurrentSunCoreRed = *(int*)0x8F2534;
int &CTimeCycle::m_nCurrentSunCoreGreen = *(int*)0x8F6264;
int &CTimeCycle::m_nCurrentSunCoreBlue = *(int*)0x94149C;
int &CTimeCycle::m_nCurrentSunCoronaRed = *(int*)0x8F2C1C;
int &CTimeCycle::m_nCurrentSunCoronaGreen = *(int*)0x885B54;
int &CTimeCycle::m_nCurrentSunCoronaBlue = *(int*)0x880F60;
float &CTimeCycle::m_fCurrentSunSize = *(float*)0x940588;
float &CTimeCycle::m_fCurrentSpriteSize = *(float*)0x8F1AA8;
float &CTimeCycle::m_fCurrentSpriteBrightness = *(float*)0x8F5FDC;
int &CTimeCycle::m_nCurrentShadowStrength = *(int*)0x95CC76;
int &CTimeCycle::m_nCurrentLightShadowStrength = *(int*)0x95CC66;
int &CTimeCycle::m_nCurrentTreeShadowStrength = *(int*)0x95CC86;
float &CTimeCycle::m_fCurrentFogStart = *(float*)0x8F1AE0;
float &CTimeCycle::m_fCurrentFarClip = *(float*)0x8F5FD8;
float &CTimeCycle::m_fCurrentLightsOnGroundBrightness = *(float*)0x8F1B60;
int &CTimeCycle::m_nCurrentLowCloudsRed = *(int*)0x95CB54;
int &CTimeCycle::m_nCurrentLowCloudsGreen = *(int*)0x95CB48;
int &CTimeCycle::m_nCurrentLowCloudsBlue = *(int*)0x95CC1C;
int &CTimeCycle::m_nCurrentFluffyCloudsTopRed = *(int*)0x8F2550;
int &CTimeCycle::m_nCurrentFluffyCloudsTopGreen = *(int*)0x8F59CC;
int &CTimeCycle::m_nCurrentFluffyCloudsTopBlue = *(int*)0x941434;
int &CTimeCycle::m_nCurrentFluffyCloudsBottomRed = *(int*)0x8F1A38;
int &CTimeCycle::m_nCurrentFluffyCloudsBottomGreen = *(int*)0x8E28B8;
int &CTimeCycle::m_nCurrentFluffyCloudsBottomBlue = *(int*)0x8F3960;
float &CTimeCycle::m_fCurrentBlurRed = *(float*)0x8F6000;
float &CTimeCycle::m_fCurrentBlurGreen = *(float*)0x9405A0;
float &CTimeCycle::m_fCurrentBlurBlue = *(float*)0x8F250C;
float &CTimeCycle::m_fCurrentBlurAlpha = *(float*)0x940728;
int &CTimeCycle::m_nCurrentFogColourRed = *(int*)0x940714;
int &CTimeCycle::m_nCurrentFogColourGreen = *(int*)0x8E2A60;
int &CTimeCycle::m_nCurrentFogColourBlue = *(int*)0x8F57EC;
int &CTimeCycle::m_FogReduction = *(int*)0x880FB8;
int &CTimeCycle::m_CurrentStoredValue = *(int*)0x94057C;
CVector *CTimeCycle::m_VectorToSun = (CVector*)0x665548; // [16]

111
src/Timecycle.h Normal file
View file

@ -0,0 +1,111 @@
class CTimeCycle
{
static int (*m_nAmbientRed)[NUMWEATHERS];
static int (*m_nAmbientGreen)[NUMWEATHERS];
static int (*m_nAmbientBlue)[NUMWEATHERS];
static int (*m_nDirectionalRed)[NUMWEATHERS];
static int (*m_nDirectionalGreen)[NUMWEATHERS];
static int (*m_nDirectionalBlue)[NUMWEATHERS];
static int (*m_nSkyTopRed)[NUMWEATHERS];
static int (*m_nSkyTopGreen)[NUMWEATHERS];
static int (*m_nSkyTopBlue)[NUMWEATHERS];
static int (*m_nSkyBottomRed)[NUMWEATHERS];
static int (*m_nSkyBottomGreen)[NUMWEATHERS];
static int (*m_nSkyBottomBlue)[NUMWEATHERS];
static int (*m_nSunCoreRed)[NUMWEATHERS];
static int (*m_nSunCoreGreen)[NUMWEATHERS];
static int (*m_nSunCoreBlue)[NUMWEATHERS];
static int (*m_nSunCoronaRed)[NUMWEATHERS];
static int (*m_nSunCoronaGreen)[NUMWEATHERS];
static int (*m_nSunCoronaBlue)[NUMWEATHERS];
static float (*m_fSunSize)[NUMWEATHERS];
static float (*m_fSpriteSize)[NUMWEATHERS];
static float (*m_fSpriteBrightness)[NUMWEATHERS];
static short (*m_nShadowStrength)[NUMWEATHERS];
static short (*m_nLightShadowStrength)[NUMWEATHERS];
static short (*m_nTreeShadowStrength)[NUMWEATHERS];
static float (*m_fFogStart)[NUMWEATHERS];
static float (*m_fFarClip)[NUMWEATHERS];
static float (*m_fLightsOnGroundBrightness)[NUMWEATHERS];
static int (*m_nLowCloudsRed)[NUMWEATHERS];
static int (*m_nLowCloudsGreen)[NUMWEATHERS];
static int (*m_nLowCloudsBlue)[NUMWEATHERS];
static int (*m_nFluffyCloudsTopRed)[NUMWEATHERS];
static int (*m_nFluffyCloudsTopGreen)[NUMWEATHERS];
static int (*m_nFluffyCloudsTopBlue)[NUMWEATHERS];
static int (*m_nFluffyCloudsBottomRed)[NUMWEATHERS];
static int (*m_nFluffyCloudsBottomGreen)[NUMWEATHERS];
static int (*m_nFluffyCloudsBottomBlue)[NUMWEATHERS];
static float (*m_fBlurRed)[NUMWEATHERS];
static float (*m_fBlurGreen)[NUMWEATHERS];
static float (*m_fBlurBlue)[NUMWEATHERS];
static float (*m_fBlurAlpha)[NUMWEATHERS];
static float &m_fCurrentAmbientRed;
static float &m_fCurrentAmbientGreen;
static float &m_fCurrentAmbientBlue;
static float &m_fCurrentDirectionalRed;
static float &m_fCurrentDirectionalGreen;
static float &m_fCurrentDirectionalBlue;
static int &m_nCurrentSkyTopRed;
static int &m_nCurrentSkyTopGreen;
static int &m_nCurrentSkyTopBlue;
static int &m_nCurrentSkyBottomRed;
static int &m_nCurrentSkyBottomGreen;
static int &m_nCurrentSkyBottomBlue;
static int &m_nCurrentSunCoreRed;
static int &m_nCurrentSunCoreGreen;
static int &m_nCurrentSunCoreBlue;
static int &m_nCurrentSunCoronaRed;
static int &m_nCurrentSunCoronaGreen;
static int &m_nCurrentSunCoronaBlue;
static float &m_fCurrentSunSize;
static float &m_fCurrentSpriteSize;
static float &m_fCurrentSpriteBrightness;
static int &m_nCurrentShadowStrength;
static int &m_nCurrentLightShadowStrength;
static int &m_nCurrentTreeShadowStrength;
static float &m_fCurrentFogStart;
static float &m_fCurrentFarClip;
static float &m_fCurrentLightsOnGroundBrightness;
static int &m_nCurrentLowCloudsRed;
static int &m_nCurrentLowCloudsGreen;
static int &m_nCurrentLowCloudsBlue;
static int &m_nCurrentFluffyCloudsTopRed;
static int &m_nCurrentFluffyCloudsTopGreen;
static int &m_nCurrentFluffyCloudsTopBlue;
static int &m_nCurrentFluffyCloudsBottomRed;
static int &m_nCurrentFluffyCloudsBottomGreen;
static int &m_nCurrentFluffyCloudsBottomBlue;
static float &m_fCurrentBlurRed;
static float &m_fCurrentBlurGreen;
static float &m_fCurrentBlurBlue;
static float &m_fCurrentBlurAlpha;
static int &m_nCurrentFogColourRed;
static int &m_nCurrentFogColourGreen;
static int &m_nCurrentFogColourBlue;
static int &m_FogReduction;
public:
static int &m_CurrentStoredValue;
static CVector *m_VectorToSun; // [16]
static float GetAmbientRed(void) { return m_fCurrentAmbientRed; }
static float GetAmbientGreen(void) { return m_fCurrentAmbientGreen; }
static float GetAmbientBlue(void) { return m_fCurrentAmbientBlue; }
static float GetDirectionalRed(void) { return m_fCurrentDirectionalRed; }
static float GetDirectionalGreen(void) { return m_fCurrentDirectionalGreen; }
static float GetDirectionalBlue(void) { return m_fCurrentDirectionalBlue; }
static int GetLowCloudsRed(void) { return m_nCurrentLowCloudsRed; }
static int GetLowCloudsGreen(void) { return m_nCurrentLowCloudsGreen; }
static int GetLowCloudsBlue(void) { return m_nCurrentLowCloudsBlue; }
static int GetFluffyCloudsTopRed(void) { return m_nCurrentFluffyCloudsTopRed; }
static int GetFluffyCloudsTopGreen(void) { return m_nCurrentFluffyCloudsTopGreen; }
static int GetFluffyCloudsTopBlue(void) { return m_nCurrentFluffyCloudsTopBlue; }
static int GetFluffyCloudsBottomRed(void) { return m_nCurrentFluffyCloudsBottomRed; }
static int GetFluffyCloudsBottomGreen(void) { return m_nCurrentFluffyCloudsBottomGreen; }
static int GetFluffyCloudsBottomBlue(void) { return m_nCurrentFluffyCloudsBottomBlue; }
};

14
src/Timer.cpp Normal file
View file

@ -0,0 +1,14 @@
#include "common.h"
#include "patcher.h"
#include "Timer.h"
uint32 &CTimer::m_snTimeInMilliseconds = *(uint32*)0x885B48;
uint32 &CTimer::m_snTimeInMillisecondsPauseMode = *(uint32*)0x5F7614;
uint32 &CTimer::m_snTimeInMillisecondsNonClipped = *(uint32*)0x9412E8;
uint32 &CTimer::m_snPreviousTimeInMilliseconds = *(uint32*)0x8F29E4;
uint32 &CTimer::m_FrameCounter = *(uint32*)0x9412EC;
float &CTimer::ms_fTimeScale = *(float*)0x8F2C20;
float &CTimer::ms_fTimeStep = *(float*)0x8E2CB4;
float &CTimer::ms_fTimeStepNonClipped = *(float*)0x8E2C4C;
bool &CTimer::m_UserPause = *(bool*)0x95CD7C;
bool &CTimer::m_CodePause = *(bool*)0x95CDB1;

19
src/Timer.h Normal file
View file

@ -0,0 +1,19 @@
#pragma once
class CTimer
{
static uint32 &m_snTimeInMilliseconds;
static uint32 &m_snTimeInMillisecondsPauseMode;
static uint32 &m_snTimeInMillisecondsNonClipped;
static uint32 &m_snPreviousTimeInMilliseconds;
static uint32 &m_FrameCounter;
static float &ms_fTimeScale;
static float &ms_fTimeStep;
static float &ms_fTimeStepNonClipped;
static bool &m_UserPause;
static bool &m_CodePause;
public:
static float GetTimeStep(void) { return ms_fTimeStep; }
static uint32 GetFrameCounter(void) { return m_FrameCounter; }
static uint32 GetTimeInMilliseconds(void) { return m_snTimeInMilliseconds; }
};

158
src/TxdStore.cpp Normal file
View file

@ -0,0 +1,158 @@
#include "common.h"
#include "patcher.h"
#include "templates.h"
#include "Streaming.h"
#include "TxdStore.h"
CPool<TxdDef,TxdDef> *&CTxdStore::ms_pTxdPool = *(CPool<TxdDef,TxdDef>**)0x8F5FB8;
RwTexDictionary *&CTxdStore::ms_pStoredTxd = *(RwTexDictionary**)0x9405BC;
void
CTxdStore::Initialize(void)
{
if(ms_pTxdPool == nil)
ms_pTxdPool = new CPool<TxdDef,TxdDef>(TXDSTORESIZE);
}
void
CTxdStore::Shutdown(void)
{
if(ms_pTxdPool)
delete ms_pTxdPool;
}
int
CTxdStore::AddTxdSlot(const char *name)
{
TxdDef *def = ms_pTxdPool->New();
assert(def);
def->texDict = nil;
def->refCount = 0;
strcpy(def->name, name);
return ms_pTxdPool->GetJustIndex(def);
}
int
CTxdStore::FindTxdSlot(const char *name)
{
char *defname;
int size = ms_pTxdPool->GetSize();
for(int i = 0; i < size; i++){
defname = GetTxdName(i);
if(defname && _strcmpi(defname, name) == 0)
return i;
}
return -1;
}
char*
CTxdStore::GetTxdName(int slot)
{
TxdDef *def = GetSlot(slot);
return def ? def->name : nil;
}
void
CTxdStore::PushCurrentTxd(void)
{
ms_pStoredTxd = RwTexDictionaryGetCurrent();
}
void
CTxdStore::PopCurrentTxd(void)
{
RwTexDictionarySetCurrent(ms_pStoredTxd);
ms_pStoredTxd = nil;
}
void
CTxdStore::SetCurrentTxd(int slot)
{
TxdDef *def = GetSlot(slot);
if(def)
RwTexDictionarySetCurrent(def->texDict);
}
void
CTxdStore::Create(int slot)
{
GetSlot(slot)->texDict = RwTexDictionaryCreate();
}
void
CTxdStore::AddRef(int slot)
{
GetSlot(slot)->refCount++;
}
void
CTxdStore::RemoveRef(int slot)
{
if(--GetSlot(slot)->refCount <= 0)
CStreaming::RemoveModel(slot + STREAM_OFFSET_TXD);
}
void
CTxdStore::RemoveRefWithoutDelete(int slot)
{
GetSlot(slot)->refCount--;
}
/*
bool
CTxdStore::LoadTxd(int slot, RwStream *stream)
{
TxdDef *def = GetSlot(slot);
if(!rw::findChunk(stream, rw::ID_TEXDICTIONARY, nil, nil)){
return false;
}else{
def->texDict = rw::TexDictionary::streamRead(stream);
convertTxd(def->texDict);
return def->texDict != nil;
}
}
bool
CTxdStore::LoadTxd(int slot, const char *filename)
{
rw::StreamFile stream;
if(stream.open(getPath(filename), "rb")){
LoadTxd(slot, &stream);
stream.close();
return true;
}
printf("Failed to open TXD\n");
return false;
}
*/
void
CTxdStore::RemoveTxd(int slot)
{
TxdDef *def = GetSlot(slot);
if(def->texDict)
RwTexDictionaryDestroy(def->texDict);
def->texDict = nil;
}
//bool
//CTxdStore::isTxdLoaded(int slot)
//{
// return GetSlot(slot)->texDict != nil;
//}
STARTPATCHES
InjectHook(0x527440, CTxdStore::Initialize, PATCH_JUMP);
InjectHook(0x527470, CTxdStore::Shutdown, PATCH_JUMP);
InjectHook(0x5274E0, CTxdStore::AddTxdSlot, PATCH_JUMP);
InjectHook(0x5275D0, CTxdStore::FindTxdSlot, PATCH_JUMP);
InjectHook(0x527590, CTxdStore::GetTxdName, PATCH_JUMP);
InjectHook(0x527900, CTxdStore::PushCurrentTxd, PATCH_JUMP);
InjectHook(0x527910, CTxdStore::PopCurrentTxd, PATCH_JUMP);
InjectHook(0x5278C0, CTxdStore::SetCurrentTxd, PATCH_JUMP);
InjectHook(0x527830, CTxdStore::Create, PATCH_JUMP);
InjectHook(0x527930, CTxdStore::AddRef, PATCH_JUMP);
InjectHook(0x527970, CTxdStore::RemoveRef, PATCH_JUMP);
InjectHook(0x5279C0, CTxdStore::RemoveRefWithoutDelete, PATCH_JUMP);
InjectHook(0x527870, CTxdStore::RemoveTxd, PATCH_JUMP);
ENDPATCHES

34
src/TxdStore.h Normal file
View file

@ -0,0 +1,34 @@
#pragma once
#include "templates.h"
struct TxdDef {
RwTexDictionary *texDict;
int refCount;
char name[20];
};
class CTxdStore
{
static CPool<TxdDef,TxdDef> *&ms_pTxdPool;
static RwTexDictionary *&ms_pStoredTxd;
public:
static void Initialize(void);
static void Shutdown(void);
static int AddTxdSlot(const char *name);
static int FindTxdSlot(const char *name);
static char *GetTxdName(int slot);
static void PushCurrentTxd(void);
static void PopCurrentTxd(void);
static void SetCurrentTxd(int slot);
static void Create(int slot);
static void AddRef(int slot);
static void RemoveRef(int slot);
static void RemoveRefWithoutDelete(int slot);
static bool LoadTxd(int slot, RwStream *stream);
static bool LoadTxd(int slot, const char *filename);
static void RemoveTxd(int slot);
static TxdDef *GetSlot(int slot) { return ms_pTxdPool->GetSlot(slot); }
static bool isTxdLoaded(int slot);
};

27
src/Weather.cpp Normal file
View file

@ -0,0 +1,27 @@
#include "common.h"
#include "Weather.h"
int32 &CWeather::SoundHandle = *(int32*)0x5FFBC4;
int32 &CWeather::WeatherTypeInList = *(int32*)0x8F626C;
int16 &CWeather::OldWeatherType = *(int16*)0x95CCEC;
int16 &CWeather::NewWeatherType = *(int16*)0x95CC70;
int16 &CWeather::ForcedWeatherType = *(int16*)0x95CC80;
bool &CWeather::LightningFlash = *(bool*)0x95CDA3;
bool &CWeather::LightningBurst = *(bool*)0x95CDAC;
uint32 &CWeather::LightningStart = *(uint32*)0x8F5F84;
uint32 &CWeather::LightningFlashLastChange = *(uint32*)0x8E2C0C;
uint32 &CWeather::WhenToPlayLightningSound = *(uint32*)0x8F57E4;
uint32 &CWeather::LightningDuration = *(uint32*)0x940578;
float &CWeather::Foggyness = *(float*)0x885AF4;
float &CWeather::CloudCoverage = *(float*)0x8E2818;
float &CWeather::Wind = *(float*)0x8E2BF8;
float &CWeather::Rain = *(float*)0x8E2BFC;
float &CWeather::InterpolationValue = *(float*)0x8F2520;
float &CWeather::WetRoads = *(float*)0x8F5FF8;
float &CWeather::Rainbow = *(float*)0x940598;
bool &CWeather::bScriptsForceRain = *(bool*)0x95CD7D;
bool &CWeather::Stored_StateStored = *(bool*)0x95CDC1;

35
src/Weather.h Normal file
View file

@ -0,0 +1,35 @@
enum {
WEATHER_SUNNY,
WEATHER_CLOUDY,
WEATHER_RAINY,
WEATHER_FOGGY
};
class CWeather
{
public:
static int32 &SoundHandle;
static int32 &WeatherTypeInList;
static int16 &OldWeatherType;
static int16 &NewWeatherType;
static int16 &ForcedWeatherType;
static bool &LightningFlash;
static bool &LightningBurst;
static uint32 &LightningStart;
static uint32 &LightningFlashLastChange;
static uint32 &WhenToPlayLightningSound;
static uint32 &LightningDuration;
static float &Foggyness;
static float &CloudCoverage;
static float &Wind;
static float &Rain;
static float &InterpolationValue;
static float &WetRoads;
static float &Rainbow;
static bool &bScriptsForceRain;
static bool &Stored_StateStored;
};

39
src/World.cpp Normal file
View file

@ -0,0 +1,39 @@
#include "common.h"
#include "patcher.h"
#include "Entity.h"
#include "World.h"
CPtrList *CWorld::ms_bigBuildingsList = (CPtrList*)0x6FAB60;
CPtrList &CWorld::ms_listMovingEntityPtrs = *(CPtrList*)0x8F433C;
CSector (*CWorld::ms_aSectors)[NUMSECTORS_X] = (CSector (*)[NUMSECTORS_Y])0x665608;
uint16 &CWorld::ms_nCurrentScanCode = *(uint16*)0x95CC64;
bool &CWorld::bNoMoreCollisionTorque = *(bool*)0x95CDCC;
void
CWorld::ClearScanCodes(void)
{
CPtrNode *node;
for(int i = 0; i < NUMSECTORS_Y; i++)
for(int j = 0; j < NUMSECTORS_X; j++){
CSector *s = &ms_aSectors[i][j];
for(node = s->m_lists[ENTITYLIST_BUILDINGS].first; node; node = node->next)
((CEntity*)node->item)->m_scanCode = 0;
for(node = s->m_lists[ENTITYLIST_VEHICLES].first; node; node = node->next)
((CEntity*)node->item)->m_scanCode = 0;
for(node = s->m_lists[ENTITYLIST_PEDS].first; node; node = node->next)
((CEntity*)node->item)->m_scanCode = 0;
for(node = s->m_lists[ENTITYLIST_OBJECTS].first; node; node = node->next)
((CEntity*)node->item)->m_scanCode = 0;
for(node = s->m_lists[ENTITYLIST_DUMMIES].first; node; node = node->next)
((CEntity*)node->item)->m_scanCode = 0;
}
}
STARTPATCHES
InjectHook(0x4B1F60, CWorld::ClearScanCodes, PATCH_JUMP);
ENDPATCHES
WRAPPER CVector &FindPlayerCoors(CVector &v) { EAXJMP(0x4A1030); }
WRAPPER CVehicle *FindPlayerVehicle(void) { EAXJMP(0x4A10C0); }
WRAPPER CVehicle *FindPlayerTrain(void) { EAXJMP(0x4A1120); }

66
src/World.h Normal file
View file

@ -0,0 +1,66 @@
#pragma once
#include "Game.h"
#include "Lists.h"
/* Sectors span from -2000 to 2000 in x and y.
* With 100x100 sectors, each is 40x40 units. */
#define NUMSECTORS_X 100
#define NUMSECTORS_Y 100
enum
{
ENTITYLIST_BUILDINGS,
ENTITYLIST_BUILDINGS_OVERLAP,
ENTITYLIST_OBJECTS,
ENTITYLIST_OBJECTS_OVERLAP,
ENTITYLIST_VEHICLES,
ENTITYLIST_VEHICLES_OVERLAP,
ENTITYLIST_PEDS,
ENTITYLIST_PEDS_OVERLAP,
ENTITYLIST_DUMMIES,
ENTITYLIST_DUMMIES_OVERLAP,
NUMSECTORENTITYLISTS
};
class CSector
{
public:
CPtrList m_lists[NUMSECTORENTITYLISTS];
};
static_assert(sizeof(CSector) == 0x28, "CSector: error");
class CWorld
{
static CPtrList *ms_bigBuildingsList; // [4];
static CPtrList &ms_listMovingEntityPtrs;
static CSector (*ms_aSectors)[NUMSECTORS_X]; // [NUMSECTORS_Y][NUMSECTORS_X];
static uint16 &ms_nCurrentScanCode;
public:
static bool &bNoMoreCollisionTorque;
static CSector *GetSector(int x, int y) { return &ms_aSectors[y][x]; }
static CPtrList &GetBigBuildingList(eLevelName i) { return ms_bigBuildingsList[i]; }
static CPtrList &GetMovingEntityList(void) { return ms_listMovingEntityPtrs; }
static uint16 GetCurrentScanCode(void) { return ms_nCurrentScanCode; }
static void AdvanceCurrentScanCode(void){
if(++CWorld::ms_nCurrentScanCode == 0){
CWorld::ClearScanCodes();
CWorld::ms_nCurrentScanCode = 1;
}
}
static void ClearScanCodes(void);
static float GetSectorX(float f) { return ((f + 2000.0f)/40.0f); }
static float GetSectorY(float f) { return ((f + 2000.0f)/40.0f); }
static int GetSectorIndexX(float f) { return (int)GetSectorX(f); }
static int GetSectorIndexY(float f) { return (int)GetSectorY(f); }
};
CVector &FindPlayerCoors(CVector &v);
class CVehicle;
CVehicle *FindPlayerVehicle(void);
CVehicle *FindPlayerTrain(void);

614
src/Zones.cpp Normal file
View file

@ -0,0 +1,614 @@
#include "common.h"
#include "patcher.h"
#include "World.h"
#include "Clock.h"
#include "Zones.h"
eLevelName &CTheZones::m_CurrLevel = *(eLevelName*)0x8F2BC8;
CZone *&CTheZones::m_pPlayersZone = *(CZone**)0x8F254C;
int16 &CTheZones::FindIndex = *(int16*)0x95CC40;
uint16 &CTheZones::NumberOfAudioZones = *(uint16*)0x95CC84;
int16 *CTheZones::AudioZoneArray = (int16*)0x713BC0;
uint16 &CTheZones::TotalNumberOfMapZones = *(uint16*)0x95CC74;
uint16 &CTheZones::TotalNumberOfZones = *(uint16*)0x95CC36;
CZone *CTheZones::ZoneArray = (CZone*)0x86BEE0;
CZone *CTheZones::MapZoneArray = (CZone*)0x663EC0;
uint16 &CTheZones::TotalNumberOfZoneInfos = *(uint16*)0x95CC3C;
CZoneInfo *CTheZones::ZoneInfoArray = (CZoneInfo*)0x714400;
#define SWAPF(a, b) { float t; t = a; a = b; b = t; }
void
CTheZones::Init(void)
{
int i;
for(i = 0; i < NUMAUDIOZONES; i++)
AudioZoneArray[i] = -1;
NumberOfAudioZones = 0;
CZoneInfo *zonei;
int x = 1000/6;
for(i = 0; i < 2*NUMZONES; i++){
zonei = &ZoneInfoArray[i];
zonei->carDensity = 10;
zonei->carThreshold[0] = x;
zonei->carThreshold[1] = zonei->carThreshold[0] + x;
zonei->carThreshold[2] = zonei->carThreshold[1] + x;
zonei->carThreshold[3] = zonei->carThreshold[2] + x;
zonei->carThreshold[4] = zonei->carThreshold[3];
zonei->carThreshold[5] = zonei->carThreshold[4];
zonei->copThreshold = zonei->carThreshold[5] + x;
zonei->gangThreshold[0] = zonei->copThreshold;
zonei->gangThreshold[1] = zonei->gangThreshold[0];
zonei->gangThreshold[2] = zonei->gangThreshold[1];
zonei->gangThreshold[3] = zonei->gangThreshold[2];
zonei->gangThreshold[4] = zonei->gangThreshold[3];
zonei->gangThreshold[5] = zonei->gangThreshold[4];
zonei->gangThreshold[6] = zonei->gangThreshold[5];
zonei->gangThreshold[7] = zonei->gangThreshold[6];
zonei->gangThreshold[8] = zonei->gangThreshold[7];
}
TotalNumberOfZoneInfos = 1; // why 1?
for(i = 0; i < NUMZONES; i++)
memset(&ZoneArray[i], 0, sizeof(CZone));
strcpy(ZoneArray[0].name, "CITYZON");
ZoneArray[0].minx = -4000.0f;
ZoneArray[0].miny = -4000.0f;
ZoneArray[0].minz = -500.0f;
ZoneArray[0].maxx = 4000.0f;
ZoneArray[0].maxy = 4000.0f;
ZoneArray[0].maxz = 500.0f;
ZoneArray[0].level = LEVEL_NONE;
TotalNumberOfZones = 1;
m_CurrLevel = LEVEL_NONE;
m_pPlayersZone = &ZoneArray[0];
for(i = 0; i < NUMMAPZONES; i++){
memset(&MapZoneArray[i], 0, sizeof(CZone));
MapZoneArray[i].type = ZONE_MAPZONE;
}
strcpy(MapZoneArray[0].name, "THEMAP");
MapZoneArray[0].minx = -4000.0f;
MapZoneArray[0].miny = -4000.0f;
MapZoneArray[0].minz = -500.0f;
MapZoneArray[0].maxx = 4000.0f;
MapZoneArray[0].maxy = 4000.0f;
MapZoneArray[0].maxz = 500.0f;
MapZoneArray[0].level = LEVEL_NONE;
TotalNumberOfMapZones = 1;
}
void
CTheZones::Update(void)
{
CVector pos;
FindPlayerCoors(pos);
m_pPlayersZone = FindSmallestZonePosition(&pos);
m_CurrLevel = GetLevelFromPosition(pos);
}
void
CTheZones::CreateZone(char *name, eZoneType type,
float minx, float miny, float minz,
float maxx, float maxy, float maxz,
eLevelName level)
{
CZone *zone;
char *p;
if(minx > maxx) SWAPF(minx, maxx);
if(miny > maxy) SWAPF(miny, maxy);
if(minz > maxz) SWAPF(minz, maxz);
// make upper case
for(p = name; *p; p++) if(islower(*p)) *p = toupper(*p);
// add zone
zone = &ZoneArray[TotalNumberOfZones++];
strncpy(zone->name, name, 7);
zone->name[7] = '\0';
zone->type = type;
zone->minx = minx;
zone->miny = miny;
zone->minz = minz;
zone->maxx = maxx;
zone->maxy = maxy;
zone->maxz = maxz;
zone->level = level;
if(type == ZONE_AUDIO || type == ZONE_TYPE1 || type == ZONE_TYPE2){
zone->zoneinfoDay = TotalNumberOfZoneInfos++;
zone->zoneinfoNight = TotalNumberOfZoneInfos++;
}
}
void
CTheZones::CreateMapZone(char *name, eZoneType type,
float minx, float miny, float minz,
float maxx, float maxy, float maxz,
eLevelName level)
{
CZone *zone;
char *p;
if(minx > maxx) SWAPF(minx, maxx);
if(miny > maxy) SWAPF(miny, maxy);
if(minz > maxz) SWAPF(minz, maxz);
// make upper case
for(p = name; *p; p++) if(islower(*p)) *p = toupper(*p);
// add zone
zone = &MapZoneArray[TotalNumberOfMapZones++];
strncpy(zone->name, name, 7);
zone->name[7] = '\0';
zone->type = type;
zone->minx = minx;
zone->miny = miny;
zone->minz = minz;
zone->maxx = maxx;
zone->maxy = maxy;
zone->maxz = maxz;
zone->level = level;
}
void
CTheZones::PostZoneCreation(void)
{
int i;
for(i = 1; i < TotalNumberOfZones; i++)
InsertZoneIntoZoneHierarchy(&ZoneArray[i]);
InitialiseAudioZoneArray();
}
void
CTheZones::InsertZoneIntoZoneHierarchy(CZone *zone)
{
zone->child = nil;
zone->parent = nil;
zone->next = nil;
InsertZoneIntoZoneHierRecursive(zone, &ZoneArray[0]);
}
bool
CTheZones::InsertZoneIntoZoneHierRecursive(CZone *inner, CZone *outer)
{
int n;
CZone *child, *next, *insert;
// return false if inner was not inserted into outer
if(outer == nil ||
!ZoneIsEntirelyContainedWithinOtherZone(inner, outer))
return false;
// try to insert inner into children of outer
for(child = outer->child; child; child = child->next)
if(InsertZoneIntoZoneHierRecursive(inner, child))
return true;
// insert inner as child of outer
// count number of outer's children contained within inner
n = 0;
for(child = outer->child; child; child = child->next)
if(ZoneIsEntirelyContainedWithinOtherZone(child, inner))
n++;
inner->next = outer->child;
inner->parent = outer;
outer->child = inner;
// move children from outer to inner
if(n){
insert = inner;
for(child = inner->next; child; child = next){
next = child->next;
if(ZoneIsEntirelyContainedWithinOtherZone(child,inner)){
insert->next = child->next;
child->parent = inner;
child->next = inner->child;
inner->child = child;
}else
insert = child;
}
}
return true;
}
bool
CTheZones::ZoneIsEntirelyContainedWithinOtherZone(CZone *inner, CZone *outer)
{
char tmp[100];
if(inner->minx < outer->minx ||
inner->maxx > outer->maxx ||
inner->miny < outer->miny ||
inner->maxy > outer->maxy ||
inner->minz < outer->minz ||
inner->maxz > outer->maxz){
CVector vmin(inner->minx, inner->miny, inner->minz);
if(PointLiesWithinZone(vmin, outer))
sprintf(tmp, "Overlapping zones %s and %s\n",
inner->name, outer->name);
CVector vmax(inner->maxx, inner->maxy, inner->maxz);
if(PointLiesWithinZone(vmax, outer))
sprintf(tmp, "Overlapping zones %s and %s\n",
inner->name, outer->name);
return false;
}
return true;
}
bool
CTheZones::PointLiesWithinZone(const CVector &v, CZone *zone)
{
return zone->minx <= v.x && v.x <= zone->maxx &&
zone->miny <= v.y && v.y <= zone->maxy &&
zone->minz <= v.z && v.z <= zone->maxz;
}
eLevelName
CTheZones::GetLevelFromPosition(CVector const &v)
{
int i;
// char tmp[116];
// if(!PointLiesWithinZone(v, &MapZoneArray[0]))
// sprintf(tmp, "x = %.3f y= %.3f z = %.3f\n", v.x, v.y, v.z);
for(i = 1; i < TotalNumberOfMapZones; i++)
if(PointLiesWithinZone(v, &MapZoneArray[i]))
return MapZoneArray[i].level;
return MapZoneArray[0].level;
}
CZone*
CTheZones::FindSmallestZonePosition(const CVector *v)
{
CZone *best = &ZoneArray[0];
// zone to test next
CZone *zone = ZoneArray[0].child;
while(zone)
// if in zone, descent into children
if(PointLiesWithinZone(*v, zone)){
best = zone;
zone = zone->child;
// otherwise try next zone
}else
zone = zone->next;
return best;
}
CZone*
CTheZones::FindSmallestZonePositionType(const CVector *v, eZoneType type)
{
CZone *best = nil;
if(ZoneArray[0].type == type)
best = &ZoneArray[0];
// zone to test next
CZone *zone = ZoneArray[0].child;
while(zone)
// if in zone, descent into children
if(PointLiesWithinZone(*v, zone)){
if(zone->type == type)
best = zone;
zone = zone->child;
// otherwise try next zone
}else
zone = zone->next;
return best;
}
CZone*
CTheZones::FindSmallestZonePositionILN(const CVector *v)
{
CZone *best = nil;
if(ZoneArray[0].type == ZONE_AUDIO ||
ZoneArray[0].type == ZONE_TYPE1 ||
ZoneArray[0].type == ZONE_TYPE2)
best = &ZoneArray[0];
// zone to test next
CZone *zone = ZoneArray[0].child;
while(zone)
// if in zone, descent into children
if(PointLiesWithinZone(*v, zone)){
if(zone->type == ZONE_AUDIO ||
zone->type == ZONE_TYPE1 ||
zone->type == ZONE_TYPE2)
best = zone;
zone = zone->child;
// otherwise try next zone
}else
zone = zone->next;
return best;
}
int16
CTheZones::FindZoneByLabelAndReturnIndex(char *name)
{
char str[8];
memset(str, 0, 8);
strncpy(str, name, 8);
for(FindIndex = 0; FindIndex < TotalNumberOfZones; FindIndex++)
if(strcmp(GetZone(FindIndex)->name, name) == 0)
return FindIndex;
return -1;
}
CZoneInfo*
CTheZones::GetZoneInfo(const CVector *v, uint8 day)
{
CZone *zone;
zone = FindSmallestZonePositionILN(v);
if(zone == nil)
return &ZoneInfoArray[0];
return &ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight];
}
void
CTheZones::GetZoneInfoForTimeOfDay(const CVector *pos, CZoneInfo *info)
{
CZoneInfo *day, *night;
float d, n;
day = GetZoneInfo(pos, 1);
night = GetZoneInfo(pos, 0);
if(CClock::GetIsTimeInRange(8, 19))
*info = *day;
else if(CClock::GetIsTimeInRange(22, 5))
*info = *night;
else{
if(CClock::GetIsTimeInRange(19, 22)){
n = (CClock::GetHours() - 19) / 3.0f;
d = n - 1.0f;
}else{
d = (CClock::GetHours() - 5) / 3.0f;
n = d - 1.0f;
}
info->carDensity = day->carDensity * d + night->carDensity * n;
info->carThreshold[0] = day->carThreshold[0] * d + night->carThreshold[0] * n;
info->carThreshold[1] = day->carThreshold[1] * d + night->carThreshold[1] * n;
info->carThreshold[2] = day->carThreshold[2] * d + night->carThreshold[2] * n;
info->carThreshold[3] = day->carThreshold[3] * d + night->carThreshold[3] * n;
info->carThreshold[4] = day->carThreshold[4] * d + night->carThreshold[4] * n;
info->carThreshold[5] = day->carThreshold[5] * d + night->carThreshold[5] * n;
info->copThreshold = day->copThreshold * d + night->copThreshold * n;
info->gangThreshold[0] = day->gangThreshold[0] * d + night->gangThreshold[0] * n;
info->gangThreshold[1] = day->gangThreshold[1] * d + night->gangThreshold[1] * n;
info->gangThreshold[2] = day->gangThreshold[2] * d + night->gangThreshold[2] * n;
info->gangThreshold[3] = day->gangThreshold[3] * d + night->gangThreshold[3] * n;
info->gangThreshold[4] = day->gangThreshold[4] * d + night->gangThreshold[4] * n;
info->gangThreshold[5] = day->gangThreshold[5] * d + night->gangThreshold[5] * n;
info->gangThreshold[6] = day->gangThreshold[6] * d + night->gangThreshold[6] * n;
info->gangThreshold[7] = day->gangThreshold[7] * d + night->gangThreshold[7] * n;
info->gangThreshold[8] = day->gangThreshold[8] * d + night->gangThreshold[8] * n;
info->pedDensity = day->pedDensity * d + night->pedDensity * n;
info->copDensity = day->copDensity * d + night->copDensity * n;
info->gangDensity[0] = day->gangDensity[0] * d + night->gangDensity[0] * n;
info->gangDensity[1] = day->gangDensity[1] * d + night->gangDensity[1] * n;
info->gangDensity[2] = day->gangDensity[2] * d + night->gangDensity[2] * n;
info->gangDensity[3] = day->gangDensity[3] * d + night->gangDensity[3] * n;
info->gangDensity[4] = day->gangDensity[4] * d + night->gangDensity[4] * n;
info->gangDensity[5] = day->gangDensity[5] * d + night->gangDensity[5] * n;
info->gangDensity[6] = day->gangDensity[6] * d + night->gangDensity[6] * n;
info->gangDensity[7] = day->gangDensity[7] * d + night->gangDensity[7] * n;
info->gangDensity[8] = day->gangDensity[8] * d + night->gangDensity[8] * n;
}
if(CClock::GetIsTimeInRange(5, 19))
info->pedGroup = day->pedGroup;
else
info->pedGroup = night->pedGroup;
}
void
CTheZones::SetZoneCarInfo(uint16 zoneid, uint8 day, int16 carDensity,
int16 gang0Num, int16 gang1Num, int16 gang2Num,
int16 gang3Num, int16 gang4Num, int16 gang5Num,
int16 gang6Num, int16 gang7Num, int16 gang8Num,
int16 copNum,
int16 car0Num, int16 car1Num, int16 car2Num,
int16 car3Num, int16 car4Num, int16 car5Num)
{
CZone *zone;
CZoneInfo *info;
zone = GetZone(zoneid);
info = &ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight];
if(carDensity != -1) info->carDensity = carDensity;
int16 oldCar1Num = info->carThreshold[1] - info->carThreshold[0];
int16 oldCar2Num = info->carThreshold[2] - info->carThreshold[1];
int16 oldCar3Num = info->carThreshold[3] - info->carThreshold[2];
int16 oldCar4Num = info->carThreshold[4] - info->carThreshold[3];
int16 oldCar5Num = info->carThreshold[5] - info->carThreshold[4];
int16 oldCopNum = info->copThreshold - info->carThreshold[5];
int16 oldGang0Num = info->gangThreshold[0] - info->copThreshold;
int16 oldGang1Num = info->gangThreshold[1] - info->gangThreshold[0];
int16 oldGang2Num = info->gangThreshold[2] - info->gangThreshold[1];
int16 oldGang3Num = info->gangThreshold[3] - info->gangThreshold[2];
int16 oldGang4Num = info->gangThreshold[4] - info->gangThreshold[3];
int16 oldGang5Num = info->gangThreshold[5] - info->gangThreshold[4];
int16 oldGang6Num = info->gangThreshold[6] - info->gangThreshold[5];
int16 oldGang7Num = info->gangThreshold[7] - info->gangThreshold[6];
int16 oldGang8Num = info->gangThreshold[8] - info->gangThreshold[7];
if(car0Num != -1) info->carThreshold[0] = car0Num;
if(car1Num != -1) info->carThreshold[1] = info->carThreshold[0] + car1Num;
else info->carThreshold[1] = info->carThreshold[0] + oldCar1Num;
if(car2Num != -1) info->carThreshold[2] = info->carThreshold[1] + car2Num;
else info->carThreshold[2] = info->carThreshold[1] + oldCar2Num;
if(car3Num != -1) info->carThreshold[3] = info->carThreshold[2] + car3Num;
else info->carThreshold[3] = info->carThreshold[2] + oldCar3Num;
if(car4Num != -1) info->carThreshold[4] = info->carThreshold[3] + car4Num;
else info->carThreshold[4] = info->carThreshold[3] + oldCar4Num;
if(car5Num != -1) info->carThreshold[5] = info->carThreshold[4] + car5Num;
else info->carThreshold[5] = info->carThreshold[4] + oldCar5Num;
if(copNum != -1) info->copThreshold = info->carThreshold[5] + copNum;
else info->copThreshold = info->carThreshold[5] + oldCopNum;
if(gang0Num != -1) info->gangThreshold[0] = info->copThreshold + gang0Num;
else info->gangThreshold[0] = info->copThreshold + oldGang0Num;
if(gang1Num != -1) info->gangThreshold[1] = info->gangThreshold[0] + gang1Num;
else info->gangThreshold[1] = info->gangThreshold[0] + oldGang1Num;
if(gang2Num != -1) info->gangThreshold[2] = info->gangThreshold[1] + gang2Num;
else info->gangThreshold[2] = info->gangThreshold[1] + oldGang2Num;
if(gang3Num != -1) info->gangThreshold[3] = info->gangThreshold[2] + gang3Num;
else info->gangThreshold[3] = info->gangThreshold[2] + oldGang3Num;
if(gang4Num != -1) info->gangThreshold[4] = info->gangThreshold[3] + gang4Num;
else info->gangThreshold[4] = info->gangThreshold[3] + oldGang4Num;
if(gang5Num != -1) info->gangThreshold[5] = info->gangThreshold[4] + gang5Num;
else info->gangThreshold[5] = info->gangThreshold[4] + oldGang5Num;
if(gang6Num != -1) info->gangThreshold[6] = info->gangThreshold[5] + gang6Num;
else info->gangThreshold[6] = info->gangThreshold[5] + oldGang6Num;
if(gang7Num != -1) info->gangThreshold[7] = info->gangThreshold[6] + gang7Num;
else info->gangThreshold[7] = info->gangThreshold[6] + oldGang7Num;
if(gang8Num != -1) info->gangThreshold[8] = info->gangThreshold[7] + gang8Num;
else info->gangThreshold[8] = info->gangThreshold[7] + oldGang8Num;
}
void
CTheZones::SetZonePedInfo(uint16 zoneid, uint8 day, int16 pedDensity,
int16 gang0Density, int16 gang1Density, int16 gang2Density, int16 gang3Density,
int16 gang4Density, int16 gang5Density, int16 gang6Density, int16 gang7Density,
int16 gang8Density, int16 copDensity)
{
CZone *zone;
CZoneInfo *info;
zone = GetZone(zoneid);
info = &ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight];
if(pedDensity != -1) info->pedDensity = pedDensity;
if(copDensity != -1) info->copDensity = copDensity;
if(gang0Density != -1) info->gangThreshold[0] = gang0Density;
if(gang1Density != -1) info->gangThreshold[1] = gang1Density;
if(gang2Density != -1) info->gangThreshold[2] = gang2Density;
if(gang3Density != -1) info->gangThreshold[3] = gang3Density;
if(gang4Density != -1) info->gangThreshold[4] = gang4Density;
if(gang5Density != -1) info->gangThreshold[5] = gang5Density;
if(gang6Density != -1) info->gangThreshold[6] = gang6Density;
if(gang7Density != -1) info->gangThreshold[7] = gang7Density;
if(gang8Density != -1) info->gangThreshold[8] = gang8Density;
}
void
CTheZones::SetCarDensity(uint16 zoneid, uint8 day, uint16 cardensity)
{
CZone *zone;
zone = GetZone(zoneid);
if(zone->type == ZONE_AUDIO || zone->type == ZONE_TYPE1 || zone->type == ZONE_TYPE2)
ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight].carDensity = cardensity;
}
void
CTheZones::SetPedDensity(uint16 zoneid, uint8 day, uint16 peddensity)
{
CZone *zone;
zone = GetZone(zoneid);
if(zone->type == ZONE_AUDIO || zone->type == ZONE_TYPE1 || zone->type == ZONE_TYPE2)
ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight].pedDensity = peddensity;
}
void
CTheZones::SetPedGroup(uint16 zoneid, uint8 day, uint16 pedgroup)
{
CZone *zone;
zone = GetZone(zoneid);
if(zone->type == ZONE_AUDIO || zone->type == ZONE_TYPE1 || zone->type == ZONE_TYPE2)
ZoneInfoArray[day ? zone->zoneinfoDay : zone->zoneinfoNight].pedGroup = pedgroup;
}
int16
CTheZones::FindAudioZone(CVector *pos)
{
int i;
for(i = 0; i < NumberOfAudioZones; i++)
if(PointLiesWithinZone(*pos, GetZone(AudioZoneArray[i])))
return i;
return -1;
}
eLevelName
CTheZones::FindZoneForPoint(const CVector &pos)
{
if(PointLiesWithinZone(pos, GetZone(FindZoneByLabelAndReturnIndex("IND_ZON"))))
return LEVEL_INDUSTRIAL;
if(PointLiesWithinZone(pos, GetZone(FindZoneByLabelAndReturnIndex("COM_ZON"))))
return LEVEL_COMMERCIAL;
if(PointLiesWithinZone(pos, GetZone(FindZoneByLabelAndReturnIndex("SUB_ZON"))))
return LEVEL_SUBURBAN;
return LEVEL_NONE;
}
void
CTheZones::AddZoneToAudioZoneArray(CZone *zone)
{
int i, z;
if(zone->type != ZONE_AUDIO)
return;
/* This is a bit stupid */
z = -1;
for(i = 0; i < NUMZONES; i++)
if(&ZoneArray[i] == zone)
z = i;
AudioZoneArray[NumberOfAudioZones++] = z;
}
void
CTheZones::InitialiseAudioZoneArray(void)
{
bool gonext;
CZone *zone;
gonext = false;
zone = &ZoneArray[0];
// Go deep first,
// set gonext when backing up a level to visit the next child
while(zone)
if(gonext){
AddZoneToAudioZoneArray(zone);
if(zone->next){
gonext = false;
zone = zone->next;
}else
zone = zone->parent;
}else if(zone->child)
zone = zone->child;
else{
AddZoneToAudioZoneArray(zone);
if(zone->next)
zone = zone->next;
else{
gonext = true;
zone = zone->parent;
}
}
}
STARTPATCHES
InjectHook(0x4B5DE0, CTheZones::Init, PATCH_JUMP);
InjectHook(0x4B61D0, CTheZones::Update, PATCH_JUMP);
InjectHook(0x4B6210, CTheZones::CreateZone, PATCH_JUMP);
InjectHook(0x4B6380, CTheZones::CreateMapZone, PATCH_JUMP);
InjectHook(0x4B64C0, CTheZones::PostZoneCreation, PATCH_JUMP);
InjectHook(0x4B6500, CTheZones::InsertZoneIntoZoneHierarchy, PATCH_JUMP);
InjectHook(0x4B6530, CTheZones::InsertZoneIntoZoneHierRecursive, PATCH_JUMP);
InjectHook(0x4B65F0, CTheZones::ZoneIsEntirelyContainedWithinOtherZone, PATCH_JUMP);
InjectHook(0x4B6710, CTheZones::PointLiesWithinZone, PATCH_JUMP);
InjectHook(0x4B6910, CTheZones::GetLevelFromPosition, PATCH_JUMP);
InjectHook(0x4B69B0, CTheZones::FindSmallestZonePosition, PATCH_JUMP);
InjectHook(0x4B6790, CTheZones::FindSmallestZonePositionType, PATCH_JUMP);
InjectHook(0x4B6890, CTheZones::FindSmallestZonePositionILN, PATCH_JUMP);
InjectHook(0x4B6800, CTheZones::FindZoneByLabelAndReturnIndex, PATCH_JUMP);
InjectHook(0x4B6A10, CTheZones::GetZoneInfo, PATCH_JUMP);
InjectHook(0x4B6FB0, CTheZones::GetZoneInfoForTimeOfDay, PATCH_JUMP);
InjectHook(0x4B6A50, CTheZones::SetZoneCarInfo, PATCH_JUMP);
InjectHook(0x4B6DC0, CTheZones::SetZonePedInfo, PATCH_JUMP);
InjectHook(0x4B6EB0, CTheZones::SetCarDensity, PATCH_JUMP);
InjectHook(0x4B6F00, CTheZones::SetPedDensity, PATCH_JUMP);
InjectHook(0x4B6F50, CTheZones::SetPedGroup, PATCH_JUMP);
InjectHook(0x4B83E0, CTheZones::FindAudioZone, PATCH_JUMP);
InjectHook(0x4B8430, CTheZones::FindZoneForPoint, PATCH_JUMP);
InjectHook(0x4B8340, CTheZones::AddZoneToAudioZoneArray, PATCH_JUMP);
InjectHook(0x4B8380, CTheZones::InitialiseAudioZoneArray, PATCH_JUMP);
ENDPATCHES

107
src/Zones.h Normal file
View file

@ -0,0 +1,107 @@
#pragma once
#include "Game.h"
enum eZoneType
{
ZONE_AUDIO,
ZONE_TYPE1, // this should be NAVIG
ZONE_TYPE2, // this should be INFO...but all except MAPINFO get zoneinfo??
ZONE_MAPZONE,
};
class CZone
{
public:
char name[8];
float minx;
float miny;
float minz;
float maxx;
float maxy;
float maxz;
eZoneType type;
eLevelName level;
int16 zoneinfoDay;
int16 zoneinfoNight;
CZone *child;
CZone *parent;
CZone *next;
};
class CZoneInfo
{
public:
// Car data
uint16 carDensity;
uint16 carThreshold[6];
uint16 copThreshold;
uint16 gangThreshold[9];
// Ped data
uint16 pedDensity;
uint16 copDensity;
uint16 gangDensity[9];
uint16 pedGroup;
};
class CTheZones
{
public:
static eLevelName &m_CurrLevel;
static CZone *&m_pPlayersZone;
static int16 &FindIndex;
static uint16 &NumberOfAudioZones;
static int16 *AudioZoneArray; //[NUMAUDIOZONES];
static uint16 &TotalNumberOfMapZones;
static uint16 &TotalNumberOfZones;
static CZone *ZoneArray; //[NUMZONES];
static CZone *MapZoneArray; //[NUMMAPZONES];
static uint16 &TotalNumberOfZoneInfos;
static CZoneInfo *ZoneInfoArray; //[2*NUMZONES];
static void Init(void);
static void Update(void);
static void CreateZone(char *name, eZoneType type,
float minx, float miny, float minz,
float maxx, float maxy, float maxz,
eLevelName level);
static void CreateMapZone(char *name, eZoneType type,
float minx, float miny, float minz,
float maxx, float maxy, float maxz,
eLevelName level);
static CZone *GetZone(uint16 i) { return &ZoneArray[i]; }
static void PostZoneCreation(void);
static void InsertZoneIntoZoneHierarchy(CZone *zone);
static bool InsertZoneIntoZoneHierRecursive(CZone *z1, CZone *z2);
static bool ZoneIsEntirelyContainedWithinOtherZone(CZone *z1, CZone *z2);
static bool PointLiesWithinZone(const CVector &v, CZone *zone);
static eLevelName GetLevelFromPosition(CVector const &v);
static CZone *FindSmallestZonePosition(const CVector *v);
static CZone *FindSmallestZonePositionType(const CVector *v, eZoneType type);
static CZone *FindSmallestZonePositionILN(const CVector *v);
static int16 FindZoneByLabelAndReturnIndex(char *name);
static CZoneInfo *GetZoneInfo(const CVector *v, uint8 day);
static void GetZoneInfoForTimeOfDay(const CVector *pos, CZoneInfo *info);
static void SetZoneCarInfo(uint16 zoneid, uint8 day, int16 carDensity,
int16 gang0Num, int16 gang1Num, int16 gang2Num,
int16 gang3Num, int16 gang4Num, int16 gang5Num,
int16 gang6Num, int16 gang7Num, int16 gang8Num,
int16 copNum,
int16 car0Num, int16 car1Num, int16 car2Num,
int16 car3Num, int16 car4Num, int16 car5Num);
static void SetZonePedInfo(uint16 zoneid, uint8 day, int16 pedDensity,
int16 gang0Density, int16 gang1Density, int16 gang2Density, int16 gang3Density,
int16 gang4Density, int16 gang5Density, int16 gang6Density, int16 gang7Density,
int16 gang8Density, int16 copDensity);
static void SetCarDensity(uint16 zoneid, uint8 day, uint16 cardensity);
static void SetPedDensity(uint16 zoneid, uint8 day, uint16 peddensity);
static void SetPedGroup(uint16 zoneid, uint8 day, uint16 pedgroup);
static int16 FindAudioZone(CVector *pos);
static eLevelName FindZoneForPoint(const CVector &pos);
static CZone *GetPointerForZoneIndex(int32 i) { return i == -1 ? nil : &ZoneArray[i]; }
static void AddZoneToAudioZoneArray(CZone *zone);
static void InitialiseAudioZoneArray(void);
};

97
src/common.h Normal file
View file

@ -0,0 +1,97 @@
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES
#pragma warning(disable: 4244) // int to float
#pragma warning(disable: 4800) // int to bool
#pragma warning(disable: 4838) // narrowing conversion
#include <stdint.h>
#include <math.h>
#include <assert.h>
#include <new>
#include <rwcore.h>
#include <rpworld.h>
#define rwVENDORID_ROCKSTAR 0x0253F2
typedef uint8_t uint8;
typedef int8_t int8;
typedef uint16_t uint16;
typedef int16_t int16;
typedef uint32_t uint32;
typedef int32_t int32;
typedef uintptr_t uintptr;
#define nil NULL
#include "config.h"
#define ALIGNPTR(p) (void*)((((uintptr)(void*)p) + sizeof(void*)-1) & ~(sizeof(void*)-1))
// little hack
extern void **rwengine;
#define RwEngineInstance (*rwengine)
// TODO
struct RsInputDevice
{
int inputDeviceType;
int used;
void *inputEventHandler;
};
struct RsGlobalType
{
const char *appName;
int width;
int height;
int maximumWidth;
int maximumHeight;
int maxFPS;
int quit;
void *ps;
RsInputDevice keyboard;
RsInputDevice mouse;
RsInputDevice pad;
};
extern RsGlobalType &RsGlobal;
#define SCREENW (RsGlobal.maximumWidth)
#define SCREENH (RsGlobal.maximumHeight)
struct GlobalScene
{
RpWorld *world;
RwCamera *camera;
};
extern GlobalScene &Scene;
#include "math/Vector.h"
#include "math/Vector2D.h"
#include "math/Matrix.h"
#include "math/Rect.h"
class CRGBA
{
public:
uint8 r, g, b, a;
CRGBA(void) { }
CRGBA(uint8 r, uint8 g, uint8 b, uint8 a) : r(r), g(g), b(b), a(a) { }
};
inline float
clamp(float v, float min, float max){ return v<min ? min : v>max ? max : v; }
inline float
sq(float x) { return x*x; }
#define PI M_PI
#define DEGTORAD(d) (d/180.0f*PI)
int myrand(void);
void mysrand(unsigned int seed);
#define debug printf
//#define min(a, b) ((a) < (b) ? (a) : (b))
//#define max(a, b) ((a) > (b) ? (a) : (b))

56
src/config.h Normal file
View file

@ -0,0 +1,56 @@
#ifndef _CONFIG_H_
#define _CONFIG_H_
enum Config {
NUMCDIMAGES = 50, // was 12
MODELINFOSIZE = 5500,
TXDSTORESIZE = 850,
EXTRADIRSIZE = 128,
SIMPLEMODELSIZE = 5000,
TIMEMODELSIZE = 30,
CLUMPMODELSIZE = 5,
PEDMODELSIZE = 90,
VEHICLEMODELSIZE = 120,
TWODFXSIZE = 2000,
NUMOBJECTINFO = 168, // object.dat
// Pool sizes
NUMPTRNODES = 30000, // 26000 on PS2
NUMENTRYINFOS = 5400, // 3200 on PS2
NUMPEDS = 140, // 90 on PS2
NUMVEHICLES = 110, // 70 on PS2
NUMBUILDINGS = 5500, // 4915 on PS2
NUMTREADABLES = 1214,
NUMOBJECTS = 450,
NUMDUMMIES = 2802, // 2368 on PS2
NUMAUDIOSCRIPTOBJECTS = 256,
// Link list lengths
// TODO: alpha list
NUMCOLCACHELINKS = 200,
NUMREFERENCES = 800,
// Zones
NUMAUDIOZONES = 36,
NUMZONES = 50,
NUMMAPZONES = 25,
// Cull zones
NUMCULLZONES = 512,
NUMATTRIBZONES = 288,
NUMZONEINDICES = 55000,
NUMPEDSTATS = 35,
NUMHANDLINGS = 57,
PATHNODESIZE = 4500,
NUMWEATHERS = 4,
NUMHOURS = 24,
};
#endif

154
src/debugmenu_public.h Normal file
View file

@ -0,0 +1,154 @@
extern "C" {
typedef void (*TriggerFunc)(void);
struct DebugMenuEntry;
typedef DebugMenuEntry *(*DebugMenuAddInt8_TYPE)(const char *path, const char *name, int8_t *ptr, TriggerFunc triggerFunc, int8_t step, int8_t lowerBound, int8_t upperBound, const char **strings);
typedef DebugMenuEntry *(*DebugMenuAddInt16_TYPE)(const char *path, const char *name, int16_t *ptr, TriggerFunc triggerFunc, int16_t step, int16_t lowerBound, int16_t upperBound, const char **strings);
typedef DebugMenuEntry *(*DebugMenuAddInt32_TYPE)(const char *path, const char *name, int32_t *ptr, TriggerFunc triggerFunc, int32_t step, int32_t lowerBound, int32_t upperBound, const char **strings);
typedef DebugMenuEntry *(*DebugMenuAddInt64_TYPE)(const char *path, const char *name, int64_t *ptr, TriggerFunc triggerFunc, int64_t step, int64_t lowerBound, int64_t upperBound, const char **strings);
typedef DebugMenuEntry *(*DebugMenuAddUInt8_TYPE)(const char *path, const char *name, uint8_t *ptr, TriggerFunc triggerFunc, uint8_t step, uint8_t lowerBound, uint8_t upperBound, const char **strings);
typedef DebugMenuEntry *(*DebugMenuAddUInt16_TYPE)(const char *path, const char *name, uint16_t *ptr, TriggerFunc triggerFunc, uint16_t step, uint16_t lowerBound, uint16_t upperBound, const char **strings);
typedef DebugMenuEntry *(*DebugMenuAddUInt32_TYPE)(const char *path, const char *name, uint32_t *ptr, TriggerFunc triggerFunc, uint32_t step, uint32_t lowerBound, uint32_t upperBound, const char **strings);
typedef DebugMenuEntry *(*DebugMenuAddUInt64_TYPE)(const char *path, const char *name, uint64_t *ptr, TriggerFunc triggerFunc, uint64_t step, uint64_t lowerBound, uint64_t upperBound, const char **strings);
typedef DebugMenuEntry *(*DebugMenuAddFloat32_TYPE)(const char *path, const char *name, float *ptr, TriggerFunc triggerFunc, float step, float lowerBound, float upperBound);
typedef DebugMenuEntry *(*DebugMenuAddFloat64_TYPE)(const char *path, const char *name, double *ptr, TriggerFunc triggerFunc, double step, double lowerBound, double upperBound);
typedef DebugMenuEntry *(*DebugMenuAddCmd_TYPE)(const char *path, const char *name, TriggerFunc triggerFunc);
typedef void (*DebugMenuEntrySetWrap_TYPE)(DebugMenuEntry *e, bool wrap);
typedef void (*DebugMenuEntrySetStrings_TYPE)(DebugMenuEntry *e, const char **strings);
typedef void (*DebugMenuEntrySetAddress_TYPE)(DebugMenuEntry *e, void *addr);
struct DebugMenuAPI
{
bool isLoaded;
HMODULE module;
DebugMenuAddInt8_TYPE addint8;
DebugMenuAddInt16_TYPE addint16;
DebugMenuAddInt32_TYPE addint32;
DebugMenuAddInt64_TYPE addint64;
DebugMenuAddUInt8_TYPE adduint8;
DebugMenuAddUInt16_TYPE adduint16;
DebugMenuAddUInt32_TYPE adduint32;
DebugMenuAddUInt64_TYPE adduint64;
DebugMenuAddFloat32_TYPE addfloat32;
DebugMenuAddFloat64_TYPE addfloat64;
DebugMenuAddCmd_TYPE addcmd;
DebugMenuEntrySetWrap_TYPE setwrap;
DebugMenuEntrySetStrings_TYPE setstrings;
DebugMenuEntrySetAddress_TYPE setaddress;
};
extern DebugMenuAPI gDebugMenuAPI;
inline DebugMenuEntry *DebugMenuAddInt8(const char *path, const char *name, int8_t *ptr, TriggerFunc triggerFunc, int8_t step, int8_t lowerBound, int8_t upperBound, const char **strings)
{ return gDebugMenuAPI.addint8(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
inline DebugMenuEntry *DebugMenuAddInt16(const char *path, const char *name, int16_t *ptr, TriggerFunc triggerFunc, int16_t step, int16_t lowerBound, int16_t upperBound, const char **strings)
{ return gDebugMenuAPI.addint16(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
inline DebugMenuEntry *DebugMenuAddInt32(const char *path, const char *name, int32_t *ptr, TriggerFunc triggerFunc, int32_t step, int32_t lowerBound, int32_t upperBound, const char **strings)
{ return gDebugMenuAPI.addint32(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
inline DebugMenuEntry *DebugMenuAddInt64(const char *path, const char *name, int64_t *ptr, TriggerFunc triggerFunc, int64_t step, int64_t lowerBound, int64_t upperBound, const char **strings)
{ return gDebugMenuAPI.addint64(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
inline DebugMenuEntry *DebugMenuAddUInt8(const char *path, const char *name, uint8_t *ptr, TriggerFunc triggerFunc, uint8_t step, uint8_t lowerBound, uint8_t upperBound, const char **strings)
{ return gDebugMenuAPI.adduint8(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
inline DebugMenuEntry *DebugMenuAddUInt16(const char *path, const char *name, uint16_t *ptr, TriggerFunc triggerFunc, uint16_t step, uint16_t lowerBound, uint16_t upperBound, const char **strings)
{ return gDebugMenuAPI.adduint16(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
inline DebugMenuEntry *DebugMenuAddUInt32(const char *path, const char *name, uint32_t *ptr, TriggerFunc triggerFunc, uint32_t step, uint32_t lowerBound, uint32_t upperBound, const char **strings)
{ return gDebugMenuAPI.adduint32(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
inline DebugMenuEntry *DebugMenuAddUInt64(const char *path, const char *name, uint64_t *ptr, TriggerFunc triggerFunc, uint64_t step, uint64_t lowerBound, uint64_t upperBound, const char **strings)
{ return gDebugMenuAPI.adduint64(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
inline DebugMenuEntry *DebugMenuAddFloat32(const char *path, const char *name, float *ptr, TriggerFunc triggerFunc, float step, float lowerBound, float upperBound)
{ return gDebugMenuAPI.addfloat32(path, name, ptr, triggerFunc, step, lowerBound, upperBound); }
inline DebugMenuEntry *DebugMenuAddFloat64(const char *path, const char *name, double *ptr, TriggerFunc triggerFunc, double step, double lowerBound, double upperBound)
{ return gDebugMenuAPI.addfloat64(path, name, ptr, triggerFunc, step, lowerBound, upperBound); }
inline DebugMenuEntry *DebugMenuAddCmd(const char *path, const char *name, TriggerFunc triggerFunc)
{ return gDebugMenuAPI.addcmd(path, name, triggerFunc); }
inline void DebugMenuEntrySetWrap(DebugMenuEntry *e, bool wrap)
{ gDebugMenuAPI.setwrap(e, wrap); }
inline void DebugMenuEntrySetStrings(DebugMenuEntry *e, const char **strings)
{ gDebugMenuAPI.setstrings(e, strings); }
inline void DebugMenuEntrySetAddress(DebugMenuEntry *e, void *addr)
{ gDebugMenuAPI.setaddress(e, addr); }
inline bool DebugMenuLoad(void)
{
if(gDebugMenuAPI.isLoaded)
return true;
HMODULE mod = LoadLibraryA("debugmenu");
if(mod == 0){
char modulePath[MAX_PATH];
HMODULE dllModule;
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)&gDebugMenuAPI, &dllModule);
GetModuleFileNameA(dllModule, modulePath, MAX_PATH);
char *p = strchr(modulePath, '\\');
if(p) p[1] = '\0';
strcat(modulePath, "debugmenu");
mod = LoadLibraryA(modulePath);
}
if(mod == 0)
return false;
gDebugMenuAPI.addint8 = (DebugMenuAddInt8_TYPE)GetProcAddress(mod, "DebugMenuAddInt8");
gDebugMenuAPI.addint16 = (DebugMenuAddInt16_TYPE)GetProcAddress(mod, "DebugMenuAddInt16");
gDebugMenuAPI.addint32 = (DebugMenuAddInt32_TYPE)GetProcAddress(mod, "DebugMenuAddInt32");
gDebugMenuAPI.addint64 = (DebugMenuAddInt64_TYPE)GetProcAddress(mod, "DebugMenuAddInt64");
gDebugMenuAPI.adduint8 = (DebugMenuAddUInt8_TYPE)GetProcAddress(mod, "DebugMenuAddUInt8");
gDebugMenuAPI.adduint16 = (DebugMenuAddUInt16_TYPE)GetProcAddress(mod, "DebugMenuAddUInt16");
gDebugMenuAPI.adduint32 = (DebugMenuAddUInt32_TYPE)GetProcAddress(mod, "DebugMenuAddUInt32");
gDebugMenuAPI.adduint64 = (DebugMenuAddUInt64_TYPE)GetProcAddress(mod, "DebugMenuAddUInt64");
gDebugMenuAPI.addfloat32 = (DebugMenuAddFloat32_TYPE)GetProcAddress(mod, "DebugMenuAddFloat32");
gDebugMenuAPI.addfloat64 = (DebugMenuAddFloat64_TYPE)GetProcAddress(mod, "DebugMenuAddFloat64");
gDebugMenuAPI.addcmd = (DebugMenuAddCmd_TYPE)GetProcAddress(mod, "DebugMenuAddCmd");
gDebugMenuAPI.setwrap = (DebugMenuEntrySetWrap_TYPE)GetProcAddress(mod, "DebugMenuEntrySetWrap");
gDebugMenuAPI.setstrings = (DebugMenuEntrySetStrings_TYPE)GetProcAddress(mod, "DebugMenuEntrySetStrings");
gDebugMenuAPI.setaddress = (DebugMenuEntrySetAddress_TYPE)GetProcAddress(mod, "DebugMenuEntrySetAddress");
gDebugMenuAPI.isLoaded = true;
gDebugMenuAPI.module = mod;
return true;
}
}
// Also overload them for simplicity
inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int8_t *ptr, TriggerFunc triggerFunc, int8_t step, int8_t lowerBound, int8_t upperBound, const char **strings)
{ return gDebugMenuAPI.addint8(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int16_t *ptr, TriggerFunc triggerFunc, int16_t step, int16_t lowerBound, int16_t upperBound, const char **strings)
{ return gDebugMenuAPI.addint16(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int32_t *ptr, TriggerFunc triggerFunc, int32_t step, int32_t lowerBound, int32_t upperBound, const char **strings)
{ return gDebugMenuAPI.addint32(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int64_t *ptr, TriggerFunc triggerFunc, int64_t step, int64_t lowerBound, int64_t upperBound, const char **strings)
{ return gDebugMenuAPI.addint64(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint8_t *ptr, TriggerFunc triggerFunc, uint8_t step, uint8_t lowerBound, uint8_t upperBound, const char **strings)
{ return gDebugMenuAPI.adduint8(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint16_t *ptr, TriggerFunc triggerFunc, uint16_t step, uint16_t lowerBound, uint16_t upperBound, const char **strings)
{ return gDebugMenuAPI.adduint16(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint32_t *ptr, TriggerFunc triggerFunc, uint32_t step, uint32_t lowerBound, uint32_t upperBound, const char **strings)
{ return gDebugMenuAPI.adduint32(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint64_t *ptr, TriggerFunc triggerFunc, uint64_t step, uint64_t lowerBound, uint64_t upperBound, const char **strings)
{ return gDebugMenuAPI.adduint64(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, float *ptr, TriggerFunc triggerFunc, float step, float lowerBound, float upperBound)
{ return gDebugMenuAPI.addfloat32(path, name, ptr, triggerFunc, step, lowerBound, upperBound); }
inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, double *ptr, TriggerFunc triggerFunc, double step, double lowerBound, double upperBound)
{ return gDebugMenuAPI.addfloat64(path, name, ptr, triggerFunc, step, lowerBound, upperBound); }
inline DebugMenuEntry *DebugMenuAddVarBool32(const char *path, const char *name, int32_t *ptr, TriggerFunc triggerFunc)
{
static const char *boolstr[] = { "Off", "On" };
DebugMenuEntry *e = DebugMenuAddVar(path, name, ptr, triggerFunc, 1, 0, 1, boolstr);
DebugMenuEntrySetWrap(e, true);
return e;
}
inline DebugMenuEntry *DebugMenuAddVarBool16(const char *path, const char *name, int16_t *ptr, TriggerFunc triggerFunc)
{
static const char *boolstr[] = { "Off", "On" };
DebugMenuEntry *e = DebugMenuAddVar(path, name, ptr, triggerFunc, 1, 0, 1, boolstr);
DebugMenuEntrySetWrap(e, true);
return e;
}
inline DebugMenuEntry *DebugMenuAddVarBool8(const char *path, const char *name, int8_t *ptr, TriggerFunc triggerFunc)
{
static const char *boolstr[] = { "Off", "On" };
DebugMenuEntry *e = DebugMenuAddVar(path, name, ptr, triggerFunc, 1, 0, 1, boolstr);
DebugMenuEntrySetWrap(e, true);
return e;
}

View file

@ -0,0 +1,7 @@
#include "common.h"
#include "rpworld.h"
#include "Building.h"
#include "Pools.h"
void *CBuilding::operator new(size_t sz) { return CPools::GetBuildingPool()->New(); }
void CBuilding::operator delete(void *p, size_t sz) { CPools::GetBuildingPool()->Delete((CBuilding*)p); }

15
src/entities/Building.h Normal file
View file

@ -0,0 +1,15 @@
#pragma once
#include "Entity.h"
class CBuilding : public CEntity
{
public:
// TODO: ReplaceWithNewModel
// TODO: ctor
static void *operator new(size_t);
static void operator delete(void*, size_t);
virtual bool GetIsATreadable(void) { return false; }
};
static_assert(sizeof(CBuilding) == 0x64, "CBuilding: error");

View file

@ -0,0 +1,2 @@
#include "common.h"
#include "CutsceneHead.h"

View file

@ -0,0 +1,10 @@
#pragma once
#include "CutsceneObject.h"
class CCutsceneHead : public CCutsceneObject
{
public:
RwFrame *m_pHeadNode;
};
static_assert(sizeof(CCutsceneHead) == 0x19C, "CCutsceneHead: error");

View file

@ -0,0 +1,2 @@
#include "common.h"
#include "CutsceneObject.h"

View file

@ -0,0 +1,9 @@
#pragma once
#include "Object.h"
class CCutsceneObject : public CObject
{
public:
};
static_assert(sizeof(CCutsceneObject) == 0x198, "CCutsceneObject: error");

391
src/entities/Entity.cpp Normal file
View file

@ -0,0 +1,391 @@
#include "common.h"
#include "rpworld.h"
#include "Placeable.h"
#include "Entity.h"
#include "Lights.h"
#include "World.h"
#include "Camera.h"
#include "References.h"
#include "TxdStore.h"
#include "Zones.h"
#include "patcher.h"
int gBuildings;
void
CEntity::GetBoundCentre(CVector &out)
{
out = m_matrix * CModelInfo::GetModelInfo(m_modelIndex)->GetColModel()->boundingSphere.center;
};
bool
CEntity::GetIsTouching(CVector const &center, float radius)
{
return sq(GetBoundRadius()+radius) > (GetBoundCentre()-center).MagnitudeSqr();
}
bool
CEntity::GetIsOnScreen(void)
{
return TheCamera.IsSphereVisible(GetBoundCentre(), GetBoundRadius(),
&TheCamera.GetCameraMatrix());
}
bool
CEntity::GetIsOnScreenComplex(void)
{
RwV3d boundBox[8];
if(TheCamera.IsPointVisible(GetBoundCentre(), &TheCamera.GetCameraMatrix()))
return true;
CRect rect = GetBoundRect();
CColModel *colmodel = CModelInfo::GetModelInfo(m_modelIndex)->GetColModel();
float z = GetPosition().z;
float minz = z + colmodel->boundingBox.min.z;
float maxz = z + colmodel->boundingBox.max.z;
boundBox[0].x = rect.left;
boundBox[0].y = rect.top;
boundBox[0].z = minz;
boundBox[1].x = rect.left;
boundBox[1].y = rect.bottom;
boundBox[1].z = minz;
boundBox[2].x = rect.right;
boundBox[2].y = rect.top;
boundBox[2].z = minz;
boundBox[3].x = rect.right;
boundBox[3].y = rect.bottom;
boundBox[3].z = minz;
boundBox[4].x = rect.left;
boundBox[4].y = rect.top;
boundBox[4].z = maxz;
boundBox[5].x = rect.left;
boundBox[5].y = rect.bottom;
boundBox[5].z = maxz;
boundBox[6].x = rect.right;
boundBox[6].y = rect.top;
boundBox[6].z = maxz;
boundBox[7].x = rect.right;
boundBox[7].y = rect.bottom;
boundBox[7].z = maxz;
return TheCamera.IsBoxVisible(boundBox, &TheCamera.GetCameraMatrix());
}
void
CEntity::Add(void)
{
int x, xstart, xmid, xend;
int y, ystart, ymid, yend;
CSector *s;
CPtrList *list;
CRect bounds = GetBoundRect();
xstart = CWorld::GetSectorIndexX(bounds.left);
xend = CWorld::GetSectorIndexX(bounds.right);
xmid = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f);
ystart = CWorld::GetSectorIndexY(bounds.bottom);
yend = CWorld::GetSectorIndexY(bounds.top);
ymid = CWorld::GetSectorIndexY((bounds.bottom + bounds.top)/2.0f);
assert(xstart >= 0);
assert(xend < NUMSECTORS_X);
assert(ystart >= 0);
assert(yend < NUMSECTORS_Y);
for(y = ystart; y <= yend; y++)
for(x = xstart; x <= xend; x++){
s = CWorld::GetSector(x, y);
if(x == xmid && y == ymid) switch(m_type){
case ENTITY_TYPE_BUILDING:
list = &s->m_lists[ENTITYLIST_BUILDINGS];
break;
case ENTITY_TYPE_VEHICLE:
list = &s->m_lists[ENTITYLIST_VEHICLES];
break;
case ENTITY_TYPE_PED:
list = &s->m_lists[ENTITYLIST_PEDS];
break;
case ENTITY_TYPE_OBJECT:
list = &s->m_lists[ENTITYLIST_OBJECTS];
break;
case ENTITY_TYPE_DUMMY:
list = &s->m_lists[ENTITYLIST_DUMMIES];
break;
}else switch(m_type){
case ENTITY_TYPE_BUILDING:
list = &s->m_lists[ENTITYLIST_BUILDINGS_OVERLAP];
break;
case ENTITY_TYPE_VEHICLE:
list = &s->m_lists[ENTITYLIST_VEHICLES_OVERLAP];
break;
case ENTITY_TYPE_PED:
list = &s->m_lists[ENTITYLIST_PEDS_OVERLAP];
break;
case ENTITY_TYPE_OBJECT:
list = &s->m_lists[ENTITYLIST_OBJECTS_OVERLAP];
break;
case ENTITY_TYPE_DUMMY:
list = &s->m_lists[ENTITYLIST_DUMMIES_OVERLAP];
break;
}
list->InsertItem(this);
}
}
void
CEntity::Remove(void)
{
int x, xstart, xmid, xend;
int y, ystart, ymid, yend;
CSector *s;
CPtrList *list;
CRect bounds = GetBoundRect();
xstart = CWorld::GetSectorIndexX(bounds.left);
xend = CWorld::GetSectorIndexX(bounds.right);
xmid = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f);
ystart = CWorld::GetSectorIndexY(bounds.bottom);
yend = CWorld::GetSectorIndexY(bounds.top);
ymid = CWorld::GetSectorIndexY((bounds.bottom + bounds.top)/2.0f);
assert(xstart >= 0);
assert(xend < NUMSECTORS_X);
assert(ystart >= 0);
assert(yend < NUMSECTORS_Y);
for(y = ystart; y <= yend; y++)
for(x = xstart; x <= xend; x++){
s = CWorld::GetSector(x, y);
if(x == xmid && y == ymid) switch(m_type){
case ENTITY_TYPE_BUILDING:
list = &s->m_lists[ENTITYLIST_BUILDINGS];
break;
case ENTITY_TYPE_VEHICLE:
list = &s->m_lists[ENTITYLIST_VEHICLES];
break;
case ENTITY_TYPE_PED:
list = &s->m_lists[ENTITYLIST_PEDS];
break;
case ENTITY_TYPE_OBJECT:
list = &s->m_lists[ENTITYLIST_OBJECTS];
break;
case ENTITY_TYPE_DUMMY:
list = &s->m_lists[ENTITYLIST_DUMMIES];
break;
}else switch(m_type){
case ENTITY_TYPE_BUILDING:
list = &s->m_lists[ENTITYLIST_BUILDINGS_OVERLAP];
break;
case ENTITY_TYPE_VEHICLE:
list = &s->m_lists[ENTITYLIST_VEHICLES_OVERLAP];
break;
case ENTITY_TYPE_PED:
list = &s->m_lists[ENTITYLIST_PEDS_OVERLAP];
break;
case ENTITY_TYPE_OBJECT:
list = &s->m_lists[ENTITYLIST_OBJECTS_OVERLAP];
break;
case ENTITY_TYPE_DUMMY:
list = &s->m_lists[ENTITYLIST_DUMMIES_OVERLAP];
break;
}
list->RemoveItem(this);
}
}
void
CEntity::CreateRwObject(void)
{
CBaseModelInfo *mi;
mi = CModelInfo::GetModelInfo(m_modelIndex);
m_rwObject = mi->CreateInstance();
if(m_rwObject){
if(IsBuilding())
gBuildings++;
if(RwObjectGetType(m_rwObject) == rpATOMIC)
m_matrix.AttachRW(RwFrameGetMatrix(RpAtomicGetFrame(m_rwObject)), false);
else if(RwObjectGetType(m_rwObject) == rpCLUMP)
m_matrix.AttachRW(RwFrameGetMatrix(RpClumpGetFrame(m_rwObject)), false);
mi->AddRef();
}
}
void
CEntity::DeleteRwObject(void)
{
RwFrame *f;
m_matrix.Detach();
if(m_rwObject){
if(RwObjectGetType(m_rwObject) == rpATOMIC){
f = RpAtomicGetFrame(m_rwObject);
RpAtomicDestroy((RpAtomic*)m_rwObject);
RwFrameDestroy(f);
}else if(RwObjectGetType(m_rwObject) == rpCLUMP)
RpClumpDestroy((RpClump*)m_rwObject);
m_rwObject = nil;
CModelInfo::GetModelInfo(m_modelIndex)->RemoveRef();
if(IsBuilding())
gBuildings--;
}
}
void
CEntity::UpdateRwFrame(void)
{
if(m_rwObject){
if(RwObjectGetType(m_rwObject) == rpATOMIC)
RwFrameUpdateObjects(RpAtomicGetFrame(m_rwObject));
else if(RwObjectGetType(m_rwObject) == rpCLUMP)
RwFrameUpdateObjects(RpClumpGetFrame(m_rwObject));
}
}
void
CEntity::SetupBigBuilding(void)
{
CSimpleModelInfo *mi;
mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(m_modelIndex);
bIsBIGBuilding = true;
m_flagC20 = true;
bUsesCollision = false;
m_level = CTheZones::GetLevelFromPosition(GetPosition());
if(m_level == LEVEL_NONE){
if(mi->GetTxdSlot() != CTxdStore::FindTxdSlot("generic")){
mi->SetTexDictionary("generic");
printf("%d:%s txd has been set to generic\n", m_modelIndex, mi->GetName());
}
}
if(mi->m_lodDistances[0] > 2000.0f)
m_level = LEVEL_NONE;
}
CRect
CEntity::GetBoundRect(void)
{
CRect rect;
CVector v;
CColModel *col = CModelInfo::GetModelInfo(m_modelIndex)->GetColModel();
rect.ContainPoint(m_matrix * col->boundingBox.min);
rect.ContainPoint(m_matrix * col->boundingBox.max);
v = col->boundingBox.min;
v.x = col->boundingBox.max.x;
rect.ContainPoint(m_matrix * v);
v = col->boundingBox.max;
v.x = col->boundingBox.min.x;
rect.ContainPoint(m_matrix * v);
return rect;
}
void
CEntity::PreRender(void)
{
}
void
CEntity::Render(void)
{
if(m_rwObject){
bImBeingRendered = true;
if(RwObjectGetType(m_rwObject) == rpATOMIC)
RpAtomicRender((RpAtomic*)m_rwObject);
else
RpClumpRender((RpClump*)m_rwObject);
bImBeingRendered = false;
}
}
bool
CEntity::SetupLighting(void)
{
DeActivateDirectional();
SetAmbientColours();
return false;
}
void
CEntity::RegisterReference(CEntity **pent)
{
if(IsBuilding())
return;
CReference *ref;
// check if already registered
for(ref = m_pFirstReference; ref; ref = ref->next)
if(ref->pentity == pent)
return;
// have to allocate new reference
ref = CReferences::pEmptyList;
if(ref){
CReferences::pEmptyList = ref->next;
ref->pentity = pent;
ref->next = m_pFirstReference;
m_pFirstReference = ref;
return;
}
return;
}
// Clear all references to this entity
void
CEntity::ResolveReferences(void)
{
CReference *ref;
// clear pointers to this entity
for(ref = m_pFirstReference; ref; ref = ref->next)
if(*ref->pentity == this)
*ref->pentity = nil;
// free list
if(m_pFirstReference){
for(ref = m_pFirstReference; ref->next; ref = ref->next)
;
ref->next = CReferences::pEmptyList;
CReferences::pEmptyList = ref;
m_pFirstReference = nil;
}
}
// Free all references that no longer point to this entity
void
CEntity::PruneReferences(void)
{
CReference *ref, *next, **lastnextp;
lastnextp = &m_pFirstReference;
for(ref = m_pFirstReference; ref; ref = next){
next = ref->next;
if(*ref->pentity == this)
lastnextp = &ref->next;
else{
*lastnextp = ref->next;
ref->next = CReferences::pEmptyList;
CReferences::pEmptyList = ref;
}
}
}
STARTPATCHES
InjectHook(0x4742C0, (void (CEntity::*)(CVector&))&CEntity::GetBoundCentre, PATCH_JUMP);
InjectHook(0x474310, &CEntity::GetBoundRadius, PATCH_JUMP);
InjectHook(0x474C10, &CEntity::GetIsTouching, PATCH_JUMP);
InjectHook(0x474CC0, &CEntity::GetIsOnScreen, PATCH_JUMP);
InjectHook(0x474D20, &CEntity::GetIsOnScreenComplex, PATCH_JUMP);
InjectHook(0x474CA0, &CEntity::IsVisible, PATCH_JUMP);
InjectHook(0x474330, &CEntity::UpdateRwFrame, PATCH_JUMP);
InjectHook(0x4755E0, &CEntity::SetupBigBuilding, PATCH_JUMP);
InjectHook(0x4A7480, &CEntity::RegisterReference, PATCH_JUMP);
InjectHook(0x4A74E0, &CEntity::ResolveReferences, PATCH_JUMP);
InjectHook(0x4A7530, &CEntity::PruneReferences, PATCH_JUMP);
InjectHook(0x475080, &CEntity::Add_, PATCH_JUMP);
InjectHook(0x475310, &CEntity::Remove_, PATCH_JUMP);
InjectHook(0x473EA0, &CEntity::CreateRwObject_, PATCH_JUMP);
InjectHook(0x473F90, &CEntity::DeleteRwObject_, PATCH_JUMP);
InjectHook(0x474000, &CEntity::GetBoundRect_, PATCH_JUMP);
InjectHook(0x474BD0, &CEntity::Render_, PATCH_JUMP);
InjectHook(0x4A7C60, &CEntity::SetupLighting_, PATCH_JUMP);
ENDPATCHES

146
src/entities/Entity.h Normal file
View file

@ -0,0 +1,146 @@
#pragma once
#include "ModelInfo.h"
#include "Placeable.h"
struct CReference;
enum eEntityType
{
ENTITY_TYPE_NOTHING = 0,
ENTITY_TYPE_BUILDING,
ENTITY_TYPE_VEHICLE,
ENTITY_TYPE_PED,
ENTITY_TYPE_OBJECT,
ENTITY_TYPE_DUMMY,
ENTITY_TYPE_6,
ENTITY_TYPE_7,
};
enum eEntityStatus
{
// from SA MTA! let's hope they didn't change from III
STATUS_PLAYER = 0,
STATUS_PLAYER_PLAYBACKFROMBUFFER,
STATUS_SIMPLE,
STATUS_PHYSICS,
STATUS_ABANDONED,
STATUS_WRECKED,
STATUS_TRAIN_MOVING,
STATUS_TRAIN_NOT_MOVING,
STATUS_HELI,
STATUS_PLANE,
STATUS_PLAYER_REMOTE,
STATUS_PLAYER_DISABLED,
//STATUS_TRAILER,
//STATUS_SIMPLE_TRAILER
};
class CEntity : public CPlaceable
{
public:
RwObject *m_rwObject;
uint32 m_type : 3;
uint32 m_status : 5;
// flagsA
uint32 bUsesCollision : 1;
uint32 bCollisionProcessed : 1;
uint32 bIsStatic : 1;
uint32 bHasContacted : 1;
uint32 bPedPhysics : 1;
uint32 bIsStuck : 1;
uint32 bIsInSafePosition : 1;
uint32 bUseCollisionRecords : 1;
// flagsB
uint32 bWasPostponed : 1;
uint32 m_flagB2 : 1; // explosion proof?
uint32 bIsVisible : 1;
uint32 bHasCollided : 1; //
uint32 bRenderScorched : 1;
uint32 m_flagB20 : 1; // bFlashing?
uint32 bIsBIGBuilding : 1;
// VC inserts one more flag here: if drawdist <= 2000
uint32 bRenderDamaged : 1;
// flagsC
uint32 m_flagC1 : 1; // bullet proof?
uint32 m_flagC2 : 1; // fire proof?
uint32 m_flagC4 : 1; // collision proof?
uint32 m_flagC8 : 1; // melee proof?
uint32 m_flagC10 : 1; // bOnlyDamagedByPlayer?
uint32 m_flagC20 : 1;
uint32 m_bZoneCulled : 1;
uint32 m_bZoneCulled2 : 1; // only treadables+10m
// flagsD
uint32 bRemoveFromWorld : 1;
uint32 bHasHitWall : 1;
uint32 bImBeingRendered : 1;
uint32 m_flagD8 : 1;
uint32 m_flagD10 : 1;
uint32 bDrawLast : 1;
uint32 m_flagD40 : 1;
uint32 m_flagD80 : 1;
// flagsE
uint32 bDistanceFade : 1;
uint32 m_flagE2 : 1;
uint16 m_scanCode;
int16 m_randomSeed;
int16 m_modelIndex;
uint16 m_level; // int16
CReference *m_pFirstReference;
virtual void Add(void);
virtual void Remove(void);
virtual void SetModelIndex(uint32 i) { m_modelIndex = i; CreateRwObject(); }
virtual void SetModelIndexNoCreate(uint32 i) { m_modelIndex = i; }
virtual void CreateRwObject(void);
virtual void DeleteRwObject(void);
virtual CRect GetBoundRect(void);
virtual void ProcessControl(void) {}
virtual void ProcessCollision(void) {}
virtual void ProcessShift(void) {}
virtual void Teleport(CVector v) {}
virtual void PreRender(void);
virtual void Render(void);
virtual bool SetupLighting(void);
virtual void RemoveLighting(bool) {}
virtual void FlagToDestroyWhenNextProcessed(void) {}
bool IsBuilding(void) { return m_type == ENTITY_TYPE_BUILDING; }
bool IsVehicle(void) { return m_type == ENTITY_TYPE_VEHICLE; }
bool IsPed(void) { return m_type == ENTITY_TYPE_PED; }
bool IsObject(void) { return m_type == ENTITY_TYPE_OBJECT; }
bool IsDummy(void) { return m_type == ENTITY_TYPE_DUMMY; }
void GetBoundCentre(CVector &out);
CVector GetBoundCentre(void) { CVector v; GetBoundCentre(v); return v; }
float GetBoundRadius(void) { return CModelInfo::GetModelInfo(m_modelIndex)->GetColModel()->boundingSphere.radius; }
bool GetIsTouching(CVector const &center, float r);
bool GetIsOnScreen(void);
bool GetIsOnScreenComplex(void);
bool IsVisible(void) { return m_rwObject && bIsVisible && GetIsOnScreen(); }
bool IsVisibleComplex(void) { return m_rwObject && bIsVisible && GetIsOnScreenComplex(); }
int GetModelIndex(void) { return m_modelIndex; }
void UpdateRwFrame(void);
void SetupBigBuilding(void);
void RegisterReference(CEntity **pent);
void ResolveReferences(void);
void PruneReferences(void);
// to make patching virtual functions possible
void Add_(void) { CEntity::Add(); }
void Remove_(void) { CEntity::Remove(); }
void CreateRwObject_(void) { CEntity::CreateRwObject(); }
void DeleteRwObject_(void) { CEntity::DeleteRwObject(); }
CRect GetBoundRect_(void) { return CEntity::GetBoundRect(); }
void Render_(void) { CEntity::Render(); }
bool SetupLighting_(void) { return CEntity::SetupLighting(); }
};
static_assert(sizeof(CEntity) == 0x64, "CEntity: error");

9
src/entities/Object.cpp Normal file
View file

@ -0,0 +1,9 @@
#include "common.h"
#include "patcher.h"
#include "Object.h"
#include "Pools.h"
void *CObject::operator new(size_t sz) { return CPools::GetObjectPool()->New(); }
void CObject::operator delete(void *p, size_t sz) { CPools::GetObjectPool()->Delete((CObject*)p); }
WRAPPER void CObject::ObjectDamage(float amount) { EAXJMP(0x4BB240); }

50
src/entities/Object.h Normal file
View file

@ -0,0 +1,50 @@
#pragma once
#include "Physical.h"
enum {
GAME_OBJECT = 1,
MISSION_OBJECT = 2,
TEMP_OBJECT = 3,
};
class CObject : public CPhysical
{
public:
CMatrix m_objectMatrix;
float m_fUprootLimit;
int8 ObjectCreatedBy;
// int8 m_nObjectFlags;
int8 m_obj_flag1 : 1;
int8 m_obj_flag2 : 1;
int8 m_obj_flag4 : 1;
int8 m_obj_flag8 : 1;
int8 m_obj_flag10 : 1;
int8 bHasBeenDamaged : 1;
int8 m_obj_flag40 : 1;
int8 m_obj_flag80 : 1;
int8 field_172;
int8 field_173;
float m_fCollisionDamageMultiplier;
int8 m_nCollisionDamageEffect;
int8 m_bSpecialCollisionResponseCases;
int8 m_bCameraToAvoidThisObject;
int8 field_17B;
int8 field_17C;
int8 field_17D;
int8 field_17E;
int8 field_17F;
int32 m_nEndOfLifeTime;
int16 m_nRefModelIndex;
int8 field_186;
int8 field_187;
CEntity *m_pCurSurface;
CEntity *field_18C;
int8 m_colour1, m_colour2;
static void *operator new(size_t);
static void operator delete(void*, size_t);
void ObjectDamage(float amount);
};
static_assert(sizeof(CObject) == 0x198, "CObject: error");

35
src/entities/Ped.h Normal file
View file

@ -0,0 +1,35 @@
#pragma once
#include "Physical.h"
enum PedAction
{
PED_PASSENGER = 44,
};
class CVehicle;
class CPed : public CPhysical
{
public:
// 0x128
uint8 stuff1[252];
int32 m_nPedState;
uint8 stuff2[196];
CEntity *m_pCurrentPhysSurface;
CVector m_vecOffsetFromPhysSurface;
CEntity *m_pCurSurface;
uint8 stuff3[16];
CVehicle *m_pMyVehicle;
bool bInVehicle;
uint8 stuff4[23];
int32 m_nPedType;
uint8 stuff5[528];
bool IsPlayer(void) { return m_nPedType == 0 || m_nPedType== 1 || m_nPedType == 2 || m_nPedType == 3; }
};
static_assert(offsetof(CPed, m_nPedState) == 0x224, "CPed: error");
static_assert(offsetof(CPed, m_pCurSurface) == 0x2FC, "CPed: error");
static_assert(offsetof(CPed, m_pMyVehicle) == 0x310, "CPed: error");
static_assert(offsetof(CPed, m_nPedType) == 0x32C, "CPed: error");
static_assert(sizeof(CPed) == 0x540, "CPed: error");

916
src/entities/Physical.cpp Normal file
View file

@ -0,0 +1,916 @@
#include "common.h"
#include "patcher.h"
#include "World.h"
#include "Timer.h"
#include "ModelIndices.h"
#include "Vehicle.h"
#include "Ped.h"
#include "Object.h"
#include "Glass.h"
#include "ParticleObject.h"
#include "Particle.h"
#include "SurfaceTable.h"
#include "Physical.h"
void
CPhysical::Add(void)
{
int x, xstart, xmid, xend;
int y, ystart, ymid, yend;
CSector *s;
CPtrList *list;
CRect bounds = GetBoundRect();
xstart = CWorld::GetSectorIndexX(bounds.left);
xend = CWorld::GetSectorIndexX(bounds.right);
xmid = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f);
ystart = CWorld::GetSectorIndexY(bounds.bottom);
yend = CWorld::GetSectorIndexY(bounds.top);
ymid = CWorld::GetSectorIndexY((bounds.bottom + bounds.top)/2.0f);
assert(xstart >= 0);
assert(xend < NUMSECTORS_X);
assert(ystart >= 0);
assert(yend < NUMSECTORS_Y);
for(y = ystart; y <= yend; y++)
for(x = xstart; x <= xend; x++){
s = CWorld::GetSector(x, y);
if(x == xmid && y == ymid) switch(m_type){
case ENTITY_TYPE_VEHICLE:
list = &s->m_lists[ENTITYLIST_VEHICLES];
break;
case ENTITY_TYPE_PED:
list = &s->m_lists[ENTITYLIST_PEDS];
break;
case ENTITY_TYPE_OBJECT:
list = &s->m_lists[ENTITYLIST_OBJECTS];
break;
default:
assert(0);
}else switch(m_type){
case ENTITY_TYPE_VEHICLE:
list = &s->m_lists[ENTITYLIST_VEHICLES_OVERLAP];
break;
case ENTITY_TYPE_PED:
list = &s->m_lists[ENTITYLIST_PEDS_OVERLAP];
break;
case ENTITY_TYPE_OBJECT:
list = &s->m_lists[ENTITYLIST_OBJECTS_OVERLAP];
break;
default:
assert(0);
}
CPtrNode *node = list->InsertItem(this);
assert(node);
m_entryInfoList.InsertItem(list, node, s);
}
}
void
CPhysical::Remove(void)
{
CEntryInfoNode *node, *next;
for(node = m_entryInfoList.first; node; node = next){
next = node->next;
node->list->DeleteNode(node->listnode);
m_entryInfoList.DeleteNode(node);
}
}
void
CPhysical::RemoveAndAdd(void)
{
int x, xstart, xmid, xend;
int y, ystart, ymid, yend;
CSector *s;
CPtrList *list;
CRect bounds = GetBoundRect();
xstart = CWorld::GetSectorIndexX(bounds.left);
xend = CWorld::GetSectorIndexX(bounds.right);
xmid = CWorld::GetSectorIndexX((bounds.left + bounds.right)/2.0f);
ystart = CWorld::GetSectorIndexY(bounds.bottom);
yend = CWorld::GetSectorIndexY(bounds.top);
ymid = CWorld::GetSectorIndexY((bounds.bottom + bounds.top)/2.0f);
assert(xstart >= 0);
assert(xend < NUMSECTORS_X);
assert(ystart >= 0);
assert(yend < NUMSECTORS_Y);
// we'll try to recycle nodes from here
CEntryInfoNode *next = m_entryInfoList.first;
for(y = ystart; y <= yend; y++)
for(x = xstart; x <= xend; x++){
s = CWorld::GetSector(x, y);
if(x == xmid && y == ymid) switch(m_type){
case ENTITY_TYPE_VEHICLE:
list = &s->m_lists[ENTITYLIST_VEHICLES];
break;
case ENTITY_TYPE_PED:
list = &s->m_lists[ENTITYLIST_PEDS];
break;
case ENTITY_TYPE_OBJECT:
list = &s->m_lists[ENTITYLIST_OBJECTS];
break;
}else switch(m_type){
case ENTITY_TYPE_VEHICLE:
list = &s->m_lists[ENTITYLIST_VEHICLES_OVERLAP];
break;
case ENTITY_TYPE_PED:
list = &s->m_lists[ENTITYLIST_PEDS_OVERLAP];
break;
case ENTITY_TYPE_OBJECT:
list = &s->m_lists[ENTITYLIST_OBJECTS_OVERLAP];
break;
}
if(next){
// If we still have old nodes, use them
next->list->RemoveNode(next->listnode);
list->InsertNode(next->listnode);
next->list = list;
next->sector = s;
next = next->next;
}else{
CPtrNode *node = list->InsertItem(this);
m_entryInfoList.InsertItem(list, node, s);
}
}
// Remove old nodes we no longer need
CEntryInfoNode *node;
for(node = next; node; node = next){
next = node->next;
node->list->DeleteNode(node->listnode);
m_entryInfoList.DeleteNode(node);
}
}
CRect
CPhysical::GetBoundRect(void)
{
CVector center;
float radius;
GetBoundCentre(center);
radius = GetBoundRadius();
return CRect(center.x-radius, center.y-radius, center.x+radius, center.y+radius);
}
void
CPhysical::AddToMovingList(void)
{
m_movingListNode = CWorld::GetMovingEntityList().InsertItem(this);
}
void
CPhysical::RemoveFromMovingList(void)
{
if(m_movingListNode){
CWorld::GetMovingEntityList().DeleteNode(m_movingListNode);
m_movingListNode = nil;
}
}
/*
* Some quantities (german in parens):
*
* acceleration: distance/time^2: a
* velocity: distance/time: v (GTA: speed)
* momentum (impuls): velocity*mass: p
* impulse (kraftstoss): delta momentum, force*time: J
*
* angular equivalents:
* velocity -> angular velocity (GTA: turn speed)
* momentum -> angular momentum (drehimpuls): L = r cross p
* force -> torque (drehmoment): tau = r cross F
* mass -> moment of inertia, angular mass (drehmoment, drehmasse): I = L/omega (GTA: turn mass)
*/
CVector
CPhysical::GetSpeed(const CVector &r)
{
return m_vecMoveSpeed + m_vecMoveFriction + CrossProduct(m_vecTurnFriction + m_vecTurnSpeed, r);
}
void
CPhysical::ApplyMoveSpeed(void)
{
GetPosition() += m_vecMoveSpeed * CTimer::GetTimeStep();
}
void
CPhysical::ApplyTurnSpeed(void)
{
// Move the coordinate axes by their speed
// Note that this denormalizes the matrix
CVector turnvec = m_vecTurnSpeed*CTimer::GetTimeStep();
GetRight() += CrossProduct(turnvec, GetRight());
GetForward() += CrossProduct(turnvec, GetForward());
GetUp() += CrossProduct(turnvec, GetUp());
}
void
CPhysical::ApplyMoveForce(float jx, float jy, float jz)
{
m_vecMoveSpeed += CVector(jx, jy, jz)*(1.0f/m_fMass);
}
void
CPhysical::ApplyTurnForce(float jx, float jy, float jz, float px, float py, float pz)
{
CVector com = Multiply3x3(m_matrix, m_vecCentreOfMass);
CVector turnimpulse = CrossProduct(CVector(px, py, pz)-com, CVector(jx, jy, jz));
m_vecTurnSpeed += turnimpulse*(1.0f/m_fTurnMass);
}
void
CPhysical::ApplyFrictionMoveForce(float jx, float jy, float jz)
{
m_vecMoveFriction += CVector(jx, jy, jz)*(1.0f/m_fMass);
}
void
CPhysical::ApplyFrictionTurnForce(float jx, float jy, float jz, float px, float py, float pz)
{
CVector com = Multiply3x3(m_matrix, m_vecCentreOfMass);
CVector turnimpulse = CrossProduct(CVector(px, py, pz)-com, CVector(jx, jy, jz));
m_vecTurnFriction += turnimpulse*(1.0f/m_fTurnMass);
}
void
CPhysical::ApplySpringCollision(float f1, CVector &v, CVector &p, float f2, float f3)
{
if(1.0f - f2 <= 0.0f)
return;
float step = min(CTimer::GetTimeStep(), 3.0f);
float strength = -0.008f*m_fMass*2.0f*step * f1 * (1.0f-f2) * f3;
ApplyMoveForce(v.x*strength, v.y*strength, v.z*strength);
ApplyTurnForce(v.x*strength, v.y*strength, v.z*strength, p.x, p.y, p.z);
}
void
CPhysical::ApplyGravity(void)
{
if(bAffectedByGravity)
m_vecMoveSpeed.z -= 0.008f * CTimer::GetTimeStep();
}
void
CPhysical::ApplyFriction(void)
{
m_vecMoveSpeed += m_vecMoveFriction;
m_vecTurnSpeed += m_vecTurnFriction;
m_vecMoveFriction = CVector(0.0f, 0.0f, 0.0f);
m_vecTurnFriction = CVector(0.0f, 0.0f, 0.0f);
}
void
CPhysical::ApplyAirResistance(void)
{
if(m_fAirResistance > 0.1f){
float f = powf(m_fAirResistance, CTimer::GetTimeStep());
m_vecMoveSpeed *= f;
m_vecTurnSpeed *= f;
}else{
float f = powf(1.0f/(m_fAirResistance*0.5f*m_vecMoveSpeed.MagnitudeSqr() + 1.0f), CTimer::GetTimeStep());
m_vecMoveSpeed *= f;
m_vecTurnSpeed *= 0.99f;
}
}
bool
CPhysical::ApplyCollision(CPhysical *B, CColPoint &colpoint, float &impulseA, float &impulseB)
{
float eA, eB;
CPhysical *A = this;
CObject *Bobj = (CObject*)B;
bool ispedcontactA = false;
bool ispedcontactB = false;
float timestepA;
if(B->bPedPhysics){
timestepA = 10.0f;
if(B->IsPed() && ((CPed*)B)->m_pCurrentPhysSurface == A)
ispedcontactA = true;
}else
timestepA = A->m_phy_flagA1 ? 2.0f : 1.0f;
float timestepB;
if(A->bPedPhysics){
if(A->IsPed() && ((CPed*)A)->IsPlayer() && B->IsVehicle() &&
(B->m_status == STATUS_ABANDONED || B->m_status == STATUS_WRECKED || A->bHasHitWall))
timestepB = 2200.0f / B->m_fMass;
else
timestepB = 10.0f;
if(A->IsPed() && ((CPed*)A)->m_pCurrentPhysSurface == B)
ispedcontactB = true;
}else
timestepB = B->m_phy_flagA1 ? 2.0f : 1.0f;
float speedA, speedB;
if(B->bIsStatic){
if(A->bPedPhysics){
speedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal);
if(speedA < 0.0f){
if(B->IsObject()){
impulseA = -speedA * A->m_fMass;
impulseB = impulseA;
if(impulseA > Bobj->m_fUprootLimit){
if(IsGlass(B->GetModelIndex()))
CGlass::WindowRespondsToCollision(B, impulseA, A->m_vecMoveSpeed, colpoint.point, false);
else if(!B->bInfiniteMass)
B->bIsStatic = false;
}else{
if(IsGlass(B->GetModelIndex()))
CGlass::WindowRespondsToSoftCollision(B, impulseA);
if(!A->bInfiniteMass)
A->ApplyMoveForce(colpoint.normal*(1.0f + A->m_fElasticity)*impulseA);
return true;
}
}else if(!B->bInfiniteMass)
B->bIsStatic = false;
if(B->bInfiniteMass){
impulseA = -speedA * A->m_fMass;
impulseB = 0.0f;
if(!A->bInfiniteMass)
A->ApplyMoveForce(colpoint.normal*(1.0f + A->m_fElasticity)*impulseA);
return true;
}
}
}else{
CVector pointposA = colpoint.point - A->GetPosition();
speedA = DotProduct(A->GetSpeed(pointposA), colpoint.normal);
if(speedA < 0.0f){
if(B->IsObject()){
if(A->bHasHitWall)
eA = -1.0f;
else
eA = -(1.0f + A->m_fElasticity);
impulseA = eA * speedA * A->GetMass(pointposA, colpoint.normal);
impulseB = impulseA;
if(Bobj->m_nCollisionDamageEffect && impulseA > 20.0f){
Bobj->ObjectDamage(impulseA);
if(!B->bUsesCollision){
if(!A->bInfiniteMass){
A->ApplyMoveForce(colpoint.normal*0.2f*impulseA);
A->ApplyTurnForce(colpoint.normal*0.2f*impulseA, pointposA);
}
return false;
}
}
if((impulseA > Bobj->m_fUprootLimit || A->bIsStuck) &&
!B->bInfiniteMass){
if(IsGlass(B->GetModelIndex()))
CGlass::WindowRespondsToCollision(B, impulseA, A->m_vecMoveSpeed, colpoint.point, false);
else
B->bIsStatic = false;
int16 model = B->GetModelIndex();
if(model == MI_FIRE_HYDRANT && !Bobj->bHasBeenDamaged){
CParticleObject::AddObject(POBJECT_FIRE_HYDRANT, B->GetPosition() - CVector(0.0f, 0.0f, 0.5f), true);
Bobj->bHasBeenDamaged = true;
}else if(B->IsObject() && model != MI_EXPLODINGBARREL && model != MI_PETROLPUMP)
Bobj->bHasBeenDamaged = true;
}else{
if(IsGlass(B->GetModelIndex()))
CGlass::WindowRespondsToSoftCollision(B, impulseA);
CVector f = colpoint.normal * impulseA;
if(A->IsVehicle() && colpoint.normal.z < 0.7f)
f.z *= 0.3f;
if(!A->bInfiniteMass){
A->ApplyMoveForce(f);
if(!A->IsVehicle() || !CWorld::bNoMoreCollisionTorque)
A->ApplyTurnForce(f, pointposA);
}
return true;
}
}else if(!B->bInfiniteMass)
B->bIsStatic = false;
}
}
if(B->bIsStatic)
return false;
if(!B->bInfiniteMass)
B->AddToMovingList();
}
// B is not static
if(A->bPedPhysics && B->bPedPhysics){
// negative if A is moving towards B
speedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal);
// positive if B is moving towards A
// not interested in how much B moves into A apparently?
// only interested in cases where A collided into B
speedB = max(0.0f, DotProduct(B->m_vecMoveSpeed, colpoint.normal));
// A has moved into B
if(speedA < speedB){
if(!A->bHasHitWall)
speedB -= (speedA - speedB) * (A->m_fElasticity+B->m_fElasticity)/2.0f;
impulseA = (speedB-speedA) * A->m_fMass * timestepA;
if(!A->bInfiniteMass)
A->ApplyMoveForce(colpoint.normal*(impulseA/timestepA));
return true;
}
}else if(A->bPedPhysics){
CVector pointposB = colpoint.point - B->GetPosition();
speedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal);
speedB = DotProduct(B->GetSpeed(pointposB), colpoint.normal);
float a = A->m_fMass*timestepA;
float b = B->GetMassTime(pointposB, colpoint.normal, timestepB);
float speedSum = (b*speedB + a*speedA)/(a + b);
if(speedA < speedSum){
if(A->bHasHitWall)
eA = speedSum;
else
eA = speedSum - (speedA - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f;
if(B->bHasHitWall)
eB = speedSum;
else
eB = speedSum - (speedB - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f;
impulseA = (eA - speedA) * a;
impulseB = -(eB - speedB) * b;
CVector fA = colpoint.normal*(impulseA/timestepA);
CVector fB = colpoint.normal*(-impulseB/timestepB);
if(!A->bInfiniteMass){
if(fA.z < 0.0f) fA.z = 0.0f;
if(ispedcontactB){
fA.x *= 2.0f;
fA.y *= 2.0f;
}
A->ApplyMoveForce(fA);
}
if(!B->bInfiniteMass && !ispedcontactB){
B->ApplyMoveForce(fB);
B->ApplyTurnForce(fB, pointposB);
}
return true;
}
}else if(B->bPedPhysics){
CVector pointposA = colpoint.point - A->GetPosition();
speedA = DotProduct(A->GetSpeed(pointposA), colpoint.normal);
speedB = DotProduct(B->m_vecMoveSpeed, colpoint.normal);
float a = A->GetMassTime(pointposA, colpoint.normal, timestepA);
float b = B->m_fMass*timestepB;
float speedSum = (b*speedB + a*speedA)/(a + b);
if(speedA < speedSum){
if(A->bHasHitWall)
eA = speedSum;
else
eA = speedSum - (speedA - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f;
if(B->bHasHitWall)
eB = speedSum;
else
eB = speedSum - (speedB - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f;
impulseA = (eA - speedA) * a;
impulseB = -(eB - speedB) * b;
CVector fA = colpoint.normal*(impulseA/timestepA);
CVector fB = colpoint.normal*(-impulseB/timestepB);
if(!A->bInfiniteMass && !ispedcontactA){
if(fA.z < 0.0f) fA.z = 0.0f;
A->ApplyMoveForce(fA);
A->ApplyTurnForce(fA, pointposA);
}
if(!B->bInfiniteMass){
if(fB.z < 0.0f){
fB.z = 0.0f;
if(fabs(speedA) < 0.01f)
fB *= 0.5f;
}
if(ispedcontactA){
fB.x *= 2.0f;
fB.y *= 2.0f;
}
B->ApplyMoveForce(fB);
}
return true;
}
}else{
CVector pointposA = colpoint.point - A->GetPosition();
CVector pointposB = colpoint.point - B->GetPosition();
speedA = DotProduct(A->GetSpeed(pointposA), colpoint.normal);
speedB = DotProduct(B->GetSpeed(pointposB), colpoint.normal);
float a = A->GetMassTime(pointposA, colpoint.normal, timestepA);
float b = B->GetMassTime(pointposB, colpoint.normal, timestepB);
float speedSum = (b*speedB + a*speedA)/(a + b);
if(speedA < speedSum){
if(A->bHasHitWall)
eA = speedSum;
else
eA = speedSum - (speedA - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f;
if(B->bHasHitWall)
eB = speedSum;
else
eB = speedSum - (speedB - speedSum) * (A->m_fElasticity+B->m_fElasticity)/2.0f;
impulseA = (eA - speedA) * a;
impulseB = -(eB - speedB) * b;
CVector fA = colpoint.normal*(impulseA/timestepA);
CVector fB = colpoint.normal*(-impulseB/timestepB);
if(A->IsVehicle() && !A->bHasHitWall){
fA.x *= 1.4f;
fA.y *= 1.4f;
if(colpoint.normal.z < 0.7f)
fA.z *= 0.3f;
if(A->m_status == STATUS_PLAYER)
pointposA *= 0.8f;
if(CWorld::bNoMoreCollisionTorque){
A->ApplyFrictionMoveForce(fA*-0.3f);
A->ApplyFrictionTurnForce(fA*-0.3f, pointposA);
}
}
if(B->IsVehicle() && !B->bHasHitWall){
fB.x *= 1.4f;
fB.y *= 1.4f;
if(colpoint.normal.z < 0.7f)
fB.z *= 0.3f;
if(B->m_status == STATUS_PLAYER)
pointposB *= 0.8f;
if(CWorld::bNoMoreCollisionTorque){
// BUG: the game actually uses A here, but this can't be right
B->ApplyFrictionMoveForce(fB*-0.3f);
B->ApplyFrictionTurnForce(fB*-0.3f, pointposB);
}
}
if(!A->bInfiniteMass){
A->ApplyMoveForce(fA);
A->ApplyTurnForce(fA, pointposA);
}
if(!B->bInfiniteMass){
if(B->bIsInSafePosition)
B->UnsetIsInSafePosition();
B->ApplyMoveForce(fB);
B->ApplyTurnForce(fB, pointposB);
}
return true;
}
}
return false;
}
bool
CPhysical::ApplyCollisionAlt(CEntity *B, CColPoint &colpoint, float &impulse, CVector &moveSpeed, CVector &turnSpeed)
{
float normalSpeed;
float e;
CVector speed;
CVector vImpulse;
if(bPedPhysics){
normalSpeed = DotProduct(m_vecMoveSpeed, colpoint.normal);
if(normalSpeed < 0.0f){
impulse = -normalSpeed * m_fMass;
ApplyMoveForce(colpoint.normal * impulse);
return true;
}
}else{
CVector pointpos = colpoint.point - GetPosition();
speed = GetSpeed(pointpos);
normalSpeed = DotProduct(speed, colpoint.normal);
if(normalSpeed < 0.0f){
float minspeed = 0.0104f * CTimer::GetTimeStep();
if((IsObject() || IsVehicle() && GetUp().z < -0.3f) &&
!bHasContacted &&
fabs(m_vecMoveSpeed.x) < minspeed &&
fabs(m_vecMoveSpeed.y) < minspeed &&
fabs(m_vecMoveSpeed.z) < minspeed*2.0f)
e = -1.0f;
else
e = -(m_fElasticity + 1.0f);
impulse = normalSpeed * e * GetMass(pointpos, colpoint.normal);
// ApplyMoveForce
vImpulse = colpoint.normal*impulse;
if(IsVehicle() &&
(!bHasHitWall ||
!(m_vecMoveSpeed.MagnitudeSqr() > 0.1 || !(B->IsBuilding() || ((CPhysical*)B)->bInfiniteMass))))
moveSpeed += vImpulse * 1.2f * (1.0f/m_fMass);
else
moveSpeed += vImpulse * (1.0f/m_fMass);
// ApplyTurnForce
CVector com = Multiply3x3(m_matrix, m_vecCentreOfMass);
CVector turnimpulse = CrossProduct(pointpos-com, vImpulse);
turnSpeed += turnimpulse*(1.0f/m_fTurnMass);
return true;
}
}
return false;
}
bool
CPhysical::ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint)
{
CVector speedA, speedB;
float normalSpeedA, normalSpeedB;
CVector vOtherSpeedA, vOtherSpeedB;
float fOtherSpeedA, fOtherSpeedB;
float speedSum;
CVector frictionDir;
float impulseA, impulseB;
float impulseLimit;
CPhysical *A = this;
if(A->bPedPhysics && B->bPedPhysics){
normalSpeedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal);
normalSpeedB = DotProduct(B->m_vecMoveSpeed, colpoint.normal);
vOtherSpeedA = A->m_vecMoveSpeed - colpoint.normal*normalSpeedA;
vOtherSpeedB = B->m_vecMoveSpeed - colpoint.normal*normalSpeedB;
fOtherSpeedA = vOtherSpeedA.Magnitude();
fOtherSpeedB = vOtherSpeedB.Magnitude();
frictionDir = vOtherSpeedA * (1.0f/fOtherSpeedA);
speedSum = (B->m_fMass*fOtherSpeedB + A->m_fMass*fOtherSpeedA)/(B->m_fMass + A->m_fMass);
if(fOtherSpeedA > speedSum){
impulseA = (speedSum - fOtherSpeedA) * A->m_fMass;
impulseB = (speedSum - fOtherSpeedB) * B->m_fMass;
impulseLimit = adhesiveLimit*CTimer::GetTimeStep();
if(impulseA < -impulseLimit) impulseA = -impulseLimit;
if(impulseB > impulseLimit) impulseB = impulseLimit; // BUG: game has A's clamp again here, but this can't be right
A->ApplyFrictionMoveForce(frictionDir*impulseA);
B->ApplyFrictionMoveForce(frictionDir*impulseB);
return true;
}
}else if(A->bPedPhysics){
if(B->IsVehicle())
return false;
CVector pointposB = colpoint.point - B->GetPosition();
speedB = B->GetSpeed(pointposB);
normalSpeedA = DotProduct(A->m_vecMoveSpeed, colpoint.normal);
normalSpeedB = DotProduct(speedB, colpoint.normal);
vOtherSpeedA = A->m_vecMoveSpeed - colpoint.normal*normalSpeedA;
vOtherSpeedB = speedB - colpoint.normal*normalSpeedB;
fOtherSpeedA = vOtherSpeedA.Magnitude();
fOtherSpeedB = vOtherSpeedB.Magnitude();
frictionDir = vOtherSpeedA * (1.0f/fOtherSpeedA);
float massB = B->GetMass(pointposB, frictionDir);
speedSum = (massB*fOtherSpeedB + A->m_fMass*fOtherSpeedA)/(massB + A->m_fMass);
if(fOtherSpeedA > speedSum){
impulseA = (speedSum - fOtherSpeedA) * A->m_fMass;
impulseB = (speedSum - fOtherSpeedB) * massB;
impulseLimit = adhesiveLimit*CTimer::GetTimeStep();
if(impulseA < -impulseLimit) impulseA = -impulseLimit;
if(impulseB > impulseLimit) impulseB = impulseLimit;
A->ApplyFrictionMoveForce(frictionDir*impulseA);
B->ApplyFrictionMoveForce(frictionDir*impulseB);
B->ApplyFrictionTurnForce(frictionDir*impulseB, pointposB);
return true;
}
}else if(B->bPedPhysics){
if(A->IsVehicle())
return false;
CVector pointposA = colpoint.point - A->GetPosition();
speedA = A->GetSpeed(pointposA);
normalSpeedA = DotProduct(speedA, colpoint.normal);
normalSpeedB = DotProduct(B->m_vecMoveSpeed, colpoint.normal);
vOtherSpeedA = speedA - colpoint.normal*normalSpeedA;
vOtherSpeedB = B->m_vecMoveSpeed - colpoint.normal*normalSpeedB;
fOtherSpeedA = vOtherSpeedA.Magnitude();
fOtherSpeedB = vOtherSpeedB.Magnitude();
frictionDir = vOtherSpeedA * (1.0f/fOtherSpeedA);
float massA = A->GetMass(pointposA, frictionDir);
speedSum = (B->m_fMass*fOtherSpeedB + massA*fOtherSpeedA)/(B->m_fMass + massA);
if(fOtherSpeedA > speedSum){
impulseA = (speedSum - fOtherSpeedA) * massA;
impulseB = (speedSum - fOtherSpeedB) * B->m_fMass;
impulseLimit = adhesiveLimit*CTimer::GetTimeStep();
if(impulseA < -impulseLimit) impulseA = -impulseLimit;
if(impulseB > impulseLimit) impulseB = impulseLimit;
A->ApplyFrictionMoveForce(frictionDir*impulseA);
A->ApplyFrictionTurnForce(frictionDir*impulseA, pointposA);
B->ApplyFrictionMoveForce(frictionDir*impulseB);
return true;
}
}else{
CVector pointposA = colpoint.point - A->GetPosition();
CVector pointposB = colpoint.point - B->GetPosition();
speedA = A->GetSpeed(pointposA);
speedB = B->GetSpeed(pointposB);
normalSpeedA = DotProduct(speedA, colpoint.normal);
normalSpeedB = DotProduct(speedB, colpoint.normal);
vOtherSpeedA = speedA - colpoint.normal*normalSpeedA;
vOtherSpeedB = speedB - colpoint.normal*normalSpeedB;
fOtherSpeedA = vOtherSpeedA.Magnitude();
fOtherSpeedB = vOtherSpeedB.Magnitude();
frictionDir = vOtherSpeedA * (1.0f/fOtherSpeedA);
float massA = A->GetMass(pointposA, frictionDir);
float massB = B->GetMass(pointposB, frictionDir);
speedSum = (massB*fOtherSpeedB + massA*fOtherSpeedA)/(massB + massA);
if(fOtherSpeedA > speedSum){
impulseA = (speedSum - fOtherSpeedA) * massA;
impulseB = (speedSum - fOtherSpeedB) * massB;
impulseLimit = adhesiveLimit*CTimer::GetTimeStep();
if(impulseA < -impulseLimit) impulseA = -impulseLimit;
if(impulseB > impulseLimit) impulseB = impulseLimit;
A->ApplyFrictionMoveForce(frictionDir*impulseA);
A->ApplyFrictionTurnForce(frictionDir*impulseA, pointposA);
B->ApplyFrictionMoveForce(frictionDir*impulseB);
B->ApplyFrictionTurnForce(frictionDir*impulseB, pointposB);
return true;
}
}
return false;
}
bool
CPhysical::ApplyFriction(float adhesiveLimit, CColPoint &colpoint)
{
CVector speed;
float normalSpeed;
CVector vOtherSpeed;
float fOtherSpeed;
CVector frictionDir;
float fImpulse;
float impulseLimit;
if(bPedPhysics){
normalSpeed = DotProduct(m_vecMoveSpeed, colpoint.normal);
vOtherSpeed = m_vecMoveSpeed - colpoint.normal*normalSpeed;
fOtherSpeed = vOtherSpeed.Magnitude();
if(fOtherSpeed > 0.0f){
frictionDir = vOtherSpeed * (1.0f/fOtherSpeed);
// not really impulse but speed
// maybe use ApplyFrictionMoveForce instead?
fImpulse = -fOtherSpeed;
impulseLimit = adhesiveLimit*CTimer::GetTimeStep() / m_fMass;
if(fImpulse < -impulseLimit) fImpulse = -impulseLimit;
CVector vImpulse = frictionDir*fImpulse;
m_vecMoveFriction += CVector(vImpulse.x, vImpulse.y, 0.0f);
return true;
}
}else{
CVector pointpos = colpoint.point - GetPosition();
speed = GetSpeed(pointpos);
normalSpeed = DotProduct(speed, colpoint.normal);
vOtherSpeed = speed - colpoint.normal*normalSpeed;
fOtherSpeed = vOtherSpeed.Magnitude();
if(fOtherSpeed > 0.0f){
frictionDir = vOtherSpeed * (1.0f/fOtherSpeed);
fImpulse = -fOtherSpeed * m_fMass;
impulseLimit = adhesiveLimit*CTimer::GetTimeStep() * 1.5f;
if(fImpulse < -impulseLimit) fImpulse = -impulseLimit;
ApplyFrictionMoveForce(frictionDir*fImpulse);
ApplyFrictionTurnForce(frictionDir*fImpulse, pointpos);
if(fOtherSpeed > 0.1f &&
colpoint.surfaceB != SURFACE_2 && colpoint.surfaceB != SURFACE_4 &&
CSurfaceTable::GetAdhesionGroup(colpoint.surfaceA) == ADHESIVE_HARD){
CVector v = frictionDir * fOtherSpeed * 0.25f;
for(int i = 0; i < 4; i++)
CParticle::AddParticle(PARTICLE_SPARK_SMALL, colpoint.point, v);
}
return true;
}
}
return false;
}
void
CPhysical::AddCollisionRecord(CEntity *ent)
{
AddCollisionRecord_Treadable(ent);
this->bHasCollided = true;
ent->bHasCollided = true;
if(IsVehicle() && ent->IsVehicle()){
if(((CVehicle*)this)->m_nAlarmState == -1)
((CVehicle*)this)->m_nAlarmState = 15000;
if(((CVehicle*)ent)->m_nAlarmState == -1)
((CVehicle*)ent)->m_nAlarmState = 15000;
}
if(bUseCollisionRecords){
int i;
for(i = 0; i < m_nCollisionRecords; i++)
if(m_aCollisionRecords[i] == ent)
return;
if(m_nCollisionRecords < PHYSICAL_MAX_COLLISIONRECORDS)
m_aCollisionRecords[m_nCollisionRecords++] = ent;
m_nLastTimeCollided = CTimer::GetTimeInMilliseconds();
}
}
void
CPhysical::AddCollisionRecord_Treadable(CEntity *ent)
{
if(ent->IsBuilding() && ((CBuilding*)ent)->GetIsATreadable()){
CTreadable *t = (CTreadable*)ent;
if(t->m_nodeIndicesPeds[0] >= 0 ||
t->m_nodeIndicesPeds[1] >= 0 ||
t->m_nodeIndicesPeds[2] >= 0 ||
t->m_nodeIndicesPeds[3] >= 0)
m_pedTreadable = t;
if(t->m_nodeIndicesCars[0] >= 0 ||
t->m_nodeIndicesCars[1] >= 0 ||
t->m_nodeIndicesCars[2] >= 0 ||
t->m_nodeIndicesCars[3] >= 0)
m_carTreadable = t;
}
}
bool
CPhysical::GetHasCollidedWith(CEntity *ent)
{
int i;
if(bUseCollisionRecords)
for(i = 0; i < m_nCollisionRecords; i++)
if(m_aCollisionRecords[i] == ent)
return true;
return false;
}
void
CPhysical::ProcessControl(void)
{
if(!IsPed())
m_phy_flagA8 = false;
bHasContacted = false;
bIsInSafePosition = false;
bWasPostponed = false;
bHasHitWall = false;
if(m_status == STATUS_SIMPLE)
return;
m_nCollisionRecords = 0;
bHasCollided = false;
m_nCollisionPieceType = 0;
m_fCollisionImpulse = 0.0f;
m_pCollidingEntity = nil;
if(!bIsStuck){
if(IsObject() ||
IsPed() && !bPedPhysics){
m_vecMoveSpeedAvg = (m_vecMoveSpeedAvg + m_vecMoveSpeed)/2.0f;
m_vecTurnSpeedAvg = (m_vecTurnSpeedAvg + m_vecTurnSpeed)/2.0f;
float step = CTimer::GetTimeStep() * 0.003;
if(m_vecMoveSpeedAvg.MagnitudeSqr() < step*step &&
m_vecTurnSpeedAvg.MagnitudeSqr() < step*step){
m_nStaticFrames++;
if(m_nStaticFrames > 10){
m_nStaticFrames = 10;
bIsStatic = true;
m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f);
m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f);
m_vecMoveFriction = m_vecMoveSpeed;
m_vecTurnFriction = m_vecTurnSpeed;
return;
}
}else
m_nStaticFrames = 0;
}
}
ApplyGravity();
ApplyFriction();
ApplyAirResistance();
}
STARTPATCHES
InjectHook(0x4951F0, &CPhysical::Add_, PATCH_JUMP);
InjectHook(0x4954B0, &CPhysical::Remove_, PATCH_JUMP);
InjectHook(0x495540, &CPhysical::RemoveAndAdd, PATCH_JUMP);
InjectHook(0x495F10, &CPhysical::ProcessControl_, PATCH_JUMP);
InjectHook(0x4958F0, &CPhysical::AddToMovingList, PATCH_JUMP);
InjectHook(0x495940, &CPhysical::RemoveFromMovingList, PATCH_JUMP);
InjectHook(0x497180, &CPhysical::AddCollisionRecord, PATCH_JUMP);
InjectHook(0x4970C0, &CPhysical::AddCollisionRecord_Treadable, PATCH_JUMP);
InjectHook(0x497240, &CPhysical::GetHasCollidedWith, PATCH_JUMP);
#define F3 float, float, float
InjectHook(0x495B10, &CPhysical::ApplyMoveSpeed, PATCH_JUMP);
InjectHook(0x497280, &CPhysical::ApplyTurnSpeed, PATCH_JUMP);
InjectHook(0x4959A0, (void (CPhysical::*)(F3))&CPhysical::ApplyMoveForce, PATCH_JUMP);
InjectHook(0x495A10, (void (CPhysical::*)(F3, F3))&CPhysical::ApplyTurnForce, PATCH_JUMP);
InjectHook(0x495D90, (void (CPhysical::*)(F3))&CPhysical::ApplyFrictionMoveForce, PATCH_JUMP);
InjectHook(0x495E10, (void (CPhysical::*)(F3, F3))&CPhysical::ApplyFrictionTurnForce, PATCH_JUMP);
InjectHook(0x499890, &CPhysical::ApplySpringCollision, PATCH_JUMP);
InjectHook(0x495B50, &CPhysical::ApplyGravity, PATCH_JUMP);
InjectHook(0x495B80, (void (CPhysical::*)(void))&CPhysical::ApplyFriction, PATCH_JUMP);
InjectHook(0x495C20, &CPhysical::ApplyAirResistance, PATCH_JUMP);
InjectHook(0x4973A0, &CPhysical::ApplyCollision, PATCH_JUMP);
InjectHook(0x4992A0, &CPhysical::ApplyCollisionAlt, PATCH_JUMP);
InjectHook(0x499BE0, (bool (CPhysical::*)(float, CColPoint&))&CPhysical::ApplyFriction, PATCH_JUMP);
InjectHook(0x49A180, (bool (CPhysical::*)(CPhysical*, float, CColPoint&))&CPhysical::ApplyFriction, PATCH_JUMP);
ENDPATCHES

137
src/entities/Physical.h Normal file
View file

@ -0,0 +1,137 @@
#pragma once
#include "Lists.h"
#include "Entity.h"
#include "Treadable.h"
enum {
PHYSICAL_MAX_COLLISIONRECORDS = 6
};
class CPhysical : public CEntity
{
public:
// The not properly indented fields haven't been checked properly yet
int uAudioEntityId;
float unk1;
CTreadable *m_carTreadable;
CTreadable *m_pedTreadable;
uint32 m_nLastTimeCollided;
CVector m_vecMoveSpeed; // velocity
CVector m_vecTurnSpeed; // angular velocity
CVector m_vecMoveFriction;
CVector m_vecTurnFriction;
CVector m_vecMoveSpeedAvg;
CVector m_vecTurnSpeedAvg;
float m_fMass;
float m_fTurnMass; // moment of inertia
float fForceMultiplier;
float m_fAirResistance;
float m_fElasticity;
float fPercentSubmerged;
CVector m_vecCentreOfMass;
CEntryInfoList m_entryInfoList;
CPtrNode *m_movingListNode;
char field_EC;
uint8 m_nStaticFrames;
uint8 m_nCollisionRecords;
char field_EF;
CEntity *m_aCollisionRecords[PHYSICAL_MAX_COLLISIONRECORDS];
float m_fDistanceTravelled;
// damaged piece
float m_fCollisionImpulse;
CEntity *m_pCollidingEntity;
CVector m_vecCollisionDirection;
int16 m_nCollisionPieceType;
uint8 m_phy_flagA1 : 1;
uint8 bAffectedByGravity : 1;
uint8 bInfiniteMass : 1;
uint8 m_phy_flagA8 : 1;
uint8 m_phy_flagA10 : 1;
uint8 m_phy_flagA20 : 1;
uint8 m_phy_flagA40 : 1;
uint8 m_phy_flagA80 : 1;
uint8 m_phy_flagB1 : 1;
uint8 m_phy_flagB2 : 1;
uint8 m_phy_flagB4 : 1;
uint8 m_phy_flagB8 : 1;
uint8 m_phy_flagB10 : 1;
uint8 m_phy_flagB20 : 1;
uint8 m_phy_flagB40 : 1;
uint8 m_phy_flagB80 : 1;
char byteLastCollType;
char byteZoneLevel;
int16 pad;
// from CEntity
void Add(void);
void Remove(void);
CRect GetBoundRect(void);
void ProcessControl(void);
void RemoveAndAdd(void);
void AddToMovingList(void);
void RemoveFromMovingList(void);
// get speed of point p relative to entity center
CVector GetSpeed(const CVector &r);
CVector GetSpeed(void) { return GetSpeed(CVector(0.0f, 0.0f, 0.0f)); }
float GetMass(const CVector &pos, const CVector &dir) {
return 1.0f / (CrossProduct(pos, dir).MagnitudeSqr()/m_fTurnMass +
1.0f/m_fMass);
}
float GetMassTime(const CVector &pos, const CVector &dir, float t) {
return 1.0f / (CrossProduct(pos, dir).MagnitudeSqr()/(m_fTurnMass*t) +
1.0f/(m_fMass*t));
}
void UnsetIsInSafePosition(void) {
m_vecMoveSpeed *= -1.0f;
m_vecTurnSpeed *= -1.0f;
ApplyTurnSpeed();
ApplyMoveSpeed();
m_vecMoveSpeed *= -1.0f;
m_vecTurnSpeed *= -1.0f;
bIsInSafePosition = false;
}
void ApplyMoveSpeed(void);
void ApplyTurnSpeed(void);
// Force actually means Impulse here
void ApplyMoveForce(float jx, float jy, float jz);
void ApplyMoveForce(const CVector &j) { ApplyMoveForce(j.x, j.y, j.z); }
// v(x,y,z) is direction of force, p(x,y,z) is point relative to model center where force is applied
void ApplyTurnForce(float jx, float jy, float jz, float rx, float ry, float rz);
// v is direction of force, p is point relative to model center where force is applied
void ApplyTurnForce(const CVector &j, const CVector &p) { ApplyTurnForce(j.x, j.y, j.z, p.x, p.y, p.z); }
void ApplyFrictionMoveForce(float jx, float jy, float jz);
void ApplyFrictionMoveForce(const CVector &j) { ApplyFrictionMoveForce(j.x, j.y, j.z); }
void ApplyFrictionTurnForce(float jx, float jy, float jz, float rx, float ry, float rz);
void ApplyFrictionTurnForce(const CVector &j, const CVector &p) { ApplyFrictionTurnForce(j.x, j.y, j.z, p.x, p.y, p.z); }
void ApplySpringCollision(float f1, CVector &v, CVector &p, float f2, float f3);
void ApplyGravity(void);
void ApplyFriction(void);
void ApplyAirResistance(void);
bool ApplyCollision(CPhysical *B, CColPoint &colpoint, float &impulseA, float &impulseB);
bool ApplyCollisionAlt(CEntity *B, CColPoint &colpoint, float &impulse, CVector &moveSpeed, CVector &turnSpeed);
bool ApplyFriction(CPhysical *B, float adhesiveLimit, CColPoint &colpoint);
bool ApplyFriction(float adhesiveLimit, CColPoint &colpoint);
void AddCollisionRecord(CEntity *ent);
void AddCollisionRecord_Treadable(CEntity *ent);
bool GetHasCollidedWith(CEntity *ent);
// to make patching virtual functions possible
void Add_(void) { CPhysical::Add(); }
void Remove_(void) { CPhysical::Remove(); }
CRect GetBoundRect_(void) { return CPhysical::GetBoundRect(); }
void ProcessControl_(void) { CPhysical::ProcessControl(); }
};
static_assert(sizeof(CPhysical) == 0x128, "CPhysical: error");

View file

@ -0,0 +1,7 @@
#include "common.h"
#include "rpworld.h"
#include "Treadable.h"
#include "Pools.h"
void *CTreadable::operator new(size_t sz) { return CPools::GetTreadablePool()->New(); }
void CTreadable::operator delete(void *p, size_t sz) { CPools::GetTreadablePool()->Delete((CTreadable*)p); }

16
src/entities/Treadable.h Normal file
View file

@ -0,0 +1,16 @@
#pragma once
#include "Building.h"
class CTreadable : public CBuilding
{
public:
static void *operator new(size_t);
static void operator delete(void*, size_t);
int16 m_nodeIndicesCars[12];
int16 m_nodeIndicesPeds[12];
virtual bool GetIsATreadable(void) { return true; }
};
static_assert(sizeof(CTreadable) == 0x94, "CTreadable: error");

21
src/entities/Vehicle.h Normal file
View file

@ -0,0 +1,21 @@
#pragma once
#include "Physical.h"
class CPed;
class CVehicle : public CPhysical
{
public:
// 0x128
uint8 stuff1[120];
int16 m_nAlarmState;
CPed *pDriver;
CPed *pPassengers[8];
uint8 stuff2[24];
CEntity *m_pCurSurface;
uint8 stuff3[160];
int32 m_vehType;
};
static_assert(sizeof(CVehicle) == 0x288, "CVehicle: error");
static_assert(offsetof(CVehicle, m_pCurSurface) == 0x1E0, "CVehicle: error");

98
src/main.cpp Normal file
View file

@ -0,0 +1,98 @@
#include "common.h"
#include <Windows.h>
#include "patcher.h"
#include "Renderer.h"
#include "debugmenu_public.h"
void **rwengine = *(void***)0x5A10E1;
RsGlobalType &RsGlobal = *(RsGlobalType*)0x8F4360;
GlobalScene &Scene = *(GlobalScene*)0x726768;
DebugMenuAPI gDebugMenuAPI;
WRAPPER void *gtanew(uint32 sz) { EAXJMP(0x5A0690); }
WRAPPER void gtadelete(void *p) { EAXJMP(0x5A07E0); }
// overload our own new/delete with GTA's functions
void *operator new(size_t sz) { return gtanew(sz); }
void operator delete(void *ptr) noexcept { gtadelete(ptr); }
// Use our own implementation of rand, stolen from PS2
unsigned __int64 myrand_seed = 1;
int
myrand(void)
{
myrand_seed = 0x5851F42D4C957F2D * myrand_seed + 1;
return ((myrand_seed >> 32) & 0x7FFFFFFF);
}
void
mysrand(unsigned int seed)
{
myrand_seed = seed;
}
int (*open_script_orig)(const char *path, const char *mode);
int
open_script(const char *path, const char *mode)
{
if(GetAsyncKeyState('D') & 0x8000)
return open_script_orig("main_d.scm", mode);
// if(GetAsyncKeyState('R') & 0x8000)
return open_script_orig("main_freeroam.scm", mode);
return open_script_orig(path, mode);
}
int (*RsEventHandler_orig)(int a, int b);
int
delayedPatches10(int a, int b)
{
if(DebugMenuLoad()){
DebugMenuAddVarBool8("Debug", "Show Ped Road Groups", (int8*)&gbShowPedRoadGroups, nil);
DebugMenuAddVarBool8("Debug", "Show Car Road Groups", (int8*)&gbShowCarRoadGroups, nil);
DebugMenuAddVarBool8("Debug", "Show Collision Polys", (int8*)&gbShowCollisionPolys, nil);
DebugMenuAddVarBool8("Debug", "Don't render Buildings", (int8*)&gbDontRenderBuildings, nil);
DebugMenuAddVarBool8("Debug", "Don't render Big Buildings", (int8*)&gbDontRenderBigBuildings, nil);
DebugMenuAddVarBool8("Debug", "Don't render Peds", (int8*)&gbDontRenderPeds, nil);
DebugMenuAddVarBool8("Debug", "Don't render Objects", (int8*)&gbDontRenderObjects, nil);
}
return RsEventHandler_orig(a, b);
}
void
patch()
{
StaticPatcher::Apply();
Patch<float>(0x46BC61+6, 1.0f); // car distance
InjectHook(0x59E460, printf, PATCH_JUMP);
InterceptCall(&open_script_orig, open_script, 0x438869);
InterceptCall(&RsEventHandler_orig, delayedPatches10, 0x58275E);
}
BOOL WINAPI
DllMain(HINSTANCE hInst, DWORD reason, LPVOID)
{
if(reason == DLL_PROCESS_ATTACH){
AllocConsole();
freopen("CONIN$", "r", stdin);
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
if (*(DWORD*)0x5C1E75 == 0xB85548EC) // 1.0
patch();
else
return FALSE;
}
return TRUE;
}

245
src/math/Matrix.h Normal file
View file

@ -0,0 +1,245 @@
#pragma once
class CMatrix
{
public:
RwMatrix m_matrix;
RwMatrix *m_attachment;
bool m_hasRwMatrix; // are we the owner?
CMatrix(void){
m_attachment = nil;
m_hasRwMatrix = false;
}
CMatrix(CMatrix const &m){
m_attachment = nil;
m_hasRwMatrix = false;
*this = m;
}
CMatrix(RwMatrix *matrix, bool attach){
m_attachment = nil;
Attach(matrix, attach);
}
~CMatrix(void){
if(m_hasRwMatrix && m_attachment)
RwMatrixDestroy(m_attachment);
}
void Attach(RwMatrix *matrix, bool attach){
if(m_hasRwMatrix && m_attachment)
RwMatrixDestroy(m_attachment);
m_attachment = matrix;
m_hasRwMatrix = attach;
Update();
}
void AttachRW(RwMatrix *matrix, bool attach){
if(m_hasRwMatrix && m_attachment)
RwMatrixDestroy(m_attachment);
m_attachment = matrix;
m_hasRwMatrix = attach;
UpdateRW();
}
void Detach(void){
if(m_hasRwMatrix && m_attachment)
RwMatrixDestroy(m_attachment);
m_attachment = nil;
}
void Update(void){
m_matrix = *m_attachment;
}
void UpdateRW(void){
if(m_attachment){
*m_attachment = m_matrix;
RwMatrixUpdate(m_attachment);
}
}
void operator=(CMatrix const &rhs){
m_matrix = rhs.m_matrix;
if(m_attachment)
UpdateRW();
}
CVector *GetPosition(void){ return (CVector*)&m_matrix.pos; }
CVector *GetRight(void) { return (CVector*)&m_matrix.right; }
CVector *GetForward(void) { return (CVector*)&m_matrix.up; }
CVector *GetUp(void) { return (CVector*)&m_matrix.at; }
void SetScale(float s){
m_matrix.right.x = s;
m_matrix.right.y = 0.0f;
m_matrix.right.z = 0.0f;
m_matrix.up.x = 0.0f;
m_matrix.up.y = s;
m_matrix.up.z = 0.0f;
m_matrix.at.x = 0.0f;
m_matrix.at.y = 0.0f;
m_matrix.at.z = s;
m_matrix.pos.x = 0.0f;
m_matrix.pos.y = 0.0f;
m_matrix.pos.z = 0.0f;
}
void SetRotateXOnly(float angle){
float c = cos(angle);
float s = sin(angle);
m_matrix.right.x = 1.0f;
m_matrix.right.y = 0.0f;
m_matrix.right.z = 0.0f;
m_matrix.up.x = 0.0f;
m_matrix.up.y = c;
m_matrix.up.z = s;
m_matrix.at.x = 0.0f;
m_matrix.at.y = -s;
m_matrix.at.z = c;
}
void SetRotateX(float angle){
SetRotateXOnly(angle);
m_matrix.pos.x = 0.0f;
m_matrix.pos.y = 0.0f;
m_matrix.pos.z = 0.0f;
}
void SetRotateYOnly(float angle){
float c = cos(angle);
float s = sin(angle);
m_matrix.right.x = c;
m_matrix.right.y = 0.0f;
m_matrix.right.z = -s;
m_matrix.up.x = 0.0f;
m_matrix.up.y = 1.0f;
m_matrix.up.z = 0.0f;
m_matrix.at.x = s;
m_matrix.at.y = 0.0f;
m_matrix.at.z = c;
}
void SetRotateY(float angle){
SetRotateYOnly(angle);
m_matrix.pos.x = 0.0f;
m_matrix.pos.y = 0.0f;
m_matrix.pos.z = 0.0f;
}
void SetRotateZOnly(float angle){
float c = cos(angle);
float s = sin(angle);
m_matrix.right.x = c;
m_matrix.right.y = s;
m_matrix.right.z = 0.0f;
m_matrix.up.x = -s;
m_matrix.up.y = c;
m_matrix.up.z = 0.0f;
m_matrix.at.x = 0.0f;
m_matrix.at.y = 0.0f;
m_matrix.at.z = 1.0f;
}
void SetRotateZ(float angle){
SetRotateZOnly(angle);
m_matrix.pos.x = 0.0f;
m_matrix.pos.y = 0.0f;
m_matrix.pos.z = 0.0f;
}
void Reorthogonalise(void){
CVector &r = *GetRight();
CVector &f = *GetForward();
CVector &u = *GetUp();
u = CrossProduct(r, f);
u.Normalise();
r = CrossProduct(f, u);
r.Normalise();
f = CrossProduct(u, r);
}
};
inline CMatrix&
Invert(const CMatrix &src, CMatrix &dst)
{
// GTA handles this as a raw 4x4 orthonormal matrix
// and trashes the RW flags, let's not do that
// actual copy of librw code:
RwMatrix *d = &dst.m_matrix;
const RwMatrix *s = &src.m_matrix;
d->right.x = s->right.x;
d->right.y = s->up.x;
d->right.z = s->at.x;
d->up.x = s->right.y;
d->up.y = s->up.y;
d->up.z = s->at.y;
d->at.x = s->right.z;
d->at.y = s->up.z;
d->at.z = s->at.z;
d->pos.x = -(s->pos.x*s->right.x +
s->pos.y*s->right.y +
s->pos.z*s->right.z);
d->pos.y = -(s->pos.x*s->up.x +
s->pos.y*s->up.y +
s->pos.z*s->up.z);
d->pos.z = -(s->pos.x*s->at.x +
s->pos.y*s->at.y +
s->pos.z*s->at.z);
d->flags = rwMATRIXTYPEORTHONORMAL;
return dst;
}
inline CMatrix
Invert(const CMatrix &matrix)
{
CMatrix inv;
return Invert(matrix, inv);
}
inline CVector
operator*(const CMatrix &mat, const CVector &vec)
{
return CVector(
mat.m_matrix.right.x * vec.x + mat.m_matrix.up.x * vec.y + mat.m_matrix.at.x * vec.z + mat.m_matrix.pos.x,
mat.m_matrix.right.y * vec.x + mat.m_matrix.up.y * vec.y + mat.m_matrix.at.y * vec.z + mat.m_matrix.pos.y,
mat.m_matrix.right.z * vec.x + mat.m_matrix.up.z * vec.y + mat.m_matrix.at.z * vec.z + mat.m_matrix.pos.z);
}
inline CMatrix
operator*(const CMatrix &m1, const CMatrix &m2)
{
CMatrix out;
RwMatrix *dst = &out.m_matrix;
const RwMatrix *src1 = &m1.m_matrix;
const RwMatrix *src2 = &m2.m_matrix;
dst->right.x = src1->right.x*src2->right.x + src1->up.x*src2->right.y + src1->at.x*src2->right.z;
dst->right.y = src1->right.y*src2->right.x + src1->up.y*src2->right.y + src1->at.y*src2->right.z;
dst->right.z = src1->right.z*src2->right.x + src1->up.z*src2->right.y + src1->at.z*src2->right.z;
dst->up.x = src1->right.x*src2->up.x + src1->up.x*src2->up.y + src1->at.x*src2->up.z;
dst->up.y = src1->right.y*src2->up.x + src1->up.y*src2->up.y + src1->at.y*src2->up.z;
dst->up.z = src1->right.z*src2->up.x + src1->up.z*src2->up.y + src1->at.z*src2->up.z;
dst->at.x = src1->right.x*src2->at.x + src1->up.x*src2->at.y + src1->at.x*src2->at.z;
dst->at.y = src1->right.y*src2->at.x + src1->up.y*src2->at.y + src1->at.y*src2->at.z;
dst->at.z = src1->right.z*src2->at.x + src1->up.z*src2->at.y + src1->at.z*src2->at.z;
dst->pos.x = src1->right.x*src2->pos.x + src1->up.x*src2->pos.y + src1->at.x*src2->pos.z + src1->pos.x;
dst->pos.y = src1->right.y*src2->pos.x + src1->up.y*src2->pos.y + src1->at.y*src2->pos.z + src1->pos.y;
dst->pos.z = src1->right.z*src2->pos.x + src1->up.z*src2->pos.y + src1->at.z*src2->pos.z + src1->pos.z;
return out;
}
inline CVector
MultiplyInverse(const CMatrix &mat, const CVector &vec)
{
CVector v(vec.x - mat.m_matrix.pos.x, vec.y - mat.m_matrix.pos.y, vec.z - mat.m_matrix.pos.z);
return CVector(
mat.m_matrix.right.x * v.x + mat.m_matrix.right.y * v.y + mat.m_matrix.right.z * v.z,
mat.m_matrix.up.x * v.x + mat.m_matrix.up.y * v.y + mat.m_matrix.up.z * v.z,
mat.m_matrix.at.x * v.x + mat.m_matrix.at.y * v.y + mat.m_matrix.at.z * v.z);
}
inline CVector
Multiply3x3(const CMatrix &mat, const CVector &vec)
{
return CVector(
mat.m_matrix.right.x * vec.x + mat.m_matrix.up.x * vec.y + mat.m_matrix.at.x * vec.z,
mat.m_matrix.right.y * vec.x + mat.m_matrix.up.y * vec.y + mat.m_matrix.at.y * vec.z,
mat.m_matrix.right.z * vec.x + mat.m_matrix.up.z * vec.y + mat.m_matrix.at.z * vec.z);
}

31
src/math/Rect.h Normal file
View file

@ -0,0 +1,31 @@
#pragma once
#pragma once
class CRect
{
public:
float left; // x min
float top; // y max
float right; // x max
float bottom; // y min
CRect(void){
left = 1000000.0f;
bottom = 1000000.0f;
right = -1000000.0f;
top = -1000000.0f;
}
CRect(float l, float b, float r, float t){
left = l;
bottom = b;
right = r;
top = t;
}
void ContainPoint(CVector const &v){
if(v.x < left) left = v.x;
if(v.x > right) right = v.x;
if(v.y < bottom) bottom = v.y;
if(v.y > top) top = v.y;
}
};

82
src/math/Vector.h Normal file
View file

@ -0,0 +1,82 @@
#pragma once
class CVector
{
public:
float x, y, z;
CVector(void) {}
CVector(float x, float y, float z) : x(x), y(y), z(z) {}
// CVector(rw::V3d const &v) : x(v.x), y(v.y), z(v.z) {}
float Magnitude(void) const { return sqrt(x*x + y*y + z*z); }
float MagnitudeSqr(void) const { return x*x + y*y + z*z; }
float Magnitude2D(void) const { return sqrt(x*x + y*y); }
void Normalise(void){
float sq = MagnitudeSqr();
if(sq > 0.0f){
float invsqrt = 1.0f/sqrt(sq);
x *= invsqrt;
y *= invsqrt;
z *= invsqrt;
}else
x = 1.0f;
}
// rw::V3d ToRW(void){
// return rw::makeV3d(x, y, z);
// }
// void operator=(rw::V3d const &rhs){
// x = rhs.x;
// y = rhs.y;
// z = rhs.z;
// }
CVector operator-(const CVector &rhs) const {
return CVector(x-rhs.x, y-rhs.y, z-rhs.z);
}
CVector operator+(const CVector &rhs) const {
return CVector(x+rhs.x, y+rhs.y, z+rhs.z);
}
CVector operator*(float t) const {
return CVector(x*t, y*t, z*t);
}
CVector operator/(float t) const {
return CVector(x/t, y/t, z/t);
}
CVector &operator-=(const CVector &rhs) {
this->x -= rhs.x;
this->y -= rhs.y;
this->z -= rhs.z;
return *this;
}
CVector &operator+=(const CVector &rhs) {
this->x += rhs.x;
this->y += rhs.y;
this->z += rhs.z;
return *this;
}
CVector &operator*=(float t) {
this->x *= t;
this->y *= t;
this->z *= t;
return *this;
}
CVector &operator/=(float t) {
this->x /= t;
this->y /= t;
this->z /= t;
return *this;
}
};
inline float
DotProduct(const CVector &v1, const CVector &v2)
{
return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
}
inline CVector
CrossProduct(const CVector &v1, const CVector &v2)
{
return CVector(
v1.y*v2.z - v1.z*v2.y,
v1.z*v2.x - v1.x*v2.z,
v1.x*v2.y - v1.y*v2.x);
}

37
src/math/Vector2D.h Normal file
View file

@ -0,0 +1,37 @@
#pragma once
class CVector2D
{
public:
float x, y;
CVector2D(void) {}
CVector2D(float x, float y) : x(x), y(y) {}
CVector2D(const CVector &v) : x(v.x), y(v.y) {}
float Magnitude(void) const { return sqrt(x*x + y*y); }
float MagnitudeSqr(void) const { return x*x + y*y; }
void Normalise(void){
float sq = MagnitudeSqr();
if(sq > 0.0f){
float invsqrt = 1.0f/sqrt(sq);
x *= invsqrt;
y *= invsqrt;
}else
x = 0.0f;
}
CVector2D operator-(const CVector2D &rhs) const {
return CVector2D(x-rhs.x, y-rhs.y);
}
CVector2D operator+(const CVector2D &rhs) const {
return CVector2D(x+rhs.x, y+rhs.y);
}
CVector2D operator*(float t) const {
return CVector2D(x*t, y*t);
}
};
inline float
CrossProduct2D(const CVector2D &v1, const CVector2D &v2)
{
return v1.x*v2.y - v1.y*v2.x;
}

View file

@ -0,0 +1,117 @@
#include "common.h"
#include "patcher.h"
#include "templates.h"
#include "TxdStore.h"
#include "2dEffect.h"
#include "BaseModelInfo.h"
CBaseModelInfo::CBaseModelInfo(ModeInfoType type)
{
m_colModel = nil;
m_twodEffects = 0;
m_objectId = -1;
m_refCount = 0;
m_txdSlot = -1;
m_type = type;
m_num2dEffects = 0;
m_freeCol = false;
}
void
CBaseModelInfo::Shutdown(void)
{
DeleteCollisionModel();
DeleteRwObject();
m_twodEffects = 0;
m_num2dEffects = 0;
m_txdSlot = -1;
}
void
CBaseModelInfo::DeleteCollisionModel(void)
{
if(m_colModel && m_freeCol){
if(m_colModel)
delete m_colModel;
m_colModel = nil;
}
}
void
CBaseModelInfo::AddRef(void)
{
m_refCount++;
AddTexDictionaryRef();
}
void
CBaseModelInfo::RemoveRef(void)
{
m_refCount--;
RemoveTexDictionaryRef();
}
void
CBaseModelInfo::SetTexDictionary(const char *name)
{
int slot = CTxdStore::FindTxdSlot(name);
if(slot < 0)
slot = CTxdStore::AddTxdSlot(name);
m_txdSlot = slot;
}
void
CBaseModelInfo::AddTexDictionaryRef(void)
{
CTxdStore::AddRef(m_txdSlot);
}
void
CBaseModelInfo::RemoveTexDictionaryRef(void)
{
CTxdStore::RemoveRef(m_txdSlot);
}
void
CBaseModelInfo::Init2dEffects(void)
{
m_twodEffects = nil;
m_num2dEffects = 0;
}
void
CBaseModelInfo::Add2dEffect(C2dEffect *fx)
{
if(m_twodEffects)
m_num2dEffects++;
else{
m_twodEffects = fx;
m_num2dEffects = 1;
}
}
C2dEffect*
CBaseModelInfo::Get2dEffect(int n)
{
if(m_twodEffects)
return &m_twodEffects[n];
else
return nil;
}
STARTPATCHES
// can't easily replace ctor at 4F6A50
InjectHook(0x4F6A90, &CBaseModelInfo::Shutdown_, PATCH_JUMP);
InjectHook(0x4F6AC0, &CBaseModelInfo::DeleteCollisionModel, PATCH_JUMP);
InjectHook(0x4F6B70, &CBaseModelInfo::ClearTexDictionary, PATCH_JUMP);
InjectHook(0x4F6BA0, &CBaseModelInfo::AddRef, PATCH_JUMP);
InjectHook(0x4F6BB0, &CBaseModelInfo::RemoveRef, PATCH_JUMP);
InjectHook(0x4F6B40, &CBaseModelInfo::SetTexDictionary, PATCH_JUMP);
InjectHook(0x4F6B80, &CBaseModelInfo::AddTexDictionaryRef, PATCH_JUMP);
InjectHook(0x4F6B90, &CBaseModelInfo::RemoveTexDictionaryRef, PATCH_JUMP);
InjectHook(0x4F6B20, &CBaseModelInfo::Add2dEffect, PATCH_JUMP);
InjectHook(0x4F6AF0, &CBaseModelInfo::Init2dEffects, PATCH_JUMP);
InjectHook(0x4F6B00, &CBaseModelInfo::Get2dEffect, PATCH_JUMP);
ENDPATCHES

View file

@ -0,0 +1,66 @@
#pragma once
#include "Collision.h"
enum ModeInfoType : uint8
{
MITYPE_NA = 0,
MITYPE_SIMPLE = 1,
MITYPE_MLO = 2,
MITYPE_TIME = 3,
MITYPE_CLUMP = 4,
MITYPE_VEHICLE = 5,
MITYPE_PED = 6,
MITYPE_XTRACOMPS = 7,
};
static_assert(sizeof(ModeInfoType) == 1, "ModeInfoType: error");
class C2dEffect;
class CBaseModelInfo
{
protected:
// TODO?: make more things protected
char m_name[24];
CColModel *m_colModel;
C2dEffect *m_twodEffects;
int16 m_objectId;
public:
uint16 m_refCount;
int16 m_txdSlot;
ModeInfoType m_type;
uint8 m_num2dEffects;
bool m_freeCol;
CBaseModelInfo(ModeInfoType type);
virtual ~CBaseModelInfo() {}
virtual void Shutdown(void);
virtual void DeleteRwObject(void) = 0;
virtual RwObject *CreateInstance(RwMatrix *) = 0;
virtual RwObject *CreateInstance(void) = 0;
virtual RwObject *GetRwObject(void) = 0;
bool IsSimple(void) { return m_type == MITYPE_SIMPLE || m_type == MITYPE_TIME; }
char *GetName(void) { return m_name; }
void SetName(const char *name) { strncpy(m_name, name, 24); }
void SetColModel(CColModel *col, bool free = false){
m_colModel = col; m_freeCol = free; }
CColModel *GetColModel(void) { return m_colModel; }
void DeleteCollisionModel(void);
void ClearTexDictionary(void) { m_txdSlot = -1; }
short GetObjectID(void) { return m_objectId; }
void SetObjectID(short id) { m_objectId = id; }
short GetTxdSlot(void) { return m_txdSlot; }
void AddRef(void);
void RemoveRef(void);
void SetTexDictionary(const char *name);
void AddTexDictionaryRef(void);
void RemoveTexDictionaryRef(void);
void Init2dEffects(void);
void Add2dEffect(C2dEffect *fx);
C2dEffect *Get2dEffect(int n);
void Shutdown_(void) { this->CBaseModelInfo::Shutdown(); }
};
static_assert(sizeof(CBaseModelInfo) == 0x30, "CBaseModelInfo: error");

View file

@ -0,0 +1,156 @@
#include "common.h"
#include "patcher.h"
#include "NodeName.h"
#include "VisibilityPlugins.h"
#include "ModelInfo.h"
void
CClumpModelInfo::DeleteRwObject(void)
{
if(m_clump){
RpClumpDestroy(m_clump);
m_clump = nil;
RemoveTexDictionaryRef();
}
}
RwObject*
CClumpModelInfo::CreateInstance(void)
{
if(m_clump)
return (RwObject*)RpClumpClone(m_clump);
return nil;
}
RwObject*
CClumpModelInfo::CreateInstance(RwMatrix *m)
{
if(m_clump){
RpClump *clump = (RpClump*)CreateInstance();
*RwFrameGetMatrix(RpClumpGetFrame(clump)) = *m;
return (RwObject*)clump;
}
return nil;
}
RpAtomic*
CClumpModelInfo::SetAtomicRendererCB(RpAtomic *atomic, void *data)
{
CVisibilityPlugins::SetAtomicRenderCallback(atomic, (RpAtomicCallBackRender)data);
return atomic;
}
void
CClumpModelInfo::SetClump(RpClump *clump)
{
m_clump = clump;
CVisibilityPlugins::SetClumpModelInfo(m_clump, this);
AddTexDictionaryRef();
RpClumpForAllAtomics(clump, SetAtomicRendererCB, nil);
if(strncmp(GetName(), "playerh", 8) == 0)
RpClumpForAllAtomics(clump, SetAtomicRendererCB, CVisibilityPlugins::RenderPlayerCB);
}
void
CClumpModelInfo::SetFrameIds(RwObjectNameIdAssocation *assocs)
{
int32 i;
RwObjectNameAssociation objname;
for(i = 0; assocs[i].name; i++)
if((assocs[i].flags & CLUMP_FLAG_NO_HIERID) == 0){
objname.frame = nil;
objname.name = assocs[i].name;
RwFrameForAllChildren(RpClumpGetFrame(m_clump), FindFrameFromNameWithoutIdCB, &objname);
if(objname.frame)
CVisibilityPlugins::SetFrameHierarchyId(objname.frame, assocs[i].hierId);
}
}
RwFrame*
CClumpModelInfo::FindFrameFromIdCB(RwFrame *frame, void *data)
{
RwObjectIdAssociation *assoc = (RwObjectIdAssociation*)data;
if(CVisibilityPlugins::GetFrameHierarchyId(frame) != assoc->id){
RwFrameForAllChildren(frame, FindFrameFromIdCB, assoc);
return assoc->frame ? nil : frame;
}else{
assoc->frame = frame;
return nil;
}
}
RwFrame*
CClumpModelInfo::FindFrameFromNameCB(RwFrame *frame, void *data)
{
RwObjectNameAssociation *assoc = (RwObjectNameAssociation*)data;
if(_strcmpi(GetFrameNodeName(frame), assoc->name) != 0){
RwFrameForAllChildren(frame, FindFrameFromNameCB, assoc);
return assoc->frame ? nil : frame;
}else{
assoc->frame = frame;
return nil;
}
}
RwFrame*
CClumpModelInfo::FindFrameFromNameWithoutIdCB(RwFrame *frame, void *data)
{
RwObjectNameAssociation *assoc = (RwObjectNameAssociation*)data;
if(CVisibilityPlugins::GetFrameHierarchyId(frame) ||
_strcmpi(GetFrameNodeName(frame), assoc->name) != 0){
RwFrameForAllChildren(frame, FindFrameFromNameWithoutIdCB, assoc);
return assoc->frame ? nil : frame;
}else{
assoc->frame = frame;
return nil;
}
}
RwFrame*
CClumpModelInfo::FillFrameArrayCB(RwFrame *frame, void *data)
{
int32 id;
RwFrame **frames = (RwFrame**)data;
id = CVisibilityPlugins::GetFrameHierarchyId(frame);
if(id > 0)
frames[id] = frame;
RwFrameForAllChildren(frame, FillFrameArrayCB, data);
return frame;
}
void
CClumpModelInfo::FillFrameArray(RpClump *clump, RwFrame **frames)
{
RwFrameForAllChildren(RpClumpGetFrame(clump), FillFrameArrayCB, frames);
}
RwFrame*
CClumpModelInfo::GetFrameFromId(RpClump *clump, int32 id)
{
RwObjectIdAssociation assoc;
assoc.id = id;
assoc.frame = nil;
RwFrameForAllChildren(RpClumpGetFrame(clump), FindFrameFromIdCB, &assoc);
return assoc.frame;
}
STARTPATCHES
InjectHook(0x4F8800, &CClumpModelInfo::DeleteRwObject_, PATCH_JUMP);
InjectHook(0x4F8920, &CClumpModelInfo::CreateInstance_1, PATCH_JUMP);
InjectHook(0x4F88A0, &CClumpModelInfo::CreateInstance_2, PATCH_JUMP);
InjectHook(0x50C1C0, &CClumpModelInfo::GetRwObject_, PATCH_JUMP);
InjectHook(0x4F8830, &CClumpModelInfo::SetClump_, PATCH_JUMP);
InjectHook(0x4F8940, &CClumpModelInfo::SetAtomicRendererCB, PATCH_JUMP);
InjectHook(0x4F8960, &CClumpModelInfo::FindFrameFromNameCB, PATCH_JUMP);
InjectHook(0x4F8A10, &CClumpModelInfo::FindFrameFromNameWithoutIdCB, PATCH_JUMP);
InjectHook(0x4F8AD0, &CClumpModelInfo::FindFrameFromIdCB, PATCH_JUMP);
InjectHook(0x4F8BB0, &CClumpModelInfo::SetFrameIds, PATCH_JUMP);
InjectHook(0x4F8B20, &CClumpModelInfo::FillFrameArrayCB, PATCH_JUMP);
InjectHook(0x4F8B90, &CClumpModelInfo::FillFrameArray, PATCH_JUMP);
InjectHook(0x4F8B50, &CClumpModelInfo::GetFrameFromId, PATCH_JUMP);
ENDPATCHES

View file

@ -0,0 +1,60 @@
#pragma once
#include "BaseModelInfo.h"
struct RwObjectNameIdAssocation
{
char *name;
int32 hierId;
uint32 flags;
};
struct RwObjectNameAssociation
{
char *name;
RwFrame *frame;
};
struct RwObjectIdAssociation
{
int32 id;
RwFrame *frame;
};
enum {
CLUMP_FLAG_NO_HIERID = 0x1,
};
class CClumpModelInfo : public CBaseModelInfo
{
public:
RpClump *m_clump;
CClumpModelInfo(void) : CBaseModelInfo(MITYPE_CLUMP) {}
CClumpModelInfo(ModeInfoType id) : CBaseModelInfo(id) {}
~CClumpModelInfo() {}
void DeleteRwObject(void);
RwObject *CreateInstance(void);
RwObject *CreateInstance(RwMatrix *);
RwObject *GetRwObject(void) { return (RwObject*)m_clump; }
virtual void SetClump(RpClump *);
static RpAtomic *SetAtomicRendererCB(RpAtomic *atomic, void *data);
void SetFrameIds(RwObjectNameIdAssocation *assocs);
static RwFrame *FindFrameFromNameCB(RwFrame *frame, void *data);
static RwFrame *FindFrameFromNameWithoutIdCB(RwFrame *frame, void *data);
static RwFrame *FindFrameFromIdCB(RwFrame *frame, void *data);
static void FillFrameArray(RpClump *clump, RwFrame **frames);
static RwFrame *FillFrameArrayCB(RwFrame *frame, void *data);
static RwFrame *GetFrameFromId(RpClump *clump, int32 id);
void DeleteRwObject_(void) { this->CClumpModelInfo::DeleteRwObject(); }
RwObject *CreateInstance_1(void) { return this->CClumpModelInfo::CreateInstance(); }
RwObject *CreateInstance_2(RwMatrix *m) { return this->CClumpModelInfo::CreateInstance(m); }
RwObject *GetRwObject_(void) { return this->CClumpModelInfo::GetRwObject(); }
void SetClump_(RpClump *clump) { this->CClumpModelInfo::SetClump(clump); }
};
static_assert(sizeof(CClumpModelInfo) == 0x34, "CClumpModelInfo: error");

View file

@ -0,0 +1,32 @@
#include "common.h"
#include "patcher.h"
#include "ModelIndices.h"
#define X(name, var, addr) int16 &var = *(int16*)addr;
MODELINDICES
#undef X
void
InitModelIndices(void)
{
#define X(name, var, addr) var = -1;
MODELINDICES
#undef X
}
void
MatchModelString(const char *modelname, int16 id)
{
#define X(name, var, addr) \
if(strcmp(name, modelname) == 0){ \
var = id; \
return; \
}
MODELINDICES
#undef X
}
STARTPATCHES
InjectHook(0x48EB60, InitModelIndices, PATCH_JUMP);
InjectHook(0x48F030, MatchModelString, PATCH_JUMP);
ENDPATCHES

View file

@ -0,0 +1,224 @@
#define MODELINDICES \
X("fire_hydrant", MI_FIRE_HYDRANT, 0x5F5A00) \
X("bagelstnd02", MI_BAGELSTAND2, 0x5F59FC) \
X("fish01", MI_FISHSTALL01, 0x5F59EC) \
X("fishstall02", MI_FISHSTALL02, 0x5F59F0) \
X("fishstall03", MI_FISHSTALL03, 0x5F59F4) \
X("fishstall04", MI_FISHSTALL04, 0x5F59F8) \
X("taxisign", MI_TAXISIGN, 0x5F59E8) \
X("phonesign", MI_PHONESIGN, 0x5F59E4) \
X("noparkingsign1", MI_NOPARKINGSIGN1, 0x5F59E0) \
X("bussign1", MI_BUSSIGN1, 0x5F59DC) \
X("roadworkbarrier1", MI_ROADWORKBARRIER1, 0x5F59D8) \
X("dump1", MI_DUMP1, 0x5F59D4) \
X("trafficcone", MI_TRAFFICCONE, 0x5F59D0) \
X("newsstand1", MI_NEWSSTAND, 0x5F59CC) \
X("postbox1", MI_POSTBOX1, 0x5F59C8) \
X("bin1", MI_BIN, 0x5F59C4) \
X("wastebin", MI_WASTEBIN, 0x5F59C0) \
X("phonebooth1", MI_PHONEBOOTH1, 0x5F59BC) \
X("parkingmeter", MI_PARKINGMETER, 0x5F59B8) \
X("trafficlight1", MI_TRAFFICLIGHTS, 0x5F5958) \
X("lamppost1", MI_SINGLESTREETLIGHTS1, 0x5F595C) \
X("lamppost2", MI_SINGLESTREETLIGHTS2, 0x5F5960) \
X("lamppost3", MI_SINGLESTREETLIGHTS3, 0x5F5964) \
X("doublestreetlght1", MI_DOUBLESTREETLIGHTS, 0x5F5968) \
X("rd_Road2A10", MI_ROADSFORROADBLOCKSSTART, 0x5F596C) \
X("rd_Road1A30", MI_ROADSFORROADBLOCKSEND, 0x5F5970) \
X("veg_tree1", MI_TREE1, 0x5F5974) \
X("veg_tree3", MI_TREE2, 0x5F5978) \
X("veg_treea1", MI_TREE3, 0x5F597C) \
X("veg_treenew01", MI_TREE4, 0x5F5980) \
X("veg_treenew05", MI_TREE5, 0x5F5984) \
X("veg_treeb1", MI_TREE6, 0x5F5988) \
X("veg_treenew10", MI_TREE7, 0x5F598C) \
X("veg_treea3", MI_TREE8, 0x5F5990) \
X("veg_treenew09", MI_TREE9, 0x5F5994) \
X("veg_treenew08", MI_TREE10, 0x5F5998) \
X("veg_treenew03", MI_TREE11, 0x5F599C) \
X("veg_treenew16", MI_TREE12, 0x5F59A0) \
X("veg_treenew17", MI_TREE13, 0x5F59A4) \
X("veg_treenew06", MI_TREE14, 0x5F59A8) \
X("doc_crane_cab", MODELID_CRANE_1, 0x5F59AC) \
X("cranetopb", MODELID_CRANE_2, 0x5F59B0) \
X("cranetopa", MODELID_CRANE_3, 0x5F59B4) \
X("package1", MI_COLLECTABLE1, 0x5F5A04) \
X("Money", MI_MONEY, 0x5F5A08) \
X("barrel1", MI_CARMINE, 0x5F5A0C) \
X("oddjgaragdoor", MI_GARAGEDOOR1, 0x5F5A10) \
X("bombdoor", MI_GARAGEDOOR2, 0x5F5A14) \
X("door_bombshop", MI_GARAGEDOOR3, 0x5F5A18) \
X("vheistlocdoor", MI_GARAGEDOOR4, 0x5F5A1C) \
X("door2_garage", MI_GARAGEDOOR5, 0x5F5A20) \
X("ind_slidedoor", MI_GARAGEDOOR6, 0x5F5A24) \
X("bankjobdoor", MI_GARAGEDOOR7, 0x5F5A28) \
X("door_jmsgrage", MI_GARAGEDOOR9, 0x5F5A2C) \
X("jamesgrge_kb", MI_GARAGEDOOR10, 0x5F5A30) \
X("door_sfehousegrge", MI_GARAGEDOOR11, 0x5F5A34) \
X("shedgaragedoor", MI_GARAGEDOOR12, 0x5F5A38) \
X("door4_garage", MI_GARAGEDOOR13, 0x5F5A3C) \
X("door_col_compnd_01", MI_GARAGEDOOR14, 0x5F5A40) \
X("door_col_compnd_02", MI_GARAGEDOOR15, 0x5F5A44) \
X("door_col_compnd_03", MI_GARAGEDOOR16, 0x5F5A48) \
X("door_col_compnd_04", MI_GARAGEDOOR17, 0x5F5A4C) \
X("door_col_compnd_05", MI_GARAGEDOOR18, 0x5F5A50) \
X("impex_door", MI_GARAGEDOOR19, 0x5F5A54) \
X("SalvGarage", MI_GARAGEDOOR20, 0x5F5A58) \
X("door3_garage", MI_GARAGEDOOR21, 0x5F5A5C) \
X("leveldoor2", MI_GARAGEDOOR22, 0x5F5A60) \
X("double_garage_dr", MI_GARAGEDOOR23, 0x5F5A64) \
X("amcogaragedoor", MI_GARAGEDOOR24, 0x5F5A68) \
X("towergaragedoor1", MI_GARAGEDOOR25, 0x5F5A6C) \
X("towergaragedoor2", MI_GARAGEDOOR26, 0x5F5A70) \
X("towergaragedoor3", MI_GARAGEDOOR27, 0x5F5A74) \
X("plysve_gragedoor", MI_GARAGEDOOR28, 0x5F5A78) \
X("impexpsubgrgdoor", MI_GARAGEDOOR29, 0x5F5A7C) \
X("Sub_sprayshopdoor", MI_GARAGEDOOR30, 0x5F5A80) \
X("ind_plyrwoor", MI_GARAGEDOOR31, 0x5F5A84) \
X("8ballsuburbandoor", MI_GARAGEDOOR32, 0x5F5A88) \
X("barrel2", MI_NAUTICALMINE, 0x5F5A8C) \
X("crushercrush", MI_CRUSHERBODY, 0x5F5A90) \
X("crushertop", MI_CRUSHERLID, 0x5F5A94) \
X("donkeymag", MI_DONKEYMAG, 0x5F5A98) \
X("bullion", MI_BULLION, 0x5F5A9C) \
X("floatpackge1", MI_FLOATPACKAGE1, 0x5F5AA0) \
X("briefcase", MI_BRIEFCASE, 0x5F5AA4) \
X("chinabanner1", MI_CHINABANNER1, 0x5F5AA8) \
X("chinabanner2", MI_CHINABANNER2, 0x5F5AAC) \
X("chinabanner3", MI_CHINABANNER3, 0x5F5AB0) \
X("chinabanner4", MI_CHINABANNER4, 0x5F5AB4) \
X("iten_chinatown5", MI_CHINABANNER5, 0x5F5AB8) \
X("iten_chinatown7", MI_CHINABANNER6, 0x5F5ABC) \
X("iten_chinatown3", MI_CHINABANNER7, 0x5F5AC0) \
X("iten_chinatown2", MI_CHINABANNER8, 0x5F5AC4) \
X("iten_chinatown4", MI_CHINABANNER9, 0x5F5AC8) \
X("iten_washline01", MI_CHINABANNER10, 0x5F5ACC) \
X("iten_washline02", MI_CHINABANNER11, 0x5F5AD0) \
X("iten_washline03", MI_CHINABANNER12, 0x5F5AD4) \
X("chinalanterns", MI_CHINALANTERN, 0x5F5AD8) \
X("glassfx1", MI_GLASS1, 0x5F5ADC) \
X("glassfx2", MI_GLASS2, 0x5F5AE0) \
X("glassfx3", MI_GLASS3, 0x5F5AE4) \
X("glassfx4", MI_GLASS4, 0x5F5AE8) \
X("glassfx55", MI_GLASS5, 0x5F5AEC) \
X("glassfxsub1", MI_GLASS6, 0x5F5AF0) \
X("glassfxsub2", MI_GLASS7, 0x5F5AF4) \
X("glassfx_composh", MI_GLASS8, 0x5F5AF8) \
X("bridge_liftsec", MI_BRIDGELIFT, 0x5F5AFC) \
X("bridge_liftweight", MI_BRIDGEWEIGHT, 0x5F5B00) \
X("subbridge_lift", MI_BRIDGEROADSEGMENT, 0x5F5B04) \
X("barrel4", MI_EXPLODINGBARREL, 0x5F5B08) \
X("flagsitaly", MI_ITALYBANNER1, 0x5F5B0C) \
X("adrenaline", MI_PICKUP_ADRENALINE, 0x5F5B10) \
X("bodyarmour", MI_PICKUP_BODYARMOUR, 0x5F5B14) \
X("info", MI_PICKUP_INFO, 0x5F5B18) \
X("health", MI_PICKUP_HEALTH, 0x5F5B1C) \
X("bonus", MI_PICKUP_BONUS, 0x5F5B20) \
X("bribe", MI_PICKUP_BRIBE, 0x5F5B24) \
X("killfrenzy", MI_PICKUP_KILLFRENZY, 0x5F5B28) \
X("camerapickup", MI_PICKUP_CAMERA, 0x5F5B2C) \
X("bollardlight", MI_BOLLARDLIGHT, 0x5F5B30) \
X("magnet", MI_MAGNET, 0x5F5B34) \
X("streetlamp1", MI_STREETLAMP1, 0x5F5B38) \
X("streetlamp2", MI_STREETLAMP2, 0x5F5B3C) \
X("railtrax_lo4b", MI_RAILTRACKS, 0x5F5B40) \
X("bar_barrier10", MI_FENCE, 0x5F5B44) \
X("bar_barrier12", MI_FENCE2, 0x5F5B48) \
X("petrolpump", MI_PETROLPUMP, 0x5F5B4C) \
X("bodycast", MI_BODYCAST, 0x5F5B50) \
X("backdoor", MI_BACKDOOR, 0x5F5B54) \
X("coffee", MI_COFFEE, 0x5F5B58) \
X("bouy", MI_BUOY, 0x5F5B5C) \
X("parktable1", MI_PARKTABLE, 0x5F5B60) \
X("sbwy_tunl_start", MI_SUBWAY1, 0x5F5B64) \
X("sbwy_tunl_bit", MI_SUBWAY2, 0x5F5B68) \
X("sbwy_tunl_bend", MI_SUBWAY3, 0x5F5B6C) \
X("sbwy_tunl_cstm6", MI_SUBWAY4, 0x5F5B70) \
X("sbwy_tunl_cstm7", MI_SUBWAY5, 0x5F5B74) \
X("sbwy_tunl_cstm8", MI_SUBWAY6, 0x5F5B78) \
X("sbwy_tunl_cstm10", MI_SUBWAY7, 0x5F5B7C) \
X("sbwy_tunl_cstm9", MI_SUBWAY8, 0x5F5B80) \
X("sbwy_tunl_cstm11", MI_SUBWAY9, 0x5F5B84) \
X("sbwy_tunl_cstm1", MI_SUBWAY10, 0x5F5B88) \
X("sbwy_tunl_cstm2", MI_SUBWAY11, 0x5F5B8C) \
X("sbwy_tunl_cstm4", MI_SUBWAY12, 0x5F5B90) \
X("sbwy_tunl_cstm3", MI_SUBWAY13, 0x5F5B94) \
X("sbwy_tunl_cstm5", MI_SUBWAY14, 0x5F5B98) \
X("subplatform_n2", MI_SUBWAY15, 0x5F5B9C) \
X("suby_tunl_start", MI_SUBWAY16, 0x5F5BA0) \
X("sbwy_tunl_start2", MI_SUBWAY17, 0x5F5BA4) \
X("indy_tunl_start", MI_SUBWAY18, 0x5F5BA8) \
X("indsubway03", MI_SUBPLATFORM_IND, 0x5F5BAC) \
X("comerside_subway", MI_SUBPLATFORM_COMS, 0x5F5BB0) \
X("subplatform", MI_SUBPLATFORM_COMS2, 0x5F5BB4) \
X("subplatform_n", MI_SUBPLATFORM_COMN, 0x5F5BB8) \
X("Otherside_subway", MI_SUBPLATFORM_SUB, 0x5F5BBC) \
X("subplatform_sub", MI_SUBPLATFORM_SUB2, 0x5F5BC0) \
X("files", MI_FILES, 0x5F5BC4)
#define X(name, var, addr) extern int16 &var;
MODELINDICES
#undef X
// and some hardcoded ones
// expand as needed
enum
{
MI_COP = 1,
MI_SWAT,
MI_FBI,
MI_ARMY,
MI_MEDIC,
MI_FIREMAN,
MI_MALE01,
MI_TAXI_D,
MI_PIMP,
MI_GANG01,
MI_GANG02,
MI_GANG03,
MI_GANG04,
MI_GANG05,
MI_GANG06,
MI_GANG07,
MI_GANG08,
MI_GANG09,
MI_GANG10,
MI_GANG11,
MI_GANG12,
MI_GANG13,
MI_GANG14,
MI_CRIMINAL01,
MI_CRIMINAL02,
MI_SPECIAL01,
MI_SPECIAL02,
MI_SPECIAL03,
MI_SPECIAL04,
MI_MALE02,
MI_MALE03,
MI_FATMALE01,
MI_FATMALE02,
MI_FEMALE01,
MI_FEMALE02,
MI_FEMALE03,
MI_FATFEMALE01,
MI_FATFEMALE02,
MI_RHINO = 122,
MI_COACH = 127,
};
void InitModelIndices(void);
void MatchModelString(const char *name, int16 id);
inline bool
IsGlass(int16 id)
{
return id == MI_GLASS1 ||
id == MI_GLASS2 ||
id == MI_GLASS3 ||
id == MI_GLASS4 ||
id == MI_GLASS5 ||
id == MI_GLASS6 ||
id == MI_GLASS7 ||
id == MI_GLASS8;
}

124
src/modelinfo/ModelInfo.cpp Normal file
View file

@ -0,0 +1,124 @@
#include "common.h"
#include "patcher.h"
#include "ModelInfo.h"
CBaseModelInfo **CModelInfo::ms_modelInfoPtrs = (CBaseModelInfo**)0x83D408;
//CStore<CSimpleModelInfo, SIMPLEMODELSIZE> &CModelInfo::ms_simpleModelStore = *(CStore<CSimpleModelInfo, SIMPLEMODELSIZE>*)0x885BB4;
//CStore<CTimeModelInfo, TIMEMODELSIZE> &CModelInfo::ms_timeModelStore = *(CStore<CTimeModelInfo, TIMEMODELSIZE>*)0x94076C;
//CStore<C2dEffect, TWODFXSIZE> &CModelInfo::ms_2dEffectStore = *(CStore<C2dEffect, TWODFXSIZE>*)0x9434F8;
CStore<CSimpleModelInfo, SIMPLEMODELSIZE> CModelInfo::ms_simpleModelStore;
CStore<CTimeModelInfo, TIMEMODELSIZE> CModelInfo::ms_timeModelStore;
CStore<CClumpModelInfo, CLUMPMODELSIZE> CModelInfo::ms_clumpModelStore;
CStore<CPedModelInfo, PEDMODELSIZE> CModelInfo::ms_pedModelStore;
CStore<CVehicleModelInfo, VEHICLEMODELSIZE> CModelInfo::ms_vehicleModelStore;
CStore<C2dEffect, TWODFXSIZE> CModelInfo::ms_2dEffectStore;
void
CModelInfo::Initialise(void)
{
int i;
for(i = 0; i < MODELINFOSIZE; i++)
ms_modelInfoPtrs[i] = nil;
ms_2dEffectStore.clear();
ms_simpleModelStore.clear();
ms_timeModelStore.clear();
ms_clumpModelStore.clear();
ms_pedModelStore.clear();
ms_vehicleModelStore.clear();
}
void
CModelInfo::Shutdown(void)
{
int i;
for(i = 0; i < ms_simpleModelStore.allocPtr; i++)
ms_simpleModelStore.store[i].Shutdown();
for(i = 0; i < ms_timeModelStore.allocPtr; i++)
ms_timeModelStore.store[i].Shutdown();
for(i = 0; i < ms_clumpModelStore.allocPtr; i++)
ms_clumpModelStore.store[i].Shutdown();
for(i = 0; i < ms_pedModelStore.allocPtr; i++)
ms_pedModelStore.store[i].Shutdown();
for(i = 0; i < ms_vehicleModelStore.allocPtr; i++)
ms_vehicleModelStore.store[i].Shutdown();
}
CSimpleModelInfo*
CModelInfo::AddSimpleModel(int id)
{
CSimpleModelInfo *modelinfo;
modelinfo = CModelInfo::ms_simpleModelStore.alloc();
CModelInfo::ms_modelInfoPtrs[id] = modelinfo;
modelinfo->Init();
return modelinfo;
}
CTimeModelInfo*
CModelInfo::AddTimeModel(int id)
{
CTimeModelInfo *modelinfo;
modelinfo = CModelInfo::ms_timeModelStore.alloc();
CModelInfo::ms_modelInfoPtrs[id] = modelinfo;
modelinfo->Init();
return modelinfo;
}
CClumpModelInfo*
CModelInfo::AddClumpModel(int id)
{
CClumpModelInfo *modelinfo;
modelinfo = CModelInfo::ms_clumpModelStore.alloc();
CModelInfo::ms_modelInfoPtrs[id] = modelinfo;
modelinfo->m_clump = nil;
return modelinfo;
}
CPedModelInfo*
CModelInfo::AddPedModel(int id)
{
CPedModelInfo *modelinfo;
modelinfo = CModelInfo::ms_pedModelStore.alloc();
CModelInfo::ms_modelInfoPtrs[id] = modelinfo;
modelinfo->m_clump = nil;
return modelinfo;
}
CVehicleModelInfo*
CModelInfo::AddVehicleModel(int id)
{
CVehicleModelInfo *modelinfo;
modelinfo = CModelInfo::ms_vehicleModelStore.alloc();
CModelInfo::ms_modelInfoPtrs[id] = modelinfo;
modelinfo->m_clump = nil;
modelinfo->m_vehicleType = -1;
modelinfo->m_wheelId = -1;
modelinfo->m_materials1[0] = nil;
modelinfo->m_materials2[0] = nil;
modelinfo->m_bikeSteerAngle = 999.99f;
return modelinfo;
}
CBaseModelInfo*
CModelInfo::GetModelInfo(const char *name, int *id)
{
CBaseModelInfo *modelinfo;
for(int i = 0; i < MODELINFOSIZE; i++){
modelinfo = CModelInfo::ms_modelInfoPtrs[i];
if(modelinfo && _strcmpi(modelinfo->GetName(), name) == 0){
if(id)
*id = i;
return modelinfo;
}
}
return nil;
}
STARTPATCHES
// InjectHook(0x50B920, CModelInfo::AddSimpleModel, PATCH_JUMP);
// InjectHook(0x50B9C0, CModelInfo::AddTimeModel, PATCH_JUMP);
// InjectHook(0x50BA10, CModelInfo::AddClumpModel, PATCH_JUMP);
// InjectHook(0x50BAD0, CModelInfo::AddPedModel, PATCH_JUMP);
// InjectHook(0x50BA60, CModelInfo::AddPedModel, PATCH_JUMP);
InjectHook(0x50B860, (CBaseModelInfo *(*)(const char*, int*))CModelInfo::GetModelInfo, PATCH_JUMP);
ENDPATCHES

35
src/modelinfo/ModelInfo.h Normal file
View file

@ -0,0 +1,35 @@
#pragma once
#include "2dEffect.h"
#include "BaseModelInfo.h"
#include "SimpleModelInfo.h"
#include "TimeModelInfo.h"
#include "ClumpModelInfo.h"
#include "PedModelInfo.h"
#include "VehicleModelInfo.h"
class CModelInfo
{
static CBaseModelInfo **ms_modelInfoPtrs; //[MODELINFOSIZE];
static CStore<CSimpleModelInfo, SIMPLEMODELSIZE> ms_simpleModelStore;
static CStore<CTimeModelInfo, TIMEMODELSIZE> ms_timeModelStore;
static CStore<CClumpModelInfo, CLUMPMODELSIZE> ms_clumpModelStore;
static CStore<CPedModelInfo, PEDMODELSIZE> ms_pedModelStore;
static CStore<CVehicleModelInfo, VEHICLEMODELSIZE> ms_vehicleModelStore;
static CStore<C2dEffect, TWODFXSIZE> ms_2dEffectStore;
public:
static void Initialise(void);
static void Shutdown(void);
static CSimpleModelInfo *AddSimpleModel(int id);
static CTimeModelInfo *AddTimeModel(int id);
static CClumpModelInfo *AddClumpModel(int id);
static CPedModelInfo *AddPedModel(int id);
static CVehicleModelInfo *AddVehicleModel(int id);
static CBaseModelInfo *GetModelInfo(const char *name, int *id);
static CBaseModelInfo *GetModelInfo(int id){
return ms_modelInfoPtrs[id];
}
};

View file

@ -0,0 +1,197 @@
#include "common.h"
#include "patcher.h"
#include "NodeName.h"
#include "VisibilityPlugins.h"
#include "ModelInfo.h"
void
CPedModelInfo::DeleteRwObject(void)
{
CClumpModelInfo::DeleteRwObject();
if(m_hitColModel)
delete m_hitColModel;
m_hitColModel = nil;
}
RwObjectNameIdAssocation CPedModelInfo::m_pPedIds[12] = {
{ "Smid", PED_TORSO, 0, }, // that is strange...
{ "Shead", PED_HEAD, 0, },
{ "Supperarml", PED_UPPERARML, 0, },
{ "Supperarmr", PED_UPPERARMR, 0, },
{ "SLhand", PED_HANDL, 0, },
{ "SRhand", PED_HANDR, 0, },
{ "Supperlegl", PED_UPPERLEGL, 0, },
{ "Supperlegr", PED_UPPERLEGR, 0, },
{ "Sfootl", PED_FOOTL, 0, },
{ "Sfootr", PED_FOOTR, 0, },
{ "Slowerlegr", PED_LOWERLEGR, 0, },
{ NULL, 0, 0, },
};
void
CPedModelInfo::SetClump(RpClump *clump)
{
CClumpModelInfo::SetClump(clump);
SetFrameIds(m_pPedIds);
if(m_hitColModel == nil)
CreateHitColModel();
if(strncmp(GetName(), "player", 7) == 0)
RpClumpForAllAtomics(m_clump, SetAtomicRendererCB, CVisibilityPlugins::RenderPlayerCB);
}
RpAtomic*
CountAtomicsCB(RpAtomic *atomic, void *data)
{
(*(int32*)data)++;
return atomic;
}
RpAtomic*
GetAtomicListCB(RpAtomic *atomic, void *data)
{
**(RpAtomic***)data = atomic;
(*(RpAtomic***)data)++;
return atomic;
}
RwFrame*
FindPedFrameFromNameCB(RwFrame *frame, void *data)
{
RwObjectNameAssociation *assoc = (RwObjectNameAssociation*)data;
if(_strcmpi(GetFrameNodeName(frame)+1, assoc->name+1) != 0){
RwFrameForAllChildren(frame, FindPedFrameFromNameCB, assoc);
return assoc->frame ? nil : frame;
}else{
assoc->frame = frame;
return nil;
}
}
void
CPedModelInfo::SetLowDetailClump(RpClump *lodclump)
{
RpAtomic *atomics[16];
RpAtomic **pAtm;
int32 numAtm, numLodAtm;
int i;
RwObjectNameAssociation assoc;
numAtm = 0;
numLodAtm = 0;
RpClumpForAllAtomics(m_clump, CountAtomicsCB, &numAtm); // actually unused
RpClumpForAllAtomics(lodclump, CountAtomicsCB, &numLodAtm);
RpClumpForAllAtomics(m_clump, SetAtomicRendererCB, CVisibilityPlugins::RenderPedHiDetailCB);
RpClumpForAllAtomics(lodclump, SetAtomicRendererCB, CVisibilityPlugins::RenderPedLowDetailCB);
pAtm = atomics;
RpClumpForAllAtomics(lodclump, GetAtomicListCB, &pAtm);
for(i = 0; i < numLodAtm; i++){
assoc.name = GetFrameNodeName(RpAtomicGetFrame(atomics[i]));
assoc.frame = nil;
RwFrameForAllChildren(RpClumpGetFrame(m_clump), FindPedFrameFromNameCB, &assoc);
if(assoc.frame){
RpAtomicSetFrame(atomics[i], assoc.frame);
RpClumpRemoveAtomic(lodclump, atomics[i]);
RpClumpAddAtomic(m_clump, atomics[i]);
}
}
}
struct ColNodeInfo
{
char *name;
int pedNode;
int pieceType;
float x, z;
float radius;
};
// TODO: find out piece types
#define NUMPEDINFONODES 8
ColNodeInfo m_pColNodeInfos[NUMPEDINFONODES] = {
{ NULL, PED_HEAD, 6, 0.0f, 0.05f, 0.2f },
{ "Storso", 0, 0, 0.0f, 0.15f, 0.2f },
{ "Storso", 0, 0, 0.0f, -0.05f, 0.3f },
{ NULL, PED_TORSO, 1, 0.0f, -0.07f, 0.3f },
{ NULL, PED_UPPERARML, 2, 0.07f, -0.1f, 0.2f },
{ NULL, PED_UPPERARMR, 3, -0.07f, -0.1f, 0.2f },
{ "Slowerlegl", 0, 4, 0.0f, 0.07f, 0.25f },
{ NULL, PED_LOWERLEGR, 5, 0.0f, 0.07f, 0.25f },
};
RwObject*
FindHeadRadiusCB(RwObject *object, void *data)
{
RpAtomic *atomic = (RpAtomic*)object;
*(float*)data = RpAtomicGetBoundingSphere(atomic)->radius;
return nil;
}
void
CPedModelInfo::CreateHitColModel(void)
{
RwObjectNameAssociation nameAssoc;
RwObjectIdAssociation idAssoc;
CVector center;
RwFrame *nodeFrame;
CColModel *colmodel = new CColModel;
CColSphere *spheres = (CColSphere*)RwMalloc(NUMPEDINFONODES*sizeof(CColSphere));
RwFrame *root = RpClumpGetFrame(m_clump);
RwMatrix *mat = RwMatrixCreate();
for(int i = 0; i < NUMPEDINFONODES; i++){
nodeFrame = nil;
if(m_pColNodeInfos[i].name){
nameAssoc.name = m_pColNodeInfos[i].name;
nameAssoc.frame = nil;
RwFrameForAllChildren(root, FindFrameFromNameCB, &nameAssoc);
nodeFrame = nameAssoc.frame;
}else{
idAssoc.id = m_pColNodeInfos[i].pedNode;
idAssoc.frame = nil;
RwFrameForAllChildren(root, FindFrameFromIdCB, &idAssoc);
nodeFrame = idAssoc.frame;
}
if(nodeFrame){
float radius = m_pColNodeInfos[i].radius;
if(m_pColNodeInfos[i].pieceType == 6)
RwFrameForAllObjects(nodeFrame, FindHeadRadiusCB, &radius);
RwMatrixTransform(mat, RwFrameGetMatrix(nodeFrame), rwCOMBINEREPLACE);
const char *name = GetFrameNodeName(nodeFrame);
for(nodeFrame = RwFrameGetParent(nodeFrame);
nodeFrame;
nodeFrame = RwFrameGetParent(nodeFrame)){
name = GetFrameNodeName(nodeFrame);
RwMatrixTransform(mat, RwFrameGetMatrix(nodeFrame), rwCOMBINEPOSTCONCAT);
if(RwFrameGetParent(nodeFrame) == root)
break;
}
center.x = mat->pos.x + m_pColNodeInfos[i].x;
center.y = mat->pos.y + 0.0f;
center.z = mat->pos.z + m_pColNodeInfos[i].z;
spheres[i].Set(radius, center, 17, m_pColNodeInfos[i].pieceType);
}
}
RwMatrixDestroy(mat);
colmodel->spheres = spheres;
colmodel->numSpheres = NUMPEDINFONODES;
center.x = center.y = center.z = 0.0f;
colmodel->boundingSphere.Set(2.0f, center, 0, 0);
CVector min, max;
min.x = min.y = -0.5f;
min.z = -1.2f;
max.x = max.y = 0.5f;
max.z = 1.2f;
colmodel->boundingBox.Set(min, max, 0, 0);
colmodel->level = 0;
m_hitColModel = colmodel;
}
STARTPATCHES
InjectHook(0x510210, &CPedModelInfo::SetClump_, PATCH_JUMP);
InjectHook(0x510280, &CPedModelInfo::DeleteRwObject_, PATCH_JUMP);
InjectHook(0x510390, &CPedModelInfo::SetLowDetailClump, PATCH_JUMP);
InjectHook(0x5104D0, &CPedModelInfo::CreateHitColModel, PATCH_JUMP);
ENDPATCHES

View file

@ -0,0 +1,47 @@
#pragma once
#include "ClumpModelInfo.h"
enum PedNode {
PED_WAIST,
PED_TORSO, // Smid on PS2/PC, Storso on mobile/xbox
PED_HEAD,
PED_UPPERARML,
PED_UPPERARMR,
PED_HANDL,
PED_HANDR,
PED_UPPERLEGL,
PED_UPPERLEGR,
PED_FOOTL,
PED_FOOTR,
PED_LOWERLEGR,
// This is not valid apparently
PED_LOWERLEGL,
};
class CPedModelInfo : public CClumpModelInfo
{
public:
void *m_animGroup; // TODO
int32 m_pedType;
int32 m_pedStatType;
uint32 m_carsCanDrive;
CColModel *m_hitColModel;
RpAtomic *m_head;
RpAtomic *m_lhand;
RpAtomic *m_rhand;
static RwObjectNameIdAssocation m_pPedIds[12];
CPedModelInfo(void) : CClumpModelInfo(MITYPE_PED) { }
void DeleteRwObject(void);
void SetClump(RpClump *);
void SetLowDetailClump(RpClump*);
void CreateHitColModel(void);
void DeleteRwObject_(void) { this->CPedModelInfo::DeleteRwObject(); }
void SetClump_(RpClump *clump) { this->CPedModelInfo::SetClump(clump); }
};
static_assert(sizeof(CPedModelInfo) == 0x54, "CPedModelInfo: error");

View file

@ -0,0 +1,174 @@
#include "common.h"
#include "patcher.h"
#include "Camera.h"
#include "ModelInfo.h"
#define LOD_DISTANCE (300.0f)
void
CSimpleModelInfo::DeleteRwObject(void)
{
int i;
RwFrame *f;
for(i = 0; i < m_numAtomics; i++)
if(m_atomics[i]){
f = RpAtomicGetFrame(m_atomics[i]);
RpAtomicDestroy(m_atomics[i]);
RwFrameDestroy(f);
m_atomics[i] = nil;
RemoveTexDictionaryRef();
}
}
RwObject*
CSimpleModelInfo::CreateInstance(void)
{
RpAtomic *atomic;
if(m_atomics[0] == nil)
return nil;
atomic = RpAtomicClone(m_atomics[0]);
RpAtomicSetFrame(atomic, RwFrameCreate());
return (RwObject*)atomic;
}
RwObject*
CSimpleModelInfo::CreateInstance(RwMatrix *matrix)
{
RpAtomic *atomic;
RwFrame *frame;
if(m_atomics[0] == nil)
return nil;
atomic = RpAtomicClone(m_atomics[0]);
frame = RwFrameCreate();
*RwFrameGetMatrix(frame) = *matrix;
RpAtomicSetFrame(atomic, frame);
return (RwObject*)atomic;
}
void
CSimpleModelInfo::Init(void)
{
m_atomics[0] = nil;
m_atomics[1] = nil;
m_atomics[2] = nil;
m_numAtomics = 0;
m_furthest = 0;
m_normalCull = 0;
m_isDamaged = 0;
m_isBigBuilding = 0;
m_noFade = 0;
m_drawLast = 0;
m_additive = 0;
m_isSubway = 0;
m_ignoreLight = 0;
m_noZwrite = 0;
}
void
CSimpleModelInfo::SetAtomic(int n, RpAtomic *atomic)
{
AddTexDictionaryRef();
m_atomics[n] = atomic;
if(m_ignoreLight){
RpGeometry *geo = RpAtomicGetGeometry(atomic);
RpGeometrySetFlags(geo, RpGeometryGetFlags(geo) & ~rpGEOMETRYLIGHT);
}
}
void
CSimpleModelInfo::SetLodDistances(float *dist)
{
m_lodDistances[0] = dist[0];
m_lodDistances[1] = dist[1];
m_lodDistances[2] = dist[2];
}
void
CSimpleModelInfo::IncreaseAlpha(void)
{
if(m_alpha >= 0xEF)
m_alpha = 0xFF;
else
m_alpha += 0x10;
}
float
CSimpleModelInfo::GetNearDistance(void)
{
return m_lodDistances[2] * TheCamera.LODDistMultiplier;
}
float
CSimpleModelInfo::GetLargestLodDistance(void)
{
float d;
// TODO: what exactly is going on here?
if(m_furthest != 0 && !m_isDamaged)
d = m_lodDistances[m_furthest-1];
else
d = m_lodDistances[m_numAtomics-1];
return d * TheCamera.LODDistMultiplier;
}
RpAtomic*
CSimpleModelInfo::GetAtomicFromDistance(float dist)
{
int i;
i = 0;
// TODO: what exactly is going on here?
if(m_isDamaged)
i = m_furthest;
for(; i < m_numAtomics; i++)
if(dist < m_lodDistances[i] *TheCamera.LODDistMultiplier)
return m_atomics[i];
return nil;
}
void
CSimpleModelInfo::FindRelatedModel(void)
{
int i;
CBaseModelInfo *mi;
for(i = 0; i < MODELINFOSIZE; i++){
mi = CModelInfo::GetModelInfo(i);
if(mi && mi != this &&
strcmp(GetName()+3, mi->GetName()+3) == 0){
assert(mi->IsSimple());
this->SetRelatedModel((CSimpleModelInfo*)mi);
return;
}
}
}
void
CSimpleModelInfo::SetupBigBuilding(void)
{
CSimpleModelInfo *related;
if(m_lodDistances[0] > LOD_DISTANCE && m_atomics[2] == nil){
m_isBigBuilding = 1;
FindRelatedModel();
related = GetRelatedModel();
if(related)
m_lodDistances[2] = related->GetLargestLodDistance()/TheCamera.LODDistMultiplier;
else
m_lodDistances[2] = 100.0f;
}
}
STARTPATCHES
InjectHook(0x5179B0, &CSimpleModelInfo::DeleteRwObject_, PATCH_JUMP);
InjectHook(0x517B60, &CSimpleModelInfo::CreateInstance_1, PATCH_JUMP);
InjectHook(0x517AC0, &CSimpleModelInfo::CreateInstance_2, PATCH_JUMP);
InjectHook(0x4A9BA0, &CSimpleModelInfo::GetRwObject_, PATCH_JUMP);
InjectHook(0x517990, &CSimpleModelInfo::Init, PATCH_JUMP);
InjectHook(0x517C60, &CSimpleModelInfo::IncreaseAlpha, PATCH_JUMP);
InjectHook(0x517950, &CSimpleModelInfo::SetAtomic, PATCH_JUMP);
InjectHook(0x517AA0, &CSimpleModelInfo::SetLodDistances, PATCH_JUMP);
InjectHook(0x517A90, &CSimpleModelInfo::GetNearDistance, PATCH_JUMP);
InjectHook(0x517A60, &CSimpleModelInfo::GetLargestLodDistance, PATCH_JUMP);
InjectHook(0x517A00, &CSimpleModelInfo::GetAtomicFromDistance, PATCH_JUMP);
InjectHook(0x517C00, &CSimpleModelInfo::FindRelatedModel, PATCH_JUMP);
InjectHook(0x517B90, &CSimpleModelInfo::SetupBigBuilding, PATCH_JUMP);
ENDPATCHES

View file

@ -0,0 +1,57 @@
#pragma once
#include "BaseModelInfo.h"
class CSimpleModelInfo : public CBaseModelInfo
{
public:
// atomics[2] is often a pointer to the non-LOD modelinfo
RpAtomic *m_atomics[3];
// m_lodDistances[2] holds the near distance for LODs
float m_lodDistances[3];
uint8 m_numAtomics;
uint8 m_alpha;
uint16 m_furthest : 2; // 0: numAtomics-1 is furthest visible
// 1: atomic 0 is furthest
// 2: atomic 1 is furthest
uint16 m_normalCull : 1;
uint16 m_isDamaged : 1;
uint16 m_isBigBuilding : 1;
uint16 m_noFade : 1;
uint16 m_drawLast : 1;
uint16 m_additive : 1;
uint16 m_isSubway : 1;
uint16 m_ignoreLight : 1;
uint16 m_noZwrite : 1;
CSimpleModelInfo(void) : CBaseModelInfo(MITYPE_SIMPLE) {}
CSimpleModelInfo(ModeInfoType id) : CBaseModelInfo(id) {}
~CSimpleModelInfo() {}
void DeleteRwObject(void);
RwObject *CreateInstance(void);
RwObject *CreateInstance(RwMatrix *);
RwObject *GetRwObject(void) { return (RwObject*)m_atomics[0]; }
void Init(void);
void IncreaseAlpha(void);
void SetAtomic(int n, RpAtomic *atomic);
void SetLodDistances(float *dist);
float GetLodDistance(int i) { return m_lodDistances[i]; }
float GetNearDistance(void);
float GetLargestLodDistance(void);
RpAtomic *GetAtomicFromDistance(float dist);
void FindRelatedModel(void);
void SetupBigBuilding(void);
void SetNumAtomics(int n) { m_numAtomics = n; }
CSimpleModelInfo *GetRelatedModel(void){
return (CSimpleModelInfo*)m_atomics[2]; }
void SetRelatedModel(CSimpleModelInfo *m){
m_atomics[2] = (RpAtomic*)m; }
void DeleteRwObject_(void) { this->CSimpleModelInfo::DeleteRwObject(); }
RwObject *CreateInstance_1(void) { return this->CSimpleModelInfo::CreateInstance(); }
RwObject *CreateInstance_2(RwMatrix *m) { return this->CSimpleModelInfo::CreateInstance(m); }
RwObject *GetRwObject_(void) { return this->CSimpleModelInfo::GetRwObject(); }
};
static_assert(sizeof(CSimpleModelInfo) == 0x4C, "CSimpleModelInfo: error");

View file

@ -0,0 +1,36 @@
#include "common.h"
#include "patcher.h"
#include "Camera.h"
#include "ModelInfo.h"
CTimeModelInfo*
CTimeModelInfo::FindOtherTimeModel(void)
{
char name[40];
char *p;
int i;
strcpy(name, GetName());
// change _nt to _dy
if(p = strstr(name, "_nt"))
strncpy(p, "_dy", 4);
// change _dy to _nt
else if(p = strstr(name, "_dy"))
strncpy(p, "_nt", 4);
else
return nil;
for(i = 0; i < MODELINFOSIZE; i++){
CBaseModelInfo *mi = CModelInfo::GetModelInfo(i);
if(mi && mi->m_type == MITYPE_TIME &&
strncmp(name, mi->GetName(), 24) == 0){
m_otherTimeModelID = i;
return (CTimeModelInfo*)mi;
}
}
return nil;
}
STARTPATCHES
InjectHook(0x517C80, &CTimeModelInfo::FindOtherTimeModel, PATCH_JUMP);
ENDPATCHES

View file

@ -0,0 +1,18 @@
#pragma once
#include "SimpleModelInfo.h"
class CTimeModelInfo : public CSimpleModelInfo
{
int32 m_timeOn;
int32 m_timeOff;
int32 m_otherTimeModelID;
public:
CTimeModelInfo(void) : CSimpleModelInfo(MITYPE_TIME) { m_otherTimeModelID = -1; }
int32 GetTimeOn(void) { return m_timeOn; }
int32 GetTimeOff(void) { return m_timeOff; }
int32 GetOtherTimeModel(void) { return m_otherTimeModelID; }
CTimeModelInfo *FindOtherTimeModel(void);
};
static_assert(sizeof(CTimeModelInfo) == 0x58, "CTimeModelInfo: error");

View file

@ -0,0 +1,917 @@
#include "common.h"
#include <rpmatfx.h>
#include "patcher.h"
#include "RwHelper.h"
#include "General.h"
#include "NodeName.h"
#include "TxdStore.h"
#include "Weather.h"
#include "VisibilityPlugins.h"
#include "ModelInfo.h"
int8 *CVehicleModelInfo::ms_compsToUse = (int8*)0x5FF2EC; // -2, -2
int8 *CVehicleModelInfo::ms_compsUsed = (int8*)0x95CCB2;
RwTexture **CVehicleModelInfo::ms_pEnvironmentMaps = (RwTexture **)0x8F1A30;
RwRGBA *CVehicleModelInfo::ms_vehicleColourTable = (RwRGBA*)0x86BA88;
RwTexture **CVehicleModelInfo::ms_colourTextureTable = (RwTexture**)0x711C40;
RwTexture *&gpWhiteTexture = *(RwTexture**)0x64C4F8;
RwFrame *&pMatFxIdentityFrame = *(RwFrame**)0x64C510;
// TODO This depends on handling
WRAPPER void CVehicleModelInfo::SetVehicleComponentFlags(RwFrame *frame, uint32 flags) { EAXJMP(0x5203C0); }
enum {
CAR_WHEEL_RF = 1,
CAR_WHEEL_RM = 2,
CAR_WHEEL_RB = 3,
CAR_WHEEL_LF = 4,
CAR_WHEEL_LM = 5,
CAR_WHEEL_LB = 6,
CAR_BUMP_FRONT = 7,
CAR_BUMP_REAR = 8,
CAR_WING_RF = 9,
CAR_WING_RR = 10,
CAR_DOOR_RF = 11,
CAR_DOOR_RR = 12,
CAR_WING_LF = 13,
CAR_WING_LR = 14,
CAR_DOOR_LF = 15,
CAR_DOOR_LR = 16,
CAR_BONNET = 17,
CAR_BOOT = 18,
CAR_WINDSCREEN = 19,
CAR_POS_HEADLIGHTS = 0,
CAR_POS_TAILLIGHTS = 1,
CAR_POS_FRONTSEAT = 2,
CAR_POS_BACKSEAT = 3,
CAR_POS_EXHAUST = 9,
};
enum {
VEHICLE_FLAG_COLLAPSE = 0x2,
VEHICLE_FLAG_ADD_WHEEL = 0x4,
VEHICLE_FLAG_POS = 0x8,
VEHICLE_FLAG_DOOR = 0x10,
VEHICLE_FLAG_LEFT = 0x20,
VEHICLE_FLAG_RIGHT = 0x40,
VEHICLE_FLAG_FRONT = 0x80,
VEHICLE_FLAG_REAR = 0x100,
VEHICLE_FLAG_COMP = 0x200,
VEHICLE_FLAG_DRAWLAST = 0x400,
VEHICLE_FLAG_WINDSCREEN = 0x800,
VEHICLE_FLAG_ANGLECULL = 0x1000,
VEHICLE_FLAG_REARDOOR = 0x2000,
VEHICLE_FLAG_FRONTDOOR = 0x4000,
};
RwObjectNameIdAssocation carIds[] = {
{ "wheel_rf_dummy", CAR_WHEEL_RF, VEHICLE_FLAG_RIGHT | VEHICLE_FLAG_ADD_WHEEL },
{ "wheel_rm_dummy", CAR_WHEEL_RM, VEHICLE_FLAG_RIGHT | VEHICLE_FLAG_ADD_WHEEL },
{ "wheel_rb_dummy", CAR_WHEEL_RB, VEHICLE_FLAG_RIGHT | VEHICLE_FLAG_ADD_WHEEL },
{ "wheel_lf_dummy", CAR_WHEEL_LF, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_ADD_WHEEL },
{ "wheel_lm_dummy", CAR_WHEEL_LM, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_ADD_WHEEL },
{ "wheel_lb_dummy", CAR_WHEEL_LB, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_ADD_WHEEL },
{ "bump_front_dummy", CAR_BUMP_FRONT, VEHICLE_FLAG_FRONT | VEHICLE_FLAG_COLLAPSE },
{ "bonnet_dummy", CAR_BONNET, VEHICLE_FLAG_COLLAPSE },
{ "wing_rf_dummy", CAR_WING_RF, VEHICLE_FLAG_COLLAPSE },
{ "wing_rr_dummy", CAR_WING_RR, VEHICLE_FLAG_RIGHT | VEHICLE_FLAG_COLLAPSE },
{ "door_rf_dummy", CAR_DOOR_RF, VEHICLE_FLAG_FRONTDOOR | VEHICLE_FLAG_ANGLECULL | VEHICLE_FLAG_RIGHT | VEHICLE_FLAG_DOOR | VEHICLE_FLAG_COLLAPSE },
{ "door_rr_dummy", CAR_DOOR_RR, VEHICLE_FLAG_REARDOOR | VEHICLE_FLAG_ANGLECULL | VEHICLE_FLAG_REAR | VEHICLE_FLAG_RIGHT | VEHICLE_FLAG_DOOR | VEHICLE_FLAG_COLLAPSE },
{ "wing_lf_dummy", CAR_WING_LF, VEHICLE_FLAG_COLLAPSE },
{ "wing_lr_dummy", CAR_WING_LR, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_COLLAPSE },
{ "door_lf_dummy", CAR_DOOR_LF, VEHICLE_FLAG_FRONTDOOR | VEHICLE_FLAG_ANGLECULL | VEHICLE_FLAG_LEFT | VEHICLE_FLAG_DOOR | VEHICLE_FLAG_COLLAPSE },
{ "door_lr_dummy", CAR_DOOR_LR, VEHICLE_FLAG_REARDOOR | VEHICLE_FLAG_ANGLECULL | VEHICLE_FLAG_REAR | VEHICLE_FLAG_LEFT | VEHICLE_FLAG_DOOR | VEHICLE_FLAG_COLLAPSE },
{ "boot_dummy", CAR_BOOT, VEHICLE_FLAG_REAR | VEHICLE_FLAG_COLLAPSE },
{ "bump_rear_dummy", CAR_BUMP_REAR, VEHICLE_FLAG_REAR | VEHICLE_FLAG_COLLAPSE },
{ "windscreen_dummy", CAR_WINDSCREEN, VEHICLE_FLAG_WINDSCREEN | VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_FRONT | VEHICLE_FLAG_COLLAPSE },
{ "ped_frontseat", CAR_POS_FRONTSEAT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ "ped_backseat", CAR_POS_BACKSEAT, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ "headlights", CAR_POS_HEADLIGHTS, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ "taillights", CAR_POS_TAILLIGHTS, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ "exhaust", CAR_POS_EXHAUST, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ "extra1", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID },
{ "extra2", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID },
{ "extra3", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID },
{ "extra4", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID },
{ "extra5", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID },
{ "extra6", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID },
{ nil, 0, 0 }
};
RwObjectNameIdAssocation boatIds[] = {
{ "boat_moving_hi", 1, VEHICLE_FLAG_COLLAPSE },
{ "boat_rudder_hi", 3, VEHICLE_FLAG_COLLAPSE },
{ "windscreen", 2, VEHICLE_FLAG_WINDSCREEN | VEHICLE_FLAG_COLLAPSE },
{ "ped_frontseat", 0, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ nil, 0, 0 }
};
RwObjectNameIdAssocation trainIds[] = {
{ "door_lhs_dummy", 1, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_COLLAPSE },
{ "door_rhs_dummy", 2, VEHICLE_FLAG_LEFT | VEHICLE_FLAG_COLLAPSE },
{ "light_front", 0, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ "light_rear", 1, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ "ped_left_entry", 2, VEHICLE_FLAG_DOOR | VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ "ped_mid_entry", 3, VEHICLE_FLAG_DOOR | VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ "ped_right_entry", 4, VEHICLE_FLAG_DOOR | VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ nil, 0, 0 }
};
RwObjectNameIdAssocation heliIds[] = {
{ "chassis_dummy", 1, VEHICLE_FLAG_COLLAPSE },
{ "toprotor", 2, 0 },
{ "backrotor", 3, 0 },
{ "tail", 4, 0 },
{ "topknot", 5, 0 },
{ "skid_left", 6, 0 },
{ "skid_right", 7, 0 },
{ nil, 0, 0 }
};
RwObjectNameIdAssocation planeIds[] = {
{ "wheel_front_dummy", 2, 0 },
{ "wheel_rear_dummy", 3, 0 },
{ "light_tailplane", 2, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ "light_left", 0, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ "light_right", 1, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ nil, 0, 0 }
};
RwObjectNameIdAssocation bikeIds[] = {
{ "chassis_dummy", 1, 0 },
{ "forks_front", 2, 0 },
{ "forks_rear", 3, 0 },
{ "wheel_front", 4, 0 },
{ "wheel_rear", 5, 0 },
{ "mudguard", 6, 0 },
{ "ped_frontseat", 2, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ "headlights", 0, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ "taillights", 1, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ "exhaust", 9, VEHICLE_FLAG_POS | CLUMP_FLAG_NO_HIERID },
{ "extra1", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID },
{ "extra2", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID },
{ "extra3", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID },
{ "extra4", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID },
{ "extra5", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID },
{ "extra6", 0, VEHICLE_FLAG_DRAWLAST | VEHICLE_FLAG_COMP | CLUMP_FLAG_NO_HIERID },
{ nil, 0, 0 }
};
RwObjectNameIdAssocation *CVehicleModelInfo::ms_vehicleDescs[] = {
carIds,
boatIds,
trainIds,
heliIds,
planeIds,
bikeIds
};
CVehicleModelInfo::CVehicleModelInfo(void)
: CClumpModelInfo(MITYPE_VEHICLE)
{
int32 i;
for(i = 0; i < NUM_VEHICLE_POSITIONS; i++){
m_positions[i].x = 0.0f;
m_positions[i].y = 0.0f;
m_positions[i].z = 0.0f;
}
m_numColours = 0;
}
void
CVehicleModelInfo::DeleteRwObject(void)
{
int32 i;
RwFrame *f;
for(i = 0; i < m_numComps; i++){
f = RpAtomicGetFrame(m_comps[i]);
RpAtomicDestroy(m_comps[i]);
RwFrameDestroy(f);
}
m_numComps = 0;
CClumpModelInfo::DeleteRwObject();
}
RwObject*
CVehicleModelInfo::CreateInstance(void)
{
RpClump *clump;
RpAtomic *atomic;
RwFrame *clumpframe, *f;
int32 comp1, comp2;
clump = (RpClump*)CClumpModelInfo::CreateInstance();
if(m_numComps != 0){
clumpframe = RpClumpGetFrame(clump);
comp1 = ChooseComponent();
if(comp1 != -1){
atomic = RpAtomicClone(m_comps[comp1]);
f = RwFrameCreate();
RwFrameTransform(f,
RwFrameGetMatrix(RpAtomicGetFrame(m_comps[comp1])),
rwCOMBINEREPLACE);
RpAtomicSetFrame(atomic, f);
RpClumpAddAtomic(clump, atomic);
RwFrameAddChild(clumpframe, f);
}
ms_compsUsed[0] = comp1;
comp2 = ChooseSecondComponent();
if(comp2 != -1){
atomic = RpAtomicClone(m_comps[comp2]);
f = RwFrameCreate();
RwFrameTransform(f,
RwFrameGetMatrix(RpAtomicGetFrame(m_comps[comp2])),
rwCOMBINEREPLACE);
RpAtomicSetFrame(atomic, f);
RpClumpAddAtomic(clump, atomic);
RwFrameAddChild(clumpframe, f);
}
ms_compsUsed[1] = comp2;
}else{
ms_compsUsed[0] = -1;
ms_compsUsed[1] = -1;
}
return (RwObject*)clump;
}
void
CVehicleModelInfo::SetClump(RpClump *clump)
{
CClumpModelInfo::SetClump(clump);
SetAtomicRenderCallbacks();
SetFrameIds(ms_vehicleDescs[m_vehicleType]);
PreprocessHierarchy();
FindEditableMaterialList();
m_envMap = nil;
SetEnvironmentMap();
}
RwFrame*
CVehicleModelInfo::CollapseFramesCB(RwFrame *frame, void *data)
{
RwFrameForAllChildren(frame, CollapseFramesCB, data);
RwFrameForAllObjects(frame, MoveObjectsCB, data);
RwFrameDestroy(frame);
return frame;
}
RwObject*
CVehicleModelInfo::MoveObjectsCB(RwObject *object, void *data)
{
RpAtomicSetFrame((RpAtomic*)object, (RwFrame*)data);
return object;
}
RpAtomic*
CVehicleModelInfo::HideDamagedAtomicCB(RpAtomic *atomic, void *data)
{
if(strstr(GetFrameNodeName(RpAtomicGetFrame(atomic)), "_dam")){
RpAtomicSetFlags(atomic, 0);
CVisibilityPlugins::SetAtomicFlag(atomic, ATOMIC_FLAG_DAM);
}else if(strstr(GetFrameNodeName(RpAtomicGetFrame(atomic)), "_ok"))
CVisibilityPlugins::SetAtomicFlag(atomic, ATOMIC_FLAG_OK);
return atomic;
}
RpMaterial*
CVehicleModelInfo::HasAlphaMaterialCB(RpMaterial *material, void *data)
{
if(RpMaterialGetColor(material)->alpha != 0xFF){
*(bool*)data = true;
return nil;
}
return material;
}
RpAtomic*
CVehicleModelInfo::SetAtomicRendererCB(RpAtomic *atomic, void *data)
{
RpClump *clump;
char *name;
bool alpha;
clump = (RpClump*)data;
name = GetFrameNodeName(RpAtomicGetFrame(atomic));
alpha = false;
RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), HasAlphaMaterialCB, &alpha);
if(strstr(name, "_hi") || strncmp(name, "extra", 5) == 0){
if(alpha || strncmp(name, "windscreen", 10) == 0)
CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailAlphaCB);
else
CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailCB);
}else if(strstr(name, "_lo")){
RpClumpRemoveAtomic(clump, atomic);
RpAtomicDestroy(atomic);
return atomic; // BUG: not done by gta
}else if(strstr(name, "_vlo"))
CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleReallyLowDetailCB);
else
CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil);
HideDamagedAtomicCB(atomic, nil);
return atomic;
}
RpAtomic*
CVehicleModelInfo::SetAtomicRendererCB_BigVehicle(RpAtomic *atomic, void *data)
{
char *name;
bool alpha;
name = GetFrameNodeName(RpAtomicGetFrame(atomic));
alpha = false;
RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), HasAlphaMaterialCB, &alpha);
if(strstr(name, "_hi") || strncmp(name, "extra", 5) == 0){
if(alpha)
CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailAlphaCB_BigVehicle);
else
CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailCB_BigVehicle);
}else if(strstr(name, "_lo")){
if(alpha)
CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleLowDetailAlphaCB_BigVehicle);
else
CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleLowDetailCB_BigVehicle);
}else if(strstr(name, "_vlo"))
CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleReallyLowDetailCB_BigVehicle);
else
CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil);
HideDamagedAtomicCB(atomic, nil);
return atomic;
}
RpAtomic*
CVehicleModelInfo::SetAtomicRendererCB_Train(RpAtomic *atomic, void *data)
{
char *name;
bool alpha;
name = GetFrameNodeName(RpAtomicGetFrame(atomic));
alpha = false;
RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), HasAlphaMaterialCB, &alpha);
if(strstr(name, "_hi")){
if(alpha)
CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderTrainHiDetailAlphaCB);
else
CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderTrainHiDetailCB);
}else if(strstr(name, "_vlo"))
CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleReallyLowDetailCB_BigVehicle);
else
CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil);
HideDamagedAtomicCB(atomic, nil);
return atomic;
}
RpAtomic*
CVehicleModelInfo::SetAtomicRendererCB_Boat(RpAtomic *atomic, void *data)
{
RpClump *clump;
char *name;
clump = (RpClump*)data;
name = GetFrameNodeName(RpAtomicGetFrame(atomic));
if(strcmp(name, "boat_hi") == 0 || strncmp(name, "extra", 5) == 0)
CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailCB_Boat);
else if(strstr(name, "_hi"))
CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleHiDetailCB);
else if(strstr(name, "_lo")){
RpClumpRemoveAtomic(clump, atomic);
RpAtomicDestroy(atomic);
return atomic; // BUG: not done by gta
}else if(strstr(name, "_vlo"))
CVisibilityPlugins::SetAtomicRenderCallback(atomic, CVisibilityPlugins::RenderVehicleReallyLowDetailCB_BigVehicle);
else
CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil);
HideDamagedAtomicCB(atomic, nil);
return atomic;
}
RpAtomic*
CVehicleModelInfo::SetAtomicRendererCB_Heli(RpAtomic *atomic, void *data)
{
CVisibilityPlugins::SetAtomicRenderCallback(atomic, nil);
return atomic;
}
void
CVehicleModelInfo::SetAtomicRenderCallbacks(void)
{
switch(m_vehicleType){
case VEHICLE_TYPE_TRAIN:
RpClumpForAllAtomics(m_clump, SetAtomicRendererCB_Train, nil);
break;
case VEHICLE_TYPE_HELI:
RpClumpForAllAtomics(m_clump, SetAtomicRendererCB_Heli, nil);
break;
case VEHICLE_TYPE_PLANE:
RpClumpForAllAtomics(m_clump, SetAtomicRendererCB_BigVehicle, nil);
break;
case VEHICLE_TYPE_BOAT:
RpClumpForAllAtomics(m_clump, SetAtomicRendererCB_Boat, m_clump);
break;
default:
RpClumpForAllAtomics(m_clump, SetAtomicRendererCB, m_clump);
break;
}
}
RpAtomic*
CVehicleModelInfo::SetAtomicFlagCB(RpAtomic *atomic, void *data)
{
CVisibilityPlugins::SetAtomicFlag(atomic, (int)data);
return atomic;
}
RpAtomic*
CVehicleModelInfo::ClearAtomicFlagCB(RpAtomic *atomic, void *data)
{
CVisibilityPlugins::ClearAtomicFlag(atomic, (int)data);
return atomic;
}
RwObject*
GetOkAndDamagedAtomicCB(RwObject *object, void *data)
{
RpAtomic *atomic = (RpAtomic*)object;
if(CVisibilityPlugins::GetAtomicId(atomic) & ATOMIC_FLAG_OK)
((RpAtomic**)data)[0] = atomic;
else if(CVisibilityPlugins::GetAtomicId(atomic) & ATOMIC_FLAG_DAM)
((RpAtomic**)data)[1] = atomic;
return object;
}
void
CVehicleModelInfo::PreprocessHierarchy(void)
{
int32 i;
RwObjectNameIdAssocation *desc;
RwFrame *f;
RpAtomic *atomic;
RwV3d *rwvec;
desc = ms_vehicleDescs[m_vehicleType];
m_numDoors = 0;
m_numComps = 0;
for(i = 0; desc[i].name; i++){
RwObjectNameAssociation assoc;
if((desc[i].flags & (VEHICLE_FLAG_COMP|VEHICLE_FLAG_POS)) == 0)
continue;
assoc.frame = nil;
assoc.name = desc[i].name;
RwFrameForAllChildren(RpClumpGetFrame(m_clump),
FindFrameFromNameWithoutIdCB, &assoc);
if(assoc.frame == nil)
continue;
if(desc[i].flags & VEHICLE_FLAG_DOOR)
m_numDoors++;
if(desc[i].flags & VEHICLE_FLAG_POS){
f = assoc.frame;
rwvec = (RwV3d*)&m_positions[desc[i].hierId];
*rwvec = *RwMatrixGetPos(RwFrameGetMatrix(f));
for(f = RwFrameGetParent(f); f; f = RwFrameGetParent(f))
RwV3dTransformPoints(rwvec, rwvec, 1, RwFrameGetMatrix(f));
RwFrameDestroy(assoc.frame);
}else{
atomic = (RpAtomic*)GetFirstObject(assoc.frame);
RpClumpRemoveAtomic(m_clump, atomic);
RwFrameRemoveChild(assoc.frame);
SetVehicleComponentFlags(assoc.frame, desc[i].flags);
m_comps[m_numComps++] = atomic;
}
}
for(i = 0; desc[i].name; i++){
RwObjectIdAssociation assoc;
if(desc[i].flags & (VEHICLE_FLAG_COMP|VEHICLE_FLAG_POS))
continue;
assoc.frame = nil;
assoc.id = desc[i].hierId;
RwFrameForAllChildren(RpClumpGetFrame(m_clump),
FindFrameFromIdCB, &assoc);
if(assoc.frame == nil)
continue;
if(desc[i].flags & VEHICLE_FLAG_DOOR)
m_numDoors++;
if(desc[i].flags & VEHICLE_FLAG_COLLAPSE){
RpAtomic *okdam[2] = { nil, nil };
RwFrameForAllChildren(assoc.frame, CollapseFramesCB, assoc.frame);
RwFrameUpdateObjects(assoc.frame);
RwFrameForAllObjects(assoc.frame, GetOkAndDamagedAtomicCB, okdam);
if(okdam[0] && okdam[1])
RpAtomicSetRenderCallBack(okdam[1], RpAtomicGetRenderCallBack(okdam[0]));
}
SetVehicleComponentFlags(assoc.frame, desc[i].flags);
if(desc[i].flags & VEHICLE_FLAG_ADD_WHEEL){
if(m_wheelId == -1)
RwFrameDestroy(assoc.frame);
else{
RwV3d scale;
atomic = (RpAtomic*)CModelInfo::GetModelInfo(m_wheelId)->CreateInstance();
RwFrameDestroy(RpAtomicGetFrame(atomic));
RpAtomicSetFrame(atomic, assoc.frame);
RpClumpAddAtomic(m_clump, atomic);
CVisibilityPlugins::SetAtomicRenderCallback(atomic,
CVisibilityPlugins::RenderWheelAtomicCB);
scale.x = m_wheelScale;
scale.y = m_wheelScale;
scale.z = m_wheelScale;
RwFrameScale(assoc.frame, &scale, rwCOMBINEPRECONCAT);
}
}
}
}
#define COMPRULE_RULE(comprule) (((comprule) >> 12) & 0xF)
#define COMPRULE_COMPS(comprule) ((comprule) & 0xFFF)
#define COMPRULE_COMPN(comps, n) (((comps) >> 4*(n)) & 0xF)
#define COMPRULE2_RULE(comprule) (((comprule) >> (12+16)) & 0xF)
#define COMPRULE2_COMPS(comprule) ((comprule >> 16) & 0xFFF)
#define COMPRULE2_COMPN(comps, n) (((comps >> 16) >> 4*(n)) & 0xF)
bool
IsValidCompRule(int rule)
{
if(rule == 2)
return CWeather::OldWeatherType == WEATHER_RAINY ||
CWeather::NewWeatherType == WEATHER_RAINY;
return true;
}
int32
CountCompsInRule(int comps)
{
int32 n;
for(n = 0; comps != 0; comps >>= 4)
if((comps & 0xF) != 0xF)
n++;
return n;
}
int32
ChooseComponent(int32 rule, int32 comps)
{
int32 n;
switch(rule){
// identical cases....
case 1:
n = CGeneral::GetRandomNumberInRange(0, CountCompsInRule(comps));
return COMPRULE_COMPN(comps, n);
case 2:
// only valid in rain
n = CGeneral::GetRandomNumberInRange(0, CountCompsInRule(comps));
return COMPRULE_COMPN(comps, n);
}
return -1;
}
int32
GetListOfComponentsNotUsedByRules(uint32 comprules, int32 numComps, int32 *comps)
{
int32 i, n;
int32 unused[6] = { 0, 1, 2, 3, 4, 5 };
// first comprule
if(COMPRULE_RULE(comprules) && IsValidCompRule(COMPRULE_RULE(comprules)))
for(i = 0; i < 3; i++){
n = COMPRULE_COMPN(comprules, i);
if(n != 0xF)
unused[n] = 0xF;
}
// second comprule
comprules >>= 16;
if(COMPRULE_RULE(comprules) && IsValidCompRule(COMPRULE_RULE(comprules)))
for(i = 0; i < 3; i++){
n = COMPRULE_COMPN(comprules, i);
if(n != 0xF)
unused[n] = 0xF;
}
n = 0;
for(i = 0; i < numComps; i++)
if(unused[i] != 0xF)
comps[n++] = unused[i];
return n;
}
int32 wheelIds[] = { CAR_WHEEL_LF, CAR_WHEEL_LB, CAR_WHEEL_RF, CAR_WHEEL_RB };
void
CVehicleModelInfo::GetWheelPosn(int32 n, CVector &pos)
{
RwMatrix *m = RwFrameGetMatrix(GetFrameFromId(m_clump, wheelIds[n]));
pos.x = RwMatrixGetPos(m)->x;
pos.y = RwMatrixGetPos(m)->y;
pos.z = RwMatrixGetPos(m)->z;
}
int32
CVehicleModelInfo::ChooseComponent(void)
{
int32 comp;
int32 comps[8];
int32 n;
comp = -1;
if(ms_compsToUse[0] == -2){
if(COMPRULE_RULE(m_compRules) && IsValidCompRule(COMPRULE_RULE(m_compRules)))
comp = ::ChooseComponent(COMPRULE_RULE(m_compRules), COMPRULE_COMPS(m_compRules));
else if(CGeneral::GetRandomNumberInRange(0, 3) < 2){
n = GetListOfComponentsNotUsedByRules(m_compRules, m_numComps, comps);
if(n)
comp = comps[(int)CGeneral::GetRandomNumberInRange(0, n)];
}
}else{
comp = ms_compsToUse[0];
ms_compsToUse[0] = -2;
}
return comp;
}
int32
CVehicleModelInfo::ChooseSecondComponent(void)
{
int32 comp;
int32 comps[8];
int32 n;
comp = -1;
if(ms_compsToUse[1] == -2){
if(COMPRULE2_RULE(m_compRules) && IsValidCompRule(COMPRULE2_RULE(m_compRules)))
comp = ::ChooseComponent(COMPRULE2_RULE(m_compRules), COMPRULE2_COMPS(m_compRules));
else if(COMPRULE_RULE(m_compRules) && IsValidCompRule(COMPRULE_RULE(m_compRules)) &&
CGeneral::GetRandomNumberInRange(0, 3) < 2){
n = GetListOfComponentsNotUsedByRules(m_compRules, m_numComps, comps);
if(n)
comp = comps[(int)CGeneral::GetRandomNumberInRange(0, n)];
}
}else{
comp = ms_compsToUse[1];
ms_compsToUse[1] = -2;
}
return comp;
}
struct editableMatCBData
{
CVehicleModelInfo *vehicle;
int32 numMats1;
int32 numMats2;
};
RpMaterial*
CVehicleModelInfo::GetEditableMaterialListCB(RpMaterial *material, void *data)
{
static RwRGBA white = { 255, 255, 255, 255 };
RwRGBA *col;
editableMatCBData *cbdata;
cbdata = (editableMatCBData*)data;
col = RpMaterialGetColor(material);
if(col->red == 0x3C && col->green == 0xFF && col->blue == 0){
cbdata->vehicle->m_materials1[cbdata->numMats1++] = material;
RpMaterialSetColor(material, &white);
}else if(col->red == 0xFF && col->green == 0 && col->blue == 0xAF){
cbdata->vehicle->m_materials2[cbdata->numMats2++] = material;
RpMaterialSetColor(material, &white);
}
return material;
}
RpAtomic*
CVehicleModelInfo::GetEditableMaterialListCB(RpAtomic *atomic, void *data)
{
RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), GetEditableMaterialListCB, data);
return atomic;
}
void
CVehicleModelInfo::FindEditableMaterialList(void)
{
editableMatCBData cbdata;
int32 i;
cbdata.vehicle = this;
cbdata.numMats1 = 0;
cbdata.numMats2 = 0;
RpClumpForAllAtomics(m_clump, GetEditableMaterialListCB, &cbdata);
for(i = 0; i < m_numComps; i++)
GetEditableMaterialListCB(m_comps[i], &cbdata);
m_materials1[cbdata.numMats1] = nil;
m_materials2[cbdata.numMats2] = nil;
m_currentColour1 = -1;
m_currentColour2 = -1;
}
void
CVehicleModelInfo::SetVehicleColour(uint8 c1, uint8 c2)
{
RwRGBA col, *colp;
RwTexture *coltex;
RpMaterial **matp;
if(c1 != m_currentColour1){
col = ms_vehicleColourTable[c1];
coltex = ms_colourTextureTable[c1];
for(matp = m_materials1; *matp; matp++){
if(RpMaterialGetTexture(*matp) && RpMaterialGetTexture(*matp)->name[0] != '@'){
colp = RpMaterialGetColor(*matp);
colp->red = col.red;
colp->green = col.green;
colp->blue = col.blue;
}else
RpMaterialSetTexture(*matp, coltex);
}
m_currentColour1 = c1;
}
if(c2 != m_currentColour2){
col = ms_vehicleColourTable[c2];
coltex = ms_colourTextureTable[c2];
for(matp = m_materials2; *matp; matp++){
if(RpMaterialGetTexture(*matp) && RpMaterialGetTexture(*matp)->name[0] != '@'){
colp = RpMaterialGetColor(*matp);
colp->red = col.red;
colp->green = col.green;
colp->blue = col.blue;
}else
RpMaterialSetTexture(*matp, coltex);
}
m_currentColour2 = c2;
}
}
RpMaterial*
CVehicleModelInfo::HasSpecularMaterialCB(RpMaterial *material, void *data)
{
if(RpMaterialGetSurfaceProperties(material)->specular <= 0.0f)
return material;
*(bool*)data = true;
return nil;
}
RpMaterial*
CVehicleModelInfo::SetEnvironmentMapCB(RpMaterial *material, void *data)
{
float spec;
spec = RpMaterialGetSurfaceProperties(material)->specular;
if(spec <= 0.0f)
RpMatFXMaterialSetEffects(material, rpMATFXEFFECTNULL);
else{
if(RpMaterialGetTexture(material) == 0)
RpMaterialSetTexture(material, gpWhiteTexture);
RpMatFXMaterialSetEffects(material, rpMATFXEFFECTENVMAP);
spec *= 0.5f; // Tone down a bit for PC
RpMatFXMaterialSetupEnvMap(material, (RwTexture*)data, pMatFxIdentityFrame, false, spec);
}
return material;
}
RpAtomic*
CVehicleModelInfo::SetEnvironmentMapCB(RpAtomic *atomic, void *data)
{
bool hasSpec;
RpGeometry *geo;
geo = RpAtomicGetGeometry(atomic);
hasSpec = 0;
RpGeometryForAllMaterials(geo, HasSpecularMaterialCB, &hasSpec);
if(hasSpec){
RpGeometryForAllMaterials(geo, SetEnvironmentMapCB, data);
RpGeometrySetFlags(geo, RpGeometryGetFlags(geo) | rpGEOMETRYMODULATEMATERIALCOLOR);
RpMatFXAtomicEnableEffects(atomic);
// PS2 sets of PS2Manager lighting CB here
}
return atomic;
}
void
CVehicleModelInfo::SetEnvironmentMap(void)
{
CSimpleModelInfo *wheelmi;
int32 i;
if(pMatFxIdentityFrame == nil){
pMatFxIdentityFrame = RwFrameCreate();
RwMatrixSetIdentity(RwFrameGetMatrix(pMatFxIdentityFrame));
RwFrameUpdateObjects(pMatFxIdentityFrame);
RwFrameGetLTM(pMatFxIdentityFrame);
}
if(m_envMap != ms_pEnvironmentMaps[0]){
m_envMap = ms_pEnvironmentMaps[0];
RpClumpForAllAtomics(m_clump, SetEnvironmentMapCB, m_envMap);
if(m_wheelId != -1){
wheelmi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(m_wheelId);
for(i = 0; i < wheelmi->m_numAtomics; i++)
SetEnvironmentMapCB(wheelmi->m_atomics[i], m_envMap);
}
}
}
void
CVehicleModelInfo::LoadEnvironmentMaps(void)
{
char *texnames[] = {
"reflection01", // only one used
"reflection02",
"reflection03",
"reflection04",
"reflection05",
"reflection06",
};
int32 txdslot;
int32 i;
txdslot = CTxdStore::FindTxdSlot("particle");
CTxdStore::PushCurrentTxd();
CTxdStore::SetCurrentTxd(txdslot);
for(i = 0; i < NUM_VEHICLE_ENVMAPS; i++){
ms_pEnvironmentMaps[i] = RwTextureRead(texnames[i], nil);
RwTextureSetFilterMode(ms_pEnvironmentMaps[i], rwFILTERLINEAR);
}
if(gpWhiteTexture == nil){
gpWhiteTexture = RwTextureRead("white", nil);
gpWhiteTexture->name[0] = '@';
RwTextureSetFilterMode(gpWhiteTexture, rwFILTERLINEAR);
}
CTxdStore::PopCurrentTxd();
}
void
CVehicleModelInfo::ShutdownEnvironmentMaps(void)
{
int32 i;
// ignoring "initialised" as that's a PS2 thing only
RwTextureDestroy(gpWhiteTexture);
gpWhiteTexture = nil;
for(i = 0; i < NUM_VEHICLE_ENVMAPS; i++)
if(ms_pEnvironmentMaps[i])
RwTextureDestroy(ms_pEnvironmentMaps[i]);
RwFrameDestroy(pMatFxIdentityFrame);
pMatFxIdentityFrame = nil;
}
STARTPATCHES
InjectHook(0x51FDC0, &CVehicleModelInfo::DeleteRwObject_, PATCH_JUMP);
InjectHook(0x51FCB0, &CVehicleModelInfo::CreateInstance_, PATCH_JUMP);
InjectHook(0x51FC60, &CVehicleModelInfo::SetClump_, PATCH_JUMP);
InjectHook(0x51FE10, &CVehicleModelInfo::CollapseFramesCB, PATCH_JUMP);
InjectHook(0x51FE50, &CVehicleModelInfo::MoveObjectsCB, PATCH_JUMP);
InjectHook(0x51FE70, &CVehicleModelInfo::HideDamagedAtomicCB, PATCH_JUMP);
InjectHook(0x51FEF0, &CVehicleModelInfo::HasAlphaMaterialCB, PATCH_JUMP);
InjectHook(0x51FF10, &CVehicleModelInfo::SetAtomicRendererCB, PATCH_JUMP);
InjectHook(0x520030, &CVehicleModelInfo::SetAtomicRendererCB_BigVehicle, PATCH_JUMP);
InjectHook(0x520230, &CVehicleModelInfo::SetAtomicRendererCB_Train, PATCH_JUMP);
InjectHook(0x520120, &CVehicleModelInfo::SetAtomicRendererCB_Boat, PATCH_JUMP);
InjectHook(0x520210, &CVehicleModelInfo::SetAtomicRendererCB_Heli, PATCH_JUMP);
InjectHook(0x5202C0, &CVehicleModelInfo::SetAtomicRenderCallbacks, PATCH_JUMP);
InjectHook(0x520340, &CVehicleModelInfo::SetAtomicFlagCB, PATCH_JUMP);
InjectHook(0x520360, &CVehicleModelInfo::ClearAtomicFlagCB, PATCH_JUMP);
InjectHook(0x5204D0, &CVehicleModelInfo::PreprocessHierarchy, PATCH_JUMP);
InjectHook(0x520840, &CVehicleModelInfo::GetWheelPosn, PATCH_JUMP);
InjectHook(0x520880, IsValidCompRule, PATCH_JUMP);
InjectHook(0x520990, CountCompsInRule, PATCH_JUMP);
InjectHook(0x5209C0, ChooseComponent, PATCH_JUMP);
InjectHook(0x5208C0, GetListOfComponentsNotUsedByRules, PATCH_JUMP);
InjectHook(0x520AB0, &CVehicleModelInfo::ChooseComponent, PATCH_JUMP);
InjectHook(0x520BE0, &CVehicleModelInfo::ChooseSecondComponent, PATCH_JUMP);
InjectHook(0x520DC0, (RpAtomic *(*)(RpAtomic*, void*))CVehicleModelInfo::GetEditableMaterialListCB, PATCH_JUMP);
InjectHook(0x520D30, (RpMaterial *(*)(RpMaterial*, void*))CVehicleModelInfo::GetEditableMaterialListCB, PATCH_JUMP);
InjectHook(0x520DE0, &CVehicleModelInfo::FindEditableMaterialList, PATCH_JUMP);
InjectHook(0x520E70, &CVehicleModelInfo::SetVehicleColour, PATCH_JUMP);
InjectHook(0x521820, (RpAtomic *(*)(RpAtomic*, void*))CVehicleModelInfo::SetEnvironmentMapCB, PATCH_JUMP);
InjectHook(0x5217A0, (RpMaterial *(*)(RpMaterial*, void*))CVehicleModelInfo::SetEnvironmentMapCB, PATCH_JUMP);
InjectHook(0x521770, CVehicleModelInfo::HasSpecularMaterialCB, PATCH_JUMP);
InjectHook(0x521890, &CVehicleModelInfo::SetEnvironmentMap, PATCH_JUMP);
InjectHook(0x521680, CVehicleModelInfo::LoadEnvironmentMaps, PATCH_JUMP);
InjectHook(0x521720, CVehicleModelInfo::ShutdownEnvironmentMaps, PATCH_JUMP);
ENDPATCHES

View file

@ -0,0 +1,115 @@
#pragma once
#include "ClumpModelInfo.h"
enum {
NUM_VEHICLE_POSITIONS = 10,
NUM_FIRST_MATERIALS = 26,
NUM_SECOND_MATERIALS = 26,
NUM_VEHICLE_COLOURS = 8,
NUM_VEHICLE_ENVMAPS = 1
};
enum {
ATOMIC_FLAG_OK = 0x1,
ATOMIC_FLAG_DAM = 0x2,
ATOMIC_FLAG_LEFT = 0x4,
ATOMIC_FLAG_RIGHT = 0x8,
ATOMIC_FLAG_FRONT = 0x10,
ATOMIC_FLAG_REAR = 0x20,
ATOMIC_FLAG_DRAWLAST = 0x40,
ATOMIC_FLAG_WINDSCREEN = 0x80,
ATOMIC_FLAG_ANGLECULL = 0x100,
ATOMIC_FLAG_REARDOOR = 0x200,
ATOMIC_FLAG_FRONTDOOR = 0x400,
ATOMIC_FLAG_NOCULL = 0x800,
};
enum {
VEHICLE_TYPE_CAR,
VEHICLE_TYPE_BOAT,
VEHICLE_TYPE_TRAIN,
VEHICLE_TYPE_HELI,
VEHICLE_TYPE_PLANE,
VEHICLE_TYPE_BIKE,
NUM_VEHICLE_TYPES
};
class CVehicleModelInfo : public CClumpModelInfo
{
public:
uint8 m_lastColour1;
uint8 m_lastColour2;
char m_gameName[32];
int32 m_vehicleType;
int32 m_wheelId;
float m_wheelScale;
int32 m_numDoors;
int32 m_handlingId;
int32 m_vehicleClass;
int32 m_level;
CVector m_positions[NUM_VEHICLE_POSITIONS];
uint32 m_compRules;
float m_bikeSteerAngle;
RpMaterial *m_materials1[NUM_FIRST_MATERIALS];
RpMaterial *m_materials2[NUM_SECOND_MATERIALS];
uint8 m_colours1[NUM_VEHICLE_COLOURS];
uint8 m_colours2[NUM_VEHICLE_COLOURS];
uint8 m_numColours;
uint8 m_bLastColorVariation; //
uint8 m_currentColour1;
uint8 m_currentColour2;
RwTexture *m_envMap;
RpAtomic *m_comps[6];
int32 m_numComps;
static int8 *ms_compsToUse; // [2];
static int8 *ms_compsUsed; // [2];
static RwTexture **ms_pEnvironmentMaps; // [NUM_VEHICLE_ENVMAPS]
static RwRGBA *ms_vehicleColourTable; // [256]
static RwTexture **ms_colourTextureTable; // [256]
static RwObjectNameIdAssocation *ms_vehicleDescs[NUM_VEHICLE_TYPES];
CVehicleModelInfo(void);
void DeleteRwObject(void);
RwObject *CreateInstance(void);
void SetClump(RpClump *);
static RwFrame *CollapseFramesCB(RwFrame *frame, void *data);
static RwObject *MoveObjectsCB(RwObject *object, void *data);
static RpAtomic *HideDamagedAtomicCB(RpAtomic *atomic, void *data);
static RpMaterial *HasAlphaMaterialCB(RpMaterial *material, void *data);
static RpAtomic *SetAtomicRendererCB(RpAtomic *atomic, void *data);
static RpAtomic *SetAtomicRendererCB_BigVehicle(RpAtomic *atomic, void *data);
static RpAtomic *SetAtomicRendererCB_Train(RpAtomic *atomic, void *data);
static RpAtomic *SetAtomicRendererCB_Boat(RpAtomic *atomic, void *data);
static RpAtomic *SetAtomicRendererCB_Heli(RpAtomic *atomic, void *data);
void SetAtomicRenderCallbacks(void);
static RpAtomic *SetAtomicFlagCB(RpAtomic *atomic, void *data);
static RpAtomic *ClearAtomicFlagCB(RpAtomic *atomic, void *data);
void SetVehicleComponentFlags(RwFrame *frame, uint32 flags);
void PreprocessHierarchy(void);
void GetWheelPosn(int32 n, CVector &pos);
int32 ChooseComponent(void);
int32 ChooseSecondComponent(void);
static RpMaterial *GetEditableMaterialListCB(RpMaterial *material, void *data);
static RpAtomic *GetEditableMaterialListCB(RpAtomic *atomic, void *data);
void FindEditableMaterialList(void);
void SetVehicleColour(uint8 c1, uint8 c2);
static RpAtomic *SetEnvironmentMapCB(RpAtomic *atomic, void *data);
static RpMaterial *SetEnvironmentMapCB(RpMaterial *material, void *data);
static RpMaterial *HasSpecularMaterialCB(RpMaterial *material, void *data);
void SetEnvironmentMap(void);
static void LoadEnvironmentMaps(void);
static void ShutdownEnvironmentMaps(void);
void DeleteRwObject_(void) { this->CVehicleModelInfo::DeleteRwObject(); }
RwObject *CreateInstance_(void) { return this->CVehicleModelInfo::CreateInstance(); }
void SetClump_(RpClump *clump) { this->CVehicleModelInfo::SetClump(clump); }
};
static_assert(sizeof(CVehicleModelInfo) == 0x1F8, "CVehicleModelInfo: error");

22
src/patcher.cpp Normal file
View file

@ -0,0 +1,22 @@
#include "common.h"
#include "patcher.h"
StaticPatcher *StaticPatcher::ms_head;
StaticPatcher::StaticPatcher(Patcher func)
: m_func(func)
{
m_next = ms_head;
ms_head = this;
}
void
StaticPatcher::Apply()
{
StaticPatcher *current = ms_head;
while(current){
current->Run();
current = current->m_next;
}
ms_head = nil;
}

171
src/patcher.h Normal file
View file

@ -0,0 +1,171 @@
#pragma once
#define WRAPPER __declspec(naked)
#define DEPRECATED __declspec(deprecated)
#define EAXJMP(a) { _asm mov eax, a _asm jmp eax }
#define VARJMP(a) { _asm jmp a }
#define WRAPARG(a) UNREFERENCED_PARAMETER(a)
#define NOVMT __declspec(novtable)
#define SETVMT(a) *((DWORD_PTR*)this) = (DWORD_PTR)a
enum
{
PATCH_CALL,
PATCH_JUMP,
PATCH_NOTHING,
};
enum
{
III_10 = 1,
III_11,
III_STEAM,
VC_10,
VC_11,
VC_STEAM
};
extern int gtaversion;
template<typename T>
inline T AddressByVersion(uint32_t addressIII10, uint32_t addressIII11, uint32_t addressIIISteam, uint32_t addressvc10, uint32_t addressvc11, uint32_t addressvcSteam)
{
if(gtaversion == -1){
if(*(uint32_t*)0x5C1E75 == 0xB85548EC) gtaversion = III_10;
else if(*(uint32_t*)0x5C2135 == 0xB85548EC) gtaversion = III_11;
else if(*(uint32_t*)0x5C6FD5 == 0xB85548EC) gtaversion = III_STEAM;
else if(*(uint32_t*)0x667BF5 == 0xB85548EC) gtaversion = VC_10;
else if(*(uint32_t*)0x667C45 == 0xB85548EC) gtaversion = VC_11;
else if(*(uint32_t*)0x666BA5 == 0xB85548EC) gtaversion = VC_STEAM;
else gtaversion = 0;
}
switch(gtaversion){
case III_10:
return (T)addressIII10;
case III_11:
return (T)addressIII11;
case III_STEAM:
return (T)addressIIISteam;
case VC_10:
return (T)addressvc10;
case VC_11:
return (T)addressvc11;
case VC_STEAM:
return (T)addressvcSteam;
default:
return (T)0;
}
}
inline bool
is10(void)
{
return gtaversion == III_10 || gtaversion == VC_10;
}
inline bool
isIII(void)
{
return gtaversion >= III_10 && gtaversion <= III_STEAM;
}
inline bool
isVC(void)
{
return gtaversion >= VC_10 && gtaversion <= VC_STEAM;
}
#define PTRFROMCALL(addr) (uint32_t)(*(uint32_t*)((uint32_t)addr+1) + (uint32_t)addr + 5)
#define INTERCEPT(saved, func, a) \
{ \
saved = PTRFROMCALL(a); \
InjectHook(a, func); \
}
template<typename T, typename AT> inline void
Patch(AT address, T value)
{
DWORD dwProtect[2];
VirtualProtect((void*)address, sizeof(T), PAGE_EXECUTE_READWRITE, &dwProtect[0]);
*(T*)address = value;
VirtualProtect((void*)address, sizeof(T), dwProtect[0], &dwProtect[1]);
}
template<typename AT> inline void
Nop(AT address, unsigned int nCount)
{
DWORD dwProtect[2];
VirtualProtect((void*)address, nCount, PAGE_EXECUTE_READWRITE, &dwProtect[0]);
memset((void*)address, 0x90, nCount);
VirtualProtect((void*)address, nCount, dwProtect[0], &dwProtect[1]);
}
template<typename AT, typename HT> inline void
InjectHook(AT address, HT hook, unsigned int nType=PATCH_NOTHING)
{
DWORD dwProtect[2];
switch ( nType )
{
case PATCH_JUMP:
VirtualProtect((void*)address, 5, PAGE_EXECUTE_READWRITE, &dwProtect[0]);
*(BYTE*)address = 0xE9;
break;
case PATCH_CALL:
VirtualProtect((void*)address, 5, PAGE_EXECUTE_READWRITE, &dwProtect[0]);
*(BYTE*)address = 0xE8;
break;
default:
VirtualProtect((void*)((DWORD)address + 1), 4, PAGE_EXECUTE_READWRITE, &dwProtect[0]);
break;
}
DWORD dwHook;
_asm
{
mov eax, hook
mov dwHook, eax
}
*(ptrdiff_t*)((DWORD)address + 1) = (DWORD)dwHook - (DWORD)address - 5;
if ( nType == PATCH_NOTHING )
VirtualProtect((void*)((DWORD)address + 1), 4, dwProtect[0], &dwProtect[1]);
else
VirtualProtect((void*)address, 5, dwProtect[0], &dwProtect[1]);
}
inline void ExtractCall(void *dst, uint32_t a)
{
*(uint32_t*)dst = (uint32_t)(*(uint32_t*)(a+1) + a + 5);
}
template<typename T>
inline void InterceptCall(void *dst, T func, uint32_t a)
{
ExtractCall(dst, a);
InjectHook(a, func);
}
template<typename T>
inline void InterceptVmethod(void *dst, T func, uint32_t a)
{
*(uint32_t*)dst = *(uint32_t*)a;
Patch(a, func);
}
class StaticPatcher
{
private:
using Patcher = void(*)();
Patcher m_func;
StaticPatcher *m_next;
static StaticPatcher *ms_head;
void Run() { m_func(); }
public:
StaticPatcher(Patcher func);
static void Apply();
};
#define STARTPATCHES static StaticPatcher Patcher([](){
#define ENDPATCHES });

39
src/render/2dEffect.h Normal file
View file

@ -0,0 +1,39 @@
class C2dEffect
{
public:
struct Light {
float dist;
float outerRange;
float size;
float innerRange;
uint8 flash;
uint8 wet;
uint8 flare;
uint8 shadowIntens;
uint8 flag;
RwTexture *corona;
RwTexture *shadow;
};
struct Particle {
int particleType;
float dir[3];
float scale;
};
struct Attractor {
CVector dir;
uint8 flag;
uint8 probability;
};
CVector pos;
RwRGBA col;
uint8 type;
union {
Light light;
Particle particle;
Attractor attractor;
};
C2dEffect(void) {}
};
static_assert(sizeof(C2dEffect) == 0x34, "C2dEffect: error");

430
src/render/Clouds.cpp Normal file
View file

@ -0,0 +1,430 @@
#include "common.h"
#include "patcher.h"
#include "Sprite.h"
#include "General.h"
#include "Coronas.h"
#include "Camera.h"
#include "TxdStore.h"
#include "Weather.h"
#include "Clock.h"
#include "Timer.h"
#include "Timecycle.h"
#include "Renderer.h"
#include "Clouds.h"
#define SMALLSTRIPHEIGHT 4.0f
#define HORIZSTRIPHEIGHT 48.0f
RwTexture **gpCloudTex = (RwTexture**)0x9411C0; //[5];
float &CClouds::CloudRotation = *(float*)0x8F5F40;
uint32 &CClouds::IndividualRotation = *(uint32*)0x943078;
float &CClouds::ms_cameraRoll = *(float*)0x8F29CC;
float &CClouds::ms_horizonZ = *(float*)0x8F31C0;
CRGBA &CClouds::ms_colourTop = *(CRGBA*)0x94143C;
CRGBA &CClouds::ms_colourBottom = *(CRGBA*)0x8F2C38;
void
CClouds::Init(void)
{
CTxdStore::PushCurrentTxd();
CTxdStore::SetCurrentTxd(CTxdStore::FindTxdSlot("particle"));
gpCloudTex[0] = RwTextureRead("cloud1", nil);
gpCloudTex[1] = RwTextureRead("cloud2", nil);
gpCloudTex[2] = RwTextureRead("cloud3", nil);
gpCloudTex[3] = RwTextureRead("cloudhilit", nil);
gpCloudTex[4] = RwTextureRead("cloudmasked", nil);
CTxdStore::PopCurrentTxd();
CloudRotation = 0.0f;
}
void
CClouds::Update(void)
{
float s = sin(TheCamera.Orientation - 0.85f);
CloudRotation += CWeather::Wind*s*0.0025f;
IndividualRotation += (CWeather::Wind*CTimer::GetTimeStep() + 0.3f) * 60.0f;
}
void
CClouds::Render(void)
{
int i;
float szx, szy;
RwV3d screenpos;
RwV3d worldpos;
CCoronas::SunBlockedByClouds = false;
RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE);
RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
CSprite::InitSpriteBuffer();
int minute = CClock::GetHours()*60 + CClock::GetMinutes();
RwV3d campos = *(RwV3d*)&TheCamera.GetPosition();
float coverage = CWeather::CloudCoverage <= CWeather::Foggyness ? CWeather::Foggyness : CWeather::CloudCoverage;
// Moon
int moonfadeout = abs(minute - 180); // fully visible at 3AM
if(moonfadeout < 180){ // fade in/out 3 hours
int brightness = (1.0f - coverage) * (180 - moonfadeout);
RwV3d pos = { 0.0f, -100.0f, 15.0f };
RwV3dAdd(&worldpos, &campos, &pos);
if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){
RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpCoronaTexture[2]->raster);
if(CCoronas::bSmallMoon){
szx *= 4.0f;
szy *= 4.0f;
}else{
szx *= 10.0f;
szy *= 10.0f;
}
CSprite::RenderOneXLUSprite(screenpos.x, screenpos.y, screenpos.z,
szx, szy, brightness, brightness, brightness, 255, 1.0f/screenpos.z, 255);
}
}
// The R* logo
int starintens = 0;
if(CClock::GetHours() < 22 && CClock::GetHours() > 5)
starintens = 0;
else if(CClock::GetHours() > 22 || CClock::GetHours() < 5)
starintens = 255;
else if(CClock::GetHours() == 22)
starintens = 255 * CClock::GetMinutes()/60.0f;
else if(CClock::GetHours() == 5)
starintens = 255 * (60 - CClock::GetMinutes())/60.0f;
if(starintens != 0){
// R
static float StarCoorsX[9] = { 0.0f, 0.05f, 0.12f, 0.5f, 0.8f, 0.6f, 0.27f, 0.55f, 0.75f };
static float StarCoorsY[9] = { 0.0f, 0.45f, 0.9f, 1.0f, 0.85f, 0.52f, 0.48f, 0.35f, 0.2f };
static float StarSizes[9] = { 1.0f, 1.4f, 0.9f, 1.0f, 0.6f, 1.5f, 1.3f, 1.0f, 0.8f };
int brightness = (1.0f - coverage) * starintens;
RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpCoronaTexture[0]->raster);
for(i = 0; i < 11; i++){
RwV3d pos = { 100.0f, 0.0f, 10.0f };
if(i >= 9) pos.x = -pos.x;
RwV3dAdd(&worldpos, &campos, &pos);
worldpos.y -= 90.0f*StarCoorsX[i%9];
worldpos.z += 80.0f*StarCoorsY[i%9];
if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){
float sz = 0.8f*StarSizes[i%9];
CSprite::RenderBufferedOneXLUSprite(screenpos.x, screenpos.y, screenpos.z,
szx*sz, szy*sz, brightness, brightness, brightness, 255, 1.0f/screenpos.z, 255);
}
}
CSprite::FlushSpriteBuffer();
// *
RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpCoronaTexture[0]->raster);
RwV3d pos = { 100.0f, 0.0f, 10.0f };
RwV3dAdd(&worldpos, &campos, &pos);
worldpos.y -= 90.0f;
if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){
brightness *= (CGeneral::GetRandomNumber()&127) / 640.0f + 0.5f;
CSprite::RenderOneXLUSprite(screenpos.x, screenpos.y, screenpos.z,
szx*5.0f, szy*5.0f, brightness, brightness, brightness, 255, 1.0f/screenpos.z, 255);
}
}
// Low clouds
static float LowCloudsX[12] = { 1.0f, 0.7f, 0.0f, -0.7f, -1.0f, -0.7f,
0.0f, 0.7f, 0.8f, -0.8f, 0.4f, -0.4f };
static float LowCloudsY[12] = { 0.0f, -0.7f, -1.0f, -0.7f, 0.0f, 0.7f,
1.0f, 0.7f, 0.4f, 0.4f, -0.8f, -0.8f };
static float LowCloudsZ[12] = { 0.0f, 1.0f, 0.5f, 0.0f, 1.0f, 0.3f,
0.9f, 0.4f, 1.3f, 1.4f, 1.2f, 1.7f };
float lowcloudintensity = 1.0f - coverage;
int r = CTimeCycle::GetLowCloudsRed() * lowcloudintensity;
int g = CTimeCycle::GetLowCloudsGreen() * lowcloudintensity;
int b = CTimeCycle::GetLowCloudsBlue() * lowcloudintensity;
for(int cloudtype = 0; cloudtype < 3; cloudtype++){
for(i = cloudtype; i < 12; i += 3){
RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpCloudTex[cloudtype]->raster);
RwV3d pos = { 800.0f*LowCloudsX[i], 800.0f*LowCloudsY[i], 60.0f*LowCloudsZ[i] };
worldpos.x = campos.x + pos.x;
worldpos.y = campos.y + pos.y;
worldpos.z = 40.0f + pos.z;
if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false))
CSprite::RenderBufferedOneXLUSprite_Rotate_Dimension(screenpos.x, screenpos.y, screenpos.z,
szx*320.0f, szy*40.0f, r, g, b, 255, 1.0f/screenpos.z, ms_cameraRoll, 255);
}
CSprite::FlushSpriteBuffer();
}
// Fluffy clouds
float rot_sin = sin(CloudRotation);
float rot_cos = cos(CloudRotation);
int fluffyalpha = 160 * (1.0f - CWeather::Foggyness);
if(fluffyalpha != 0){
static float CoorsOffsetX[37] = {
0.0f, 60.0f, 72.0f, 48.0f, 21.0f, 12.0f,
9.0f, -3.0f, -8.4f, -18.0f, -15.0f, -36.0f,
-40.0f, -48.0f, -60.0f, -24.0f, 100.0f, 100.0f,
100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f,
100.0f, 100.0f, -30.0f, -20.0f, 10.0f, 30.0f,
0.0f, -100.0f, -100.0f, -100.0f, -100.0f, -100.0f, -100.0f
};
static float CoorsOffsetY[37] = {
100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f,
100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f,
100.0f, 100.0f, 100.0f, 100.0f, -30.0f, 10.0f,
-25.0f, -5.0f, 28.0f, -10.0f, 10.0f, 0.0f,
15.0f, 40.0f, -100.0f, -100.0f, -100.0f, -100.0f,
-100.0f, -40.0f, -20.0f, 0.0f, 10.0f, 30.0f, 35.0f
};
static float CoorsOffsetZ[37] = {
2.0f, 1.0f, 0.0f, 0.3f, 0.7f, 1.4f,
1.7f, 0.24f, 0.7f, 1.3f, 1.6f, 1.0f,
1.2f, 0.3f, 0.7f, 1.4f, 0.0f, 0.1f,
0.5f, 0.4f, 0.55f, 0.75f, 1.0f, 1.4f,
1.7f, 2.0f, 2.0f, 2.3f, 1.9f, 2.4f,
2.0f, 2.0f, 1.5f, 1.2f, 1.7f, 1.5f, 2.1f
};
static bool bCloudOnScreen[37];
float hilight;
RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpCloudTex[4]->raster);
for(i = 0; i < 37; i++){
RwV3d pos = { 2.0f*CoorsOffsetX[i], 2.0f*CoorsOffsetY[i], 40.0f*CoorsOffsetZ[i] + 40.0f };
worldpos.x = pos.x*rot_cos + pos.y*rot_sin + campos.x;
worldpos.y = pos.x*rot_sin - pos.y*rot_cos + campos.y;
worldpos.z = pos.z;
if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){
float sundist = sqrt(sq(screenpos.x-CCoronas::SunScreenX) + sq(screenpos.y-CCoronas::SunScreenY));
int tr = CTimeCycle::GetFluffyCloudsTopRed();
int tg = CTimeCycle::GetFluffyCloudsTopGreen();
int tb = CTimeCycle::GetFluffyCloudsTopBlue();
int br = CTimeCycle::GetFluffyCloudsBottomRed();
int bg = CTimeCycle::GetFluffyCloudsBottomGreen();
int bb = CTimeCycle::GetFluffyCloudsBottomBlue();
if(sundist < SCREENW/2){
hilight = (1.0f - coverage) * (1.0f - sundist/(SCREENW/2));
tr = tr*(1.0f-hilight) + 255*hilight;
tg = tg*(1.0f-hilight) + 190*hilight;
tb = tb*(1.0f-hilight) + 190*hilight;
br = br*(1.0f-hilight) + 255*hilight;
bg = bg*(1.0f-hilight) + 190*hilight;
bb = bb*(1.0f-hilight) + 190*hilight;
if(sundist < SCREENW/10)
CCoronas::SunBlockedByClouds = true;
}else
hilight = 0.0f;
CSprite::RenderBufferedOneXLUSprite_Rotate_2Colours(screenpos.x, screenpos.y, screenpos.z,
szx*55.0f, szy*55.0f,
tr, tg, tb, br, bg, bb, 0.0f, -1.0f,
1.0f/screenpos.z,
IndividualRotation/65336.0f * 2*3.14f + ms_cameraRoll,
fluffyalpha);
bCloudOnScreen[i] = true;
}else
bCloudOnScreen[i] = false;
}
CSprite::FlushSpriteBuffer();
// Highlights
RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE);
RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpCloudTex[3]->raster);
for(i = 0; i < 37; i++){
RwV3d pos = { 2.0f*CoorsOffsetX[i], 2.0f*CoorsOffsetY[i], 40.0f*CoorsOffsetZ[i] + 40.0f };
worldpos.x = campos.x*rot_cos + campos.y*rot_sin + pos.x;
worldpos.y = campos.x*rot_sin + campos.y*rot_cos + pos.y;
worldpos.z = pos.z;
if(bCloudOnScreen[i] && CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false)){
// BUG: this is stupid....would have to do this for each cloud individually
if(hilight > 0.0f){
CSprite::RenderBufferedOneXLUSprite_Rotate_Aspect(screenpos.x, screenpos.y, screenpos.z,
szx*30.0f, szy*30.0f,
200*hilight, 0, 0, 255, 1.0f/screenpos.z,
1.7f - CGeneral::GetATanOfXY(screenpos.x-CCoronas::SunScreenX, screenpos.y-CCoronas::SunScreenY) + CClouds::ms_cameraRoll, 255);
}
}
}
CSprite::FlushSpriteBuffer();
}
// Rainbow
if(CWeather::Rainbow != 0.0f){
static uint8 BowRed[6] = { 30, 30, 30, 10, 0, 15 };
static uint8 BowGreen[6] = { 0, 15, 30, 30, 0, 0 };
static uint8 BowBlue[6] = { 0, 0, 0, 10, 30, 30 };
RwRenderStateSet(rwRENDERSTATETEXTURERASTER, gpCoronaTexture[0]->raster);
for(i = 0; i < 6; i++){
RwV3d pos = { i*1.5f, 100.0f, 5.0f };
RwV3dAdd(&worldpos, &campos, &pos);
if(CSprite::CalcScreenCoors(worldpos, &screenpos, &szx, &szy, false))
CSprite::RenderBufferedOneXLUSprite(screenpos.x, screenpos.y, screenpos.z,
2.0f*szx, 50.0*szy,
BowRed[i]*CWeather::Rainbow, BowGreen[i]*CWeather::Rainbow, BowBlue[i]*CWeather::Rainbow,
255, 1.0f/screenpos.z, 255);
}
CSprite::FlushSpriteBuffer();
}
RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
}
bool
UseDarkBackground(void)
{
return RwFrameGetLTM(RwCameraGetFrame(TheCamera.m_pRwCamera))->up.z < -0.9f ||
gbShowCollisionPolys;
}
void
CClouds::RenderBackground(int16 topred, int16 topgreen, int16 topblue,
int16 botred, int16 botgreen, int16 botblue, int16 alpha)
{
RwMatrix *mat = RwFrameGetLTM(RwCameraGetFrame(TheCamera.m_pRwCamera));
float c = sqrt(mat->right.x * mat->right.x + mat->right.y * mat->right.y);
if(c > 1.0f)
c = 1.0f;
ms_cameraRoll = acos(c);
if(mat->right.z < 0.0f)
ms_cameraRoll = -ms_cameraRoll;
if(UseDarkBackground()){
ms_colourTop.r = 50;
ms_colourTop.g = 50;
ms_colourTop.b = 50;
ms_colourTop.a = 255;
if(gbShowCollisionPolys){
if(CTimer::GetFrameCounter() & 1){
ms_colourTop.r = 0;
ms_colourTop.g = 0;
ms_colourTop.b = 0;
}else{
ms_colourTop.r = 255;
ms_colourTop.g = 255;
ms_colourTop.b = 255;
}
}
ms_colourBottom = ms_colourTop;
CRect r(0, 0, SCREENW, SCREENH);
CSprite2d::DrawRect(r, ms_colourBottom, ms_colourBottom, ms_colourTop, ms_colourTop);
}else{
ms_horizonZ = CSprite::CalcHorizonCoors();
// Draw top/bottom gradient
float gradheight = SCREENH/2.0f;
float topedge = ms_horizonZ - gradheight;
float botpos, toppos;
if(ms_horizonZ > 0.0f && topedge < SCREENH){
ms_colourTop.r = topred;
ms_colourTop.g = topgreen;
ms_colourTop.b = topblue;
ms_colourTop.a = alpha;
ms_colourBottom.r = botred;
ms_colourBottom.g = botgreen;
ms_colourBottom.b = botblue;
ms_colourBottom.a = alpha;
if(ms_horizonZ < SCREENH)
botpos = ms_horizonZ;
else{
float f = (ms_horizonZ - SCREENH)/gradheight;
ms_colourBottom.r = topred*f + (1.0f-f)*botred;
ms_colourBottom.g = topgreen*f + (1.0f-f)*botgreen;
ms_colourBottom.b = topblue*f + (1.0f-f)*botblue;
botpos = SCREENH;
}
if(topedge >= 0.0f)
toppos = topedge;
else{
float f = (0.0f - topedge)/gradheight;
ms_colourTop.r = botred*f + (1.0f-f)*topred;
ms_colourTop.g = botgreen*f + (1.0f-f)*topgreen;
ms_colourTop.b = botblue*f + (1.0f-f)*topblue;
toppos = 0.0f;
}
CSprite2d::DrawRect(CRect(0, toppos, SCREENW, botpos),
ms_colourBottom, ms_colourBottom, ms_colourTop, ms_colourTop);
}
// draw the small stripe (whatever it's supposed to be)
if(ms_horizonZ > -SMALLSTRIPHEIGHT && ms_horizonZ < SCREENH){
// Same colour as fog
ms_colourTop.r = (topred + 2 * botred) / 3;
ms_colourTop.g = (topgreen + 2 * botgreen) / 3;
ms_colourTop.b = (topblue + 2 * botblue) / 3;
CSprite2d::DrawRect(CRect(0, ms_horizonZ, SCREENW, ms_horizonZ+SMALLSTRIPHEIGHT),
ms_colourTop, ms_colourTop, ms_colourTop, ms_colourTop);
}
// Only top
if(topedge > 0.0f){
ms_colourTop.r = topred;
ms_colourTop.g = topgreen;
ms_colourTop.b = topblue;
ms_colourTop.a = alpha;
ms_colourBottom.r = topred;
ms_colourBottom.g = topgreen;
ms_colourBottom.b = topblue;
ms_colourBottom.a = alpha;
botpos = min(SCREENH, topedge);
CSprite2d::DrawRect(CRect(0, 0, SCREENW, botpos),
ms_colourBottom, ms_colourBottom, ms_colourTop, ms_colourTop);
}
// Set both to fog colour for RenderHorizon
ms_colourTop.r = (topred + 2 * botred) / 3;
ms_colourTop.g = (topgreen + 2 * botgreen) / 3;
ms_colourTop.b = (topblue + 2 * botblue) / 3;
ms_colourBottom.r = (topred + 2 * botred) / 3;
ms_colourBottom.g = (topgreen + 2 * botgreen) / 3;
ms_colourBottom.b = (topblue + 2 * botblue) / 3;
}
}
void
CClouds::RenderHorizon(void)
{
if(UseDarkBackground())
return;
ms_colourBottom.a = 230;
ms_colourTop.a = 80;
if(ms_horizonZ > SCREENH)
return;
float z1 = min(ms_horizonZ + SMALLSTRIPHEIGHT, SCREENH);
CSprite2d::DrawRectXLU(CRect(0, ms_horizonZ, SCREENW, z1),
ms_colourBottom, ms_colourBottom, ms_colourTop, ms_colourTop);
// This is just weird
float a = SCREENH/400.0f * HORIZSTRIPHEIGHT +
SCREENH/300.0f * max(TheCamera.GetPosition().z, 0.0f);
float b = TheCamera.GetUp().z < 0.0f ?
SCREENH :
SCREENH * fabs(TheCamera.GetRight().z);
float z2 = z1 + (a + b)*TheCamera.LODDistMultiplier;
z2 = min(z2, SCREENH);
CSprite2d::DrawRect(CRect(0, z1, SCREENW, z2),
ms_colourBottom, ms_colourBottom, ms_colourTop, ms_colourTop);
}
STARTPATCHES
InjectHook(0x4F6C10, CClouds::Init, PATCH_JUMP);
InjectHook(0x4F6CE0, CClouds::Update, PATCH_JUMP);
InjectHook(0x4F6D90, CClouds::Render, PATCH_JUMP);
InjectHook(0x4F7F00, CClouds::RenderBackground, PATCH_JUMP);
InjectHook(0x4F85F0, CClouds::RenderHorizon, PATCH_JUMP);
ENDPATCHES

20
src/render/Clouds.h Normal file
View file

@ -0,0 +1,20 @@
#pragma once
class CClouds
{
public:
static float &CloudRotation;
static uint32 &IndividualRotation;
static float &ms_cameraRoll;
static float &ms_horizonZ;
static CRGBA &ms_colourTop;
static CRGBA &ms_colourBottom;
static void Init(void);
static void Update(void);
static void Render(void);
static void RenderBackground(int16 topred, int16 topgreen, int16 topblue,
int16 botred, int16 botgreen, int16 botblue, int16 alpha);
static void RenderHorizon(void);
};

10
src/render/Coronas.cpp Normal file
View file

@ -0,0 +1,10 @@
#include "common.h"
#include "Coronas.h"
RwTexture **gpCoronaTexture = (RwTexture**)0x5FAF44; //[9]
float &CCoronas::LightsMult = *(float*)0x5FB088; // 1.0
float &CCoronas::SunScreenX = *(float*)0x8F4358;
float &CCoronas::SunScreenY = *(float*)0x8F4354;
bool &CCoronas::bSmallMoon = *(bool*)0x95CD49;
bool &CCoronas::SunBlockedByClouds = *(bool*)0x95CD73;

13
src/render/Coronas.h Normal file
View file

@ -0,0 +1,13 @@
#pragma once
extern RwTexture **gpCoronaTexture; //[9]
class CCoronas
{
public:
static float &LightsMult;
static float &SunScreenY;
static float &SunScreenX;
static bool &bSmallMoon;
static bool &SunBlockedByClouds;
};

Some files were not shown because too many files have changed in this diff Show more