1 | #include <stdlib.h>
|
---|
2 | #include <stdarg.h>
|
---|
3 |
|
---|
4 | #include "../Daodan.h"
|
---|
5 | #include "Input.h"
|
---|
6 | #include "../Oni/Oni.h"
|
---|
7 | #include "../Daodan_Config.h"
|
---|
8 | #include "../Daodan_Patch.h"
|
---|
9 | #include "Utility.h"
|
---|
10 |
|
---|
11 | typedef struct {
|
---|
12 | LItActionDescription descr;
|
---|
13 | DDtActionEventType eventType;
|
---|
14 |
|
---|
15 | DDtCustomActionCallback callback;
|
---|
16 | intptr_t ctx;
|
---|
17 | } DDtCustomAction;
|
---|
18 |
|
---|
19 | static DDtCustomAction DDgCustomActions[100] = { 0 };
|
---|
20 |
|
---|
21 | // Extra keys (make sure these don't collide with Oni's LIc_* keys)
|
---|
22 | enum {
|
---|
23 | DDcKey_MouseButton5 = LIcKey_Max,
|
---|
24 | DDcKey_ScrollUp,
|
---|
25 | DDcKey_ScrollDown,
|
---|
26 | DDcKey_ScrollLeft,
|
---|
27 | DDcKey_ScrollRight,
|
---|
28 | };
|
---|
29 |
|
---|
30 | // Enhanced version of LIgInputNames from Oni with some extra keys
|
---|
31 | static const LItInputName DDgInputNames[] = {
|
---|
32 | // The following key names are mapped in Oni
|
---|
33 | {"fkey1", LIcKey_FKey1}, {"fkey2", LIcKey_FKey2}, {"fkey3", LIcKey_FKey3},
|
---|
34 | {"fkey4", LIcKey_FKey4}, {"fkey5", LIcKey_FKey5}, {"fkey6", LIcKey_FKey6},
|
---|
35 | {"fkey7", LIcKey_FKey7}, {"fkey8", LIcKey_FKey8}, {"fkey9", LIcKey_FKey9},
|
---|
36 | {"fkey10", LIcKey_FKey10}, {"fkey11", LIcKey_FKey11},
|
---|
37 | {"fkey12", LIcKey_FKey12}, {"fkey13", LIcKey_FKey13},
|
---|
38 | {"fkey14", LIcKey_FKey14}, {"fkey15", LIcKey_FKey15},
|
---|
39 | {"backspace", LIcKey_Backspace}, {"tab", LIcKey_Tab},
|
---|
40 | {"capslock", LIcKey_CapsLock}, {"enter", LIcKey_Enter},
|
---|
41 | {"leftshift", LIcKey_LeftShift}, {"rightshift", LIcKey_RightShift},
|
---|
42 | {"leftcontrol", LIcKey_LeftControl},
|
---|
43 | {"leftwindows", 0x94}, {"leftoption", 0x94}, // Does nothing in Oni
|
---|
44 | {"leftalt", LIcKey_LeftAlt}, {"space", ' '}, {"rightalt", LIcKey_RightAlt},
|
---|
45 | {"rightoption", 0x97}, {"rightwindows", 0x97}, // Does nothing in Oni
|
---|
46 | {"rightcontrol", LIcKey_RightControl}, {"printscreen", LIcKey_PrintScreen},
|
---|
47 | {"scrolllock", LIcKey_ScrollLock}, {"pause", LIcKey_Pause},
|
---|
48 | {"insert", LIcKey_Insert}, {"home", LIcKey_Home}, {"pageup", LIcKey_PageUp},
|
---|
49 | {"delete", LIcKey_Delete}, {"end", LIcKey_End},
|
---|
50 | {"pagedown", LIcKey_PageDown}, {"uparrow", LIcKey_UpArrow},
|
---|
51 | {"leftarrow", LIcKey_LeftArrow}, {"downarrow", LIcKey_DownArrow},
|
---|
52 | {"rightarrow", LIcKey_RightArrow}, {"numlock", LIcKey_NumLock},
|
---|
53 | {"divide", LIcKey_Divide}, {"multiply", LIcKey_Multiply},
|
---|
54 | {"subtract", LIcKey_Subtract}, {"add", LIcKey_Add},
|
---|
55 | {"numpadequals", LIcKey_NumpadEquals}, {"numpadenter", LIcKey_NumpadEnter},
|
---|
56 | {"decimal", LIcKey_Decimal}, {"numpad0", LIcKey_Numpad0},
|
---|
57 | {"numpad1", LIcKey_Numpad1}, {"numpad2", LIcKey_Numpad2},
|
---|
58 | {"numpad3", LIcKey_Numpad3}, {"numpad4", LIcKey_Numpad4},
|
---|
59 | {"numpad5", LIcKey_Numpad5}, {"numpad6", LIcKey_Numpad6},
|
---|
60 | {"numpad7", LIcKey_Numpad7}, {"numpad8", LIcKey_Numpad8},
|
---|
61 | {"numpad9", LIcKey_Numpad9}, {"backslash", '\\'}, {"semicolon", ';'},
|
---|
62 | {"period", '.'}, {"apostrophe", '\''}, {"slash", '/'}, {"leftbracket", '['},
|
---|
63 | {"rightbracket", ']'}, {"comma", ','},
|
---|
64 | {"mousebutton1", LIcKey_MouseButton1},
|
---|
65 | {"mousebutton2", LIcKey_MouseButton2},
|
---|
66 | {"mousebutton3", LIcKey_MouseButton3},
|
---|
67 | {"mousebutton4", LIcKey_MouseButton4},
|
---|
68 | {"mousexaxis", LIcKey_MouseXAxis}, {"mouseyaxis", LIcKey_MouseYAxis},
|
---|
69 | {"mousezaxis", LIcKey_MouseZAxis},
|
---|
70 |
|
---|
71 | // Extra keys for Daodan Input
|
---|
72 | {"mousebutton5", DDcKey_MouseButton5},
|
---|
73 | {"scrollup", DDcKey_ScrollUp},
|
---|
74 | {"scrolldown", DDcKey_ScrollDown},
|
---|
75 | {"scrollleft", DDcKey_ScrollLeft},
|
---|
76 | {"scrollright", DDcKey_ScrollRight},
|
---|
77 |
|
---|
78 | {"", 0}
|
---|
79 | };
|
---|
80 |
|
---|
81 | // Enhanced version of LIgPlatform_ScanCodeToChar from Oni
|
---|
82 | static const uint8_t DDgPlatform_ScanCodeToChar[256] = {
|
---|
83 | // The following scan codes are mapped in Oni
|
---|
84 | [0x01] = LIcKey_Escape, [0x02] = '1', [0x03] = '2', [0x04] = '3',
|
---|
85 | [0x05] = '4', [0x06] = '5', [0x07] = '6', [0x08] = '7', [0x09] = '8',
|
---|
86 | [0x0a] = '9', [0x0b] = '0', [0x0c] = '-', [0x0d] = '=',
|
---|
87 | [0x0e] = LIcKey_Backspace, [0x0f] = LIcKey_Tab, [0x10] = 'q', [0x11] = 'w',
|
---|
88 | [0x12] = 'e', [0x13] = 'r', [0x14] = 't', [0x15] = 'y', [0x16] = 'u',
|
---|
89 | [0x17] = 'i', [0x18] = 'o', [0x19] = 'p', [0x1a] = '[', [0x1b] = ']',
|
---|
90 | [0x1c] = LIcKey_Enter, [0x1d] = LIcKey_LeftControl, [0x1e] = 'a',
|
---|
91 | [0x1f] = 's', [0x20] = 'd', [0x21] = 'f', [0x22] = 'g', [0x23] = 'h',
|
---|
92 | [0x24] = 'j', [0x25] = 'k', [0x26] = 'l', [0x27] = ';', [0x28] = '\'',
|
---|
93 | [0x29] = '`', [0x2a] = LIcKey_LeftShift, [0x2b] = '\\', [0x2c] = 'z',
|
---|
94 | [0x2d] = 'x', [0x2e] = 'c', [0x2f] = 'v', [0x30] = 'b', [0x31] = 'n',
|
---|
95 | [0x32] = 'm', [0x33] = ',', [0x34] = '.', [0x35] = '/',
|
---|
96 | [0x36] = LIcKey_RightShift, [0x37] = LIcKey_Multiply,
|
---|
97 | [0x38] = LIcKey_LeftAlt, [0x39] = ' ', [0x3a] = LIcKey_CapsLock,
|
---|
98 | [0x3b] = LIcKey_FKey1, [0x3c] = LIcKey_FKey2, [0x3d] = LIcKey_FKey3,
|
---|
99 | [0x3e] = LIcKey_FKey4, [0x3f] = LIcKey_FKey5, [0x40] = LIcKey_FKey6,
|
---|
100 | [0x41] = LIcKey_FKey7, [0x42] = LIcKey_FKey8, [0x43] = LIcKey_FKey9,
|
---|
101 | [0x44] = LIcKey_FKey10, [0x45] = LIcKey_NumLock, [0x46] = LIcKey_ScrollLock,
|
---|
102 | [0x47] = LIcKey_Numpad7, [0x48] = LIcKey_Numpad8, [0x49] = LIcKey_Numpad9,
|
---|
103 | [0x4a] = LIcKey_Subtract, [0x4b] = LIcKey_Numpad4, [0x4c] = LIcKey_Numpad5,
|
---|
104 | [0x4d] = LIcKey_Numpad6, [0x4e] = LIcKey_Add, [0x4f] = LIcKey_Numpad1,
|
---|
105 | [0x50] = LIcKey_Numpad2, [0x51] = LIcKey_Numpad3, [0x52] = LIcKey_Numpad0,
|
---|
106 | [0x53] = LIcKey_Decimal, [0x57] = LIcKey_FKey11, [0x58] = LIcKey_FKey12,
|
---|
107 | [0x64] = LIcKey_FKey13, [0x65] = LIcKey_FKey14, [0x66] = LIcKey_FKey15,
|
---|
108 | [0x8d] = LIcKey_NumpadEquals, [0x9c] = LIcKey_NumpadEnter,
|
---|
109 | [0x9d] = LIcKey_RightControl, [0xb3] = LIcKey_NumpadComma,
|
---|
110 | [0xb5] = LIcKey_Divide, [0xb8] = LIcKey_RightAlt, [0xc7] = LIcKey_Home,
|
---|
111 | [0xc8] = LIcKey_UpArrow, [0xc9] = LIcKey_PageUp, [0xcb] = LIcKey_LeftArrow,
|
---|
112 | [0xcd] = LIcKey_RightArrow, [0xcf] = LIcKey_End, [0xd0] = LIcKey_DownArrow,
|
---|
113 | [0xd2] = LIcKey_Insert, [0xd3] = LIcKey_Delete, [0xdb] = LIcKey_LeftWindows,
|
---|
114 | [0xdc] = LIcKey_RightWindows, [0xdd] = LIcKey_Apps,
|
---|
115 |
|
---|
116 | // Missing in Oni
|
---|
117 | [0xd1] = LIcKey_PageDown,
|
---|
118 | };
|
---|
119 |
|
---|
120 | // Set in Patches.c if the Daodan input patches are applied. This just enables
|
---|
121 | // the windows message handling for now
|
---|
122 | bool DDgUseDaodanInput = false;
|
---|
123 |
|
---|
124 | // The Oni key codes that correspond to the togglable keys
|
---|
125 | static uint8_t DDgCapsOniKey = 0;
|
---|
126 | static uint8_t DDgScrollOniKey = 0;
|
---|
127 | static uint8_t DDgNumLockOniKey = 0;
|
---|
128 |
|
---|
129 | // Multiplier for mouse values
|
---|
130 | static float DDgMouseSensitivity = 1.0;
|
---|
131 |
|
---|
132 | // Accumulators for mouse scrolling. These are needed because some mice have
|
---|
133 | // continuous scroll wheels (not to mention touchpads.) We should only add an
|
---|
134 | // action to Oni's input if one of these accumulators exceeds +/-WHEEL_DELTA.
|
---|
135 | static int DDgWheelDelta_V = 0;
|
---|
136 | static int DDgWheelDelta_H = 0;
|
---|
137 |
|
---|
138 | // UUrMachineTime_High for the last update of the accumulators. Used so they can
|
---|
139 | // be reset after a period of no scroll events.
|
---|
140 | static int64_t DDgWheelDelta_Updated = 0;
|
---|
141 |
|
---|
142 | // Temporary action buffer that we build over the duration of a frame with the
|
---|
143 | // input we're going to send to the engine. This includes the accumulated
|
---|
144 | // movement of the mouse cursor and all actions (keyboard and mouse buttons)
|
---|
145 | // that were pressed this frame (but not held down from previous frames - that
|
---|
146 | // gets added later from DDgInputState.)
|
---|
147 | static LItActionBuffer DDgActionBuffer = { 0 };
|
---|
148 |
|
---|
149 | // Temporary buffer containing the current state of the keyboard and mouse
|
---|
150 | // buttons, that is, if they're being held now
|
---|
151 | static char DDgInputState[256] = { 0 };
|
---|
152 |
|
---|
153 | static short ONICALL DDrBinding_Add(int key, const char *name)
|
---|
154 | {
|
---|
155 | // First try to replace an existing binding for the same key
|
---|
156 | LItBinding *binding = NULL;
|
---|
157 | for (int i = 0; i < 100; i++) {
|
---|
158 | if (LIgBindingArray[i].key == key) {
|
---|
159 | binding = &LIgBindingArray[i];
|
---|
160 | break;
|
---|
161 | }
|
---|
162 | }
|
---|
163 |
|
---|
164 | // If there are no existing bindings for this key, find a free entry
|
---|
165 | if (!binding) {
|
---|
166 | for (int i = 0; i < 100; i++) {
|
---|
167 | if (!LIgBindingArray[i].key) {
|
---|
168 | binding = &LIgBindingArray[i];
|
---|
169 | break;
|
---|
170 | }
|
---|
171 | }
|
---|
172 | }
|
---|
173 | // No free entries, so give up
|
---|
174 | if (!binding)
|
---|
175 | return 2;
|
---|
176 |
|
---|
177 | // Now try to find the action to bind to. First check Oni's built-in list
|
---|
178 | // of actions.
|
---|
179 | LItActionDescription *descr = NULL;
|
---|
180 | for (int i = 0; LIgActionDescriptions[i].name[0]; i++) {
|
---|
181 | if (!UUrString_Compare_NoCase(name, LIgActionDescriptions[i].name)) {
|
---|
182 | descr = &LIgActionDescriptions[i];
|
---|
183 | break;
|
---|
184 | }
|
---|
185 | }
|
---|
186 |
|
---|
187 | // Next, try Daodan's list of custom actions
|
---|
188 | if (!descr) {
|
---|
189 | for (int i = 0; i < ARRAY_SIZE(DDgCustomActions); i++) {
|
---|
190 | if (!DDgCustomActions[i].descr.name[0])
|
---|
191 | break;
|
---|
192 |
|
---|
193 | if (!UUrString_Compare_NoCase(name, DDgCustomActions[i].descr.name)) {
|
---|
194 | descr = &DDgCustomActions[i].descr;
|
---|
195 | break;
|
---|
196 | }
|
---|
197 | }
|
---|
198 | }
|
---|
199 | if (!descr)
|
---|
200 | return 0;
|
---|
201 |
|
---|
202 | binding->key = key;
|
---|
203 | binding->descr = descr;
|
---|
204 | return 0;
|
---|
205 | }
|
---|
206 |
|
---|
207 | static void ONICALL DDrGameState_HandleUtilityInput(GameInput *input)
|
---|
208 | {
|
---|
209 | // Mac Oni 1.2.1 checks the cheat binds here, so we should too. Note that
|
---|
210 | // unlike Mac Oni, which hardcodes each cheat here, we use our flexible
|
---|
211 | // custom action system.
|
---|
212 | for (int i = 0; i < ARRAY_SIZE(DDgCustomActions); i++) {
|
---|
213 | if (!DDgCustomActions[i].descr.name[0])
|
---|
214 | break;
|
---|
215 |
|
---|
216 | uint64_t action = 1ull << DDgCustomActions[i].descr.index;
|
---|
217 | bool active = false;
|
---|
218 |
|
---|
219 | switch (DDgCustomActions[i].eventType) {
|
---|
220 | case DDcEventType_KeyPress:
|
---|
221 | if (input->ActionsPressed & action)
|
---|
222 | active = true;
|
---|
223 | break;
|
---|
224 | case DDcEventType_KeyDown:
|
---|
225 | if (input->ActionsDown & action)
|
---|
226 | active = true;
|
---|
227 | break;
|
---|
228 | }
|
---|
229 |
|
---|
230 | if (active)
|
---|
231 | DDgCustomActions[i].callback(DDgCustomActions[i].ctx);
|
---|
232 | }
|
---|
233 |
|
---|
234 | // Now do everything Oni does in this function
|
---|
235 | ONrGameState_HandleUtilityInput(input);
|
---|
236 |
|
---|
237 | // This is for show_triggervolumes. Mac Oni does this at the end of
|
---|
238 | // HandleUtilityInput too.
|
---|
239 | if (ONrDebugKey_WentDown(7))
|
---|
240 | OBJgTriggerVolume_Visible = !OBJgTriggerVolume_Visible;
|
---|
241 | }
|
---|
242 |
|
---|
243 | static int GetLowestFreeDigitalAction(void)
|
---|
244 | {
|
---|
245 | // Get the digital action indexes that Oni is using right now, plus any in
|
---|
246 | // use by our custom actions
|
---|
247 | uint64_t used = 0;
|
---|
248 | for (int i = 0; LIgActionDescriptions[i].name[0]; i++) {
|
---|
249 | if (LIgActionDescriptions[i].type != LIcActionType_Digital)
|
---|
250 | continue;
|
---|
251 | used |= 1ull << LIgActionDescriptions[i].index;
|
---|
252 | }
|
---|
253 | for (int i = 0; i < ARRAY_SIZE(DDgCustomActions); i++) {
|
---|
254 | if (!DDgCustomActions[i].descr.name[0])
|
---|
255 | break;
|
---|
256 |
|
---|
257 | if (DDgCustomActions[i].descr.type != LIcActionType_Digital)
|
---|
258 | continue;
|
---|
259 | used |= 1ull << DDgCustomActions[i].descr.index;
|
---|
260 | }
|
---|
261 |
|
---|
262 | // Get the lowest unused action index and use it. This isn't totally safe
|
---|
263 | // since Oni _might_ have "orphaned" actions that are checked in the code
|
---|
264 | // but not bindable, but Mac Oni 1.2.1 seems to have allocated its new
|
---|
265 | // bindings this way, including filling the gaps between eg. f12 and
|
---|
266 | // lookmode, so we're probably fine to do the same thing.
|
---|
267 | unsigned long lowest;
|
---|
268 | if (BitScanForward(&lowest, ~(unsigned long)used))
|
---|
269 | return lowest;
|
---|
270 | if (BitScanForward(&lowest, ~(unsigned long)(used >> 32)))
|
---|
271 | return lowest + 32;
|
---|
272 | return -1;
|
---|
273 | }
|
---|
274 |
|
---|
275 | void DDrInput_RegisterCustomAction(const char *name, DDtActionEventType type,
|
---|
276 | DDtCustomActionCallback callback,
|
---|
277 | intptr_t ctx)
|
---|
278 | {
|
---|
279 | int index = GetLowestFreeDigitalAction();
|
---|
280 | if (index < 0) {
|
---|
281 | STARTUPMESSAGE("Registering action %s failed, maximum actions reached",
|
---|
282 | name);
|
---|
283 | return;
|
---|
284 | }
|
---|
285 |
|
---|
286 | DDtCustomAction *action;
|
---|
287 | for (int i = 0; i < ARRAY_SIZE(DDgCustomActions); i++) {
|
---|
288 | if (!DDgCustomActions[i].descr.name[0]) {
|
---|
289 | action = &DDgCustomActions[i];
|
---|
290 | break;
|
---|
291 | }
|
---|
292 | }
|
---|
293 | if (!action) {
|
---|
294 | STARTUPMESSAGE("Registering action %s failed, maximum actions reached",
|
---|
295 | name);
|
---|
296 | return;
|
---|
297 | }
|
---|
298 |
|
---|
299 | *action = (DDtCustomAction) {
|
---|
300 | .descr = {
|
---|
301 | .type = 1,
|
---|
302 | .index = index,
|
---|
303 | },
|
---|
304 | .callback = callback,
|
---|
305 | .ctx = ctx,
|
---|
306 | };
|
---|
307 | UUrString_Copy(action->descr.name, name, sizeof(action->descr.name));
|
---|
308 | }
|
---|
309 |
|
---|
310 | static uint8_t VKeyToChar(UINT vkey)
|
---|
311 | {
|
---|
312 | int sc = MapVirtualKeyA(vkey, MAPVK_VK_TO_VSC_EX);
|
---|
313 | if ((sc & 0xff00) == 0xe000)
|
---|
314 | sc |= 0x80;
|
---|
315 | sc &= 0xff;
|
---|
316 | return DDgPlatform_ScanCodeToChar[sc];
|
---|
317 | }
|
---|
318 |
|
---|
319 | static int ONICALL DDrTranslate_InputName(char *name)
|
---|
320 | {
|
---|
321 | // Mutate the source argument to convert to lowercase. It's ugly but Oni
|
---|
322 | // does this too. Unlike Oni, we don't use tolower, since passing
|
---|
323 | // potentially out-of-range values to tolower is undefined.
|
---|
324 | for (char *c = name; *c; c++) {
|
---|
325 | if (*c >= 'A' && *c <= 'Z')
|
---|
326 | *c = *c - 0x20;
|
---|
327 | }
|
---|
328 |
|
---|
329 | // Single character names just use that character as the key code. Unlike
|
---|
330 | // Oni, we restrict this to printable ASCII.
|
---|
331 | if (strlen(name) == 1 && name[0] >= ' ' && name[0] <= '~')
|
---|
332 | return name[0];
|
---|
333 |
|
---|
334 | // Otherwise, look up the name in DDgInputNames
|
---|
335 | for (int i = 0; DDgInputNames[i].name[0]; i++) {
|
---|
336 | if (!strcmp(name, DDgInputNames[i].name))
|
---|
337 | return DDgInputNames[i].key;
|
---|
338 | }
|
---|
339 | return 0;
|
---|
340 | }
|
---|
341 |
|
---|
342 | static void CenterCursor(void)
|
---|
343 | {
|
---|
344 | // This can be set to false by script. Not sure why you'd turn it off, but
|
---|
345 | // let's respect it.
|
---|
346 | if (!LIgCenterCursor)
|
---|
347 | return;
|
---|
348 |
|
---|
349 | RECT rc;
|
---|
350 | if (!GetClientRect(LIgPlatform_HWND, &rc))
|
---|
351 | return;
|
---|
352 | POINT mid = { rc.right / 2, rc.bottom / 2 };
|
---|
353 | if (!ClientToScreen(LIgPlatform_HWND, &mid))
|
---|
354 | return;
|
---|
355 | SetCursorPos(mid.x, mid.y);
|
---|
356 | }
|
---|
357 |
|
---|
358 | static void ONICALL DDrPlatform_Mode_Set(int active)
|
---|
359 | {
|
---|
360 | // Oni's input system uses LIgPlatform_HWND instead of
|
---|
361 | // ONgPlatformData.Window, but they should both have the same value
|
---|
362 | DDmAssert(LIgPlatform_HWND);
|
---|
363 |
|
---|
364 | // Clear the input state when switching input modes
|
---|
365 | for (int i = 0; i < ARRAY_SIZE(DDgInputState); i++)
|
---|
366 | DDgInputState[i] = 0;
|
---|
367 | DDgActionBuffer = (LItActionBuffer) { 0 };
|
---|
368 |
|
---|
369 | // Center the cursor before switching modes. Raw Input doesn't need the
|
---|
370 | // cursor to be centered, but when switching modes, centering the cursor
|
---|
371 | // means it will be in a predictable position for using the pause or F1
|
---|
372 | // menu, which are centered on the screen. Also, the cursor must be inside
|
---|
373 | // the clip region when we call ClipCursor, otherwise it doesn't work.
|
---|
374 | CenterCursor();
|
---|
375 |
|
---|
376 | // If leaving input mode (switching from gameplay to menus,) unregister the
|
---|
377 | // input device. Otherwise, register it.
|
---|
378 | RegisterRawInputDevices(&(RAWINPUTDEVICE) {
|
---|
379 | .usUsagePage = 0x01, // HID_USAGE_PAGE_GENERIC
|
---|
380 | .usUsage = 0x02, // HID_USAGE_GENERIC_MOUSE
|
---|
381 | .hwndTarget = LIgPlatform_HWND,
|
---|
382 | .dwFlags = active ? 0 : RIDEV_REMOVE,
|
---|
383 | }, 1, sizeof(RAWINPUTDEVICE));
|
---|
384 |
|
---|
385 | if (active) {
|
---|
386 | DDgMouseSensitivity =
|
---|
387 | DDrConfig_GetOptOfType("windows.mousesensitivity", C_FLOAT)->value.floatVal;
|
---|
388 |
|
---|
389 | // Get the Oni key codes corresponding to the togglable keys
|
---|
390 | DDgCapsOniKey = VKeyToChar(VK_CAPITAL);
|
---|
391 | DDgScrollOniKey = VKeyToChar(VK_SCROLL);
|
---|
392 | DDgNumLockOniKey = VKeyToChar(VK_NUMLOCK);
|
---|
393 |
|
---|
394 | // Clip the cursor to the window bounds when entering input mode to
|
---|
395 | // prevent other programs being clicked in windowed mode
|
---|
396 | RECT rc;
|
---|
397 | if (GetClientRect(LIgPlatform_HWND, &rc)) {
|
---|
398 | if (MapWindowRect(LIgPlatform_HWND, NULL, &rc))
|
---|
399 | ClipCursor(&rc);
|
---|
400 | }
|
---|
401 | } else {
|
---|
402 | ClipCursor(NULL);
|
---|
403 | }
|
---|
404 | }
|
---|
405 |
|
---|
406 | static void ONICALL DDrPlatform_InputEvent_GetMouse(int active,
|
---|
407 | LItInputEvent *event)
|
---|
408 | {
|
---|
409 | POINT pt;
|
---|
410 | if (!GetCursorPos(&pt))
|
---|
411 | goto error;
|
---|
412 |
|
---|
413 | // Unlike Oni's version of this function, we support windowed mode by
|
---|
414 | // mapping the cursor coordinates to the window's client area
|
---|
415 | if (!ScreenToClient(LIgPlatform_HWND, &pt))
|
---|
416 | goto error;
|
---|
417 |
|
---|
418 | *event = (LItInputEvent) { .mouse_pos = { pt.x, pt.y } };
|
---|
419 | return;
|
---|
420 |
|
---|
421 | error:
|
---|
422 | *event = (LItInputEvent) { 0 };
|
---|
423 | return;
|
---|
424 | }
|
---|
425 |
|
---|
426 | static UUtBool ONICALL DDrPlatform_TestKey(int ch, int active)
|
---|
427 | {
|
---|
428 | // DDrPlatform_TestKey is always called with active = LIgMode_Internal
|
---|
429 |
|
---|
430 | if (active) {
|
---|
431 | // The input system is running, which means DDgInputState will be up to
|
---|
432 | // date, so just use that
|
---|
433 | return DDgInputState[ch];
|
---|
434 | } else {
|
---|
435 | // Use Oni's map from key codes to DirectInput scan codes to get the
|
---|
436 | // scan code we want to test for
|
---|
437 | int sc = 0;
|
---|
438 | for (int i = 0; i < 256; i++) {
|
---|
439 | if (DDgPlatform_ScanCodeToChar[i] == ch) {
|
---|
440 | sc = i;
|
---|
441 | break;
|
---|
442 | }
|
---|
443 | }
|
---|
444 | if (!sc)
|
---|
445 | return UUcFalse;
|
---|
446 |
|
---|
447 | // DirectInput scan codes have 0x80 set for extended keys. Replace this
|
---|
448 | // with an 0xe0 prefix for MapVirtualKey.
|
---|
449 | if (sc & 0x80) {
|
---|
450 | sc &= 0x7f;
|
---|
451 | sc |= 0xe000;
|
---|
452 | }
|
---|
453 | int vkey = MapVirtualKeyA(sc, MAPVK_VSC_TO_VK_EX);
|
---|
454 |
|
---|
455 | // Now check if the key is down. We must use GetAsyncKeyState here
|
---|
456 | // because DDrPlatform_TestKey can be called from anywhere, even before
|
---|
457 | // we have a message loop or game loop. For example, it's called from
|
---|
458 | // ONiMain to test the state of the shift key on startup.
|
---|
459 | return (GetAsyncKeyState(vkey) & 0x8000) ? UUcTrue : UUcFalse;
|
---|
460 | }
|
---|
461 | }
|
---|
462 |
|
---|
463 | // Update DDgInputState and DDgActionBuffer with a new key state
|
---|
464 | static void SetKeyState(int key, bool down)
|
---|
465 | {
|
---|
466 | // Keep track of held keys. Held keys are added to every buffer and they're
|
---|
467 | // also checked in DDrPlatform_TestKey.
|
---|
468 | DDgInputState[key] = down;
|
---|
469 |
|
---|
470 | if (down) {
|
---|
471 | // Add the key to the next buffer. This is so key presses are never
|
---|
472 | // dropped, even if the key is released before Oni checks the buffer.
|
---|
473 | LIrActionBuffer_Add(&DDgActionBuffer, &(LItDeviceInput) {
|
---|
474 | .input = key,
|
---|
475 | .analog = 1.0,
|
---|
476 | });
|
---|
477 | }
|
---|
478 | }
|
---|
479 |
|
---|
480 | static void ProcessRawInputPacket(RAWINPUT *ri)
|
---|
481 | {
|
---|
482 | if (ri->header.dwType != RIM_TYPEMOUSE)
|
---|
483 | return;
|
---|
484 |
|
---|
485 | // We don't handle MOUSE_MOVE_ABSOLUTE at all yet
|
---|
486 | if (!(ri->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE)) {
|
---|
487 | LIrActionBuffer_Add(&DDgActionBuffer, &(LItDeviceInput) {
|
---|
488 | .input = LIcKey_MouseXAxis,
|
---|
489 | .analog = (float)ri->data.mouse.lLastX * 0.25 * DDgMouseSensitivity,
|
---|
490 | });
|
---|
491 | LIrActionBuffer_Add(&DDgActionBuffer, &(LItDeviceInput) {
|
---|
492 | .input = LIcKey_MouseYAxis,
|
---|
493 | .analog = (float)ri->data.mouse.lLastY *
|
---|
494 | (LIgMode_InvertMouse ? -0.25 : 0.25) * DDgMouseSensitivity,
|
---|
495 | });
|
---|
496 | }
|
---|
497 |
|
---|
498 | // Oni supports using the mouse wheel to look up and down or left and right
|
---|
499 | // by binding mousezaxis to aim_lr or aim_ud. We don't support this
|
---|
500 | // incredibly useful feature, but if you need it, let me know. Instead, we
|
---|
501 | // allow scrolling to be bound to digital actions.
|
---|
502 | if (ri->data.mouse.usButtonFlags & (RI_MOUSE_WHEEL | RI_MOUSE_HWHEEL)) {
|
---|
503 | int64_t now = UUrMachineTime_High();
|
---|
504 | int64_t last_updated = now - DDgWheelDelta_Updated;
|
---|
505 | DDgWheelDelta_Updated = now;
|
---|
506 |
|
---|
507 | // Reset the accumulators if too much time has passed since the last
|
---|
508 | // scroll event. The player is assumed to have finished scrolling.
|
---|
509 | if (last_updated / UUrMachineTime_High_Frequency() > 0.3) {
|
---|
510 | DDgWheelDelta_V = 0;
|
---|
511 | DDgWheelDelta_H = 0;
|
---|
512 | }
|
---|
513 |
|
---|
514 | int neg_key, pos_key;
|
---|
515 | int *delta;
|
---|
516 | if (ri->data.mouse.usButtonFlags & RI_MOUSE_WHEEL) {
|
---|
517 | neg_key = DDcKey_ScrollUp;
|
---|
518 | pos_key = DDcKey_ScrollDown;
|
---|
519 | delta = &DDgWheelDelta_V;
|
---|
520 | } else {
|
---|
521 | neg_key = DDcKey_ScrollLeft;
|
---|
522 | pos_key = DDcKey_ScrollRight;
|
---|
523 | delta = &DDgWheelDelta_H;
|
---|
524 | }
|
---|
525 |
|
---|
526 | // To support touchpad scrolling and mice with continuous scroll wheels,
|
---|
527 | // accumulate the wheel delta and only generate an input event once it
|
---|
528 | // crosses the WHEEL_DELTA threshold
|
---|
529 | *delta += (short)ri->data.mouse.usButtonData;
|
---|
530 | if (*delta >= WHEEL_DELTA) {
|
---|
531 | LIrActionBuffer_Add(&DDgActionBuffer, &(LItDeviceInput) {
|
---|
532 | .input = neg_key,
|
---|
533 | .analog = 1.0,
|
---|
534 | });
|
---|
535 |
|
---|
536 | *delta -= (*delta / WHEEL_DELTA) * WHEEL_DELTA;
|
---|
537 | } else if (*delta <= -WHEEL_DELTA) {
|
---|
538 | LIrActionBuffer_Add(&DDgActionBuffer, &(LItDeviceInput) {
|
---|
539 | .input = pos_key,
|
---|
540 | .analog = 1.0,
|
---|
541 | });
|
---|
542 |
|
---|
543 | *delta -= (*delta / -WHEEL_DELTA) * -WHEEL_DELTA;
|
---|
544 | }
|
---|
545 | }
|
---|
546 |
|
---|
547 | // This probably doesn't obey SM_SWAPBUTTON... should it?
|
---|
548 | if (ri->data.mouse.usButtonFlags & RI_MOUSE_LEFT_BUTTON_DOWN)
|
---|
549 | SetKeyState(LIcKey_MouseButton1, true);
|
---|
550 | if (ri->data.mouse.usButtonFlags & RI_MOUSE_LEFT_BUTTON_UP)
|
---|
551 | SetKeyState(LIcKey_MouseButton1, false);
|
---|
552 | if (ri->data.mouse.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_DOWN)
|
---|
553 | SetKeyState(LIcKey_MouseButton2, true);
|
---|
554 | if (ri->data.mouse.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_UP)
|
---|
555 | SetKeyState(LIcKey_MouseButton2, false);
|
---|
556 | if (ri->data.mouse.usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_DOWN)
|
---|
557 | SetKeyState(LIcKey_MouseButton3, true);
|
---|
558 | if (ri->data.mouse.usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_UP)
|
---|
559 | SetKeyState(LIcKey_MouseButton3, false);
|
---|
560 |
|
---|
561 | // Oni supports binding this button too. It's the back button on most mice.
|
---|
562 | if (ri->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN)
|
---|
563 | SetKeyState(LIcKey_MouseButton4, true);
|
---|
564 | if (ri->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_UP)
|
---|
565 | SetKeyState(LIcKey_MouseButton4, false);
|
---|
566 |
|
---|
567 | // Daodan supports binding the forward button too
|
---|
568 | if (ri->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN)
|
---|
569 | SetKeyState(DDcKey_MouseButton5, true);
|
---|
570 | if (ri->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_UP)
|
---|
571 | SetKeyState(DDcKey_MouseButton5, false);
|
---|
572 | }
|
---|
573 |
|
---|
574 | static void DrainRawInput(void)
|
---|
575 | {
|
---|
576 | if (!LIgMode_Internal)
|
---|
577 | return;
|
---|
578 |
|
---|
579 | UINT ri_size = 10240;
|
---|
580 | static RAWINPUT *ri_buf = NULL;
|
---|
581 | if (!ri_buf)
|
---|
582 | ri_buf = calloc(1, ri_size);
|
---|
583 |
|
---|
584 | BOOL wow_hack;
|
---|
585 | IsWow64Process(GetCurrentProcess(), &wow_hack);
|
---|
586 |
|
---|
587 | for (;;) {
|
---|
588 | UINT count = GetRawInputBuffer(ri_buf, &ri_size, sizeof ri_buf->header);
|
---|
589 | if (count == 0 || count == (UINT)-1)
|
---|
590 | return;
|
---|
591 |
|
---|
592 | RAWINPUT *ri = ri_buf;
|
---|
593 | for (UINT i = 0; i < count; i++) {
|
---|
594 | // In WOW64, these structures are aligned like in Win64 and they
|
---|
595 | // have to be fixed to use from 32-bit code. Yes, really.
|
---|
596 | if (wow_hack) {
|
---|
597 | memmove(&ri->data, ((char *)&ri->data) + 8,
|
---|
598 | ri->header.dwSize - offsetof(RAWINPUT, data) - 8);
|
---|
599 | }
|
---|
600 |
|
---|
601 | ProcessRawInputPacket(ri);
|
---|
602 | ri = NEXTRAWINPUTBLOCK(ri);
|
---|
603 | }
|
---|
604 | }
|
---|
605 | }
|
---|
606 |
|
---|
607 | static UUtBool ONICALL DDiPlatform_InputEvent_GetEvent(void)
|
---|
608 | {
|
---|
609 | // Center the cursor just in case. Raw Input doesn't need it, but sometimes
|
---|
610 | // ClipCursor doesn't work for some reason and in that case we should still
|
---|
611 | // prevent the user from accidentally clicking on other windows.
|
---|
612 | if (LIgMode_Internal)
|
---|
613 | CenterCursor();
|
---|
614 |
|
---|
615 | // Do a buffered read of raw input. Apparently this is faster for high-res
|
---|
616 | // mice. Note that we still have to handle WM_INPUT in our wndproc in case
|
---|
617 | // a WM_INPUT message arrives during the message loop.
|
---|
618 | DrainRawInput();
|
---|
619 |
|
---|
620 | // Oni only processes a maximum of three messages here (for performance
|
---|
621 | // reasons?) We're actually using Windows messages for input so we need to
|
---|
622 | // process all of them.
|
---|
623 | MSG msg;
|
---|
624 | while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
|
---|
625 | TranslateMessage(&msg);
|
---|
626 | DispatchMessageA(&msg);
|
---|
627 | }
|
---|
628 |
|
---|
629 | // Oni returns true here if there are still messages to process, so that
|
---|
630 | // LIrMode_Set and LIrMode_Set_Internal can call this repeatedly to drain
|
---|
631 | // all messages. We always drain all messages so return false.
|
---|
632 | return UUcFalse;
|
---|
633 | }
|
---|
634 |
|
---|
635 | static bool HandleWmInput(HRAWINPUT hri, WPARAM wparam)
|
---|
636 | {
|
---|
637 | if (!LIgMode_Internal)
|
---|
638 | return false;
|
---|
639 |
|
---|
640 | static RAWINPUT* ri = NULL;
|
---|
641 | static UINT ri_size = 0;
|
---|
642 | UINT minsize = 0;
|
---|
643 |
|
---|
644 | GetRawInputData(hri, RID_INPUT, NULL, &minsize, sizeof ri->header);
|
---|
645 | if (ri_size < minsize) {
|
---|
646 | if (ri)
|
---|
647 | free(ri);
|
---|
648 | ri_size = minsize;
|
---|
649 | ri = calloc(1, ri_size);
|
---|
650 | }
|
---|
651 | if (GetRawInputData(hri, RID_INPUT, ri, &ri_size, sizeof ri->header) == (UINT)-1)
|
---|
652 | return false;
|
---|
653 |
|
---|
654 | ProcessRawInputPacket(ri);
|
---|
655 | return true;
|
---|
656 | }
|
---|
657 |
|
---|
658 | static void HandleWmWindowPosChanged(WINDOWPOS *pos)
|
---|
659 | {
|
---|
660 | if (!LIgMode_Internal)
|
---|
661 | return;
|
---|
662 |
|
---|
663 | CenterCursor();
|
---|
664 |
|
---|
665 | RECT rc = { pos->x, pos->y, pos->x + pos->cx, pos->y + pos->cy };
|
---|
666 | ClipCursor(&rc);
|
---|
667 | }
|
---|
668 |
|
---|
669 | static void HandleWmKeyboard(int vkey, WORD repeat_count, WORD flags)
|
---|
670 | {
|
---|
671 | if (!LIgMode_Internal)
|
---|
672 | return;
|
---|
673 |
|
---|
674 | bool is_extended = flags & KF_EXTENDED;
|
---|
675 | bool is_repeat = flags & KF_REPEAT;
|
---|
676 | bool is_up = flags & KF_UP;
|
---|
677 | BYTE sc = LOBYTE(flags);
|
---|
678 |
|
---|
679 | // Ignore togglable keys since we handle them specially
|
---|
680 | if (vkey == VK_CAPITAL || vkey == VK_SCROLL || vkey == VK_NUMLOCK)
|
---|
681 | return;
|
---|
682 |
|
---|
683 | // Ignore key down messages sent because of key-repeat
|
---|
684 | if (!is_up && is_repeat)
|
---|
685 | return;
|
---|
686 |
|
---|
687 | // Apparently some synthetic keyboard messages can be missing the scancode,
|
---|
688 | // so get it from the vkey
|
---|
689 | if (!sc)
|
---|
690 | sc = MapVirtualKeyA(vkey, MAPVK_VK_TO_VSC);
|
---|
691 |
|
---|
692 | // DirectInput scan codes have 0x80 set for extended keys, and we're using
|
---|
693 | // a map based on Oni's DirectInput map to convert to key codes
|
---|
694 | if (is_extended)
|
---|
695 | sc |= 0x80;
|
---|
696 | uint8_t ch = DDgPlatform_ScanCodeToChar[sc];
|
---|
697 | if (!ch)
|
---|
698 | return;
|
---|
699 |
|
---|
700 | SetKeyState(ch, !is_up);
|
---|
701 | }
|
---|
702 |
|
---|
703 | bool DDrInput_WindowProc(HWND window, UINT msg, WPARAM wparam, LPARAM lparam,
|
---|
704 | LRESULT* res)
|
---|
705 | {
|
---|
706 | // This is called from our own window proc for now, so we only want to use
|
---|
707 | // it when Daodan input is enabled
|
---|
708 | if (!DDgUseDaodanInput)
|
---|
709 | return false;
|
---|
710 |
|
---|
711 | switch (msg) {
|
---|
712 | case WM_INPUT:
|
---|
713 | if (HandleWmInput((HRAWINPUT)lparam, wparam)) {
|
---|
714 | *res = 0;
|
---|
715 | return true;
|
---|
716 | }
|
---|
717 | break;
|
---|
718 | case WM_WINDOWPOSCHANGED:
|
---|
719 | HandleWmWindowPosChanged((WINDOWPOS *)lparam);
|
---|
720 | break;
|
---|
721 | case WM_KEYDOWN:
|
---|
722 | case WM_SYSKEYDOWN:
|
---|
723 | case WM_KEYUP:
|
---|
724 | case WM_SYSKEYUP:
|
---|
725 | HandleWmKeyboard(LOWORD(wparam), LOWORD(lparam), HIWORD(lparam));
|
---|
726 | break;
|
---|
727 | }
|
---|
728 |
|
---|
729 | return false;
|
---|
730 | }
|
---|
731 |
|
---|
732 | static void ONICALL DDrActionBuffer_Get(short* count, LItActionBuffer** buffers)
|
---|
733 | {
|
---|
734 | // So, Oni's version of this function was totally different. In unpatched
|
---|
735 | // Oni, action buffers were produced at 60Hz by a separate high-priority
|
---|
736 | // input thread, LIiInterruptHandleProc, and consumed by
|
---|
737 | // LIrActionBuffer_Get, which was called from the game loop and could
|
---|
738 | // provide multiple outstanding action buffers to the engine at once.
|
---|
739 | //
|
---|
740 | // That was a problem for a couple of reasons. Firstly, the resolution of
|
---|
741 | // Windows timers is limited by the timer frequency, which can be as low as
|
---|
742 | // 15.6ms and in the worst case would cause a delay of 31.2ms between action
|
---|
743 | // buffers. That meant that even if Oni was running at a steady 60 fps, the
|
---|
744 | // input thread would provide no action buffers on a lot of frames.
|
---|
745 | //
|
---|
746 | // Secondly, even though Oni drained the list of pending action buffers by
|
---|
747 | // calling DDrActionBuffer_Get on every frame, the engine only uses them
|
---|
748 | // when the internal game time advances, and that happens on a separate 60Hz
|
---|
749 | // timer which was totally unsynchronized with the 60Hz timer on the input
|
---|
750 | // thread. That wasn't too much of a problem when the game loop was running
|
---|
751 | // at less than 60 fps, but when it ran faster, the only action buffers that
|
---|
752 | // got processed were the ones produced when the game timer and the input
|
---|
753 | // thread timer happened to tick at the same time, meaning potentially a lot
|
---|
754 | // of dropped input.
|
---|
755 | //
|
---|
756 | // Oni's input system was probably designed that way so that input would
|
---|
757 | // still run at 60Hz even on PCs that weren't powerful enough to render at
|
---|
758 | // 60 fps. It was a well-meaning design, but due to the aforementioned
|
---|
759 | // flaws, we do something much different and simpler here. On the frames
|
---|
760 | // that Oni will consume input, we generate a single action buffer inside
|
---|
761 | // DDrActionBuffer_Get based on most up-to-date input.
|
---|
762 |
|
---|
763 | // Update ONgGameState->TargetGameTime. We use TargetGameTime to determine
|
---|
764 | // if Oni is going to consume input on this frame. Unfortunately, in
|
---|
765 | // unpatched Oni, the call to ONrGameState_UpdateServerTime happened after
|
---|
766 | // LIrActionBuffer_Get. In Daodan, we NOOP out the original call and call it
|
---|
767 | // here instead, so it runs before our code.
|
---|
768 | ONrGameState_UpdateServerTime(ONgGameState);
|
---|
769 | bool time_updated = ONgGameState->GameTime != ONgGameState->TargetGameTime;
|
---|
770 |
|
---|
771 | // Only produce input buffers when input is enabled. LIrActionBuffer_Get
|
---|
772 | // does the same thing. Also only produce them when Oni will consume them.
|
---|
773 | if (!LIgMode_Internal || !time_updated) {
|
---|
774 | *count = 0;
|
---|
775 | *buffers = NULL;
|
---|
776 | return;
|
---|
777 | }
|
---|
778 |
|
---|
779 | // Add held keys to the action buffer
|
---|
780 | for (int i = 0; i < ARRAY_SIZE(DDgInputState); i++) {
|
---|
781 | if (DDgInputState[i]) {
|
---|
782 | LIrActionBuffer_Add(&DDgActionBuffer, &(LItDeviceInput) {
|
---|
783 | .input = i,
|
---|
784 | .analog = 1.0,
|
---|
785 | });
|
---|
786 | }
|
---|
787 | }
|
---|
788 |
|
---|
789 | // Add togglable keys to the action buffer
|
---|
790 | if (DDgCapsOniKey && (GetKeyState(VK_CAPITAL) & 0x01)) {
|
---|
791 | LIrActionBuffer_Add(&DDgActionBuffer, &(LItDeviceInput) {
|
---|
792 | .input = DDgCapsOniKey,
|
---|
793 | .analog = 1.0,
|
---|
794 | });
|
---|
795 | }
|
---|
796 | if (DDgScrollOniKey && (GetKeyState(VK_SCROLL) & 0x01)) {
|
---|
797 | LIrActionBuffer_Add(&DDgActionBuffer, &(LItDeviceInput) {
|
---|
798 | .input = DDgScrollOniKey,
|
---|
799 | .analog = 1.0,
|
---|
800 | });
|
---|
801 | }
|
---|
802 | if (DDgNumLockOniKey && (GetKeyState(VK_NUMLOCK) & 0x01)) {
|
---|
803 | LIrActionBuffer_Add(&DDgActionBuffer, &(LItDeviceInput) {
|
---|
804 | .input = DDgNumLockOniKey,
|
---|
805 | .analog = 1.0,
|
---|
806 | });
|
---|
807 | }
|
---|
808 |
|
---|
809 | // Make a copy of our temporary action buffer with all the input we've
|
---|
810 | // gathered this frame. This is the copy that Oni's engine will see.
|
---|
811 | static LItActionBuffer buf = { 0 };
|
---|
812 | buf = DDgActionBuffer;
|
---|
813 | DDgActionBuffer = (LItActionBuffer) { 0 };
|
---|
814 |
|
---|
815 | *count = 1;
|
---|
816 | *buffers = &buf;
|
---|
817 | }
|
---|
818 |
|
---|
819 | void DDrInput_PatchUtilityInput(void)
|
---|
820 | {
|
---|
821 | // Patch the call to ONrGameState_HandleUtilityInput in
|
---|
822 | // ONrGameState_ProcessHeartbeat. This is where Oni checks a bunch of
|
---|
823 | // miscellaneous inputs, and where Mac Oni 1.2.1 checks the cheat bindings.
|
---|
824 | // It's also where Mac Oni toggles the show_triggervolumes flag.
|
---|
825 | DDrPatch_MakeCall((void *)0x004fa91c, (void *)DDrGameState_HandleUtilityInput);
|
---|
826 | }
|
---|
827 |
|
---|
828 | void DDrInput_PatchCustomActions(void)
|
---|
829 | {
|
---|
830 | DDrInput_PatchUtilityInput();
|
---|
831 |
|
---|
832 | // Replace the function which adds bindings with ours, which checks the list
|
---|
833 | // of custom bindings as well
|
---|
834 | DDrPatch_MakeJump((void *)LIrBinding_Add, (void *)DDrBinding_Add);
|
---|
835 | }
|
---|
836 |
|
---|
837 | void DDrInput_PatchDaodanInput(void)
|
---|
838 | {
|
---|
839 | // In LIrInitialize, NOOP the call to UUrInterruptProc_Install and
|
---|
840 | // associated error checking
|
---|
841 | DDrPatch_NOOP((char *)(OniExe + 0x421f), 106);
|
---|
842 |
|
---|
843 | // In LIrPlatform_Initialize, NOOP the Windows version checks so we never
|
---|
844 | // use DirectInput
|
---|
845 | DDrPatch_NOOP((char *)(OniExe + 0x2e64), 11);
|
---|
846 |
|
---|
847 | // Replace Oni's Windows message loop with one that does buffered raw input
|
---|
848 | // reads and processes all messages
|
---|
849 | DDrPatch_MakeJump((void *)LIiPlatform_InputEvent_GetEvent, (void *)DDiPlatform_InputEvent_GetEvent);
|
---|
850 |
|
---|
851 | // Replace the function that gets the latest input frames
|
---|
852 | DDrPatch_MakeJump((void *)LIrActionBuffer_Get, (void *)DDrActionBuffer_Get);
|
---|
853 |
|
---|
854 | // Replace the function that gets the mouse cursor position
|
---|
855 | DDrPatch_MakeJump((void *)LIrPlatform_InputEvent_GetMouse, (void *)DDrPlatform_InputEvent_GetMouse);
|
---|
856 |
|
---|
857 | // Replace the function that performs platform-specific actions when the
|
---|
858 | // input mode changes
|
---|
859 | DDrPatch_MakeJump((void *)LIrPlatform_Mode_Set, (void *)DDrPlatform_Mode_Set);
|
---|
860 |
|
---|
861 | // Replaces the function that tests the state of keyboard keys
|
---|
862 | DDrPatch_MakeJump((void *)LIrPlatform_TestKey, (void *)DDrPlatform_TestKey);
|
---|
863 |
|
---|
864 | // Enable extra key names in key_config.txt
|
---|
865 | DDrPatch_MakeJump((void *)LIrTranslate_InputName, (void *)DDrTranslate_InputName);
|
---|
866 |
|
---|
867 | // Patch out the call to ONrGameState_UpdateServerTime in ONiRunGame because
|
---|
868 | // we want to do it earlier, in DDrActionBuffer_Get
|
---|
869 | DDrPatch_NOOP((char *)(OniExe + 0xd4708), 11);
|
---|
870 |
|
---|
871 | DDgUseDaodanInput = true;
|
---|
872 | }
|
---|