code:
#include "common.h"

#include "Game.h"
#include "General.h"
#include "World.h"
#include "Population.h"
#include "CopPed.h"
#include "Wanted.h"
#include "FileMgr.h"
#include "Gangs.h"
#include "ModelIndices.h"
#include "Zones.h"
#include "CivilianPed.h"
#include "EmergencyPed.h"
#include "Replay.h"
#include "Camera.h"
#include "CutsceneMgr.h"
#include "CarCtrl.h"
#include "IniFile.h"
#include "VisibilityPlugins.h"
#include "PedPlacement.h"
#include "DummyObject.h"
#include "Script.h"
#include "Shadows.h"
#include "Bike.h"

#define MIN_CREATION_DIST		40.0f // not for start of the game (look at the GeneratePedsAtStartOfGame)
#define CREATION_RANGE			10.0f // added over the MIN_CREATION_DIST.
#define OFFSCREEN_CREATION_MULT	0.5f
#define PED_REMOVE_DIST			(MIN_CREATION_DIST + CREATION_RANGE + 1.0f)
#define PED_REMOVE_DIST_SPECIAL	(MIN_CREATION_DIST + CREATION_RANGE + 15.0f) // for peds with bCullExtraFarAway flag

// Transition areas between zones
const RegenerationPoint aSafeZones[] = {
	LEVEL_INDUSTRIAL, LEVEL_COMMERCIAL, 400.0f, 814.0f, -954.0f, -903.0f, 30.0f, 100.0f,
		790.0f, -917.0f, 39.0f, 775.0f, -921.0f, 39.0f, 424.0f, -942.0f, 38.0f, 439.0f, -938.0f, 38.0f,
	LEVEL_INDUSTRIAL, LEVEL_COMMERCIAL, 555.0f, 711.0f, 118.0f, 186.0f, -30.0f, -10.0f,
		CVector(698.0f,  182.0f, -20.0f), CVector(681.0f,  178.0f, -20.0f), CVector(586.0f,  144.0f, -20.0f), CVector(577.0f,  135.0f, -20.0f),
	LEVEL_INDUSTRIAL, LEVEL_COMMERCIAL, 626.0f, 744.0f, -124.0f, -87.0f, -20.0f, -6.0f,
		CVector(736.0f, -117.0f, -13.0f), CVector(730.0f, -115.0f, -13.0f), CVector(635.0f, -93.0f, -12.5f), CVector(650.0f, -89.0f, -12.5f),
	LEVEL_INDUSTRIAL, LEVEL_COMMERCIAL, 645.0f, 734.0f, -780.0f, -750.0f, -25.0f, -6.0f,
		CVector(729.0f, -764.0f, -18.0f), CVector(720.0f, -769.0f, -17.0f), CVector(652.0f, -774.0f, -10.5f), CVector(659.0f, -770.0f, -10.5f),
	LEVEL_COMMERCIAL, LEVEL_SUBURBAN, -532.0f, -136.0f, -668.0f, -599.0f, 34.0f, 60.0f,
		CVector(-172.0f, -619.0f,  44.0f), CVector(-183.0f, -623.0f,  44.0f), CVector(-511.0f, -645.0f,  41.0f), CVector(-493.0f, -639.0f,  41.5f),
	LEVEL_COMMERCIAL, LEVEL_SUBURBAN, -325.0f, -175.0f, 27.0f, 75.0f, -30.0f, -10.0f,
		CVector(-185.0f,  40.8f, -20.5f), CVector(-202.0f,  37.0f, -20.5f), CVector(-315.0f,  65.5f, -20.5f), CVector(-306.0f,  62.4f, -20.5f),
	LEVEL_COMMERCIAL, LEVEL_SUBURBAN, -410.0f, -310.0f, -1055.0f, -1030.0f, -20.0f, -6.0f,
		CVector(-321.0f, -1043.0f, -13.2f), CVector(-328.0f, -1045.0f, -13.2f), CVector(-398.0f, -1044.0f, -13.5f), CVector(-390.0f, -1040.5f, -13.5f),
	LEVEL_COMMERCIAL, LEVEL_SUBURBAN, -425.0f, -280.0f, -471.0f, -447.0f, -20.0f, -5.0f,
		CVector(-292.0f, -457.0f, -11.6f), CVector(-310.0f, -461.0f, -11.6f), CVector(-413.0f, -461.0f, -11.5f), CVector(-399.0f, -457.0f, -11.3f)
};

PedGroup CPopulation::ms_pPedGroups[NUMPEDGROUPS];
bool CPopulation::ms_bGivePedsWeapons;
int32 CPopulation::m_AllRandomPedsThisType = -1;
float CPopulation::PedDensityMultiplier = 1.0f;
uint32 CPopulation::ms_nTotalMissionPeds;
int32 CPopulation::MaxNumberOfPedsInUse = DEFAULT_MAX_NUMBER_OF_PEDS;
uint32 CPopulation::ms_nNumCivMale;
uint32 CPopulation::ms_nNumCivFemale;
uint32 CPopulation::ms_nNumCop;
bool CPopulation::bZoneChangeHasHappened;
uint32 CPopulation::ms_nNumEmergency;
int8 CPopulation::m_CountDownToPedsAtStart;
uint32 CPopulation::ms_nNumGang1;
uint32 CPopulation::ms_nNumGang2;
uint32 CPopulation::ms_nTotalPeds;
uint32 CPopulation::ms_nNumGang3;
uint32 CPopulation::ms_nTotalGangPeds;
uint32 CPopulation::ms_nNumGang4;
uint32 CPopulation::ms_nTotalCivPeds;
uint32 CPopulation::ms_nNumGang5;
uint32 CPopulation::ms_nNumDummy;
uint32 CPopulation::ms_nNumGang6;
uint32 CPopulation::ms_nNumGang9;
uint32 CPopulation::ms_nNumGang7;
uint32 CPopulation::ms_nNumGang8;
CVector CPopulation::RegenerationPoint_a;
CVector CPopulation::RegenerationPoint_b;
CVector CPopulation::RegenerationFront;

void
CPopulation::Initialise()
{
	debug("Initialising CPopulation...\n");

	ms_nNumCivMale = 0;
	ms_nNumCivFemale = 0;
	ms_nNumCop = 0;
	ms_nNumEmergency = 0;
	ms_nNumGang1 = 0;
	ms_nNumGang2 = 0;
	ms_nNumGang3 = 0;
	ms_nNumGang4 = 0;
	ms_nNumGang5 = 0;
	ms_nNumGang6 = 0;
	ms_nNumGang7 = 0;
	ms_nNumGang8 = 0;
	ms_nNumGang9 = 0;
	ms_nNumDummy = 0;

	m_AllRandomPedsThisType = -1;
	PedDensityMultiplier = 1.0f;
	bZoneChangeHasHappened = false;
	m_CountDownToPedsAtStart = 2;

	ms_nTotalMissionPeds = 0;
	ms_nTotalPeds = 0;
	ms_nTotalGangPeds = 0;
	ms_nTotalCivPeds = 0;

	LoadPedGroups();
	DealWithZoneChange(LEVEL_COMMERCIAL, LEVEL_INDUSTRIAL, true);

	debug("CPopulation ready\n");
}

void
CPopulation::RemovePed(CPed *ent)
{
	CWorld::Remove((CEntity*)ent);
	delete ent;
}

int32
CPopulation::ChooseCivilianOccupation(int32 group)
{
	return ms_pPedGroups[group].models[CGeneral::GetRandomNumberInRange(0, NUMMODELSPERPEDGROUP)];
}

// returns eCopType
int32
CPopulation::ChoosePolicePedOccupation()
{
	CGeneral::GetRandomNumber();
	return COP_STREET;
}

void
CPopulation::LoadPedGroups()
{
	int fd;
	char line[1024];
	int nextPedGroup = 0;
	// char unused[16]; // non-existence of that in mobile kinda verifies that
	char modelName[256];

	CFileMgr::ChangeDir("\\DATA\\");
	fd = CFileMgr::OpenFile("PEDGRP.DAT", "r");
	CFileMgr::ChangeDir("\\");
	while (CFileMgr::ReadLine(fd, line, sizeof(line))) {
		int end;
		// find end of line
		for (end = 0; ; end++) {
			if (line[end] == '\n')
				break;
			if (line[end] == ',' || line[end] == '\r')
				line[end] = ' ';
		}
		line[end] = '\0';
		int cursor = 0;
		int i;
		for (i = 0; i < NUMMODELSPERPEDGROUP; i++) {
			while (line[cursor] <= ' ' && line[cursor] != '\0')
				++cursor;

			if (line[cursor] == '#')
				break;

			// find next whitespace
			int nextWhitespace;
			for (nextWhitespace = cursor; line[nextWhitespace] > ' '; ++nextWhitespace)
				;

			if (cursor == nextWhitespace)
				break;

			// read until next whitespace
			strncpy(modelName, &line[cursor], nextWhitespace - cursor);
			modelName[nextWhitespace - cursor] = '\0';
			CModelInfo::GetModelInfo(modelName, &ms_pPedGroups[nextPedGroup].models[i]);
			cursor = nextWhitespace;
		}
		if (i == NUMMODELSPERPEDGROUP)
			nextPedGroup++;
	}
	CFileMgr::CloseFile(fd);
}

void
CPopulation::UpdatePedCount(ePedType pedType, bool decrease)
{
	if (decrease) {
		switch (pedType) {
			case PEDTYPE_PLAYER1:
			case PEDTYPE_PLAYER2:
			case PEDTYPE_PLAYER3:
			case PEDTYPE_PLAYER4:
			case PEDTYPE_UNUSED1:
			case PEDTYPE_SPECIAL:
				return;
			case PEDTYPE_CIVMALE:
				--ms_nNumCivMale;
				break;
			case PEDTYPE_CIVFEMALE:
				--ms_nNumCivFemale;
				break;
			case PEDTYPE_COP:
				--ms_nNumCop;
				break;
			case PEDTYPE_GANG1:
				--ms_nNumGang1;
				break;
			case PEDTYPE_GANG2:
				--ms_nNumGang2;
				break;
			case PEDTYPE_GANG3:
				--ms_nNumGang3;
				break;
			case PEDTYPE_GANG4:
				--ms_nNumGang4;
				break;
			case PEDTYPE_GANG5:
				--ms_nNumGang5;
				break;
			case PEDTYPE_GANG6:
				--ms_nNumGang6;
				break;
			case PEDTYPE_GANG7:
				--ms_nNumGang7;
				break;
			case PEDTYPE_GANG8:
				--ms_nNumGang8;
				break;
			case PEDTYPE_GANG9:
				--ms_nNumGang9;
				break;
			case PEDTYPE_EMERGENCY:
			case PEDTYPE_FIREMAN:
				--ms_nNumEmergency;
				break;
			case PEDTYPE_CRIMINAL:
				--ms_nNumCivMale;
				break;
			case PEDTYPE_PROSTITUTE:
				--ms_nNumCivFemale;
				break;
			case PEDTYPE_UNUSED2:
				--ms_nNumDummy;
				break;
			default:
				Error("Unknown ped type, UpdatePedCount, Population.cpp");
				break;
		}
	} else {
		switch (pedType) {
			case PEDTYPE_PLAYER1:
			case PEDTYPE_PLAYER2:
			case PEDTYPE_PLAYER3:
			case PEDTYPE_PLAYER4:
			case PEDTYPE_UNUSED1:
			case PEDTYPE_SPECIAL:
				return;
			case PEDTYPE_CIVMALE:
				++ms_nNumCivMale;
				break;
			case PEDTYPE_CIVFEMALE:
				++ms_nNumCivFemale;
				break;
			case PEDTYPE_COP:
				++ms_nNumCop;
				break;
			case PEDTYPE_GANG1:
				++ms_nNumGang1;
				break;
			case PEDTYPE_GANG2:
				++ms_nNumGang2;
				break;
			case PEDTYPE_GANG3:
				++ms_nNumGang3;
				break;
			case PEDTYPE_GANG4:
				++ms_nNumGang4;
				break;
			case PEDTYPE_GANG5:
				++ms_nNumGang5;
				break;
			case PEDTYPE_GANG6:
				++ms_nNumGang6;
				break;
			case PEDTYPE_GANG7:
				++ms_nNumGang7;
				break;
			case PEDTYPE_GANG8:
				++ms_nNumGang8;
				break;
			case PEDTYPE_GANG9:
				++ms_nNumGang9;
				break;
			case PEDTYPE_EMERGENCY:
			case PEDTYPE_FIREMAN:
				++ms_nNumEmergency;
				break;
			case PEDTYPE_CRIMINAL:
				++ms_nNumCivMale;
				break;
			case PEDTYPE_PROSTITUTE:
				++ms_nNumCivFemale;
				break;
			case PEDTYPE_UNUSED2:
				++ms_nNumDummy;
				break;
			default:
				Error("Unknown ped type, UpdatePedCount, Population.cpp");
				break;
		}
	}
}

int
CPopulation::ChooseGangOccupation(int gangId)
{
	int8 modelOverride = CGangs::GetGangPedModelOverride(gangId);

	// All gangs have 2 models
	int firstGangModel = 2 * gangId + MI_GANG01;

	// GetRandomNumberInRange never returns max. value
	if (modelOverride == -1)
		return CGeneral::GetRandomNumberInRange(firstGangModel, firstGangModel + 2);

	if (modelOverride != 0)
		return firstGangModel + 1;
	else
		return firstGangModel;
}

