code:
#include "common.h"

#include "Camera.h"
#include "Clock.h"
#include "Coronas.h"
#include "General.h"
#include "PathFind.h"
#include "PointLights.h"
#include "Shadows.h"
#include "SpecialFX.h"
#include "Timecycle.h"
#include "Timer.h"
#include "TrafficLights.h"
#include "Vehicle.h"
#include "Weather.h"
#include "World.h"

// TODO: figure out the meaning of this
enum { SOME_FLAG = 0x80 };

void
CTrafficLights::DisplayActualLight(CEntity *ent)
{
	if(ent->GetUp().z < 0.96f || ent->bRenderDamaged)
		return;

	int phase;
	if(FindTrafficLightType(ent) == 1)
		phase = LightForCars1();
	else
		phase = LightForCars2();

	int i;
	CBaseModelInfo *mi = CModelInfo::GetModelInfo(ent->GetModelIndex());
	float x = mi->Get2dEffect(0)->pos.x;
	float yMin = mi->Get2dEffect(0)->pos.y;
	float yMax = mi->Get2dEffect(0)->pos.y;
	float zMin = mi->Get2dEffect(0)->pos.z;
	float zMax = mi->Get2dEffect(0)->pos.z;
	for(i = 1; i < 6; i++){
		assert(mi->Get2dEffect(i));
		yMin = Min(yMin, mi->Get2dEffect(i)->pos.y);
		yMax = Max(yMax, mi->Get2dEffect(i)->pos.y);
		zMin = Min(zMin, mi->Get2dEffect(i)->pos.z);
		zMax = Max(zMax, mi->Get2dEffect(i)->pos.z);
	}

	CVector pos1, pos2;
	uint8 r, g;
	int id;
	switch(phase){
	case CAR_LIGHTS_GREEN:
		r = 0;
		g = 255;
		pos1 = ent->GetMatrix() * CVector(x, yMax, zMin);
		pos2 = ent->GetMatrix() * CVector(x, yMin, zMin);
		id = 0;
		break;
	case CAR_LIGHTS_YELLOW:
		r = 255;
		g = 128;
		pos1 = ent->GetMatrix() * CVector(x, yMax, (zMin+zMax)/2.0f);
		pos2 = ent->GetMatrix() * CVector(x, yMin, (zMin+zMax)/2.0f);
		id = 1;
		break;
	case CAR_LIGHTS_RED:
	default:
		r = 255;
		g = 0;
		pos1 = ent->GetMatrix() * CVector(x, yMax, zMax);
		pos2 = ent->GetMatrix() * CVector(x, yMin, zMax);
		id = 2;
		break;
	}

	if(CClock::GetHours() > 19 || CClock::GetHours() < 6 || CWeather::Foggyness > 0.05f)
		CPointLights::AddLight(CPointLights::LIGHT_POINT,
			pos1, CVector(0.0f, 0.0f, 0.0f), 8.0f,
			r/255.0f, g/255.0f, 0/255.0f, CPointLights::FOG_NORMAL, true);

	CShadows::StoreStaticShadow((uintptr)ent,
		SHADOWTYPE_ADDITIVE, gpShadowExplosionTex, &pos1,
		8.0f, 0.0f, 0.0f, -8.0f, 128,
		r*CTimeCycle::GetLightOnGroundBrightness()/8.0f,
		g*CTimeCycle::GetLightOnGroundBrightness()/8.0f,
		0*CTimeCycle::GetLightOnGroundBrightness()/8.0f,
		12.0f, 1.0f, 40.0f, false, 0.0f);

	if(DotProduct(TheCamera.GetForward(), ent->GetForward()) < 0.0f)
		CCoronas::RegisterCorona((uintptr)ent + id,
			r*CTimeCycle::GetSpriteBrightness()*0.7f,
			g*CTimeCycle::GetSpriteBrightness()*0.7f,
			0*CTimeCycle::GetSpriteBrightness()*0.7f,
			255,
			pos1, 1.75f*CTimeCycle::GetSpriteSize(), 50.0f,
			CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
			CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);
	else
		CCoronas::RegisterCorona((uintptr)ent + id + 3,
			r*CTimeCycle::GetSpriteBrightness()*0.7f,
			g*CTimeCycle::GetSpriteBrightness()*0.7f,
			0*CTimeCycle::GetSpriteBrightness()*0.7f,
			255,
			pos2, 1.75f*CTimeCycle::GetSpriteSize(), 50.0f,
			CCoronas::TYPE_STAR, CCoronas::FLARE_NONE, CCoronas::REFLECTION_ON,
			CCoronas::LOSCHECK_OFF, CCoronas::STREAK_OFF, 0.0f);

	CBrightLights::RegisterOne(pos1, ent->GetUp(), ent->GetRight(), CVector(0.0f, 0.0f, 0.0f), id + BRIGHTLIGHT_TRAFFIC_GREEN);
	CBrightLights::RegisterOne(pos2, ent->GetUp(), -ent->GetRight(), CVector(0.0f, 0.0f, 0.0f), id + BRIGHTLIGHT_TRAFFIC_GREEN);

	static const float top = -0.127f;
	static const float bot = -0.539f;
	static const float mid = bot + (top-bot)/3.0f;
	static const float left = 1.256f;
	static const float right = 0.706f;
	phase = CTrafficLights::LightForPeds();
	if(phase == PED_LIGHTS_DONT_WALK){
		CVector p0(2.7f, right, top);
		CVector p1(2.7f, left, top);
		CVector p2(2.7f, right, mid);
		CVector p3(2.7f, left, mid);
		CShinyTexts::RegisterOne(ent->GetMatrix()*p0, ent->GetMatrix()*p1, ent->GetMatrix()*p2, ent->GetMatrix()*p3,
			1.0f, 0.0f,  0.0f, 0.0f,  1.0f, 1.0f,  0.0f, 1.0f,
			SHINYTEXT_WALK, 255, 0, 0, 60.0f);
	}else if(phase == PED_LIGHTS_WALK || CTimer::GetTimeInMilliseconds() & 0x100){
		CVector p0(2.7f, right, mid);
		CVector p1(2.7f, left, mid);
		CVector p2(2.7f, right, bot);
		CVector p3(2.7f, left, bot);
		CShinyTexts::RegisterOne(ent->GetMatrix()*p0, ent->GetMatrix()*p1, ent->GetMatrix()*p2, ent->GetMatrix()*p3,
			1.0f, 0.5f,  0.0f, 0.5f,  1.0f, 1.0f,  0.0f, 1.0f,
			SHINYTEXT_WALK, 255, 255, 255, 60.0f);
	}
}

