Supermodel/Src/Inputs/Input.cpp
Nik Henson 8835d6fe36 Some updates to Supermodel made at beginning of the year but only now got around to checking in (better late than never...):
- hooked up the remaining controls in Supermodel (except for Magical Truck Adventure which does not work at all yet).  The new controls are:
  * InputAnalogJoyTrigger2 and InputAnalogJoyEvent2 for the additional second trigger and event buttons that were missing from Star Wars Trilogy,
  * InputRearBrake and InputMusicSelect for the rear brake and music selection buttons that were missing from Harley Davidson,
  * InputAnalogGunXXX, InputAnalogTriggerXXX, InputAnalogGunXXX2 and InputAnalogTriggerXXX2 for the analogue guns of Ocean Hunter and LA Machineguns (NOTE: these controls must be calibrated in the games' service menus otherwise they will not work properly.  Also, the alignment of the gun cursor does not line up very well with the mouse position at the moment, but at least the games are a bit more playable now, although still with numerous graphical glitches...)
  * InputSkiXXX for the controls of Ski Champ, making the game playable now.
- hooked up existing InputViewChange control to Harley Davidson's view change button
- improved the handling of InputGearShiftUp/Down inputs so that they work better with the driving games.  With Dirt Devils, ECA, Harley and LeMans this means they map directly to the game's own shift up/down controls, while with the 4-speed games such as Daytona 2, Scud Racer and Sega Rally 2, they simulate the user shifting up and down through the gears
- added defaults for the new controls to Supermodel.ini
- other small code tweaks:
  * fix small bug with handling of pos/neg inputs mapping to a control with inverted range (0XFF to 0x00) - this was needed to get Ski Champ's X-axis to work properly
  * removed Wait method from InputSystem and added to CThread as CThread::Sleep instead
  * added FrameTimings struct to hold all frame timings in a single place
No networking code yet as just haven't had a chance to work on it since initial progress at the beginning of the year - am *hoping* might have some time to pick it up again over Christmas...
2013-11-30 19:39:59 +00:00

206 lines
6 KiB
C++

/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel 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 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Input.cpp
*
* Implementation of CInput, the base input class. Input types are derived
* from this.
*/
#include "Supermodel.h"
CInput::CInput(const char *inputId, const char *inputLabel, unsigned inputFlags, unsigned inputGameFlags, const char *defaultMapping, UINT16 initValue) :
id(inputId), label(inputLabel), flags(inputFlags), gameFlags(inputGameFlags), m_defaultMapping(defaultMapping), value(initValue), prevValue(initValue),
m_system(NULL), m_source(NULL)
{
ResetToDefaultMapping();
}
CInput::~CInput()
{
// Release source, if any
if (m_source != NULL)
m_source->Release();
}
void CInput::CreateSource()
{
// If already have a source, then release it now
if (m_source != NULL)
m_source->Release();
// If no system set yet or mapping is empty or NONE, then set source to NULL
if (m_system == NULL || m_mapping[0] == '\0' || stricmp(m_mapping, "NONE") == 0)
m_source = NULL;
else
{
// Otherwise, ask system to parse mapping into appropriate input source
m_source = m_system->ParseSource(m_mapping, !!(flags & INPUT_FLAGS_AXIS));
// Check that mapping was parsed okay and if so acquire it
if (m_source != NULL)
m_source->Acquire();
else
{
// Otherwise, fall back to default mapping
if (stricmp(m_mapping, m_defaultMapping) != 0)
{
ErrorLog("Unable to map input %s to [%s] - switching to default [%s].\n", id, m_mapping, m_defaultMapping);
ResetToDefaultMapping();
}
}
}
}
void CInput::Initialize(CInputSystem *system)
{
m_system = system;
CreateSource();
}
const char* CInput::GetInputGroup()
{
switch (gameFlags)
{
case GAME_INPUT_UI: return "User Interface Controls";
case GAME_INPUT_COMMON: return "Common Controls";
case GAME_INPUT_JOYSTICK1: // Fall through to below
case GAME_INPUT_JOYSTICK2: return "4-Way Joysticks";
case GAME_INPUT_FIGHTING: return "Fighting Game Buttons";
case GAME_INPUT_SPIKEOUT: return "Spikeout Buttons";
case GAME_INPUT_SOCCER: return "Virtua Striker Buttons";
case GAME_INPUT_VEHICLE: return "Racing Game Steering Controls";
case GAME_INPUT_SHIFT4: return "Racing Game Gear 4-Way Shift";
case GAME_INPUT_SHIFTUPDOWN: return "Racing Game Gear Up/Down Shift";
case GAME_INPUT_VR4: return "Racing Game 4 VR View Buttons";
case GAME_INPUT_VIEWCHANGE: return "Racing Game View Change";
case GAME_INPUT_HANDBRAKE: return "Racing Game Handbrake";
case GAME_INPUT_HARLEY: return "Harley Davidson Controls";
case GAME_INPUT_TWIN_JOYSTICKS: return "Virtual On Controls";
case GAME_INPUT_ANALOG_JOYSTICK: return "Analog Joystick";
case GAME_INPUT_GUN1: // Fall through to below
case GAME_INPUT_GUN2: return "Light Guns";
case GAME_INPUT_ANALOG_GUN1: // Fall through to below
case GAME_INPUT_ANALOG_GUN2: return "Analog Guns";
case GAME_INPUT_SKI: return "Ski Controls";
default: return "Misc";
}
}
const char *CInput::GetMapping()
{
return m_mapping;
}
void CInput::ClearMapping()
{
SetMapping("NONE");
}
void CInput::SetMapping(const char *mapping)
{
strncpy(m_mapping, mapping, MAX_MAPPING_LENGTH);
m_mapping[MAX_MAPPING_LENGTH] = '\0';
CreateSource();
}
void CInput::AppendMapping(const char *mapping)
{
// If mapping is empty or NONE, then simply set mapping
if (m_mapping[0] == '\0' || stricmp(m_mapping, "NONE") == 0)
SetMapping(mapping);
else
{
// Otherwise, append to mapping string and recreate source from new mapping string
int size = MAX_MAPPING_LENGTH - strlen(m_mapping);
strncat(m_mapping, ",", size--);
strncat(m_mapping, mapping, size);
CreateSource();
}
}
void CInput::ResetToDefaultMapping()
{
SetMapping(m_defaultMapping);
}
void CInput::ReadFromINIFile(CINIFile *ini, const char *section)
{
// See if input is configurable
if (IsConfigurable())
{
// If so, check INI file for mapping string
string key("Input");
key.append(id);
string mapping;
if (ini->Get(section, key, mapping) == OKAY)
{
// If found, then set mapping string
SetMapping(mapping.c_str());
return;
}
}
// If input has not been configured, then force recreation of source anyway since input system settings may have changed
CreateSource();
}
void CInput::WriteToINIFile(CINIFile *ini, const char *section)
{
if (!IsConfigurable())
return;
string key("Input");
key.append(id);
ini->Set(section, key, m_mapping);
}
void CInput::InputSystemChanged()
{
// If input system or its settings have changed, then force recreation of source
CreateSource();
}
bool CInput::Configure(bool append, const char *escapeMapping)
{
char mapping[MAX_MAPPING_LENGTH];
if (!m_system->ReadMapping(mapping, MAX_MAPPING_LENGTH, !!(flags & INPUT_FLAGS_AXIS), READ_ALL, escapeMapping))
return false;
if (append)
AppendMapping(mapping);
else
SetMapping(mapping);
return true;
}
bool CInput::Changed()
{
return value != prevValue;
}
bool CInput::SendForceFeedbackCmd(ForceFeedbackCmd ffCmd)
{
if (m_source == NULL)
return false;
return m_source->SendForceFeedbackCmd(ffCmd);
}