void
CPopulation::DealWithZoneChange(eLevelName oldLevel, eLevelName newLevel, bool forceIndustrialZone)
{
	bZoneChangeHasHappened = true;

	CVector findSafeZoneAround;
	int safeZone;

	if (forceIndustrialZone) {
		// Commercial to industrial transition area on Callahan Bridge
		findSafeZoneAround.x = 690.0f;
		findSafeZoneAround.y = -920.0f;
		findSafeZoneAround.z = 42.0f;
	} else {
		findSafeZoneAround = FindPlayerCoors();
	}
	eLevelName level;
	FindCollisionZoneForCoors(&findSafeZoneAround, &safeZone, &level);

	// We aren't in a "safe zone", find closest one
	if (safeZone < 0)
		FindClosestZoneForCoors(&findSafeZoneAround, &safeZone, oldLevel, newLevel);

	// No, there should be one!
	if (safeZone < 0) {
		if (newLevel == LEVEL_INDUSTRIAL) {
			safeZone = 0;
		} else if (newLevel == LEVEL_SUBURBAN) {
			safeZone = 4;
		}
	}

	if (aSafeZones[safeZone].srcLevel == newLevel) {
		CPopulation::RegenerationPoint_a = aSafeZones[safeZone].srcPosA;
		CPopulation::RegenerationPoint_b = aSafeZones[safeZone].srcPosB;
		CPopulation::RegenerationFront = aSafeZones[safeZone].destPosA - aSafeZones[safeZone].srcPosA;
		RegenerationFront.Normalise();
	} else if (aSafeZones[safeZone].destLevel == newLevel) {
		CPopulation::RegenerationPoint_a = aSafeZones[safeZone].destPosA;
		CPopulation::RegenerationPoint_b = aSafeZones[safeZone].destPosB;
		CPopulation::RegenerationFront = aSafeZones[safeZone].srcPosA - aSafeZones[safeZone].destPosA;
		RegenerationFront.Normalise();
	}
}

void
CPopulation::FindCollisionZoneForCoors(CVector *coors, int *safeZoneOut, eLevelName *levelOut)
{
	*safeZoneOut = -1;
	for (int i = 0; i < ARRAY_SIZE(aSafeZones); i++) {
		if (coors->x > aSafeZones[i].x1 && coors->x < aSafeZones[i].x2) {
			if (coors->y > aSafeZones[i].y1 && coors->y < aSafeZones[i].y2) {
				if (coors->z > aSafeZones[i].z1 && coors->z < aSafeZones[i].z2)
					*safeZoneOut = i;
			}
		}
	}
	// Then it's transition area
	if (*safeZoneOut >= 0)
		*levelOut = LEVEL_GENERIC;
	else
		*levelOut = CTheZones::GetLevelFromPosition(coors);
}

void
CPopulation::FindClosestZoneForCoors(CVector *coors, int *safeZoneOut, eLevelName level1, eLevelName level2)
{
	float minDist = 10000000.0f;
	int closestSafeZone = -1;
	for (int i = 0; i < ARRAY_SIZE(aSafeZones); i++) {
		if ((level1 == aSafeZones[i].srcLevel || level1 == aSafeZones[i].destLevel) && (level2 == aSafeZones[i].srcLevel || level2 == aSafeZones[i].destLevel)) {
			CVector2D safeZoneDistVec(coors->x - (aSafeZones[i].x1 + aSafeZones[i].x2) * 0.5f, coors->y - (aSafeZones[i].y1 + aSafeZones[i].y2) * 0.5f);
			float safeZoneDist = safeZoneDistVec.Magnitude();
			if (safeZoneDist < minDist) {
				minDist = safeZoneDist;
				closestSafeZone = i;
			}
		}
	}
	*safeZoneOut = closestSafeZone;
}

void
CPopulation::Update()
{
	if (!CReplay::IsPlayingBack()) {
		ManagePopulation();
		MoveCarsAndPedsOutOfAbandonedZones();
		if (m_CountDownToPedsAtStart != 0) {
			if (--m_CountDownToPedsAtStart == 0)
				GeneratePedsAtStartOfGame();
		} else {
			ms_nTotalCivPeds = ms_nNumCivFemale + ms_nNumCivMale;
			ms_nTotalGangPeds = ms_nNumGang9 + ms_nNumGang8 + ms_nNumGang7
				+ ms_nNumGang6 + ms_nNumGang5 + ms_nNumGang4 + ms_nNumGang3
				+ ms_nNumGang2 + ms_nNumGang1;
			ms_nTotalPeds = ms_nNumDummy + ms_nNumEmergency + ms_nNumCop
				+ ms_nTotalGangPeds + ms_nNumCivFemale + ms_nNumCivMale;
			if (!CCutsceneMgr::IsRunning()) {
				float pcdm = PedCreationDistMultiplier();
				AddToPopulation(pcdm * (MIN_CREATION_DIST * TheCamera.GenerationDistMultiplier),
					pcdm * ((MIN_CREATION_DIST + CREATION_RANGE) * TheCamera.GenerationDistMultiplier),
					pcdm * (MIN_CREATION_DIST + CREATION_RANGE) * OFFSCREEN_CREATION_MULT - CREATION_RANGE,
					pcdm * (MIN_CREATION_DIST + CREATION_RANGE) * OFFSCREEN_CREATION_MULT);
			}
		}
	}
}

void
CPopulation::GeneratePedsAtStartOfGame()
{
	for (int i = 0; i < 50; i++) {
		ms_nTotalCivPeds = ms_nNumCivFemale + ms_nNumCivMale;
		ms_nTotalGangPeds = ms_nNumGang9 + ms_nNumGang8 + ms_nNumGang7
			+ ms_nNumGang6 + ms_nNumGang5 + ms_nNumGang4
			+ ms_nNumGang3 + ms_nNumGang2 + ms_nNumGang1;
		ms_nTotalPeds = ms_nNumDummy + ms_nNumEmergency + ms_nNumCop
			+ ms_nTotalGangPeds + ms_nNumCivFemale + ms_nNumCivMale;

		// Min dist is 10.0f only for start of the game (naturally)
		AddToPopulation(10.0f, PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE),
			10.0f, PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE));
	}
}

bool
CPopulation::IsPointInSafeZone(CVector *coors)
{
	for (int i = 0; i < ARRAY_SIZE(aSafeZones); i++) {
		if (coors->x > aSafeZones[i].x1 && coors->x < aSafeZones[i].x2) {
			if (coors->y > aSafeZones[i].y1 && coors->y < aSafeZones[i].y2) {
				if (coors->z > aSafeZones[i].z1 && coors->z < aSafeZones[i].z2)
					return true;
			}
		}
	}
	return false;
}

// More speed = wider area to spawn peds
float
CPopulation::PedCreationDistMultiplier()
{
	CVehicle *veh = FindPlayerVehicle();
	if (!veh)
		return 1.0f;

	float vehSpeed = veh->m_vecMoveSpeed.Magnitude2D();
	return Clamp(vehSpeed - 0.1f + 1.0f, 1.0f, 1.5f);
}

CPed*
CPopulation::AddPed(ePedType pedType, uint32 miOrCopType, CVector const &coors)
{
	switch (pedType) {
		case PEDTYPE_CIVMALE:
		case PEDTYPE_CIVFEMALE:
		{
			CCivilianPed *ped = new CCivilianPed(pedType, miOrCopType);
			ped->SetPosition(coors);
			ped->SetOrientation(0.0f, 0.0f, 0.0f);
			CWorld::Add(ped);
			if (ms_bGivePedsWeapons) {
				eWeaponType weapon = (eWeaponType)CGeneral::GetRandomNumberInRange(WEAPONTYPE_UNARMED, WEAPONTYPE_DETONATOR);
				if (weapon != WEAPONTYPE_UNARMED) {
					ped->SetCurrentWeapon(ped->GiveWeapon(weapon, 25001));
				}
			}
			return ped;
		}
		case PEDTYPE_COP:
		{
			CCopPed *ped = new CCopPed((eCopType)miOrCopType);
			ped->SetPosition(coors);
			ped->SetOrientation(0.0f, 0.0f, 0.0f);
			CWorld::Add(ped);
			return ped;
		}
		case PEDTYPE_GANG1:
		case PEDTYPE_GANG2:
		case PEDTYPE_GANG3:
		case PEDTYPE_GANG4:
		case PEDTYPE_GANG5:
		case PEDTYPE_GANG6:
		case PEDTYPE_GANG7:
		case PEDTYPE_GANG8:
		case PEDTYPE_GANG9:
		{
			CCivilianPed *ped = new CCivilianPed(pedType, miOrCopType);
			ped->SetPosition(coors);
			ped->SetOrientation(0.0f, 0.0f, 0.0f);
			CWorld::Add(ped);

			uint32 weapon;
			if (CGeneral::GetRandomNumberInRange(0, 100) >= 50)
				weapon = ped->GiveWeapon((eWeaponType)CGangs::GetGangInfo(pedType - PEDTYPE_GANG1)->m_Weapon2, 25001);
			else
				weapon = ped->GiveWeapon((eWeaponType)CGangs::GetGangInfo(pedType - PEDTYPE_GANG1)->m_Weapon1, 25001);
			ped->SetCurrentWeapon(weapon);
			return ped;
		}
		case PEDTYPE_EMERGENCY:
		{
			CEmergencyPed *ped = new CEmergencyPed(PEDTYPE_EMERGENCY);
		    ped->SetPosition(coors);
			ped->SetOrientation(0.0f, 0.0f, 0.0f);
			CWorld::Add(ped);
			return ped;
		}
		case PEDTYPE_FIREMAN:
		{
			CEmergencyPed *ped = new CEmergencyPed(PEDTYPE_FIREMAN);
		    ped->SetPosition(coors);
			ped->SetOrientation(0.0f, 0.0f, 0.0f);
			CWorld::Add(ped);
			return ped;
		}
		case PEDTYPE_CRIMINAL:
		case PEDTYPE_PROSTITUTE:
		{
			CCivilianPed *ped = new CCivilianPed(pedType, miOrCopType);
			ped->SetPosition(coors);
			ped->SetOrientation(0.0f, 0.0f, 0.0f);
			CWorld::Add(ped);
			return ped;
		}
		default:
			Error("Unknown ped type, AddPed, Population.cpp");
			return nil;
	}
}