void
CTrafficLights::ScanForLightsOnMap(void)
{
	int x, y;
	int i, j, k, l;
	CPtrNode *node;

	for(x = 0; x < NUMSECTORS_X; x++)
	for(y = 0; y < NUMSECTORS_Y; y++){
		CPtrList &list = CWorld::GetSector(x, y)->m_lists[ENTITYLIST_DUMMIES];
		for(node = list.first; node; node = node->next){
			CEntity *light = (CEntity*)node->item;
			if(light->GetModelIndex() != MI_TRAFFICLIGHTS)
				continue;

			// Check cars
			for(i = 0; i < ThePaths.m_numCarPathLinks; i++){
				CVector2D dist = ThePaths.m_carPathLinks[i].GetPosition() - light->GetPosition();
				float dotY = Abs(DotProduct2D(dist, light->GetForward()));	// forward is direction of car light
				float dotX = DotProduct2D(dist, light->GetRight());	// towards base of light
				// it has to be on the correct side of the node and also not very far away
				if(dotX < 0.0f && dotX > -15.0f && dotY < 3.0f){
					float dz = ThePaths.m_pathNodes[ThePaths.m_carPathLinks[i].pathNodeIndex].GetZ() -
						light->GetPosition().z;
					if(dz < 15.0f){
						ThePaths.m_carPathLinks[i].trafficLightType = FindTrafficLightType(light);
						// Find two neighbour nodes of this one
						int n1 = -1;
						int n2 = -1;
						for(j = 0; j < ThePaths.m_numPathNodes; j++)
							for(l = 0; l < ThePaths.m_pathNodes[j].numLinks; l++)
								if(ThePaths.m_carPathConnections[ThePaths.m_pathNodes[j].firstLink + l] == i){
									if(n1 == -1)
										n1 = j;
									else
										n2 = j;
								}
						// What's going on here?
						if(ThePaths.m_pathNodes[n1].numLinks <= ThePaths.m_pathNodes[n2].numLinks)
							n1 = n2;
						if(ThePaths.m_carPathLinks[i].pathNodeIndex != n1)
							ThePaths.m_carPathLinks[i].trafficLightType |= SOME_FLAG;
					}
				}
			}

			// Check peds
			for(i = ThePaths.m_numCarPathNodes; i < ThePaths.m_numPathNodes; i++){
				float dist1, dist2;
				dist1 = Abs(ThePaths.m_pathNodes[i].GetX() - light->GetPosition().x) +
					Abs(ThePaths.m_pathNodes[i].GetY() - light->GetPosition().y);
				if(dist1 < 50.0f){
					for(l = 0; l < ThePaths.m_pathNodes[i].numLinks; l++){
						j = ThePaths.m_pathNodes[i].firstLink + l;
						if(ThePaths.ConnectionCrossesRoad(j)){
							k = ThePaths.ConnectedNode(j);
							dist2 = Abs(ThePaths.m_pathNodes[k].GetX() - light->GetPosition().x) +
								Abs(ThePaths.m_pathNodes[k].GetY() - light->GetPosition().y);
							if(dist1 < 15.0f || dist2 < 15.0f)
								ThePaths.ConnectionSetTrafficLight(j);
						}
					}
				}
			}
		}
	}
}

