Mercurial > hg > index.fcgi > lj > lj046-2players
comparison src/pcjoy.c @ 0:c84446dfb3f5
initial add
author | paulo@localhost |
---|---|
date | Fri, 13 Mar 2009 00:39:12 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:3da0152d3e3f |
---|---|
1 /* PC joystick code | |
2 | |
3 Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net> | |
4 | |
5 This work is free software; you can redistribute it and/or modify | |
6 it under the terms of the GNU General Public License as published by | |
7 the Free Software Foundation; either version 2 of the License, or | |
8 (at your option) any later version. | |
9 | |
10 This program is distributed in the hope that it will be useful, | |
11 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 GNU General Public License for more details. | |
14 | |
15 You should have received a copy of the GNU General Public License | |
16 along with this program; if not, write to the Free Software | |
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | |
19 Original game concept and design by Alexey Pajitnov. | |
20 The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, | |
21 or The Tetris Company LLC. | |
22 | |
23 */ | |
24 | |
25 #include "pcjoy.h" | |
26 #include <allegro.h> | |
27 #include <string.h> | |
28 #include "ljpath.h" | |
29 | |
30 extern const FONT *aver32, *aver16; | |
31 extern int bgColor, fgColor, hiliteColor; | |
32 | |
33 static volatile int lastScancodePressed = -1; | |
34 static void (*oldKeyListener)(int scancode); | |
35 volatile int wantsClose = 0; | |
36 void ezPlaySample(const char *filename, int vol); | |
37 | |
38 static void ljpcKeyListener(int scancode) { | |
39 if (!(scancode & 0x80)) { | |
40 lastScancodePressed = scancode; | |
41 } | |
42 if (oldKeyListener) { | |
43 oldKeyListener(scancode); | |
44 } | |
45 } END_OF_FUNCTION(ljpcKeyListener); | |
46 | |
47 | |
48 /** | |
49 * Presses the escape key when the user clicks the close box. | |
50 */ | |
51 static void setWantsClose() { | |
52 wantsClose = 1; | |
53 } | |
54 | |
55 static int withJoystick = 0; | |
56 | |
57 #define N_VKEYS 14 | |
58 #define N_PLAYERS 2 | |
59 | |
60 static const char keysFileName[] = "lj-keys.043"; | |
61 | |
62 static const struct pkeyMapping defaultKeymappings[N_PLAYERS][N_VKEYS] = { | |
63 { | |
64 // [0]: player 1 | |
65 {-1, -1, KEY_UP}, | |
66 {-1, -1, KEY_DOWN}, | |
67 {-1, -1, KEY_LEFT}, | |
68 {-1, -1, KEY_RIGHT}, | |
69 {-1, -1, KEY_Z}, | |
70 {-1, -1, KEY_X}, | |
71 {-1, -1, KEY_S}, | |
72 {-1, -1, KEY_SPACE}, | |
73 {-1, -1, KEY_C}, | |
74 {-1, -1, KEY_W}, | |
75 {-1, -1, KEY_Q}, | |
76 {-1, -1, KEY_E}, | |
77 {-1, -1, KEY_ENTER}, | |
78 {-1, -1, KEY_D} | |
79 }, | |
80 { | |
81 // [1]: player 2 | |
82 {-1, -1, KEY_UP}, | |
83 {-1, -1, KEY_DOWN}, | |
84 {-1, -1, KEY_LEFT}, | |
85 {-1, -1, KEY_RIGHT}, | |
86 {-1, -1, KEY_Z}, | |
87 {-1, -1, KEY_X}, | |
88 {-1, -1, KEY_S}, | |
89 {-1, -1, KEY_SPACE}, | |
90 {-1, -1, KEY_C}, | |
91 {-1, -1, KEY_W}, | |
92 {-1, -1, KEY_Q}, | |
93 {-1, -1, KEY_E}, | |
94 {-1, -1, KEY_ENTER}, | |
95 {-1, -1, KEY_D} | |
96 } | |
97 }; | |
98 | |
99 static struct pkeyMapping keymappings[N_PLAYERS][N_VKEYS]; | |
100 | |
101 void getPkeyName(char *dst, int j, int s, int a) { | |
102 if (j < 0) { | |
103 if (a >= 0 && a < KEY_MAX) { | |
104 usprintf(dst, "Key %d (%s)", a, scancode_to_name(a)); | |
105 } else { | |
106 usprintf(dst, "Key %d (???"")", a); | |
107 } | |
108 } else if (s < 0) { | |
109 usprintf(dst, | |
110 "Joy %d button %s", | |
111 j, | |
112 joy[j].button[a].name); | |
113 } else if (a < 0) { | |
114 usprintf(dst, | |
115 "Joy %d stick %s axis %s -", | |
116 j, | |
117 joy[j].stick[s].name, | |
118 joy[j].stick[s].axis[~a].name); | |
119 } else { | |
120 usprintf(dst, | |
121 "Joy %d stick %s axis %s +", | |
122 j, | |
123 joy[j].stick[s].name, | |
124 joy[j].stick[s].axis[a].name); | |
125 } | |
126 } | |
127 | |
128 static int getPkeyState(int j, int s, int a) { | |
129 int k; | |
130 if (j < 0) { | |
131 k = key[a]; | |
132 } else if (s < 0) { | |
133 k = joy[j].button[a].b; | |
134 } else if (a < 0) { | |
135 k = joy[j].stick[s].axis[~a].d1; | |
136 } else { | |
137 k = joy[j].stick[s].axis[a].d2; | |
138 } | |
139 return k; | |
140 } | |
141 | |
142 const char *const vkeyNames[] = { | |
143 "Hard Drop (Up)", | |
144 "Soft Drop (Down)", | |
145 "Left", | |
146 "Right", | |
147 "Rotate Left", | |
148 "Rotate Right", | |
149 "Hold", | |
150 "Item (unused)", | |
151 "Alt. Rotate Left", | |
152 "Rotate Left Twice", | |
153 "Far Left", | |
154 "Far Right", | |
155 "Alt. Firm Drop", | |
156 "Alt. Hold", | |
157 "Macro G", | |
158 "Macro H" | |
159 }; | |
160 | |
161 static int getVkeyState(int player, int vkey) { | |
162 int j = keymappings[player][vkey].joy; | |
163 int s = keymappings[player][vkey].stick; | |
164 int a = keymappings[player][vkey].axis; | |
165 | |
166 return getPkeyState(j, s, a); | |
167 } | |
168 | |
169 LJBits readPad(unsigned int player) { | |
170 int keys = 0; | |
171 poll_joystick(); | |
172 | |
173 for (int i = 0; | |
174 i < N_VKEYS; | |
175 ++i) { | |
176 if (getVkeyState(player, i)) { | |
177 keys |= 1 << i; | |
178 } | |
179 } | |
180 return keys; | |
181 } | |
182 | |
183 LJBits menuReadPad(void) { | |
184 if (key[KEY_ENTER]) { | |
185 return VKEY_ROTR | VKEY_START; | |
186 } else if (key[KEY_ESC]) { | |
187 return VKEY_ROTL; | |
188 } else if (key[KEY_UP]) { | |
189 return VKEY_UP; | |
190 } else if (key[KEY_DOWN]) { | |
191 return VKEY_DOWN; | |
192 } else if (key[KEY_LEFT]) { | |
193 return VKEY_LEFT; | |
194 } else if (key[KEY_RIGHT]) { | |
195 return VKEY_RIGHT; | |
196 } else { | |
197 return readPad(0) | readPad(1); | |
198 } | |
199 } | |
200 | |
201 // These contain the states of ALL buttons on ALL | |
202 // joysticks | |
203 static LJBits lastConfigButtons[8]; | |
204 static LJBits lastConfigStickAxis[8]; | |
205 | |
206 static int newButton(int *outJ, int *outS, int *outA) { | |
207 poll_joystick(); | |
208 int found = 0; | |
209 | |
210 if (lastScancodePressed >= 0) { | |
211 *outJ = -1; | |
212 *outS = -1; | |
213 *outA = lastScancodePressed; | |
214 lastScancodePressed = -1; | |
215 return 1; | |
216 } | |
217 | |
218 for (int j = 0; | |
219 j < num_joysticks; | |
220 ++j) { | |
221 LJBits cur = 0; | |
222 | |
223 for (int b = 0; b < joy[j].num_buttons; ++b) { | |
224 if (joy[j].button[b].b) { | |
225 if (!(lastConfigButtons[j] & (1 << b)) && !found) { | |
226 *outJ = j; | |
227 *outS = -1; | |
228 *outA = b; | |
229 found = 1; | |
230 } | |
231 cur |= 1 << b; | |
232 } | |
233 } | |
234 lastConfigButtons[j] = cur; | |
235 } | |
236 if (found) { | |
237 return 1; | |
238 } | |
239 | |
240 for (int j = 0; | |
241 j < num_joysticks; | |
242 ++j) { | |
243 LJBits cur = 0; | |
244 LJBits mask = 1; | |
245 | |
246 for (int s = 0; s < joy[j].num_sticks; ++s) { | |
247 for (int a = 0; a < joy[j].stick[s].num_axis; ++a) { | |
248 if (joy[j].stick[s].axis[a].d1) { | |
249 if (!(lastConfigStickAxis[j] & mask) && !found) { | |
250 *outJ = j; | |
251 *outS = s; | |
252 *outA = ~a; | |
253 found = 1; | |
254 } | |
255 cur |= mask; | |
256 } | |
257 mask <<= 1; | |
258 if (joy[j].stick[s].axis[a].d2) { | |
259 if (!(lastConfigStickAxis[j] & mask) && !found) { | |
260 *outJ = j; | |
261 *outS = s; | |
262 *outA = a; | |
263 found = 1; | |
264 } | |
265 cur |= mask; | |
266 } | |
267 mask <<= 1; | |
268 } | |
269 } | |
270 lastConfigStickAxis[j] = cur; | |
271 } | |
272 return found; | |
273 } | |
274 | |
275 static void clearNewButton(void) { | |
276 int j, s, a; | |
277 while (newButton(&j, &s, &a)); | |
278 } | |
279 | |
280 void loadKeys(const char *filename) { | |
281 FILE *fp = ljfopen(filename, "rb"); | |
282 | |
283 memcpy(keymappings, defaultKeymappings, sizeof(keymappings)); | |
284 if (fp) { | |
285 for (unsigned int player = 0; player < 2; ++player) { | |
286 for (unsigned int vkey = 0; | |
287 vkey < N_VKEYS; | |
288 ++vkey) { | |
289 int j = fgetc(fp); | |
290 int s = fgetc(fp); | |
291 int a = fgetc(fp); | |
292 | |
293 if (a == EOF) { | |
294 break; | |
295 } | |
296 keymappings[player][vkey].joy = j; | |
297 keymappings[player][vkey].stick = s; | |
298 keymappings[player][vkey].axis = a; | |
299 } | |
300 } | |
301 fclose(fp); | |
302 } | |
303 } | |
304 | |
305 void saveKeys(const char *filename) { | |
306 FILE *fp = ljfopen(filename, "wb"); | |
307 | |
308 if (fp) { | |
309 for (unsigned int player = 0; player < 2; ++player) { | |
310 for (unsigned int vkey = 0; | |
311 vkey < N_VKEYS && !feof(fp); | |
312 ++vkey) { | |
313 fputc(keymappings[player][vkey].joy, fp); | |
314 fputc(keymappings[player][vkey].stick, fp); | |
315 fputc(keymappings[player][vkey].axis, fp); | |
316 } | |
317 } | |
318 fclose(fp); | |
319 } | |
320 } | |
321 | |
322 #define VKEY_ROWHT 24 | |
323 #define VKEY_TOP 120 | |
324 | |
325 void drawVkeyRow(int vkey, int hilite) { | |
326 char name[256]; | |
327 int y = VKEY_TOP + vkey * VKEY_ROWHT; | |
328 | |
329 rectfill(screen, | |
330 16, y, 719, y + VKEY_ROWHT - 1, | |
331 hilite ? hiliteColor : bgColor); | |
332 textout_ex(screen, aver16, vkeyNames[vkey], 24, y + 4, fgColor, -1); | |
333 for (int player = 0; player < N_PLAYERS; ++player) { | |
334 if (player + 1 == hilite) { | |
335 rect(screen, | |
336 240 + 240 * player, y, | |
337 479 + 240 * player, y + VKEY_ROWHT - 1, | |
338 fgColor); | |
339 } | |
340 getPkeyName(name, | |
341 keymappings[player][vkey].joy, | |
342 keymappings[player][vkey].stick, | |
343 keymappings[player][vkey].axis); | |
344 textout_ex(screen, aver16, name, 240 + 240 * player, y + 4, fgColor, -1); | |
345 } | |
346 } | |
347 | |
348 /** | |
349 * Waits for a key or button press. If any key but Esc is pressed, | |
350 * reassigns the vkey. Otherwise, does nothing. | |
351 * @param vkey the index of the vkey to reassign | |
352 * @return 0 if key not changed; nonzero if key was changed | |
353 */ | |
354 static int changeKey(int player, int vkey) { | |
355 int changed = 0; | |
356 int phase = 0; | |
357 | |
358 clearNewButton(); | |
359 while (!changed) { | |
360 int j, s, a; | |
361 | |
362 if (phase == 5) { | |
363 drawVkeyRow(vkey, 0); | |
364 } else if (phase == 0) { | |
365 drawVkeyRow(vkey, player + 1); | |
366 phase = 15; | |
367 } | |
368 --phase; | |
369 | |
370 if (keypressed()) { | |
371 int scancode; | |
372 ureadkey(&scancode); | |
373 if (scancode == KEY_ESC) { | |
374 changed = -1; | |
375 } | |
376 } | |
377 if (vkey < N_VKEYS && newButton(&j, &s, &a)) { | |
378 if (j >= 0 || s > 0 || a != KEY_ESC) { | |
379 keymappings[player][vkey].joy = j; | |
380 keymappings[player][vkey].stick = s; | |
381 keymappings[player][vkey].axis = a; | |
382 ezPlaySample("nextS_wav", 128); | |
383 changed = 1; | |
384 } else { | |
385 changed = -1; | |
386 } | |
387 } | |
388 if (wantsClose) { | |
389 changed = -1; | |
390 } | |
391 | |
392 rest(30); | |
393 } | |
394 | |
395 // Draw new vkey value | |
396 drawVkeyRow(vkey, 0); | |
397 return changed > 0; | |
398 } | |
399 | |
400 void configureKeys(void) { | |
401 clear_to_color(screen, bgColor); | |
402 textout_ex(screen, aver32, "LOCKJAW > Game Keys", 16, 32, fgColor, -1); | |
403 textout_ex(screen, aver16, | |
404 "Use arrow keys to select a key. To reassign the selected key, press Enter", | |
405 40, 80, fgColor, -1); | |
406 textout_ex(screen, aver16, | |
407 "and then the key you want to use. When done, press Esc.", | |
408 40, 96, fgColor, -1); | |
409 | |
410 // Draw each vkey's name | |
411 for (int vkey = 0; vkey < N_VKEYS; ++vkey) { | |
412 drawVkeyRow(vkey, 0); | |
413 } | |
414 | |
415 clearNewButton(); | |
416 int vkey = 0; | |
417 int player = 0; | |
418 | |
419 drawVkeyRow(vkey, player + 1); | |
420 | |
421 while (vkey >= 0 && !wantsClose) { | |
422 int scancode = 0; | |
423 | |
424 rest(30); | |
425 if (keypressed()) { | |
426 ureadkey(&scancode); | |
427 } | |
428 | |
429 switch (scancode) { | |
430 | |
431 case KEY_RIGHT: | |
432 if (player < N_PLAYERS - 1) { | |
433 ezPlaySample("shift_wav", 128); | |
434 drawVkeyRow(vkey, 0); | |
435 ++player; | |
436 drawVkeyRow(vkey, player + 1); | |
437 } | |
438 break; | |
439 | |
440 case KEY_LEFT: | |
441 if (player > 0) { | |
442 ezPlaySample("shift_wav", 128); | |
443 drawVkeyRow(vkey, 0); | |
444 --player; | |
445 drawVkeyRow(vkey, player + 1); | |
446 } | |
447 break; | |
448 | |
449 case KEY_UP: | |
450 if (vkey > 0) { | |
451 ezPlaySample("shift_wav", 128); | |
452 drawVkeyRow(vkey, 0); | |
453 --vkey; | |
454 drawVkeyRow(vkey, player + 1); | |
455 } | |
456 break; | |
457 case KEY_DOWN: | |
458 if (vkey < N_VKEYS - 1) { | |
459 ezPlaySample("shift_wav", 128); | |
460 drawVkeyRow(vkey, 0); | |
461 ++vkey; | |
462 drawVkeyRow(vkey, player + 1); | |
463 } | |
464 break; | |
465 case KEY_ENTER: | |
466 ezPlaySample("rotate_wav", 96); | |
467 while (vkey < N_VKEYS && changeKey(player, vkey)) { | |
468 rest(150); | |
469 ++vkey; | |
470 } | |
471 if (vkey >= N_VKEYS) { | |
472 ezPlaySample("land_wav", 128); | |
473 vkey = N_VKEYS - 1; | |
474 } else { | |
475 ezPlaySample("nextO_wav", 128); | |
476 } | |
477 drawVkeyRow(vkey, player + 1); | |
478 break; | |
479 case KEY_ESC: | |
480 vkey = -1; | |
481 break; | |
482 } | |
483 } | |
484 saveKeys(keysFileName); | |
485 ezPlaySample("line_wav", 128); | |
486 } | |
487 | |
488 | |
489 void initKeys(void) { | |
490 LOCK_FUNCTION(ljpcKeyListener); | |
491 LOCK_VARIABLE(lastScancodePressed); | |
492 LOCK_VARIABLE(oldKeyListener); | |
493 LOCK_FUNCTION(setWantsClose); | |
494 LOCK_VARIABLE(wantsClose); | |
495 | |
496 oldKeyListener = keyboard_lowlevel_callback; | |
497 keyboard_lowlevel_callback = ljpcKeyListener; | |
498 withJoystick = !install_joystick(JOY_TYPE_AUTODETECT); | |
499 loadKeys(keysFileName); | |
500 set_close_button_callback(setWantsClose); | |
501 } | |
502 | |
503 |