void
CPopulation::AddToPopulation(float minDist, float maxDist, float minDistOffScreen, float maxDistOffScreen)
{
	uint32 pedTypeToAdd;
	int32 modelToAdd;
	int pedAmount;

	CZoneInfo zoneInfo;
	CPed *gangLeader = nil;
	bool addCop = false;
	CPlayerInfo *playerInfo = &CWorld::Players[CWorld::PlayerInFocus];
	CVector playerCentreOfWorld = FindPlayerCentreOfWorld(CWorld::PlayerInFocus);
	CTheZones::GetZoneInfoForTimeOfDay(&playerCentreOfWorld, &zoneInfo);
	CWanted *wantedInfo = playerInfo->m_pPed->m_pWanted;
	if (wantedInfo->GetWantedLevel() > 2) {
		if (ms_nNumCop < wantedInfo->m_MaxCops && !playerInfo->m_pPed->bInVehicle
			&& (CCarCtrl::NumLawEnforcerCars >= wantedInfo->m_MaximumLawEnforcerVehicles
				|| CCarCtrl::NumRandomCars >= playerInfo->m_nTrafficMultiplier * CCarCtrl::CarDensityMultiplier
				|| CCarCtrl::NumFiretrucksOnDuty + CCarCtrl::NumAmbulancesOnDuty + CCarCtrl::NumParkedCars
				+ CCarCtrl::NumMissionCars + CCarCtrl::NumLawEnforcerCars + CCarCtrl::NumRandomCars >= CCarCtrl::MaxNumberOfCarsInUse)) {
			addCop = true;
			minDist = PedCreationDistMultiplier() * MIN_CREATION_DIST;
			maxDist = PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE);
		}
	}
	// Yeah, float
	float maxPossiblePedsForArea = (zoneInfo.pedDensity + zoneInfo.carDensity) * playerInfo->m_fRoadDensity * PedDensityMultiplier * CIniFile::PedNumberMultiplier;
	maxPossiblePedsForArea = Min(maxPossiblePedsForArea, MaxNumberOfPedsInUse);

	if (ms_nTotalPeds < maxPossiblePedsForArea || addCop) {
		int decisionThreshold = CGeneral::GetRandomNumberInRange(0, 1000);
		if (decisionThreshold < zoneInfo.copDensity || addCop) {
			pedTypeToAdd = PEDTYPE_COP;
			modelToAdd = ChoosePolicePedOccupation();
		} else {
			int16 density = zoneInfo.copDensity;
			for (int i = 0; i < NUM_GANGS; i++) {
				density += zoneInfo.gangDensity[i];
				if (decisionThreshold < density) {
					pedTypeToAdd = PEDTYPE_GANG1 + i;
					break;
				}

				if (i == NUM_GANGS - 1) {
					modelToAdd = ChooseCivilianOccupation(zoneInfo.pedGroup);
					if (modelToAdd == -1)
						return;
					pedTypeToAdd = ((CPedModelInfo*)CModelInfo::GetModelInfo(modelToAdd))->m_pedType;
				}

			}
		}
		if (!addCop && m_AllRandomPedsThisType > PEDTYPE_PLAYER1)
			pedTypeToAdd = m_AllRandomPedsThisType;

		if (pedTypeToAdd >= PEDTYPE_GANG1 && pedTypeToAdd <= PEDTYPE_GANG9) {
			int randVal = CGeneral::GetRandomNumber() % 100;
			if (randVal < 50)
				return;

			if (randVal < 57) {
				pedAmount = 1;
			} else if (randVal >= 74) {
				if (randVal >= 85)
					pedAmount = 4;
				else
					pedAmount = 3;
			} else {
				pedAmount = 2;
			}
		} else
			pedAmount = 1;

		CVector generatedCoors;
		int node1, node2;
		float randomPos;
		bool foundCoors = !!ThePaths.GeneratePedCreationCoors(playerCentreOfWorld.x, playerCentreOfWorld.y, minDist, maxDist, minDistOffScreen, maxDistOffScreen,
			&generatedCoors, &node1, &node2, &randomPos, nil);

		if (!foundCoors)
			return;

		for (int i = 0; i < pedAmount; ++i) {
			if (pedTypeToAdd >= PEDTYPE_GANG1 && pedTypeToAdd <= PEDTYPE_GANG9)
				modelToAdd = ChooseGangOccupation(pedTypeToAdd - PEDTYPE_GANG1);

			if (pedTypeToAdd == PEDTYPE_COP) {
				// Unused code, ChoosePolicePedOccupation returns COP_STREET. Spawning FBI/SWAT/Army done in somewhere else.
				if (modelToAdd == COP_STREET) {
					if (!CModelInfo::GetModelInfo(MI_COP)->GetRwObject())
						return;

				} else if (modelToAdd == COP_FBI) {
					if (!CModelInfo::GetModelInfo(MI_FBI)->GetRwObject())
						return;

				} else if (modelToAdd == COP_SWAT) {
					if (!CModelInfo::GetModelInfo(MI_SWAT)->GetRwObject())
						return;

				} else if (modelToAdd == COP_ARMY && !CModelInfo::GetModelInfo(MI_ARMY)->GetRwObject()) {
					return;
				}
			} else if (!CModelInfo::GetModelInfo(modelToAdd)->GetRwObject()) {
				return;
			}
			generatedCoors.z += 0.7f;

			// What? How can this not be met?
			if (i < pedAmount) {
				//rand()
				if (gangLeader) {
					// Align gang members in formation. (btw i can't be 0 in here)
					float offsetMin = i * 0.75f;
					float offsetMax = (i + 1.0f) * 0.75f - offsetMin;
					float xOffset = CGeneral::GetRandomNumberInRange(offsetMin, offsetMin + offsetMax);
					float yOffset = CGeneral::GetRandomNumberInRange(offsetMin, offsetMin + offsetMax);
					if (CGeneral::GetRandomNumber() & 1)
						xOffset = -xOffset;
					if (CGeneral::GetRandomNumber() & 1)
						yOffset = -yOffset;
					generatedCoors.x = xOffset + gangLeader->GetPosition().x;
					generatedCoors.y = yOffset + gangLeader->GetPosition().y;
				}
			}
			if (!CPedPlacement::IsPositionClearForPed(&generatedCoors))
				break;

			// Why no love for last gang member?!
			if (i + 1 < pedAmount) {
				bool foundGround;
				float groundZ = CWorld::FindGroundZFor3DCoord(generatedCoors.x, generatedCoors.y, 2.0f + generatedCoors.z, &foundGround) + 0.7f;
				if (!foundGround)
					return;

				generatedCoors.z = Max(generatedCoors.z, groundZ);
			}
			bool farEnoughToAdd = true;
			if (TheCamera.IsSphereVisible(generatedCoors, 2.0f)) {
				if (PedCreationDistMultiplier() * MIN_CREATION_DIST > (generatedCoors - playerCentreOfWorld).Magnitude2D())
					farEnoughToAdd = false;
			}
			if (!farEnoughToAdd)
				break;
			CPed *newPed = AddPed((ePedType)pedTypeToAdd, modelToAdd, generatedCoors);
			newPed->SetWanderPath(CGeneral::GetRandomNumberInRange(0, 8));

			if (i != 0) {
				// Gang member
				newPed->SetLeader(gangLeader);
#if !defined(FIX_BUGS) && GTA_VERSION >= GTA3_PC_10
				// seems to be a miami leftover (this code is not on PS2) but gang peds end up just being frozen
				newPed->SetPedState(PED_UNKNOWN);
				gangLeader->SetPedState(PED_UNKNOWN);
				newPed->m_fRotationCur = CGeneral::GetRadianAngleBetweenPoints(
					gangLeader->GetPosition().x, gangLeader->GetPosition().y,
					newPed->GetPosition().x, newPed->GetPosition().y);
				newPed->m_fRotationDest = newPed->m_fRotationCur;
#endif
			} else {
				gangLeader = newPed;
			}
			CVisibilityPlugins::SetClumpAlpha(newPed->GetClump(), 0);
			/*
			// Pointless, this is already a for loop
			if (i + 1 > pedAmount)
				break;
			if (pedAmount <= 1)
				break; */
		}
	}
}

CPed*
CPopulation::AddPedInCar(CVehicle* car)
{
	int defaultModel = MI_MALE01;
	bool imSureThatModelIsLoaded = true;
	CVector coors = FindPlayerCoors();
	CZoneInfo zoneInfo;
	int pedType;

	// May be eCopType, model index or non-sense(for medic), AddPed knows that by looking to ped type.
	int preferredModel;

	CTheZones::GetZoneInfoForTimeOfDay(&coors, &zoneInfo);
	switch (car->GetModelIndex()) {
		case MI_FIRETRUCK:
			preferredModel = 0;
			pedType = PEDTYPE_FIREMAN;
			break;
		case MI_AMBULAN:
			preferredModel = 0;
			pedType = PEDTYPE_EMERGENCY;
			break;
		case MI_FBICAR:
			preferredModel = COP_FBI;
			pedType = PEDTYPE_COP;
			break;
		case MI_POLICE:
			preferredModel = COP_STREET;
			pedType = PEDTYPE_COP;
			break;
		case MI_ENFORCER:
			preferredModel = COP_SWAT;
			pedType = PEDTYPE_COP;
			break;
		case MI_RHINO:
		case MI_BARRACKS:
			preferredModel = COP_ARMY;
			pedType = PEDTYPE_COP;
			break;
		case MI_TAXI:
		case MI_CABBIE:
		case MI_BORGNINE:
			if (CGeneral::GetRandomTrueFalse()) {
				pedType = PEDTYPE_CIVMALE;
				preferredModel = MI_TAXI_D;
				break;
			}
			defaultModel = MI_TAXI_D;

			// fall through
		default:
			int gangOfPed = 0;
			imSureThatModelIsLoaded = false;

			while (gangOfPed < NUM_GANGS && CGangs::GetGangInfo(gangOfPed)->m_nVehicleMI != car->GetModelIndex())
				gangOfPed++;

			if (gangOfPed < NUM_GANGS) {
				pedType = gangOfPed + PEDTYPE_GANG1;
				preferredModel = ChooseGangOccupation(gangOfPed);
			} else if (gangOfPed == NUM_GANGS) {
			    CVehicleModelInfo *carModelInfo = ((CVehicleModelInfo *)CModelInfo::GetModelInfo(car->GetModelIndex()));
				int i = 15;
				for(; i >= 0; i--) {
					// Should return random model each time
					preferredModel = ChooseCivilianOccupation(zoneInfo.pedGroup);
					if (preferredModel == -1)
						preferredModel = defaultModel;

					if (((CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel))->m_carsCanDrive & (1 << carModelInfo->m_vehicleClass))
						break;
				}
				if (i == -1)
					preferredModel = defaultModel;

				pedType = ((CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel))->m_pedType;
			}
			break;
	}
	if (!imSureThatModelIsLoaded && !((CPedModelInfo*)CModelInfo::GetModelInfo(preferredModel))->GetRwObject()) {
		preferredModel = defaultModel;
		pedType = ((CPedModelInfo*)CModelInfo::GetModelInfo(defaultModel))->m_pedType;
	}

	CPed *newPed = CPopulation::AddPed((ePedType)pedType, preferredModel, car->GetPosition());
	newPed->bUsesCollision = false;

	// what??
	if (pedType != PEDTYPE_COP) {
		newPed->SetCurrentWeapon(WEAPONTYPE_COLT45);
		newPed->RemoveWeaponModel(CWeaponInfo::GetWeaponInfo(newPed->GetWeapon()->m_eWeaponType)->m_nModelId);
	}

	// Miami leftover
	if (car->m_vehType == VEHICLE_TYPE_BIKE) {
		newPed->m_pVehicleAnim = CAnimManager::BlendAnimation(newPed->GetClump(), ASSOCGRP_STD, ((CBike*)car)->m_bikeSitAnimation, 100.0f);
	} else

	// FIX: Make peds comfortable while driving car/boat
#ifdef FIX_BUGS
	{
		newPed->m_pVehicleAnim = CAnimManager::BlendAnimation(newPed->GetClump(), ASSOCGRP_STD, car->GetDriverAnim(), 100.0f);
	}
#else
	{
		newPed->m_pVehicleAnim = CAnimManager::BlendAnimation(newPed->GetClump(), ASSOCGRP_STD, ANIM_STD_CAR_SIT, 100.0f);
	}
#endif

	newPed->StopNonPartialAnims();
	return newPed;
}

void
CPopulation::MoveCarsAndPedsOutOfAbandonedZones()
{
	eLevelName level;
	int zone;
	int frame = CTimer::GetFrameCounter() & 7;
	if (frame == 1) {
		int movedVehicleCount = 0;
		int poolSize = CPools::GetVehiclePool()->GetSize();
		for (int poolIndex = poolSize - 1; poolIndex >= 0; poolIndex--) {

			CVehicle* veh = CPools::GetVehiclePool()->GetSlot(poolIndex);
			if (veh && veh->m_nZoneLevel == LEVEL_GENERIC && veh->IsCar()) {

				if(veh->GetStatus() != STATUS_ABANDONED && veh->GetStatus() != STATUS_WRECKED && veh->GetStatus() != STATUS_PLAYER &&
					veh->GetStatus() != STATUS_PLAYER_REMOTE) {

					CVector vehPos(veh->GetPosition());
					CPopulation::FindCollisionZoneForCoors(&vehPos, &zone, &level);

					// Level 0 is transition zones, and we don't wanna touch cars on transition zones.
					if (level != LEVEL_GENERIC && level != CCollision::ms_collisionInMemory && vehPos.z > -4.0f) {
						if (veh->bIsLocked || !veh->CanBeDeleted()) {
							switch (movedVehicleCount & 3) {
								case 0:
									veh->SetPosition(RegenerationPoint_a);
									break;
								case 1:
									veh->SetPosition(RegenerationPoint_b);
									break;
								case 2:
									veh->SetPosition(RegenerationPoint_a.x, RegenerationPoint_b.y, RegenerationPoint_a.z);
									break;
								case 3:
									veh->SetPosition(RegenerationPoint_b.x, RegenerationPoint_a.y, RegenerationPoint_a.z);
									break;
								default:
									break;
							}
							veh->GetMatrix().GetPosition().z += (movedVehicleCount / 4) * 7.0f;
							veh->GetMatrix().GetForward() = RegenerationFront;
							((CAutomobile*)veh)->PlaceOnRoadProperly();
							CCarCtrl::JoinCarWithRoadSystem(veh);
							CTheScripts::ClearSpaceForMissionEntity(veh->GetPosition(), veh);
							++movedVehicleCount;
						} else {
							CWorld::Remove(veh);
							delete veh;
						}
					}
				}
			}
		}
	} else if (frame == 5) {
		int poolSize = CPools::GetPedPool()->GetSize();
		for (int poolIndex = poolSize - 1; poolIndex >= 0; poolIndex--) {

			CPed *ped = CPools::GetPedPool()->GetSlot(poolIndex);
			if (ped && ped->m_nZoneLevel == LEVEL_GENERIC && !ped->bInVehicle) {

				CVector pedPos(ped->GetPosition());
				CPopulation::FindCollisionZoneForCoors(&pedPos, &zone, &level);

				// Level 0 is transition zones, and we don't wanna touch peds on transition zones.
				if (level != LEVEL_GENERIC && level != CCollision::ms_collisionInMemory && pedPos.z > -4.0f) {
					if (ped->CanBeDeleted()) {
						CWorld::Remove(ped);
						delete ped;
					} else if (ped->m_nPedType != PEDTYPE_PLAYER1 && ped->m_nPedType != PEDTYPE_PLAYER2) {
						ped->SetPosition(RegenerationPoint_a);

						bool foundGround;
						float groundZ = CWorld::FindGroundZFor3DCoord(ped->GetPosition().x, ped->GetPosition().y,
							ped->GetPosition().z + 2.0f, &foundGround);

						if (foundGround) {
							ped->GetMatrix().GetPosition().z = 1.0f + groundZ;
							//ped->GetPosition().z += 0.0f;
							CTheScripts::ClearSpaceForMissionEntity(ped->GetPosition(), ped);
						}
					}
				}
			}
		}
	}
}