bool
CTrafficLights::ShouldCarStopForLight(CVehicle *vehicle, bool alwaysStop)
{
	int node, type;

	node = vehicle->AutoPilot.m_nNextPathNodeInfo;
	type = ThePaths.m_carPathLinks[node].trafficLightType;
	if(type){
		if((type & SOME_FLAG || ThePaths.m_carPathLinks[node].pathNodeIndex == vehicle->AutoPilot.m_nNextRouteNode) &&
		   (!(type & SOME_FLAG) || ThePaths.m_carPathLinks[node].pathNodeIndex != vehicle->AutoPilot.m_nNextRouteNode))
			if(alwaysStop ||
			   (type&~SOME_FLAG) == 1 && LightForCars1() != CAR_LIGHTS_GREEN ||
			   (type&~SOME_FLAG) == 2 && LightForCars2() != CAR_LIGHTS_GREEN){
				float dist = DotProduct2D(CVector2D(vehicle->GetPosition()) - ThePaths.m_carPathLinks[node].GetPosition(),
						ThePaths.m_carPathLinks[node].GetDirection());
				if(vehicle->AutoPilot.m_nNextDirection == -1){
					if(dist > 0.0f && dist < 8.0f)
						return true;
				}else{
					if(dist < 0.0f && dist > -8.0f)
						return true;
				}
			}
	}

	node = vehicle->AutoPilot.m_nCurrentPathNodeInfo;
	type = ThePaths.m_carPathLinks[node].trafficLightType;
	if(type){
		if((type & SOME_FLAG || ThePaths.m_carPathLinks[node].pathNodeIndex == vehicle->AutoPilot.m_nCurrentRouteNode) &&
		   (!(type & SOME_FLAG) || ThePaths.m_carPathLinks[node].pathNodeIndex != vehicle->AutoPilot.m_nCurrentRouteNode))
			if(alwaysStop ||
			   (type&~SOME_FLAG) == 1 && LightForCars1() != CAR_LIGHTS_GREEN ||
			   (type&~SOME_FLAG) == 2 && LightForCars2() != CAR_LIGHTS_GREEN){
				float dist = DotProduct2D(CVector2D(vehicle->GetPosition()) - ThePaths.m_carPathLinks[node].GetPosition(),
						ThePaths.m_carPathLinks[node].GetDirection());
				if(vehicle->AutoPilot.m_nCurrentDirection == -1){
					if(dist > 0.0f && dist < 8.0f)
						return true;
				}else{
					if(dist < 0.0f && dist > -8.0f)
						return true;
				}
			}
	}

	if(vehicle->GetStatus() == STATUS_PHYSICS){
		node = vehicle->AutoPilot.m_nPreviousPathNodeInfo;
		type = ThePaths.m_carPathLinks[node].trafficLightType;
		if(type){
			if((type & SOME_FLAG || ThePaths.m_carPathLinks[node].pathNodeIndex == vehicle->AutoPilot.m_nPrevRouteNode) &&
			   (!(type & SOME_FLAG) || ThePaths.m_carPathLinks[node].pathNodeIndex != vehicle->AutoPilot.m_nPrevRouteNode))
				if(alwaysStop ||
				   (type&~SOME_FLAG) == 1 && LightForCars1() != CAR_LIGHTS_GREEN ||
				   (type&~SOME_FLAG) == 2 && LightForCars2() != CAR_LIGHTS_GREEN){
					float dist = DotProduct2D(CVector2D(vehicle->GetPosition()) - ThePaths.m_carPathLinks[node].GetPosition(),
							ThePaths.m_carPathLinks[node].GetDirection());
					if(vehicle->AutoPilot.m_nPreviousDirection == -1){
						if(dist > 0.0f && dist < 6.0f)
							return true;
					}else{
						if(dist < 0.0f && dist > -6.0f)
							return true;
					}
				}
		}
	}

	return false;
}

bool
CTrafficLights::ShouldCarStopForBridge(CVehicle *vehicle)
{
	return ThePaths.m_carPathLinks[vehicle->AutoPilot.m_nNextPathNodeInfo].bBridgeLights &&
		!ThePaths.m_carPathLinks[vehicle->AutoPilot.m_nCurrentPathNodeInfo].bBridgeLights;
}

