Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

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