Redline/source/controls.cpp

691 lines
18 KiB
C++
Raw Permalink Normal View History

#include <stdio.h>
#include <string.h>
#include <math.h>
#include "entities.h"
#include "gameframe.h"
#include "carphysics.h"
#include "controls.h"
#include "config.h"
#include "renderframe.h"
#include "gameinitexit.h"
#include "particles.h"
#include "gametime.h"
#include "network.h"
#include "random.h"
#include "initexit.h"
#include "text.h"
#define kThrottleTime 0.3
#define kThrottleReleaseTime 0.2
#define kThrottleTCRReleaseTime 0.7
#define kThrottleTimeSpinning 0.6
#define kBrakeTime 0.4
#define kBrakeReleaseTime 0.2
#define kSteerReleaseTime 0.6
#define kVeloSteerReleaseTime 0
//#define kSteerTime 1.2
#define kSteerTime 0.6
#define kVeloSteerTime 0.05
#define kHandbrakeTime 0.2
#define kHandbrakeReleaseTime 0.01
#define sign(x) ((x)<0?-1:1)
#define kMaxThottleSlip 0.033
#define kMinSpinAngularVelo (4*PI)
#define kMaxBrakeSlip 0.05
int gGearUpPress=false;
int gGearDownPress=false;
int gLightPress=false;
int gCameraPress=false;
int gReplayCameraPress=false;
int gTauntPress=false;
int gArrowPress=false;
int gEscPress=false;
int gPausePress=false;
float gSteerAxis=0;
float gThrottleAxis=0;
int gAxisSteering=false;
int gAxisThrottleBrake=false;
int gInputChatMode=false;
int gInputEscMode=kInputEscModeNone;
int gInputEscSelection;
int gGameChatMessageSize=0;
#define kMaxGameChatMessageSize 128
char gGameChatMessage[kMaxGameChatMessageSize]="";
void ButtonThrottle(tCarDefinition *car,tCarPhysics *phys,float driveSlip,float angularVelo,float velo,float throttleInput,int tcr)
{
float maxSlip=velo<20?0.2-velo/20*(0.2-kMaxThottleSlip):kMaxThottleSlip;
if(throttleInput<0)
throttleInput=0;
if(throttleInput>phys->throttle){
if(driveSlip*sign(phys->gear)<maxSlip||fabs(angularVelo)<kMinSpinAngularVelo||!tcr||(phys->clutch<1&&phys->rpm>car->clutchRPM))
{
phys->throttle+=kFrameTime*(1/kThrottleTime);
if(throttleInput>phys->throttle)
throttleInput=phys->throttle;
}
else
{
phys->throttle-=kFrameTime*(1/kThrottleTCRReleaseTime);
if(throttleInput<phys->throttle)
throttleInput=phys->throttle;
}
}
else {
phys->throttle-=kFrameTime*(1/kThrottleReleaseTime);
if(throttleInput<phys->throttle)
throttleInput=phys->throttle;
}
if(phys->throttle>1)
phys->throttle=1;
else if(phys->throttle<phys->idleThrottle&&phys->rpm<=car->idleRPM)
phys->throttle=phys->idleThrottle;
phys->clutch=1;
}
void ButtonBrake(tCarPhysics *phys,float slip,float angularVelo,float brakeInput,int als)
{
if(brakeInput>1)
brakeInput=1;
else if(brakeInput<0)
brakeInput=0;
if(brakeInput<0)
brakeInput=0;
if(brakeInput>phys->brake)
{
if((-slip*sign(angularVelo)<kMaxBrakeSlip)||(phys->brake<0.1)||!als)
phys->brake+=kFrameTime*(1/kBrakeTime);
else
phys->brake-=kFrameTime*(1/kBrakeTime);
phys->arcadeBrake+=kFrameTime*(1/kBrakeTime);
if(brakeInput<phys->brake)
phys->brake=brakeInput;
if(brakeInput<phys->arcadeBrake)
phys->arcadeBrake=brakeInput;
}
else
{
phys->brake-=kFrameTime*(1/kBrakeReleaseTime);
phys->arcadeBrake-=kFrameTime*(1/kBrakeReleaseTime);
if(brakeInput>phys->brake)
phys->brake=brakeInput;
if(brakeInput>phys->arcadeBrake)
phys->arcadeBrake=brakeInput;
}
}
void CalcThrottleBrake(tCarDefinition *car,tCarPhysics *phys,float driveSlip,float angularVelo,float velo,float slip)
{
if(car->clutchRPM==0)
car->clutchRPM=3000;
int raceOver=(phys->lapCount>gGameInfo->numLaps&&gGameInfo->numLaps!=-1);
if(!raceOver)
{
float gas,brake,gasbrake;
if(gConfig->seperateGasBrake)
{
if(gConfig->reverseGas)
{
gas=GetAxisInput(kInputThrottleAxis);
brake=GetAxisInput(kInputBrakeAxis);
}
else
{
gas=1-GetAxisInput(kInputThrottleAxis);
brake=1-GetAxisInput(kInputBrakeAxis);
}
gasbrake=gas-brake;
}
else
{
gasbrake=GetAxisInput(kInputThrottleBrakeAxis);
if(gConfig->reverseGas)
gasbrake=-gasbrake;
gas=gasbrake;
brake=-gasbrake;
}
if(gAxisThrottleBrake)
{
if(gConfig->automatic&&phys->gear==-1)
{
ButtonBrake(phys,slip,angularVelo,gas,true);
ButtonThrottle(car,phys,driveSlip,angularVelo,velo,brake,(brake<0.95)&&!gConfig->disableAnalogueTCS);
}
else
{
ButtonBrake(phys,slip,angularVelo,brake,true);
ButtonThrottle(car,phys,driveSlip,angularVelo,velo,gas,(gas<0.95)&&!gConfig->disableAnalogueTCS);
}
if(GetButtonInput(kInputKickdownButton)||GetButtonInput(kInputBrakeButton)||GetButtonInput(kInputGasButton))
gAxisThrottleBrake=false;
}
else
{
int brake=GetButtonInput(kInputBrakeButton);
int gas=GetButtonInput(kInputGasButton);
int kickdown=GetButtonInput(kInputKickdownButton);
int gasInput=(gConfig->automatic?(phys->gear>=0?gas||kickdown:brake):(gas||kickdown))?1:0;
ButtonThrottle(car,phys,driveSlip,angularVelo,velo,gasInput,!kickdown);
int brakeInput=(gConfig->automatic?(phys->gear>=0?brake:gas||kickdown):brake)?1:0;
ButtonBrake(phys,slip,angularVelo,brakeInput,true);
if(gThrottleAxis!=gasbrake)
gAxisThrottleBrake=true;
}
gThrottleAxis=gasbrake;
}
else
{
ButtonBrake(phys,slip,angularVelo,1,true);
ButtonThrottle(car,phys,driveSlip,angularVelo,velo,0,true);
}
}
void ButtonSteering(tGameEntity *entity,tCarDefinition *car,tCarPhysics *phys,float velo,float input)
{
if(input>1) input=1;
else if(input<-1) input=-1;
tVector3 carDir=!Vector(MatrixGetZVector(entity->dir)->x,0,MatrixGetZVector(entity->dir)->z);
tVector3 veloDir=!Vector(entity->velo.x,0,entity->velo.z);
float angleSin=(veloDir%carDir).y;
if(fabs(angleSin)>1.0)
angleSin=sign(angleSin);
float angle=-asin(angleSin);
float optimalSteering=angle/car->wheels[0].maxAngle;
if(velo<1)
optimalSteering=0;
if(input>phys->steering)
{
float steerSpeed=(phys->steering<0)?(1/(kSteerReleaseTime+kVeloSteerReleaseTime*velo)) :(1/(kSteerTime+kVeloSteerTime*velo));
if(phys->steering<optimalSteering)
steerSpeed*=1+(optimalSteering-phys->steering)*4;
if(gGameInfo->arcade==kGameModeTurbo)
steerSpeed*=2;
phys->steering+=kFrameTime*steerSpeed;
if(input<phys->steering)
phys->steering=input;
}
else if(input<phys->steering)
{
float steerSpeed=(phys->steering>0)?(1/(kSteerReleaseTime+kVeloSteerReleaseTime*velo)) :(1/(kSteerTime+kVeloSteerTime*velo));
if(phys->steering>optimalSteering)
steerSpeed*=1+(phys->steering-optimalSteering)*4;
if(gGameInfo->arcade==kGameModeTurbo)
steerSpeed*=2;
phys->steering-=kFrameTime*steerSpeed;
if(input>phys->steering)
phys->steering=input;
}
}
void CalcSteering(tGameEntity *entity,tCarDefinition *car,tCarPhysics *phys,float velo)
{
if(gAxisSteering)
{
gSteerAxis=GetAxisInput(kInputSteerAxis);
float ax3=gSteerAxis*gSteerAxis*gSteerAxis;
float ax=0.3*gSteerAxis+0.7*ax3;
ButtonSteering(entity,car,phys,velo,ax);
if(GetButtonInput(kInputSteerLeftButton)||GetButtonInput(kInputSteerRightButton))
gAxisSteering=false;
}
else
{
float input=0;
int l=GetButtonInput(kInputSteerLeftButton);
int r=GetButtonInput(kInputSteerRightButton);
if(l&&r)
input=phys->steering;
else if(l)
input=1;
else if(r)
input=-1;
ButtonSteering(entity,car,phys,velo,input);
if(gSteerAxis!=GetAxisInput(kInputSteerAxis))
gAxisSteering=true;
}
}
typedef struct{
char *name;
int numCopies;
}tRegData;
void ControlEntityUserInput(tGameEntity *entity)
{
if(entity->physicsType!=kPhysicsTypeCar)
return;
tCarPhysics *phys=(tCarPhysics*)entity->physics;
tCarDefinition *car=&(phys->car);
float velo=~entity->velo;
float driveSlip=0,slip=0,angularVelo=0;
int wheelsOnGround=0;
for(int i=0;i<car->numWheels;i++)
{
if(phys->wheels[i].onGround)
{
driveSlip+=phys->wheels[i].slip*car->wheels[i].powered;
slip+=phys->wheels[i].slip;
wheelsOnGround++;
}
angularVelo+=phys->wheels[i].angularVelo*car->wheels[i].powered;
}
if(wheelsOnGround)
slip/=wheelsOnGround;
else
slip=0;
CalcThrottleBrake(car,phys,driveSlip,angularVelo,velo,slip);
CalcSteering(entity,car,phys,velo);
if(GetButtonInput(kInputHandbrakeButton))
phys->handbrake+=kFrameTime*(1/kHandbrakeTime);
else
phys->handbrake-=kFrameTime*(1/kHandbrakeReleaseTime);
if(phys->handbrake>1)
phys->handbrake=1;
else if(phys->handbrake<0)
phys->handbrake=0;
if(gFrameCount*kFrameTime>=kStartGameDelaySeconds)
{
if(gConfig->automatic)
{
if((gFrameCount-1)*kFrameTime>phys->lastGearSwitch+car->gearSwitchTime)
{
if(phys->gear>=1)
{
int shiftUp=false;
if(car->shiftUpRPMFix)
shiftUp=phys->rpm>car->shiftUpRPMFix;
else
shiftUp=phys->rpm>car->maxRPM;
if(shiftUp&&phys->gear<car->numGears-2)
{
if(!GetButtonInput(kInputKickdownButton)&&phys->gear>0&&gFrameCount*kFrameTime>=kStartGameDelaySeconds+1)
{
phys->lastGear=phys->gear;
phys->gear++;
phys->lastGearSwitch=gFrameCount*kFrameTime;
if(gGameInfo->arcade==kGameModeTurbo)
phys->lastGearSwitch-=car->gearSwitchTime*0.5;
}
}
else if(phys->rpm<car->shiftDownRPM&&phys->gear>1)
{
phys->lastGear=phys->gear;
phys->gear--;
phys->lastGearSwitch=gFrameCount*kFrameTime;
if(gGameInfo->arcade==kGameModeTurbo)
phys->lastGearSwitch-=car->gearSwitchTime*0.5;
}
}
if(phys->gear==0||velo<1)
{
if(phys->gear<1&&((GetButtonInput(kInputKickdownButton)||GetButtonInput(kInputGasButton)))||(gAxisThrottleBrake&&gThrottleAxis>0))
{
phys->lastGear=phys->gear;
phys->gear=1;
phys->lastGearSwitch=gFrameCount*kFrameTime;
}
else if(phys->gear>=0&&GetButtonInput(kInputBrakeButton)||(gAxisThrottleBrake&&gThrottleAxis<0))
{
phys->lastGear=phys->gear;
phys->gear=-1;
phys->lastGearSwitch=gFrameCount*kFrameTime;
}
}
}
}
else
{
if(GetButtonInput(kInputGearUp)){
if(phys->gear<car->numGears-2&&!gGearUpPress){
phys->lastGear=phys->gear;
phys->gear++;
gGearUpPress=true;
phys->lastGearSwitch=gFrameCount*kFrameTime;
if(gGameInfo->arcade==kGameModeTurbo)
phys->lastGearSwitch-=car->gearSwitchTime*0.5;
}
}
else gGearUpPress=false;
if(GetButtonInput(kInputGearDown)){
if(phys->gear>-1&&!gGearDownPress){
phys->lastGear=phys->gear;
phys->gear--;
gGearDownPress=true;
phys->lastGearSwitch=gFrameCount*kFrameTime;
if(gGameInfo->arcade==kGameModeTurbo)
phys->lastGearSwitch-=car->gearSwitchTime*0.5;
}
}
else gGearDownPress=false;
}
float gearSwitchTime=phys->gear<=1?0.3:car->gearSwitchTime;
if(gFrameCount*kFrameTime<phys->lastGearSwitch+gearSwitchTime)
{
float switchTime=(gFrameCount*kFrameTime-phys->lastGearSwitch)/gearSwitchTime;
phys->clutch=switchTime;
if(phys->rpm<car->clutchRPM)
phys->clutch=(phys->rpm-car->idleRPM*1.5)/(car->clutchRPM-car->idleRPM*1.5);
if(switchTime<0.5&&phys->gear>1)
if(phys->lastGear>phys->gear)
phys->throttle=0.7;
else
phys->throttle=phys->idleThrottle;
}
else
if(phys->rpm<car->clutchRPM)
phys->clutch=(phys->rpm-car->idleRPM*1.5)/(car->clutchRPM-car->idleRPM*1.5);
else
phys->clutch=1;
if(phys->clutch<0)
phys->clutch=0;
}
if(GetButtonInput(kInputHorn))
phys->lightFlags|=kLightFlagHorn;
else
phys->lightFlags&=~kLightFlagHorn;
if(GetButtonInput(kInputLeftIndicator))
phys->lightFlags|=kLightFlagLIndLight;
else if(GetButtonInput(kInputRightIndicator))
phys->lightFlags|=kLightFlagRIndLight;
else
phys->lightFlags&=~(kLightFlagLIndLight+kLightFlagRIndLight);
if(GetButtonInput(kInputLightButton)){
if(!gLightPress)
{
gLightPress=true;
phys->lightFlags^=kLightFlagDriveLight;
}
}else gLightPress=false;
}
void ControlEntityAIInput(tGameEntity *entity);
extern float gEscDisplay;
void HandleEscModeInput()
{
if(GetInterfaceKey(kInterfaceKeyEsc)||GetInterfaceKey(kInterfaceKeyDelete))
{
if(!gEscPress)
{
gInputEscMode=kInputEscModeNone;
gEscPress=true;
if(!gGameInfo->network)
UnPauseGame();
}
}
else
gEscPress=false;
if(GetInterfaceKey(kInterfaceKeyDown))
{
if(!gArrowPress)
{
gInputEscSelection--;
if(gInputEscSelection<0)
gInputEscSelection=(gGameInfo->network?1:2);
gArrowPress=true;
}
}
else if(GetInterfaceKey(kInterfaceKeyUp))
{
if(!gArrowPress)
{
gInputEscSelection=(gInputEscSelection+1)%(gGameInfo->network?2:3);
gArrowPress=true;
}
}
else
gArrowPress=false;
if(GetInterfaceKey(kInterfaceKeyReturn)||GetInterfaceKey(kInterfaceKeyEnter))
{
if(gInputEscSelection==1)
if(gInputEscMode==kInputEscModeQuit)
Exit();
else
gGameEnd=kEndGame;
if(gInputEscSelection==2)
gGameEnd=kEndGameRestart;
if(gInputEscMode==kInputEscModeQuit)
gEscDisplay=0;
gInputEscMode=kInputEscModeNone;
if(!gGameInfo->network)
UnPauseGame();
}
}
void HandleChatModeInput()
{
while(char ch=GetKeyInput(NULL))
{
if(ch=='\r')
{
if(ch=='\r'&&strlen(gGameChatMessage)>0)
{
tChatMessage msg;
memset((void*)&msg,0,sizeof(tChatMessage));
sprintf(msg.str,"%s: \255#a\255%s",gConfig->playerName,gGameChatMessage);
msg.flags=0;
NetworkSendPacket(kMessageTypeChat,&msg,sizeof(tChatMessage),kMessagePriorityHigh,kMessageSendToAll);
}
gGameChatMessage[0]='\0';
gGameChatMessageSize=0;
gInputChatMode=false;
}
if((ch==0x7f||ch==0x08)&&gGameChatMessageSize>0)
gGameChatMessage[--gGameChatMessageSize]='\0';
if(ch>=' '&&ch<='z'&&gGameChatMessageSize<kMaxGameChatMessageSize-1)
{
gGameChatMessage[gGameChatMessageSize++]=ch;
gGameChatMessage[gGameChatMessageSize]='\0';
}
}
}
void HandleNetworkInput()
{
if(GetButtonInput(kInputChat))
{
gInputChatMode=true;
FlushKeys();
}
int tauntPress=false;
for(int i=kInputTaunt1;i<=kInputTaunt5;i++)
if(GetButtonInput(i))
{
tauntPress=true;
if(!gTauntPress&&*gConfig->taunts[i-kInputTaunt1])
{
tChatMessage msg;
memset((void*)&msg,0,sizeof(tChatMessage));
snprintf(msg.str,256,"%s: \255#a\255%s",gConfig->playerName,gConfig->taunts[i-kInputTaunt1]);
msg.flags=0;
NetworkSendPacket(kMessageTypeChat,&msg,sizeof(tChatMessage),kMessagePriorityHigh,kMessageSendToAll);
}
}
gTauntPress=tauntPress;
if(gInputChatMode)
HandleChatModeInput();
}
void HandleGameInput()
{
tCarPhysics *phys=(tCarPhysics*)gCarEntities[gGameInfo->playerID]->physics;
if(GetButtonInput(kInputCamera))
{
if(!gCameraPress)
{
ParticlesClearScreen();
gCameraMode=(++gCameraMode)%(gConfig->allCams||gReplay||phys->lapCount>gGameInfo->numLaps&&gGameInfo->numLaps!=-1?kCameraNumModes:kCameraLeftWheel);
gReplayAutoCam=false;
}
gCameraPress++;
if(gCameraPress>=kFPS&&gCameraMode!=kCameraFree)
{
gCameraMode=kCameraFree;
TextPrintfToBufferFormatedFading(Vector(0,1),0.03,kTextAlignMiddle,1,2,"freecam mode.");
}
}
else gCameraPress=0;
if(GetButtonInput(kInputCameraChangeCar)){
if(!gReplayCameraPress)
{
if(gReplay||phys->lapCount>gGameInfo->numLaps)
{
do{
gReplayViewedEntityID=(gReplayViewedEntityID+1)%gGameInfo->numPlayers;
}while(gGameInfo->netID[gReplayViewedEntityID]==-1&&gGameInfo->network);
if(gReplay)
gReplayAutoCam=false;
gReplayCameraPress=true;
}
}
}else
gReplayCameraPress=false;
if(gReplay&&!gGameInfo->network)
{
float oldStretch=gTimeStretch;
if(GetButtonInput(kInputChat))
gTimeStretch=0.25;
else
gTimeStretch=1.0;
if(gTimeStretch!=oldStretch)
{
float curTime=gStartTime+(gFrameCount*kFrameTime)/oldStretch;
gStartTime=curTime-(gFrameCount*kFrameTime)/gTimeStretch;
}
}
gCameraReverse=GetButtonInput(kInputCameraReverse);
if(GetButtonInput(kInputPause)&&!gReplay&&!gPaused&&!gPausePress)
{
if(!gGameInfo->network)
PauseGame();
else
{
int pauseFrameCount=gFrameCount+kFPS*0.5;
S32Swap(pauseFrameCount);
NetworkSendPacket(kMessageTypePause,&pauseFrameCount,sizeof(int),kMessagePriorityHigh,kMessageSendToAll);
}
gPausePress=true;
}
if(((GetButtonInput(kInputPause)&&!gPausePress))&&gPaused&&!gInputChatMode)
{
if(!gGameInfo->network)
UnPauseGame();
else
NetworkSendPacket(kMessageTypeResume,NULL,0,kMessagePriorityHigh,kMessageSendToAll);
gPausePress=true;
}
if(!GetButtonInput(kInputPause))
gPausePress=false;
if(gGameInfo->network)
HandleNetworkInput();
/* if(GetInterfaceKey(kInterfaceKeyCmd)&&GetInterfaceKey(kInterfaceKeyQ))
{
gInputEscMode=kInputEscModeQuit;
gInputEscSelection=1;
if(!gGameInfo->network)
PauseGame();
}*/
if(GetInterfaceKey(kInterfaceKeyEsc))//||(GetInterfaceKey(kInterfaceKeyDelete)&&!gInputChatMode))
{
if(gInputChatMode)
{
gGameChatMessage[0]='\0';
gGameChatMessageSize=0;
gInputChatMode=false;
gEscPress=true;
}
else if(!gEscPress)
// if(!gGameInfo->network&&((phys->lapCount>gGameInfo->numLaps&&gGameInfo->numLaps!=-1)||(gFrameCount*kFrameTime-kStartGameDelaySeconds>gGameInfo->maxTime&&gGameInfo->maxTime>0)))
if((gReplay||((phys->lapCount>gGameInfo->numLaps&&gGameInfo->numLaps!=-1)))&&(!gGameInfo->network||gGameInfo->playerID==0))//||(gFrameCount*kFrameTime-kStartGameDelaySeconds>gGameInfo->maxTime&&gGameInfo->maxTime>0)))
gGameEnd=kEndGame;
else
{
gEscPress=true;
gInputEscMode=kInputEscModeNormal;
gInputEscSelection=1;
if(!gGameInfo->network)
PauseGame();
}
}
else
gEscPress=false;
}
void ControlFrame()
{
if(!gPaused&&!gReplay)
{
tGameEntity *entity=(tGameEntity*)gFirstEntity->next;
while(entity!=gFirstEntity)
{
if(entity->physicsMachine==kPhysicsLocal)
{
switch(entity->controlType)
{
case kControlTypeUserInput:
ControlEntityUserInput(entity);
break;
case kControlTypeAIInput:
ControlEntityAIInput(entity);
break;
}
}
entity=(tGameEntity*)entity->next;
}
}
if(!gInputEscMode)
HandleGameInput();
else
HandleEscModeInput();
}