1 #include "pch.h"
2 #include "common/Def_Str.h"
3 #include "common/Gui_Def.h"
4 #include "common/GuiCom.h"
5 #include "../vdrift/game.h"
6 #include "CGame.h"
7 #include "CGui.h"
8 #include <OgreRoot.h>
9 #include <MyGUI_Gui.h>
10 #include <MyGUI_Button.h>
11 #include <MyGUI_ImageBox.h>
12 #include <MyGUI_EditBox.h>
13 #include <MyGUI_TabControl.h>
14 #include <MyGUI_TabItem.h>
15 #include <MyGUI_ComboBox.h>
16 using namespace MyGUI;
17 using namespace Ogre;
18 
19 
GetKeyName(SDL_Keycode key,bool omit=false)20 std::string GetKeyName(SDL_Keycode key, bool omit = false)
21 {
22 	if (key == SDLK_UNKNOWN)
23 		return omit ? "" : TR("#{InputKeyUnassigned}");
24 	else
25 		return std::string(SDL_GetKeyName(key));
26 }
27 
28 
29 ///  Input caption  ---------------------
UpdateInputButton(Button * button,const InputAction & action,EBind bind)30 void CGui::UpdateInputButton(Button* button, const InputAction& action, EBind bind)
31 {
32 	std::string s, sAssign = TR("#FFA030#{InputAssignKey}");  // caption
33 
34 	SDL_Keycode decKey = action.mICS->getKeyBinding(action.mControl, ICS::Control::DECREASE);
35 	SDL_Keycode incKey = action.mICS->getKeyBinding(action.mControl, ICS::Control::INCREASE);
36 
37 	//  B_First:  "<Assign>"
38 	//  B_Second: "Key1, <Assign>"
39 	//  B_Done:   "Key1, Key2"
40 
41 	if (action.mType == InputAction::Axis)
42 	{
43 		if (bind == B_First)
44 			s = sAssign;
45 		else
46 		{	s += GetKeyName(decKey,true);
47 			if (!s.empty())  s += " , ";
48 			if (bind == B_Second)
49 				s += sAssign;
50 			else
51 				s += GetKeyName(incKey,true);
52 		}
53 	}else
54 	{	if (bind == B_First)
55 			s = sAssign;
56 		else
57 			s += GetKeyName(incKey, action.mType & InputAction::Axis);
58 	}
59 
60 	if (bind == B_Done)
61 	{
62 		for (int j=0; j < SDL_NumJoysticks(); ++j)
63 		{
64 			int axis = action.mICS->getJoystickAxisBinding(action.mControl, j, ICS::Control::INCREASE);
65 			if (axis != ICS::InputControlSystem::UNASSIGNED)
66 			{
67 				if (!s.empty())  s += " / ";
68 				s += "J"+toStr(j) + ".Axis " + toStr(axis);
69 			}
70 			int btn = action.mICS->getJoystickButtonBinding(action.mControl, j, ICS::Control::INCREASE);
71 			if (btn != ICS::InputControlSystem::UNASSIGNED)
72 			{
73 				if (!s.empty())  s += " / ";
74 				s += "J"+toStr(j) + ".Button " + toStr(btn);
75 			}
76 		}
77 	}
78 	if (s.empty())  s = TR("#{InputKeyUnassigned}");
79 	button->setCaption(s);
80 }
81 
82 
83 ///  Gui Init - Input tabs
84 //----------------------------------------------------------------------------------------------------------------------------------
CreateInputTab(int iTab,bool player,const std::vector<InputAction> & actions,ICS::InputControlSystem * ICS)85 void CGui::CreateInputTab(int iTab, bool player,
86 	const std::vector<InputAction>& actions, ICS::InputControlSystem* ICS)
87 {
88 	if (!tabInput)  return;
89 	TabItemPtr tabitem = tabInput->getItemAt(iTab);
90 	std::string sPlr = toStr(iTab);
91 
92 	//  dimensions
93 	const int sx = 150, sy = 24,  // button size
94 		//  columns positon x  txt,btn,bar,>
95 		x0 = 16, x1 = 160, x2 = 325, x3 = 464,
96 		yHdr0 = 12, yHdr = yHdr0+6,  // header start
97 		yAdd = 14,  // y add with new row
98 		s0 = x1-x0-5;  // descr size x
99 
100 	#define CreateText(x,y, w,h, name, text)  {  Txt txt =  \
101 		tabitem->createWidget<TextBox>("TextBox", x,y+2, w,h, Align::Default, name);  \
102 		gcom->setOrigPos(txt, "OptionsWnd");  \
103 		txt->setCaption(text);  }
104 
105 
106 	///  Headers  action, binding, value
107 	CreateText(x0,yHdr0, sx,sy, "hdrTxt1_"+sPlr, TR("#90B0F0#{InputHeaderTxt1}"));
108 	CreateText(x1,yHdr0, sx,sy, "hdrTxt2_"+sPlr, TR("#A0C0FF#{InputHeaderTxt2}"));
109 	if (player)  {
110 		CreateText(x2,yHdr0, sx,sy, "hdrTxt3_"+sPlr, TR("#90B0F0#{InputHeaderTxt3}"));
111 		CreateText(x3,yHdr0, sx,sy, "hdrTxt4_"+sPlr, TR("#80A0E0#{InputHeaderTxt4}"));  }
112 
113 	//  spacing for add y
114 	static std::map <std::string, int> yRow;
115 	int y = 2;
116 	if (yRow.empty())
117 	{	//  player
118 		yRow["Throttle"]=y;   y+=2;     yRow["Brake"]=y;       y+=2;
119 		yRow["Steering"]=y;   y+=2 +1;  yRow["HandBrake"]=y;   y+=2;
120 		yRow["Boost"]=y;      y+=2;     yRow["Flip"]=y;        y+=2;
121 		yRow["Rewind"]=y;     y+=2 +2;
122 		yRow["NextCamera"]=y; y+=2;     yRow["PrevCamera"]=y;  y+=2 +1;
123 		yRow["ShiftUp"]=y;    y+=2;     yRow["ShiftDown"]=y;   y+=2 +1;
124 		yRow["LastChk"]=y;    y+=2;
125 		//  general
126 		y = 2;
127 		yRow["ShowOptions"]=y; y+=2 +1;
128 		yRow["RestartGame"]=y; y+=2;    yRow["ResetGame"]=y;  y+=2 +1;
129 		yRow["Screenshot"]=y;  y+=2 +1;
130 		yRow["PrevTab"]=y;     y+=2;    yRow["NextTab"]=y;    y+=2 +2;
131 	}
132 
133 	///  Actions  ------------------------------------------------
134 	int i = 0;
135 	for (std::vector<InputAction>::const_iterator it = actions.begin(); it != actions.end(); ++it)
136 	{
137 		std::string name = it->mName;
138 		y = yHdr + yRow[name] * yAdd;
139 
140 		//  description label  ----------------
141 		Txt desc = tabitem->createWidget<TextBox>("TextBox",
142 			x0, y+5, s0, sy,  Align::Default);
143 		gcom->setOrigPos(desc, "OptionsWnd");
144 		desc->setCaption( TR("#{InputMap" + name + "}") );
145 		desc->setTextColour( !player ?
146 			(i==0||i==3||i==4 ? Colour(0.86f,0.93f,1.f) : Colour(0.6f,0.8f,0.95f)) :  // general
147 
148 			i==11 ? Colour(0.8f,0.6f,1.f) : // rewind
149 			i==10 ? Colour(0.55f,0.5f,0.6f) : // last chk-
150 			(i==4||i==5) ? Colour(0.4f,1.f,1.f) : // boost,flip
151 			(i==6||i==7) ? Colour(0.7f,0.7f,0.7f) : // gear
152 			(i>=8 ? Colour(0.6f,0.75f,1.f) : // camera
153 			Colour(0.75f,0.88f,1.f)) );  // car steer
154 		//desc->setTextShadow(true);
155 
156 		//  bind info
157 		bool analog = it->mType & InputAction::Axis;
158 		bool twosided = it->mType == InputAction::Axis;
159 
160 		//  binding button  ----------------
161 		Btn btn1 = tabitem->createWidget<Button>("Button",
162 			x1, y, sx, sy,  Align::Default);
163 		gcom->setOrigPos(btn1, "OptionsWnd");
164 		UpdateInputButton(btn1, *it);
165 		btn1->eventMouseButtonClick += newDelegate(this, &CGui::inputBindBtnClicked);
166 		btn1->eventMouseButtonPressed += newDelegate(this, &CGui::inputBindBtn2);
167 		btn1->setUserData(*it);
168 		Colour clr =    !player ? Colour(0.7f,0.9f,1.f) :
169 			(analog ? (twosided ? Colour(0.7f,0.7f,1.f) : Colour(0.5f,0.75f,1.f)) : Colour(0.6f,0.9f,1.f));
170 		Colour txclr =  !player ? Colour(0.8f,1.f,1.f) :
171 			(analog ? (twosided ? Colour(0.9f,0.9f,1.f) : Colour(0.85f,0.93f,1.f)) : Colour(0.8f,1.f,1.f));
172 		btn1->setColour(clr);
173 		btn1->setTextColour(txclr);
174 
175 		//  value bar  --------------
176 		if (player)
177 		{
178 			Img bar = tabitem->createWidget<ImageBox>("ImageBox",
179 				x2 + (twosided ? 0 : 64), y+4, twosided ? 128 : 64, 16, Align::Default,
180 				"bar_" + toStr(i) + "_" + sPlr);
181 			gcom->setOrigPos(bar, "OptionsWnd");
182 			bar->setUserData(*it);
183 			bar->setImageTexture(String("input_bar.png"));  bar->setImageCoord(IntCoord(0,0,128,16));
184 		}
185 
186 		//  detail btn  ----------------
187 		if (analog)
188 		{	btn1 = tabitem->createWidget<Button>("Button",
189 				x3, y, 32, sy,  Align::Default,
190 				"inputdetail_" + toStr(i) + "_" + sPlr + "_1");
191 			gcom->setOrigPos(btn1, "OptionsWnd");
192 			btn1->setCaption(">");
193 			btn1->setTextColour(Colour(0.6f,0.7f,0.8f));
194 			btn1->setColour(Colour(0.6f,0.8f,1.0f));
195 			btn1->setUserData(*it);
196 			btn1->eventMouseButtonClick += newDelegate(this, &CGui::inputDetailBtn);
197 		}
198 
199 		//  icon  --------------
200 		#define CrtImg(x,y, ux,uy)  {  Img img =  \
201 			tabitem->createWidget<ImageBox>("ImageBox", x-32,y, 28,28, Align::Default);  \
202 			gcom->setOrigPos(img, "OptionsWnd");  \
203 			img->setAlpha(0.9f);  img->setImageCoord(IntCoord(ux,uy,128,128));  \
204 			img->setImageTexture("gui_icons.png");  }
205 
206 		if (player)  switch (i)
207 		{	//case 0:  CrtImg(x1,y, 128,256);  break;  // gauge
208 			case 2:  CrtImg(x1,y, 128,384);  break;  // steer
209 			case 6:  CrtImg(x1,y, 128,512);  break;  // gear
210 			case 9:  CrtImg(x1,y, 0,512);  break;    // camera
211 
212 			case 4:  CrtImg(x1,y, 512,0);  break;    // boost
213 			case 5:  CrtImg(x1,y, 512,128);  break;  // flip
214 			//case 7:  CrtImg(x1,y, 512,256);  break;  // damage
215 			case 11:  CrtImg(x1,y, 512,384);  break; // rewind
216 		}	++i;
217 	}
218 
219 	if (player)
220 	{	y = yHdr + 32 * yAdd;
221 		CreateText(x1,y, 500,24, "txtunb" + sPlr, TR("#80B0F0#{InputUnbind}"));
222 	}
223 }
224 
InitInputGui()225 void CGui::InitInputGui()
226 {
227 	app->input->LoadInputDefaults();
228 
229 
230 	txtInpDetail = fTxt("InputDetail");
231 	panInputDetail = fWP("PanInputDetail");
232 
233 	Tab(tabInput, "SubTabInput", tabInputChg);
234 	if (!tabInput)  return;
235 
236 	//  details edits
237 	Btn btn, bchk;
238 	Btn("InputInv", btnInputInv);  //Ed(InputMul, editInput);
239 	Ed(InputIncrease, editInput);  //Ed(InputReturn, editInput);
240 	Chk("OneAxisThrBrk", chkOneAxis, false);  chOneAxis = bchk;
241 
242 	//  key emul presets combo
243 	Cmb cmb;
244 	Cmb(cmb, "CmbInputKeysAllPreset", comboInputKeyAllPreset);
245 	if (cmb)
246 	{	cmb->removeAllItems();  cmb->addItem("");
247 		cmb->addItem(TR("#{InpSet_Slow}"));
248 		cmb->addItem(TR("#{InpSet_Medium}"));
249 		cmb->addItem(TR("#{InpSet_Fast}"));
250 	}
251 
252 	///  fill global and 4 players tabs
253 	CreateInputTab(4, false, app->input->mInputActions, app->mInputCtrl);
254 	for (int i=0; i < 4; ++i)
255 		CreateInputTab(i, true, app->input->mInputActionsPlayer[i], app->mInputCtrlPlayer[i]);
256 }
257 
258 
259 ///  Bind Input
260 //----------------------------------------------------------------------------------------------------------------------------------
261 
inputBindBtn2(WP sender,int,int,MouseButton mb)262 void CGui::inputBindBtn2(WP sender, int, int, MouseButton mb)
263 {
264 	if (mb == MouseButton::Right)
265 		inputUnbind(sender);
266 }
267 
inputBindBtnClicked(WP sender)268 void CGui::inputBindBtnClicked(WP sender)
269 {
270 	sender->castType<Button>()->setCaption( TR("#FFA030#{InputAssignKey}"));
271 
272 	InputAction* action = sender->getUserData<InputAction>();
273 	mBindingAction = action;
274 	mBindingSender = sender->castType<Button>();
275 
276 	if (mBindingAction->mType == InputAction::Axis)
277 	{	// bind decrease (ie left) first
278 		action->mICS->enableDetectingBindingState(action->mControl, ICS::Control::DECREASE);
279 	}else
280 		action->mICS->enableDetectingBindingState(action->mControl, ICS::Control::INCREASE);
281 
282 	UpdateInputButton(mBindingSender, *action, B_First);
283 
284 	// activate key capture mode
285 	app->bAssignKey = true;
286 	app->hideMouse();
287 }
288 
notifyInputActionBound(bool complete)289 void CGui::notifyInputActionBound(bool complete)
290 {
291 	UpdateInputButton(mBindingSender, *mBindingAction, complete ? B_Done : B_Second);
292 	if (complete)
293 	{	app->bAssignKey = false;
294 
295 		// If a key was assigned that used to belong to another control, it will now be unassigned,
296 		// so we need to force-update button labels
297 		TabControl* inputTab = fTab("SubTabInput");  if (!inputTab)  return;
298 		TabItem* current = inputTab->getItemSelected();
299 		for (int i=0; i < current->getChildCount(); ++i)
300 		{
301 			Button* button = current->getChildAt(i)->castType<Button>(false);
302 			if (!button || button->getCaption() == ">") // HACK: we don't want the detail buttons
303 				continue;
304 			if (button->getUserData<InputAction>() != mBindingAction)
305 				UpdateInputButton(button, *button->getUserData<InputAction>());
306 		}
307 	}
308 }
309 
inputUnbind(WP sender)310 void CGui::inputUnbind(WP sender)
311 {
312 	InputAction* action = sender->getUserData<InputAction>();
313 	mBindingAction = action;
314 	mBindingSender = sender->castType<Button>();
315 
316 	SDL_Keycode key = action->mICS->getKeyBinding(action->mControl, ICS::Control::INCREASE);
317 	action->mICS->removeKeyBinding(key);
318 
319 	key = action->mICS->getKeyBinding(action->mControl, ICS::Control::DECREASE);
320 	action->mICS->removeKeyBinding(key);
321 
322 	for (int j=0; j < SDL_NumJoysticks(); ++j)
323 	{
324 		int axis = action->mICS->getJoystickAxisBinding(action->mControl, j, ICS::Control::INCREASE);
325 		if (axis != ICS::InputControlSystem::UNASSIGNED)
326 			action->mICS->removeJoystickAxisBinding(j, axis);
327 
328 		int btn = action->mICS->getJoystickButtonBinding(action->mControl, j, ICS::Control::INCREASE);
329 		if (btn != ICS::InputControlSystem::UNASSIGNED)
330 			action->mICS->removeJoystickButtonBinding(j, btn);
331 	}
332 	UpdateInputButton(mBindingSender, *action);
333 }
334 
335 
336 ///  edit details
337 //-------------------------------------------------------------------------------
inputDetailBtn(WP sender)338 void CGui::inputDetailBtn(WP sender)
339 {
340 	const InputAction& action = *sender->getUserData<InputAction>();
341 	if (txtInpDetail)
342 		txtInpDetail->setCaptionWithReplacing( TR("#{InputDetailsFor}")+":  #{InputMap"+action.mName+"}");
343 
344 	mBindingAction = sender->getUserData<InputAction>();
345 	if (panInputDetail)
346 		panInputDetail->setVisible(false);
347 
348 	Button* btnInputInv = fBtn("InputInv");
349 	if (btnInputInv)
350 		btnInputInv->setStateSelected( mBindingAction->mControl->getInverted());
351 	if (edInputIncrease)
352 		edInputIncrease->setCaption( toStr(action.mControl->getStepSize() * action.mControl->getStepsPerSeconds()));
353 }
354 
editInput(EditPtr ed)355 void CGui::editInput(EditPtr ed)
356 {
357 	Real vInc = s2r(edInputIncrease->getCaption());
358 	mBindingAction->mControl->setStepSize(0.1);
359 	mBindingAction->mControl->setStepsPerSeconds(vInc*10);
360 }
361 
btnInputInv(WP wp)362 void CGui::btnInputInv(WP wp)
363 {
364 	ButtonPtr chk = wp->castType<Button>();
365 	chk->setStateSelected(!chk->getStateSelected());
366 	mBindingAction->mControl->setInverted(chk->getStateSelected());
367 }
368 
chkOneAxis(WP wp)369 void CGui::chkOneAxis(WP wp)
370 {
371 	int id=0;  if (!TabInputId(&id))  return;
372 	ButtonPtr chk = wp->castType<Button>();
373 	bool b = !app->mInputCtrlPlayer[id]->mbOneAxisThrottleBrake;
374 	app->mInputCtrlPlayer[id]->mbOneAxisThrottleBrake = b;
375     chk->setStateSelected(b);
376 }
377 
tabInputChg(TabPtr tab,size_t val)378 void CGui::tabInputChg(TabPtr tab, size_t val)
379 {
380 	int id=0;  bool vis = TabInputId(&id);
381 	chOneAxis->setVisible(vis);
382 	//txtInpDetail;  panInputDetail;
383 	//edInputIncrease;
384 	if (vis)
385 	{
386 		bool b = app->mInputCtrlPlayer[id]->mbOneAxisThrottleBrake;
387 		chOneAxis->setStateSelected(b);
388 	}
389 }
390 
391 //  returns player id 0..3, false if not player tab
TabInputId(int * pId)392 bool CGui::TabInputId(int* pId)
393 {
394 	if (!tabInput)  return false;
395 	int id = tabInput->getIndexSelected();
396 	if (id > 3)  return false;
397 	*pId = id;  return true;
398 }
399 
comboInputKeyAllPreset(ComboBoxPtr cmb,size_t val)400 void CGui::comboInputKeyAllPreset(ComboBoxPtr cmb, size_t val)
401 {
402 	if (val == 0)  return;  cmb->setIndexSelected(0);
403 	int id=0;  if (!TabInputId(&id))  return;
404 
405 	const int numActs = 6;  // these actions have key emul params (analog)
406 	int keyActs[numActs] = {A_Boost, A_Brake, A_Flip, A_HandBrake, A_Steering, A_Throttle};
407 	const Real speeds[3] = {2.f, 3.f, 4.f};
408 	Real vInc = speeds[val-1];
409 
410 	for (int i=0; i < numActs; ++i)
411 	{
412 		ICS::Control* control = app->mInputCtrlPlayer[id]->getControl(keyActs[i]);
413 
414 		control->setStepSize(0.1f);
415 		control->setStepsPerSeconds(vInc*10.f);
416 	}
417 	if (edInputIncrease)  edInputIncrease->setCaption(toStr(vInc));
418 }
419 
420 
421 ///  update input bars vis,dbg
422 //-------------------------------------------------------------------------------
UpdateInputBars()423 void CGui::UpdateInputBars()
424 {
425 	TabControl* inputTab = fTab("SubTabInput");  if (!inputTab)  return;
426 	TabItem* current = inputTab->getItemSelected();
427 	for (int i=0; i<current->getChildCount(); ++i)
428 	{
429 		ImageBox* image = current->getChildAt(i)->castType<ImageBox>(false);
430 		if (!image)  continue;
431 		InputAction* ia = image->getUserData<InputAction>(false);
432 		if (!ia)  continue;
433 
434 		const InputAction& action = *ia;
435 		float val = action.mICS->getChannel(action.mId)->getValue();
436 
437 		const int wf = 128, w = 256;
438 		int v = -val * 128, vf = -(val*2-1) * 64, s=512, s0=s/2;
439 
440 		bool full = action.mType == InputAction::Axis;
441 
442 		if (full)	image->setImageCoord(IntCoord(std::max(0, std::min(s-wf, vf + s0 -wf/2)), 0, wf, 16));
443 		else		image->setImageCoord(IntCoord(std::max(0, std::min(s-w,  v  + s0))      , 0, w, 16));
444 	}
445 }
446 
447 //  Bind Key   . . . . .
keyBindingDetected(ICS::InputControlSystem * pICS,ICS::Control * control,SDL_Keycode key,ICS::Control::ControlChangingDirection direction)448 void CGui::keyBindingDetected(
449 	ICS::InputControlSystem* pICS, ICS::Control* control,
450 	SDL_Keycode key,
451 	ICS::Control::ControlChangingDirection direction)
452 {
453 	ICS::DetectingBindingListener::keyBindingDetected(pICS, control, key, direction);
454 
455 	if (direction == ICS::Control::DECREASE)
456 	{
457 		pICS->enableDetectingBindingState(control, ICS::Control::INCREASE);
458 		notifyInputActionBound(false);  //  second key still needs binding
459 	}else
460 		notifyInputActionBound(true);  //  done
461 }
462 
463 //  Bind Joy Axis   . . . . .
joystickAxisBindingDetected(ICS::InputControlSystem * pICS,ICS::Control * control,int deviceId,int axis,ICS::Control::ControlChangingDirection direction)464 void CGui::joystickAxisBindingDetected(
465 	ICS::InputControlSystem* pICS, ICS::Control* control,
466 	int deviceId, int axis,
467 	ICS::Control::ControlChangingDirection direction)
468 {
469 	ICS::DetectingBindingListener::joystickAxisBindingDetected(
470 		pICS, control, deviceId, axis, ICS::Control::INCREASE);
471 
472 	std::string s = control->getName();
473 	//LogO("Control "+s);
474 
475 	///  inverted throttle and brake by default
476 	bool inv = s != "2" && s != "5";  //  only steering and flip normal
477 	control->setInverted(inv);
478 
479 	notifyInputActionBound(true);
480 }
481 
482 //  Bind Joy Button  . . . . .
joystickButtonBindingDetected(ICS::InputControlSystem * pICS,ICS::Control * control,int deviceId,unsigned int button,ICS::Control::ControlChangingDirection direction)483 void CGui::joystickButtonBindingDetected(
484 	ICS::InputControlSystem* pICS, ICS::Control* control,
485 	int deviceId, unsigned int button,
486 	ICS::Control::ControlChangingDirection direction)
487 {
488 	//  2-sided axis can't be bound with a JS button
489 	if (mBindingAction->mType == InputAction::Axis)
490 		return;
491 
492 	ICS::DetectingBindingListener::joystickButtonBindingDetected(
493 		pICS, control, deviceId, button, ICS::Control::INCREASE);
494 
495 	notifyInputActionBound(true);
496 }
497