void
CPopulation::ConvertAllObjectsToDummyObjects()
{
	uint32 i = CPools::GetObjectPool()->GetSize();
	while(i--) {
		CObject *obj = CPools::GetObjectPool()->GetSlot(i);
		if (obj) {
			if (obj->CanBeDeleted())
				ConvertToDummyObject(obj);
		}
	}
}

void
CPopulation::ConvertToRealObject(CDummyObject *dummy)
{
	if (!TestSafeForRealObject(dummy))
		return;

	CObject *obj = new CObject(dummy);
	if (!obj)
		return;

	CWorld::Remove(dummy);
	delete dummy;
	CWorld::Add(obj);

	if (IsGlass(obj->GetModelIndex())) {
		obj->bIsVisible = false;
	} else if (obj->GetModelIndex() == MI_BUOY) {
		obj->SetIsStatic(false);
		obj->m_vecMoveSpeed = CVector(0.0f, 0.0f, -0.001f);
		obj->bTouchingWater = true;
		obj->AddToMovingList();
	}
}

void
CPopulation::ConvertToDummyObject(CObject *obj)
{
	CDummyObject *dummy = new CDummyObject(obj);

	dummy->GetMatrix() = obj->m_objectMatrix;
	dummy->GetMatrix().UpdateRW();
	dummy->UpdateRwFrame();

	if (IsGlass(obj->GetModelIndex()))
		dummy->bIsVisible = false;

	CWorld::Remove(obj);
	delete obj;
	CWorld::Add(dummy);
}

bool
CPopulation::TestRoomForDummyObject(CObject *obj)
{
	int16 collidingObjs;
	CWorld::FindObjectsKindaColliding(obj->m_objectMatrix.GetPosition(), CModelInfo::GetColModel(obj->GetModelIndex())->boundingSphere.radius,
		false, &collidingObjs, 2, nil, false, true, true, false, false);

	return collidingObjs == 0;
}

bool
CPopulation::TestSafeForRealObject(CDummyObject *dummy)
{
	CPtrNode *ptrNode;
	CColModel *dummyCol = dummy->GetColModel();

	float radius = dummyCol->boundingSphere.radius;
	int minX = CWorld::GetSectorIndexX(dummy->GetPosition().x - radius);
	if (minX < 0) minX = 0;
	int minY = CWorld::GetSectorIndexY(dummy->GetPosition().y - radius);
	if (minY < 0) minY = 0;
	int maxX = CWorld::GetSectorIndexX(dummy->GetPosition().x + radius);
#ifdef FIX_BUGS
	if (maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X - 1;
#else
	if (maxX >= NUMSECTORS_X) maxX = NUMSECTORS_X;
#endif

	int maxY = CWorld::GetSectorIndexY(dummy->GetPosition().y + radius);
#ifdef FIX_BUGS
	if (maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y - 1;
#else
	if (maxY >= NUMSECTORS_Y) maxY = NUMSECTORS_Y;
#endif

	float colRadius = dummy->GetBoundRadius();
	CVUVECTOR colCentre;
	dummy->GetBoundCentre(colCentre);

	static CColPoint aTempColPoints[MAX_COLLISION_POINTS];

	for (int curY = minY; curY <= maxY; curY++) {
		for (int curX = minX; curX <= maxX; curX++) {
			CSector *sector = CWorld::GetSector(curX, curY);

			for (ptrNode = sector->m_lists[ENTITYLIST_VEHICLES].first; ptrNode; ptrNode = ptrNode->next) {
				CVehicle *veh = (CVehicle*)ptrNode->item;
				if (veh->m_scanCode != CWorld::GetCurrentScanCode()) {
					if (veh->GetIsTouching(colCentre, colRadius)) {
						veh->m_scanCode = CWorld::GetCurrentScanCode();
						if (CCollision::ProcessColModels(dummy->GetMatrix(), *dummyCol, veh->GetMatrix(), *veh->GetColModel(), aTempColPoints, nil, nil) > 0)
							return false;
					}
				}
			}

			for (ptrNode = sector->m_lists[ENTITYLIST_VEHICLES_OVERLAP].first; ptrNode; ptrNode = ptrNode->next) {
				CVehicle *veh = (CVehicle*)ptrNode->item;
				if (veh->m_scanCode != CWorld::GetCurrentScanCode()) {
					if (veh->GetIsTouching(colCentre, colRadius)) {
						veh->m_scanCode = CWorld::GetCurrentScanCode();
						if (CCollision::ProcessColModels(dummy->GetMatrix(), *dummyCol, veh->GetMatrix(), *veh->GetColModel(), aTempColPoints, nil, nil) > 0)
							return false;
					}
				}
			}
		}
	}
	return true;
}

void
CPopulation::ManagePopulation(void)
{
	int frameMod32 = CTimer::GetFrameCounter() & 31;
	CVector playerPos = FindPlayerCentreOfWorld(CWorld::PlayerInFocus);

	// Why this code is here?! Delete temporary objects when they got too far, and convert others to "dummy" objects. (like lamp posts)
	int objectPoolSize = CPools::GetObjectPool()->GetSize();
	for (int i = objectPoolSize * frameMod32 / 32; i < objectPoolSize * (frameMod32 + 1) / 32; i++) {
		CObject *obj = CPools::GetObjectPool()->GetSlot(i);
		if (obj && obj->CanBeDeleted()) {
			if ((obj->GetPosition() - playerPos).Magnitude() <= 80.0f ||
				(obj->m_objectMatrix.GetPosition() - playerPos).Magnitude() <= 80.0f) {
				if (obj->ObjectCreatedBy == TEMP_OBJECT && CTimer::GetTimeInMilliseconds() > obj->m_nEndOfLifeTime) {
					CWorld::Remove(obj);
					delete obj;
				}
			} else {
				if (obj->ObjectCreatedBy == TEMP_OBJECT) {
					CWorld::Remove(obj);
					delete obj;
				} else if (obj->ObjectCreatedBy != CUTSCENE_OBJECT && TestRoomForDummyObject(obj)) {
					ConvertToDummyObject(obj);
				}
			}
		}
	}

	// Convert them back to real objects. Dummy objects don't have collisions, so they need to be converted.
	int dummyPoolSize = CPools::GetDummyPool()->GetSize();
	for (int i = dummyPoolSize * frameMod32 / 32; i < dummyPoolSize * (frameMod32 + 1) / 32; i++) {
		CDummy *dummy = CPools::GetDummyPool()->GetSlot(i);
		if (dummy) {
			if ((dummy->GetPosition() - playerPos).Magnitude() < 80.0f)
				ConvertToRealObject((CDummyObject*)dummy);
		}
	}

	int pedPoolSize = CPools::GetPedPool()->GetSize();
#ifndef SQUEEZE_PERFORMANCE
	for (int poolIndex = pedPoolSize-1; poolIndex >= 0; poolIndex--) {
#else
	for (int poolIndex = (pedPoolSize * (frameMod32 + 1) / 32) - 1; poolIndex >= pedPoolSize * frameMod32 / 32; poolIndex--) {
#endif
		CPed *ped = CPools::GetPedPool()->GetSlot(poolIndex);

		if (ped && !ped->IsPlayer() && ped->CanBeDeleted() && !ped->bInVehicle) {
			if (ped->m_nPedState == PED_DEAD && CTimer::GetTimeInMilliseconds() - ped->m_bloodyFootprintCountOrDeathTime > 60000)
				ped->bFadeOut = true;

			if (ped->bFadeOut && CVisibilityPlugins::GetClumpAlpha(ped->GetClump()) == 0) {
				RemovePed(ped);
				continue;
			}

			float dist = (ped->GetPosition() - playerPos).Magnitude2D();

			bool pedIsFarAway = false;
			if (PedCreationDistMultiplier() * (PED_REMOVE_DIST_SPECIAL * TheCamera.GenerationDistMultiplier) < dist
				|| (!ped->bCullExtraFarAway && PedCreationDistMultiplier() * PED_REMOVE_DIST * TheCamera.GenerationDistMultiplier < dist)
#ifndef EXTENDED_OFFSCREEN_DESPAWN_RANGE
				|| (PedCreationDistMultiplier() * (MIN_CREATION_DIST + CREATION_RANGE) * OFFSCREEN_CREATION_MULT < dist
				&& !ped->GetIsOnScreen()
				&& TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_SNIPER
				&& TheCamera.Cams[TheCamera.ActiveCam].Mode != CCam::MODE_SNIPER_RUNABOUT
				&& !TheCamera.Cams[TheCamera.ActiveCam].LookingLeft
				&& !TheCamera.Cams[TheCamera.ActiveCam].LookingRight
				&& !TheCamera.Cams[TheCamera.ActiveCam].LookingBehind)
#endif
				)
				pedIsFarAway = true;

			if (!pedIsFarAway)
				continue;

			if (ped->m_nPedState == PED_DEAD && !ped->bFadeOut) {
				CVector pedPos = ped->GetPosition();

				float randAngle = (uint8) CGeneral::GetRandomNumber() * (3.14f / 128.0f); // Not PI, 3.14
				switch (CGeneral::GetRandomNumber() % 3) {
					case 0:
						CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpOutline1Tex, &pedPos,
							0.9f * Cos(randAngle), 0.9f * Sin(randAngle), 0.9f * Sin(randAngle), -0.9f * Cos(randAngle),
							255, 255, 255, 255, 4.0f, 40000, 1.0f);
						break;
					case 1:
						CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpOutline2Tex, &pedPos,
							0.9f * Cos(randAngle), 0.9f * Sin(randAngle), 0.9f * Sin(randAngle), -0.9f * Cos(randAngle),
							255, 255, 255, 255, 4.0f, 40000, 1.0f);
						break;
					case 2:
						CShadows::AddPermanentShadow(SHADOWTYPE_DARK, gpOutline3Tex, &pedPos,
							0.9f * Cos(randAngle), 0.9f * Sin(randAngle), 0.9f * Sin(randAngle), -0.9f * Cos(randAngle),
							255, 255, 255, 255, 4.0f, 40000, 1.0f);
						break;
					default:
						break;
				}
			}
			if (ped->GetIsOnScreen())
				ped->bFadeOut = true;
			else
				RemovePed(ped);
		}
	}
}


code:
#pragma once

#include "Game.h"
#include "PedType.h"

class CPed;
class CVehicle;
class CDummyObject;
class CObject;

struct PedGroup
{
	int32 models[NUMMODELSPERPEDGROUP];
};

// Don't know the original name
struct RegenerationPoint
{
	eLevelName srcLevel; // this and below one may need to be exchanged
	eLevelName destLevel;
	float x1;
	float x2;
	float y1;
	float y2;
	float z1;
	float z2;
	RwV3d destPosA;
	RwV3d destPosB;
	RwV3d srcPosA;
	RwV3d srcPosB;
};

class CPopulation
{
public:
	static PedGroup ms_pPedGroups[NUMPEDGROUPS];
	static bool ms_bGivePedsWeapons;
	static int32 m_AllRandomPedsThisType;
	static float PedDensityMultiplier;
	static uint32 ms_nTotalMissionPeds;
	static int32 MaxNumberOfPedsInUse;
	static uint32 ms_nNumCivMale;
	static uint32 ms_nNumCivFemale;
	static uint32 ms_nNumCop;
	static bool bZoneChangeHasHappened;
	static uint32 ms_nNumEmergency;
	static int8 m_CountDownToPedsAtStart;
	static uint32 ms_nNumGang1;
	static uint32 ms_nNumGang2;
	static uint32 ms_nTotalPeds;
	static uint32 ms_nNumGang3;
	static uint32 ms_nTotalGangPeds;
	static uint32 ms_nNumGang4;
	static uint32 ms_nTotalCivPeds;
	static uint32 ms_nNumGang5;
	static uint32 ms_nNumDummy;
	static uint32 ms_nNumGang6;
	static uint32 ms_nNumGang9;
	static uint32 ms_nNumGang7;
	static uint32 ms_nNumGang8;
	static CVector RegenerationPoint_a;
	static CVector RegenerationPoint_b;
	static CVector RegenerationFront;

	static void Initialise();
	static void Update(void);
	static void LoadPedGroups();
	static void UpdatePedCount(ePedType, bool);
	static void DealWithZoneChange(eLevelName oldLevel, eLevelName newLevel, bool);
	static CPed *AddPedInCar(CVehicle *car);
	static bool IsPointInSafeZone(CVector *coors);
	static void RemovePed(CPed *ent);
	static int32 ChooseCivilianOccupation(int32);
	static int32 ChoosePolicePedOccupation();
	static int32 ChooseGangOccupation(int);
	static void FindCollisionZoneForCoors(CVector*, int*, eLevelName*);
	static void FindClosestZoneForCoors(CVector*, int*, eLevelName, eLevelName);
	static void GeneratePedsAtStartOfGame();
	static float PedCreationDistMultiplier();
	static CPed *AddPed(ePedType pedType, uint32 mi, CVector const &coors);
	static void AddToPopulation(float, float, float, float);
	static void ManagePopulation(void);
	static void MoveCarsAndPedsOutOfAbandonedZones(void);
	static void ConvertToRealObject(CDummyObject*);
	static void ConvertToDummyObject(CObject*);
	static void ConvertAllObjectsToDummyObjects(void);
	static bool TestRoomForDummyObject(CObject*);
	static bool TestSafeForRealObject(CDummyObject*);
};

license: mit


code:
#pragma once

enum {
	EFFECT_LIGHT,
	EFFECT_PARTICLE,
	EFFECT_ATTRACTOR
};

enum {
	LIGHT_ON,
	LIGHT_ON_NIGHT,
	LIGHT_FLICKER,
	LIGHT_FLICKER_NIGHT,
	LIGHT_FLASH1,
	LIGHT_FLASH1_NIGHT,
	LIGHT_FLASH2,
	LIGHT_FLASH2_NIGHT,
	LIGHT_FLASH3,
	LIGHT_FLASH3_NIGHT,
	LIGHT_RANDOM_FLICKER,
	LIGHT_RANDOM_FLICKER_NIGHT,
	LIGHT_SPECIAL,
	LIGHT_BRIDGE_FLASH1,
	LIGHT_BRIDGE_FLASH2,
};

enum {
	ATTRACTORTYPE_ICECREAM,
	ATTRACTORTYPE_STARE
};

enum {
	LIGHTFLAG_LOSCHECK = 1,
	// same order as CPointLights flags, must start at 2
	LIGHTFLAG_FOG_NORMAL = 2,	// can have light and fog
	LIGHTFLAG_FOG_ALWAYS = 4,	// fog only
	LIGHTFLAG_FOG = (LIGHTFLAG_FOG_NORMAL|LIGHTFLAG_FOG_ALWAYS)
};

class C2dEffect
{
public:
	struct Light {
		float dist;
		float range;	// of pointlight
		float size;
		float shadowSize;
		uint8 lightType;	// LIGHT_
		uint8 roadReflection;
		uint8 flareType;
		uint8 shadowIntensity;
		uint8 flags;		// LIGHTFLAG_
		RwTexture *corona;
		RwTexture *shadow;
	};
	struct Particle {
		int particleType;
		CVector dir;
		float scale;
	};
	struct Attractor {
		CVector dir;
		int8 type;
		uint8 probability;
	};

	CVector pos;
	CRGBA col;
	uint8 type;
	union {
		Light light;
		Particle particle;
		Attractor attractor;
	};

	C2dEffect(void) {}
	void Shutdown(void){
		if(type == EFFECT_LIGHT){
			if(light.corona)
				RwTextureDestroy(light.corona);
#if GTA_VERSION >= GTA3_PC_11
			light.corona = nil;
#endif
			if(light.shadow)
				RwTextureDestroy(light.shadow);
#if GTA_VERSION >= GTA3_PC_11
			light.shadow = nil;
#endif
		}
	}
};

VALIDATE_SIZE(C2dEffect, 0x34);


code:
#include "common.h"

#include "main.h"
#include "Antennas.h"

CAntenna CAntennas::aAntennas[NUMANTENNAS];

void
CAntennas::Init(void)
{
	int i;
	for(i = 0; i < NUMANTENNAS; i++){
		aAntennas[i].active = false;
		aAntennas[i].updatedLastFrame = false;
	}
}

// Free antennas that aren't used anymore
void
CAntennas::Update(void)
{
	int i;

	for(i = 0; i < NUMANTENNAS; i++){
		if(aAntennas[i].active && !aAntennas[i].updatedLastFrame)
			aAntennas[i].active = false;
		aAntennas[i].updatedLastFrame = false;
	}
}

// Add a new one or update an old one
void
CAntennas::RegisterOne(uint32 id, CVector dir, CVector position, float length)
{
	int i, j;

	for(i = 0; i < NUMANTENNAS; i++)
		if(aAntennas[i].active && aAntennas[i].id == id)
			break;

	if(i >= NUMANTENNAS){
		// not found, register new one

		// find empty slot
		for(i = 0; i < NUMANTENNAS; i++)
			if(!aAntennas[i].active)
				break;

		// there is space
		if(i < NUMANTENNAS){
			aAntennas[i].active = true;
			aAntennas[i].updatedLastFrame = true;
			aAntennas[i].id = id;
			aAntennas[i].segmentLength = length/6.0f;
			for(j = 0; j < 6; j++){
				aAntennas[i].pos[j] = position + dir*j*aAntennas[i].segmentLength;
				aAntennas[i].speed[j] = CVector(0.0f, 0.0f, 0.0f);
			}
		}
	}else{
		// found, update
		aAntennas[i].Update(dir, position);
		aAntennas[i].updatedLastFrame = true;
	}
}

static RwIm3DVertex vertexbufferA[2];

void
CAntennas::Render(void)
{
	int i, j;

	PUSH_RENDERGROUP("CAntennas::Render");
	for(i = 0; i < NUMANTENNAS; i++){
		if(!aAntennas[i].active)
			continue;

		RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
		RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
		RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
		RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
		RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);

		for(j = 0; j < 5; j++){
			RwIm3DVertexSetRGBA(&vertexbufferA[0], 200, 200, 200, 100);
			RwIm3DVertexSetPos(&vertexbufferA[0],
				aAntennas[i].pos[j].x,
				aAntennas[i].pos[j].y,
				aAntennas[i].pos[j].z);
			RwIm3DVertexSetRGBA(&vertexbufferA[1], 200, 200, 200, 100);
			RwIm3DVertexSetPos(&vertexbufferA[1],
				aAntennas[i].pos[j+1].x,
				aAntennas[i].pos[j+1].y,
				aAntennas[i].pos[j+1].z);

			// LittleTest();
			if(RwIm3DTransform(vertexbufferA, 2, nil, 0)){
				RwIm3DRenderLine(0, 1);
				RwIm3DEnd();
			}
		}
	}

	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);

	POP_RENDERGROUP();
}

