annotate src/pcjoy.c @ 0:c84446dfb3f5

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