1 /*
2 * Portions of this file are copyright Rebirth contributors and licensed as
3 * described in COPYING.txt.
4 * Portions of this file are copyright Parallax Software and licensed
5 * according to the Parallax license below.
6 * See COPYING.txt for license details.
7
8 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
9 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
10 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
11 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
12 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
13 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
14 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
15 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
16 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
17 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
18 */
19
20 /*
21 *
22 * Routines to configure keyboard, joystick, etc..
23 *
24 */
25
26 #include <algorithm>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <ctype.h>
32 #include <cstddef>
33 #include <stdexcept>
34 #include <functional>
35
36 #include "dxxerror.h"
37 #include "pstypes.h"
38 #include "gr.h"
39 #include "window.h"
40 #include "console.h"
41 #include "palette.h"
42 #include "physfsx.h"
43 #include "game.h"
44 #include "gamefont.h"
45 #include "u_mem.h"
46 #include "kconfig.h"
47 #include "digi.h"
48 #include "key.h"
49 #include "mouse.h"
50 #include "newmenu.h"
51 #include "multi.h"
52 #include "timer.h"
53 #include "text.h"
54 #include "player.h"
55 #include "args.h"
56 #include "playsave.h"
57 #include "screens.h"
58
59 #include "d_array.h"
60 #include "d_range.h"
61 #include "d_zip.h"
62 #include "partial_range.h"
63
64 using std::min;
65 using std::max;
66 using std::plus;
67 using std::minus;
68
69 namespace dcx {
70
71 // Array used to 'blink' the cursor while waiting for a keypress.
72 const std::array<int8_t, 64> fades{{
73 1,1,1,2,2,3,4,4,5,6,8,9,10,12,13,15,
74 16,17,19,20,22,23,24,26,27,28,28,29,30,30,31,31,
75 31,31,31,30,30,29,28,28,27,26,24,23,22,20,19,17,
76 16,15,13,12,10,9,8,6,5,4,4,3,2,2,1,1
77 }};
78
79 const std::array<char[2], 2> invert_text{{"N", "Y"}};
80 #if DXX_MAX_JOYSTICKS
81 joybutton_text_t joybutton_text;
82 #endif
83 #if DXX_MAX_AXES_PER_JOYSTICK
84 joyaxis_text_t joyaxis_text;
85 #endif
86 constexpr char mouseaxis_text[][8] = { "L/R", "F/B", "WHEEL" };
87 constexpr char mousebutton_text[][8] = { "LEFT", "RIGHT", "MID", "M4", "M5", "M6", "M7", "M8", "M9", "M10","M11","M12","M13","M14","M15","M16" };
88
89 const std::array<uint8_t, 19> system_keys{{
90 KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_MINUS, KEY_EQUAL, KEY_PRINT_SCREEN,
91 // KEY_*LOCK should always be last since we wanna skip these if -nostickykeys
92 KEY_CAPSLOCK, KEY_SCROLLOCK, KEY_NUMLOCK
93 }};
94
95 fix Cruise_speed=0;
96
97 #define INFO_Y (188)
98
99 const std::array<uint8_t, MAX_DXX_REBIRTH_CONTROLS> DefaultKeySettingsRebirth{{ 0x2,0xff,0xff,0x3,0xff,0xff,0x4,0xff,0xff,0x5,0xff,0xff,0x6,0xff,0xff,0x7,0xff,0xff,0x8,0xff,0xff,0x9,0xff,0xff,0xa,0xff,0xff,0xb,0xff,0xff }};
100
kconfig_begin_loop(control_info & Controls)101 void kconfig_begin_loop(control_info &Controls)
102 {
103 Controls.pitch_time = Controls.vertical_thrust_time = Controls.heading_time = Controls.sideways_thrust_time = Controls.bank_time = Controls.forward_thrust_time = 0;
104 }
105
106 namespace {
107
108 struct kc_mitem {
109 uint8_t oldvalue;
110 uint8_t value; // what key,button,etc
111 };
112
113 enum kc_type : uint8_t
114 {
115 BT_KEY = 0,
116 BT_MOUSE_BUTTON = 1,
117 BT_MOUSE_AXIS = 2,
118 BT_JOY_BUTTON = 3,
119 BT_JOY_AXIS = 4,
120 BT_INVERT = 5,
121 };
122
123 enum kc_state : uint8_t
124 {
125 STATE_NONE = 0,
126 STATE_BIT1 = 1,
127 STATE_BIT2 = 2,
128 STATE_BIT3 = 4,
129 STATE_BIT4 = 8,
130 STATE_BIT5 = 16,
131 };
132
kc_drawquestion(grs_canvas & canvas,const grs_font & cv_font,uint8_t & menu_fade_index,const short item_xinput,const short item_y,const int8_t item_w2,const color_palette_index color)133 static void kc_drawquestion(grs_canvas &canvas, const grs_font &cv_font, uint8_t &menu_fade_index, const short item_xinput, const short item_y, const int8_t item_w2, const color_palette_index color)
134 {
135 if (++ menu_fade_index > 63)
136 menu_fade_index = 0;
137 const auto &&[w, h] = gr_get_string_size(cv_font, "?");
138 const auto &&fspacx = FSPACX();
139 const auto &&fspacy = FSPACY();
140 const auto &&fspacx_item_xinput = fspacx(item_xinput);
141 const auto &&fspacy_item_y = fspacy(item_y);
142 gr_urect(canvas, fspacx_item_xinput, fspacy(item_y - 1), fspacx(item_xinput + item_w2), fspacy_item_y + h, color);
143 gr_set_fontcolor(canvas, BM_XRGB(28, 28, 28), -1);
144 const auto x = fspacx_item_xinput + ((fspacx(item_w2) - w) / 2);
145 gr_string(canvas, cv_font, x, fspacy_item_y, "?", w, h);
146 }
147
148 }
149
150 }
151
152 namespace dsx {
153
154 control_info Controls;
155
156 namespace {
157
158 #define kc_item kc_item
159 struct kc_item
160 {
161 const short x, y; // x, y pos of label
162 const short xinput; // x pos of input field
163 const int8_t w2; // length of input field
164 const uint8_t u,d,l,r; // neighboring field ids for cursor navigation
165 const kc_type type;
166 const kc_state state_bit;
167 union {
168 uint8_t control_info::state_controls_t::*const ci_state_ptr;
169 uint8_t control_info::state_controls_t::*const ci_count_ptr;
170 };
171 };
172
173 struct kc_menu : window
174 {
175 using window::window;
176 const char *litems;
177 const kc_item *items;
178 kc_mitem *mitems;
179 const char *title;
180 unsigned nitems;
181 unsigned citem = 0;
182 uint8_t changing = 0;
183 ubyte q_fade_i; // for flashing the question mark
184 uint8_t mouse_state = 0;
185 std::array<int, 3> old_maxis;
186 #if DXX_MAX_AXES_PER_JOYSTICK
187 std::array<int, JOY_MAX_AXES> old_jaxis;
188 #endif
189 virtual window_event_result event_handler(const d_event &) override;
190 };
191
192 }
193
194 const struct player_config::KeySettings DefaultKeySettings{
195 /* Keyboard */ {{{
196 #if defined(DXX_BUILD_DESCENT_I)
197 KEY_UP, KEY_PAD8, KEY_DOWN, KEY_PAD2, KEY_LEFT, KEY_PAD4, KEY_RIGHT, KEY_PAD6, KEY_LALT, 0xff, KEY_A, KEY_PAD1, KEY_D, KEY_PAD3, KEY_C, KEY_PADMINUS, KEY_X, KEY_PADPLUS, 0xff, 0xff, KEY_Q, KEY_PAD7, KEY_E, KEY_PAD9, KEY_LCTRL, KEY_RCTRL, KEY_SPACEBAR, 0xff, KEY_F, 0xff, KEY_W, 0xff, KEY_S, 0xff, KEY_B, 0xff, KEY_R, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, KEY_TAB, 0xff, KEY_COMMA, 0x0, KEY_PERIOD, 0x0
198 #elif defined(DXX_BUILD_DESCENT_II)
199 KEY_UP, KEY_PAD8, KEY_DOWN, KEY_PAD2, KEY_LEFT, KEY_PAD4, KEY_RIGHT, KEY_PAD6, KEY_LALT, 0xff, KEY_A, KEY_PAD1, KEY_D, KEY_PAD3, KEY_C, KEY_PADMINUS, KEY_X, KEY_PADPLUS, 0xff, 0xff, KEY_Q, KEY_PAD7, KEY_E, KEY_PAD9, KEY_LCTRL, KEY_RCTRL, KEY_SPACEBAR, 0xff, KEY_F, 0xff, KEY_W, 0xff, KEY_S, 0xff, KEY_B, 0xff, KEY_R, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, KEY_TAB, 0xff, KEY_LSHIFT, 0xff, KEY_COMMA, 0xff, KEY_PERIOD, 0xff, KEY_H, 0xff, KEY_T, 0xff, 0xff, 0xff, 0x0, 0x0
200 #endif
201 }}},
202 #if DXX_MAX_JOYSTICKS
203 {{{
204 #if defined(DXX_BUILD_DESCENT_I)
205 0x0, 0x1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
206 #elif defined(DXX_BUILD_DESCENT_II)
207 0x0, 0x1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
208 #endif
209 }}},
210 #endif
211 /* Mouse */ {{{
212 #if defined(DXX_BUILD_DESCENT_I)
213 0x0, 0x1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
214 #elif defined(DXX_BUILD_DESCENT_II)
215 0x0, 0x1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0
216 #endif
217 }}}
218 };
219
220 #if defined(DXX_BUILD_DESCENT_I)
221 #include /* generated by similar/main/generate-kconfig-udlr.py */ "d1x-rebirth/kconfig.udlr.h"
222 #elif defined(DXX_BUILD_DESCENT_II)
223 #include /* generated by similar/main/generate-kconfig-udlr.py */ "d2x-rebirth/kconfig.udlr.h"
224 #endif
225
226 namespace {
227
228 #if defined(DXX_BUILD_DESCENT_I)
229 #define D2X_EXTENDED_WEAPON_STRING(X)
230 #elif defined(DXX_BUILD_DESCENT_II)
231 #define D2X_EXTENDED_WEAPON_STRING(X) X
232 #endif
233
234 #define WEAPON_STRING_LASER D2X_EXTENDED_WEAPON_STRING("(SUPER)") "LASER CANNON"
235 #define WEAPON_STRING_VULCAN "VULCAN" D2X_EXTENDED_WEAPON_STRING("/GAUSS") " CANNON"
236 #define WEAPON_STRING_SPREADFIRE "SPREADFIRE" D2X_EXTENDED_WEAPON_STRING("/HELIX") " CANNON"
237 #define WEAPON_STRING_PLASMA "PLASMA" D2X_EXTENDED_WEAPON_STRING("/PHOENIX") " CANNON"
238 #define WEAPON_STRING_FUSION "FUSION" D2X_EXTENDED_WEAPON_STRING("/OMEGA") " CANNON"
239 #define WEAPON_STRING_CONCUSSION "CONCUSSION" D2X_EXTENDED_WEAPON_STRING("/FLASH") " MISSILE"
240 #define WEAPON_STRING_HOMING "HOMING" D2X_EXTENDED_WEAPON_STRING("/GUIDED") " MISSILE"
241 #define WEAPON_STRING_PROXIMITY "PROXIMITY BOMB" D2X_EXTENDED_WEAPON_STRING("/SMART MINE")
242 #define WEAPON_STRING_SMART "SMART" D2X_EXTENDED_WEAPON_STRING("/MERCURY") " MISSILE"
243 #define WEAPON_STRING_MEGA "MEGA" D2X_EXTENDED_WEAPON_STRING("/EARTHSHAKER") " MISSILE"
244
245 #if DXX_MAX_BUTTONS_PER_JOYSTICK || DXX_MAX_HATS_PER_JOYSTICK
246 #define DXX_KCONFIG_ITEM_JOY_WIDTH(I) I
247 #else
248 #define DXX_KCONFIG_ITEM_JOY_WIDTH(I) (static_cast<void>(I), 0)
249 #endif
250
251 #include "kconfig.ui-table.cpp"
252
253 static enumerated_array<kc_mitem, std::size(kc_keyboard), dxx_kconfig_ui_kc_keyboard> kcm_keyboard;
254 #if DXX_MAX_JOYSTICKS
255 static enumerated_array<kc_mitem, std::size(kc_joystick), dxx_kconfig_ui_kc_joystick> kcm_joystick;
256 #endif
257 static enumerated_array<kc_mitem, std::size(kc_mouse), dxx_kconfig_ui_kc_mouse> kcm_mouse;
258 static std::array<kc_mitem, std::size(kc_rebirth)> kcm_rebirth;
259
kconfig_start_changing(kc_menu & menu)260 static void kconfig_start_changing(kc_menu &menu)
261 {
262 const auto citem = menu.citem;
263 if (menu.items[citem].type == BT_INVERT)
264 {
265 menu.changing = 0; // in case we were changing something else
266 auto &value = menu.mitems[citem].value;
267 value = value ? 0 : 1;
268 return;
269 }
270 menu.q_fade_i = 0; // start question mark flasher
271 menu.changing = 1;
272 }
273
kc_set_exclusive_binding(kc_menu & menu,kc_mitem & mitem,const unsigned type,const unsigned value)274 static void kc_set_exclusive_binding(kc_menu &menu, kc_mitem &mitem, const unsigned type, const unsigned value)
275 {
276 const auto nitems = menu.nitems;
277 for (auto &&[iterate_mitem, iterate_item] : zip(unchecked_partial_range(menu.mitems, nitems), unchecked_partial_range(menu.items, nitems)))
278 {
279 if (&iterate_mitem != &mitem && iterate_mitem.value == value && iterate_item.type == type)
280 iterate_mitem.value = 255;
281 }
282 mitem.value = value;
283 menu.changing = 0;
284 }
285
286 }
287 }
288
289 static void kc_drawinput(grs_canvas &, const grs_font &, const kc_item &item, kc_mitem &mitem, int is_current, const char *label);
290 static void kc_change_key( kc_menu &menu,const d_event &event, kc_mitem& mitem );
291 #if DXX_MAX_BUTTONS_PER_JOYSTICK || DXX_MAX_HATS_PER_JOYSTICK
292 static void kc_change_joybutton( kc_menu &menu,const d_event &event, kc_mitem& mitem );
293 #endif
294 static void kc_change_mousebutton( kc_menu &menu,const d_event &event, kc_mitem& mitem );
295 #if DXX_MAX_AXES_PER_JOYSTICK
296 static void kc_change_joyaxis( kc_menu &menu,const d_event &event, kc_mitem& mitem );
297 #endif
298 static void kc_change_mouseaxis( kc_menu &menu,const d_event &event, kc_mitem& mitem );
299
get_item_text(const kc_item & item,const kc_mitem & mitem,char (& buf)[10])300 static const char *get_item_text(const kc_item &item, const kc_mitem &mitem, char (&buf)[10])
301 {
302 if (mitem.value==255) {
303 return "";
304 } else {
305 switch( item.type ) {
306 case BT_KEY:
307 return key_properties[mitem.value].key_text;
308 case BT_MOUSE_BUTTON:
309 return mousebutton_text[mitem.value];
310 case BT_MOUSE_AXIS:
311 return mouseaxis_text[mitem.value];
312 #if DXX_MAX_BUTTONS_PER_JOYSTICK || DXX_MAX_HATS_PER_JOYSTICK
313 case BT_JOY_BUTTON:
314 if (joybutton_text.size() > mitem.value)
315 return &joybutton_text[mitem.value][0];
316 else
317 {
318 snprintf(buf, sizeof(buf), "BTN%2d", mitem.value + 1);
319 return buf;
320 }
321 break;
322 #else
323 (void)buf;
324 #endif
325 #if DXX_MAX_AXES_PER_JOYSTICK
326 case BT_JOY_AXIS:
327 if (joyaxis_text.size() > mitem.value)
328 return &joyaxis_text[mitem.value][0];
329 else
330 {
331 snprintf(buf, sizeof(buf), "AXIS%2d", mitem.value + 1);
332 return buf;
333 }
334 break;
335 #else
336 (void)buf;
337 #endif
338 case BT_INVERT:
339 return invert_text[mitem.value];
340 default:
341 return NULL;
342 }
343 }
344 }
345
get_item_height(const grs_font & cv_font,const kc_item & item,const kc_mitem & mitem)346 static int get_item_height(const grs_font &cv_font, const kc_item &item, const kc_mitem &mitem)
347 {
348 char buf[10];
349 const char *btext;
350
351 btext = get_item_text(item, mitem, buf);
352 if (!btext)
353 return 0;
354 const auto h = gr_get_string_size(cv_font, btext).height;
355 return h;
356 }
357
kc_gr_2y_string(grs_canvas & canvas,const grs_font & cv_font,const char * const s,const font_y_scaled_float y,const font_x_scaled_float x0,const font_x_scaled_float x1)358 static void kc_gr_2y_string(grs_canvas &canvas, const grs_font &cv_font, const char *const s, const font_y_scaled_float y, const font_x_scaled_float x0, const font_x_scaled_float x1)
359 {
360 const auto &&[w, h] = gr_get_string_size(cv_font, s);
361 gr_string(canvas, cv_font, x0, y, s, w, h);
362 gr_string(canvas, cv_font, x1, y, s, w, h);
363 }
364
kconfig_draw(kc_menu & menu)365 static void kconfig_draw(kc_menu &menu)
366 {
367 grs_canvas &save_canvas = *grd_curcanv;
368 const auto &&fspacx = FSPACX();
369 const auto &&fspacy = FSPACY();
370 int w = menu.w_canv.cv_bitmap.bm_w, h = menu.w_canv.cv_bitmap.bm_h;
371
372 gr_set_default_canvas();
373 nm_draw_background(*grd_curcanv, ((SWIDTH - w) / 2) - BORDERX, ((SHEIGHT - h) / 2) - BORDERY, ((SWIDTH - w) / 2) + w + BORDERX, ((SHEIGHT - h) / 2) + h + BORDERY);
374
375 gr_set_current_canvas(menu.w_canv);
376 auto &canvas = *grd_curcanv;
377
378 const grs_font *save_font = canvas.cv_font;
379
380 auto &medium3_font = *MEDIUM3_FONT;
381 gr_string(canvas, medium3_font, 0x8000, fspacy(8), menu.title);
382
383 auto &game_font = *GAME_FONT;
384 gr_set_fontcolor(canvas, BM_XRGB(28, 28, 28), -1);
385 gr_string(canvas, game_font, 0x8000, fspacy(21), "Enter changes, ctrl-d deletes, ctrl-r resets defaults, ESC exits");
386
387 if (menu.items == kc_keyboard)
388 {
389 gr_set_fontcolor(canvas, BM_XRGB(31, 27, 6), -1);
390 const uint8_t color = BM_XRGB(31,27,6);
391 const auto &&fspacx98 = fspacx(98);
392 const auto &&fspacx128 = fspacx(128);
393 const auto &&fspacy42 = fspacy(42);
394 gr_rect(canvas, fspacx98, fspacy42, fspacx(106), fspacy42, color); // horiz/left
395 gr_rect(canvas, fspacx(120), fspacy42, fspacx128, fspacy42, color); // horiz/right
396 const auto &&fspacy44 = fspacy(44);
397 gr_rect(canvas, fspacx98, fspacy42, fspacx98, fspacy44, color); // vert/left
398 gr_rect(canvas, fspacx128, fspacy42, fspacx128, fspacy44, color); // vert/right
399
400 const auto &&fspacx253 = fspacx(253);
401 const auto &&fspacx283 = fspacx(283);
402 gr_rect(canvas, fspacx253, fspacy42, fspacx(261), fspacy42, color); // horiz/left
403 gr_rect(canvas, fspacx(275), fspacy42, fspacx283, fspacy42, color); // horiz/right
404 gr_rect(canvas, fspacx253, fspacy42, fspacx253, fspacy44, color); // vert/left
405 gr_rect(canvas, fspacx283, fspacy42, fspacx283, fspacy44, color); // vert/right
406
407 kc_gr_2y_string(canvas, game_font, "OR", fspacy(40), fspacx(109), fspacx(264));
408 }
409 #if DXX_MAX_JOYSTICKS
410 else if (menu.items == kc_joystick)
411 {
412 const uint8_t color = BM_XRGB(31, 27, 6);
413 gr_set_fontcolor(canvas, color, -1);
414 #if DXX_MAX_AXES_PER_JOYSTICK
415 constexpr auto kconfig_axis_labels_top_y = DXX_KCONFIG_UI_JOYSTICK_AXES_TOP_Y + 8;
416 const auto &&fspacy_lower_label2 = fspacy(kconfig_axis_labels_top_y);
417 gr_string(canvas, game_font, 0x8000, fspacy(DXX_KCONFIG_UI_JOYSTICK_AXES_TOP_Y), TXT_AXES);
418 gr_set_fontcolor(canvas, BM_XRGB(28, 28, 28), -1);
419 kc_gr_2y_string(canvas, game_font, TXT_AXIS, fspacy_lower_label2, fspacx(81), fspacx(230));
420 kc_gr_2y_string(canvas, game_font, TXT_INVERT, fspacy_lower_label2, fspacx(111), fspacx(260));
421 gr_set_fontcolor(canvas, color, -1);
422 #endif
423
424 #if DXX_MAX_BUTTONS_PER_JOYSTICK || DXX_MAX_HATS_PER_JOYSTICK
425 constexpr auto kconfig_buttons_top_y = DXX_KCONFIG_UI_JOYSTICK_TOP_Y + 6;
426 constexpr auto kconfig_buttons_labels_top_y = kconfig_buttons_top_y + 4;
427 gr_string(canvas, game_font, 0x8000, fspacy(DXX_KCONFIG_UI_JOYSTICK_TOP_Y), TXT_BUTTONS);
428 const auto &&fspacx115 = fspacx(115);
429 const auto &&fspacy_horiz_OR_line = fspacy(kconfig_buttons_labels_top_y);
430 gr_rect(canvas, fspacx115, fspacy_horiz_OR_line, fspacx(123), fspacy_horiz_OR_line, color); // horiz/left
431 const auto &&fspacx145 = fspacx(145);
432 gr_rect(canvas, fspacx(137), fspacy_horiz_OR_line, fspacx145, fspacy_horiz_OR_line, color); // horiz/right
433 const auto &&fspacx261 = fspacx(261);
434 gr_rect(canvas, fspacx261, fspacy_horiz_OR_line, fspacx(269), fspacy_horiz_OR_line, color); // horiz/left
435 const auto &&fspacx291 = fspacx(291);
436 gr_rect(canvas, fspacx(283), fspacy_horiz_OR_line, fspacx291, fspacy_horiz_OR_line, color); // horiz/right
437
438 const auto &&fspacy_vert_OR_line = fspacy(kconfig_buttons_labels_top_y + 2);
439 gr_rect(canvas, fspacx115, fspacy_horiz_OR_line, fspacx115, fspacy_vert_OR_line, color); // vert/left
440 gr_rect(canvas, fspacx145, fspacy_horiz_OR_line, fspacx145, fspacy_vert_OR_line, color); // vert/right
441 gr_rect(canvas, fspacx261, fspacy_horiz_OR_line, fspacx261, fspacy_vert_OR_line, color); // vert/left
442 gr_rect(canvas, fspacx291, fspacy_horiz_OR_line, fspacx291, fspacy_vert_OR_line, color); // vert/right
443
444 kc_gr_2y_string(canvas, game_font, "OR", fspacy(kconfig_buttons_top_y + 2), fspacx(126), fspacx(272));
445 #endif
446 }
447 #endif
448 else if (menu.items == kc_mouse)
449 {
450 gr_set_fontcolor(canvas, BM_XRGB(31, 27, 6), -1);
451 gr_string(canvas, game_font, 0x8000, fspacy(37), TXT_BUTTONS);
452 gr_string(canvas, game_font, 0x8000, fspacy(137), TXT_AXES);
453 gr_set_fontcolor(canvas, BM_XRGB(28, 28, 28), -1);
454 const auto &&fspacy145 = fspacy(145);
455 kc_gr_2y_string(canvas, game_font, TXT_AXIS, fspacy145, fspacx( 87), fspacx(242));
456 kc_gr_2y_string(canvas, game_font, TXT_INVERT, fspacy145, fspacx(120), fspacx(274));
457 }
458 else if (menu.items == kc_rebirth)
459 {
460 gr_set_fontcolor(canvas, BM_XRGB(31, 27, 6), -1);
461
462 const auto &&fspacy60 = fspacy(60);
463 gr_string(canvas, game_font, fspacx(152), fspacy60, "KEYBOARD");
464 #if DXX_MAX_BUTTONS_PER_JOYSTICK || DXX_MAX_HATS_PER_JOYSTICK
465 gr_string(canvas, game_font, fspacx(210), fspacy60, "JOYSTICK");
466 #endif
467 gr_string(canvas, game_font, fspacx(273), fspacy60, "MOUSE");
468 }
469
470 unsigned citem = menu.citem;
471 const char *current_label = NULL;
472 const char *litem = menu.litems;
473 const auto nitems = menu.nitems;
474 for (const auto i : xrange(nitems))
475 {
476 auto next_label = (i + 1 >= menu.nitems || menu.items[i + 1].y != menu.items[i].y) ? litem : nullptr;
477 if (i == citem)
478 current_label = litem;
479 else if (menu.items[i].w2)
480 kc_drawinput(canvas, game_font, menu.items[i], menu.mitems[i], 0, next_label);
481 if (next_label)
482 litem += strlen(litem) + 1;
483 }
484 kc_drawinput(canvas, game_font, menu.items[citem], menu.mitems[citem], 1, current_label);
485
486 gr_set_fontcolor(canvas, BM_XRGB(28, 28, 28), -1);
487 if (menu.changing)
488 {
489 const char *s;
490 switch(menu.items[menu.citem].type)
491 {
492 case BT_KEY:
493 s = TXT_PRESS_NEW_KEY;
494 break;
495 case BT_MOUSE_BUTTON:
496 s = TXT_PRESS_NEW_MBUTTON;
497 break;
498 case BT_MOUSE_AXIS:
499 s = TXT_MOVE_NEW_MSE_AXIS;
500 break;
501 #if DXX_MAX_BUTTONS_PER_JOYSTICK || DXX_MAX_HATS_PER_JOYSTICK
502 case BT_JOY_BUTTON:
503 s = TXT_PRESS_NEW_JBUTTON;
504 break;
505 #endif
506 #if DXX_MAX_AXES_PER_JOYSTICK
507 case BT_JOY_AXIS:
508 s = TXT_MOVE_NEW_JOY_AXIS;
509 break;
510 #endif
511 default:
512 s = nullptr;
513 break;
514 }
515 if (s)
516 gr_string(canvas, game_font, 0x8000, fspacy(INFO_Y), s);
517 auto &item = menu.items[menu.citem];
518 auto &menu_fade_index = menu.q_fade_i;
519 const auto fade_element = fades[menu_fade_index];
520 #if defined(DXX_BUILD_DESCENT_I)
521 const auto color = gr_fade_table[static_cast<gr_fade_level>(fade_element)][BM_XRGB(21, 0, 24)];
522 #elif defined(DXX_BUILD_DESCENT_II)
523 const auto color = BM_XRGB(21 * fade_element / 31, 0, 24 * fade_element / 31);
524 #endif
525 kc_drawquestion(canvas, game_font, menu_fade_index, item.xinput, item.y, item.w2, color);
526 }
527 canvas.cv_font = save_font;
528 gr_set_current_canvas( save_canvas );
529 }
530
in_bounds(unsigned mx,unsigned my,unsigned x1,unsigned xw,unsigned y1,unsigned yh)531 static inline int in_bounds(unsigned mx, unsigned my, unsigned x1, unsigned xw, unsigned y1, unsigned yh)
532 {
533 if (mx <= x1)
534 return 0;
535 if (my <= y1)
536 return 0;
537 if (mx >= x1 + xw)
538 return 0;
539 if (my >= y1 + yh)
540 return 0;
541 return 1;
542 }
543
544 namespace dsx {
545
546 namespace {
547
kconfig_mouse(kc_menu & menu,const d_event & event)548 static window_event_result kconfig_mouse(kc_menu &menu, const d_event &event)
549 {
550 grs_canvas &save_canvas = *grd_curcanv;
551 int mx, my, mz, x1, y1;
552 window_event_result rval = window_event_result::ignored;
553
554 gr_set_current_canvas(menu.w_canv);
555 auto &canvas = *grd_curcanv;
556
557 if (menu.mouse_state)
558 {
559 int item_height;
560
561 mouse_get_pos(&mx, &my, &mz);
562 const auto &&fspacx = FSPACX();
563 const auto &&fspacy = FSPACY();
564 const auto nitems = menu.nitems;
565 for (const auto i : xrange(nitems))
566 {
567 item_height = get_item_height(*canvas.cv_font, menu.items[i], menu.mitems[i]);
568 x1 = canvas.cv_bitmap.bm_x + fspacx(menu.items[i].xinput);
569 y1 = canvas.cv_bitmap.bm_y + fspacy(menu.items[i].y);
570 if (in_bounds(mx, my, x1, fspacx(menu.items[i].w2), y1, item_height)) {
571 menu.citem = i;
572 rval = window_event_result::handled;
573 break;
574 }
575 }
576 }
577 else if (event.type == EVENT_MOUSE_BUTTON_UP)
578 {
579 int item_height;
580
581 mouse_get_pos(&mx, &my, &mz);
582 item_height = get_item_height(*canvas.cv_font, menu.items[menu.citem], menu.mitems[menu.citem]);
583 const auto &&fspacx = FSPACX();
584 x1 = canvas.cv_bitmap.bm_x + fspacx(menu.items[menu.citem].xinput);
585 y1 = canvas.cv_bitmap.bm_y + FSPACY(menu.items[menu.citem].y);
586 if (in_bounds(mx, my, x1, fspacx(menu.items[menu.citem].w2), y1, item_height)) {
587 kconfig_start_changing(menu);
588 rval = window_event_result::handled;
589 }
590 else
591 {
592 // Click out of changing mode - kreatordxx
593 menu.changing = 0;
594 rval = window_event_result::handled;
595 }
596 }
597
598 gr_set_current_canvas(save_canvas);
599
600 return rval;
601 }
602
603 }
604
605 }
606
607 namespace dcx {
608
609 namespace {
610
611 template <std::size_t M, std::size_t C>
reset_mitem_values(std::array<kc_mitem,M> & m,const std::array<uint8_t,C> & c)612 static void reset_mitem_values(std::array<kc_mitem, M> &m, const std::array<uint8_t, C> &c)
613 {
614 for (std::size_t i = 0; i != min(M, C); ++i)
615 m[i].value = c[i];
616 }
617
step_citem_past_empty_cell(unsigned & citem,const kc_item * const items,const uint8_t kc_item::* const next)618 static void step_citem_past_empty_cell(unsigned &citem, const kc_item *const items, const uint8_t kc_item::*const next)
619 {
620 do {
621 citem = items[citem].*next;
622 } while (!items[citem].w2);
623 }
624
kconfig_key_command(kc_menu & menu,const d_event & event)625 static window_event_result kconfig_key_command(kc_menu &menu, const d_event &event)
626 {
627 auto k = event_key_get(event);
628
629 // when changing, process no keys instead of ESC
630 if (menu.changing && (k != -2 && k != KEY_ESC))
631 return window_event_result::ignored;
632 switch (k)
633 {
634 case KEY_CTRLED+KEY_D:
635 menu.mitems[menu.citem].value = 255;
636 return window_event_result::handled;
637 case KEY_CTRLED+KEY_R:
638 if (menu.items==kc_keyboard)
639 reset_mitem_values(kcm_keyboard, DefaultKeySettings.Keyboard);
640 #if DXX_MAX_JOYSTICKS
641 else if (menu.items == kc_joystick)
642 reset_mitem_values(kcm_joystick, DefaultKeySettings.Joystick);
643 #endif
644 else if (menu.items == kc_mouse)
645 reset_mitem_values(kcm_mouse, DefaultKeySettings.Mouse);
646 else if (menu.items == kc_rebirth)
647 reset_mitem_values(kcm_rebirth, DefaultKeySettingsRebirth);
648 return window_event_result::handled;
649 case KEY_DELETE:
650 menu.mitems[menu.citem].value = 255;
651 return window_event_result::handled;
652 case KEY_UP:
653 case KEY_PAD8:
654 step_citem_past_empty_cell(menu.citem, menu.items, &kc_item::u);
655 return window_event_result::handled;
656 case KEY_DOWN:
657 case KEY_PAD2:
658 step_citem_past_empty_cell(menu.citem, menu.items, &kc_item::d);
659 return window_event_result::handled;
660 case KEY_LEFT:
661 case KEY_PAD4:
662 step_citem_past_empty_cell(menu.citem, menu.items, &kc_item::l);
663 return window_event_result::handled;
664 case KEY_RIGHT:
665 case KEY_PAD6:
666 step_citem_past_empty_cell(menu.citem, menu.items, &kc_item::r);
667 return window_event_result::handled;
668 case KEY_ENTER:
669 case KEY_PADENTER:
670 kconfig_start_changing(menu);
671 return window_event_result::handled;
672 case KEY_ESC:
673 if (menu.changing)
674 menu.changing = 0;
675 else
676 {
677 return window_event_result::close;
678 }
679 return window_event_result::handled;
680 case 0: // some other event
681 break;
682
683 default:
684 break;
685 }
686 return window_event_result::ignored;
687 }
688
689 }
690
691 }
692
693 namespace dsx {
694
695 namespace {
696
event_handler(const d_event & event)697 window_event_result kc_menu::event_handler(const d_event &event)
698 {
699 #if DXX_MAX_BUTTONS_PER_JOYSTICK
700 if (!changing && joy_translate_menu_key(event))
701 return window_event_result::handled;
702 #endif
703
704 switch (event.type)
705 {
706 case EVENT_WINDOW_ACTIVATED:
707 game_flush_inputs(Controls);
708 break;
709
710 case EVENT_WINDOW_DEACTIVATED:
711 mouse_state = 0;
712 break;
713
714 case EVENT_MOUSE_BUTTON_DOWN:
715 case EVENT_MOUSE_BUTTON_UP:
716 if (changing && (items[citem].type == BT_MOUSE_BUTTON) && (event.type == EVENT_MOUSE_BUTTON_UP))
717 {
718 kc_change_mousebutton(*this, event, mitems[citem]);
719 mouse_state = (event.type == EVENT_MOUSE_BUTTON_DOWN);
720 return window_event_result::handled;
721 }
722
723 if (event_mouse_get_button(event) == MBTN_RIGHT)
724 {
725 if (!changing)
726 {
727 return window_event_result::close;
728 }
729 return window_event_result::handled;
730 }
731 else if (event_mouse_get_button(event) != MBTN_LEFT)
732 return window_event_result::ignored;
733
734 mouse_state = (event.type == EVENT_MOUSE_BUTTON_DOWN);
735 return kconfig_mouse(*this, event);
736
737 case EVENT_MOUSE_MOVED:
738 if (changing && items[citem].type == BT_MOUSE_AXIS)
739 kc_change_mouseaxis(*this, event, mitems[citem]);
740 else
741 event_mouse_get_delta(event, &old_maxis[0], &old_maxis[1], &old_maxis[2]);
742 break;
743
744 #if DXX_MAX_BUTTONS_PER_JOYSTICK || DXX_MAX_HATS_PER_JOYSTICK
745 case EVENT_JOYSTICK_BUTTON_DOWN:
746 if (changing && items[citem].type == BT_JOY_BUTTON)
747 kc_change_joybutton(*this, event, mitems[citem]);
748 break;
749 #endif
750
751 #if DXX_MAX_AXES_PER_JOYSTICK
752 case EVENT_JOYSTICK_MOVED:
753 if (changing && items[citem].type == BT_JOY_AXIS)
754 kc_change_joyaxis(*this, event, mitems[citem]);
755 else
756 {
757 const auto &av = event_joystick_get_axis(event);
758 const auto &axis = av.axis;
759 const auto &value = av.value;
760 old_jaxis[axis] = value;
761 }
762 break;
763 #endif
764
765 case EVENT_KEY_COMMAND:
766 {
767 window_event_result rval = kconfig_key_command(*this, event);
768 if (rval != window_event_result::ignored)
769 return rval;
770 if (changing && items[citem].type == BT_KEY)
771 kc_change_key(*this, event, mitems[citem]);
772 return window_event_result::ignored;
773 }
774
775 case EVENT_IDLE:
776 kconfig_mouse(*this, event);
777 break;
778
779 case EVENT_WINDOW_DRAW:
780 if (changing)
781 timer_delay(f0_1/10);
782 else
783 timer_delay2(50);
784 kconfig_draw(*this);
785 break;
786
787 case EVENT_WINDOW_CLOSE:
788 // Update save values...
789 for (auto &&[kcm, setting] : zip(kcm_keyboard, PlayerCfg.KeySettings.Keyboard))
790 setting = kcm.value;
791 #if DXX_MAX_JOYSTICKS
792 for (auto &&[kcm, setting] : zip(kcm_joystick, PlayerCfg.KeySettings.Joystick))
793 setting = kcm.value;
794 #endif
795
796 for (auto &&[kcm, setting] : zip(kcm_mouse, PlayerCfg.KeySettings.Mouse))
797 setting = kcm.value;
798 for (auto &&[kcm, setting] : zip(kcm_rebirth, PlayerCfg.KeySettingsRebirth))
799 setting = kcm.value;
800 return window_event_result::ignored; // continue closing
801 default:
802 return window_event_result::ignored;
803 }
804 return window_event_result::handled;
805 }
806
kconfig_sub(const char * litems,const kc_item * items,kc_mitem * mitems,int nitems,const char * title)807 static void kconfig_sub(const char *litems, const kc_item * items,kc_mitem *mitems,int nitems, const char *title)
808 {
809 set_screen_mode(SCREEN_MENU);
810 kc_set_controls();
811
812 const auto &&fspacx = FSPACX();
813 const auto &&fspacy = FSPACY();
814 const uint16_t target_window_width = fspacx(320);
815 const uint16_t target_window_height = fspacy(220);
816 const auto swidth = SWIDTH;
817 const auto sheight = SHEIGHT;
818 const auto window_width = std::min(swidth, target_window_width);
819 const auto window_height = std::min(sheight, target_window_height);
820 auto menu = window_create<kc_menu>(grd_curscreen->sc_canvas, (swidth - window_width) / 2, (sheight - window_height) / 2, window_width, window_height);
821 menu->items = items;
822 menu->litems = litems;
823 menu->mitems = mitems;
824 menu->nitems = nitems;
825 menu->title = title;
826 if (!items[0].w2)
827 step_citem_past_empty_cell(menu->citem, items, &kc_item::r);
828 }
829
830 template <std::size_t N>
kconfig_sub(const char * litems,const kc_item (& items)[N],std::array<kc_mitem,N> & mitems,const char * title)831 static void kconfig_sub(const char *litems, const kc_item (&items)[N], std::array<kc_mitem, N> &mitems, const char *title)
832 {
833 kconfig_sub(litems, items, mitems.data(), N, title);
834 }
835
836 }
837
838 }
839
kc_drawinput(grs_canvas & canvas,const grs_font & cv_font,const kc_item & item,kc_mitem & mitem,const int is_current,const char * const label)840 static void kc_drawinput(grs_canvas &canvas, const grs_font &cv_font, const kc_item &item, kc_mitem &mitem, const int is_current, const char *const label)
841 {
842 char buf[10];
843 const char *btext;
844 const auto &&fspacx = FSPACX();
845 const auto &&fspacy = FSPACY();
846 unsigned r, g, b;
847 if (label)
848 {
849 if (is_current)
850 r = 20 * 2, g = 20 * 2, b = 29 * 2;
851 else
852 r = 15 * 2, g = 15 * 2, b = 24 * 2;
853 gr_set_fontcolor(canvas, gr_find_closest_color(r, g, b), -1);
854 gr_string(canvas, cv_font, fspacx(item.x), fspacy(item.y), label);
855 }
856
857 btext = get_item_text(item, mitem, buf);
858 if (!btext)
859 return;
860 {
861 if (is_current)
862 r = 21 * 2, g = 0, b = 24 * 2;
863 else
864 r = 16 * 2, g = 0, b = 19 * 2;
865
866 int x;
867 const auto color = gr_find_closest_color(r, g, b);
868 const auto &&[w, h] = gr_get_string_size(cv_font, btext);
869 const auto &&fspacx_item_xinput = fspacx(item.xinput);
870 const auto &&fspacy_item_y = fspacy(item.y);
871 gr_urect(canvas, fspacx_item_xinput, fspacy(item.y - 1), fspacx(item.xinput + item.w2), fspacy_item_y + h, color);
872
873 if (mitem.oldvalue == mitem.value)
874 r = b = 28 * 2;
875 else
876 r = b = 0;
877 gr_set_fontcolor(canvas, gr_find_closest_color(r, 28 * 2, b), -1);
878
879 x = fspacx_item_xinput + ((fspacx(item.w2) - w) / 2);
880
881 gr_string(canvas, cv_font, x, fspacy_item_y, btext, w, h);
882 }
883 }
884
kc_change_key(kc_menu & menu,const d_event & event,kc_mitem & mitem)885 static void kc_change_key( kc_menu &menu,const d_event &event, kc_mitem &mitem )
886 {
887 ubyte keycode = 255;
888
889 Assert(event.type == EVENT_KEY_COMMAND);
890 keycode = event_key_get_raw(event);
891
892 auto e = end(system_keys);
893 if (unlikely(CGameArg.CtlNoStickyKeys))
894 e = std::prev(e, 3);
895 const auto predicate = [keycode](uint8_t k) { return keycode == k; };
896 if (std::any_of(begin(system_keys), e, predicate))
897 return;
898
899 kc_set_exclusive_binding(menu, mitem, BT_KEY, keycode);
900 }
901
902 #if DXX_MAX_BUTTONS_PER_JOYSTICK
kc_change_joybutton(kc_menu & menu,const d_event & event,kc_mitem & mitem)903 static void kc_change_joybutton( kc_menu &menu,const d_event &event, kc_mitem &mitem )
904 {
905 int button = 255;
906
907 Assert(event.type == EVENT_JOYSTICK_BUTTON_DOWN);
908 button = event_joystick_get_button(event);
909
910 kc_set_exclusive_binding(menu, mitem, BT_JOY_BUTTON, button);
911 }
912 #endif
913
kc_change_mousebutton(kc_menu & menu,const d_event & event,kc_mitem & mitem)914 static void kc_change_mousebutton( kc_menu &menu,const d_event &event, kc_mitem &mitem)
915 {
916 int button;
917
918 Assert(event.type == EVENT_MOUSE_BUTTON_DOWN || event.type == EVENT_MOUSE_BUTTON_UP);
919 button = event_mouse_get_button(event);
920
921 kc_set_exclusive_binding(menu, mitem, BT_MOUSE_BUTTON, button);
922 }
923
924 #if DXX_MAX_AXES_PER_JOYSTICK
kc_change_joyaxis(kc_menu & menu,const d_event & event,kc_mitem & mitem)925 static void kc_change_joyaxis( kc_menu &menu,const d_event &event, kc_mitem &mitem )
926 {
927 const auto &av = event_joystick_get_axis(event);
928 const auto &axis = av.axis;
929 const auto &value = av.value;
930
931 if ( abs(value-menu.old_jaxis[axis])<32 )
932 return;
933 con_printf(CON_DEBUG, "Axis Movement detected: Axis %i", axis);
934
935 kc_set_exclusive_binding(menu, mitem, BT_JOY_AXIS, axis);
936 }
937 #endif
938
kc_change_mouseaxis(kc_menu & menu,const d_event & event,kc_mitem & mitem)939 static void kc_change_mouseaxis( kc_menu &menu,const d_event &event, kc_mitem &mitem )
940 {
941 int dx, dy, dz;
942
943 Assert(event.type == EVENT_MOUSE_MOVED);
944 event_mouse_get_delta( event, &dx, &dy, &dz );
945 uint8_t code;
946 if (abs(dz) > 5)
947 code = 2;
948 else if (abs(dy) > 5)
949 code = 1;
950 else if (abs(dx) > 5)
951 code = 0;
952 else
953 return;
954 kc_set_exclusive_binding(menu, mitem, BT_MOUSE_AXIS, code);
955 }
956
kconfig(const kconfig_type n)957 void kconfig(const kconfig_type n)
958 {
959 switch (n)
960 {
961 #define kconfig_case(TYPE,TITLE) \
962 case kconfig_type::TYPE: \
963 kconfig_sub(DXX_KCONFIG_UI_LABEL_kc_##TYPE, kc_##TYPE, kcm_##TYPE, TITLE); \
964 break;
965 kconfig_case(keyboard, "KEYBOARD");
966 #if DXX_MAX_JOYSTICKS
967 kconfig_case(joystick, "JOYSTICK");
968 #endif
969 kconfig_case(mouse, "MOUSE");
970 kconfig_case(rebirth, "WEAPON KEYS");
971 #undef kconfig_case
972 default:
973 Int3();
974 return;
975 }
976 }
977
978 namespace dsx {
979
input_button_matched(control_info & Controls,const kc_item & item,int down)980 static void input_button_matched(control_info &Controls, const kc_item& item, int down)
981 {
982 if (item.state_bit)
983 {
984 if (!item.ci_state_ptr)
985 throw std::logic_error("NULL state pointer with non-zero state bit");
986 if (down)
987 Controls.state.*item.ci_state_ptr |= item.state_bit;
988 else
989 Controls.state.*item.ci_state_ptr &= ~item.state_bit;
990 }
991 else
992 {
993 if (item.ci_count_ptr != NULL && down)
994 Controls.state.*item.ci_count_ptr += 1;
995 }
996 }
997
998 }
999
1000 namespace dcx {
1001
1002 namespace {
1003
1004 template <template<typename> class F>
adjust_ramped_keyboard_field(float & keydown_time,ubyte & state,fix & time,const float & sensitivity,const int & speed_factor,const int & speed_divisor=1)1005 static void adjust_ramped_keyboard_field(float& keydown_time, ubyte& state, fix& time, const float& sensitivity, const int& speed_factor, const int& speed_divisor = 1)
1006 #define adjust_ramped_keyboard_field(F, M, ...) \
1007 (adjust_ramped_keyboard_field<F>(Controls.down_time.M, Controls.state.M, __VA_ARGS__))
1008 {
1009 if (state)
1010 {
1011 if (keydown_time < F1_0)
1012 keydown_time += (static_cast<float>(FrameTime) * 6.66) * ((sensitivity + 1) / 17); // values based on observation that the original game uses a keyboard ramp of 8 frames. Full sensitivity should reflect 60FPS behaviour, half sensitivity reflects 30FPS behaviour (give or take a frame).
1013 time = F<fix>()(time, speed_factor / speed_divisor * (keydown_time / F1_0));
1014 }
1015 else
1016 keydown_time = 0;
1017 }
1018
1019 template <std::size_t N>
adjust_axis_field(fix & time,const std::array<fix,N> & axes,unsigned value,unsigned invert,const int & sensitivity)1020 static void adjust_axis_field(fix& time, const std::array<fix, N> &axes, unsigned value, unsigned invert, const int& sensitivity)
1021 {
1022 if (value == 255)
1023 return;
1024 fix amount = (axes.at(value) * sensitivity) / 8;
1025 if ( !invert ) // If not inverted...
1026 time -= amount;
1027 else
1028 time += amount;
1029 }
1030
clamp_value(fix & value,const fix & lower,const fix & upper)1031 static void clamp_value(fix& value, const fix& lower, const fix& upper)
1032 {
1033 value = min(max(value, lower), upper);
1034 }
1035
clamp_symmetric_value(fix & value,const fix & bound)1036 static void clamp_symmetric_value(fix& value, const fix& bound)
1037 {
1038 clamp_value(value, -bound, bound);
1039 }
1040
1041 #if DXX_MAX_AXES_PER_JOYSTICK
convert_raw_joy_axis(control_info::joystick_axis_values & Controls,const uint_fast32_t player_cfg_index,const uint_fast32_t i)1042 static void convert_raw_joy_axis(control_info::joystick_axis_values &Controls, const uint_fast32_t player_cfg_index, const uint_fast32_t i)
1043 {
1044 const auto raw_joy_axis = Controls.raw_joy_axis[i];
1045 const auto joy_axis = (abs(raw_joy_axis) <= (128 * PlayerCfg.JoystickLinear[player_cfg_index]) / 16)
1046 ? (raw_joy_axis * (FrameTime * PlayerCfg.JoystickSpeed[player_cfg_index]) / 16)
1047 : (raw_joy_axis * FrameTime);
1048 Controls.joy_axis[i] = joy_axis / 128;
1049 }
1050
convert_raw_joy_axis(control_info::joystick_axis_values & Controls,const dxx_kconfig_ui_kc_joystick kcm_index,const uint_fast32_t player_cfg_index,const uint_fast32_t i)1051 static void convert_raw_joy_axis(control_info::joystick_axis_values &Controls, const dxx_kconfig_ui_kc_joystick kcm_index, const uint_fast32_t player_cfg_index, const uint_fast32_t i)
1052 {
1053 if (i != kcm_joystick[kcm_index].value)
1054 return;
1055 convert_raw_joy_axis(Controls, player_cfg_index, i);
1056 }
1057 #endif
1058
adjust_button_time(fix & o,uint8_t add,uint8_t sub,fix v)1059 static inline void adjust_button_time(fix &o, uint8_t add, uint8_t sub, fix v)
1060 {
1061 if (add)
1062 {
1063 if (sub)
1064 return;
1065 o += v;
1066 }
1067 else if (sub)
1068 o -= v;
1069 }
1070
clamp_kconfig_control_with_overrun(fix & value,const fix & bound,fix & excess,const fix & ebound)1071 static void clamp_kconfig_control_with_overrun(fix &value, const fix &bound, fix &excess, const fix &ebound)
1072 {
1073 /* Assume no integer overflow here */
1074 value += excess;
1075 const auto ivalue = value;
1076 clamp_symmetric_value(value, bound);
1077 excess = ivalue - value;
1078 clamp_symmetric_value(excess, ebound);
1079 }
1080
allow_uncapped_turning()1081 static unsigned allow_uncapped_turning()
1082 {
1083 const auto game_mode = Game_mode;
1084 if (!(game_mode & GM_MULTI))
1085 return PlayerCfg.MouselookFlags & MouselookMode::Singleplayer;
1086 return PlayerCfg.MouselookFlags &
1087 Netgame.MouselookFlags &
1088 ((game_mode & GM_MULTI_COOP)
1089 ? MouselookMode::MPCoop
1090 : MouselookMode::MPAnarchy);
1091 }
1092
1093 }
1094
1095 }
1096
1097 namespace dsx {
1098
kconfig_read_controls(control_info & Controls,const d_event & event,int automap_flag)1099 void kconfig_read_controls(control_info &Controls, const d_event &event, int automap_flag)
1100 {
1101 static fix64 mouse_delta_time = 0;
1102
1103 #ifndef NDEBUG
1104 // --- Don't do anything if in debug mode ---
1105 if ( keyd_pressed[KEY_DELETE] )
1106 {
1107 Controls = {};
1108 return;
1109 }
1110 #endif
1111
1112 const auto frametime = FrameTime;
1113
1114 switch (event.type)
1115 {
1116 case EVENT_KEY_COMMAND:
1117 case EVENT_KEY_RELEASE:
1118 {
1119 const auto &&key = event_key_get_raw(event);
1120 if (key < 255)
1121 {
1122 for (auto &&[kc, kcm] : zip(kc_keyboard, kcm_keyboard))
1123 {
1124 if (kcm.value == key)
1125 input_button_matched(Controls, kc, event.type == EVENT_KEY_COMMAND);
1126 }
1127 if (!automap_flag && event.type == EVENT_KEY_COMMAND)
1128 for (uint_fast32_t i = 0, j = 0; i < 28; i += 3, j++)
1129 if (kcm_rebirth[i].value == key)
1130 {
1131 Controls.state.select_weapon = j+1;
1132 break;
1133 }
1134 }
1135 }
1136 break;
1137 #if DXX_MAX_BUTTONS_PER_JOYSTICK
1138 case EVENT_JOYSTICK_BUTTON_DOWN:
1139 case EVENT_JOYSTICK_BUTTON_UP:
1140 if (!(PlayerCfg.ControlType & CONTROL_USING_JOYSTICK))
1141 break;
1142 {
1143 const auto &&button = event_joystick_get_button(event);
1144 if (button < 255)
1145 {
1146 for (auto &&[kc, kcm] : zip(kc_joystick, kcm_joystick))
1147 {
1148 if (kc.type == BT_JOY_BUTTON && kcm.value == button)
1149 input_button_matched(Controls, kc, event.type == EVENT_JOYSTICK_BUTTON_DOWN);
1150 }
1151 if (!automap_flag && event.type == EVENT_JOYSTICK_BUTTON_DOWN)
1152 for (uint_fast32_t i = 1, j = 0; i < 29; i += 3, j++)
1153 if (kcm_rebirth[i].value == button)
1154 {
1155 Controls.state.select_weapon = j+1;
1156 break;
1157 }
1158 }
1159 break;
1160 }
1161 #endif
1162 case EVENT_MOUSE_BUTTON_DOWN:
1163 case EVENT_MOUSE_BUTTON_UP:
1164 if (!(PlayerCfg.ControlType & CONTROL_USING_MOUSE))
1165 break;
1166 {
1167 const auto &&button = event_mouse_get_button(event);
1168 if (button < 255)
1169 {
1170 for (auto &&[kc, kcm] : zip(kc_mouse, kcm_mouse))
1171 {
1172 if (kc.type == BT_MOUSE_BUTTON && kcm.value == button)
1173 input_button_matched(Controls, kc, event.type == EVENT_MOUSE_BUTTON_DOWN);
1174 }
1175 if (!automap_flag && event.type == EVENT_MOUSE_BUTTON_DOWN)
1176 for (uint_fast32_t i = 2, j = 0; i < 30; i += 3, j++)
1177 if (kcm_rebirth[i].value == button)
1178 {
1179 Controls.state.select_weapon = j+1;
1180 break;
1181 }
1182 }
1183 }
1184 break;
1185 #if DXX_MAX_AXES_PER_JOYSTICK
1186 case EVENT_JOYSTICK_MOVED:
1187 {
1188 int joy_null_value = 0;
1189 if (!(PlayerCfg.ControlType & CONTROL_USING_JOYSTICK))
1190 break;
1191 const auto &av = event_joystick_get_axis(event);
1192 const auto &axis = av.axis;
1193 auto value = av.value;
1194
1195 if (axis == PlayerCfg.KeySettings.Joystick[dxx_kconfig_ui_kc_joystick_pitch]) // Pitch U/D Deadzone
1196 joy_null_value = PlayerCfg.JoystickDead[1]*8;
1197 else if (axis == PlayerCfg.KeySettings.Joystick[dxx_kconfig_ui_kc_joystick_turn]) // Turn L/R Deadzone
1198 joy_null_value = PlayerCfg.JoystickDead[0]*8;
1199 else if (axis == PlayerCfg.KeySettings.Joystick[dxx_kconfig_ui_kc_joystick_slide_lr]) // Slide L/R Deadzone
1200 joy_null_value = PlayerCfg.JoystickDead[2]*8;
1201 else if (axis == PlayerCfg.KeySettings.Joystick[dxx_kconfig_ui_kc_joystick_slide_ud]) // Slide U/D Deadzone
1202 joy_null_value = PlayerCfg.JoystickDead[3]*8;
1203 #ifdef dxx_kconfig_ui_kc_joystick_bank
1204 else if (axis == PlayerCfg.KeySettings.Joystick[dxx_kconfig_ui_kc_joystick_bank]) // Bank Deadzone
1205 joy_null_value = PlayerCfg.JoystickDead[4]*8;
1206 #endif
1207 #ifdef dxx_kconfig_ui_kc_joystick_throttle
1208 else if (axis == PlayerCfg.KeySettings.Joystick[dxx_kconfig_ui_kc_joystick_throttle]) // Throttle - default deadzone
1209 joy_null_value = PlayerCfg.JoystickDead[5]*3;
1210 #endif
1211
1212 Controls.raw_joy_axis[axis] = apply_deadzone(value, joy_null_value);
1213 break;
1214 }
1215 #endif
1216 case EVENT_MOUSE_MOVED:
1217 {
1218 if (!(PlayerCfg.ControlType & CONTROL_USING_MOUSE))
1219 break;
1220 if (PlayerCfg.MouseFlightSim)
1221 {
1222 int ax[3];
1223 event_mouse_get_delta( event, &ax[0], &ax[1], &ax[2] );
1224 for (uint_fast32_t i = 0; i <= 2; i++)
1225 {
1226 int mouse_null_value = (i==2?16:PlayerCfg.MouseFSDead*8);
1227 Controls.raw_mouse_axis[i] += ax[i];
1228 clamp_symmetric_value(Controls.raw_mouse_axis[i], MOUSEFS_DELTA_RANGE);
1229 if (Controls.raw_mouse_axis[i] > mouse_null_value)
1230 Controls.mouse_axis[i] = (((Controls.raw_mouse_axis[i] - mouse_null_value) * MOUSEFS_DELTA_RANGE) / (MOUSEFS_DELTA_RANGE - mouse_null_value) * frametime) / MOUSEFS_DELTA_RANGE;
1231 else if (Controls.raw_mouse_axis[i] < -mouse_null_value)
1232 Controls.mouse_axis[i] = (((Controls.raw_mouse_axis[i] + mouse_null_value) * MOUSEFS_DELTA_RANGE) / (MOUSEFS_DELTA_RANGE - mouse_null_value) * frametime) / MOUSEFS_DELTA_RANGE;
1233 else
1234 Controls.mouse_axis[i] = 0;
1235 }
1236 }
1237 else
1238 {
1239 event_mouse_get_delta( event, &Controls.raw_mouse_axis[0], &Controls.raw_mouse_axis[1], &Controls.raw_mouse_axis[2] );
1240 Controls.mouse_axis[0] = (Controls.raw_mouse_axis[0] * frametime) / 8;
1241 Controls.mouse_axis[1] = (Controls.raw_mouse_axis[1] * frametime) / 8;
1242 Controls.mouse_axis[2] = (Controls.raw_mouse_axis[2] * frametime);
1243 mouse_delta_time = timer_query() + DESIGNATED_GAME_FRAMETIME;
1244 }
1245 break;
1246 }
1247 case EVENT_IDLE:
1248 default:
1249 if (!PlayerCfg.MouseFlightSim && mouse_delta_time < timer_query())
1250 {
1251 Controls.mouse_axis[0] = Controls.mouse_axis[1] = Controls.mouse_axis[2] = 0;
1252 mouse_delta_time = timer_query() + DESIGNATED_GAME_FRAMETIME;
1253 }
1254 break;
1255 }
1256
1257 #if DXX_MAX_AXES_PER_JOYSTICK
1258 for (int i = 0; i < JOY_MAX_AXES; i++) {
1259 convert_raw_joy_axis(Controls, dxx_kconfig_ui_kc_joystick_turn, 0, i); // Turn L/R
1260 convert_raw_joy_axis(Controls, dxx_kconfig_ui_kc_joystick_pitch, 1, i); // Pitch U/D
1261 convert_raw_joy_axis(Controls, dxx_kconfig_ui_kc_joystick_slide_lr, 2, i); // Slide L/R
1262 convert_raw_joy_axis(Controls, dxx_kconfig_ui_kc_joystick_slide_ud, 3, i); // Slide U/D
1263 convert_raw_joy_axis(Controls, dxx_kconfig_ui_kc_joystick_bank, 4, i); // Bank
1264 convert_raw_joy_axis(Controls, dxx_kconfig_ui_kc_joystick_throttle, 5, i); // Throttle
1265 }
1266 #endif
1267
1268 const auto speed_factor = (cheats.turbo ? 2 : 1) * frametime;
1269
1270 //------------ Read pitch_time -----------
1271 if ( !Controls.state.slide_on )
1272 {
1273 // From keyboard...
1274 adjust_ramped_keyboard_field(plus, key_pitch_forward, Controls.pitch_time, PlayerCfg.KeyboardSens[1], speed_factor, 2);
1275 adjust_ramped_keyboard_field(minus, key_pitch_backward, Controls.pitch_time, PlayerCfg.KeyboardSens[1], speed_factor, 2);
1276 // From joystick...
1277 #ifdef dxx_kconfig_ui_kc_joystick_pitch
1278 adjust_axis_field(Controls.pitch_time, Controls.joy_axis, kcm_joystick[dxx_kconfig_ui_kc_joystick_pitch].value, kcm_joystick[dxx_kconfig_ui_kc_joystick_invert_pitch].value, PlayerCfg.JoystickSens[1]);
1279 #endif
1280 // From mouse...
1281 #ifdef dxx_kconfig_ui_kc_mouse_pitch
1282 adjust_axis_field(Controls.pitch_time, Controls.mouse_axis, kcm_mouse[dxx_kconfig_ui_kc_mouse_pitch].value, kcm_mouse[dxx_kconfig_ui_kc_mouse_invert_pitch].value, PlayerCfg.MouseSens[1]);
1283 #endif
1284 }
1285 else Controls.pitch_time = 0;
1286
1287
1288 //----------- Read vertical_thrust_time -----------------
1289 if ( Controls.state.slide_on )
1290 {
1291 // From keyboard...
1292 adjust_ramped_keyboard_field(plus, key_pitch_forward, Controls.vertical_thrust_time, PlayerCfg.KeyboardSens[3], speed_factor);
1293 adjust_ramped_keyboard_field(minus, key_pitch_backward, Controls.vertical_thrust_time, PlayerCfg.KeyboardSens[3], speed_factor);
1294 // From joystick...
1295 // NOTE: Use Slide U/D invert setting
1296 #if defined(dxx_kconfig_ui_kc_joystick_pitch) && defined(dxx_kconfig_ui_kc_joystick_slide_ud)
1297 adjust_axis_field(Controls.vertical_thrust_time, Controls.joy_axis, kcm_joystick[dxx_kconfig_ui_kc_joystick_pitch].value, !kcm_joystick[dxx_kconfig_ui_kc_joystick_invert_slide_ud].value, PlayerCfg.JoystickSens[3]);
1298 #endif
1299 // From mouse...
1300 adjust_axis_field(Controls.vertical_thrust_time, Controls.mouse_axis, kcm_mouse[dxx_kconfig_ui_kc_mouse_pitch].value, kcm_mouse[dxx_kconfig_ui_kc_mouse_invert_slide_ud].value, PlayerCfg.MouseSens[3]);
1301 }
1302 // From keyboard...
1303 adjust_ramped_keyboard_field(plus, key_slide_up, Controls.vertical_thrust_time, PlayerCfg.KeyboardSens[3], speed_factor);
1304 adjust_ramped_keyboard_field(minus, key_slide_down, Controls.vertical_thrust_time, PlayerCfg.KeyboardSens[3], speed_factor);
1305 // From buttons...
1306 adjust_button_time(Controls.vertical_thrust_time, Controls.state.btn_slide_up, Controls.state.btn_slide_down, speed_factor);
1307 // From joystick...
1308 #ifdef dxx_kconfig_ui_kc_joystick_slide_ud
1309 adjust_axis_field(Controls.vertical_thrust_time, Controls.joy_axis, kcm_joystick[dxx_kconfig_ui_kc_joystick_slide_ud].value, !kcm_joystick[dxx_kconfig_ui_kc_joystick_invert_slide_ud].value, PlayerCfg.JoystickSens[3]);
1310 #endif
1311 // From mouse...
1312 adjust_axis_field(Controls.vertical_thrust_time, Controls.mouse_axis, kcm_mouse[dxx_kconfig_ui_kc_mouse_slide_ud].value, !kcm_mouse[dxx_kconfig_ui_kc_mouse_invert_slide_ud].value, PlayerCfg.MouseSens[3]);
1313
1314 //---------- Read heading_time -----------
1315 if (!Controls.state.slide_on && !Controls.state.bank_on)
1316 {
1317 // From keyboard...
1318 adjust_ramped_keyboard_field(plus, key_heading_right, Controls.heading_time, PlayerCfg.KeyboardSens[0], speed_factor);
1319 adjust_ramped_keyboard_field(minus, key_heading_left, Controls.heading_time, PlayerCfg.KeyboardSens[0], speed_factor);
1320 // From joystick...
1321 #ifdef dxx_kconfig_ui_kc_joystick_turn
1322 adjust_axis_field(Controls.heading_time, Controls.joy_axis, kcm_joystick[dxx_kconfig_ui_kc_joystick_turn].value, !kcm_joystick[dxx_kconfig_ui_kc_joystick_invert_turn].value, PlayerCfg.JoystickSens[0]);
1323 #endif
1324 // From mouse...
1325 adjust_axis_field(Controls.heading_time, Controls.mouse_axis, kcm_mouse[dxx_kconfig_ui_kc_mouse_turn].value, !kcm_mouse[dxx_kconfig_ui_kc_mouse_invert_turn].value, PlayerCfg.MouseSens[0]);
1326 }
1327 else Controls.heading_time = 0;
1328
1329 //----------- Read sideways_thrust_time -----------------
1330 if ( Controls.state.slide_on )
1331 {
1332 // From keyboard...
1333 adjust_ramped_keyboard_field(plus, key_heading_right, Controls.sideways_thrust_time, PlayerCfg.KeyboardSens[2], speed_factor);
1334 adjust_ramped_keyboard_field(minus, key_heading_left, Controls.sideways_thrust_time, PlayerCfg.KeyboardSens[2], speed_factor);
1335 // From joystick...
1336 #if defined(dxx_kconfig_ui_kc_joystick_turn) && defined(dxx_kconfig_ui_kc_joystick_slide_lr)
1337 adjust_axis_field(Controls.sideways_thrust_time, Controls.joy_axis, kcm_joystick[dxx_kconfig_ui_kc_joystick_turn].value, !kcm_joystick[dxx_kconfig_ui_kc_joystick_invert_slide_lr].value, PlayerCfg.JoystickSens[2]);
1338 #endif
1339 // From mouse...
1340 adjust_axis_field(Controls.sideways_thrust_time, Controls.mouse_axis, kcm_mouse[dxx_kconfig_ui_kc_mouse_turn].value, !kcm_mouse[dxx_kconfig_ui_kc_mouse_invert_slide_lr].value, PlayerCfg.MouseSens[2]);
1341 }
1342 // From keyboard...
1343 adjust_ramped_keyboard_field(plus, key_slide_right, Controls.sideways_thrust_time, PlayerCfg.KeyboardSens[2], speed_factor);
1344 adjust_ramped_keyboard_field(minus, key_slide_left, Controls.sideways_thrust_time, PlayerCfg.KeyboardSens[2], speed_factor);
1345 // From buttons...
1346 adjust_button_time(Controls.sideways_thrust_time, Controls.state.btn_slide_right, Controls.state.btn_slide_left, speed_factor);
1347 // From joystick...
1348 #ifdef dxx_kconfig_ui_kc_joystick_slide_lr
1349 adjust_axis_field(Controls.sideways_thrust_time, Controls.joy_axis, kcm_joystick[dxx_kconfig_ui_kc_joystick_slide_lr].value, !kcm_joystick[dxx_kconfig_ui_kc_joystick_invert_slide_lr].value, PlayerCfg.JoystickSens[2]);
1350 #endif
1351 // From mouse...
1352 adjust_axis_field(Controls.sideways_thrust_time, Controls.mouse_axis, kcm_mouse[dxx_kconfig_ui_kc_mouse_slide_lr].value, !kcm_mouse[dxx_kconfig_ui_kc_mouse_invert_slide_lr].value, PlayerCfg.MouseSens[2]);
1353
1354 //----------- Read bank_time -----------------
1355 if ( Controls.state.bank_on )
1356 {
1357 // From keyboard...
1358 adjust_ramped_keyboard_field(plus, key_heading_left, Controls.bank_time, PlayerCfg.KeyboardSens[4], speed_factor);
1359 adjust_ramped_keyboard_field(minus, key_heading_right, Controls.bank_time, PlayerCfg.KeyboardSens[4], speed_factor);
1360 // From joystick...
1361 #if defined(dxx_kconfig_ui_kc_joystick_turn) && defined(dxx_kconfig_ui_kc_joystick_bank)
1362 adjust_axis_field(Controls.bank_time, Controls.joy_axis, kcm_joystick[dxx_kconfig_ui_kc_joystick_turn].value, kcm_joystick[dxx_kconfig_ui_kc_joystick_invert_bank].value, PlayerCfg.JoystickSens[4]);
1363 #endif
1364 // From mouse...
1365 adjust_axis_field(Controls.bank_time, Controls.mouse_axis, kcm_mouse[dxx_kconfig_ui_kc_mouse_turn].value, !kcm_mouse[dxx_kconfig_ui_kc_mouse_invert_bank].value, PlayerCfg.MouseSens[4]);
1366 }
1367 // From keyboard...
1368 adjust_ramped_keyboard_field(plus, key_bank_left, Controls.bank_time, PlayerCfg.KeyboardSens[4], speed_factor);
1369 adjust_ramped_keyboard_field(minus, key_bank_right, Controls.bank_time, PlayerCfg.KeyboardSens[4], speed_factor);
1370 // From buttons...
1371 adjust_button_time(Controls.bank_time, Controls.state.btn_bank_left, Controls.state.btn_bank_right, speed_factor);
1372 // From joystick...
1373 #ifdef dxx_kconfig_ui_kc_joystick_bank
1374 adjust_axis_field(Controls.bank_time, Controls.joy_axis, kcm_joystick[dxx_kconfig_ui_kc_joystick_bank].value, kcm_joystick[dxx_kconfig_ui_kc_joystick_invert_bank].value, PlayerCfg.JoystickSens[4]);
1375 #endif
1376 // From mouse...
1377 adjust_axis_field(Controls.bank_time, Controls.mouse_axis, kcm_mouse[dxx_kconfig_ui_kc_mouse_bank].value, !kcm_mouse[dxx_kconfig_ui_kc_mouse_invert_bank].value, PlayerCfg.MouseSens[4]);
1378
1379 //----------- Read forward_thrust_time -------------
1380 // From keyboard/buttons...
1381 adjust_button_time(Controls.forward_thrust_time, Controls.state.accelerate, Controls.state.reverse, speed_factor);
1382 // From joystick...
1383 #ifdef dxx_kconfig_ui_kc_joystick_throttle
1384 adjust_axis_field(Controls.forward_thrust_time, Controls.joy_axis, kcm_joystick[dxx_kconfig_ui_kc_joystick_throttle].value, kcm_joystick[dxx_kconfig_ui_kc_joystick_invert_throttle].value, PlayerCfg.JoystickSens[5]);
1385 #endif
1386 // From mouse...
1387 adjust_axis_field(Controls.forward_thrust_time, Controls.mouse_axis, kcm_mouse[dxx_kconfig_ui_kc_mouse_throttle].value, kcm_mouse[dxx_kconfig_ui_kc_mouse_invert_throttle].value, PlayerCfg.MouseSens[5]);
1388
1389 //----------- Read cruise-control-type of throttle.
1390 if ( Controls.state.cruise_off > 0 ) Controls.state.cruise_off = Cruise_speed = 0;
1391 else
1392 {
1393 adjust_button_time(Cruise_speed, Controls.state.cruise_plus, Controls.state.cruise_minus, speed_factor * 80);
1394 clamp_value(Cruise_speed, 0, i2f(100));
1395 if (Controls.forward_thrust_time == 0)
1396 Controls.forward_thrust_time = fixmul(Cruise_speed, frametime) / 100;
1397 }
1398
1399 //----------- Clamp values between -FrameTime and FrameTime
1400 clamp_kconfig_control_with_overrun(Controls.vertical_thrust_time, frametime, Controls.excess_vertical_thrust_time, frametime * PlayerCfg.MouseOverrun[3]);
1401 clamp_kconfig_control_with_overrun(Controls.sideways_thrust_time, frametime, Controls.excess_sideways_thrust_time, frametime * PlayerCfg.MouseOverrun[2]);
1402 clamp_kconfig_control_with_overrun(Controls.forward_thrust_time, frametime, Controls.excess_forward_thrust_time, frametime * PlayerCfg.MouseOverrun[5]);
1403 if (!allow_uncapped_turning())
1404 {
1405 clamp_kconfig_control_with_overrun(Controls.pitch_time, frametime / 2, Controls.excess_pitch_time, frametime * PlayerCfg.MouseOverrun[1]);
1406 clamp_kconfig_control_with_overrun(Controls.heading_time, frametime, Controls.excess_heading_time, frametime * PlayerCfg.MouseOverrun[0]);
1407 clamp_kconfig_control_with_overrun(Controls.bank_time, frametime, Controls.excess_bank_time, frametime * PlayerCfg.MouseOverrun[4]);
1408 }
1409 }
1410
1411 }
1412
reset_cruise(void)1413 void reset_cruise(void)
1414 {
1415 Cruise_speed=0;
1416 }
1417
1418
kc_set_controls()1419 void kc_set_controls()
1420 {
1421 for (auto &&[kcm, setting] : zip(kcm_keyboard, PlayerCfg.KeySettings.Keyboard))
1422 kcm.oldvalue = kcm.value = setting;
1423
1424 #if DXX_MAX_JOYSTICKS
1425 for (auto &&[kcm, kc, setting] : zip(kcm_joystick, kc_joystick, PlayerCfg.KeySettings.Joystick))
1426 {
1427 uint8_t value = setting;
1428 if (kc.type == BT_INVERT)
1429 {
1430 if (value != 1)
1431 value = 0;
1432 setting = value;
1433 }
1434 kcm.oldvalue = kcm.value = value;
1435 }
1436 #endif
1437
1438 for (auto &&[kcm, kc, setting] : zip(kcm_mouse, kc_mouse, PlayerCfg.KeySettings.Mouse))
1439 {
1440 uint8_t value = setting;
1441 if (kc.type == BT_INVERT)
1442 {
1443 if (value != 1)
1444 value = 0;
1445 setting = value;
1446 }
1447 kcm.oldvalue = kcm.value = value;
1448 }
1449
1450 for (auto &&[kcm, setting] : zip(kcm_rebirth, PlayerCfg.KeySettingsRebirth))
1451 kcm.oldvalue = kcm.value = setting;
1452 }
1453