OutRun/src/main.cc

1390 lines
50 KiB
C++

#include <gint/display.h>
#include <gint/drivers/r61524.h>
#include <gint/rtc.h>
#include <gint/keyboard.h>
#include <gint/gint.h>
#include <gint/defs/types.h>
#include <fxlibc/printf.h>
#include <libprof.h>
#include <gint/usb.h>
#include <gint/usb-ff-bulk.h>
#include <math.h>
#include <vector>
#include "parameters.h"
#include "colors.h"
#include "include/camera.h"
#include "include/segment.h"
#include "include/circuit.h"
#include "include/drawstuff.h"
#include "include/clouds.h"
#include "include/cars.h"
#include "include/menus.h"
#include "include/saves.h"
#include <gint/kmalloc.h>
#include <stdio.h>
#include <gint/clock.h>
#define DEBUGXXX 0
#define OVERCLCK_ACTIVABLE 1
char version[5] = {'V','1','.','0','8'};
extern bopti_image_t player, smoke1, smoke2, smoke3;
extern font_t speedfont, startseq, plate, autofont;
extern bopti_image_t speedhud;
extern bopti_image_t flag, bigflag;
int CurrentCircuitBiome = PLAINS;
std::vector<Segment*> circuit;
std::vector<Clouds*> nuages;
std::vector<Cars*> traffic;
std::vector<MileStone*> jalons;
int MAX_SEGMENT=0;
camera *cam;
char texttosend[1024];
int32_t start_time = 100000000;
uint32_t elapsed_time = 0;
uint32_t score=0;
uint8_t stage=0, mode=0;
int8_t selectedCircuit=0, secondvalidation=0;
uint32_t time_update=0, time_create=0, time_project=0, time_render=0;
uint8_t DiffLevel = 1;
uint8_t CarsNumb = 1;
char PlayerName[3] = { 'S', 'L', 'Y' };
Parameters PlayerPara[1];
struct DataPerf
{
uint8_t update=0;
uint8_t project=0;
uint8_t render=0;
};
extern bool saveexist;
BestRanking HallOfFame[10][5]; // Table of score : 10 levels * 5 best scores
uint32_t FinalTime=0;
bool skipranking=false;
int ranking = -1; // ranking is -1 if not ranked and between 1 and 5 if ranked
#define MAXDATA 120
DataPerf GraphPerf[ MAXDATA ];
unsigned int DataIndex=0;
uint16_t NB_CARS_TRAFFIC=200;
bool stop = false;
bool exitToOS = false;
bool record = false;
bool screenshot = false;
bool ShowDebug1 = false;
bool ShowDebug2 = false;
bool ShowDebug3 = false;
bool BDrawDeco = true;
bool BDrawClds = true;
bool BDrawCars = true;
bool BDrawBack = true;
bool BDrawFPS = false;
bool isOCPossible = false;
bool isOCActivated = false;
bool OptionMode = false;
bool PauseMode = false;
bool SkipTime = false;
///HUD options
bool visualspeedindicator = false;
bool visualnextpoint = true;
// for multilap
bool multilap = false;
uint8_t numlap = 1;
uint8_t currentLap = 1;
int currentpoint = 0;
uint16_t currentcurve=0;
uint8_t shiftcolor=0;
float speed = 0.0;
float maxspeedforward = 5.0;
float maxspeedbackward = 2.0;
int direction = 1;
bool speedcontrol = false;
uint8_t minYRoad = 224; // We will track the upper Y (in fact the minimum Y during the RoadDrawing to optimize the rendering of the Sky
int8_t viewside = 0;
int deltaFarbackground = 0;
int deltaNearbackground = 0;
int lastindex=0;
int CC=0; // current curve
int CS=0;
int EntryLevel;
void initEverything( void )
{
CurrentCircuitBiome = PLAINS;
circuit.clear();
nuages.clear();
traffic.clear();
MAX_SEGMENT=0;
DataIndex=0;
stop = false;
record = false;
screenshot = false;
ShowDebug1 = false;
ShowDebug2 = false;
ShowDebug3 = false;
BDrawDeco = true;
BDrawClds = true;
BDrawCars = true;
BDrawFPS = false;
currentcurve=0;
shiftcolor=0;
speed = 0.0;
maxspeedforward = 5.0;
maxspeedbackward = 2.0;
direction = 1;
speedcontrol = false;
minYRoad = 224; // We will track the upper Y (in fact the minimum Y during the RoadDrawing to optimize the rendering of the Sky
viewside = 0;
deltaFarbackground = 0;
deltaNearbackground = 0;
lastindex=0;
CC=0; // current curve
CS=0;
multilap = false;
numlap = 1;
currentLap = 1;
}
float timepressed = 0.0f;
int directionPressed = 0;
bool CarIsSliding = false;
uint16_t framesmoke = 0;
static void get_inputs( float dt, int index )
{
CC = circuit[index]->Curve;
CS = circuit[index]->Slope;
maxspeedforward = 5.0 - (((float) CS)/100.0f) + (((float) CC)*f2float(cam->cX)/ROAD_WIDTH/20.0f);
key_event_t ev;
while((ev = pollevent()).type != KEYEV_NONE)
{
}
speedcontrol = false;
viewside=0;
if (CC<0)
{
viewside=-1;
if (CurrentCircuitBiome==FINLAND) cam->decX( CC*dt*speed/50); // 50 ); // very hard to turn in Finland with snow
else if (CurrentCircuitBiome==AFRICA) cam->decX( CC*dt*speed/70); //75 ); // hard in Africa with dirt
else cam->decX( CC*dt*speed/85); // 100 ); // more easy on normal roads
}
else if (CC>0)
{
viewside=+1;
if (CurrentCircuitBiome==FINLAND) cam->decX( CC*dt*speed/50); // 50 ); // very hard to turn in Finland with snow
else if (CurrentCircuitBiome==AFRICA) cam->decX( CC*dt*speed/70); //75 ); // hard in Africa with dirt
else cam->decX( CC*dt*speed/85); // 100 ); // more easy on normal roads
}
if(keydown(KEY_LEFT) || keydown(KEY_F5))
{
cam->decX(5.0*speed*direction);
/// This is to implement sliding and speed loss
if (directionPressed!=-1) directionPressed=-1, timepressed = 0.0f;
else if (directionPressed==-1 && CC<0) timepressed += dt;
viewside=-1;
if (CC<0) viewside=-2; // We are in a curve and turning
if (CC>0) viewside=0;
}
else if(keydown(KEY_RIGHT) || keydown(KEY_F6))
{
cam->incX(5.0*speed*direction);
/// This is to implement sliding and speed loss
if (directionPressed!=1) directionPressed=1, timepressed = 0.0f;
else if (directionPressed==1 && CC>0) timepressed += dt;
viewside=1;
if (CC>0) viewside=+2;
if (CC<0) viewside=0;
}
else if (!keydown(KEY_RIGHT) && !keydown(KEY_F6) && !keydown(KEY_LEFT) && !keydown(KEY_F5))
{
directionPressed=0, timepressed = 0.0f;
}
if(keydown(KEY_SHIFT) || keydown(KEY_XOT) || keydown(KEY_F1)) // Accelerates
{
if (direction==-1 && speed > 0)
{
direction = -1;
speed -= 0.5;
if (speed<0) speed=0;
cam->decZ(speed*dt);
}
else
{
direction = 1;
speed+=0.05;
if (speed>maxspeedforward) speed=maxspeedforward;
cam->incZ(speed*dt);
}
speedcontrol = true;
}
if(keydown(KEY_ALPHA) || keydown(KEY_FRAC) || keydown(KEY_F2)) // breaks or rear if speed <0
{
if (direction==1 && speed > 0)
{
direction = 1;
speed -= 0.5;
if (speed<0) speed=0;
cam->incZ(speed*dt);
}
else
{
direction = -1;
speed+=0.025;
if (speed>maxspeedbackward) speed=maxspeedbackward;
cam->decZ(speed*dt);
}
speedcontrol = true;
}
if(keydown(KEY_EXIT)) drawPauseQuit();
if(keydown(KEY_OPTN)) drawOptions();
#if IS_FXLIB==1
if(keydown(KEY_7)) screenshot = true;
if(keydown(KEY_8)) record = !record;
#endif // IS_FXLIB
if (speedcontrol==false)
{
speed-=0.1;
if (speed<0) speed=0;
if (direction==1)
cam->incZ(speed*dt);
else
cam->decZ(speed*dt);
}
// Adjust position of the background
if (lastindex!=index)
{
deltaFarbackground -= CC*speed*dt/250;
deltaNearbackground -= CC*speed*dt/100;
}
lastindex = index;
// adjust speed if we drive on the side of the road
if (fround(cam->cX)<-1*ROAD_WIDTH && speed>2.0) speed=2.0;
if (fround(cam->cX)>ROAD_WIDTH && speed>2.0) speed=2.0;
if (fround(cam->cX)<-1.35*ROAD_WIDTH && speed>0.0)
{
speed=0.0;
cam->cX=fix(-0.0*ROAD_WIDTH); //set the car in the center of the road (was formerly -0.75)
}
if (fround(cam->cX)>1.35*ROAD_WIDTH && speed>0.0)
{
speed=0.0;
cam->cX=fix(0.0*ROAD_WIDTH); //set the car in the center of the road (was formerly 0.75)
}
/// This part controls the sliding of player's car, if we keep pressed the keys to turn too long, the car starts sliding, smoke will appear and speed is decreasing
if (timepressed>=300.0f) // was 500.0f
{
CarIsSliding = true;
cam->decX( CC*dt*speed/50 );
speed = speed * 0.975;
framesmoke++;
}
else
{
CarIsSliding = false;
framesmoke=0;
}
}
static void get_minimum_inputs( void )
{
key_event_t ev;
while((ev = pollevent()).type != KEYEV_NONE)
{
}
if(keydown(KEY_EXIT) || keydown(KEY_SHIFT)) drawPauseQuit();
if(keydown(KEY_OPTN)) drawOptions();
#if IS_FXLIB==1
if(keydown(KEY_7)) screenshot = true;
if(keydown(KEY_8)) record = !record;
#endif // IS_FXLIB
}
int main(void)
{
__printf_enable_fp();
__printf_enable_fixed();
#if IS_FXLIB==1
usb_interface_t const *interfaces[] = { &usb_ff_bulk, NULL };
usb_open(interfaces, GINT_CALL_NULL);
#endif
gint_world_switch( GINT_CALL( is_save_existing ));
if (!saveexist) // if we do not have saves
gint_world_switch( GINT_CALL( saveprogress ) ); // we create a progress file which is empty
gint_world_switch( GINT_CALL( loadprogress )); // then we load it
gint_world_switch( GINT_CALL( is_param_existing ));
if (!saveexist) // if we do not have saves
gint_world_switch( GINT_CALL( saveparameters ) ); // we create a progress file which is empty
gint_world_switch( GINT_CALL( loadparameters )); // then we load it
DiffLevel=PlayerPara[0].DiffLevel;
CarsNumb=PlayerPara[0].CarsNumb;
PlayerName[0]=PlayerPara[0].PlayerName[0];
PlayerName[1]=PlayerPara[0].PlayerName[1];
PlayerName[2]=PlayerPara[0].PlayerName[2];
kmalloc_arena_t *_uram = kmalloc_get_arena("_uram");
kmalloc_gint_stats_t *_uram_stats;
/* allow more RAM */
char const *osv = (char*) 0x80020020;
static kmalloc_arena_t extended_ram = { 0 };
kmalloc_gint_stats_t *extram_stats;
bool canWeAllocate3Mb = false;
#if (OVERCLCK_ACTIVABLE==1)
if (gint[HWCALC] == HWCALC_FXCG50 || gint[HWCALC] == HWCALC_PRIZM)
{
EntryLevel = clock_get_speed();
isOCPossible = true;
}
else
isOCPossible = false;
#endif
if((!strncmp(osv, "03.", 3) && osv[3] <= '6') && gint[HWCALC] == HWCALC_FXCG50) // CG-50
{
extended_ram.name = "extram";
extended_ram.is_default = true;
extended_ram.start = (void *)0x8c200000;
extended_ram.end = (void *)0x8c500000 ;
kmalloc_init_arena(&extended_ram, true);
kmalloc_add_arena(&extended_ram );
canWeAllocate3Mb = true;
}
else if (gint[HWCALC] == HWCALC_PRIZM) // CG-10/20
{
extended_ram.name = "extram";
extended_ram.is_default = true;
uint16_t *vram1, *vram2;
dgetvram(&vram1, &vram2);
dsetvram(vram1, vram1);
extended_ram.start = vram2;
extended_ram.end = (char *)vram2 + 396*224*2;
kmalloc_init_arena(&extended_ram, true);
kmalloc_add_arena(&extended_ram );
canWeAllocate3Mb = false;
}
else if (gint[HWCALC] == HWCALC_FXCG_MANAGER) // CG-50 EMULATOR
{
extended_ram.name = "extram";
extended_ram.is_default = true;
extended_ram.start = (void *)0x88200000;
extended_ram.end = (void *)0x88500000 ;
kmalloc_init_arena(&extended_ram, true);
kmalloc_add_arena(&extended_ram );
canWeAllocate3Mb = true;
}
else abort();
srand( rtc_ticks() );
prof_init();
drawStartTitle();
exitToOS = false;
do
{
// Try to restart from very begining
initEverything();
prof_t perf_update, perf_create, perf_project, perf_render;
start_time = 100000000;
elapsed_time = 0;
score=0;
stage=0;
mode=0;
selectedCircuit=0;
secondvalidation=0;
time_update=0;
time_create=0;
time_project=0;
time_render=0;
ranking = -1;
currentpoint = 0;
exitToOS = false;
stop = false;
bool exitflag = false;
do
{
exitflag = false;
mode = drawMainMenu();
if (mode==0)
{
selectedCircuit = drawMenuCircuitSelect();
if (selectedCircuit != -1) secondvalidation = drawMenuCircuitDetails( selectedCircuit );
if (selectedCircuit != -1 && secondvalidation != -1) exitflag=true;
}
else if (mode==1)
{
selectedCircuit = drawMenuCircuitSelect();
if (selectedCircuit != -1) secondvalidation = drawMenuCircuitDetails( selectedCircuit );
if (selectedCircuit != -1 && secondvalidation != -1) secondvalidation = drawMenuBestTime( selectedCircuit );
if (selectedCircuit != -1 && secondvalidation != -1) exitflag=true;
}
else if (mode==2)
{
drawGameOptions();
PlayerPara[0].DiffLevel = DiffLevel;
PlayerPara[0].CarsNumb = CarsNumb;
PlayerPara[0].PlayerName[0]=PlayerName[0];
PlayerPara[0].PlayerName[1]=PlayerName[1];
PlayerPara[0].PlayerName[2]=PlayerName[2];
gint_world_switch( GINT_CALL( saveparameters ) );
if ((gint[HWCALC] == HWCALC_FXCG50 || gint[HWCALC] == HWCALC_PRIZM) && isOCActivated == true) clock_set_speed( CLOCK_SPEED_F5 );
else if ((gint[HWCALC] == HWCALC_FXCG50 || gint[HWCALC] == HWCALC_PRIZM) && isOCActivated == false && EntryLevel != CLOCK_SPEED_UNKNOWN) clock_set_speed( EntryLevel );
else if ((gint[HWCALC] == HWCALC_FXCG50 || gint[HWCALC] == HWCALC_PRIZM) && isOCActivated == false && EntryLevel == CLOCK_SPEED_UNKNOWN) clock_set_speed( CLOCK_SPEED_DEFAULT );
}
else if (mode==3)
{
drawOptions();
}
else if (mode==4)
{
drawCredit();
}
else if (mode==5)
{
exitToOS = true;
exitflag=true;
}
}
while (!exitflag);
skipranking = false;
if (!exitToOS)
{
if (selectedCircuit==0) CurrentCircuitBiome = PLAINS, multilap = false, currentLap = 1, numlap = 1;
else if (selectedCircuit==1) CurrentCircuitBiome = DESERT, multilap = false, currentLap = 1, numlap = 1;
else if (selectedCircuit==2) CurrentCircuitBiome = USA, multilap = false, currentLap = 1, numlap = 1;
else if (selectedCircuit==3) CurrentCircuitBiome = FINLAND, multilap = false, currentLap = 1, numlap = 1;
else if (selectedCircuit==4) CurrentCircuitBiome = AFRICA, multilap = false, currentLap = 1, numlap = 1;
else if (selectedCircuit==5) CurrentCircuitBiome = PLAINS, multilap = true, currentLap = 1, numlap = 3;
else if (selectedCircuit==6) CurrentCircuitBiome = DESERT, multilap = true, currentLap = 1, numlap = 3;
else if (selectedCircuit==7) CurrentCircuitBiome = USA, multilap = true, currentLap = 1, numlap = 3;
else if (selectedCircuit==8) CurrentCircuitBiome = FINLAND, multilap = true, currentLap = 1, numlap = 3;
else if (selectedCircuit==9) CurrentCircuitBiome = AFRICA, multilap = true, currentLap = 1, numlap = 3;
else CurrentCircuitBiome = PLAINS, multilap = false, currentLap = 1, numlap = 1;
int nbInterestingSegments = (MAX_RENDER_DISTANCE / SEGMENT_LENGTH); // the number of segments to be projected considering the rendering distance
perf_create = prof_make();
prof_enter(perf_create);
initData( ); // Positioning of the Camera
createCircuit( selectedCircuit ); // Creates the circuit
MAX_SEGMENT = circuit.size();
double maxDistance = (double) ((MAX_SEGMENT-1)*SEGMENT_LENGTH);
putBillBoards();
createMilestones( numlap );
createClouds(); // Creates the Sky and Clouds
if (mode==0)
{
// linear circuits needs many cars
if (selectedCircuit>=0 && selectedCircuit<=3)
{
if (CarsNumb==0) NB_CARS_TRAFFIC=50;
else if (CarsNumb==1) NB_CARS_TRAFFIC=100;
else if (CarsNumb==2) NB_CARS_TRAFFIC=200;
else if (CarsNumb==3) NB_CARS_TRAFFIC=300;
else NB_CARS_TRAFFIC=100;
}
else // while circular needs much less
{
if (CarsNumb==0) NB_CARS_TRAFFIC=25;
else if (CarsNumb==1) NB_CARS_TRAFFIC=50;
else if (CarsNumb==2) NB_CARS_TRAFFIC=100;
else if (CarsNumb==3) NB_CARS_TRAFFIC=150;
else NB_CARS_TRAFFIC=10;
}
createTraffic( MAX_SEGMENT ); // Creates the cars
prepareTraffic(); // Prepares the multiple variations of Cars (image scaling)
}
prepareDecoration( CurrentCircuitBiome ); // Prepares the multiple variations of Decoration (image scaling)
prof_leave(perf_create);
time_create = prof_time(perf_create);
//--------------
int indexstart = 0;
int indexend = 0;
float dt=0;
uint16_t l=0;
uint32_t remaining_time;
DataIndex = 0;
bool initiateStart = true; // are we in the start sequence ?
int32_t startCount = 6000000;
int32_t startRemaining;
bool finishSequence = false; // Did we reached the end of the race ?
bool failSequence = false; // Did we run out of time ?
bool stopcounting = false;
while (!stop)
{
//new position
if (!SkipTime)
{
dt = ((float) (time_update+time_render+time_project) / 1000.0);
}
else
{
dt = 0;
SkipTime=false;
}
perf_update = prof_make();
prof_enter(perf_update);
if (!initiateStart && !finishSequence && !failSequence) get_inputs( dt, indexstart ); // The racer is not started : no need to get inputs and we are not in fail or finish sequence
else get_minimum_inputs(); // the we can just have minimum inputs
//former position
if (initiateStart)
{
startCount -= dt*1000;
startRemaining = ((float) (startCount) / 100000.0);
}
if (!stopcounting) start_time -= dt*1000;
if (!stopcounting) elapsed_time += dt*1000;
if (start_time<0) start_time=0;
remaining_time = ((float) (start_time) / 1000000.0);
//--------------
if (fround(cam->cZ)<=0) cam->cZ=fixdouble(0.0);
indexstart = fround(cam->cZ) / SEGMENT_LENGTH;
if (indexstart<0) indexstart=0;
indexend = indexstart+nbInterestingSegments+1;
prof_leave(perf_update);
time_update = prof_time(perf_update);
//--------------
perf_project = prof_make();
prof_enter(perf_project);
minYRoad = SCREEN_HEIGHT;
if (BDrawCars && mode==0)
{
updateTraffic( dt, maxDistance );
for (unsigned int k=0; k<traffic.size(); k++) // Need to project 1 more segment than actual drawing
{
uint16_t CarSegment = traffic[k]->wZ / SEGMENT_LENGTH;
if (CarSegment>=indexstart && CarSegment<indexend)
{
circuit[CarSegment]->CarList.push_back(k);
}
}
float roadpart = f2float(cam->cX)/(float) ROAD_WIDTH;
// Update car positions : car with higher speed change line not to collide with player
for(unsigned int l=0; l<circuit[indexstart]->CarList.size(); l++ ) // For all cars inside that road segment
{
uint8_t indexCar = circuit[indexstart]->CarList[l];
if (traffic[indexCar]->wX>(roadpart-0.25f) && traffic[indexCar]->wX<(roadpart+0.25f) && traffic[indexCar]->Speed>speed)
{
if (traffic[indexCar]->wX>=-0.25) traffic[indexCar]->wX-=0.5; // if we are on one of the most right lanes, we overtake by the left
else traffic[indexCar]->wX<-0.25; // else we change to the lane on the right
}
}
for( unsigned int l=0; l<circuit[indexstart+1]->CarList.size(); l++ ) // For all cars inside that road segment
{
uint8_t indexCar = circuit[indexstart+1]->CarList[l];
if (traffic[indexCar]->wX>(roadpart-0.22f) && traffic[indexCar]->wX<(roadpart+0.22f) && traffic[indexCar]->Speed<speed)
{
speed = traffic[indexCar]->Speed - 0.5;
score = max(0, score-250);
}
}
if (DiffLevel>=1) // Normal or Hard : cars can overtake the other opponents
{
for( unsigned int k=indexstart; k<indexend; k++)
{
for( unsigned int l=0; l<circuit[k]->CarList.size(); l++ ) // For all cars inside that road segment
{
uint8_t indexCar = circuit[k]->CarList[l];
for( unsigned int m=0; m<circuit[k+1]->CarList.size(); m++ )
{
uint8_t indexCar2 = circuit[k+1]->CarList[m];
if (traffic[indexCar]->wX==traffic[indexCar2]->wX && traffic[indexCar]->Speed>=traffic[indexCar2]->Speed) // a car is following and other one just in front of ehr, at the same speed or quicker
{
//if (traffic[indexCar]->wX>=-0.25) traffic[indexCar]->wX-=0.5;
//else traffic[indexCar]->wX=-0.25;
if (traffic[indexCar]->wX>=-0.25) traffic[indexCar]->Turn = -1;
else traffic[indexCar]->Turn = +1;
}
}
}
}
if (DiffLevel==2) // if difficulty set to hard, we allo changing lane (random based if not currently overtaking an other car)
{
for (unsigned int u=indexstart; u<indexend; u++)
{
for (unsigned int v=0; v<circuit[u]->CarList.size(); v++)
{
unsigned int k = circuit[u]->CarList[v];
if (traffic[k]->Turn == 0)
{
int mov = rand() % 1000;
if (mov>=990) // we let the car change its lane in 5% of cases
{
if (traffic[k]->wX==-0.75) traffic[k]->Turn = +1;
else if (traffic[k]->wX==0.75) traffic[k]->Turn = -1;
else
{
int side = -1 + (rand()%3);
traffic[k]->Turn = side;
}
}
}
}
}
}
for (unsigned int u=indexstart; u<indexend; u++)
{
for (unsigned int v=0; v<circuit[u]->CarList.size(); v++)
{
unsigned int k = circuit[u]->CarList[v];
traffic[k]->wX += (float) traffic[k]->Turn * 0.05f;
if (traffic[k]->wX>=-0.775 && traffic[k]->wX<=-0.725) traffic[k]->wX = -0.75, traffic[k]->Turn = 0;
else if (traffic[k]->wX>=-0.275 && traffic[k]->wX<=-0.225) traffic[k]->wX = -0.25, traffic[k]->Turn = 0;
else if (traffic[k]->wX>=0.225 && traffic[k]->wX<=0.275) traffic[k]->wX = 0.25, traffic[k]->Turn = 0;
else if (traffic[k]->wX>=0.725 && traffic[k]->wX<=0.775) traffic[k]->wX = 0.75, traffic[k]->Turn = 0;
}
}
}
}
uint16_t cumulCurve=0;
for (int k=indexstart; k<=indexend; k++) // Need to project 1 more segment than actual drawing
{
projectCircuitFP( k ); // We project the current segment
if (circuit[k]->Y <= minYRoad) minYRoad = circuit[k]->Y;
circuit[k]->CumulatedCurve = cumulCurve; // This is the curve accumulated when we are drawing curves
cumulCurve += circuit[k]->Curve;
if (BDrawCars && mode==0)
for( unsigned int l=0; l<circuit[k]->CarList.size(); l++ ) // For all cars inside that road segment
{
uint8_t indexCar = circuit[k]->CarList[l];
traffic[indexCar]->Project3DFP( cam, k );
}
}
prof_leave(perf_project);
time_project = prof_time(perf_project);
//--------------
perf_render = prof_make();
prof_enter(perf_render);
//if (BDrawBack) drawSkyOptimised( DAY_BLUE_SKY );
//else drawSkyFull( DAY_BLUE_SKY );
//drawSkyFull( DAY_BLUE_SKY );
drawSkyQuick( DAY_BLUE_SKY );
if (BDrawClds)
{
drawClouds( );
}
if (BDrawBack)
{
drawFarBackground( deltaFarbackground, CurrentCircuitBiome );
drawNearBackground( deltaNearbackground, CurrentCircuitBiome );
}
cam->cY = fix( 350+2*CS ) + interpolatePositionY(fround(cam->cZ) );
for( int k=indexend-1; k>=indexstart; k--)
{
currentcurve = circuit[k]->CumulatedCurve;
drawCircuitSegment( k );
if (BDrawDeco)
drawDecoration( k );
if (BDrawCars && mode==0)
for( unsigned int l=0; l<circuit[k]->CarList.size(); l++ ) // For all cars inside that road segment
{
uint8_t indexCar = circuit[k]->CarList[l];
drawTraffic( indexCar );
}
if (BDrawCars && mode==0)
circuit[k]->CarList.clear();
}
uint32_t handicap;
if (DiffLevel==0) handicap=0; // we use normal additional time when easy
else if (DiffLevel==1) handicap=1000000; // we remove 2 seconds when normal
else if (DiffLevel==2) handicap=2000000; // we remove 4 seconds when hard
else handicap=0;
// Action raised when passing through the different points : START, CHECKPOINTS/ FINISH LINE
if (circuit[indexstart]->Special == CHECKPOINT && circuit[indexstart]->CheckValidated == false && circuit[indexstart]->CheckLap!=currentLap)
{
start_time += circuit[indexstart]->Extratime - handicap;
circuit[indexstart]->CheckLap=currentLap;
stage++;
score += 5000;
circuit[indexstart]->CheckValidated = true;
}
else if (circuit[indexstart]->Special == FINISH && circuit[indexstart]->CheckValidated == false && circuit[indexstart]->CheckLap!=currentLap)
{
circuit[indexstart]->CheckLap=currentLap;
stage++;
if (currentLap<numlap)
{
currentLap++;
start_time += circuit[indexstart]->Extratime - handicap;
score += 5000;
cam->decZ( indexstart*SEGMENT_LENGTH );
if (mode==0)
for( unsigned int k=0; k<traffic.size(); k++ )
traffic[k]->wZ-=indexstart*SEGMENT_LENGTH;
}
else
{
circuit[indexstart]->CheckValidated = true;
score+=20000;
finishSequence=true;
stopcounting = true;
FinalTime = elapsed_time;
}
#if DEBUGXXX==1
if (usb_is_open())
{
//sprintf( texttosend, "FNSH : Chrono %D - %D", elapsed_time/1000000, start_time/1000000 );
sprintf( texttosend, "FNSH : Chrono %D - Reste %D - Extra %D --> Total %D", elapsed_time/1000000, (start_time-circuit[indexstart]->Extratime+handicap)/1000000, (circuit[indexstart]->Extratime-handicap)/1000000, start_time/1000000 );
usb_fxlink_text(texttosend, 0);
}
#endif
}
else if (circuit[indexstart]->Special == START && circuit[indexstart]->CheckValidated == false && circuit[indexstart]->CheckLap!=currentLap)
{
if (currentLap==1)
{
score = 0;
start_time = circuit[indexstart]->Extratime - handicap;
circuit[indexstart]->CheckLap=currentLap;
elapsed_time = 0;
}
else
{
circuit[indexstart]->CheckLap = currentLap;
//start_time += circuit[indexstart]->Extratime - handicap;
circuit[indexstart]->CheckValidated = true;
}
#if DEBUGXXX==1
if (usb_is_open())
{
//sprintf( texttosend, "STRT : Chrono %D - %D", elapsed_time/1000000, start_time/1000000 );
sprintf( texttosend, "STRT : Chrono %D - Reste %D - Extra %D --> Total %D", elapsed_time/1000000, (start_time-circuit[indexstart]->Extratime+handicap)/1000000, (circuit[indexstart]->Extratime-handicap)/1000000, start_time/1000000 );
usb_fxlink_text(texttosend, 0);
}
#endif
}
// Draw a message when time is over
if (start_time<=0 && mode == 0)
{
failSequence=true;
stopcounting = true;
}
int mod_base=20;
int mod_comp=10;
if (abs(speed)<1.0) mod_base = 30, mod_comp = 15;
else if (abs(speed)<2.0) mod_base = 20, mod_comp = 10;
else if (abs(speed)<3.0) mod_base = 16, mod_comp = 8;
else if (abs(speed)<4.0) mod_base = 12, mod_comp = 6;
else if (abs(speed)<5.0) mod_base = 8, mod_comp = 4;
else mod_base = 4, mod_comp = 2;
if ((speed==0) || (l%mod_base<=mod_comp)) // the small rick to have the speed impression on wheels and to have the correct view of the car during turns
{
if (viewside==-2) dsubimage( SCREEN_CX-40, SCREEN_HEIGHT-46, &player, 99,1,80,46, DIMAGE_NONE);
else if (viewside==-1) dsubimage( SCREEN_CX-37, SCREEN_HEIGHT-46, &player, 181,1,74,46, DIMAGE_NONE);
else if (viewside==0) dsubimage( SCREEN_CX-36, SCREEN_HEIGHT-46, &player, 257,1,72,46, DIMAGE_NONE);
else if (viewside==1) dsubimage( SCREEN_CX-37, SCREEN_HEIGHT-46, &player, 331,1,74,46, DIMAGE_NONE);
else if (viewside==2) dsubimage( SCREEN_CX-40, SCREEN_HEIGHT-46, &player, 407,1,80,46, DIMAGE_NONE);
}
else
{
if (viewside==-2) dsubimage( SCREEN_CX-40, SCREEN_HEIGHT-46, &player, 99,49,80,46, DIMAGE_NONE);
else if (viewside==-1) dsubimage( SCREEN_CX-37, SCREEN_HEIGHT-46, &player, 181,49,74,46, DIMAGE_NONE);
else if (viewside==0) dsubimage( SCREEN_CX-36, SCREEN_HEIGHT-46, &player, 257,49,72,46, DIMAGE_NONE);
else if (viewside==1) dsubimage( SCREEN_CX-37, SCREEN_HEIGHT-46, &player, 331,49,74,46, DIMAGE_NONE);
else if (viewside==2) dsubimage( SCREEN_CX-40, SCREEN_HEIGHT-46, &player, 407,49,80,46, DIMAGE_NONE);
}
dfont(&plate);
if (viewside==-2) dprint_opt( 205, 203, C_WHITE, C_BLACK, DTEXT_CENTER, DTEXT_CENTER, "%c%c%c", PlayerName[0], PlayerName[1], PlayerName[2] );
else if (viewside==-1) dprint_opt( 202, 203, C_WHITE, C_BLACK, DTEXT_CENTER, DTEXT_CENTER, "%c%c%c", PlayerName[0], PlayerName[1], PlayerName[2] );
else if (viewside==0) dprint_opt( 197, 203, C_WHITE, C_BLACK, DTEXT_CENTER, DTEXT_CENTER, "%c%c%c", PlayerName[0], PlayerName[1], PlayerName[2] );
else if (viewside==1) dprint_opt( 193, 203, C_WHITE, C_BLACK, DTEXT_CENTER, DTEXT_CENTER, "%c%c%c", PlayerName[0], PlayerName[1], PlayerName[2] );
else if (viewside==2) dprint_opt( 189, 203, C_WHITE, C_BLACK, DTEXT_CENTER, DTEXT_CENTER, "%c%c%c", PlayerName[0], PlayerName[1], PlayerName[2] );
if (CarIsSliding)
{
if (framesmoke>=0 && framesmoke<10) dimage(SCREEN_CX-smoke1.width/2, SCREEN_HEIGHT-smoke1.height, &smoke1 );
else if (framesmoke>=10 && framesmoke<20) dimage(SCREEN_CX-smoke2.width/2, SCREEN_HEIGHT-smoke2.height, &smoke2 );
else if (framesmoke>=30 && framesmoke<30) dimage(SCREEN_CX-smoke3.width/2, SCREEN_HEIGHT-smoke3.height, &smoke3 );
else framesmoke=0;
}
if (direction==+1) score += speed*dt/10;
float drawspeed = (float) (speed*5.0f);
dsubimage( 5, 5, &speedhud, 0, 117, 37, 13, DIMAGE_NONE); // speed logo
dsubimage( 135, 5, &speedhud, 0, 130, 37, 13, DIMAGE_NONE); // timer logo
dsubimage( 245, 5, &speedhud, 37, 117, 38, 13, DIMAGE_NONE); // speed logo
dsubimage( 245, 25, &speedhud, 37, 130, 38, 13, DIMAGE_NONE); // timer logo
dfont(&speedfont);
if (drawspeed==0.0)
{
dprint_opt(122,3, C_RGB(0,0,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "0 :", drawspeed );
dprint_opt(120,1, C_RGB(255,0,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "0 :", drawspeed );
}
else
{
dprint_opt(122,3, C_RGB(0,0,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%.1f :", drawspeed );
dprint_opt(120,1, C_RGB(255,0,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%.1f :", drawspeed ); //the ':' char corresponds to "Km/h"
}
if (mode==0)
{
dprint_opt(238,3, C_RGB(0,0,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%.3D ;", remaining_time );
dprint_opt(237,1, C_RGB(255,255,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%.3D ;", remaining_time ); // the ';' char corresponds to "s"
}
else if (mode==1)
{
dprint_opt(238,3, C_RGB(0,0,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%.3D ;", elapsed_time/1000000 );
dprint_opt(237,1, C_RGB(255,255,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%.3D ;", elapsed_time/1000000 ); // the ';' char corresponds to "s"
}
dprint_opt(390,3, C_RGB(0,0,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%.3D", score );
dprint_opt(388,1, C_RGB(255,255,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%.3D", score ); // the ';' char corresponds to "s"
dprint_opt(390,23, C_RGB(0,0,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%.3D", stage );
dprint_opt(388,21, C_RGB(255,255,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%.3D", stage ); // the ';' char corresponds to "s"
if (multilap)
{
dsubimage( 135, 25, &speedhud, 28, 143, 27, 13, DIMAGE_NONE); // Lap logo
dprint_opt(233,23, C_RGB(0,0,0), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%d/%d", currentLap, numlap );
dprint_opt(231,21, C_RGB(255,0,255), C_NONE, DTEXT_RIGHT, DTEXT_TOP, "%d/%d", currentLap, numlap ); // the ';' char corresponds to "s"
}
/// Graphical speed indicator
if (visualspeedindicator)
{
if (drawspeed==0.0) dsubimage( 48, 25, &speedhud, 0, 0, 76, 13, DIMAGE_NONE);
else if (drawspeed<3.30) dsubimage( 48, 25, &speedhud, 0, 13, 76, 13, DIMAGE_NONE);
else if (drawspeed<6.70) dsubimage( 48, 25, &speedhud, 0, 26, 76, 13, DIMAGE_NONE);
else if (drawspeed<10.0) dsubimage( 48, 25, &speedhud, 0, 39, 76, 13, DIMAGE_NONE);
else if (drawspeed<13.3) dsubimage( 48, 25, &speedhud, 0, 52, 76, 13, DIMAGE_NONE);
else if (drawspeed<16.7) dsubimage( 48, 25, &speedhud, 0, 65, 76, 13, DIMAGE_NONE);
else if (drawspeed<20.0) dsubimage( 48, 25, &speedhud, 0, 78, 76, 13, DIMAGE_NONE);
else if (drawspeed<23.3) dsubimage( 48, 25, &speedhud, 0, 91, 76, 13, DIMAGE_NONE);
else dsubimage( 48, 25, &speedhud, 0, 104, 76, 13, DIMAGE_NONE);
}
/// Checkpoint indicator
if (visualnextpoint)
{
double Z1 = jalons[currentpoint]->wZ;
double Z2 = jalons[currentpoint+1]->wZ;
double frac = (fround(cam->cZ) - Z1) / (Z2 - Z1 );
if (frac<0) frac=0;
if (fround(cam->cZ) >= Z2) currentpoint++;
drect( 15, 50, 20, 150, C_WHITE );
drect( 10, 49+frac*100, 25, 51+frac*100, C_RED );
}
if (finishSequence)
{
if (mode==1 && !skipranking)
{
bool donerank = false;
int k=0;
while (!donerank && k<5)
{
if (FinalTime<=HallOfFame[selectedCircuit][k].bestTime)
{
for( int l=4; l>k; l--)
{
HallOfFame[selectedCircuit][l].playerName[0] = HallOfFame[selectedCircuit][l-1].playerName[0];
HallOfFame[selectedCircuit][l].playerName[1] = HallOfFame[selectedCircuit][l-1].playerName[1];
HallOfFame[selectedCircuit][l].playerName[2] = HallOfFame[selectedCircuit][l-1].playerName[2];
HallOfFame[selectedCircuit][l].padding = HallOfFame[selectedCircuit][l-1].padding;
HallOfFame[selectedCircuit][l].bestScore = HallOfFame[selectedCircuit][l-1].bestScore;
HallOfFame[selectedCircuit][l].bestTime = HallOfFame[selectedCircuit][l-1].bestTime;
}
HallOfFame[selectedCircuit][k].playerName[0] = PlayerName[0];
HallOfFame[selectedCircuit][k].playerName[1] = PlayerName[1];
HallOfFame[selectedCircuit][k].playerName[2] = PlayerName[2];
HallOfFame[selectedCircuit][k].padding = 'X';
HallOfFame[selectedCircuit][k].bestScore = score;
HallOfFame[selectedCircuit][k].bestTime = FinalTime;
donerank = true;
skipranking = true;
ranking = k;
gint_world_switch( GINT_CALL( saveprogress ) );
}
else k++;
}
}
speed = 0;
dimage( 198-bigflag.width/2, 112-bigflag.height/2, &bigflag );
if (ranking!=-1 || mode==0)
{
dfont(&startseq);
dprint_opt(200,108, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_BOTTOM, "YOU" );
dprint_opt(198,112, C_RGB(0,255,0), C_NONE, DTEXT_CENTER, DTEXT_BOTTOM, "YOU" );
dprint_opt(200,108, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_TOP, "WIN", ranking );
dprint_opt(198,112, C_RGB(0,255,0), C_NONE, DTEXT_CENTER, DTEXT_TOP, "WIN", ranking );
dfont(&autofont);
uint32_t temp1 = FinalTime/1000000/60;
uint32_t temp2 = FinalTime/1000000 - temp1*60;
uint32_t temp3 = (FinalTime - temp1*60*1000000 - temp2*1000000)/1000;
dprint_opt(200, 202, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_TOP, "%d - %D:%D,%D", ranking+1, temp1, temp2, temp3 );
dprint_opt(198, 200, C_RGB(255, 255, 255), C_NONE, DTEXT_CENTER, DTEXT_TOP, "%d - %D:%D,%D", ranking+1, temp1, temp2, temp3 );
}
else
{
dfont(&startseq);
dprint_opt(200,108, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_BOTTOM, "YOU" );
dprint_opt(198,112, C_RGB(255,0,0), C_NONE, DTEXT_CENTER, DTEXT_BOTTOM, "YOU" );
dprint_opt(200,108, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_TOP, "LOSE" );
dprint_opt(198,112, C_RGB(255,0,0), C_NONE, DTEXT_CENTER, DTEXT_TOP, "LOSE" );
}
}
if (failSequence)
{
speed = 0;
dimage( 198-bigflag.width/2, 112-bigflag.height/2, &bigflag );
dfont(&startseq);
dprint_opt(200,108, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_BOTTOM, "YOU" );
dprint_opt(198,112, C_RGB(255,0,0), C_NONE, DTEXT_CENTER, DTEXT_BOTTOM, "YOU" );
dprint_opt(200,108, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_TOP, "LOSE" );
dprint_opt(198,112, C_RGB(255,0,0), C_NONE, DTEXT_CENTER, DTEXT_TOP, "LOSE" );
}
if (initiateStart) // We are in the start sequence with the countdown
{
dfont(&startseq);
if (startRemaining<54 && startRemaining>46)
{
dprint_opt(200,114, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">5<" );
dprint_opt(198,112, C_RGB(255,0,255), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">5<" );
}
else if (startRemaining<44 && startRemaining>36)
{
dprint_opt(200,114, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">4<" );
dprint_opt(198,112, C_RGB(255,0,255), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">4<" );
}
else if (startRemaining<34 && startRemaining>26)
{
dprint_opt(200,114, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">3<" );
dprint_opt(198,112, C_RGB(255,0,255), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">3<" );
}
else if (startRemaining<24 && startRemaining>16)
{
dprint_opt(200,114, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">2<" );
dprint_opt(198,112, C_RGB(255,0,255), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">2<" );
}
else if (startRemaining<14 && startRemaining>6)
{
dprint_opt(200,114, C_RGB(0,0,0), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">1<" );
dprint_opt(198,112, C_RGB(255,0,255), C_NONE, DTEXT_CENTER, DTEXT_CENTER, ">1<" );
}
else if (startRemaining<4 && startRemaining>-4)
{
dimage( 198-bigflag.width/2, 112-bigflag.height/2, &bigflag );
}
else if (startRemaining<-4)
{
start_time = 99000000;
initiateStart=false;
}
}
//dupdate();
//dprint( 3, 202, C_BLACK, "%.0f", dt );
//dprint( 1, 200, C_WHITE, "%.0f", dt );
if (BDrawFPS)
{
dfont(&speedfont);
dsubimage( 5, 205, &speedhud, 0, 143, 27, 13, DIMAGE_NONE); // fps logo
dprint_opt(42,202, C_RGB(0,0,0), C_NONE, DTEXT_LEFT, DTEXT_TOP, "%.0f <", (float) (1000.0f/dt) ); // the '>' symbol corresponds to "fps"
dprint_opt(40,200, C_RGB(255,255,0), C_NONE, DTEXT_LEFT, DTEXT_TOP, "%.0f <", (float) (1000.0f/dt) );
GraphPerf[DataIndex].update = (uint8_t) (time_update/1000);
GraphPerf[DataIndex].project = (uint8_t) (time_project/1000);
GraphPerf[DataIndex].render = (uint8_t) (time_render/1000);
DataIndex++;
DataIndex = DataIndex % MAXDATA;
dline( 249, 201, 375, 201, C_BLACK );
dline( 249, 202, 375, 202, C_BLACK );
dline( 249, 200-33, 375, 200-33, C_BLACK );
dline( 249, 200-40, 375, 200-40, C_BLACK );
dline( 249, 200-50, 375, 200-50, C_BLACK );
dline( 249, 201, 249, 100, C_BLACK );
dline( 248, 201, 248, 100, C_BLACK );
for( unsigned int k=0; k<MAXDATA-1; k++)
{
dpixel( 250+k, 200-GraphPerf[k].update, C_RGB(255,0,0) );
dpixel( 250+k, 201-GraphPerf[k].update, C_RGB(255,0,0) );
dpixel( 250+k, 200-GraphPerf[k].update-GraphPerf[k].project, C_RGB(0,255,0) );
dpixel( 250+k, 201-GraphPerf[k].update-GraphPerf[k].project, C_RGB(0,255,0) );
dpixel( 250+k, 200-GraphPerf[k].update-GraphPerf[k].project-GraphPerf[k].render, C_RGB(0,0,255) );
dpixel( 250+k, 201-GraphPerf[k].update-GraphPerf[k].project-GraphPerf[k].render, C_RGB(0,0,255) );
}
}
r61524_display(gint_vram, 0, DHEIGHT, R61524_DMA_WAIT);
prof_leave(perf_render);
time_render = prof_time(perf_render);
#if IS_FXLIB==1
if (screenshot && usb_is_open())
{
usb_fxlink_screenshot(false);
screenshot = false;
}
if(record && usb_is_open())
{
usb_fxlink_videocapture(false);
}
#endif
l++;
}
/*
_uram_stats = kmalloc_get_gint_stats(_uram);
sprintf( texttosend, "Memory Status - Used: %d - Free: %d - Peak Used: %d", _uram_stats->used_memory, _uram_stats->free_memory, _uram_stats->peak_used_memory);
usb_fxlink_text(texttosend, 0);
*/
// Free all memory
freeDecoration();
for(unsigned int i=0; i<circuit.size(); i++ ) delete circuit[i]; // Not to forget to delete each elements
circuit.clear();
for(unsigned int i=0; i<jalons.size(); i++ ) delete jalons[i]; // Not to forget to delete each elements
jalons.clear();
for(unsigned int i=0; i<nuages.size(); i++ ) delete nuages[i]; // Not to forget to delete each elements
nuages.clear();
if (mode==0)
{
freeTraffic();
for(unsigned int i=0; i<traffic.size(); i++ )
delete traffic[i]; // Not to forget to delete each elements
traffic.clear();
}
}
}
while (exitToOS==false);
prof_quit();
#if IS_FXLIB==1
usb_close();
#endif
if ((gint[HWCALC] == HWCALC_FXCG50 || gint[HWCALC] == HWCALC_PRIZM) && isOCActivated == true && EntryLevel != CLOCK_SPEED_UNKNOWN) clock_set_speed( EntryLevel );
else if ((gint[HWCALC] == HWCALC_FXCG50 || gint[HWCALC] == HWCALC_PRIZM) && isOCActivated == true && EntryLevel == CLOCK_SPEED_UNKNOWN) clock_set_speed( CLOCK_SPEED_DEFAULT );
delete cam;
// We set back zeros at the end of the program
memset(extended_ram.start, 0, (char *)extended_ram.end - (char *)extended_ram.start);
return 1;
}