00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #ifdef HAVE_CONFIG_H
00020 # include <config.h>
00021 #endif
00022
00023 #include "SDL_keyboard.h"
00024
00025 #include "actors.h"
00026 #include "keys.h"
00027 #include "exult.h"
00028 #include "game.h"
00029 #include "cheat.h"
00030 #include "U7file.h"
00031 #include "Scroll_gump.h"
00032 #include "mouse.h"
00033 #include "gamewin.h"
00034 #include "utils.h"
00035 #include "keyactions.h"
00036
00037 #ifndef UNDER_CE
00038 using std::pair;
00039 using std::atoi;
00040 using std::cerr;
00041 using std::cout;
00042 using std::clog;
00043 using std::endl;
00044 using std::ifstream;
00045 using std::isspace;
00046 using std::strchr;
00047 using std::string;
00048 using std::strlen;
00049 using std::vector;
00050 #endif
00051
00052 static class Chardata
00053 {
00054 public:
00055 std::string whitespace;
00056 Chardata()
00057 {
00058 for(size_t i=0;i<256;i++)
00059 if(isspace(i))
00060 whitespace+=static_cast<char>(i);
00061 }
00062 } chardata;
00063
00064 typedef void(*ActionFunc)(int*);
00065
00066 const struct Action {
00067 const char *s;
00068 ActionFunc func;
00069 ActionFunc func_release;
00070 const char* desc;
00071 bool show;
00072 bool cheat;
00073 Exult_Game game;
00074 bool allow_during_dont_move;
00075 } ExultActions[] = {
00076 { "QUIT", ActionQuit, 0, "Quit", true, false, NONE, true },
00077 { "SAVE_RESTORE", ActionFileGump, 0, "Save/restore", true, false, NONE, true },
00078 { "QUICKSAVE", ActionQuicksave, 0, "Quick-save", true, false, NONE, false },
00079 { "QUICKRESTORE",
00080 ActionQuickrestore, 0, "Quick-restore", true, false, NONE, true },
00081 { "ABOUT", ActionAbout, 0, "About Exult", true, false, NONE, false },
00082 { "HELP", ActionHelp, 0, "List keys", true, false, NONE, false },
00083 { "CLOSE_GUMPS", ActionCloseGumps, 0, "Close gumps", false, false, NONE, false},
00084 { "CLOSE_OR_MENU", ActionCloseOrMenu, 0, "Game menu", true, false, NONE, true },
00085 { "SCREENSHOT",
00086 ActionScreenshot, 0, "Take screenshot", true, false, NONE, true },
00087 { "GAME_MENU", ActionMenuGump, 0, "Game Menu", true, false, NONE, true },
00088 { "OLD_FILE_GUMP",
00089 ActionOldFileGump, 0, "Save/restore", true, false, NONE, true },
00090
00091 { "REPAINT", ActionRepaint, 0, "Repaint screen", false, false, NONE, true },
00092 { "RESOLUTION_INCREASE",
00093 ActionResIncrease, 0, "Increase resolution", true, true, NONE, true },
00094 { "RESOLUTION_DECREASE",
00095 ActionResDecrease, 0, "Decrease resolution", true, true, NONE, true },
00096 { "BRIGHTER",
00097 ActionBrighter, 0, "Increase brightness", true, false, NONE, true },
00098 { "DARKER", ActionDarker, 0, "Decrease brightness", true, false, NONE, true },
00099 { "TOGGLE_FULLSCREEN",
00100 ActionFullscreen, 0, "Toggle fullscreen", true, false, NONE, true },
00101
00102 { "USEITEM", ActionUseItem, 0, "Use item", false, false, NONE, false },
00103 { "USEFOOD", ActionUseFood, 0, "Use food", false, false, NONE, false },
00104 { "TOGGLE_COMBAT", ActionCombat, 0, "Toggle combat", true, false, NONE, false },
00105 { "PAUSE_COMBAT", ActionCombatPause, 0, "Pause combat", true, false, NONE, false },
00106 { "TARGET_MODE", ActionTarget, 0, "Target mode", true, false, NONE, false },
00107 { "INVENTORY", ActionInventory, 0, "Show inventory", true, false, NONE, false },
00108 { "TRY_KEYS", ActionTryKeys, 0, "Try keys", true, false, NONE, false },
00109 { "STATS", ActionStats, 0, "Show stats", true, false, NONE, false },
00110 { "COMBAT_STATS",
00111 ActionCombatStats, 0, "Show combat stats", true, false, SERPENT_ISLE, false },
00112 { "FACE_STATS",
00113 ActionFaceStats, 0, "Change Face Stats State", true, false, NONE, false },
00114
00115 { "SHOW_SI_INTRO",
00116 ActionSIIntro, 0, "Show Alternate SI intro", true, true, SERPENT_ISLE, false },
00117 { "SHOW_ENDGAME", ActionEndgame, 0, "Show endgame", true, true, NONE, false },
00118 { "SCROLL_LEFT", ActionScrollLeft, 0, "Scroll left", true, true, NONE, false },
00119 { "SCROLL_RIGHT", ActionScrollRight, 0, "Scroll right", true, true, NONE, false },
00120 { "SCROLL_UP", ActionScrollUp, 0, "Scroll up", true, true, NONE, false },
00121 { "SCROLL_DOWN", ActionScrollDown, 0, "Scroll down", true, true, NONE, false },
00122 { "WALK_WEST", ActionWalkWest, ActionStopWalking, "Walk west", true, false, NONE, false },
00123 { "WALK_EAST", ActionWalkEast, ActionStopWalking, "Walk east", true, false, NONE, false },
00124 { "WALK_NORTH", ActionWalkNorth, ActionStopWalking, "Walk north", true, false, NONE, false },
00125 { "WALK_SOUTH", ActionWalkSouth, ActionStopWalking, "Walk south", true, false, NONE, false },
00126 { "WALK_NORTH_EAST", ActionWalkNorthEast, ActionStopWalking, "Walk north-east", true, false, NONE, false },
00127 { "WALK_SOUTH_EAST", ActionWalkSouthEast, ActionStopWalking, "Walk south-east", true, false, NONE, false },
00128 { "WALK_NORTH_WEST", ActionWalkNorthWest, ActionStopWalking, "Walk north-west", true, false, NONE, false },
00129 { "WALK_SOUTH_WEST", ActionWalkSouthWest, ActionStopWalking, "Walk south-west", true, false, NONE, false },
00130 { "CENTER_SCREEN", ActionCenter, 0, "Center screen", true, true, NONE, false },
00131 { "SHAPE_BROWSER",
00132 ActionShapeBrowser, 0, "Shape browser", true, true, NONE, false },
00133 { "CREATE_ITEM",
00134 ActionCreateShape, 0, "Create last shape", true, true, NONE, false },
00135 { "DELETE_OBJECT",
00136 ActionDeleteObject, 0, "Delete object", true, true, NONE, false },
00137 { "DELETE_SELECTED",
00138 ActionDeleteSelected, 0, "Delete selected", true, true, NONE, true },
00139 { "MOVE_SELECTED",
00140 ActionMoveSelected, 0, "Move selected", true, true, NONE, true },
00141 { "TOGGLE_EGGS",
00142 ActionToggleEggs, 0, "Toggle egg display", true, true, NONE, false },
00143 { "TOGGLE_GOD_MODE",
00144 ActionGodMode, 0, "Toggle god mode", true, true, NONE, false },
00145 { "CHANGE_GENDER",
00146 ActionGender, 0, "Change gender", true, true, NONE, false },
00147 { "CHEAT_HELP", ActionCheatHelp, 0, "List cheat keys", true, true, NONE, false },
00148 { "TOGGLE_INFRAVISION",
00149 ActionInfravision, 0, "Toggle infravision", true, true, NONE, false },
00150 { "SKIPLIFT_DECREMENT",
00151 ActionSkipLift, 0, "Decrement skiplift", true, true, NONE, false },
00152 { "TOGGLE_MAP_EDITOR",
00153 ActionMapEditor, 0, "Toggle map-editor mode", true, true, NONE, true },
00154 { "TOGGLE_HACK_MOVER",
00155 ActionHackMover, 0, "Toggle hack-mover mode", true, true, NONE, false },
00156 { "MAP_TELEPORT", ActionMapTeleport, 0, "Map teleport", true, true, NONE, false },
00157 { "CURSOR_TELEPORT",
00158 ActionTeleport, 0, "Teleport to cursor", true, true, NONE, false },
00159 { "NEXT_TIME_PERIOD",
00160 ActionTime, 0, "Next time period", true, true, NONE, false },
00161 { "TOGGLE_WIZARD_MODE",
00162 ActionWizard, 0, "Toggle archwizard mode", true, true, NONE, false },
00163 { "PARTY_HEAL", ActionHeal, 0, "Heal party", true, true, NONE, false },
00164 { "PARTY_INCREASE_LEVEL",
00165 ActionLevelup, 0, "Level-up party", true, true, NONE, false },
00166 { "CHEAT_SCREEN", ActionCheatScreen, 0, "Cheat Screen", true, true, NONE, true },
00167 { "PICK_POCKET",
00168 ActionPickPocket, 0, "Toggle Pick Pocket", true, true, NONE, false },
00169 { "NPC_NUMBERS",
00170 ActionNPCNumbers, 0, "Toggle NPC Numbers", true, true, NONE, false },
00171 { "GRAB_ACTOR",
00172 ActionGrabActor, 0, "Grab NPC for Cheat Screen", true, true, NONE, false },
00173 { "CUT",
00174 ActionCut, 0, "Cut Selected Objects", true, false, NONE, true},
00175 { "COPY",
00176 ActionCopy, 0, "Copy Selected Objects", true, false, NONE, true},
00177 { "PASTE",
00178 ActionPaste, 0, "Paste Selected Objects", true, false, NONE, true},
00179
00180 { "PLAY_MUSIC", ActionPlayMusic, 0, "Play song", false, true, NONE, false },
00181 { "TOGGLE_NAKED",
00182 ActionNaked, 0, "Toggle naked mode", true, true, SERPENT_ISLE, false },
00183 { "TOGGLE_PETRA",
00184 ActionPetra, 0, "Toggle Petra mode", true, true, SERPENT_ISLE, false },
00185 { "CHANGE_SKIN",
00186 ActionSkinColour, 0, "Change skin colour", true, true, NONE, false },
00187 { "SOUND_TESTER",
00188 ActionSoundTester, 0, "Sound tester", false, true, NONE, false },
00189 { "TEST", ActionTest, 0, "Test", false, false, NONE, false },
00190 { "", 0, 0, "", false, false, NONE, false }
00191 };
00192
00193 const struct {
00194 const char *s;
00195 SDLKey k;
00196 } SDLKeyStringTable[] = {
00197 {"BACKSPACE", SDLK_BACKSPACE},
00198 {"TAB", SDLK_TAB},
00199 {"ENTER", SDLK_RETURN},
00200 {"PAUSE", SDLK_PAUSE},
00201 {"ESC", SDLK_ESCAPE},
00202 {"SPACE", SDLK_SPACE},
00203 {"DEL", SDLK_DELETE},
00204 {"KP0", SDLK_KP0},
00205 {"KP1", SDLK_KP1},
00206 {"KP2", SDLK_KP2},
00207 {"KP3", SDLK_KP3},
00208 {"KP4", SDLK_KP4},
00209 {"KP5", SDLK_KP5},
00210 {"KP6", SDLK_KP6},
00211 {"KP7", SDLK_KP7},
00212 {"KP8", SDLK_KP8},
00213 {"KP9", SDLK_KP9},
00214 {"KP.", SDLK_KP_PERIOD},
00215 {"KP/", SDLK_KP_DIVIDE},
00216 {"KP*", SDLK_KP_MULTIPLY},
00217 {"KP-", SDLK_KP_MINUS},
00218 {"KP+", SDLK_KP_PLUS},
00219 {"KP_ENTER", SDLK_KP_ENTER},
00220 {"UP", SDLK_UP},
00221 {"DOWN", SDLK_DOWN},
00222 {"RIGHT", SDLK_RIGHT},
00223 {"LEFT", SDLK_LEFT},
00224 {"INSERT", SDLK_INSERT},
00225 {"HOME", SDLK_HOME},
00226 {"END", SDLK_END},
00227 {"PAGEUP", SDLK_PAGEUP},
00228 {"PAGEDOWN", SDLK_PAGEDOWN},
00229 {"F1", SDLK_F1},
00230 {"F2", SDLK_F2},
00231 {"F3", SDLK_F3},
00232 {"F4", SDLK_F4},
00233 {"F5", SDLK_F5},
00234 {"F6", SDLK_F6},
00235 {"F7", SDLK_F7},
00236 {"F8", SDLK_F8},
00237 {"F9", SDLK_F9},
00238 {"F10", SDLK_F10},
00239 {"F11", SDLK_F11},
00240 {"F12", SDLK_F12},
00241 {"F13", SDLK_F13},
00242 {"F14", SDLK_F14},
00243 {"F15", SDLK_F15},
00244 {"", SDLK_UNKNOWN}
00245 };
00246
00247
00248 typedef std::map<std::string, SDLKey> ParseKeyMap;
00249 typedef std::map<std::string, const Action*> ParseActionMap;
00250
00251 static ParseKeyMap keys;
00252 static ParseActionMap actions;
00253
00254
00255 KeyBinder::KeyBinder()
00256 {
00257 FillParseMaps();
00258 }
00259
00260 KeyBinder::~KeyBinder()
00261 {
00262 }
00263
00264 void KeyBinder::AddKeyBinding( SDLKey key, int mod, const Action* action,
00265 int nparams, int* params)
00266 {
00267 SDL_keysym k;
00268 ActionType a;
00269
00270 k.scancode = 0;
00271 k.sym = key;
00272 k.mod = (SDLMod) mod;
00273 k.unicode = 0;
00274 a.action = action;
00275 int i;
00276 for (i = 0; i < c_maxparams && i < nparams; i++)
00277 a.params[i] = params[i];
00278 for (i = nparams; i < c_maxparams; i++)
00279 a.params[i] = -1;
00280
00281 bindings[k] = a;
00282 }
00283
00284 bool KeyBinder::DoAction(ActionType a, bool press)
00285 {
00286 if (a.action->cheat && !cheat())
00287 return true;
00288 if (a.action->game != NONE && a.action->game != Game::get_game_type())
00289 return true;
00290
00291
00292 if (a.action->allow_during_dont_move
00293 || !Game_window::get_instance()->main_actor_dont_move()
00294 || cheat.in_map_editor())
00295 {
00296 if (press)
00297 a.action->func(a.params);
00298 else
00299 {
00300 if(a.action->func_release != NULL)
00301 a.action->func_release(a.params);
00302 }
00303 }
00304
00305 return true;
00306 }
00307
00308 bool KeyBinder::HandleEvent(SDL_Event &ev)
00309 {
00310 SDL_keysym key = ev.key.keysym;
00311 KeyMap::iterator sdlkey_index;
00312
00313 if (ev.type != SDL_KEYDOWN && ev.type != SDL_KEYUP)
00314 return false;
00315
00316 key.mod = KMOD_NONE;
00317 if (ev.key.keysym.mod & KMOD_SHIFT)
00318 key.mod = (SDLMod)(key.mod | KMOD_SHIFT);
00319 if (ev.key.keysym.mod & KMOD_CTRL)
00320 key.mod = (SDLMod)(key.mod | KMOD_CTRL);
00321 #if defined(MACOS) || defined(MACOSX)
00322
00323 if (ev.key.keysym.mod & KMOD_META)
00324 key.mod = (SDLMod)(key.mod | KMOD_ALT);
00325 #else
00326 if (ev.key.keysym.mod & KMOD_ALT)
00327 key.mod = (SDLMod)(key.mod | KMOD_ALT);
00328 #endif
00329
00330 sdlkey_index = bindings.find(key);
00331 if (sdlkey_index != bindings.end())
00332 return DoAction((*sdlkey_index).second, ev.type==SDL_KEYDOWN);
00333
00334 return false;
00335 }
00336
00337 void KeyBinder::ShowHelp()
00338 {
00339 Scroll_gump *scroll;
00340 scroll = new Scroll_gump();
00341
00342 std::vector<string>::iterator iter;
00343
00344 for (iter = keyhelp.begin(); iter != keyhelp.end(); iter++)
00345 scroll->add_text(iter->c_str());
00346
00347 scroll->paint();
00348 do
00349 {
00350 int x, y;
00351 Get_click(x,y, Mouse::hand);
00352 } while (scroll->show_next_page());
00353 Game_window::get_instance()->paint();
00354 delete scroll;
00355 }
00356
00357 void KeyBinder::ShowCheatHelp()
00358 {
00359 Scroll_gump *scroll;
00360 scroll = new Scroll_gump();
00361
00362 std::vector<string>::iterator iter;
00363
00364 for (iter = cheathelp.begin(); iter != cheathelp.end(); iter++)
00365 scroll->add_text(iter->c_str());
00366
00367 scroll->paint();
00368 do
00369 {
00370 int x, y;
00371 Get_click(x,y, Mouse::hand);
00372 } while (scroll->show_next_page());
00373 Game_window::get_instance()->paint();
00374 delete scroll;
00375 }
00376
00377 void KeyBinder::ParseText(char *text, int len)
00378 {
00379 char *ptr, *end;
00380 const char LF = '\n';
00381
00382 ptr = text;
00383
00384
00385 while ((ptr - text) < len && (end = strchr(ptr, LF)) != 0) {
00386 *end = '\0';
00387 ParseLine(ptr);
00388 ptr = end + 1;
00389 }
00390 }
00391
00392 static void skipspace(string &s) {
00393 size_t i=s.find_first_not_of(chardata.whitespace);
00394 if(i&&i!=string::npos)
00395 s.erase(0,i);
00396 }
00397
00398
00399 void KeyBinder::ParseLine(char *line)
00400 {
00401 size_t i;
00402 SDL_keysym k;
00403 ActionType a;
00404 k.sym = SDLK_UNKNOWN;
00405 k.mod = KMOD_NONE;
00406 string s = line, u;
00407 string d, desc, keycode;
00408 bool show;
00409
00410 skipspace(s);
00411
00412
00413 if (s.length() == 0 || s[0] == '#')
00414 return;
00415
00416 u = s;
00417 to_uppercase(u);
00418
00419
00420 while (s.length() && !isspace(s[0])) {
00421
00422
00423 if (u.substr(0,4) == "ALT-") {
00424 k.mod = (SDLMod)(k.mod | KMOD_ALT);
00425 s.erase(0,4); u.erase(0,4);
00426
00427 } else if (u.substr(0,5) == "CTRL-") {
00428 k.mod = (SDLMod)(k.mod | KMOD_CTRL);
00429 s.erase(0,5); u.erase(0,5);
00430
00431 } else if (u.substr(0,6) == "SHIFT-") {
00432 k.mod = (SDLMod)(k.mod | KMOD_SHIFT);
00433 s.erase(0,6); u.erase(0,6);
00434 } else {
00435
00436 i=s.find_first_of(chardata.whitespace);
00437
00438 keycode = s.substr(0, i); s.erase(0, i);
00439 string t(keycode);
00440 to_uppercase(t);
00441
00442 if (t.length() == 0) {
00443 cerr << "Keybinder: parse error in line: " << s << endl;
00444 return;
00445 } else if (t.length() == 1) {
00446
00447 char c = t[0];
00448 if (c >= 33 && c <= 122 && c != 37) {
00449 if (c >= 'A' && c <= 'Z')
00450 c += 32;
00451 k.sym = static_cast<SDLKey>(c);
00452 } else {
00453 cerr << "Keybinder: unsupported key: " << keycode << endl;
00454 }
00455 } else {
00456
00457 ParseKeyMap::iterator key_index;
00458 key_index = keys.find(t);
00459 if (key_index != keys.end()) {
00460 k.sym = (*key_index).second;
00461 } else {
00462 cerr << "Keybinder: unsupported key: " << keycode << endl;
00463 return;
00464 }
00465 }
00466 }
00467 }
00468
00469 if (k.sym == SDLK_UNKNOWN) {
00470 cerr << "Keybinder: parse error in line: " << s << endl;
00471 return;
00472 }
00473
00474
00475 skipspace(s);
00476
00477 i=s.find_first_of(chardata.whitespace);
00478 string t = s.substr(0, i); s.erase(0, i);
00479 to_uppercase(t);
00480
00481 ParseActionMap::iterator action_index;
00482 action_index = actions.find(t);
00483 if (action_index != actions.end()) {
00484 a.action = (*action_index).second;
00485 } else {
00486 cerr << "Keybinder: unsupported action: " << t << endl;
00487 return;
00488 }
00489
00490
00491 skipspace(s);
00492
00493 int np = 0;
00494 while (s.length() && s[0] != '#' && np < c_maxparams) {
00495 i=s.find_first_of(chardata.whitespace);
00496 string t = s.substr(0, i);
00497 s.erase(0, i);
00498 skipspace(s);
00499
00500 int p = atoi(t.c_str());
00501 a.params[np++] = p;
00502 }
00503
00504
00505 if (s.length() >= 1 && s[0] == '#') {
00506 if (s.length() >= 2 && s[1] == '-') {
00507 show = false;
00508 } else {
00509 s.erase(0,1);
00510 skipspace(s);
00511 d = s;
00512 show = true;
00513 }
00514 } else {
00515 d = a.action->desc;
00516 show = a.action->show;
00517 }
00518
00519 if (show) {
00520 desc = "";
00521 if (k.mod & KMOD_CTRL)
00522 desc += "Ctrl-";
00523 #if defined(MACOS) || defined(MACOSX)
00524 if (k.mod & KMOD_ALT)
00525 desc += "Cmd-";
00526 #else
00527 if (k.mod & KMOD_ALT)
00528 desc += "Alt-";
00529 #endif
00530 if (k.mod & KMOD_SHIFT)
00531 desc += "Shift-";
00532 desc += keycode;
00533
00534 desc += " - " + d;
00535
00536
00537 if (a.action->cheat)
00538 cheathelp.push_back(desc);
00539 else
00540 keyhelp.push_back(desc);
00541 }
00542
00543
00544 AddKeyBinding(k.sym, k.mod, a.action, np, a.params);
00545 }
00546
00547 void KeyBinder::LoadFromFile(const char* filename)
00548 {
00549 ifstream keyfile;
00550
00551 Flush();
00552
00553 cout << "Loading keybindings from file " << filename << endl;
00554
00555 U7open(keyfile, filename, true);
00556 char temp[1024];
00557 while(!keyfile.eof()) {
00558 keyfile.getline(temp, 1024);
00559 if (keyfile.gcount() >= 1023) {
00560 cerr << "Keybinder: parse error: line too long. Skipping rest of file."
00561 << endl;
00562 return;
00563 }
00564 ParseLine(temp);
00565 }
00566 keyfile.close();
00567 }
00568
00569 void KeyBinder::LoadDefaults()
00570 {
00571 Flush();
00572
00573 cout << "Loading default keybindings" << endl;
00574
00575 str_int_pair resource = game->get_resource("config/defaultkeys");
00576
00577 U7object txtobj(resource.str, resource.num);
00578 size_t len;
00579 char *txt = txtobj.retrieve(len);
00580 ParseText(txt, len);
00581
00582 delete [] txt;
00583 }
00584
00585
00586 void KeyBinder::FillParseMaps()
00587 {
00588 int i;
00589 for (i = 0; strlen(SDLKeyStringTable[i].s) > 0; i++)
00590 keys[SDLKeyStringTable[i].s] = SDLKeyStringTable[i].k;
00591
00592 for (i = 0; strlen(ExultActions[i].s) > 0; i++)
00593 actions[ExultActions[i].s] = &(ExultActions[i]);
00594 }