void
CAntenna::Update(CVector dir, CVector basepos)
{
	int i;

	pos[0] = basepos;
	pos[1] = basepos + dir*segmentLength;

	for(i = 2; i < 6; i++){
		CVector basedir = pos[i-1] - pos[i-2];
		CVector newdir = pos[i] - pos[i-1] +	// drag along
			dir*0.1f +	// also drag up a bit for stiffness
			speed[i];	// and keep moving
		newdir.Normalise();
		newdir *= segmentLength;
		CVector newpos = pos[i-1] + (basedir + newdir)/2.0f;
		speed[i] = (newpos - pos[i])*0.9f;
		pos[i] = newpos;
	}
}


code:
#pragma once

class CAntenna
{
public:
	bool active;
	bool updatedLastFrame;
	uint32 id;
	float segmentLength;
	CVector pos[6];
	CVector speed[6];

	void Update(CVector dir, CVector pos);
};

class CAntennas
{
	// no need to use game's array
	static CAntenna aAntennas[NUMANTENNAS];
public:
	static void Init(void);
	static void Update(void);
	static void RegisterOne(uint32 id, CVector dir, CVector position, float length);
	static void Render(void);
};


code:
#include "common.h"

#include "main.h"
#include "Sprite.h"
#include "Sprite2d.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[5];

float CClouds::CloudRotation;
uint32 CClouds::IndividualRotation;

float CClouds::ms_cameraRoll;
float CClouds::ms_horizonZ;
CRGBA CClouds::ms_colourTop;
CRGBA CClouds::ms_colourBottom;

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::Shutdown(void)
{
	RwTextureDestroy(gpCloudTex[0]);
#if GTA_VERSION >= GTA3_PC_11
	gpCloudTex[0] = nil;
#endif
	RwTextureDestroy(gpCloudTex[1]);
#if GTA_VERSION >= GTA3_PC_11
	gpCloudTex[1] = nil;
#endif
	RwTextureDestroy(gpCloudTex[2]);
#if GTA_VERSION >= GTA3_PC_11
	gpCloudTex[2] = nil;
#endif
	RwTextureDestroy(gpCloudTex[3]);
#if GTA_VERSION >= GTA3_PC_11
	gpCloudTex[3] = nil;
#endif
	RwTextureDestroy(gpCloudTex[4]);
#if GTA_VERSION >= GTA3_PC_11
	gpCloudTex[4] = nil;
#endif
}

void
CClouds::Update(void)
{
	float s = Sin(TheCamera.Orientation - 0.85f);
#ifdef FIX_BUGS
	CloudRotation += CWeather::Wind*s*0.0025f*CTimer::GetTimeStepFix();
	IndividualRotation += (CWeather::Wind*CTimer::GetTimeStep() + 0.3f*CTimer::GetTimeStepFix()) * 60.0f;
#else
	CloudRotation += CWeather::Wind*s*0.0025f;
	IndividualRotation += (CWeather::Wind*CTimer::GetTimeStep() + 0.3f) * 60.0f;
#endif
}

float StarCoorsX[9] = { 0.0f, 0.05f, 0.12f, 0.5f, 0.8f, 0.6f, 0.27f, 0.55f, 0.75f };
float StarCoorsY[9] = { 0.0f, 0.45f, 0.9f, 1.0f, 0.85f, 0.52f, 0.48f, 0.35f, 0.2f };
float StarSizes[9] = { 1.0f, 1.4f, 0.9f, 1.0f, 0.6f, 1.5f, 1.3f, 1.0f, 0.8f };

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 };
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 };
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 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
};
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
};
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
};

uint8 BowRed[6] = { 30, 30, 30, 10, 0, 15 };
uint8 BowGreen[6] = { 0, 15, 30, 30, 0, 0 };
uint8 BowBlue[6] = { 0, 0, 0, 10, 30, 30 };

void
CClouds::Render(void)
{
	int i;
	float szx, szy;
	RwV3d screenpos;
	RwV3d worldpos;

	PUSH_RENDERGROUP("CClouds::Render");

	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 = TheCamera.GetPosition();

	// Moon
	int moonfadeout = Abs(minute - 180);	// fully visible at 3AM
	if(moonfadeout < 180){			// fade in/out 3 hours
		float coverage = Max(CWeather::Foggyness, CWeather::CloudCoverage);
		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, RwTextureGetRaster(gpCoronaTexture[2]));
			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){
		float coverage = Max(CWeather::Foggyness, CWeather::CloudCoverage);
		int brightness = (1.0f - coverage) * starintens;

		// R
		RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[0]));
		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, RwTextureGetRaster(gpCoronaTexture[0]));
		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
	float lowcloudintensity = 1.0f - Max(CWeather::Foggyness, CWeather::CloudCoverage);
	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, RwTextureGetRaster(gpCloudTex[cloudtype]));
			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 bool bCloudOnScreen[37];
		float hilight;

		RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
		RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
		RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCloudTex[4]));
		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 < SCREEN_WIDTH/2){
					hilight = (1.0f - Max(CWeather::Foggyness, CWeather::CloudCoverage)) * (1.0f - sundist/(SCREEN_WIDTH/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 < SCREEN_WIDTH/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,
					(uint16)IndividualRotation/65336.0f * 6.28f + ms_cameraRoll,
					fluffyalpha);
				bCloudOnScreen[i] = true;
			}else
				bCloudOnScreen[i] = false;
		}
		CSprite::FlushSpriteBuffer();

		// Highlights
		RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE);
		RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
		RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCloudTex[3]));

		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(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){
		RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[0]));
		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);

	POP_RENDERGROUP();
}

bool
UseDarkBackground(void)
{
	return TheCamera.GetForward().z < -0.9f || gbShowCollisionPolys;
}

void
CClouds::RenderBackground(int16 topred, int16 topgreen, int16 topblue,
	int16 botred, int16 botgreen, int16 botblue, int16 alpha)
{
	PUSH_RENDERGROUP("CClouds::RenderBackground");

	CVector left = TheCamera.GetRight();
	float c = left.Magnitude2D();
	if(c > 1.0f)
		c = 1.0f;
	ms_cameraRoll = Acos(c);
	if(left.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, SCREEN_WIDTH, SCREEN_HEIGHT);
		CSprite2d::DrawRect(r, ms_colourBottom, ms_colourBottom, ms_colourTop, ms_colourTop);
	}else{
		ms_horizonZ = CSprite::CalcHorizonCoors();

		// Draw top/bottom gradient
		float gradheight = SCREEN_HEIGHT/2.0f;
		float topedge = ms_horizonZ - gradheight;
		float botpos, toppos;
		if(ms_horizonZ > 0.0f && topedge < SCREEN_HEIGHT){
			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 < SCREEN_HEIGHT)
				botpos = ms_horizonZ;
			else{
				float f = (ms_horizonZ - SCREEN_HEIGHT)/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 = SCREEN_HEIGHT;
			}
			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, SCREEN_WIDTH, 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 < SCREEN_HEIGHT){
			// 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, SCREEN_WIDTH, 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(SCREEN_HEIGHT, topedge);
			CSprite2d::DrawRect(CRect(0, 0, SCREEN_WIDTH, 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;
	}

	POP_RENDERGROUP();
}

void
CClouds::RenderHorizon(void)
{
	if(UseDarkBackground())
		return;

	ms_colourBottom.a = 230;
	ms_colourTop.a = 80;

	if(ms_horizonZ > SCREEN_HEIGHT)
		return;

	PUSH_RENDERGROUP("CClouds::RenderHorizon");

	float z1 = Min(ms_horizonZ + SMALLSTRIPHEIGHT, SCREEN_HEIGHT);
	CSprite2d::DrawRectXLU(CRect(0, ms_horizonZ, SCREEN_WIDTH, z1),
		ms_colourBottom, ms_colourBottom, ms_colourTop, ms_colourTop);

	// This is just weird
	float a = SCREEN_HEIGHT/400.0f * HORIZSTRIPHEIGHT +
		SCREEN_HEIGHT/300.0f * Max(TheCamera.GetPosition().z, 0.0f);
	float b = TheCamera.GetUp().z < 0.0f ?
		SCREEN_HEIGHT :
		SCREEN_HEIGHT * Abs(TheCamera.GetRight().z);
	float z2 = z1 + (a + b)*TheCamera.LODDistMultiplier;
	z2 = Min(z2, SCREEN_HEIGHT);
	CSprite2d::DrawRect(CRect(0, z1, SCREEN_WIDTH, z2),
		ms_colourBottom, ms_colourBottom, ms_colourTop, ms_colourTop);

	POP_RENDERGROUP();
}


code:
#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 Shutdown(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);
};


