speed-dreams/src/libs/robottools/rthumandriver.cpp

2558 lines
95 KiB
C++

/***************************************************************************
created : Sat Mar 18 23:16:38 CET 2000
copyright : (C) 2000 by Eric Espie
email : torcs@free.fr
version : $Id$
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/** @file
@author <a href=mailto:torcs@free.fr>Eric Espie</a>
@version $Id$
*/
/*
* 2013/3/21 Tom Low-Shang
*
* Merged original contents of:
*
* drivers/human/human.cpp,
* drivers/human/human.h,
* drivers/human/pref.cpp,
* drivers/human/pref.h,
*
* except for CMD_* defines which are in interfaces/playerpref.h.
*
* Functions have been renamed for the class interface. Where possible,
* function bodies have not been not been changed, except for indentation.
*/
// Flags for selecting the "binary control steering code" variant :
// * Old variant
#define OLD 0
// * Jepz variant
#define JEPZ 1
// * JPM variant
#define JPM 2
// * if neither JEPZ or JPM variant are selected, the old code is used.
#define BINCTRL_STEERING JPM
#include <map>
#include <vector>
#include <string>
#include <tgfclient.h>
#include <portability.h>
#include "robottools.h" //Rt*
#include <robot.h>
#include <playerpref.h>
#include <car.h>
#include "humandriver.h"
#include "forcefeedback.h"
extern TGFCLIENT_API ForceFeedbackManager forceFeedback;
typedef struct {
const char *name; /* Name of the control */
int type; /* Type of control (analog, joy button, keyboard) */
int val; /* control index */
const char *minName; /* Name of the min attribute */
float min; /* min value for analog control */
float minVal; /* another min value for analog control*/
const char *maxName; /* Name of the max attribute */
float max; /* max value for analog control */
const char *sensName; /* Name of the sens attribute */
float sens; /* sensitivity of control */
const char *powName; /* Name of the pow attribute */
float pow; /* power of curve command = pow(control, power) */
const char *spdSensName; /* Name of the speed sensitivity attribute */
float spdSens; /* speed sensitivity */
const char *deadZoneName; /* Name of the dead zone attribute */
float deadZone; /* dead zone (% of the available zone) */
} tControlCmd;
typedef struct HumanContext
{
int nbPitStops;
int lastPitStopLap;
bool autoReverseEngaged;
tdble shiftThld[MAX_GEARS+1];
tdble gear;
tdble distToStart;
float clutchTime;
float maxClutchTime;
float clutchdelay;
float antiLock;
float antiSlip;
int lap;
float prevLeftSteer;
float prevRightSteer;
float paccel;
float pbrake;
bool manual;
tGearChangeMode transmission;
int nbPitStopProg;
bool paramAsr;
bool paramAbs;
bool relButNeutral;
bool seqShftAllowNeutral;
bool seqShftAllowReverse;
bool autoReverse;
tDriveType driveTrain;
bool autoClutch;
tControlCmd *cmdControl;
bool mouseControlUsed;
int lightCmd;
int dashboardCounter;
int lastForceFeedbackIndex;
int lastForceFeedbackLevel;
int lastForceFeedbackDir;
// simuV4 ...
bool useESP;
float brakeRep;
float brakeCorr;
float brakeFront;
float brakeRear;
float brakeLeft;
float brakeRight;
// ... simuV4
} tHumanContext;
static const int FuelReserve = 3;
static const tdble MaxFuelPerMeter = 0.0008; // [kg/m] fuel consumption.
static void updateKeys(void);
static void SetFuelAtRaceStart(tTrack *track, void *carHandle,
void **carParmHandle, tSituation *s, int idx);
static char sstring[1024];
static char buf[1024];
static tTrack *curTrack;
static const float color[] = {0.0, 0.0, 1.0, 1.0};
static bool joyPresent = false;
static tCtrlJoyInfo *joyInfo = NULL;
static tCtrlMouseInfo *mouseInfo = NULL;
static int ControlsUpdaterIndex = -1;
static std::vector<tHumanContext*> HCtx;
static bool speedLimiter = false;
static tdble speedLimit;
typedef struct
{
int state;
int edgeDn;
int edgeUp;
} tKeyInfo;
// Keyboard map for all players
// (key code => index of the associated command in keyInfo / lastReadKeyState).
typedef std::map<int,int> tKeyMap;
static tKeyMap mapKeys;
static int keyIndex = 0;
// Last read state for each possible player command key.
static int lastReadKeyState[GFUIK_MAX+1];
// Up-to-date info for each possible player command key (state, edge up, edge down)
static tKeyInfo keyInfo[GFUIK_MAX+1];
static bool init_keybd = true;
static bool init_mouse = false;
static bool resume_keybd = true;
static tdble lastKeyUpdate = -10.0;
static void *PrefHdle = NULL;
// Local copy of human driver names (each one allocated by moduleInitialize).
static std::vector<char*> VecNames;
// Number of human drivers (initialized by moduleWelcome).
static int NbDrivers = -1;
// List of permited gear changes with hbox transmission
// prevents mis-selection with thumbstick
// Note 'N' selectable from any gear from ..... 654321NR ... to :
const static int hboxChanges[] = { 0x02, // 0b00000010, // R
0x0B, // 0b00001011, // 1
0x17, // 0b00010111, // 2
0x2A, // 0b00101010, // 3
0x52, // 0b01010010, // 4
0xA2, // 0b10100010, // 5
0x42 // 0b01000010 // 6
};
static int preGear = 0;
/*self note: apparently these should follow thew order of definition of the cmd definitions (see playerpref.h)*/
static const tControlCmd CmdControlRef[] = {
{HM_ATT_UP_SHFT, GFCTRL_TYPE_JOY_BUT, 0, HM_ATT_UP_SHFT_MIN, 0.0, 0.0, HM_ATT_UP_SHFT_MAX, 1.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_DN_SHFT, GFCTRL_TYPE_JOY_BUT, 1, HM_ATT_DN_SHFT_MIN, 0.0, 0.0, HM_ATT_DN_SHFT_MAX, 1.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_ASR_CMD, GFCTRL_TYPE_JOY_BUT, 2, HM_ATT_ASR_MIN, 0.0, 0.0, HM_ATT_ASR_MAX, 1.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_ABS_CMD, GFCTRL_TYPE_JOY_BUT, 3, HM_ATT_ABS_MIN, 0.0, 0.0, HM_ATT_ABS_MAX, 1.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_GEAR_R, GFCTRL_TYPE_NOT_AFFECTED, -1, HM_ATT_GEAR_R_MIN, 0.0, 0.0, HM_ATT_GEAR_R_MAX, 1.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_GEAR_N, GFCTRL_TYPE_NOT_AFFECTED, -1, HM_ATT_GEAR_N_MIN, 0.0, 0.0, HM_ATT_GEAR_N_MAX, 1.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_GEAR_1, GFCTRL_TYPE_NOT_AFFECTED, -1, NULL, 0.0, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_GEAR_2, GFCTRL_TYPE_NOT_AFFECTED, -1, NULL, 0.0, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_GEAR_3, GFCTRL_TYPE_NOT_AFFECTED, -1, NULL, 0.0, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_GEAR_4, GFCTRL_TYPE_NOT_AFFECTED, -1, NULL, 0.0, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_GEAR_5, GFCTRL_TYPE_NOT_AFFECTED, -1, NULL, 0.0, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_GEAR_6, GFCTRL_TYPE_NOT_AFFECTED, -1, NULL, 0.0, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_THROTTLE, GFCTRL_TYPE_JOY_AXIS, 1, HM_ATT_THROTTLE_MIN, 0.0, 0.0, HM_ATT_THROTTLE_MAX, 1.0, HM_ATT_THROTTLE_SENS, 1.0, HM_ATT_THROTTLE_POW, 2.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_BRAKE, GFCTRL_TYPE_JOY_AXIS, 1, HM_ATT_BRAKE_MIN, 0.0, 0.0, HM_ATT_BRAKE_MAX, 1.0, HM_ATT_BRAKE_SENS, 1.0, HM_ATT_BRAKE_POW, 2.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_LEFTSTEER, GFCTRL_TYPE_JOY_AXIS, 0, HM_ATT_LEFTSTEER_MIN, 0.0, 0.0, HM_ATT_LEFTSTEER_MAX, 1.0, HM_ATT_STEER_SENS, 2.0, HM_ATT_LEFTSTEER_POW, 1.0, HM_ATT_STEER_SPD, 0.0, HM_ATT_STEER_DEAD, 0.0},
{HM_ATT_RIGHTSTEER, GFCTRL_TYPE_JOY_AXIS, 0, HM_ATT_RIGHTSTEER_MIN, 0.0, 0.0, HM_ATT_RIGHTSTEER_MAX, 1.0, HM_ATT_STEER_SENS, 2.0, HM_ATT_RIGHTSTEER_POW, 1.0, HM_ATT_STEER_SPD, 0.0, HM_ATT_STEER_DEAD, 0.0},
{HM_ATT_LIGHT1_CMD, GFCTRL_TYPE_NOT_AFFECTED, -1, HM_ATT_LIGHT1_MIN, 0.0, 0.0, HM_ATT_LIGHT1_MAX, 1.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_CLUTCH, GFCTRL_TYPE_NOT_AFFECTED, -1, HM_ATT_CLUTCH_MIN, 0.0, 0.0, HM_ATT_CLUTCH_MAX, 1.0, HM_ATT_CLUTCH_SENS, 1.0, HM_ATT_CLUTCH_POW, 2.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_SPDLIM_CMD, GFCTRL_TYPE_NOT_AFFECTED, -1, HM_ATT_SPDLIM_MIN, 0.0, 0.0, HM_ATT_SPDLIM_MAX, 1.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_EBRAKE_CMD, GFCTRL_TYPE_JOY_BUT, 19, HM_ATT_EBRAKE_MIN, 0.0, 0.0, HM_ATT_EBRAKE_MAX, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_HBOX_X, GFCTRL_TYPE_NOT_AFFECTED, -1, NULL, 0.0, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_HBOX_Y, GFCTRL_TYPE_NOT_AFFECTED, -1, NULL, 0.0, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_LEFTGLANCE, GFCTRL_TYPE_NOT_AFFECTED, -1, HM_ATT_L_GLANCE_MIN, 0.0, 0.0, HM_ATT_L_GLANCE_MAX, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_RIGHTGLANCE,GFCTRL_TYPE_NOT_AFFECTED, -1, HM_ATT_R_GLANCE_MIN, 0.0, 0.0, HM_ATT_R_GLANCE_MAX, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_DASHB_NEXT ,GFCTRL_TYPE_NOT_AFFECTED, -1, HM_ATT_DASHB_NEXT_MIN, 0.0, 0.0, HM_ATT_DASHB_NEXT_MAX, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_DASHB_PREV ,GFCTRL_TYPE_NOT_AFFECTED, -1, HM_ATT_DASHB_PREV_MIN, 0.0, 0.0, HM_ATT_DASHB_PREV_MAX, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_DASHB_INC ,GFCTRL_TYPE_NOT_AFFECTED, -1, HM_ATT_DASHB_INC_MIN, 0.0, 0.0, HM_ATT_DASHB_INC_MAX, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_DASHB_DEC ,GFCTRL_TYPE_NOT_AFFECTED, -1, HM_ATT_DASHB_DEC_MIN, 0.0, 0.0, HM_ATT_DASHB_DEC_MAX, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
{HM_ATT_CAM_LOOKBACK,GFCTRL_TYPE_NOT_AFFECTED, -1, NULL, 0.0, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0, NULL, 0.0},
};
static const int NbCmdControl = sizeof(CmdControlRef) / sizeof(CmdControlRef[0]);
typedef struct
{
const char *settings;
const char *parmName;
} tCtrl;
static tCtrl controlList[] = {
{HM_SECT_JSPREF, HM_VAL_JOYSTICK},
{HM_SECT_KEYBPREF, HM_VAL_KEYBOARD},
{HM_SECT_MOUSEPREF, HM_VAL_MOUSE}
};
static const int nbControl = sizeof(controlList) / sizeof(controlList[0]);
static const std::string Yn[] = {HM_VAL_YES, HM_VAL_NO};
/*
* Changes from original:
*
* Deallocates cmdControl memory.
*/
void HumanDriver::shutdown(const int index)
{
int idx = index - 1;
free(VecNames[idx]);
VecNames[idx] = 0;
if (HCtx[idx]->cmdControl)
{
free(HCtx[idx]->cmdControl);
}
free (HCtx[idx]);
HCtx[idx] = 0;
resume_keybd = true;
}
/*
* Original function: InitFuncPt
*
* Changes from original:
*
* Remove robot interface function assignments which still happens in robot
* InitFuncPt().
*
* Allocate cmdControl memory. Networkhuman does not read preferences for
* remote driver leaving cmdControl uninitialized, which crashes updateKeys().
*
* Add updater_index parameter. Networkhuman does not call drive functions
* for remote player so the controls update may not run. The parameter allows
* robot code to set ControlsUpdaterIndex.
*
* Modify logic for ControlsUpdaterIndex assignment.
*/
void HumanDriver::init_context(int index, int updater_index)
{
const int idx = index - 1;
// Choose this driver as the one who will exclusively read the controls state
// (if no other was choosen in this race).
if (ControlsUpdaterIndex < 0)
{
if (updater_index)
{
ControlsUpdaterIndex = updater_index;
}
else
{
ControlsUpdaterIndex = index;
}
}
// Initialize mouse and joystick controls backend if not already done.
if (!joyInfo)
{
joyInfo = GfctrlJoyCreate();
if (joyInfo) {
joyPresent = true;
}//if joyInfo
}
if (!mouseInfo)
{
mouseInfo = GfctrlMouseCreate();
}
/* Allocate a new context for that player */
if ((int)HCtx.size() < idx + 1)
HCtx.resize(idx + 1);
HCtx[idx] = (tHumanContext *) calloc (1, sizeof (tHumanContext));
HCtx[idx]->cmdControl = (tControlCmd *)calloc(NbCmdControl,
sizeof (tControlCmd));
HCtx[idx]->antiLock = 1.0;
HCtx[idx]->antiSlip = 1.0;
// simuV4 ...
HCtx[idx]->useESP = false;
HCtx[idx]->brakeRep = 0.5f;
HCtx[idx]->brakeCorr = 0.03f;
HCtx[idx]->brakeFront = 1.0f;
HCtx[idx]->brakeRear = 1.0f;
HCtx[idx]->brakeLeft = 1.0f;
HCtx[idx]->brakeRight = 1.0f;
// ... simuV4
read_prefs(index);
}
/*
* Original function: moduleWelcome
*
* Changes from original:
*
* Remove parameters.
*
* Return NbDrivers.
*
* Replace hard coded robot name.
*/
int HumanDriver::count_drivers()
{
// Open and load the drivers params file
snprintf(buf, sizeof(buf), "%sdrivers/%s/%s.xml", GfLocalDir(),
robotname, robotname);
void *drvInfo = GfParmReadFile(buf, GFPARM_RMODE_REREAD | GFPARM_RMODE_CREAT);
// Count the number of human drivers registered in the params
NbDrivers = -1;
if (drvInfo) {
const char *driver;
do {
NbDrivers++;
snprintf(sstring, sizeof(sstring), "Robots/index/%d", NbDrivers+1);
driver = GfParmGetStr(drvInfo, sstring, "name", "");
} while (strlen(driver) > 0);
GfParmReleaseHandle(drvInfo); // Release in case we got it.
}//if drvInfo
return NbDrivers;
}
/*
* Original function: moduleInitialize
*
* Changes from original:
*
* Add function pointer parameter for InitFuncPt.
*
* Replace hard coded robot name.
*/
int HumanDriver::initialize(tModInfo *modInfo, tfModPrivInit InitFuncPt)
{
if (NbDrivers <= 0) {
GfOut("human : No human driver registered, or moduleMaxInterfaces() was not called (NbDrivers=%d)\n", NbDrivers);
return -1;
}
// Reset module interfaces info.
memset(modInfo, 0, NbDrivers*sizeof(tModInfo));
// Clear the local driver name vector (in case needed).
VecNames.clear();
// Open and load the drivers params file
snprintf(buf, sizeof(buf), "%sdrivers/%s/%s.xml", GfLocalDir(),
robotname, robotname);
void *drvInfo = GfParmReadFile(buf, GFPARM_RMODE_REREAD | GFPARM_RMODE_CREAT);
if (drvInfo) {
// Fill the module interfaces info : each driver is associated to 1 interface.
for (int i = 0; i < NbDrivers; i++) {
snprintf(sstring, sizeof(sstring), "Robots/index/%d", i+1);
const char* pszDriverName = GfParmGetStr(drvInfo, sstring, "name", 0);
if (pszDriverName && strlen(pszDriverName) > 0) {
// Don't rely on GfParm allocated data : duplicate the name ;
// and also save the pointer somewhere in order to release it at the end.
char* pszLocDriverName = strdup(pszDriverName);
VecNames.push_back(pszLocDriverName);
modInfo->name = pszLocDriverName; /* name of the module (short) */
modInfo->desc = "Joystick controlable driver"; /* description (can be longer) */
modInfo->fctInit = InitFuncPt; /* init function */
modInfo->gfId = ROB_IDENT; /* supported framework Id */
modInfo->index = i+1;
modInfo++;
}//if strlen
}//for i
GfParmReleaseHandle(drvInfo); // Release in case we got it.
}//if drvInfo
return 0;
}
/*
* Original function: moduleTerminate
*
* Changes from original:
*
* Deallocate cmdControl memory
*/
void HumanDriver::terminate()
{
if (PrefHdle)
{
GfParmReleaseHandle(PrefHdle);
}
if (joyInfo)
{
GfctrlJoyRelease(joyInfo);
}
if (mouseInfo)
{
GfctrlMouseRelease(mouseInfo);
}
GfuiKeyEventRegisterCurrent(0);
// Free the human context vector
std::vector<tHumanContext*>::iterator itDrvCtx = HCtx.begin();
while (itDrvCtx != HCtx.end())
{
if (*itDrvCtx)
{
if ((*itDrvCtx)->cmdControl)
{
free((*itDrvCtx)->cmdControl);
}
free(*itDrvCtx);
}
++itDrvCtx;
}
HCtx.clear();
// Free the local driver name vector
std::vector<char*>::iterator itDrvName = VecNames.begin();
while (itDrvName != VecNames.end())
{
free(*itDrvName);
++itDrvName;
}
VecNames.clear();
mapKeys.clear();
}
/*
* Original function: initTrack
*
* Changes from original:
*
* Search for the car name in robot specific locations but always load the car
* setup for human robot. Technically, this is no longer necessary since
* networkhuman does not call this function for remote drivers.
*
*/
void HumanDriver::init_track(int index,
tTrack* track,
void *carHandle,
void **carParmHandle,
tSituation *s)
{
char trackname[256];
const int idx = index - 1;
curTrack = track;
char *s1 = strrchr(track->filename, '/') + 1;
char *s2 = strchr(s1, '.');
strncpy(trackname, s1, s2 - s1);
trackname[s2 - s1] = 0;
snprintf(sstring, sizeof(sstring), "Robots/index/%d", index);
snprintf(buf, sizeof(buf), "%sdrivers/%s/%s.xml", GfLocalDir(),
robotname, robotname);
void *drvInfo = GfParmReadFile(buf, GFPARM_RMODE_REREAD | GFPARM_RMODE_CREAT);
std::string carname = (drvInfo != NULL)
? GfParmGetStrNC(drvInfo, sstring, "car name", NULL)
: "";
snprintf(sstring, sizeof(sstring), "%sdrivers/curcarnames.xml", GfLocalDir());
void *curCars = GfParmReadFile(sstring, GFPARM_RMODE_REREAD);
if (curCars) {
snprintf(sstring, sizeof(sstring), "drivers/%s/%d",
robotname, index + NbDrivers + 1);
carname = GfParmGetStr(curCars, sstring, "car name", carname.c_str());
}//if curCars
snprintf(sstring, sizeof(sstring), "%s/drivers/human/car.xml", GfLocalDir());
*carParmHandle = GfParmReadFile(sstring, GFPARM_RMODE_REREAD);
snprintf(sstring, sizeof(sstring), "%sdrivers/human/cars/%s/default.xml", GfLocalDir(), carname.c_str());
void *newhandle = GfParmReadFile(sstring, GFPARM_RMODE_REREAD);
if (newhandle) {
*carParmHandle = (*carParmHandle)
? GfParmMergeHandles(*carParmHandle, newhandle,
(GFPARM_MMODE_SRC|GFPARM_MMODE_DST|GFPARM_MMODE_RELSRC|GFPARM_MMODE_RELDST))
: newhandle;
}
snprintf(sstring, sizeof(sstring), "%sdrivers/human/cars/%s/%s.xml", GfLocalDir(), carname.c_str(), trackname);
newhandle = GfParmReadFile(sstring, GFPARM_RMODE_REREAD);
if (newhandle) {
*carParmHandle = (*carParmHandle)
? GfParmMergeHandles(*carParmHandle, newhandle,
(GFPARM_MMODE_SRC|GFPARM_MMODE_DST|GFPARM_MMODE_RELSRC|GFPARM_MMODE_RELDST))
: newhandle;
if (*carParmHandle) {
GfOut("Player: %s Loaded\n", sstring);
}
} else {
if (*carParmHandle) {
GfOut("Player: %s Default Setup Loaded\n", sstring);
}
}//if-else newhandle
if (curTrack->pits.type != TR_PIT_NONE) {
snprintf(sstring, sizeof(sstring), "%s/%s/%d", HM_SECT_PREF, HM_LIST_DRV, index);
HCtx[idx]->nbPitStopProg = (int)GfParmGetNum(PrefHdle, sstring, HM_ATT_NBPITS, (char*)NULL, 0);
GfOut("Player: index %d , Pit stops %d\n", index, HCtx[idx]->nbPitStopProg);
} else {
HCtx[idx]->nbPitStopProg = 0;
}//if-else curTrack->pits
//Initial fuel fill computation
SetFuelAtRaceStart(track, carHandle, carParmHandle, s, idx);
speedLimit = curTrack->pits.speedLimit;
// simuV4 ...
HCtx[idx]->brakeRep = GfParmGetNum(*carParmHandle, (char*) SECT_BRKSYST, PRM_BRKREP, (char*)NULL, 0.5);
HCtx[idx]->brakeCorr = GfParmGetNum(*carParmHandle, (char*) SECT_BRKSYST, PRM_BRKCOR_FR, (char*)NULL, 0.0f);
HCtx[idx]->useESP = HCtx[idx]->brakeCorr != 0;
// ... simuV4
if(drvInfo) {
GfParmReleaseHandle(drvInfo);
}
}
/*
* Original function: newrace
*
* Changes from original: none
*/
void HumanDriver::new_race(int index, tCarElt* car, tSituation *s)
{
const int idx = index - 1;
// Have to read engine curve
char midx[64];
struct tEdesc
{
tdble rpm;
tdble tq;
tdble drpm;
tdble dtq;
} *Edesc;
sprintf(midx, "%s/%s", SECT_ENGINE, ARR_DATAPTS);
int IMax = GfParmGetEltNb(car->_carHandle, midx);
//GfOut("IMax = %d\n", IMax);
Edesc = (struct tEdesc*) malloc((IMax + 1) * sizeof(struct tEdesc));
for (int i = 0; i < IMax; i++) {
sprintf(midx, "%s/%s/%d", SECT_ENGINE, ARR_DATAPTS, i+1);
Edesc[i].rpm = GfParmGetNum(car->_carHandle, midx, PRM_RPM, (char*) NULL, car->_enginerpmMax);
Edesc[i].tq = GfParmGetNum(car->_carHandle, midx, PRM_TQ, (char*) NULL, 0.0f);
Edesc[i].drpm = 0;
Edesc[i].dtq = 0;
if (i > 0) {
Edesc[i-1].drpm = Edesc[i].rpm - Edesc[i-1].rpm;
Edesc[i-1].dtq = Edesc[i].tq - Edesc[i-1].tq;
}
GfOut("rpm %f = tq %f \n", Edesc[i].rpm * 9.549, Edesc[i].tq);
}
// Initialize engine RPM shifting threshold table for automatic shifting mode.
for (int i = 0; i < MAX_GEARS; i++) {
HCtx[idx]->shiftThld[i] = 10000.0;
}
// only calc for forward gears, excluding top gear
for (int i = 2; i < car->_gearNb; i++) {
double rpm;
double newrpm;
double curTorque = 0;
double nextTorque = 0;
if (car->_gearRatio[i] == 0)
continue;
// scan torque to see where changing up gives more power
for (rpm = car->_enginerpmMaxTq; rpm < car->_enginerpmRedLine; rpm += 10) {
curTorque = 0;
nextTorque = 0;
newrpm = rpm * car->_gearRatio[i+1] / car->_gearRatio[i];
for (int a = 0; a < IMax - 1; a++) {
if (rpm >= Edesc[a].rpm && rpm < Edesc[a+1].rpm) {
curTorque = (Edesc[a].tq + Edesc[a].dtq * (rpm - Edesc[a].rpm) / Edesc[a].drpm) * car->_gearRatio[i];
break;
}
}
for (int a = 0; a < IMax - 1; a++) {
if (newrpm >= Edesc[a].rpm && newrpm < Edesc[a+1].rpm) {
nextTorque = (Edesc[a].tq + Edesc[a].dtq * (newrpm - Edesc[a].rpm) / Edesc[a].drpm) * car->_gearRatio[i+1];
break;
}
}
if (nextTorque > curTorque) break;
}
#if 0
HCtx[idx]->shiftThld[i] = car->_enginerpmRedLine * car->_wheelRadius(2) * 0.85 / car->_gearRatio[i];
GfOut("Old - Gear %d: Change Up RPM %f = Speed %f\n", i-1, car->_enginerpmRedLine * 0.85 * 9.549, HCtx[idx]->shiftThld[i] * 3.6);
#else
rpm = MIN(rpm, car->_enginerpmRedLine * 0.93);
HCtx[idx]->shiftThld[i] = rpm * car->_wheelRadius(2) / car->_gearRatio[i];
GfOut("New - Gear %d: Change Up RPM %f = Speed %f\n", i-1, rpm * 9.549, HCtx[idx]->shiftThld[i] * 3.6);
#endif
}
free(Edesc);
// Center the mouse.
if (HCtx[idx]->mouseControlUsed) {
GfctrlMouseCenter();
}
// Initialize key state table
memset(keyInfo, 0, sizeof(keyInfo));
memset(lastReadKeyState, 0, sizeof(lastReadKeyState));
#ifndef WIN32
#ifdef TELEMETRY
if (s->_raceType == RM_TYPE_PRACTICE) {
RtTelemInit(-10, 10);
RtTelemNewChannel("Dist", &HCtx[idx]->distToStart, 0, 0);
RtTelemNewChannel("Ax", &car->_accel_x, 0, 0);
RtTelemNewChannel("Ay", &car->_accel_y, 0, 0);
RtTelemNewChannel("Steer", &car->ctrl->steer, 0, 0);
RtTelemNewChannel("Throttle", &car->ctrl->accelCmd, 0, 0);
RtTelemNewChannel("Brake", &car->ctrl->brakeCmd, 0, 0);
RtTelemNewChannel("Gear", &HCtx[idx]->gear, 0, 0);
RtTelemNewChannel("Speed", &car->_speed_x, 0, 0);
}
#endif
#endif
const std::string traintype = GfParmGetStr(car->_carHandle, SECT_DRIVETRAIN, PRM_TYPE, VAL_TRANS_RWD);
if (traintype == VAL_TRANS_RWD) {
HCtx[idx]->driveTrain = TRANS_RWD;
} else if (traintype == VAL_TRANS_FWD) {
HCtx[idx]->driveTrain = TRANS_FWD;
} else if (traintype == VAL_TRANS_4WD) {
HCtx[idx]->driveTrain = TRANS_4WD;
}//if traintype
// Set up the autoclutch
tControlCmd *cmd = HCtx[idx]->cmdControl;
HCtx[idx]->autoClutch = true;
HCtx[idx]->maxClutchTime = GfParmGetNum(car->_carHandle, SECT_GEARBOX, PRM_SHIFTTIME, (char*)NULL, 0.2f);
switch (car->_skillLevel) {
case ARCADE:
case SEMI_ROOKIE:
case ROOKIE: // Rookie
HCtx[idx]->maxClutchTime *= 1.4;
break;
case AMATEUR: // Amateur
HCtx[idx]->maxClutchTime *= 1.3;
break;
case SEMI_PRO: // Semi-Pro
HCtx[idx]->maxClutchTime *= 1.2;
break;
case PRO: // Pro
break;
}
// Set up glancing
car->_oldglance = 0;
car->_glance = 0;
// Set default for looking back command
car->_lookback = false;
//GfOut("SteerCmd : Left : sens=%4.1f, spSens=%4.2f, deadZ=%4.2f\n",
// cmd[CMD_LEFTSTEER].sens, cmd[CMD_LEFTSTEER].spdSens, cmd[CMD_LEFTSTEER].deadZone);
//GfOut("SteerCmd : Right: sens=%4.1f, spSens=%4.2f, deadZ=%4.2f\n",
// cmd[CMD_RIGHTSTEER].sens, cmd[CMD_RIGHTSTEER].spdSens, cmd[CMD_RIGHTSTEER].deadZone);
// Setup Keyboard map (key code => index of the associated command in keyInfo / lastReadKeyState).
for (int i = 0; i < NbCmdControl; i++)
{
if (cmd[i].type == GFCTRL_TYPE_KEYBOARD)
{
if (mapKeys.find(cmd[i].val) == mapKeys.end())
{
mapKeys[cmd[i].val] = keyIndex;
keyIndex++;
}
}//KEYBOARD
}//for i
//initialize the force feedback
forceFeedback.readConfiguration(car->_carName);
}
void HumanDriver::pause_race(int index, tCarElt* /*car*/, tSituation* /*s*/)
{
const int idx = index - 1;
//reset force feedback to zero (if set)
if(HCtx[idx]->lastForceFeedbackLevel)
{
gfctrlJoyConstantForce(HCtx[idx]->lastForceFeedbackIndex, 0, 0);
}
}
/*
* Original function: resumerace
*
* Changes from original:
*
* Simplifies conditions for clearing the key map.
*/
void HumanDriver::resume_race(int index, tCarElt* car, tSituation *s)
{
const int idx = index - 1;
tControlCmd *cmd = HCtx[idx]->cmdControl;
// re-read the controls as they may have changed
read_prefs(index);
if (resume_keybd)
{
GfOut("Clearing Keyboard map (index %d)\n", index);
keyIndex = 0;
mapKeys.clear();
resume_keybd = false;
}
// Setup Keyboard map (key code => index of the associated command
// in keyInfo / lastReadKeyState).
for (int i = 0; i < NbCmdControl; i++)
{
if (cmd[i].type == GFCTRL_TYPE_KEYBOARD)
{
if (mapKeys.find(cmd[i].val) == mapKeys.end())
{
mapKeys[cmd[i].val] = keyIndex;
keyIndex++;
}
}//KEYBOARD
}//for i
//restore force feedback effect to the wheel (if was set)
if(HCtx[idx]->lastForceFeedbackLevel)
{
if(cmd[CMD_LEFTSTEER].type != GFCTRL_TYPE_KEYBOARD && cmd[CMD_LEFTSTEER].type != GFCTRL_TYPE_MOUSE_AXIS)
{
HCtx[idx]->lastForceFeedbackIndex = int((cmd[CMD_LEFTSTEER].val) / GFCTRL_JOY_NUMBER);
gfctrlJoyConstantForce(
HCtx[idx]->lastForceFeedbackIndex,
HCtx[idx]->lastForceFeedbackLevel,
HCtx[idx]->lastForceFeedbackDir );
}
else
{
HCtx[idx]->lastForceFeedbackLevel = 0; // forget force feedback level
}
}
}
void HumanDriver::end_race(int index, tCarElt* /*car*/, tSituation* /*s*/)
{
const int idx = index - 1;
//reset force feedback to zero (if set)
if(HCtx[idx]->lastForceFeedbackLevel)
{
gfctrlJoyConstantForce(HCtx[idx]->lastForceFeedbackIndex, 0, 0);
HCtx[idx]->lastForceFeedbackLevel = 0; // forget force feedback level
}
}
/*
* Changes from original: none
*/
static int lookUpKeyMap(int key)
{
const tKeyMap::const_iterator p = mapKeys.find(key);
if (p != mapKeys.end())
return p->second;
return -1;
}
/*
* Changes from original: none
*/
static void updateKeys(void)
{
int i;
int nKeyInd;
int idx;
tControlCmd *cmd;
for (idx = 0; idx < (int)HCtx.size(); idx++)
{
if (HCtx[idx])
{
cmd = HCtx[idx]->cmdControl;
for (i = 0; i < NbCmdControl; i++)
{
if (cmd[i].type == GFCTRL_TYPE_KEYBOARD)
{
nKeyInd = lookUpKeyMap(cmd[i].val);
if (lastReadKeyState[nKeyInd] == GFUI_KEY_DOWN)
{
if (keyInfo[nKeyInd].state == GFUI_KEY_UP)
{
keyInfo[nKeyInd].edgeDn = 1;
}
else
{
keyInfo[nKeyInd].edgeDn = 0;
}
}
else
{
if (keyInfo[nKeyInd].state == GFUI_KEY_DOWN)
{
keyInfo[nKeyInd].edgeUp = 1;
}
else
{
keyInfo[nKeyInd].edgeUp = 0;
}
}
keyInfo[nKeyInd].state = lastReadKeyState[nKeyInd];
}
}
}
}
}//updateKeys
/*
* Changes from original: none
*/
static int onKeyAction(int key, int modifier, int state)
{
// Update key state only if the key is assigned to a player command.
const int nKeyInd = lookUpKeyMap(key);
if (nKeyInd >= 0)
lastReadKeyState[lookUpKeyMap(key)] = state;
return 0;
}//onKeyAction
/*
* Changes from original: none
*/
static void common_drive(const int index, tCarElt* car, tSituation *s)
{
tdble ax0;
tdble brake;
tdble clutch;
tdble throttle;
tdble leftSteer;
tdble rightSteer;
tdble newGlance;;
#if (BINCTRL_STEERING == JEPZ || BINCTRL_STEERING == JPM)
tdble sensFrac, speedFrac;
#endif
int scrw, scrh, dummy;
const int idx = index - 1;
tControlCmd *cmd = HCtx[idx]->cmdControl;
if (init_keybd && !GfuiScreenIsActive(0))
{
GfuiKeyEventRegisterCurrent(onKeyAction);
init_keybd = false;
}
if (init_mouse && !GfuiScreenIsActive(0) && HCtx[idx]->mouseControlUsed)
{
GfuiMouseShow();
GfctrlMouseCenter();
GfctrlMouseInitCenter();
init_mouse = false;
}
HCtx[idx]->distToStart = RtGetDistFromStart(car);
HCtx[idx]->gear = (tdble)car->_gear; /* telemetry */
GfScrGetSize(&scrw, &scrh, &dummy, &dummy);
int oldgear = car->_gearCmd;
memset(&(car->ctrl), 0, sizeof(tCarCtrl));
car->_gearCmd = oldgear;
car->_lightCmd = HCtx[idx]->lightCmd;
if (car->_laps != HCtx[idx]->lastPitStopLap)
{
car->_raceCmd = RM_CMD_PIT_ASKED;
}
// Update the controls at most once per "robots time slice" (RCM_MAX_DT_ROBOTS s)
// (i.e. keyboard/joystick/mouse values read for all players simultaneously).
if (lastKeyUpdate != s->currentTime && index == ControlsUpdaterIndex)
{
updateKeys();
if (joyPresent)
{
GfctrlJoyGetCurrentStates(joyInfo);
}
GfctrlMouseGetCurrentState(mouseInfo);
lastKeyUpdate = s->currentTime;
}
// Perform mapping of axis->buttons functions
for (dummy=0; dummy <= CMD_END_OF_LIST; dummy++)
{
if (cmd[dummy].type == GFCTRL_TYPE_JOY_ATOB)
{
if (joyInfo->ax[cmd[dummy].val] >= cmd[dummy].min && joyInfo->ax[cmd[dummy].val] <= cmd[dummy].max)
{
// Abuse deadZone to store state; 1=just pressed, 2=held
if (cmd[dummy].deadZone < 2)
cmd[dummy].deadZone++;
}
else
cmd[dummy].deadZone = 0;
}
}
// Allow Grid Gearbox to freely change as race starts
if (s->currentTime <= 0 && HCtx[idx]->transmission == GEAR_MODE_GRID)
{
/* default to neutral gear */
preGear = 0;
for (int i = CMD_GEAR_R; i <= CMD_GEAR_6; i++)
{
if ((cmd[i].type == GFCTRL_TYPE_JOY_BUT && joyInfo->levelup[cmd[i].val])
|| (cmd[i].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->button[cmd[i].val])
#if 0 //SDW fixme
|| (cmd[i].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[i].val)].state)
#endif
)
{
preGear = i - CMD_GEAR_N;
}
}
GfOut("Gridbox Initial Gear %d\n", preGear);
}
if ((cmd[CMD_ABS].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[CMD_ABS].val])
|| (cmd[CMD_ABS].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgeup[cmd[CMD_ABS].val])
|| (cmd[CMD_ABS].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_ABS].val)].edgeUp)
|| (cmd[CMD_ABS].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_ABS].deadZone == 1))
{
HCtx[idx]->paramAbs = !HCtx[idx]->paramAbs;
snprintf(sstring, sizeof(sstring), "%s/%s/%d", HM_SECT_PREF, HM_LIST_DRV, index);
GfParmSetStr(PrefHdle, sstring, HM_ATT_ABS, Yn[!HCtx[idx]->paramAbs].c_str());
GfParmWriteFile(NULL, PrefHdle, "Human");
}
if ((cmd[CMD_ASR].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[CMD_ASR].val])
|| (cmd[CMD_ASR].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgeup[cmd[CMD_ASR].val])
|| (cmd[CMD_ASR].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_ASR].val)].edgeUp)
|| (cmd[CMD_ASR].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_ASR].deadZone == 1))
{
HCtx[idx]->paramAsr = !HCtx[idx]->paramAsr;
snprintf(sstring, sizeof(sstring), "%s/%s/%d", HM_SECT_PREF, HM_LIST_DRV, index);
GfParmSetStr(PrefHdle, sstring, HM_ATT_ASR, Yn[!HCtx[idx]->paramAsr].c_str());
GfParmWriteFile(NULL, PrefHdle, "Human");
}
sprintf(car->_msgCmd[0], "%s %s", (HCtx[idx]->paramAbs ? "ABS" : ""), (HCtx[idx]->paramAsr ? "TCS" : ""));
memcpy(car->_msgColorCmd, color, sizeof(car->_msgColorCmd));
if ((cmd[CMD_SPDLIM].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[CMD_SPDLIM].val])
|| (cmd[CMD_SPDLIM].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgeup[cmd[CMD_SPDLIM].val])
|| (cmd[CMD_SPDLIM].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_SPDLIM].val)].edgeUp)
|| (cmd[CMD_SPDLIM].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_SPDLIM].deadZone == 1))
{
speedLimiter = !speedLimiter;
}
sprintf(car->_msgCmd[1], "Speed Limiter %s", (speedLimiter ? "On" : "Off"));
if ((cmd[CMD_LIGHT1].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[CMD_LIGHT1].val])
|| (cmd[CMD_LIGHT1].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgeup[cmd[CMD_LIGHT1].val])
|| (cmd[CMD_LIGHT1].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_LIGHT1].val)].edgeUp)
|| (cmd[CMD_LIGHT1].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_LIGHT1].deadZone == 1))
{
if (HCtx[idx]->lightCmd & RM_LIGHT_HEAD1) {
HCtx[idx]->lightCmd &= ~(RM_LIGHT_HEAD1 | RM_LIGHT_HEAD2);
} else {
HCtx[idx]->lightCmd |= RM_LIGHT_HEAD1 | RM_LIGHT_HEAD2;
}
}
switch (cmd[CMD_LEFTSTEER].type)
{
case GFCTRL_TYPE_JOY_AXIS:
ax0 = joyInfo->ax[cmd[CMD_LEFTSTEER].val];
// limit and normalise
if (ax0 > cmd[CMD_LEFTSTEER].max)
{
ax0 = cmd[CMD_LEFTSTEER].max;
}
else if (ax0 < cmd[CMD_LEFTSTEER].min)
{
ax0 = cmd[CMD_LEFTSTEER].min;
}
ax0 = (ax0 - cmd[CMD_LEFTSTEER].min) / (cmd[CMD_LEFTSTEER].max - cmd[CMD_LEFTSTEER].min);
// pow used to indicate the polarity of 'more turn'
if (cmd[CMD_LEFTSTEER].pow > 0)
ax0 = ax0 - cmd[CMD_LEFTSTEER].deadZone;
else
ax0 = 1 - ax0 - cmd[CMD_LEFTSTEER].deadZone;
if (ax0 < 0) ax0 = 0;
if (1 - cmd[CMD_LEFTSTEER].deadZone != 0)
ax0 = ax0 / (1 - cmd[CMD_LEFTSTEER].deadZone);
else
ax0 = 0;
leftSteer = fabs(cmd[CMD_LEFTSTEER].pow) * pow(ax0, 1.0f / cmd[CMD_LEFTSTEER].sens) / (1.0 + cmd[CMD_LEFTSTEER].spdSens * car->_speed_xy / 100.0);
break;
case GFCTRL_TYPE_MOUSE_AXIS:
ax0 = mouseInfo->ax[cmd[CMD_LEFTSTEER].val] - cmd[CMD_LEFTSTEER].deadZone;
if (ax0 > cmd[CMD_LEFTSTEER].max)
{
ax0 = cmd[CMD_LEFTSTEER].max;
}
else if (ax0 < cmd[CMD_LEFTSTEER].min)
{
ax0 = cmd[CMD_LEFTSTEER].min;
}
ax0 = ax0 * cmd[CMD_LEFTSTEER].pow;
leftSteer = pow(fabs(ax0), 1.0f / cmd[CMD_LEFTSTEER].sens) / (1.0f + cmd[CMD_LEFTSTEER].spdSens * car->_speed_xy / 1000.0);
break;
case GFCTRL_TYPE_KEYBOARD:
case GFCTRL_TYPE_JOY_BUT:
case GFCTRL_TYPE_MOUSE_BUT:
if (cmd[CMD_LEFTSTEER].type == GFCTRL_TYPE_KEYBOARD)
{
ax0 = keyInfo[lookUpKeyMap(cmd[CMD_LEFTSTEER].val)].state;
}
else if (cmd[CMD_LEFTSTEER].type == GFCTRL_TYPE_MOUSE_BUT)
{
ax0 = mouseInfo->button[cmd[CMD_LEFTSTEER].val];
}
else
{
ax0 = joyInfo->levelup[cmd[CMD_LEFTSTEER].val];
}
#if (BINCTRL_STEERING == JEPZ)
if (ax0 == 0) {
leftSteer = HCtx[idx]->prevLeftSteer - s->deltaTime * 5.0;
} else {
ax0 = 2 * ax0 - 1;
sensFrac = 10.0 * cmd[CMD_LEFTSTEER].sens;
speedFrac = fabs(car->_speed_x * cmd[CMD_LEFTSTEER].spdSens);
if (speedFrac < 1.0) speedFrac = 1.0;
leftSteer = HCtx[idx]->prevLeftSteer + s->deltaTime * ax0 * sensFrac / speedFrac;
}
if (leftSteer > 1.0) leftSteer = 1.0;
if (leftSteer < 0.0) leftSteer = 0.0;
#elif (BINCTRL_STEERING == JPM)
// ax should be 0 or 1 here (to be checked) => -1 (zero steer) or +1 (full steer left).
ax0 = 2 * ax0 - 1;
sensFrac = 1.0 + (3.5 - 1.5 * ax0) * cmd[CMD_LEFTSTEER].sens;
speedFrac = 1.0 + car->_speed_x * car->_speed_x * cmd[CMD_LEFTSTEER].spdSens / 300.0;
leftSteer = HCtx[idx]->prevLeftSteer + s->deltaTime * ax0 * sensFrac / speedFrac;
//GfOut("Left : ax=%4.1f, ws=%4.2f, ss=%4.2f, prev=%5.2f, new=%5.2f (spd=%6.2f)\n",
// ax0, sensFrac, speedFrac, HCtx[idx]->prevLeftSteer, leftSteer, car->_speed_x);
if (leftSteer > 1.0)
leftSteer = 1.0;
else if (leftSteer < 0.0)
leftSteer = 0.0;
#else
if (ax0 == 0) {
leftSteer = 0;
} else {
ax0 = 2 * ax0 - 1;
leftSteer = HCtx[idx]->prevLeftSteer + ax0 * s->deltaTime / cmd[CMD_LEFTSTEER].sens / (1.0 + cmd[CMD_LEFTSTEER].spdSens * car->_speed_x / 1000.0);
if (leftSteer > 1.0) leftSteer = 1.0;
if (leftSteer < 0.0) leftSteer = 0.0;
}
#endif
HCtx[idx]->prevLeftSteer = leftSteer;
break;
default:
leftSteer = 0;
break;
}
switch (cmd[CMD_RIGHTSTEER].type)
{
case GFCTRL_TYPE_JOY_AXIS:
ax0 = joyInfo->ax[cmd[CMD_RIGHTSTEER].val];
// limit and normalise
if (ax0 > cmd[CMD_RIGHTSTEER].max)
{
ax0 = cmd[CMD_RIGHTSTEER].max;
}
else if (ax0 < cmd[CMD_RIGHTSTEER].min)
{
ax0 = cmd[CMD_RIGHTSTEER].min;
}
ax0 = (ax0 - cmd[CMD_RIGHTSTEER].min) / (cmd[CMD_RIGHTSTEER].max - cmd[CMD_RIGHTSTEER].min);
// pow used to indicate the polarity of 'more turn'
if (cmd[CMD_RIGHTSTEER].pow > 0)
ax0 = ax0 - cmd[CMD_RIGHTSTEER].deadZone;
else
ax0 = 1 - ax0 - cmd[CMD_RIGHTSTEER].deadZone;
if (ax0 < 0)
ax0 = 0;
if (1 - cmd[CMD_RIGHTSTEER].deadZone != 0)
ax0 = ax0 / (1 - cmd[CMD_RIGHTSTEER].deadZone);
else
ax0 = 0;
rightSteer = -1 * fabs(cmd[CMD_RIGHTSTEER].pow) * pow(ax0, 1.0f / cmd[CMD_RIGHTSTEER].sens) / (1.0 + cmd[CMD_RIGHTSTEER].spdSens * car->_speed_xy / 100.0);
break;
case GFCTRL_TYPE_MOUSE_AXIS:
ax0 = mouseInfo->ax[cmd[CMD_RIGHTSTEER].val] - cmd[CMD_RIGHTSTEER].deadZone;
if (ax0 > cmd[CMD_RIGHTSTEER].max)
{
ax0 = cmd[CMD_RIGHTSTEER].max;
}
else if (ax0 < cmd[CMD_RIGHTSTEER].min)
{
ax0 = cmd[CMD_RIGHTSTEER].min;
}
ax0 = ax0 * cmd[CMD_RIGHTSTEER].pow;
rightSteer = - pow(fabs(ax0), 1.0f / cmd[CMD_RIGHTSTEER].sens) / (1.0f + cmd[CMD_RIGHTSTEER].spdSens * car->_speed_xy / 1000.0);
break;
case GFCTRL_TYPE_KEYBOARD:
case GFCTRL_TYPE_JOY_BUT:
case GFCTRL_TYPE_MOUSE_BUT:
if (cmd[CMD_RIGHTSTEER].type == GFCTRL_TYPE_KEYBOARD)
{
ax0 = keyInfo[lookUpKeyMap(cmd[CMD_RIGHTSTEER].val)].state;
}
else if (cmd[CMD_RIGHTSTEER].type == GFCTRL_TYPE_MOUSE_BUT)
{
ax0 = mouseInfo->button[cmd[CMD_RIGHTSTEER].val];
}
else
{
ax0 = joyInfo->levelup[cmd[CMD_RIGHTSTEER].val];
}
#if (BINCTRL_STEERING == JEPZ)
if (ax0 == 0) {
rightSteer = HCtx[idx]->prevRightSteer + s->deltaTime * 5.0;
} else {
ax0 = 2 * ax0 - 1;
sensFrac = 10.0 * cmd[CMD_RIGHTSTEER].sens;
speedFrac = fabs(car->_speed_x * cmd[CMD_RIGHTSTEER].spdSens);
if (speedFrac < 1.0) speedFrac = 1.0;
rightSteer = HCtx[idx]->prevRightSteer - s->deltaTime * ax0 * sensFrac / speedFrac;
}
if (rightSteer > 0.0) rightSteer = 0.0;
if (rightSteer < -1.0) rightSteer = -1.0;
#elif (BINCTRL_STEERING == JPM)
// ax should be 0 or 1 here (to be checked) => -1 (zero steer) or +1 (full steer left).
ax0 = 2 * ax0 - 1;
sensFrac = 1.0 + (3.5 - 1.5 * ax0) * cmd[CMD_RIGHTSTEER].sens;
speedFrac = 1.0 + car->_speed_x * car->_speed_x * cmd[CMD_RIGHTSTEER].spdSens / 300.0;
rightSteer = HCtx[idx]->prevRightSteer - s->deltaTime * ax0 * sensFrac / speedFrac;
//GfOut("Right: ax=%4.1f, ws=%4.2f, ss=%4.2f, prev=%5.2f, new=%5.2f (spd=%6.2f)\n",
// ax0, sensFrac, speedFrac, HCtx[idx]->prevRightSteer, rightSteer, car->_speed_x);
if (rightSteer < -1.0)
rightSteer = -1.0;
else if (rightSteer > 0.0)
rightSteer = 0.0;
#else
if (ax0 == 0) {
rightSteer = 0;
} else {
ax0 = 2 * ax0 - 1;
rightSteer = HCtx[idx]->prevRightSteer - ax0 * s->deltaTime / cmd[CMD_RIGHTSTEER].sens / (1.0 + cmd[CMD_RIGHTSTEER].spdSens * car->_speed_x / 1000.0);
if (rightSteer > 0.0) rightSteer = 0.0;
if (rightSteer < -1.0) rightSteer = -1.0;
}
#endif
HCtx[idx]->prevRightSteer = rightSteer;
break;
default:
rightSteer = 0;
break;
}
car->_steerCmd = leftSteer + rightSteer;
//send force feedback effect to the wheel
//dont' even try to do it if steer command is on a keyboard because it somehow manage to crash (unable to identify the joystic to send FF to?)
if(cmd[CMD_LEFTSTEER].type != GFCTRL_TYPE_KEYBOARD && cmd[CMD_LEFTSTEER].type != GFCTRL_TYPE_MOUSE_AXIS){
// v<- this controller detenction does not make ->v
// v<- sense to me ->v
HCtx[idx]->lastForceFeedbackIndex = int((cmd[CMD_LEFTSTEER].val) / GFCTRL_JOY_NUMBER);
HCtx[idx]->lastForceFeedbackLevel = forceFeedback.updateForce(car, s);
HCtx[idx]->lastForceFeedbackDir = 0;
gfctrlJoyConstantForce(
HCtx[idx]->lastForceFeedbackIndex,
HCtx[idx]->lastForceFeedbackLevel,
HCtx[idx]->lastForceFeedbackDir );
}
#define GLANCERATE 3 // speed at which the driver turns his head, ~1/3s to full glance
newGlance = car->_glance;
if ((cmd[CMD_LEFTGLANCE].type == GFCTRL_TYPE_JOY_BUT && joyInfo->levelup[cmd[CMD_LEFTGLANCE].val])
|| (cmd[CMD_LEFTGLANCE].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->button[cmd[CMD_LEFTGLANCE].val])
|| (cmd[CMD_LEFTGLANCE].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_LEFTGLANCE].val)].state)
|| (cmd[CMD_LEFTGLANCE].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_LEFTGLANCE].deadZone != 0))
{
newGlance = newGlance - GLANCERATE * s->deltaTime;
if (newGlance < -0.5) newGlance=-0.5;
} else if ((cmd[CMD_RIGHTGLANCE].type == GFCTRL_TYPE_JOY_BUT && joyInfo->levelup[cmd[CMD_RIGHTGLANCE].val])
|| (cmd[CMD_RIGHTGLANCE].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->button[cmd[CMD_RIGHTGLANCE].val])
|| (cmd[CMD_RIGHTGLANCE].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_RIGHTGLANCE].val)].state)
|| (cmd[CMD_RIGHTGLANCE].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_RIGHTGLANCE].deadZone != 0))
{
newGlance = newGlance + GLANCERATE * s->deltaTime;
if (newGlance > 0.5) newGlance=0.5;
} else if (cmd[CMD_RIGHTGLANCE].type == GFCTRL_TYPE_JOY_AXIS && joyInfo->ax[cmd[CMD_RIGHTGLANCE].val] > cmd[CMD_RIGHTGLANCE].min)
{
newGlance = joyInfo->ax[cmd[CMD_RIGHTGLANCE].val];
} else if (cmd[CMD_LEFTGLANCE].type == GFCTRL_TYPE_JOY_AXIS && joyInfo->ax[cmd[CMD_LEFTGLANCE].val] < cmd[CMD_LEFTGLANCE].max)
{
newGlance = joyInfo->ax[cmd[CMD_LEFTGLANCE].val];
} else {
// return view to center
car->_oldglance = 0;
if (newGlance > 0) {
newGlance = newGlance - GLANCERATE * s->deltaTime;
if (newGlance < 0) newGlance = 0;
}
if (newGlance < 0) {
newGlance = newGlance + GLANCERATE * s->deltaTime;
if (newGlance > 0) newGlance = 0;
}
}
// limit glance
if (newGlance > 1) newGlance=1;
if (newGlance < -1) newGlance=-1;
// Limit twitching between values
if (newGlance != 0) {
if (newGlance != car->_oldglance) {
car->_oldglance = car->_glance;
car->_glance = newGlance;
}
} else {
car->_oldglance = 0;
car->_glance = 0;
}
//look back camera command
if (
(//if the look back camera button is pressed
(cmd[CMD_LOOKBACK].type == GFCTRL_TYPE_JOY_BUT && joyInfo->levelup[cmd[CMD_LOOKBACK].val]) //if is a joypad button check if the button is pressed
|| (cmd[CMD_LOOKBACK].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->button[cmd[CMD_LOOKBACK].val]) //if is a mouse button check if the button is pressed
|| (cmd[CMD_LOOKBACK].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_LOOKBACK].val)].state)//if is a keyboard key check if the key is pressed
|| (cmd[CMD_LOOKBACK].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_LOOKBACK].deadZone != 0)//if it's a joypad axis check if the axis is not at rest position (0)
)
||
(//or if both glance left and glance right buttons are pressed simultaneously
((cmd[CMD_RIGHTGLANCE].type == GFCTRL_TYPE_JOY_BUT && joyInfo->levelup[cmd[CMD_RIGHTGLANCE].val])
|| (cmd[CMD_RIGHTGLANCE].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->button[cmd[CMD_RIGHTGLANCE].val])
|| (cmd[CMD_RIGHTGLANCE].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_RIGHTGLANCE].val)].state)
|| (cmd[CMD_RIGHTGLANCE].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_RIGHTGLANCE].deadZone != 0))
&&
((cmd[CMD_LEFTGLANCE].type == GFCTRL_TYPE_JOY_BUT && joyInfo->levelup[cmd[CMD_LEFTGLANCE].val])
|| (cmd[CMD_LEFTGLANCE].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->button[cmd[CMD_LEFTGLANCE].val])
|| (cmd[CMD_LEFTGLANCE].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_LEFTGLANCE].val)].state)
|| (cmd[CMD_LEFTGLANCE].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_LEFTGLANCE].deadZone != 0))
)
)
{
car->_lookback=true;
}else{
car->_lookback=false;
}
// dashboard handling
if ((cmd[CMD_DASHB_NEXT].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[CMD_DASHB_NEXT].val])
|| (cmd[CMD_DASHB_NEXT].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgedn[cmd[CMD_DASHB_NEXT].val])
|| (cmd[CMD_DASHB_NEXT].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_DASHB_NEXT].val)].edgeDn)
|| (cmd[CMD_DASHB_NEXT].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_DASHB_NEXT].deadZone != 0))
{
car->_dashboardActiveItem++;
if (car->_dashboardActiveItem >= car->_dashboardInstantNb + car->_dashboardRequestNb)
{car->_dashboardActiveItem = 0;}
}
if ((cmd[CMD_DASHB_PREV].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[CMD_DASHB_PREV].val])
|| (cmd[CMD_DASHB_PREV].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgedn[cmd[CMD_DASHB_PREV].val])
|| (cmd[CMD_DASHB_PREV].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_DASHB_PREV].val)].edgeDn)
|| (cmd[CMD_DASHB_PREV].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_DASHB_PREV].deadZone != 0))
{
car->_dashboardActiveItem--;
if (car->_dashboardActiveItem < 0)
{car->_dashboardActiveItem = car->_dashboardInstantNb + car->_dashboardRequestNb - 1;}
}
tDashboardItem *item;
if (car->_dashboardActiveItem < car->_dashboardInstantNb) {
item = &(car->_dashboardInstant[car->_dashboardActiveItem]);
} else {
item = &(car->_dashboardRequest[car->_dashboardActiveItem - car->_dashboardInstantNb]);
}
if ((cmd[CMD_DASHB_INC].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[CMD_DASHB_INC].val])
|| (cmd[CMD_DASHB_INC].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgedn[cmd[CMD_DASHB_INC].val])
|| (cmd[CMD_DASHB_INC].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_DASHB_INC].val)].edgeDn)
|| (cmd[CMD_DASHB_INC].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_DASHB_INC].deadZone != 0))
{
HCtx[idx]->dashboardCounter = (int)(1.0/RCM_MAX_DT_ROBOTS);
item->setup->desired_value += item->setup->stepsize;
if (item->setup->desired_value > item->setup->max) {item->setup->desired_value = item->setup->max;}
item->setup->changed = TRUE;
car->ctrl.setupChangeCmd = item;
}
if ((cmd[CMD_DASHB_INC].type == GFCTRL_TYPE_JOY_BUT && joyInfo->levelup[cmd[CMD_DASHB_INC].val])
|| (cmd[CMD_DASHB_INC].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->button[cmd[CMD_DASHB_INC].val])
|| (cmd[CMD_DASHB_INC].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_DASHB_INC].val)].state)
|| (cmd[CMD_DASHB_INC].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_DASHB_INC].deadZone != 0))
{
if (HCtx[idx]->dashboardCounter > 0)
{
HCtx[idx]->dashboardCounter--;
}
else
{
item->setup->desired_value += item->setup->stepsize;
if (item->setup->desired_value > item->setup->max) {item->setup->desired_value = item->setup->max;}
item->setup->changed = TRUE;
car->ctrl.setupChangeCmd = item;
}
}
if ((cmd[CMD_DASHB_DEC].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[CMD_DASHB_DEC].val])
|| (cmd[CMD_DASHB_DEC].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgedn[cmd[CMD_DASHB_DEC].val])
|| (cmd[CMD_DASHB_DEC].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_DASHB_DEC].val)].edgeDn)
|| (cmd[CMD_DASHB_DEC].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_DASHB_DEC].deadZone != 0))
{
HCtx[idx]->dashboardCounter = (int)(1.0/RCM_MAX_DT_ROBOTS);
item->setup->desired_value -= item->setup->stepsize;
if (item->setup->desired_value < item->setup->min) {item->setup->desired_value = item->setup->min;}
item->setup->changed = TRUE;
car->ctrl.setupChangeCmd = item;
}
if ((cmd[CMD_DASHB_DEC].type == GFCTRL_TYPE_JOY_BUT && joyInfo->levelup[cmd[CMD_DASHB_DEC].val])
|| (cmd[CMD_DASHB_DEC].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->button[cmd[CMD_DASHB_DEC].val])
|| (cmd[CMD_DASHB_DEC].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_DASHB_DEC].val)].state)
|| (cmd[CMD_DASHB_DEC].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_DASHB_DEC].deadZone != 0))
{
if (HCtx[idx]->dashboardCounter > 0)
{
HCtx[idx]->dashboardCounter--;
}
else
{
item->setup->desired_value -= item->setup->stepsize;
if (item->setup->desired_value < item->setup->min) {item->setup->desired_value = item->setup->min;}
item->setup->changed = TRUE;
car->ctrl.setupChangeCmd = item;
}
}
switch (cmd[CMD_BRAKE].type)
{
case GFCTRL_TYPE_JOY_AXIS:
brake = joyInfo->ax[cmd[CMD_BRAKE].val];
if (brake > cmd[CMD_BRAKE].max)
{
brake = cmd[CMD_BRAKE].max;
}
else if (brake < cmd[CMD_BRAKE].min)
{
brake = cmd[CMD_BRAKE].min;
}
car->_brakeCmd = fabs(cmd[CMD_BRAKE].pow *
pow(fabs((brake - cmd[CMD_BRAKE].minVal) /
(cmd[CMD_BRAKE].max - cmd[CMD_BRAKE].min)),
1.0f / cmd[CMD_BRAKE].sens));
break;
case GFCTRL_TYPE_MOUSE_AXIS:
ax0 = mouseInfo->ax[cmd[CMD_BRAKE].val] - cmd[CMD_BRAKE].deadZone;
if (ax0 > cmd[CMD_BRAKE].max)
{
ax0 = cmd[CMD_BRAKE].max;
}
else if (ax0 < cmd[CMD_BRAKE].min)
{
ax0 = cmd[CMD_BRAKE].min;
}
ax0 = ax0 * cmd[CMD_BRAKE].pow;
car->_brakeCmd = pow(fabs(ax0), 1.0f / cmd[CMD_BRAKE].sens) / (1.0 + cmd[CMD_BRAKE].spdSens * car->_speed_x / 1000.0);
break;
case GFCTRL_TYPE_JOY_BUT:
car->_brakeCmd = joyInfo->levelup[cmd[CMD_BRAKE].val];
break;
case GFCTRL_TYPE_MOUSE_BUT:
car->_brakeCmd = mouseInfo->button[cmd[CMD_BRAKE].val];
break;
case GFCTRL_TYPE_KEYBOARD:
car->_brakeCmd = keyInfo[lookUpKeyMap(cmd[CMD_BRAKE].val)].state;
break;
default:
car->_brakeCmd = 0;
break;
}
switch (cmd[CMD_CLUTCH].type)
{
case GFCTRL_TYPE_JOY_AXIS:
clutch = joyInfo->ax[cmd[CMD_CLUTCH].val];
if (clutch > cmd[CMD_CLUTCH].max)
{
clutch = cmd[CMD_CLUTCH].max;
}
else if (clutch < cmd[CMD_CLUTCH].min)
{
clutch = cmd[CMD_CLUTCH].min;
}
car->_clutchCmd = fabs(cmd[CMD_CLUTCH].pow *
pow(fabs((clutch - cmd[CMD_CLUTCH].minVal) /
(cmd[CMD_CLUTCH].max - cmd[CMD_CLUTCH].min)),
1.0f / cmd[CMD_CLUTCH].sens));
break;
case GFCTRL_TYPE_MOUSE_AXIS:
ax0 = mouseInfo->ax[cmd[CMD_CLUTCH].val] - cmd[CMD_CLUTCH].deadZone;
if (ax0 > cmd[CMD_CLUTCH].max)
{
ax0 = cmd[CMD_CLUTCH].max;
}
else if (ax0 < cmd[CMD_CLUTCH].min)
{
ax0 = cmd[CMD_CLUTCH].min;
}
ax0 = ax0 * cmd[CMD_CLUTCH].pow;
car->_clutchCmd = pow(fabs(ax0), 1.0f / cmd[CMD_CLUTCH].sens) / (1.0 + cmd[CMD_CLUTCH].spdSens * car->_speed_x / 1000.0);
break;
case GFCTRL_TYPE_JOY_BUT:
car->_clutchCmd = joyInfo->levelup[cmd[CMD_CLUTCH].val];
break;
case GFCTRL_TYPE_MOUSE_BUT:
car->_clutchCmd = mouseInfo->button[cmd[CMD_CLUTCH].val];
break;
case GFCTRL_TYPE_KEYBOARD:
car->_clutchCmd = keyInfo[lookUpKeyMap(cmd[CMD_CLUTCH].val)].state;
break;
default:
car->_clutchCmd = 0;
break;
}
// if player's used the clutch manually then we dispense with autoClutch
if (car->_clutchCmd != 0.0f)
HCtx[idx]->autoClutch = false;
// Ebrake here so that it can override the clutch control
if ((cmd[CMD_EBRAKE].type == GFCTRL_TYPE_JOY_BUT && joyInfo->levelup[cmd[CMD_EBRAKE].val])
|| (cmd[CMD_EBRAKE].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->button[cmd[CMD_EBRAKE].val])
|| (cmd[CMD_EBRAKE].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_EBRAKE].val)].state == GFUI_KEY_DOWN)
|| (cmd[CMD_EBRAKE].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_EBRAKE].deadZone != 0))
{
car->_ebrakeCmd = 1;
if (HCtx[idx]->autoClutch)
car->_clutchCmd = 1;
}
else
{
car->_ebrakeCmd = 0;
}
switch (cmd[CMD_THROTTLE].type)
{
case GFCTRL_TYPE_JOY_AXIS:
throttle = joyInfo->ax[cmd[CMD_THROTTLE].val];
if (throttle > cmd[CMD_THROTTLE].max)
{
throttle = cmd[CMD_THROTTLE].max;
}
else if (throttle < cmd[CMD_THROTTLE].min)
{
throttle = cmd[CMD_THROTTLE].min;
}
car->_accelCmd = fabs(cmd[CMD_THROTTLE].pow *
pow(fabs((throttle - cmd[CMD_THROTTLE].minVal) /
(cmd[CMD_THROTTLE].max - cmd[CMD_THROTTLE].min)),
1.0f / cmd[CMD_THROTTLE].sens));
break;
case GFCTRL_TYPE_MOUSE_AXIS:
ax0 = mouseInfo->ax[cmd[CMD_THROTTLE].val] - cmd[CMD_THROTTLE].deadZone;
if (ax0 > cmd[CMD_THROTTLE].max)
{
ax0 = cmd[CMD_THROTTLE].max;
}
else if (ax0 < cmd[CMD_THROTTLE].min)
{
ax0 = cmd[CMD_THROTTLE].min;
}
ax0 = ax0 * cmd[CMD_THROTTLE].pow;
car->_accelCmd = pow(fabs(ax0), 1.0f / cmd[CMD_THROTTLE].sens) / (1.0 + cmd[CMD_THROTTLE].spdSens * car->_speed_x / 1000.0);
if (isnan (car->_accelCmd))
{
car->_accelCmd = 0;
}
/* printf(" axO:%f accelCmd:%f\n", ax0, car->_accelCmd); */
break;
case GFCTRL_TYPE_JOY_BUT:
car->_accelCmd = joyInfo->levelup[cmd[CMD_THROTTLE].val];
break;
case GFCTRL_TYPE_MOUSE_BUT:
car->_accelCmd = mouseInfo->button[cmd[CMD_THROTTLE].val];
break;
case GFCTRL_TYPE_KEYBOARD:
car->_accelCmd = keyInfo[lookUpKeyMap(cmd[CMD_THROTTLE].val)].state;
break;
default:
car->_accelCmd = 0;
break;
}
// thanks Christos for the following: gradual accel/brake changes for on/off controls.
if (cmd[CMD_BRAKE].type == GFCTRL_TYPE_JOY_BUT
|| cmd[CMD_BRAKE].type == GFCTRL_TYPE_MOUSE_BUT
|| cmd[CMD_BRAKE].type == GFCTRL_TYPE_KEYBOARD)
{
if (s->currentTime > 1.0)
{
static const tdble inc_rate = 0.2f;
tdble d_brake = car->_brakeCmd - HCtx[idx]->pbrake;
if (fabs(d_brake) > inc_rate && car->_brakeCmd > HCtx[idx]->pbrake)
car->_brakeCmd =
MIN(car->_brakeCmd, HCtx[idx]->pbrake + inc_rate*d_brake/fabs(d_brake));
}
HCtx[idx]->pbrake = car->_brakeCmd;
}
if (cmd[CMD_THROTTLE].type == GFCTRL_TYPE_JOY_BUT
|| cmd[CMD_THROTTLE].type == GFCTRL_TYPE_MOUSE_BUT
|| cmd[CMD_THROTTLE].type == GFCTRL_TYPE_KEYBOARD)
{
if (s->currentTime > 1.0)
{
static const tdble inc_rate = 0.2f;
tdble d_accel = car->_accelCmd - HCtx[idx]->paccel;
if (fabs(d_accel) > inc_rate && car->_accelCmd > HCtx[idx]->paccel)
car->_accelCmd =
MIN(car->_accelCmd, HCtx[idx]->paccel + inc_rate*d_accel/fabs(d_accel));
}
}
// Linear delay of autoclutch
if (HCtx[idx]->clutchTime > 0.0f)
HCtx[idx]->clutchTime -= s->deltaTime;
// automatically adjust throttle when auto-shifting
if (HCtx[idx]->clutchTime > 0.0f && HCtx[idx]->autoClutch && car->_gear > 1 && car->_speed_xy > 10) {
// Target RPMs slightly above ideal match
double rpm = 1.1 * car->_speed_xy * car->_gearRatio[car->_gear + car->_gearOffset] / car->_wheelRadius(2);
car->_accelCmd += (rpm - car->_enginerpm) * 4 / car->_enginerpmRedLine;
//GfOut("Desired rpms for gear %d = %f\n", car->_gear, rpm * 9.54);
car->_accelCmd = MIN(car->_accelCmd, 1.0);
car->_accelCmd = MAX(car->_accelCmd, 0.0);
}
HCtx[idx]->paccel = car->_accelCmd;
if (HCtx[idx]->autoReverseEngaged)
{
/* swap brake and throttle */
brake = car->_brakeCmd;
car->_brakeCmd = car->_accelCmd;
car->_accelCmd = brake;
}
if (HCtx[idx]->paramAbs)
{
if (fabs(car->_speed_x) > 10.0 && car->_brakeCmd > 0.0)
{
tdble brake1 = car->_brakeCmd, brake2 = car->_brakeCmd, brake3 = car->_brakeCmd;
//tdble rearskid = MAX(0.0, MAX(car->_skid[2], car->_skid[3]) - MAX(car->_skid[0], car->_skid[1]));
int i;
// reduce brake if car sliding sideways
tdble skidAng = atan2(car->_speed_Y, car->_speed_X) - car->_yaw;
NORM_PI_PI(skidAng);
if (car->_speed_x > 5 && fabs(skidAng) > 0.2)
brake1 = MIN(car->_brakeCmd, 0.10 + 0.70 * cos(skidAng));
#if 0
// reduce brake if car steering sharply
if (fabs(car->_steerCmd) > 0.1)
{
tdble decel = ((fabs(car->_steerCmd)-0.1) * (1.0 + fabs(car->_steerCmd)) * 0.2);
brake2 = MIN(car->_brakeCmd, MAX(0.35, 1.0 - decel));
}
#endif
const tdble abs_slip = 1.0;
const tdble abs_range = 9.0;
// reduce brake if wheels are slipping
tdble slip = 0;
for (i = 0; i < 4; i++)
{
slip = MAX(slip, car->_speed_x - (car->_wheelSpinVel(i) * car->_wheelRadius(i)));
}
if (slip > abs_slip)
brake3 = MAX(MIN(0.35, car->_brakeCmd), car->_brakeCmd - MIN(car->_brakeCmd*0.8, (slip - abs_slip) / abs_range));
car->_brakeCmd = MIN(brake1, MIN(brake2, brake3));
}
}
if (HCtx[idx]->paramAsr)
{
tdble origaccel = car->_accelCmd;
tdble drivespeed = 0.0;
switch (HCtx[idx]->driveTrain)
{
case TRANS_4WD:
drivespeed = ((car->_wheelSpinVel(FRNT_RGT) + car->_wheelSpinVel(FRNT_LFT)) *
car->_wheelRadius(FRNT_LFT) +
(car->_wheelSpinVel(REAR_RGT) + car->_wheelSpinVel(REAR_LFT)) *
car->_wheelRadius(REAR_LFT)) / 4.0;
break;
case TRANS_FWD:
drivespeed = (car->_wheelSpinVel(FRNT_RGT) + car->_wheelSpinVel(FRNT_LFT)) *
car->_wheelRadius(FRNT_LFT) / 2.0;
break;
case TRANS_RWD:
// ADJUSTMENTS TO RWD Asr:-
// Originally this purely returned the speed of the wheels, which when the speed of
// the car is subtracted below provides the degree of slip, which is then used to
// reduce the amount of accelerator.
//
// The new calculation below reduces the degree to which the difference between wheel
// and car speed affects slip, and instead looks at the SlipAccel and SlipSide values,
// which are more important as they signify an impending loss of control. The SlipSide
// value is reduced the faster the car's travelling, as usually it matters most in the
// low to mid speed ranges. We also take into account the difference between the
// player's steer command and the actual yaw rate of the vehicle - where the player
// is steering against the yaw rate, we decrease the amount of acceleration to stop
// tirespin sending the rear wheels into a spinout.
tdble friction = MIN(car->_wheelSeg(REAR_RGT)->surface->kFriction, car->_wheelSeg(REAR_LFT)->surface->kFriction) - 0.2;
if (friction < 1.0) friction *= MAX(0.6, friction);
bool steer_correct = (fabs(car->_yaw_rate) > fabs(car->_steerCmd * MAX(4.0, car->_speed_x/12.0) * friction) ||
(car->_yaw_rate < 0.0 && car->_steerCmd > 0.0) ||
(car->_yaw_rate > 0.0 && car->_steerCmd < 0.0));
tdble steer_diff = fabs(car->_yaw_rate - car->_steerCmd);
tdble slipf = (steer_correct ? 8 * friction : 15 * friction);
drivespeed = (((car->_wheelSpinVel(REAR_RGT) + car->_wheelSpinVel(REAR_LFT)) - (20 * friction)) *
car->_wheelRadius(REAR_LFT) +
(steer_correct ? (steer_diff * fabs(car->_yaw_rate) * (8 / friction)) : 0.0) +
MAX(0.0, (-(car->_wheelSlipAccel(REAR_RGT)) - friction)) +
MAX(0.0, (-(car->_wheelSlipAccel(REAR_LFT)) - friction)) +
fabs(car->_wheelSlipSide(REAR_RGT) * MAX(4, 80-fabs(car->_speed_x))/slipf) +
fabs(car->_wheelSlipSide(REAR_LFT) * MAX(4, 80-fabs(car->_speed_x))/slipf))
/ 2.0;
break;
}
tdble slip = drivespeed - fabs(car->_speed_x);
if (slip > 2.5)
car->_accelCmd = MIN(car->_accelCmd, origaccel - MIN(origaccel-0.2, ((slip - 2.5)/20.0)));
}
if (speedLimiter)
{
if (speedLimit != 0)
{
tdble dv = speedLimit - car->_speed_x;
if (dv > 0.0)
{
car->_accelCmd = MIN(car->_accelCmd, fabs(dv/6.0));
}
else
{
car->_brakeCmd = MAX(car->_brakeCmd, fabs(dv/5.0));
car->_accelCmd = 0;
}//if-else dv
}//if speedLimit
}//if speedLimiter
#ifndef WIN32
#ifdef TELEMETRY
if ((car->_laps > 1) && (car->_laps < 5)) {
if (HCtx[idx]->lap == 1) {
RtTelemStartMonitoring("Player");
}
RtTelemUpdate(car->_curLapTime);
}
if (car->_laps == 5) {
if (HCtx[idx]->lap == 4) {
RtTelemShutdown();
}
}
#endif
#endif
HCtx[idx]->lap = car->_laps;
}//common_drive
// simuV4 code ...
/*
* Changes from original: none
*/
static void common_brake(const int idx, tCarElt* car, tSituation *s)
{
if(car->_brakeCmd > 0.0)
{
if (HCtx[idx]->useESP)
{
float DriftAngle = atan2(car->_speed_Y,car->_speed_X) - car->_yaw;
FLOAT_NORM_PI_PI(DriftAngle);
if (DriftAngle > 4.0/180.0*PI)
{
HCtx[idx]->brakeLeft = 1.0f + 0.3f;
HCtx[idx]->brakeRight = 1.0f - 0.3f;
HCtx[idx]->brakeFront = 1.0f + HCtx[idx]->brakeCorr;
HCtx[idx]->brakeRear = 1.0f - HCtx[idx]->brakeCorr;
}
else if (DriftAngle > 2.0/180.0*PI)
{
HCtx[idx]->brakeLeft = 1.0f + 0.3f;
HCtx[idx]->brakeRight = 1.0f - 0.3f;
HCtx[idx]->brakeFront = 1.0f;
HCtx[idx]->brakeRear = 1.0f;
}
else if (DriftAngle < -4.0/180.0*PI)
{
HCtx[idx]->brakeRight = 1.0f + 0.3f;
HCtx[idx]->brakeLeft = 1.0f - 0.3f;
HCtx[idx]->brakeFront = 1.0f + HCtx[idx]->brakeCorr;
HCtx[idx]->brakeRear = 1.0f - HCtx[idx]->brakeCorr;
}
else if (DriftAngle < -2.0/180.0*PI)
{
HCtx[idx]->brakeRight = 1.0f + 0.3f;
HCtx[idx]->brakeLeft = 1.0f - 0.3f;
HCtx[idx]->brakeFront = 1.0f;
HCtx[idx]->brakeRear = 1.0f;
}
else
{
HCtx[idx]->brakeRight = 1.0f;
HCtx[idx]->brakeLeft = 1.0f;
HCtx[idx]->brakeFront = 1.0f;
HCtx[idx]->brakeRear = 1.0f;
}
car->ctrl.singleWheelBrakeMode = 1;
car->ctrl.brakeFrontRightCmd = (float) (car->_brakeCmd * HCtx[idx]->brakeRep * HCtx[idx]->brakeRight * HCtx[idx]->brakeFront);
car->ctrl.brakeFrontLeftCmd = (float) (car->_brakeCmd * HCtx[idx]->brakeRep * HCtx[idx]->brakeLeft * HCtx[idx]->brakeFront);
car->ctrl.brakeRearRightCmd = (float) (car->_brakeCmd * (1 - HCtx[idx]->brakeRep) * HCtx[idx]->brakeRight * HCtx[idx]->brakeRear);
car->ctrl.brakeRearLeftCmd = (float) (car->_brakeCmd * (1 - HCtx[idx]->brakeRep) * HCtx[idx]->brakeLeft * HCtx[idx]->brakeRear);
}
else
car->ctrl.singleWheelBrakeMode = 0;
}
}
// ... simuV4 code
/*
* Changes from original: none
*/
static tdble getAutoClutch(const int idx, int gear, int newGear, tCarElt *car)
{
tdble ret = 0.0f;
if (newGear != 0 && newGear < car->_gearNb)
{
if (newGear != gear)
HCtx[idx]->clutchTime = HCtx[idx]->maxClutchTime;
if (gear == 1 && car->_speed_xy < 10 && HCtx[idx]->clutchTime > 0)
// Hold clutch at 1/2 to allow a faster launch from stationary
HCtx[idx]->clutchTime = HCtx[idx]->maxClutchTime / 2;
ret = HCtx[idx]->clutchTime / HCtx[idx]->maxClutchTime;
}//if newGear
return ret;
}//getAutoClutch
/*
* Changes from original: none
*/
void HumanDriver::drive_mt(int index, tCarElt* car, tSituation *s)
{
const int idx = index - 1;
tControlCmd *cmd = HCtx[idx]->cmdControl;
common_drive(index, car, s);
//Can it be left out? car->_gearCmd = car->_gear;
/* manual shift sequential */
if (HCtx[idx]->transmission == GEAR_MODE_SEQ)
{
/* Up shifting command */
if ((cmd[CMD_UP_SHFT].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[CMD_UP_SHFT].val])
|| (cmd[CMD_UP_SHFT].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgeup[cmd[CMD_UP_SHFT].val])
|| (cmd[CMD_UP_SHFT].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_UP_SHFT].val)].edgeUp)
|| (cmd[CMD_UP_SHFT].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_UP_SHFT].deadZone == 1))
{
if (car->_gear > -1 && car->_gear < car->_gearNb - 1)
car->_gearCmd++;
else if (HCtx[idx]->seqShftAllowNeutral && car->_gear == -1)
car->_gearCmd = 0;
/* always allow up shift out of reverse to improve game play */
else if (car->_gear == -1)
car->_gearCmd = 1;
}
/* Down shifting command */
if ((cmd[CMD_DN_SHFT].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[CMD_DN_SHFT].val])
|| (cmd[CMD_DN_SHFT].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgeup[cmd[CMD_DN_SHFT].val])
|| (cmd[CMD_DN_SHFT].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_DN_SHFT].val)].edgeUp)
|| (cmd[CMD_DN_SHFT].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_DN_SHFT].deadZone == 1))
{
if (car->_gear > 1)
car->_gearCmd--;
else if (HCtx[idx]->seqShftAllowNeutral && car->_gear == 1)
car->_gearCmd = 0;
else if (HCtx[idx]->seqShftAllowReverse && car->_gear < 2)
car->_gearCmd = -1;
}
/* Neutral gear command */
if ((cmd[CMD_GEAR_N].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[CMD_GEAR_N].val])
|| (cmd[CMD_GEAR_N].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgeup[cmd[CMD_GEAR_N].val])
|| (cmd[CMD_GEAR_N].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_GEAR_N].val)].edgeUp)
|| (cmd[CMD_GEAR_N].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_GEAR_N].deadZone == 1))
{
car->_gearCmd = 0;
}
/* Reverse gear command */
if ((cmd[CMD_GEAR_R].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[CMD_GEAR_R].val])
|| (cmd[CMD_GEAR_R].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgeup[cmd[CMD_GEAR_R].val])
|| (cmd[CMD_GEAR_R].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_GEAR_R].val)].edgeUp)
|| (cmd[CMD_GEAR_R].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_GEAR_R].deadZone == 1))
{
/* Only allow Reverse to be selected at low speed (~40kmph) or from neutral */
if (car->_speed_x < 10 || car->_gear == 0)
car->_gearCmd = -1;
}
}
/* manual shift direct (button for each gear) */
else if (HCtx[idx]->transmission == GEAR_MODE_GRID)
{
/* Go to neutral gear if any gear command released (edge down) */
if (HCtx[idx]->relButNeutral) {
for (int i = CMD_GEAR_R; i <= CMD_GEAR_6; i++) {
if ((cmd[i].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgedn[cmd[i].val])
|| (cmd[i].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgedn[cmd[i].val])
|| (cmd[i].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[i].val)].edgeDn))
{
car->_gearCmd = 0;
}
}
}
/* Select the right gear if any gear command activated (edge up) */
for (int i = CMD_GEAR_R; i <= CMD_GEAR_6; i++) {
if ((cmd[i].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[i].val])
|| (cmd[i].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgeup[cmd[i].val])
|| (cmd[i].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[i].val)].edgeUp))
{
car->_gearCmd = i - CMD_GEAR_N;
}
}
}
/* H-Box selector using XY axis of joy/thumbstick */
else if (HCtx[idx]->transmission == GEAR_MODE_HBOX)
{
// Used to test bitfield of allowable changes
int hboxGearTest = 1 << (car->_gear + 1);
float ax0, ay0;
ax0 = joyInfo->ax[cmd[CMD_HBOX_X].val];
ay0 = joyInfo->ax[cmd[CMD_HBOX_Y].val];
if (ax0 > 0.33) {
if (ay0 < -0.66 && hboxChanges[5] & hboxGearTest)
car->_gearCmd = 5;
if (car->_speed_x < 10) {
/* 'R' Only selectable at low speed */
if (ay0 > 0.66 && hboxChanges[0] & hboxGearTest)
car->_gearCmd = -1;
} else {
/* '6' Only selectable at high speed */
if (ay0 > 0.66 && hboxChanges[6] & hboxGearTest)
car->_gearCmd = 6;
}
} else if (ax0 < -0.33) {
if (ay0 < -0.66 && hboxChanges[1] & hboxGearTest)
car->_gearCmd = 1;
if (ay0 > 0.66 && hboxChanges[2] & hboxGearTest)
car->_gearCmd = 2;
} else {
if (ay0 < -0.66 && hboxChanges[3] & hboxGearTest)
car->_gearCmd = 3;
if (ay0 > 0.66 && hboxChanges[4] & hboxGearTest)
car->_gearCmd = 4;
}
/* 'N' selectable from any gear */
if (ay0 < 0.33 && ay0 > -0.33 && ax0 > -0.5 && ax0 < -0.33)
car->_gearCmd = 0;
/* Extended 'N' area when using clutch to allow 'jumping' gears */
if (ay0 < 0.33 && ay0 > -0.33 && ax0 > -0.5 && ax0 < 0.5 && !HCtx[idx]->autoClutch)
car->_gearCmd = 0;
/* Neutral gear command */
if ((cmd[CMD_GEAR_N].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[CMD_GEAR_N].val])
|| (cmd[CMD_GEAR_N].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgeup[cmd[CMD_GEAR_N].val])
|| (cmd[CMD_GEAR_N].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_GEAR_N].val)].edgeUp)
|| (cmd[CMD_GEAR_N].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_GEAR_N].deadZone == 1))
{
car->_gearCmd = 0;
}
/* Reverse gear command */
if ((cmd[CMD_GEAR_R].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[CMD_GEAR_R].val])
|| (cmd[CMD_GEAR_R].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgeup[cmd[CMD_GEAR_R].val])
|| (cmd[CMD_GEAR_R].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_GEAR_R].val)].edgeUp)
|| (cmd[CMD_GEAR_R].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_GEAR_R].deadZone == 1))
{
/* Only allow Reverse to be selected at low speed (~40kmph) or from neutral */
if (car->_speed_x < 10 || car->_gear == 0)
car->_gearCmd = -1;
}
}
// check if Grid gear shift is left in gear
if (preGear) {
car->_gearCmd = preGear;
preGear = 0;
}
if (HCtx[idx]->autoClutch && car->_clutchCmd == 0.0f)
car->_clutchCmd = getAutoClutch(idx, car->_gear, car->_gearCmd, car);
common_brake(idx, car, s);
}
/*
* Changes from original: none
*/
void HumanDriver::drive_at(int index, tCarElt* car, tSituation *s)
{
const int idx = index - 1;
tControlCmd *cmd = HCtx[idx]->cmdControl;
common_drive(index, car, s);
/* shift */
int gear = car->_gear;
gear += car->_gearOffset;
//can it be left out? car->_gearCmd = car->_gear;
if (!HCtx[idx]->autoReverse) {
/* manual shift */
if ((cmd[CMD_UP_SHFT].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[CMD_UP_SHFT].val])
|| (cmd[CMD_UP_SHFT].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgeup[cmd[CMD_UP_SHFT].val])
|| (cmd[CMD_UP_SHFT].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_UP_SHFT].val)].edgeUp)
|| (cmd[CMD_UP_SHFT].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_UP_SHFT].deadZone == 1))
{
if (car->_gear == 0)
HCtx[idx]->manual = false; // switch back to auto gearbox
else
HCtx[idx]->manual = true;
if (!HCtx[idx]->seqShftAllowNeutral && car->_gear == -1) {
HCtx[idx]->manual = false; // switch back to auto gearbox
car->_gearCmd = 1;
} else
car->_gearCmd++;
}//CMD_UP_SHFT
if ((cmd[CMD_DN_SHFT].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[CMD_DN_SHFT].val])
|| (cmd[CMD_DN_SHFT].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgeup[cmd[CMD_DN_SHFT].val])
|| (cmd[CMD_DN_SHFT].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_DN_SHFT].val)].edgeUp)
|| (cmd[CMD_DN_SHFT].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_DN_SHFT].deadZone == 1))
{
if (car->_gear > 1)
{
car->_gearCmd--;
HCtx[idx]->manual = true;
}
else if (HCtx[idx]->seqShftAllowNeutral && car->_gear == 1)
{
car->_gearCmd = 0;
HCtx[idx]->manual = true;
}
else if (HCtx[idx]->seqShftAllowReverse && car->_gear < 2)
{
car->_gearCmd = -1;
HCtx[idx]->manual = true;
}
}//CMD_DN_SHFT
/* Neutral gear command */
if ((cmd[CMD_GEAR_N].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[CMD_GEAR_N].val])
|| (cmd[CMD_GEAR_N].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgeup[cmd[CMD_GEAR_N].val])
|| (cmd[CMD_GEAR_N].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_GEAR_N].val)].edgeUp)
|| (cmd[CMD_GEAR_N].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_GEAR_N].deadZone == 1))
{
car->_gearCmd = 0;
HCtx[idx]->manual = true;
}
/* Reverse gear command */
if ((cmd[CMD_GEAR_R].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[CMD_GEAR_R].val])
|| (cmd[CMD_GEAR_R].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgeup[cmd[CMD_GEAR_R].val])
|| (cmd[CMD_GEAR_R].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[CMD_GEAR_R].val)].edgeUp)
|| (cmd[CMD_GEAR_R].type == GFCTRL_TYPE_JOY_ATOB && cmd[CMD_GEAR_R].deadZone == 1))
{
/* Only allow Reverse to be selected at low speed (~36kmph) or from neutral */
if (car->_speed_x < 10 || car->_gear == 0) {
car->_gearCmd = -1;
HCtx[idx]->manual = true;
}
}
/* manual shift direct ie. Reverse-Park-Drive*/
if (HCtx[idx]->relButNeutral) {
for (int i = CMD_GEAR_R; i < CMD_GEAR_2; i++)
{
if ((cmd[i].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgedn[cmd[i].val])
|| (cmd[i].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgedn[cmd[i].val])
|| (cmd[i].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[i].val)].edgeDn))
{
car->_gearCmd = 0;
HCtx[idx]->manual = false; //return to auto-shift
}
}
}
/* Select the right gear if any gear command activated (edge up) */
for (int i = CMD_GEAR_R; i < CMD_GEAR_2; i++)
{
if ((cmd[i].type == GFCTRL_TYPE_JOY_BUT && joyInfo->edgeup[cmd[i].val])
|| (cmd[i].type == GFCTRL_TYPE_MOUSE_BUT && mouseInfo->edgeup[cmd[i].val])
|| (cmd[i].type == GFCTRL_TYPE_KEYBOARD && keyInfo[lookUpKeyMap(cmd[i].val)].edgeUp))
{
car->_gearCmd = i - CMD_GEAR_N;
if (car->_gearCmd > 0)
HCtx[idx]->manual = false; //return to auto-shift
else
HCtx[idx]->manual = true;
}
}
}//if !autoReverse
/* auto shift */
if (!HCtx[idx]->manual && !HCtx[idx]->autoReverseEngaged)
{
if (car->_speed_x > HCtx[idx]->shiftThld[gear]) {
car->_gearCmd++;
}
else if ((car->_gearCmd > 1) && (car->_speed_x < (HCtx[idx]->shiftThld[gear-1] - 4.0)))
{
car->_gearCmd--;
}
if (car->_gearCmd <= 0)
car->_gearCmd++;
}//if !manual && !autoReverse
/* Automatic Reverse Gear Mode */
if (HCtx[idx]->autoReverse)
{
if (!HCtx[idx]->autoReverseEngaged)
{ //currently not in autoReverse
if ((car->_brakeCmd > car->_accelCmd) && (car->_speed_xy < 1.0))
{
HCtx[idx]->autoReverseEngaged = true;
car->_gearCmd = CMD_GEAR_R - CMD_GEAR_N;
}
}
else
{ //currently in autoreverse mode
if ((car->_brakeCmd > car->_accelCmd) && (car->_speed_x > -1.0) && (car->_speed_x < 1.0))
{
HCtx[idx]->autoReverseEngaged = false;
car->_gearCmd = CMD_GEAR_1 - CMD_GEAR_N;
}
else
{
car->_gearCmd = CMD_GEAR_R - CMD_GEAR_N;
}
}//if-else autoReverseEngaged
}//if autoReverse
/* Automatic clutch mode */
if (HCtx[idx]->autoClutch && car->_clutchCmd == 0.0f)
car->_clutchCmd = getAutoClutch(idx, car->_gear, car->_gearCmd, car);
common_brake(idx, car, s);
}
/*
* Changes from original: none
*/
int HumanDriver::pit_cmd(int index, tCarElt* car, tSituation *s)
{
const int idx = index - 1;
HCtx[idx]->nbPitStops++; //Yet another pitstop
//tdble curr_fuel = car->_tank - car->_fuel; //Can receive max. this fuel
/*tdble planned_stops = 1.0
+ MAX(HCtx[idx]->nbPitStopProg - HCtx[idx]->nbPitStops, 0);*/ //Planned pitstops still ahead
//Need this amount of extra fuel to finish the race
/*tdble fuel =
( MaxFuelPerMeter
* (curTrack->length * car->_remainingLaps + car->_trkPos.seg->lgfromstart)
+ 2.7f / 60.0f * MAX(s->_totTime, 0) )
/ planned_stops
- car->_fuel;*/
//No need to check for limits as curr_fuel cannot be bigger
//than the tank capacity
//car->_pitFuel = MAX(MIN(curr_fuel, fuel), 0);
car->_pitFuel = car->setup.fuel.desired_value - car->_fuel;
HCtx[idx]->lastPitStopLap = car->_laps;
//car->_pitRepair = (int)car->_dammage;
car->_pitRepair = car->setup.reqRepair.desired_value;
if (car->setup.reqPenalty.desired_value > 0.9)
{
car->_pitStopType = RM_PIT_STOPANDGO;
}
else
{
car->_pitStopType = RM_PIT_REPAIR;
car->pitcmd.tireChange = tCarPitCmd::TireChange::NONE;
if (car->setup.reqTireset.desired_value > 0)
{
GfLogInfo("~ player tyre change asked = %.0f\n", car->setup.reqTireset.desired_value);
car->pitcmd.tireChange = (tCarPitCmd::TireChange::ALL);
GfLogInfo("~ player tyre compound asked = %.0f\n", car->setup.reqTirecompound.desired_value);
switch((int)car->setup.reqTirecompound.desired_value)
{
case 1:
car->pitcmd.tiresetChange = tCarPitCmd::SOFT;
GfLogInfo("~ player tyre compound asked = SOFT\n");
break;
case 2:
car->pitcmd.tiresetChange = tCarPitCmd::MEDIUM;
GfLogInfo("~ player tyre compound asked = MEDIUM\n");
break;
case 3:
car->pitcmd.tiresetChange = tCarPitCmd::HARD;
GfLogInfo("~ player tyre compound asked = HARD\n");
break;
case 4:
car->pitcmd.tiresetChange = tCarPitCmd::WET;
GfLogInfo("~ player tyre compound asked = WET\n");
break;
case 5:
car->pitcmd.tiresetChange = tCarPitCmd::EXTREM_WET;
GfLogInfo("~ player tyre compound asked = EXTREM WET\n");
break;
default:
car->pitcmd.tiresetChange = tCarPitCmd::HARD;
}
}
}
if (HCtx[idx])
{
const tControlCmd *cmd = HCtx[idx]->cmdControl;
for (int i = 0; i < NbCmdControl; i++)
{
if (cmd[i].type == GFCTRL_TYPE_KEYBOARD)
{
const int key = lookUpKeyMap(cmd[i].val);
keyInfo[key].state = GFUI_KEY_UP;
keyInfo[key].edgeDn = 0;
keyInfo[key].edgeUp = 0;
lastReadKeyState[key] = GFUI_KEY_UP;
}
}//for i
}//if HCtx
return ROB_PIT_IM; /* With dashboard no need for menu */
}
/*
* Changes from original: none
*/
// Trivial strategy:
// fill in as much fuel as required for the whole race,
// or if the tank is too small, fill the tank completely.
static void SetFuelAtRaceStart(tTrack* track, void *carHandle, void **carParmHandle,
tSituation *s, int idx)
{
tdble fuel_requested;
const tdble initial_fuel = GfParmGetNum(*carParmHandle, SECT_CAR,
PRM_FUEL, NULL, 0.0f);
if (initial_fuel) {
// If starting fuel is set up explicitly,
// no use computing anything...
fuel_requested = initial_fuel;
} else {
// We must load and calculate parameters.
const tdble fuel_cons_factor =
GfParmGetNum(*carParmHandle, SECT_ENGINE, PRM_FUELCONS, NULL, 1.0f);
tdble fuel_per_lap = track->length * MaxFuelPerMeter * fuel_cons_factor;
tdble fuel_for_race = fuel_per_lap * (s->_totLaps + 1.0f);// + FuelReserve;
// aimed at timed sessions:
fuel_for_race += fuel_per_lap / 60.0 * MAX(s->_totTime, 0);
// divide qty by planned pitstops:
fuel_for_race /= (1.0 + ((tdble)HCtx[idx]->nbPitStopProg));
// add some reserve:
//fuel_for_race += FuelReserve;
const tdble tank_capacity = GfParmExistsParam(*carParmHandle, SECT_CAR, PRM_TANK) ?
GfParmGetNum(*carParmHandle, SECT_CAR, PRM_TANK, NULL, 100.0f) :
GfParmGetNum(carHandle, SECT_CAR, PRM_TANK, NULL, 100.0f);
fuel_requested = MIN(fuel_for_race, tank_capacity);
}
GfLogInfo("Human #%d : Starting race session with %.1f l of fuel (%s)\n",
idx, fuel_requested, initial_fuel ? "as explicitly stated" : "computed");
GfParmSetNum(*carParmHandle, SECT_CAR, PRM_FUEL, NULL, fuel_requested);
} // SetFuelAtRaceStart
/*
* Original function: HmReadPrefs
*
* Changes from original:
*
* Add parameter for human player index. For human robot robot_index and
* player_index are always the same. For networkhuman robot, the indexes may be
* different.
*
* Remove allocation for cmdControl.
*
* Notes:
*
* Never call this function directly. Always call the read_prefs virtual
* function which determines the correct player_index for the robot.
*/
void HumanDriver::human_prefs(const int robot_index, int player_index)
{
const char *prm;
const char *defaultSettings;
char sstring[1024];
tCtrlRef *ref;
const int idx = robot_index - 1;
tControlCmd *cmdCtrl;
cmdCtrl = HCtx[idx]->cmdControl;
memcpy(cmdCtrl, CmdControlRef, NbCmdControl * sizeof (tControlCmd));
if (!PrefHdle)
{
PrefHdle = GfParmReadFileLocal(HM_PREF_FILE, GFPARM_RMODE_REREAD | GFPARM_RMODE_CREAT);
}
sprintf(sstring, "%s/%s/%d", HM_SECT_PREF, HM_LIST_DRV, player_index);
prm = GfParmGetStr(PrefHdle, sstring, HM_ATT_TRANS, HM_VAL_AUTO);
if (!strcmp(prm, HM_VAL_AUTO))
HCtx[idx]->transmission = GEAR_MODE_AUTO;
else if (!strcmp(prm, HM_VAL_SEQ))
HCtx[idx]->transmission = GEAR_MODE_SEQ;
else if (!strcmp(prm, HM_VAL_HBOX))
HCtx[idx]->transmission = GEAR_MODE_HBOX;
else
HCtx[idx]->transmission = GEAR_MODE_GRID;
/* Parameters Settings */
//ABS on/off
prm = GfParmGetStr(PrefHdle, sstring, HM_ATT_ABS, Yn[HCtx[idx]->paramAbs].c_str());
HCtx[idx]->paramAbs = (prm == Yn[0]);
//ASR on/off
prm = GfParmGetStr(PrefHdle, sstring, HM_ATT_ASR, Yn[HCtx[idx]->paramAsr].c_str());
HCtx[idx]->paramAsr = (prm == Yn[0]);
//Controls
prm = GfParmGetStr(PrefHdle, HM_SECT_PREF, HM_ATT_CONTROL, controlList[2].parmName);
prm = GfParmGetStr(PrefHdle, sstring, HM_ATT_CONTROL, prm);
int i;
for (i = 0; i < nbControl; i++) {
if (!strcmp(prm, controlList[i].parmName))
break;
}//for i
if (i == nbControl)
i = 2;
if (i == 0 && !joyPresent)
i = 2;
defaultSettings = controlList[i].settings;
/* Command Settings */
GfOut("Command settings for index %d:\n", player_index);
for (int cmd = 0; cmd < NbCmdControl; cmd++) {
prm = GfctrlGetNameByRef(cmdCtrl[cmd].type, cmdCtrl[cmd].val);
prm = GfParmGetStr(PrefHdle, defaultSettings, cmdCtrl[cmd].name, prm);
prm = GfParmGetStr(PrefHdle, sstring, cmdCtrl[cmd].name, prm);
if (!prm || strlen(prm) == 0) {
cmdCtrl[cmd].type = GFCTRL_TYPE_NOT_AFFECTED;
GfOut(" %s\t: None (-1)\n", cmdCtrl[cmd].name);
continue;
}
ref = GfctrlGetRefByName(prm);
cmdCtrl[cmd].type = ref->type; // GFCTRL_TYPE_XX
cmdCtrl[cmd].val = ref->index; // Index for joy. axis, buttons ; 1-bytes ASCII code for keys.
GfOut(" %s\t: %s\n", cmdCtrl[cmd].name, prm);
/* min value < max value */
if (cmdCtrl[cmd].minName) {
cmdCtrl[cmd].min = (float)GfParmGetNum(PrefHdle, defaultSettings, cmdCtrl[cmd].minName, (char*)NULL, (tdble)cmdCtrl[cmd].min);
cmdCtrl[cmd].min = cmdCtrl[cmd].minVal = (float)GfParmGetNum(PrefHdle, sstring, cmdCtrl[cmd].minName, (char*)NULL, (tdble)cmdCtrl[cmd].min);
}//if minName
/* max value > min value */
if (cmdCtrl[cmd].maxName) {
cmdCtrl[cmd].max = (float)GfParmGetNum(PrefHdle, defaultSettings, cmdCtrl[cmd].maxName, (char*)NULL, (tdble)cmdCtrl[cmd].max);
cmdCtrl[cmd].max = (float)GfParmGetNum(PrefHdle, sstring, cmdCtrl[cmd].maxName, (char*)NULL, (tdble)cmdCtrl[cmd].max);
}//if maxName
/* 0 < sensitivity */
if (cmdCtrl[cmd].sensName) {
cmdCtrl[cmd].sens = (float)GfParmGetNum(PrefHdle, defaultSettings, cmdCtrl[cmd].sensName, (char*)NULL, (tdble)cmdCtrl[cmd].sens);
cmdCtrl[cmd].sens = (float)GfParmGetNum(PrefHdle, sstring, cmdCtrl[cmd].sensName, (char*)NULL, (tdble)cmdCtrl[cmd].sens);
if (cmdCtrl[cmd].sens <= 0.0)
cmdCtrl[cmd].sens = 1.0e-6;
}//if sensName
/* 0 < power (1 = linear) */
if (cmdCtrl[cmd].powName) {
cmdCtrl[cmd].pow = (float)GfParmGetNum(PrefHdle, defaultSettings, cmdCtrl[cmd].powName, (char*)NULL, (tdble)cmdCtrl[cmd].pow);
cmdCtrl[cmd].pow = (float)GfParmGetNum(PrefHdle, sstring, cmdCtrl[cmd].powName, (char*)NULL, (tdble)cmdCtrl[cmd].pow);
}//if powName
/* 0 <= sensitivity to car speed */
if (cmdCtrl[cmd].spdSensName) {
cmdCtrl[cmd].spdSens = (float)GfParmGetNum(PrefHdle, defaultSettings, cmdCtrl[cmd].spdSensName, (char*)NULL, (tdble)cmdCtrl[cmd].spdSens);
cmdCtrl[cmd].spdSens = (float)GfParmGetNum(PrefHdle, sstring, cmdCtrl[cmd].spdSensName, (char*)NULL, (tdble)cmdCtrl[cmd].spdSens);
if (cmdCtrl[cmd].spdSens < 0.0)
cmdCtrl[cmd].spdSens = 0.0;
}//if spdSendName
/* 0 =< dead zone < max value - min value (not used for on/off controls like keyboard / mouse buttons / joystick buttons) */
if (cmdCtrl[cmd].deadZoneName) {
cmdCtrl[cmd].deadZone = (float)GfParmGetNum(PrefHdle, defaultSettings, cmdCtrl[cmd].deadZoneName, (char*)NULL, (tdble)cmdCtrl[cmd].deadZone);
cmdCtrl[cmd].deadZone = (float)GfParmGetNum(PrefHdle, sstring, cmdCtrl[cmd].deadZoneName, (char*)NULL, (tdble)cmdCtrl[cmd].deadZone);
if (cmdCtrl[cmd].deadZone < 0.0)
cmdCtrl[cmd].deadZone = 0.0;
else if (cmdCtrl[cmd].deadZone > 1.0)
cmdCtrl[cmd].deadZone = 1.0;
}//if deadZoneName
if (cmdCtrl[cmd].min > cmdCtrl[cmd].max)
std::swap(cmdCtrl[cmd].min, cmdCtrl[cmd].max);
//cmdCtrl[cmd].deadZone = (cmdCtrl[cmd].max - cmdCtrl[cmd].min) * cmdCtrl[cmd].deadZone;
if (cmdCtrl[cmd].type == GFCTRL_TYPE_MOUSE_AXIS)
{
HCtx[idx]->mouseControlUsed = 1;
init_mouse = true;
}
}//for cmd
prm = GfParmGetStr(PrefHdle, defaultSettings, HM_ATT_REL_BUT_NEUTRAL, Yn[HCtx[idx]->relButNeutral].c_str());
prm = GfParmGetStr(PrefHdle, sstring, HM_ATT_REL_BUT_NEUTRAL, prm);
HCtx[idx]->relButNeutral = (prm == Yn[0]);
prm = GfParmGetStr(PrefHdle, defaultSettings, HM_ATT_SEQSHFT_ALLOW_NEUTRAL, Yn[HCtx[idx]->seqShftAllowNeutral].c_str());
prm = GfParmGetStr(PrefHdle, sstring, HM_ATT_SEQSHFT_ALLOW_NEUTRAL, prm);
HCtx[idx]->seqShftAllowNeutral = (prm == Yn[0]);
prm = GfParmGetStr(PrefHdle, defaultSettings, HM_ATT_SEQSHFT_ALLOW_REVERSE, Yn[HCtx[idx]->seqShftAllowReverse].c_str());
prm = GfParmGetStr(PrefHdle, sstring, HM_ATT_SEQSHFT_ALLOW_REVERSE, prm);
HCtx[idx]->seqShftAllowReverse = (prm == Yn[0]);
prm = GfParmGetStr(PrefHdle, sstring, HM_ATT_AUTOREVERSE, Yn[HCtx[idx]->autoReverse].c_str());
HCtx[idx]->autoReverse = (prm == Yn[0]);
if (HCtx[idx]->transmission != GEAR_MODE_GRID) {
for (int k = CMD_GEAR_2; k <= CMD_GEAR_6; k++) {
cmdCtrl[k].type = GFCTRL_TYPE_NOT_AFFECTED;
}
}
if (HCtx[idx]->transmission != GEAR_MODE_HBOX) {
cmdCtrl[CMD_HBOX_X].type = GFCTRL_TYPE_NOT_AFFECTED;
cmdCtrl[CMD_HBOX_Y].type = GFCTRL_TYPE_NOT_AFFECTED;
}
if (HCtx[idx]->transmission == GEAR_MODE_HBOX)
cmdCtrl[CMD_GEAR_1].type = GFCTRL_TYPE_NOT_AFFECTED;
if (HCtx[idx]->transmission == GEAR_MODE_AUTO && HCtx[idx]->autoReverse) {
cmdCtrl[CMD_GEAR_R].type = GFCTRL_TYPE_NOT_AFFECTED;
cmdCtrl[CMD_GEAR_N].type = GFCTRL_TYPE_NOT_AFFECTED;
cmdCtrl[CMD_GEAR_1].type = GFCTRL_TYPE_NOT_AFFECTED;
}
}
HumanDriver::HumanDriver(const char *robotname)
{
this->robotname = robotname;
joyPresent = false;
joyInfo = NULL;
mouseInfo = NULL;
ControlsUpdaterIndex = -1;
speedLimiter = false;
keyIndex = 0;
lastKeyUpdate = -10.0;
PrefHdle = NULL;
NbDrivers = -1;
preGear = 0;
resume_keybd = true;
init_keybd = true;
init_mouse = false;
}
bool HumanDriver::uses_at(int index)
{
return HCtx[index-1]->transmission == GEAR_MODE_AUTO;
}
void HumanDriver::read_prefs(int index)
{
human_prefs(index, index);
}