int
CTrafficLights::FindTrafficLightType(CEntity *light)
{
	float orientation = RADTODEG(CGeneral::GetATanOfXY(light->GetForward().x, light->GetForward().y));
	if((orientation > 60.0f && orientation < 60.0f + 90.0f) ||
	   (orientation > 240.0f && orientation < 240.0f + 90.0f))
		return 1;
	return 2;
}

uint8
CTrafficLights::LightForPeds(void)
{
	uint32 period = CTimer::GetTimeInMilliseconds() % 16384;

	if(period < 12000)
		return PED_LIGHTS_DONT_WALK;
	else if(period < 16384 - 1000)
		return PED_LIGHTS_WALK;
	else
		return PED_LIGHTS_WALK_BLINK;
}

uint8
CTrafficLights::LightForCars1(void)
{
	uint32 period = CTimer::GetTimeInMilliseconds() % 16384;

	if(period < 5000)
		return CAR_LIGHTS_GREEN;
	else if(period < 5000 + 1000)
		return CAR_LIGHTS_YELLOW;
	else
		return CAR_LIGHTS_RED;
}

uint8
CTrafficLights::LightForCars2(void)
{
	uint32 period = CTimer::GetTimeInMilliseconds() % 16384;

	if(period < 6000)
		return CAR_LIGHTS_RED;
	else if(period < 12000 - 1000)
		return CAR_LIGHTS_GREEN;
	else if(period < 12000)
		return CAR_LIGHTS_YELLOW;
	else
		return CAR_LIGHTS_RED;
}

license: mit


code:
#pragma once

class CEntity;
class CVehicle;

enum {
	PED_LIGHTS_WALK,
	PED_LIGHTS_WALK_BLINK,
	PED_LIGHTS_DONT_WALK,

	CAR_LIGHTS_GREEN = 0,
	CAR_LIGHTS_YELLOW,
	CAR_LIGHTS_RED
};

class CTrafficLights
{
public:
	static void DisplayActualLight(CEntity *ent);
	static void ScanForLightsOnMap(void);
	static int FindTrafficLightType(CEntity *light);
	static uint8 LightForPeds(void);
	static uint8 LightForCars1(void);
	static uint8 LightForCars2(void);
	static bool ShouldCarStopForLight(CVehicle*, bool);
	static bool ShouldCarStopForBridge(CVehicle*);
};


code:
#include "common.h"

#include "Accident.h"

#include "Ped.h"
#include "Pools.h"
#include "World.h"

CAccidentManager gAccidentManager;

CAccident*
CAccidentManager::GetNextFreeAccident()
{
	for (int i = 0; i < NUM_ACCIDENTS; i++) {
		if (m_aAccidents[i].m_pVictim == nil)
			return &m_aAccidents[i];
	}

	return nil;
}

void
CAccidentManager::ReportAccident(CPed *ped)
{
	if (!ped->IsPlayer() && ped->CharCreatedBy != MISSION_CHAR && !ped->bRenderScorched && !ped->bBodyPartJustCameOff && ped->bAllowMedicsToReviveMe && !ped->bIsInWater) {
		for (int i = 0; i < NUM_ACCIDENTS; i++) {
			if (m_aAccidents[i].m_pVictim != nil && m_aAccidents[i].m_pVictim == ped)
				return;
		}

		if (ped->m_pCurrentPhysSurface == nil) {
			CVector point = ped->GetPosition();
			point.z -= 2.0f;

			CColPoint colPoint;
			CEntity *pEntity;

			if (!CWorld::ProcessVerticalLine(point, -100.0f, colPoint, pEntity, true, false, false, false, false, false, nil)) {
				CAccident *accident = GetNextFreeAccident();
				if (accident != nil) {
					accident->m_pVictim = ped;
					ped->RegisterReference((CEntity**)&accident->m_pVictim);
					accident->m_nMedicsPerformingCPR = 0;
					accident->m_nMedicsAttending = 0;
					ped->m_lastAccident = accident;
					WorkToDoForMedics();
				}
			}
		}
	}
}

void
CAccidentManager::Update()
{
#ifdef SQUEEZE_PERFORMANCE
	// Handled after injury registered.
	return;
#endif
	int32 e;
	if (CEventList::GetEvent(EVENT_INJURED_PED, &e)) {
		CPed *ped = CPools::GetPed(gaEvent[e].entityRef);
		if (ped) {
			ReportAccident(ped);
			CEventList::ClearEvent(e);
		}
	}
}

