Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 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
}