code:
#include "common.h"
#include <stdarg.h>

#include "Console.h"
#include "Font.h"
#include "Timer.h"

#define CONSOLE_X_POS (30.0f)
#define CONSOLE_Y_POS (10.0f)
#define CONSOLE_LINE_HEIGHT (12.0f)

CConsole TheConsole;

void
CConsole::AddLine(char *s, uint8 r, uint8 g, uint8 b)
{
	char tempstr[MAX_STR_LEN+1];

	while (strlen(s) > MAX_STR_LEN) {
		strncpy(tempstr, s, MAX_STR_LEN);
		tempstr[MAX_STR_LEN-1] = '\0';
		s += MAX_STR_LEN - 1;
		AddOneLine(tempstr, r, g, b);
	}
	AddOneLine(s, r, g, b);
}

void
CConsole::AddOneLine(char *s, uint8 r, uint8 g, uint8 b)
{
	int32 StrIndex = (m_nLineCount + m_nCurrentLine) % MAX_LINES;

	for (int32 i = 0; i < MAX_STR_LEN; i++) {
		Buffers[StrIndex][i] = s[i];
		if (s[i] == '\0') break;
	}

	uint8 _strNum1 = m_nLineCount;
	if (_strNum1 < MAX_LINES)
		_strNum1++;

	m_aTimer[StrIndex] = CTimer::GetTimeInMilliseconds();
	Buffers[StrIndex][MAX_STR_LEN-1] = '\0';
	m_aRed[StrIndex] = r;
	m_aGreen[StrIndex] = g;
	m_aBlue[StrIndex] = b;

	if (_strNum1 >= MAX_LINES)
		m_nCurrentLine = (m_nCurrentLine + 1) % MAX_LINES;
	else
		m_nLineCount = _strNum1;

}

void
CConsole::Display()
{
	CFont::SetPropOn();
	CFont::SetBackgroundOff();
	CFont::SetScale(0.6f, 0.6f);
	CFont::SetCentreOff();
	CFont::SetRightJustifyOff();
	CFont::SetJustifyOn();
	CFont::SetRightJustifyWrap(0.0f);
	CFont::SetBackGroundOnlyTextOff();
	CFont::SetFontStyle(FONT_BANK);
#ifndef FIX_BUGS
	CFont::SetPropOff(); // not sure why this is here anyway
#endif
	CFont::SetWrapx(RsGlobal.width);

	while (m_nLineCount != 0 && CTimer::GetTimeInMilliseconds() - m_aTimer[m_nCurrentLine] > 20000) {
		m_nLineCount--;
		m_nCurrentLine = (m_nCurrentLine + 1) % MAX_LINES;
	}

	for (int16 i = 0; i < m_nLineCount; i++) {
		int16 line = (i + m_nCurrentLine) % MAX_LINES;
		CFont::SetColor(CRGBA(0, 0, 0, 200));
		CFont::PrintString(CONSOLE_X_POS + 1.0f, CONSOLE_Y_POS + 1.0f + i * CONSOLE_LINE_HEIGHT, Buffers[line]);
		CFont::SetColor(CRGBA(m_aRed[line], m_aGreen[line], m_aBlue[line], 200));
		CFont::PrintString(CONSOLE_X_POS, CONSOLE_Y_POS + i * CONSOLE_LINE_HEIGHT, Buffers[line]);
	}
}

void
cprintf(char* format, ...)
{
	char s[256];
	va_list vl1, vl2;

	va_start(vl1, format);
	va_copy(vl2, vl1);
	vsprintf(s, format, vl1);
	TheConsole.AddLine(s, 255, 255, 128);
}


code:
#pragma once

class CConsole
{
	enum
	{
		MAX_LINES = 8, // BUG? only shows 7
		MAX_STR_LEN = 40,
	};

	uint8 m_nLineCount;
	uint8 m_nCurrentLine;
	wchar Buffers[MAX_LINES][MAX_STR_LEN];
	uint32 m_aTimer[MAX_LINES];
	uint8 m_aRed[MAX_LINES];
	uint8 m_aGreen[MAX_LINES];
	uint8 m_aBlue[MAX_LINES];
public:
	void AddLine(char *s, uint8 r, uint8 g, uint8 b);
	void AddOneLine(char *s, uint8 r, uint8 g, uint8 b);
	void Display();
	void Init() { m_nCurrentLine = 0; m_nLineCount = 0; }
};

extern CConsole TheConsole;

void cprintf(char*, ...);


code:
#include "common.h"

#include "main.h"
#include "General.h"
#include "Entity.h"
#include "TxdStore.h"
#include "Camera.h"
#include "Sprite.h"
#include "Timer.h"
#include "World.h"
#include "Weather.h"
#include "Collision.h"
#include "Timecycle.h"
#include "Coronas.h"
#include "PointLights.h"
#include "Shadows.h"
#include "Clock.h"
#include "Bridge.h"

struct FlareDef
{
	float position;
	float size;
	int16 red;
	int16 green;
	int16 blue;
	int16 alpha;
	int16 texture;
};

FlareDef SunFlareDef[] = {
	{ -0.5f, 15.0f, 50, 50, 0, 200, 1 },
	{ -1.0f, 10.0f, 50, 20, 0, 200, 2 },
	{ -1.5f, 15.0f, 50, 0, 0, 200, 3 },
	{ -2.5f, 25.0f, 50, 0, 0, 200, 1 },
	{ 0.5f, 12.5f, 40, 40, 25, 200, 1 },
	{ 0.05f, 20.0f, 30, 22, 9, 200, 2 },
	{ 1.3f, 7.5f, 50, 30, 9, 200, 3 },
	{ 0.0f, 0.0f, 255, 255, 255, 255, 0 }
};

FlareDef HeadLightsFlareDef[] = {
	{ -0.5f, 15.5, 70, 70, 70, 200, 1 },
	{ -1.0f, 10.0, 70, 70, 70, 200, 2 },
	{ -1.5f, 5.5f, 50, 50, 50, 200, 3 },
	{ 0.5f, 12.0f, 50, 50, 50, 200, 1 },
	{ 0.05f, 20.0f, 40, 40, 40, 200, 2 },
	{ 1.3f, 8.0f, 60, 60, 60, 200, 3 },
	{ -2.0f, 12.0f, 50, 50, 50, 200, 1 },
	{ -2.3f, 15.0f, 40, 40, 40, 200, 2 },
	{ -3.0f, 16.0f, 40, 40, 40, 200, 3 },
	{ 0.0f, 0.0f, 255, 255, 255, 255, 0 }
};

RwTexture *gpCoronaTexture[9] = { nil, nil, nil, nil, nil, nil, nil, nil, nil };

float CCoronas::LightsMult = 1.0f;
float CCoronas::SunScreenX;
float CCoronas::SunScreenY;
bool CCoronas::bSmallMoon;
bool CCoronas::SunBlockedByClouds;
int CCoronas::bChangeBrightnessImmediately;

CRegisteredCorona CCoronas::aCoronas[NUMCORONAS];

const char aCoronaSpriteNames[][32] = {
	"coronastar",
	"corona",
	"coronamoon",
	"coronareflect",
	"coronaheadlightline",
	"coronahex",
	"coronacircle",
	"coronaringa",
	"streek"
};

void
CCoronas::Init(void)
{
	int i;

	CTxdStore::PushCurrentTxd();
	CTxdStore::SetCurrentTxd(CTxdStore::FindTxdSlot("particle"));

	for(i = 0; i < 9; i++)
		if(gpCoronaTexture[i] == nil)
			gpCoronaTexture[i] = RwTextureRead(aCoronaSpriteNames[i], nil);

	CTxdStore::PopCurrentTxd();

	for(i = 0; i < NUMCORONAS; i++)
		aCoronas[i].id = 0;
}

void
CCoronas::Shutdown(void)
{
	int i;
	for(i = 0; i < 9; i++)
		if(gpCoronaTexture[i]){
			RwTextureDestroy(gpCoronaTexture[i]);
			gpCoronaTexture[i] = nil;
		}
}

void
CCoronas::Update(void)
{
	int i;
	static int LastCamLook = 0;

	LightsMult = Min(LightsMult + 0.03f * CTimer::GetTimeStep(), 1.0f);

	int CamLook = 0;
	if(TheCamera.Cams[TheCamera.ActiveCam].LookingLeft) CamLook |= 1;
	if(TheCamera.Cams[TheCamera.ActiveCam].LookingRight) CamLook |= 2;
	if(TheCamera.Cams[TheCamera.ActiveCam].LookingBehind) CamLook |= 4;
	// BUG?
	if(TheCamera.GetLookDirection() == LOOKING_BEHIND) CamLook |= 8;

	if(LastCamLook != CamLook)
		bChangeBrightnessImmediately = 3;
	else
		bChangeBrightnessImmediately = Max(bChangeBrightnessImmediately-1, 0);
	LastCamLook = CamLook;

	for(i = 0; i < NUMCORONAS; i++)
		if(aCoronas[i].id != 0)
			aCoronas[i].Update();
}

void
CCoronas::RegisterCorona(uint32 id, uint8 red, uint8 green, uint8 blue, uint8 alpha,
	const CVector &coors, float size, float drawDist, RwTexture *tex,
	int8 flareType, uint8 reflection, uint8 LOScheck, uint8 drawStreak, float someAngle)
{
	int i;

	if(sq(drawDist) < (TheCamera.GetPosition() - coors).MagnitudeSqr2D())
		return;

	for(i = 0; i < NUMCORONAS; i++)
		if(aCoronas[i].id == id)
			break;

	if(i == NUMCORONAS){
		// add a new one

		// find empty slot
		for(i = 0; i < NUMCORONAS; i++)
			if(aCoronas[i].id == 0)
				break;
		if(i == NUMCORONAS)
			return;		// no space

		aCoronas[i].fadeAlpha = 0;
		aCoronas[i].offScreen = true;
		aCoronas[i].firstUpdate = true;
		aCoronas[i].renderReflection = false;
		aCoronas[i].lastLOScheck = 0;
		aCoronas[i].sightClear = false;
		aCoronas[i].hasValue[0] = false;
		aCoronas[i].hasValue[1] = false;
		aCoronas[i].hasValue[2] = false;
		aCoronas[i].hasValue[3] = false;
		aCoronas[i].hasValue[4] = false;
		aCoronas[i].hasValue[5] = false;

	}else{
		// use existing one

		if(aCoronas[i].fadeAlpha == 0 && alpha == 0){
			// unregister
			aCoronas[i].id = 0;
			return;
		}
	}

	aCoronas[i].id = id;
	aCoronas[i].red = red;
	aCoronas[i].green = green;
	aCoronas[i].blue = blue;
	aCoronas[i].alpha = alpha;
	aCoronas[i].coors = coors;
	aCoronas[i].size = size;
	aCoronas[i].someAngle = someAngle;
	aCoronas[i].registeredThisFrame = true;
	aCoronas[i].drawDist = drawDist;
	aCoronas[i].texture = tex;
	aCoronas[i].flareType = flareType;
	aCoronas[i].reflection = reflection;
	aCoronas[i].LOScheck = LOScheck;
	aCoronas[i].drawStreak = drawStreak;
}

void
CCoronas::RegisterCorona(uint32 id, uint8 red, uint8 green, uint8 blue, uint8 alpha,
		const CVector &coors, float size, float drawDist, uint8 type,
		int8 flareType, uint8 reflection, uint8 LOScheck, uint8 drawStreak, float someAngle)
{
	RegisterCorona(id, red, green, blue, alpha, coors, size, drawDist,
		gpCoronaTexture[type], flareType, reflection, LOScheck, drawStreak, someAngle);
}

void
CCoronas::UpdateCoronaCoors(uint32 id, const CVector &coors, float drawDist, float someAngle)
{
	int i;

	if(sq(drawDist) < (TheCamera.GetPosition() - coors).MagnitudeSqr2D())
		return;

	for(i = 0; i < NUMCORONAS; i++)
		if(aCoronas[i].id == id)
			break;

	if(i == NUMCORONAS)
		return;

	if(aCoronas[i].fadeAlpha == 0)
		aCoronas[i].id = 0;	// faded out, remove
	else{
		aCoronas[i].coors = coors;
		aCoronas[i].someAngle = someAngle;
	}
}

static RwIm2DVertex vertexbufferX[2];