CAccident*
CAccidentManager::FindNearestAccident(CVector vecPos, float *pDistance)
{
	for (int i = 0; i < MAX_MEDICS_TO_ATTEND_ACCIDENT; i++){
		int accidentId = -1;
		float minDistance = 999999;
		for (int j = 0; j < NUM_ACCIDENTS; j++){
			CPed* pVictim = m_aAccidents[j].m_pVictim;
			if (!pVictim)
				continue;
			if (pVictim->CharCreatedBy == MISSION_CHAR)
				continue;
			if (pVictim->m_fHealth != 0.0f)
				continue;
			if (m_aAccidents[j].m_nMedicsPerformingCPR != i)
				continue;
			float distance = (pVictim->GetPosition() - vecPos).Magnitude2D();
			if (distance / 2 > pVictim->GetPosition().z - vecPos.z && distance < minDistance){
				minDistance = distance;
				accidentId = j;
			}
		}
		*pDistance = minDistance;
		if (accidentId != -1)
			return &m_aAccidents[accidentId];
	}
	return nil;
}

uint16
CAccidentManager::CountActiveAccidents()
{
	uint16 accidents = 0;
	for (int i = 0; i < NUM_ACCIDENTS; i++) {
		if (m_aAccidents[i].m_pVictim)
			accidents++;
	}
	return accidents;
}

bool
CAccidentManager::WorkToDoForMedics()
{
	for (int i = 0; i < NUM_ACCIDENTS; i++) {
		if (m_aAccidents[i].m_pVictim != nil && m_aAccidents[i].m_nMedicsAttending < MAX_MEDICS_TO_ATTEND_ACCIDENT)
			return true;
	}
	return false;
}

bool
CAccidentManager::UnattendedAccidents()
{
	for (int i = 0; i < NUM_ACCIDENTS; i++) {
		if (m_aAccidents[i].m_pVictim != nil && m_aAccidents[i].m_nMedicsAttending == 0)
			return true;
	}
	return false;
}


code:
#pragma once
#include "config.h"

class CPed;

class CAccident
{
public:
	CPed *m_pVictim;
	uint32 m_nMedicsAttending;
	uint32 m_nMedicsPerformingCPR;
	CAccident() : m_pVictim(nil), m_nMedicsAttending(0), m_nMedicsPerformingCPR(0) {}
};

class CAccidentManager
{
	CAccident m_aAccidents[NUM_ACCIDENTS];
	enum {
		MAX_MEDICS_TO_ATTEND_ACCIDENT = 2
	};
public:
	CAccident *GetNextFreeAccident();
	void ReportAccident(CPed *ped);
	void Update();
	CAccident *FindNearestAccident(CVector vecPos, float *pDistance);
	uint16 CountActiveAccidents();
	bool UnattendedAccidents();
	bool WorkToDoForMedics();
};

extern CAccidentManager gAccidentManager;


code:
#include "common.h"

#include "Font.h"
#include "Pad.h"
#include "Text.h"
#include "main.h"
#include "Timer.h"
#include "DMAudio.h"
#include "FileMgr.h"
#include "Streaming.h"
#include "TxdStore.h"
#include "General.h"
#include "Camera.h"
#include "Vehicle.h"
#include "PlayerSkin.h"
#include "PlayerInfo.h"
#include "World.h"
#include "Renderer.h"
#include "AnimManager.h"
#include "AnimViewer.h"
#include "PlayerPed.h"
#include "Pools.h"
#include "References.h"
#include "PathFind.h"
#include "HandlingMgr.h"
#include "TempColModels.h"
#include "Particle.h"
#include "CdStream.h"
#include "Messages.h"
#include "CarCtrl.h"
#include "FileLoader.h"
#include "ModelIndices.h"
#include "Clock.h"
#include "Timecycle.h"
#include "RpAnimBlend.h"
#include "AnimBlendAssociation.h"
#include "Shadows.h"
#include "Radar.h"
#include "Hud.h"
#include "debugmenu.h"

int CAnimViewer::animTxdSlot = 0;
CEntity *CAnimViewer::pTarget = nil;

void
CAnimViewer::Render(void) {
	if (pTarget) {
//		pTarget->GetPosition() = CVector(0.0f, 0.0f, 0.0f); // Only on Mobile
		if (pTarget) {
#ifdef FIX_BUGS
#ifdef PED_SKIN
			if(pTarget->IsPed() && IsClumpSkinned(pTarget->GetClump()))
				((CPed*)pTarget)->UpdateRpHAnim();
#endif
#endif
			pTarget->Render();
			CRenderer::RenderOneNonRoad(pTarget);
		}
	}
}

