dolphin/Source/Core/InputCommon/Src/ControllerEmu.h
Lioncash 8a9fcd3014 Kill off dangling else's in the InputCommon project.
Some indentations were also too far for some things. Fixed this.

Also update the license header to show Git instead of SVN.

Got rid of some trailing spaces/tabs too.
2013-04-14 22:53:10 -04:00

460 lines
11 KiB
C++

// Copyright (C) 2010 Dolphin Project.
// 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, version 2.0.
// This program 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official Git repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#ifndef _CONTROLLEREMU_H_
#define _CONTROLLEREMU_H_
// windows crap
#define NOMINMAX
#include <cmath>
#include <vector>
#include <string>
#include <algorithm>
#include "GCPadStatus.h"
#include "ControllerInterface/ControllerInterface.h"
#include "IniFile.h"
#define sign(x) ((x)?(x)<0?-1:1:0)
enum
{
GROUP_TYPE_OTHER,
GROUP_TYPE_STICK,
GROUP_TYPE_MIXED_TRIGGERS,
GROUP_TYPE_BUTTONS,
GROUP_TYPE_FORCE,
GROUP_TYPE_EXTENSION,
GROUP_TYPE_TILT,
GROUP_TYPE_CURSOR,
GROUP_TYPE_TRIGGERS,
GROUP_TYPE_UDPWII,
GROUP_TYPE_SLIDER,
};
const char * const named_directions[] =
{
"Up",
"Down",
"Left",
"Right"
};
class ControllerEmu
{
public:
class ControlGroup
{
public:
class Control
{
protected:
Control(ControllerInterface::ControlReference* const _ref, const char * const _name)
: control_ref(_ref), name(_name){}
public:
virtual ~Control();
ControllerInterface::ControlReference* const control_ref;
const char * const name;
};
class Input : public Control
{
public:
Input(const char * const _name)
: Control(new ControllerInterface::InputReference, _name) {}
};
class Output : public Control
{
public:
Output(const char * const _name)
: Control(new ControllerInterface::OutputReference, _name) {}
};
class Setting
{
public:
Setting(const char* const _name, const ControlState def_value
, const unsigned int _low = 0, const unsigned int _high = 100 )
: name(_name)
, value(def_value)
, default_value(def_value)
, low(_low)
, high(_high){}
const char* const name;
ControlState value;
const ControlState default_value;
const unsigned int low, high;
};
ControlGroup(const char* const _name, const unsigned int _type = GROUP_TYPE_OTHER) : name(_name), type(_type) {}
virtual ~ControlGroup();
virtual void LoadConfig(IniFile::Section *sec, const std::string& defdev = "", const std::string& base = "" );
virtual void SaveConfig(IniFile::Section *sec, const std::string& defdev = "", const std::string& base = "" );
const char* const name;
const unsigned int type;
std::vector< Control* > controls;
std::vector< Setting* > settings;
};
class AnalogStick : public ControlGroup
{
public:
template <typename C>
void GetState(C* const x, C* const y, const unsigned int base, const unsigned int range)
{
// this is all a mess
ControlState yy = controls[0]->control_ref->State() - controls[1]->control_ref->State();
ControlState xx = controls[3]->control_ref->State() - controls[2]->control_ref->State();
ControlState deadzone = settings[0]->value;
ControlState square = settings[1]->value;
ControlState m = controls[4]->control_ref->State();
// modifier code
if (m)
{
yy = (fabsf(yy)>deadzone) * sign(yy) * (m + deadzone/2);
xx = (fabsf(xx)>deadzone) * sign(xx) * (m + deadzone/2);
}
// deadzone / square stick code
if (deadzone || square)
{
// this section might be all wrong, but its working good enough, i think
ControlState ang = atan2(yy, xx);
ControlState ang_sin = sin(ang);
ControlState ang_cos = cos(ang);
// the amt a full square stick would have at current angle
ControlState square_full = std::min(ang_sin ? 1/fabsf(ang_sin) : 2, ang_cos ? 1/fabsf(ang_cos) : 2);
// the amt a full stick would have that was ( user setting squareness) at current angle
// i think this is more like a pointed circle rather than a rounded square like it should be
ControlState stick_full = (1 + (square_full - 1) * square);
ControlState dist = sqrt(xx*xx + yy*yy);
// dead zone code
dist = std::max(0.0f, dist - deadzone * stick_full);
dist /= (1 - deadzone);
// square stick code
ControlState amt = dist / stick_full;
dist -= ((square_full - 1) * amt * square);
yy = std::max(-1.0f, std::min(1.0f, ang_sin * dist));
xx = std::max(-1.0f, std::min(1.0f, ang_cos * dist));
}
*y = C(yy * range + base);
*x = C(xx * range + base);
}
AnalogStick(const char* const _name);
};
class Buttons : public ControlGroup
{
public:
Buttons(const char* const _name);
template <typename C>
void GetState(C* const buttons, const C* bitmasks)
{
std::vector<Control*>::iterator
i = controls.begin(),
e = controls.end();
for (; i!=e; ++i, ++bitmasks)
{
if ((*i)->control_ref->State() > settings[0]->value) // threshold
*buttons |= *bitmasks;
}
}
};
class MixedTriggers : public ControlGroup
{
public:
template <typename C, typename S>
void GetState(C* const digital, const C* bitmasks, S* analog, const unsigned int range)
{
const unsigned int trig_count = ((unsigned int) (controls.size() / 2));
for (unsigned int i=0; i<trig_count; ++i,++bitmasks,++analog)
{
if (controls[i]->control_ref->State() > settings[0]->value) //threshold
{
*analog = range;
*digital |= *bitmasks;
}
else
{
*analog = S(controls[i+trig_count]->control_ref->State() * range);
}
}
}
MixedTriggers(const char* const _name);
};
class Triggers : public ControlGroup
{
public:
template <typename S>
void GetState(S* analog, const unsigned int range)
{
const unsigned int trig_count = ((unsigned int) (controls.size()));
const ControlState deadzone = settings[0]->value;
for (unsigned int i=0; i<trig_count; ++i,++analog)
*analog = S(std::max(controls[i]->control_ref->State() - deadzone, 0.0f) / (1 - deadzone) * range);
}
Triggers(const char* const _name);
};
class Slider : public ControlGroup
{
public:
template <typename S>
void GetState(S* const slider, const unsigned int range, const unsigned int base = 0)
{
const float deadzone = settings[0]->value;
const float state = controls[1]->control_ref->State() - controls[0]->control_ref->State();
if (fabsf(state) > deadzone)
*slider = (S)((state - (deadzone * sign(state))) / (1 - deadzone) * range + base);
else
*slider = 0;
}
Slider(const char* const _name);
};
class Force : public ControlGroup
{
public:
Force(const char* const _name);
template <typename C, typename R>
void GetState(C* axis, const u8 base, const R range)
{
const float deadzone = settings[0]->value;
for (unsigned int i=0; i<6; i+=2)
{
float tmpf = 0;
const float state = controls[i+1]->control_ref->State() - controls[i]->control_ref->State();
if (fabsf(state) > deadzone)
tmpf = ((state - (deadzone * sign(state))) / (1 - deadzone));
float &ax = m_swing[i >> 1];
*axis++ = (C)((tmpf - ax) * range + base);
ax = tmpf;
}
}
private:
float m_swing[3];
};
class Tilt : public ControlGroup
{
public:
Tilt(const char* const _name);
template <typename C, typename R>
void GetState(C* const x, C* const y, const unsigned int base, const R range, const bool step = true)
{
// this is all a mess
ControlState yy = controls[0]->control_ref->State() - controls[1]->control_ref->State();
ControlState xx = controls[3]->control_ref->State() - controls[2]->control_ref->State();
ControlState deadzone = settings[0]->value;
ControlState circle = settings[1]->value;
auto const angle = settings[2]->value / 1.8f;
ControlState m = controls[4]->control_ref->State();
// modifier code
if (m)
{
yy = (fabsf(yy)>deadzone) * sign(yy) * (m + deadzone/2);
xx = (fabsf(xx)>deadzone) * sign(xx) * (m + deadzone/2);
}
// deadzone / circle stick code
if (deadzone || circle)
{
// this section might be all wrong, but its working good enough, i think
ControlState ang = atan2(yy, xx);
ControlState ang_sin = sin(ang);
ControlState ang_cos = cos(ang);
// the amt a full square stick would have at current angle
ControlState square_full = std::min(ang_sin ? 1/fabsf(ang_sin) : 2, ang_cos ? 1/fabsf(ang_cos) : 2);
// the amt a full stick would have that was (user setting circular) at current angle
// i think this is more like a pointed circle rather than a rounded square like it should be
ControlState stick_full = (square_full * (1 - circle)) + (circle);
ControlState dist = sqrt(xx*xx + yy*yy);
// dead zone code
dist = std::max(0.0f, dist - deadzone * stick_full);
dist /= (1 - deadzone);
// circle stick code
ControlState amt = dist / stick_full;
dist += (square_full - 1) * amt * circle;
yy = std::max(-1.0f, std::min(1.0f, ang_sin * dist));
xx = std::max(-1.0f, std::min(1.0f, ang_cos * dist));
}
// this is kinda silly here
// gui being open will make this happen 2x as fast, o well
// silly
if (step)
{
if (xx > m_tilt[0])
m_tilt[0] = std::min(m_tilt[0] + 0.1f, xx);
else if (xx < m_tilt[0])
m_tilt[0] = std::max(m_tilt[0] - 0.1f, xx);
if (yy > m_tilt[1])
m_tilt[1] = std::min(m_tilt[1] + 0.1f, yy);
else if (yy < m_tilt[1])
m_tilt[1] = std::max(m_tilt[1] - 0.1f, yy);
}
*y = C(m_tilt[1] * angle * range + base);
*x = C(m_tilt[0] * angle * range + base);
}
private:
float m_tilt[2];
};
class Cursor : public ControlGroup
{
public:
Cursor(const char* const _name);
template <typename C>
void GetState(C* const x, C* const y, C* const z, const bool adjusted = false)
{
const float zz = controls[4]->control_ref->State() - controls[5]->control_ref->State();
// silly being here
if (zz > m_z)
m_z = std::min(m_z + 0.1f, zz);
else if (zz < m_z)
m_z = std::max(m_z - 0.1f, zz);
*z = m_z;
// hide
if (controls[6]->control_ref->State() > 0.5f)
{
*x = 10000; *y = 0;
}
else
{
float yy = controls[0]->control_ref->State() - controls[1]->control_ref->State();
float xx = controls[3]->control_ref->State() - controls[2]->control_ref->State();
// adjust cursor according to settings
if (adjusted)
{
xx *= (settings[1]->value * 2);
yy *= (settings[2]->value * 2);
yy += (settings[0]->value - 0.5f);
}
*x = xx;
*y = yy;
}
}
float m_z;
};
class Extension : public ControlGroup
{
public:
Extension(const char* const _name)
: ControlGroup(_name, GROUP_TYPE_EXTENSION)
, switch_extension(0)
, active_extension(0) {}
~Extension();
void GetState(u8* const data, const bool focus = true);
std::vector<ControllerEmu*> attachments;
int switch_extension;
int active_extension;
};
virtual ~ControllerEmu();
virtual std::string GetName() const = 0;
virtual void LoadDefaults(const ControllerInterface& ciface);
virtual void LoadConfig(IniFile::Section *sec, const std::string& base = "");
virtual void SaveConfig(IniFile::Section *sec, const std::string& base = "");
void UpdateDefaultDevice();
void UpdateReferences(ControllerInterface& devi);
std::vector< ControlGroup* > groups;
ControllerInterface::DeviceQualifier default_device;
};
#endif