void
CCoronas::Render(void)
{
	int i, j;
	int screenw, screenh;

	PUSH_RENDERGROUP("CCoronas::Render");

	screenw = RwRasterGetWidth(RwCameraGetRaster(Scene.camera));
	screenh = RwRasterGetHeight(RwCameraGetRaster(Scene.camera));

	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE);
	RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);

	for(i = 0; i < NUMCORONAS; i++){
		for(j = 5; j > 0; j--){
			aCoronas[i].prevX[j] = aCoronas[i].prevX[j-1];
			aCoronas[i].prevY[j] = aCoronas[i].prevY[j-1];
			aCoronas[i].prevRed[j] = aCoronas[i].prevRed[j-1];
			aCoronas[i].prevGreen[j] = aCoronas[i].prevGreen[j-1];
			aCoronas[i].prevBlue[j] = aCoronas[i].prevBlue[j-1];
			aCoronas[i].hasValue[j] = aCoronas[i].hasValue[j-1];
		}
		aCoronas[i].hasValue[0] = false;

		if(aCoronas[i].id == 0 ||
		   aCoronas[i].fadeAlpha == 0 && aCoronas[i].alpha == 0)
			continue;

		CVector spriteCoors;
		float spritew, spriteh;
		if(!CSprite::CalcScreenCoors(aCoronas[i].coors, &spriteCoors, &spritew, &spriteh, true)){
			aCoronas[i].offScreen = true;
			aCoronas[i].sightClear = false;
		}else{
			aCoronas[i].offScreen = false;

			if(spriteCoors.x < 0.0f || spriteCoors.y < 0.0f ||
			   spriteCoors.x > screenw || spriteCoors.y > screenh){
				aCoronas[i].offScreen = true;
				aCoronas[i].sightClear = false;
			}else{
				if(CTimer::GetTimeInMilliseconds() > aCoronas[i].lastLOScheck + 2000){
					aCoronas[i].lastLOScheck = CTimer::GetTimeInMilliseconds();
					aCoronas[i].sightClear = CWorld::GetIsLineOfSightClear(
						aCoronas[i].coors, TheCamera.Cams[TheCamera.ActiveCam].Source,
						true, true, false, false, false, true, false);
				}

				// add new streak point
				if(aCoronas[i].sightClear){
					aCoronas[i].prevX[0] = spriteCoors.x;
					aCoronas[i].prevY[0] = spriteCoors.y;
					aCoronas[i].prevRed[0] = aCoronas[i].red;
					aCoronas[i].prevGreen[0] = aCoronas[i].green;
					aCoronas[i].prevBlue[0] = aCoronas[i].blue;
					aCoronas[i].hasValue[0] = true;
				}

				// if distance too big, break streak
				if(aCoronas[i].hasValue[1]){
					if(Abs(aCoronas[i].prevX[0] - aCoronas[i].prevX[1]) > 50.0f ||
					   Abs(aCoronas[i].prevY[0] - aCoronas[i].prevY[1]) > 50.0f)
						aCoronas[i].hasValue[0] = false;
				}
			}

			if(aCoronas[i].fadeAlpha && spriteCoors.z < aCoronas[i].drawDist){
				float recipz = 1.0f/spriteCoors.z;
				float fadeDistance = aCoronas[i].drawDist / 2.0f;
				float distanceFade = spriteCoors.z < fadeDistance ? 1.0f : 1.0f - (spriteCoors.z - fadeDistance)/fadeDistance;
				int totalFade = aCoronas[i].fadeAlpha * distanceFade;

				if(aCoronas[i].LOScheck)
					RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
				else
					RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);

				// render corona itself
				if(aCoronas[i].texture){
					float fogscale = CWeather::Foggyness*Min(spriteCoors.z, 40.0f)/40.0f + 1.0f;
					if(CCoronas::aCoronas[i].id == SUN_CORE)
						spriteCoors.z = 0.95f * RwCameraGetFarClipPlane(Scene.camera);
					RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(aCoronas[i].texture));
					spriteCoors.z -= 1.5f;

					if(aCoronas[i].texture == gpCoronaTexture[8]){
						// what's this?
						float f = 1.0f - aCoronas[i].someAngle*2.0f/PI;
						float wscale = 6.0f*sq(sq(sq(f))) + 0.5f;
						float hscale = 0.35f - (wscale - 0.5f) * 0.06f;
						hscale = Max(hscale, 0.15f);

						CSprite::RenderOneXLUSprite(spriteCoors.x, spriteCoors.y, spriteCoors.z,
							spritew * aCoronas[i].size * wscale,
							spriteh * aCoronas[i].size * fogscale * hscale,
							CCoronas::aCoronas[i].red / fogscale,
							CCoronas::aCoronas[i].green / fogscale,
							CCoronas::aCoronas[i].blue / fogscale,
							totalFade,
							recipz,
							255);
					}else{
						CSprite::RenderOneXLUSprite_Rotate_Aspect(
							spriteCoors.x, spriteCoors.y, spriteCoors.z,
							spritew * aCoronas[i].size * fogscale,
							spriteh * aCoronas[i].size * fogscale,
							CCoronas::aCoronas[i].red / fogscale,
							CCoronas::aCoronas[i].green / fogscale,
							CCoronas::aCoronas[i].blue / fogscale,
							totalFade,
							recipz,
							20.0f * recipz,
							255);
					}
				}

				// render flares
				if(aCoronas[i].flareType != FLARE_NONE){
					FlareDef *flare;

					switch(aCoronas[i].flareType){
					case FLARE_SUN: flare = SunFlareDef; break;
					case FLARE_HEADLIGHTS: flare = HeadLightsFlareDef; break;
					default: assert(0);
					}

					for(; flare->texture; flare++){
						RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[flare->texture + 4]));
						CSprite::RenderOneXLUSprite(
							(spriteCoors.x - (screenw/2)) * flare->position + (screenw/2),
							(spriteCoors.y - (screenh/2)) * flare->position + (screenh/2),
							spriteCoors.z,
							4.0f*flare->size * spritew/spriteh,
							4.0f*flare->size,
							(flare->red * aCoronas[i].red)>>8,
							(flare->green * aCoronas[i].green)>>8,
							(flare->blue * aCoronas[i].blue)>>8,
							(totalFade * flare->alpha)>>8,
							recipz, 255);
					}
				}
			}
		}
	}

	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE);
	RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
	RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);

	// streaks
	for(i = 0; i < NUMCORONAS; i++){
		if(aCoronas[i].id == 0 || !aCoronas[i].drawStreak)
			continue;

		for(j = 0; j < 5; j++){
			if(!aCoronas[i].hasValue[j] || !aCoronas[i].hasValue[j+1])
				continue;

			int alpha1 = (float)(6 - j) / 6 * 128;
			int alpha2 = (float)(6 - (j+1)) / 6 * 128;

			RwIm2DVertexSetScreenX(&vertexbufferX[0], aCoronas[i].prevX[j]);
			RwIm2DVertexSetScreenY(&vertexbufferX[0], aCoronas[i].prevY[j]);
			RwIm2DVertexSetIntRGBA(&vertexbufferX[0], aCoronas[i].prevRed[j] * alpha1 / 256, aCoronas[i].prevGreen[j] * alpha1 / 256, aCoronas[i].prevBlue[j] * alpha1 / 256, 255);
			RwIm2DVertexSetScreenX(&vertexbufferX[1], aCoronas[i].prevX[j+1]);
			RwIm2DVertexSetScreenY(&vertexbufferX[1], aCoronas[i].prevY[j+1]);
			RwIm2DVertexSetIntRGBA(&vertexbufferX[1], aCoronas[i].prevRed[j+1] * alpha2 / 256, aCoronas[i].prevGreen[j+1] * alpha2 / 256, aCoronas[i].prevBlue[j+1] * alpha2 / 256, 255);

#ifdef FIX_BUGS
			RwIm2DVertexSetScreenZ(&vertexbufferX[0], RwIm2DGetNearScreenZ());
			RwIm2DVertexSetCameraZ(&vertexbufferX[0], RwCameraGetNearClipPlane(Scene.camera));
			RwIm2DVertexSetRecipCameraZ(&vertexbufferX[0], 1.0f/RwCameraGetNearClipPlane(Scene.camera));
			RwIm2DVertexSetScreenZ(&vertexbufferX[1], RwIm2DGetNearScreenZ());
			RwIm2DVertexSetCameraZ(&vertexbufferX[1], RwCameraGetNearClipPlane(Scene.camera));
			RwIm2DVertexSetRecipCameraZ(&vertexbufferX[1], 1.0f/RwCameraGetNearClipPlane(Scene.camera));
#endif

			RwIm2DRenderLine(vertexbufferX, 2, 0, 1);
		}
	}

	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);

	POP_RENDERGROUP();
}

void
CCoronas::RenderReflections(void)
{
	int i;
	CColPoint point;
	CEntity *entity;

	if(CWeather::WetRoads > 0.0f){
		PUSH_RENDERGROUP("CCoronas::RenderReflections");

#ifdef FIX_BUGS
		CSprite::InitSpriteBuffer();
#endif

		RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE);
		RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
		RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
		RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
		RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE);
		RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
		RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpCoronaTexture[3]));

		for(i = 0; i < NUMCORONAS; i++){
			if(aCoronas[i].id == 0 ||
			   aCoronas[i].fadeAlpha == 0 && aCoronas[i].alpha == 0 ||
			   aCoronas[i].reflection == 0)
				continue;

			// check if we want a reflection on this corona
			if(aCoronas[i].renderReflection){
				if(((CTimer::GetFrameCounter() + i) & 0xF) == 0 &&
				   CWorld::ProcessVerticalLine(aCoronas[i].coors, -1000.0f, point, entity, true, false, false, false, true, false, nil))
					aCoronas[i].heightAboveRoad = aCoronas[i].coors.z - point.point.z;
			}else{
				if(CWorld::ProcessVerticalLine(aCoronas[i].coors, -1000.0f, point, entity, true, false, false, false, true, false, nil)){
					aCoronas[i].heightAboveRoad = aCoronas[i].coors.z - point.point.z;
					aCoronas[i].renderReflection = true;
				}
			}

			// Don't draw if reflection is too high
			if(aCoronas[i].renderReflection && aCoronas[i].heightAboveRoad < 20.0f){
				// don't draw if camera is below road
				if(CCoronas::aCoronas[i].coors.z - aCoronas[i].heightAboveRoad > TheCamera.GetPosition().z)
					continue;

				CVector coors = aCoronas[i].coors;
				coors.z -= 2.0f*aCoronas[i].heightAboveRoad;

				CVector spriteCoors;
				float spritew, spriteh;
				if(CSprite::CalcScreenCoors(coors, &spriteCoors, &spritew, &spriteh, true)){
					float drawDist = 0.75f * aCoronas[i].drawDist;
					drawDist = Min(drawDist, 55.0f);
					if(spriteCoors.z < drawDist){
						float fadeDistance = drawDist / 2.0f;
						float distanceFade = spriteCoors.z < fadeDistance ? 1.0f : 1.0f - (spriteCoors.z - fadeDistance)/fadeDistance;
						distanceFade = Clamp(distanceFade, 0.0f, 1.0f);
						float recipz = 1.0f/RwCameraGetNearClipPlane(Scene.camera);
						float heightFade = (20.0f - aCoronas[i].heightAboveRoad)/20.0f;
						int intensity = distanceFade*heightFade * 230.0 * CWeather::WetRoads;

						CSprite::RenderBufferedOneXLUSprite(
#ifdef FIX_BUGS
							spriteCoors.x, spriteCoors.y, spriteCoors.z,
#else
							spriteCoors.x, spriteCoors.y, RwIm2DGetNearScreenZ(),
#endif
							spritew * aCoronas[i].size * 0.75f,
							spriteh * aCoronas[i].size * 2.0f,
							(intensity * CCoronas::aCoronas[i].red)>>8,
							(intensity * CCoronas::aCoronas[i].green)>>8,
							(intensity * CCoronas::aCoronas[i].blue)>>8,
							255,
							recipz,
							255);
					}
				}
			}
		}
		CSprite::FlushSpriteBuffer();

		RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
		RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
		RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
		RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
		RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);

		POP_RENDERGROUP();
	}else{
		for(i = 0; i < NUMCORONAS; i++)
			aCoronas[i].renderReflection = false;
	}
}

void
CCoronas::DoSunAndMoon(void)
{
	// yeah, moon is done somewhere else....

	CVector sunCoors = CTimeCycle::GetSunDirection();
	sunCoors *= 150.0f;
	sunCoors += TheCamera.GetPosition();

	if(CTimeCycle::GetSunDirection().z > -0.2f){
		float size = ((CGeneral::GetRandomNumber()&0xFF) * 0.005f + 10.0f) * CTimeCycle::GetSunSize();
		RegisterCorona(SUN_CORE,
			CTimeCycle::GetSunCoreRed(), CTimeCycle::GetSunCoreGreen(), CTimeCycle::GetSunCoreBlue(),
			255, sunCoors, size,
			999999.88f, TYPE_STAR, FLARE_NONE, REFLECTION_OFF, LOSCHECK_OFF, STREAK_OFF, 0.0f);

		if(CTimeCycle::GetSunDirection().z > 0.0f)
			RegisterCorona(SUN_CORONA,
				CTimeCycle::GetSunCoronaRed(), CTimeCycle::GetSunCoronaGreen(), CTimeCycle::GetSunCoronaBlue(),
				255, sunCoors, 25.0f * CTimeCycle::GetSunSize(),
				999999.88f, TYPE_STAR, FLARE_SUN, REFLECTION_OFF, LOSCHECK_ON, STREAK_OFF, 0.0f);
	}

	CVector spriteCoors;
	float spritew, spriteh;
	if(CSprite::CalcScreenCoors(sunCoors, &spriteCoors, &spritew, &spriteh, true)){
		SunScreenX = spriteCoors.x;
		SunScreenY = spriteCoors.y;
	}else{
		SunScreenX = 1000000.0f;
		SunScreenY = 1000000.0f;
	}
}