void
CAnimViewer::Initialise(void) {
	// we need messages, messages needs hud, hud needs this
	CHud::m_Wants_To_Draw_Hud = false;

	animTxdSlot = CTxdStore::AddTxdSlot("generic");
	CTxdStore::Create(animTxdSlot);
	int hudSlot = CTxdStore::AddTxdSlot("hud");
	CTxdStore::LoadTxd(hudSlot, "MODELS/HUD.TXD");
	int particleSlot = CTxdStore::AddTxdSlot("particle");
	CTxdStore::LoadTxd(particleSlot, "MODELS/PARTICLE.TXD");
	CTxdStore::SetCurrentTxd(animTxdSlot);
	CPools::Initialise();
	CReferences::Init();
	TheCamera.Init();
	TheCamera.SetRwCamera(Scene.camera);
	TheCamera.Cams[TheCamera.ActiveCam].Distance = 5.0f;

	ThePaths.Init();
	ThePaths.AllocatePathFindInfoMem(4500);
	CCollision::Init();
	CWorld::Initialise();
	mod_HandlingManager.Initialise();
	CTempColModels::Initialise();
	CAnimManager::Initialise();
	CModelInfo::Initialise();
	CParticle::Initialise();
	CCarCtrl::Init();
	CPedStats::Initialise();
	CMessages::Init();
	CdStreamAddImage("MODELS\\GTA3.IMG");
	CFileLoader::LoadLevel("DATA\\ANIMVIEWER.DAT");
	CStreaming::Init();
	CStreaming::LoadInitialPeds();
	CStreaming::RequestSpecialModel(MI_PLAYER, "player", STREAMFLAGS_DONT_REMOVE);
	CStreaming::LoadAllRequestedModels(false);
	CRenderer::Init();
	CRadar::Initialise();
	CRadar::LoadTextures();
	CVehicleModelInfo::LoadVehicleColours();
#ifdef FIX_BUGS
	CVehicleModelInfo::LoadEnvironmentMaps();
#endif
	CAnimManager::LoadAnimFiles();
	CWorld::PlayerInFocus = 0;
	CWeapon::InitialiseWeapons();
	CShadows::Init();
	CPed::Initialise();
	CTimer::Initialise();
	CClock::Initialise(60000);
	CTimeCycle::Initialise();
	CCarCtrl::Init();
	CPlayerPed *player = new CPlayerPed();
	player->SetPosition(0.0f, 0.0f, 0.0f); // This is 1000.f for all axes on Xbox, but 0.f on mobile?
	CWorld::Players[0].m_pPed = player;
	CDraw::SetFOV(120.0f);
	CDraw::ms_fLODDistance = 500.0f;

	int fd = CFileMgr::OpenFile("DATA\\SPECIAL.TXT", "r");
	char animGroup[32], modelName[32];
	if (fd) {
		for (int lineId = 0; lineId < NUM_OF_SPECIAL_CHARS; lineId++) {
			if (!CFileMgr::ReadLine(fd, gString, 255))
				break;

			sscanf(gString, "%s %s", modelName, animGroup);
			int groupId;
			for (groupId = 0; groupId < NUM_ANIM_ASSOC_GROUPS; groupId++) {
				if (!strcmp(animGroup, CAnimManager::GetAnimGroupName((AssocGroupId)groupId)))
					break;
			}

			if (groupId != NUM_ANIM_ASSOC_GROUPS)
				((CPedModelInfo*)CModelInfo::GetModelInfo(MI_SPECIAL01 + lineId))->m_animGroup = groupId;

			CStreaming::RequestSpecialChar(lineId, modelName, STREAMFLAGS_DONT_REMOVE);
		}
		CFileMgr::CloseFile(fd);
	} else {
		// From xbox
		CStreaming::RequestSpecialChar(0, "luigi", STREAMFLAGS_DONT_REMOVE);
		CStreaming::RequestSpecialChar(1, "joey", STREAMFLAGS_DONT_REMOVE);
		CStreaming::RequestSpecialChar(2, "tony", STREAMFLAGS_DONT_REMOVE);
		CStreaming::RequestSpecialChar(3, "curly", STREAMFLAGS_DONT_REMOVE);
	}
}

int
LastPedModelId(int modelId)
{
	CBaseModelInfo *model;
	for(;;){
		assert(modelId < MODELINFOSIZE);
		model = CModelInfo::GetModelInfo(modelId);
		if (model && model->GetModelType() == MITYPE_PED)
			break;
		modelId--;
	}
	return modelId;
}

int
FirstCarModelId(int modelId)
{
	CBaseModelInfo *model;
	for(;;){
		assert(modelId < MODELINFOSIZE);
		model = CModelInfo::GetModelInfo(modelId);
		if (model && model->GetModelType() == MITYPE_VEHICLE)
			break;
		modelId++;
	}
	return modelId;
}

