1 /************************************************************************************
2 
3 	AstroMenace
4 	Hardcore 3D space scroll-shooter with spaceship upgrade possibilities.
5 	Copyright (c) 2006-2019 Mikhail Kurinnoi, Viewizard
6 
7 
8 	AstroMenace is free software: you can redistribute it and/or modify
9 	it under the terms of the GNU General Public License as published by
10 	the Free Software Foundation, either version 3 of the License, or
11 	(at your option) any later version.
12 
13 	AstroMenace is distributed in the hope that it will be useful,
14 	but WITHOUT ANY WARRANTY; without even the implied warranty of
15 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 	GNU General Public License for more details.
17 
18 	You should have received a copy of the GNU General Public License
19 	along with AstroMenace. If not, see <https://www.gnu.org/licenses/>.
20 
21 
22 	Website: https://viewizard.com/
23 	Project: https://github.com/viewizard/astromenace
24 	E-mail: viewizard@viewizard.com
25 
26 *************************************************************************************/
27 
28 // TODO move to SDL_GetTicks() usage
29 
30 // TODO add support for SDL_INIT_HAPTIC (force feedback)
31 
32 // NOTE SDL2 also provide SDL_INIT_GAMECONTROLLER now
33 
34 /*
35 At this time we don't provide menu options to choose joystick, first one (with index 0) will be used by default.
36 Which Joystick should be used, could be configured via config file, "JoystickNum" parameter.
37 */
38 
39 #include "../core/core.h"
40 #include "../config/config.h"
41 #include "platform.h"
42 
43 // NOTE switch to nested namespace definition (namespace A::B::C { ... }) (since C++17)
44 namespace viewizard {
45 namespace astromenace {
46 
47 namespace {
48 
49 SDL_Joystick *Joystick{nullptr};
50 int JoystickAxisX{0};
51 int JoystickAxisY{0};
52 int JoystickButtonsQuantity{0};
53 std::vector<bool> JoystickButtons{};
54 
55 float JoystickCurrentTime{0.0f};
56 float JoystickTimeDelta{0.0f};
57 
58 } // unnamed namespace
59 
60 
61 /*
62  * Joystick (re)initialization.
63  */
JoystickInit(float InitialTime)64 bool JoystickInit(float InitialTime)
65 {
66 	JoystickClose(); // since we could reinit joystick, close it first
67 
68 	if (SDL_NumJoysticks() <= 0)
69 		return false;
70 
71 	std::cout << "Found Joystick(s):\n";
72 	for (int i = 0; i < SDL_NumJoysticks(); i++) {
73 		std::cout << "Joystick Name " << i << ": " << SDL_JoystickNameForIndex(i) << "\n";
74 	}
75 
76 	if (GameConfig().JoystickNum >= SDL_NumJoysticks())
77 		ChangeGameConfig().JoystickNum = 0;
78 
79 	Joystick = SDL_JoystickOpen(GameConfig().JoystickNum);
80 
81 	if (Joystick) {
82 		std::cout << "Opened Joystick " << GameConfig().JoystickNum << "\n"
83 			  << "Joystick Name: " << SDL_JoystickNameForIndex(GameConfig().JoystickNum) << "\n"
84 			  << "Joystick Number of Axes: " << SDL_JoystickNumAxes(Joystick) << "\n"
85 			  << "Joystick Number of Buttons: " << SDL_JoystickNumButtons(Joystick) << "\n"
86 			  << "Joystick Number of Balls: " << SDL_JoystickNumBalls(Joystick) << "\n\n";
87 	} else {
88 		std::cerr << __func__ << "(): " << "SDL_JoystickOpen() failed: " << SDL_GetError() << "\n";
89 		std::cout << "Couldn't open Joystick " << GameConfig().JoystickNum << "\n\n";
90 		return false;
91 	}
92 
93 	JoystickCurrentTime = InitialTime;
94 	JoystickTimeDelta = 0.0f;
95 
96 	JoystickAxisX = SDL_JoystickGetAxis(Joystick, 0);
97 	JoystickAxisY = SDL_JoystickGetAxis(Joystick, 1);
98 	JoystickButtonsQuantity = SDL_JoystickNumButtons(Joystick);
99 	JoystickButtons.resize(JoystickButtonsQuantity, false);
100 
101 	return true;
102 }
103 
104 /*
105  * Close joystick.
106  */
JoystickClose()107 void JoystickClose()
108 {
109 	if (!Joystick)
110 		return;
111 
112 	if (SDL_JoystickGetAttached(Joystick))
113 		SDL_JoystickClose(Joystick);
114 
115 	Joystick = nullptr;
116 	JoystickButtons.clear();
117 }
118 
119 /*
120  * Check current joystick status (opened or not).
121  */
isJoystickAvailable()122 bool isJoystickAvailable()
123 {
124 	return Joystick;
125 }
126 
127 /*
128  * Get current opened joystick buttons quantity.
129  */
GetJoystickButtonsQuantity()130 int GetJoystickButtonsQuantity()
131 {
132 	if (!Joystick)
133 		return 0;
134 
135 	return JoystickButtonsQuantity;
136 }
137 
138 /*
139  * Set joystick button status.
140  */
SetJoystickButton(int ButtonNumber,bool ButtonStatus)141 void SetJoystickButton(int ButtonNumber, bool ButtonStatus)
142 {
143 	if (!Joystick ||
144 	    // we may have config that was made previously with another joystick
145 	    // don't change game's config, just ignore this buttons check
146 	    (ButtonNumber < 0) ||
147 	    (ButtonNumber >= JoystickButtonsQuantity))
148 		return;
149 
150 	JoystickButtons[ButtonNumber] = ButtonStatus;
151 }
152 
153 /*
154  * Get joystick button status.
155  */
GetJoystickButton(int ButtonNumber)156 bool GetJoystickButton(int ButtonNumber)
157 {
158 	if (!Joystick ||
159 	    // we may have config that was made previously with another joystick
160 	    // don't change game's config, just ignore this buttons check
161 	    (ButtonNumber < 0) ||
162 	    (ButtonNumber >= JoystickButtonsQuantity))
163 		return false;
164 
165 	return JoystickButtons[ButtonNumber];
166 }
167 
168 /*
169  * Emulate mouse movements.
170  */
JoystickEmulateMouseMovement(float Time)171 void JoystickEmulateMouseMovement(float Time)
172 {
173 	if (!Joystick)
174 		return;
175 
176 	JoystickTimeDelta = Time - JoystickCurrentTime;
177 	JoystickCurrentTime = Time;
178 
179 	int X = SDL_JoystickGetAxis(Joystick, 0);
180 	int Y = SDL_JoystickGetAxis(Joystick, 1);
181 
182 	// JoystickDeadZone: [0, 10]
183 	// min/max joystick axis value: -32768/32767
184 	// we are using 3000 here, since dead zone should not be same small/big as min/max joystick axis values
185 	if (abs(X) < (GameConfig().JoystickDeadZone * 3000))
186 		X = 0;
187 	if (abs(Y) < (GameConfig().JoystickDeadZone * 3000))
188 		Y = 0;
189 
190 	if ((JoystickAxisX != X) || (JoystickAxisY != Y)) {
191 		JoystickAxisX = 0;
192 		JoystickAxisY = 0;
193 
194 		// InternalWidth here, since during one second we should move cursor from left to right
195 		int Xsm{static_cast<int>(GameConfig().InternalWidth * (X / 32768.0f) * JoystickTimeDelta)};
196 		int Ysm{static_cast<int>(GameConfig().InternalWidth * (Y / 32768.0f) * JoystickTimeDelta)};
197 
198 		vw_SetMousePosRel(Xsm, Ysm);
199 	}
200 }
201 
202 /*
203  * Provide joystick button's name - "ButtonN", where N is number.
204  */
JoystickButtonName(int ButtonNum)205 std::string JoystickButtonName(int ButtonNum)
206 {
207 	if (ButtonNum < 0)
208 		return "?";
209 
210 	// ButtonNum + 1, since buttons index start from 0
211 	return vw_GetText("Button") + std::to_string(ButtonNum + 1);
212 }
213 
214 } // astromenace namespace
215 } // viewizard namespace
216