void
CRegisteredCorona::Update(void)
{
	if(!registeredThisFrame)
		alpha = 0;

	if(LOScheck &&
	   (CCoronas::SunBlockedByClouds && id == CCoronas::SUN_CORONA ||
	    !CWorld::GetIsLineOfSightClear(coors, TheCamera.GetPosition(), true, false, false, false, false, false))){
		// Corona is blocked, fade out
		fadeAlpha = Max(fadeAlpha - 15.0f*CTimer::GetTimeStep(), 0.0f);
	}else if(offScreen){
		// Same when off screen
		fadeAlpha = Max(fadeAlpha - 15.0f*CTimer::GetTimeStep(), 0.0f);
	}else{
		// Visible
		if(alpha > fadeAlpha){
			// fade in
			fadeAlpha = Min(fadeAlpha + 15.0f*CTimer::GetTimeStep(), alpha);
			if(CCoronas::bChangeBrightnessImmediately)
				fadeAlpha = alpha;
		}else if(alpha < fadeAlpha){
			// too visible, decrease alpha but not below alpha
			fadeAlpha = Max(fadeAlpha - 15.0f*CTimer::GetTimeStep(), alpha);
		}

		// darken scene when the sun is visible
		if(id == CCoronas::SUN_CORONA)
			CCoronas::LightsMult = Max(CCoronas::LightsMult - CTimer::GetTimeStep()*0.06f, 0.6f);
	}

	// remove if invisible
	if(fadeAlpha == 0 && !firstUpdate)
		id = 0;
	firstUpdate = false;
	registeredThisFrame = false;
}

void
CEntity::ProcessLightsForEntity(void)
{
	int i, n;
	C2dEffect *effect;
	CVector pos;
	bool lightOn, lightFlickering;
	uint32 flashTimer1, flashTimer2, flashTimer3;

	if(bRenderDamaged || !bIsVisible || GetUp().z < 0.96f)
		return;

	flashTimer1 = 0;
	flashTimer2 = 0;
	flashTimer3 = 0;

	n = CModelInfo::GetModelInfo(GetModelIndex())->GetNum2dEffects();
	for(i = 0; i < n; i++, flashTimer1 += 0x80, flashTimer2 += 0x100, flashTimer3 += 0x200){
		effect = CModelInfo::GetModelInfo(GetModelIndex())->Get2dEffect(i);

		if(effect->type != EFFECT_LIGHT)
			continue;

		pos = GetMatrix() * effect->pos;

		lightOn = false;
		lightFlickering = false;
		switch(effect->light.lightType){
		case LIGHT_ON:
			lightOn = true;
			break;
		case LIGHT_ON_NIGHT:
			if(CClock::GetHours() > 18 || CClock::GetHours() < 7)
				lightOn = true;
			break;
		case LIGHT_FLICKER:
			if((CTimer::GetTimeInMilliseconds() ^ m_randomSeed) & 0x60)
				lightOn = true;
			else
				lightFlickering = true;
			if((CTimer::GetTimeInMilliseconds()>>11 ^ m_randomSeed) & 3)
				lightOn = true;
			break;
		case LIGHT_FLICKER_NIGHT:
			if(CClock::GetHours() > 18 || CClock::GetHours() < 7 || CWeather::WetRoads > 0.5f){
				if((CTimer::GetTimeInMilliseconds() ^ m_randomSeed) & 0x60)
					lightOn = true;
				else
					lightFlickering = true;
				if((CTimer::GetTimeInMilliseconds()>>11 ^ m_randomSeed) & 3)
					lightOn = true;
			}
			break;
		case LIGHT_FLASH1:
			if((CTimer::GetTimeInMilliseconds() + flashTimer1) & 0x200)
				lightOn = true;
			break;
		case LIGHT_FLASH1_NIGHT:
			if(CClock::GetHours() > 18 || CClock::GetHours() < 7)
				if((CTimer::GetTimeInMilliseconds() + flashTimer1) & 0x200)
					lightOn = true;
			break;
		case LIGHT_FLASH2:
			if((CTimer::GetTimeInMilliseconds() + flashTimer2) & 0x400)
				lightOn = true;
			break;
		case LIGHT_FLASH2_NIGHT:
			if(CClock::GetHours() > 18 || CClock::GetHours() < 7)
				if((CTimer::GetTimeInMilliseconds() + flashTimer2) & 0x400)
					lightOn = true;
			break;
		case LIGHT_FLASH3:
			if((CTimer::GetTimeInMilliseconds() + flashTimer3) & 0x800)
				lightOn = true;
			break;
		case LIGHT_FLASH3_NIGHT:
			if(CClock::GetHours() > 18 || CClock::GetHours() < 7)
				if((CTimer::GetTimeInMilliseconds() + flashTimer3) & 0x800)
					lightOn = true;
			break;
		case LIGHT_RANDOM_FLICKER:
			if(m_randomSeed > 16)
				lightOn = true;
			else{
				if((CTimer::GetTimeInMilliseconds() ^ m_randomSeed*8) & 0x60)
					lightOn = true;
				else
					lightFlickering = true;
				if((CTimer::GetTimeInMilliseconds()>>11 ^ m_randomSeed*8) & 3)
					lightOn = true;
			}
			break;
		case LIGHT_RANDOM_FLICKER_NIGHT:
			if(CClock::GetHours() > 18 || CClock::GetHours() < 7){
				if(m_randomSeed > 16)
					lightOn = true;
				else{
					if((CTimer::GetTimeInMilliseconds() ^ m_randomSeed*8) & 0x60)
						lightOn = true;
					else
						lightFlickering = true;
					if((CTimer::GetTimeInMilliseconds()>>11 ^ m_randomSeed*8) & 3)
						lightOn = true;
				}
			}
			break;
		case LIGHT_BRIDGE_FLASH1:
			if(CBridge::ShouldLightsBeFlashing() && CTimer::GetTimeInMilliseconds() & 0x200)
				lightOn = true;
			break;
		case LIGHT_BRIDGE_FLASH2:
			if(CBridge::ShouldLightsBeFlashing() && (CTimer::GetTimeInMilliseconds() & 0x1FF) < 60)
				lightOn = true;
			break;
		}

		// Corona
		if(lightOn)
			CCoronas::RegisterCorona((uintptr)this + i,
				effect->col.r, effect->col.g, effect->col.b, 255,
				pos, effect->light.size, effect->light.dist,
				effect->light.corona, effect->light.flareType, effect->light.roadReflection,
				effect->light.flags&LIGHTFLAG_LOSCHECK, CCoronas::STREAK_OFF, 0.0f);
		else if(lightFlickering)
			CCoronas::RegisterCorona((uintptr)this + i,
				0, 0, 0, 255,
				pos, effect->light.size, effect->light.dist,
				effect->light.corona, effect->light.flareType, effect->light.roadReflection,
				effect->light.flags&LIGHTFLAG_LOSCHECK, CCoronas::STREAK_OFF, 0.0f);

		// Pointlight
		if(effect->light.flags & LIGHTFLAG_FOG_ALWAYS){
			CPointLights::AddLight(CPointLights::LIGHT_FOGONLY_ALWAYS,
				pos, CVector(0.0f, 0.0f, 0.0f),
				effect->light.range,
				effect->col.r/255.0f, effect->col.g/255.0f, effect->col.b/255.0f,
				CPointLights::FOG_ALWAYS, true);
		}else if(effect->light.flags & LIGHTFLAG_FOG_NORMAL && lightOn && effect->light.range == 0.0f){
			CPointLights::AddLight(CPointLights::LIGHT_FOGONLY,
				pos, CVector(0.0f, 0.0f, 0.0f),
				effect->light.range,
				effect->col.r/255.0f, effect->col.g/255.0f, effect->col.b/255.0f,
				CPointLights::FOG_NORMAL, true);
		}else if(lightOn && effect->light.range != 0.0f){
			if(effect->col.r == 0 && effect->col.g == 0 && effect->col.b == 0){
				CPointLights::AddLight(CPointLights::LIGHT_POINT,
					pos, CVector(0.0f, 0.0f, 0.0f),
					effect->light.range,
					0.0f, 0.0f, 0.0f,
					CPointLights::FOG_NONE, true);
			}else{
				CPointLights::AddLight(CPointLights::LIGHT_POINT,
					pos, CVector(0.0f, 0.0f, 0.0f),
					effect->light.range,
					effect->col.r*CTimeCycle::GetSpriteBrightness()/255.0f,
					effect->col.g*CTimeCycle::GetSpriteBrightness()/255.0f,
					effect->col.b*CTimeCycle::GetSpriteBrightness()/255.0f,
					// half-useless because LIGHTFLAG_FOG_ALWAYS can't be on
					(effect->light.flags & LIGHTFLAG_FOG) >> 1,
					true);
			}
		}

		// Light shadow
		if(effect->light.shadowSize != 0.0f){
			if(lightOn){
				CShadows::StoreStaticShadow((uintptr)this + i, SHADOWTYPE_ADDITIVE,
					effect->light.shadow, &pos,
					effect->light.shadowSize, 0.0f,
					0.0f, -effect->light.shadowSize,
					128,
					effect->col.r*CTimeCycle::GetSpriteBrightness()*effect->light.shadowIntensity/255.0f,
					effect->col.g*CTimeCycle::GetSpriteBrightness()*effect->light.shadowIntensity/255.0f,
					effect->col.b*CTimeCycle::GetSpriteBrightness()*effect->light.shadowIntensity/255.0f,
					15.0f, 1.0f, 40.0f, false, 0.0f);
			}else if(lightFlickering){
				CShadows::StoreStaticShadow((uintptr)this + i, SHADOWTYPE_ADDITIVE,
					effect->light.shadow, &pos,
					effect->light.shadowSize, 0.0f,
					0.0f, -effect->light.shadowSize,
					0, 0.0f, 0.0f, 0.0f,
					15.0f, 1.0f, 40.0f, false, 0.0f);
			}
		}
	}
}


code:
#pragma once

extern RwTexture *gpCoronaTexture[9];

struct CRegisteredCorona
{
	uint32 id;
	uint32 lastLOScheck;
	RwTexture *texture;
	uint8 red;
	uint8 green;
	uint8 blue;
	uint8 alpha;		// alpha when fully visible
	uint8 fadeAlpha;	// actual value used for rendering, faded
	CVector coors;
	float size;
	float someAngle;
	bool registeredThisFrame;
	float drawDist;
	int8 flareType;
	int8 reflection;

	uint8 LOScheck : 1;
	uint8 offScreen : 1;
	uint8 firstUpdate : 1;
	uint8 drawStreak : 1;
	uint8 sightClear : 1;

	bool renderReflection;
	float heightAboveRoad;

	float prevX[6];
	float prevY[6];
	uint8 prevRed[6];
	uint8 prevGreen[6];
	uint8 prevBlue[6];
	bool hasValue[6];

	void Update(void);
};

VALIDATE_SIZE(CRegisteredCorona, 0x80);

class CCoronas
{
	static CRegisteredCorona aCoronas[NUMCORONAS];
public:
	enum {
		SUN_CORE = 1,
		SUN_CORONA
	};
	enum {
		TYPE_STAR,
		TYPE_NORMAL,
		TYPE_MOON,
		TYPE_REFLECT,
		TYPE_HEADLIGHT,
		TYPE_HEX,
		TYPE_CIRCLE,
		TYPE_RING,
		TYPE_STREAK,
	};
	enum {
		FLARE_NONE,
		FLARE_SUN,
		FLARE_HEADLIGHTS
	};
	enum {
		REFLECTION_OFF,
		REFLECTION_ON,
	};
	enum {
		LOSCHECK_OFF,
		LOSCHECK_ON,
	};
	enum {
		STREAK_OFF,
		STREAK_ON,
	};

	static float LightsMult;
	static float SunScreenY;
	static float SunScreenX;
	static bool bSmallMoon;
	static bool SunBlockedByClouds;
	static int bChangeBrightnessImmediately;

	static void Init(void);
	static void Shutdown(void);
	static void Update(void);
	static void RegisterCorona(uint32 id, uint8 red, uint8 green, uint8 blue, uint8 alpha,
		const CVector &coors, float size, float drawDist, RwTexture *tex,
		int8 flareType, uint8 reflection, uint8 LOScheck, uint8 drawStreak, float someAngle);
	static void RegisterCorona(uint32 id, uint8 red, uint8 green, uint8 blue, uint8 alpha,
		const CVector &coors, float size, float drawDist, uint8 type,
		int8 flareType, uint8 reflection, uint8 LOScheck, uint8 drawStreak, float someAngle);
	static void UpdateCoronaCoors(uint32 id, const CVector &coors, float drawDist, float someAngle);
	static void Render(void);
	static void RenderReflections(void);
	static void DoSunAndMoon(void);
};