int
NextModelId(int modelId, int wantedChange)
{
	// Max. 2 trials wasn't here, it's me that added it.

	int tryCount = 2;
	int ogModelId = modelId;

	while(tryCount != 0) {
		modelId += wantedChange;
		if (modelId < 0 || modelId >= MODELINFOSIZE) {
			tryCount--;
			wantedChange = -wantedChange;
		} else if (modelId != 5 && modelId != 6 && modelId != 405) {
			CBaseModelInfo *model = CModelInfo::GetModelInfo(modelId);
			if (model)
			{
				//int type = model->m_type;
				return modelId;
			}
		}
	}
	return ogModelId;
}

void
PlayAnimation(RpClump *clump, AssocGroupId animGroup, AnimationId anim)
{
	CAnimBlendAssociation *currentAssoc = RpAnimBlendClumpGetAssociation(clump, anim);

	if (currentAssoc && currentAssoc->IsPartial())
		delete currentAssoc;

	RpAnimBlendClumpSetBlendDeltas(clump, ASSOC_PARTIAL, -8.0f);

	CAnimBlendAssociation *animAssoc = CAnimManager::BlendAnimation(clump, animGroup, anim, 8.0f);
	animAssoc->flags |= ASSOC_DELETEFADEDOUT;
	animAssoc->SetCurrentTime(0.0f);
	animAssoc->SetRun();
}

void
CAnimViewer::Update(void)
{
	static int modelId = 0;
	static int animId = 0;
	static bool reloadIFP = false;

	AssocGroupId animGroup = ASSOCGRP_STD;
	int nextModelId = modelId;
	CBaseModelInfo *modelInfo = CModelInfo::GetModelInfo(modelId);

	if (modelInfo->GetModelType() == MITYPE_PED) {
		int animGroup = ((CPedModelInfo*)modelInfo)->m_animGroup;

		if (animId > ANIM_STD_IDLE)
			animGroup = ASSOCGRP_STD;

		if (reloadIFP) {
			if (pTarget) {
				CWorld::Remove(pTarget);
				if (pTarget)
					delete pTarget;
			}
			pTarget = nil;

			// These calls were inside of LoadIFP function.
			CAnimManager::Shutdown();
			CAnimManager::Initialise();
			CAnimManager::LoadAnimFiles();

			reloadIFP = false;
		}
	} else {
		animGroup = ASSOCGRP_STD;
	}
	CPad::UpdatePads();
	CPad* pad = CPad::GetPad(0);
#ifdef DEBUGMENU
	DebugMenuProcess();
#endif

	CStreaming::UpdateForAnimViewer();
	CStreaming::RequestModel(modelId, 0);
	if (CStreaming::HasModelLoaded(modelId)) {

		if (!pTarget) {

			if (modelInfo->GetModelType() == MITYPE_VEHICLE) {

				CVehicleModelInfo* veh = (CVehicleModelInfo*)modelInfo;
				if (veh->m_vehicleType == VEHICLE_TYPE_CAR) {
					pTarget = new CAutomobile(modelId, RANDOM_VEHICLE);
				} else if (veh->m_vehicleType == VEHICLE_TYPE_BOAT) {
					pTarget = new CBoat(modelId, RANDOM_VEHICLE);
				} else {
					pTarget = new CObject(modelId, true);
					if (!modelInfo->GetColModel()) {
						modelInfo->SetColModel(&CTempColModels::ms_colModelWheel1);
					}
				}
				pTarget->SetStatus(STATUS_ABANDONED);
			} else if (modelInfo->GetModelType() == MITYPE_PED) {
				pTarget = new CPed(PEDTYPE_CIVMALE);
				pTarget->SetModelIndex(modelId);
			} else {
				pTarget = new CObject(modelId, true);
				if (!modelInfo->GetColModel())
				{
					modelInfo->SetColModel(&CTempColModels::ms_colModelWheel1);
				}
				pTarget->SetStatus(STATUS_ABANDONED);
			}
			pTarget->SetPosition(0.0f, 0.0f, 0.0f);
			CWorld::Add(pTarget);
			TheCamera.TakeControl(pTarget, CCam::MODE_MODELVIEW, JUMP_CUT, CAMCONTROL_SCRIPT);
		}
		if (pTarget->IsVehicle() || pTarget->IsPed() || pTarget->IsObject()) {
			((CPhysical*)pTarget)->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f);
		}
#ifdef FIX_BUGS
		// so we don't end up in the water
		pTarget->GetMatrix().GetPosition().z = 10.0f;
#else
		pTarget->GetMatrix().GetPosition().z = 0.0f;

#endif

		if (modelInfo->GetModelType() == MITYPE_PED) {
			((CPed*)pTarget)->bKindaStayInSamePlace = true;

			// Triangle in mobile
			if (pad->GetSquareJustDown()) {
				reloadIFP = true;
				AsciiToUnicode("IFP reloaded", gUString);
				CMessages::AddMessage(gUString, 1000, 0);

			} else if (pad->GetCrossJustDown()) {
				PlayAnimation(pTarget->GetClump(), animGroup, (AnimationId)animId);
				AsciiToUnicode("Animation restarted", gUString);
				CMessages::AddMessage(gUString, 1000, 0);

			} else if (pad->GetCircleJustDown()) {
				PlayAnimation(pTarget->GetClump(), animGroup, ANIM_STD_IDLE);
				AsciiToUnicode("Idle animation playing", gUString);
				CMessages::AddMessage(gUString, 1000, 0);

			} else if (pad->GetDPadUpJustDown()) {
				animId--;
				if (animId < 0) {
					animId = ANIM_STD_NUM - 1;
				}
				PlayAnimation(pTarget->GetClump(), animGroup, (AnimationId)animId);

				sprintf(gString, "Current anim: %d", animId);
				AsciiToUnicode(gString, gUString);
				CMessages::AddMessage(gUString, 1000, 0);

			} else if (pad->GetDPadDownJustDown()) {
				animId = (animId == (ANIM_STD_NUM - 1) ? 0 : animId + 1);
				PlayAnimation(pTarget->GetClump(), animGroup, (AnimationId)animId);

				sprintf(gString, "Current anim: %d", animId);
				AsciiToUnicode(gString, gUString);
				CMessages::AddMessage(gUString, 1000, 0);

			} else if (pad->GetStartJustDown()) {

			} else if (pad->GetLeftShoulder1JustDown()) {
				nextModelId = FirstCarModelId(modelId);
				AsciiToUnicode("Switched to vehicles", gUString);
				CMessages::AddMessage(gUString, 1000, 0);
				// Originally it was GetPad(1)->LeftShoulder2
			} else if (pad->NewState.Triangle) {
#ifdef PED_SKIN
				if(IsClumpSkinned(pTarget->GetClump()))
					((CPedModelInfo *)CModelInfo::GetModelInfo(pTarget->GetModelIndex()))->AnimatePedColModelSkinned(pTarget->GetClump());
				else
#endif
					CPedModelInfo::AnimatePedColModel(((CPedModelInfo *)CModelInfo::GetModelInfo(pTarget->GetModelIndex()))->GetHitColModel(),
					                                  RpClumpGetFrame(pTarget->GetClump()));
				AsciiToUnicode("Ped Col model will be animated as long as you hold the button", gUString);
				CMessages::AddMessage(gUString, 100, 0);
			}
		} else if (modelInfo->GetModelType() == MITYPE_VEHICLE) {

			if (pad->GetLeftShoulder1JustDown()) {
				nextModelId = LastPedModelId(modelId);
				AsciiToUnicode("Switched to peds", gUString);
				CMessages::AddMessage(gUString, 1000, 0);
				// Start in mobile
			} else if (pad->GetSquareJustDown()) {
				CVehicleModelInfo::LoadVehicleColours();
				AsciiToUnicode("Carcols.dat reloaded", gUString);
				CMessages::AddMessage(gUString, 1000, 0);
			}
		}
	}

	if (pad->GetDPadLeftJustDown()) {
		nextModelId = NextModelId(modelId, -1);

		sprintf(gString, "Current model ID: %d", nextModelId);
		AsciiToUnicode(gString, gUString);
		CMessages::AddMessage(gUString, 1000, 0);

	} else if (pad->GetDPadRightJustDown()) {
		nextModelId = NextModelId(modelId, 1);

		sprintf(gString, "Current model ID: %d", nextModelId);
		AsciiToUnicode(gString, gUString);
		CMessages::AddMessage(gUString, 1000, 0);
	}
	// There were extra codes here to let us change model id by 50, but xbox CPad struct is different, so I couldn't port.

	if (nextModelId != modelId) {
		modelId = nextModelId;
		if (pTarget) {
			CWorld::Remove(pTarget);
			if (pTarget)
				delete pTarget;
		}
		pTarget = nil;
		return;
	}

	CTimeCycle::Update();
	CWorld::Process();
	if (pTarget)
		TheCamera.Process();
}

void
CAnimViewer::Shutdown(void)
{
	if (CWorld::Players[0].m_pPed)
		delete CWorld::Players[0].m_pPed;

	CWorld::ShutDown();
	CModelInfo::ShutDown();
	CAnimManager::Shutdown();
	CTimer::Shutdown();
	CStreaming::Shutdown();
	CTxdStore::RemoveTxdSlot(animTxdSlot);
}


code:
#pragma once

class CAnimViewer {
public:
	static int animTxdSlot;
	static CEntity *pTarget;

	static void Initialise();
	static void Render();
	static void Shutdown();
	static void Update();
};