1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "common/scummsys.h"
24
25 #include "zvision/zvision.h"
26
27 #include "zvision/core/console.h"
28 #include "zvision/graphics/cursors/cursor_manager.h"
29 #include "zvision/graphics/render_manager.h"
30 #include "zvision/scripting/script_manager.h"
31 #include "zvision/scripting/menu.h"
32 #include "zvision/sound/zork_raw.h"
33 #include "zvision/text/string_manager.h"
34
35 #include "common/events.h"
36 #include "common/system.h"
37 #include "common/rational.h"
38 #include "audio/mixer.h"
39
40 #include "engines/util.h"
41
42 namespace ZVision {
43
pushKeyToCheatBuf(uint8 key)44 void ZVision::pushKeyToCheatBuf(uint8 key) {
45 for (int i = 0; i < KEYBUF_SIZE - 1; i++)
46 _cheatBuffer[i] = _cheatBuffer[i + 1];
47
48 _cheatBuffer[KEYBUF_SIZE - 1] = key;
49 }
50
checkCode(const char * code)51 bool ZVision::checkCode(const char *code) {
52 int codeLen = strlen(code);
53
54 if (codeLen > KEYBUF_SIZE)
55 return false;
56
57 for (int i = 0; i < codeLen; i++)
58 if (code[i] != _cheatBuffer[KEYBUF_SIZE - codeLen + i] && code[i] != '?')
59 return false;
60
61 return true;
62 }
63
getBufferedKey(uint8 pos)64 uint8 ZVision::getBufferedKey(uint8 pos) {
65 if (pos >= KEYBUF_SIZE)
66 return 0;
67 else
68 return _cheatBuffer[KEYBUF_SIZE - pos - 1];
69 }
70
shortKeys(Common::Event event)71 void ZVision::shortKeys(Common::Event event) {
72 if (event.kbd.hasFlags(Common::KBD_CTRL)) {
73 switch (event.kbd.keycode) {
74 case Common::KEYCODE_s:
75 if (_menu->getEnable() & kMenubarSave)
76 _scriptManager->changeLocation('g', 'j', 's', 'e', 0);
77 break;
78 case Common::KEYCODE_r:
79 if (_menu->getEnable() & kMenubarRestore)
80 _scriptManager->changeLocation('g', 'j', 'r', 'e', 0);
81 break;
82 case Common::KEYCODE_p:
83 if (_menu->getEnable() & kMenubarSettings)
84 _scriptManager->changeLocation('g', 'j', 'p', 'e', 0);
85 break;
86 case Common::KEYCODE_q:
87 if (_menu->getEnable() & kMenubarExit)
88 ifQuit();
89 break;
90 default:
91 break;
92 }
93 }
94 }
95
cheatCodes(uint8 key)96 void ZVision::cheatCodes(uint8 key) {
97 Location loc = _scriptManager->getCurrentLocation();
98 // Do not process cheat codes while in the game menus
99 if (loc.world == 'g' && loc.room == 'j')
100 return;
101
102 pushKeyToCheatBuf(key);
103
104 if (getGameId() == GID_GRANDINQUISITOR) {
105 if (checkCode("IMNOTDEAF")) {
106 // Unknown cheat
107 _renderManager->showDebugMsg(Common::String::format("IMNOTDEAF cheat or debug, not implemented"));
108 }
109
110 if (checkCode("3100OPB")) {
111 _renderManager->showDebugMsg(Common::String::format("Current location: %c%c%c%c",
112 _scriptManager->getStateValue(StateKey_World),
113 _scriptManager->getStateValue(StateKey_Room),
114 _scriptManager->getStateValue(StateKey_Node),
115 _scriptManager->getStateValue(StateKey_View)));
116 }
117
118 if (checkCode("KILLMENOW")) {
119 _scriptManager->changeLocation('g', 'j', 'd', 'e', 0);
120 _scriptManager->setStateValue(2201, 35);
121 }
122
123 if (checkCode("MIKESPANTS")) {
124 _scriptManager->changeLocation('g', 'j', 't', 'm', 0);
125 }
126
127 // There are 3 more cheats in script files:
128 // - "WHOAMI": gjcr.scr
129 // - "HUISOK": hp1e.scr
130 // - "EAT ME": uh1f.scr
131 } else if (getGameId() == GID_NEMESIS) {
132 if (checkCode("CHLOE")) {
133 _scriptManager->changeLocation('t', 'm', '2', 'g', 0);
134 _scriptManager->setStateValue(224, 1);
135 }
136
137 if (checkCode("77MASSAVE")) {
138 _renderManager->showDebugMsg(Common::String::format("Current location: %c%c%c%c",
139 _scriptManager->getStateValue(StateKey_World),
140 _scriptManager->getStateValue(StateKey_Room),
141 _scriptManager->getStateValue(StateKey_Node),
142 _scriptManager->getStateValue(StateKey_View)));
143 }
144
145 if (checkCode("IDKFA")) {
146 _scriptManager->changeLocation('t', 'w', '3', 'f', 0);
147 _scriptManager->setStateValue(249, 1);
148 }
149
150 if (checkCode("309NEWDORMA")) {
151 _scriptManager->changeLocation('g', 'j', 'g', 'j', 0);
152 }
153
154 if (checkCode("HELLOSAILOR")) {
155 Audio::AudioStream *soundStream;
156 if (loc == "vb10") {
157 soundStream = makeRawZorkStream("v000hpta.raw", this);
158 } else {
159 soundStream = makeRawZorkStream("v000hnta.raw", this);
160 }
161 Audio::SoundHandle handle;
162 _mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, soundStream);
163 }
164 }
165
166 if (checkCode("FRAME")) {
167 Common::String fpsStr = Common::String::format("FPS: %d", getFPS());
168 _renderManager->showDebugMsg(fpsStr);
169 }
170
171 if (checkCode("COMPUTERARCH"))
172 _renderManager->showDebugMsg("COMPUTERARCH: var-viewer not implemented");
173
174 // This cheat essentially toggles the GOxxxx cheat below
175 if (checkCode("XYZZY"))
176 _scriptManager->setStateValue(StateKey_DebugCheats, 1 - _scriptManager->getStateValue(StateKey_DebugCheats));
177
178 if (_scriptManager->getStateValue(StateKey_DebugCheats) == 1)
179 if (checkCode("GO????"))
180 _scriptManager->changeLocation(getBufferedKey(3),
181 getBufferedKey(2),
182 getBufferedKey(1),
183 getBufferedKey(0), 0);
184
185 // Show the Venus screen when "?" or "/" is pressed while inside the temple world
186 if (_scriptManager->getStateValue(StateKey_VenusEnable) == 1)
187 if (getBufferedKey(0) == 0xBF && _scriptManager->getStateValue(StateKey_World) == 't')
188 _scriptManager->changeLocation('g', 'j', 'h', 'e', 0);
189 }
190
processEvents()191 void ZVision::processEvents() {
192 while (_eventMan->pollEvent(_event)) {
193 switch (_event.type) {
194 case Common::EVENT_LBUTTONDOWN:
195 _cursorManager->cursorDown(true);
196 _scriptManager->setStateValue(StateKey_LMouse, 1);
197 _menu->onMouseDown(_event.mouse);
198 _scriptManager->addEvent(_event);
199 break;
200
201 case Common::EVENT_LBUTTONUP:
202 _cursorManager->cursorDown(false);
203 _scriptManager->setStateValue(StateKey_LMouse, 0);
204 _menu->onMouseUp(_event.mouse);
205 _scriptManager->addEvent(_event);
206 break;
207
208 case Common::EVENT_RBUTTONDOWN:
209 _cursorManager->cursorDown(true);
210 _scriptManager->setStateValue(StateKey_RMouse, 1);
211
212 if (getGameId() == GID_NEMESIS)
213 _scriptManager->inventoryCycle();
214 break;
215
216 case Common::EVENT_RBUTTONUP:
217 _cursorManager->cursorDown(false);
218 _scriptManager->setStateValue(StateKey_RMouse, 0);
219 break;
220
221 case Common::EVENT_MOUSEMOVE:
222 onMouseMove(_event.mouse);
223 break;
224
225 case Common::EVENT_KEYDOWN: {
226 switch (_event.kbd.keycode) {
227 case Common::KEYCODE_d:
228 if (_event.kbd.hasFlags(Common::KBD_CTRL)) {
229 // Start the debugger
230 _console->attach();
231 _console->onFrame();
232 }
233 break;
234
235 case Common::KEYCODE_LEFT:
236 case Common::KEYCODE_RIGHT:
237 if (_renderManager->getRenderTable()->getRenderState() == RenderTable::PANORAMA)
238 _keyboardVelocity = (_event.kbd.keycode == Common::KEYCODE_LEFT ?
239 -_scriptManager->getStateValue(StateKey_KbdRotateSpeed) :
240 _scriptManager->getStateValue(StateKey_KbdRotateSpeed)) * 2;
241 break;
242
243 case Common::KEYCODE_UP:
244 case Common::KEYCODE_DOWN:
245 if (_renderManager->getRenderTable()->getRenderState() == RenderTable::TILT)
246 _keyboardVelocity = (_event.kbd.keycode == Common::KEYCODE_UP ?
247 -_scriptManager->getStateValue(StateKey_KbdRotateSpeed) :
248 _scriptManager->getStateValue(StateKey_KbdRotateSpeed)) * 2;
249 break;
250
251 case Common::KEYCODE_F10: {
252 Common::String fpsStr = Common::String::format("FPS: %d", getFPS());
253 _renderManager->showDebugMsg(fpsStr);
254 }
255 break;
256 default:
257 break;
258 }
259
260 uint8 vkKey = getZvisionKey(_event.kbd.keycode);
261
262 _scriptManager->setStateValue(StateKey_KeyPress, vkKey);
263
264 _scriptManager->addEvent(_event);
265 shortKeys(_event);
266 cheatCodes(vkKey);
267 }
268 break;
269 case Common::EVENT_KEYUP:
270 _scriptManager->addEvent(_event);
271 switch (_event.kbd.keycode) {
272 case Common::KEYCODE_LEFT:
273 case Common::KEYCODE_RIGHT:
274 if (_renderManager->getRenderTable()->getRenderState() == RenderTable::PANORAMA)
275 _keyboardVelocity = 0;
276 break;
277 case Common::KEYCODE_UP:
278 case Common::KEYCODE_DOWN:
279 if (_renderManager->getRenderTable()->getRenderState() == RenderTable::TILT)
280 _keyboardVelocity = 0;
281 break;
282 default:
283 break;
284 }
285 break;
286 default:
287 break;
288 }
289 }
290 }
291
onMouseMove(const Common::Point & pos)292 void ZVision::onMouseMove(const Common::Point &pos) {
293 _menu->onMouseMove(pos);
294 Common::Point imageCoord(_renderManager->screenSpaceToImageSpace(pos));
295
296 bool cursorWasChanged = false;
297
298 // Graph of the function governing rotation velocity:
299 //
300 // |---------------- working window ------------------|
301 // ^ |---------|
302 // | |
303 // +Max velocity | rotation screen edge offset
304 // | /|
305 // | / |
306 // | / |
307 // | / |
308 // | / |
309 // | / |
310 // | / |
311 // | / |
312 // | / |
313 // Zero velocity |______________________________ ______________________________/_________|__________________________>
314 // | Position -> | /
315 // | | /
316 // | | /
317 // | | /
318 // | | /
319 // | | /
320 // | | /
321 // | | /
322 // | | /
323 // -Max velocity | |/
324 // |
325 // |
326 // ^
327
328 // Clip the horizontal mouse position to the working window
329 Common::Point clippedPos = pos;
330 clippedPos.x = CLIP<int16>(pos.x, _workingWindow.left + 1, _workingWindow.right - 1);
331
332 if (_workingWindow.contains(clippedPos)) {
333 cursorWasChanged = _scriptManager->onMouseMove(clippedPos, imageCoord);
334
335 RenderTable::RenderState renderState = _renderManager->getRenderTable()->getRenderState();
336 if (renderState == RenderTable::PANORAMA) {
337 if (clippedPos.x >= _workingWindow.left && clippedPos.x < _workingWindow.left + ROTATION_SCREEN_EDGE_OFFSET) {
338
339 int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4;
340 if (mspeed <= 0) {
341 mspeed = 25;
342 }
343 _mouseVelocity = MIN(((Common::Rational(mspeed, ROTATION_SCREEN_EDGE_OFFSET) * (clippedPos.x - _workingWindow.left)) - mspeed).toInt(), -1);
344
345
346 _cursorManager->changeCursor(CursorIndex_Left);
347 cursorWasChanged = true;
348 } else if (clippedPos.x <= _workingWindow.right && clippedPos.x > _workingWindow.right - ROTATION_SCREEN_EDGE_OFFSET) {
349
350 int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4;
351 if (mspeed <= 0) {
352 mspeed = 25;
353 }
354 _mouseVelocity = MAX((Common::Rational(mspeed, ROTATION_SCREEN_EDGE_OFFSET) * (clippedPos.x - _workingWindow.right + ROTATION_SCREEN_EDGE_OFFSET)).toInt(), 1);
355
356 _cursorManager->changeCursor(CursorIndex_Right);
357 cursorWasChanged = true;
358 } else {
359 _mouseVelocity = 0;
360 }
361 } else if (renderState == RenderTable::TILT) {
362 if (clippedPos.y >= _workingWindow.top && clippedPos.y < _workingWindow.top + ROTATION_SCREEN_EDGE_OFFSET) {
363
364 int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4;
365 if (mspeed <= 0) {
366 mspeed = 25;
367 }
368 _mouseVelocity = MIN(((Common::Rational(mspeed, ROTATION_SCREEN_EDGE_OFFSET) * (pos.y - _workingWindow.top)) - mspeed).toInt(), -1);
369
370 _cursorManager->changeCursor(CursorIndex_UpArr);
371 cursorWasChanged = true;
372 } else if (clippedPos.y <= _workingWindow.bottom && clippedPos.y > _workingWindow.bottom - ROTATION_SCREEN_EDGE_OFFSET) {
373
374 int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4;
375 if (mspeed <= 0) {
376 mspeed = 25;
377 }
378 _mouseVelocity = MAX((Common::Rational(MAX_ROTATION_SPEED, ROTATION_SCREEN_EDGE_OFFSET) * (pos.y - _workingWindow.bottom + ROTATION_SCREEN_EDGE_OFFSET)).toInt(), 1);
379
380 _cursorManager->changeCursor(CursorIndex_DownArr);
381 cursorWasChanged = true;
382 } else {
383 _mouseVelocity = 0;
384 }
385 } else {
386 _mouseVelocity = 0;
387 }
388 } else {
389 _mouseVelocity = 0;
390 }
391
392 if (!cursorWasChanged) {
393 _cursorManager->changeCursor(CursorIndex_Idle);
394 }
395 }
396
getZvisionKey(Common::KeyCode scummKeyCode)397 uint8 ZVision::getZvisionKey(Common::KeyCode scummKeyCode) {
398 if (scummKeyCode >= Common::KEYCODE_a && scummKeyCode <= Common::KEYCODE_z)
399 return 0x41 + scummKeyCode - Common::KEYCODE_a;
400 if (scummKeyCode >= Common::KEYCODE_0 && scummKeyCode <= Common::KEYCODE_9)
401 return 0x30 + scummKeyCode - Common::KEYCODE_0;
402 if (scummKeyCode >= Common::KEYCODE_F1 && scummKeyCode <= Common::KEYCODE_F15)
403 return 0x70 + scummKeyCode - Common::KEYCODE_F1;
404 if (scummKeyCode >= Common::KEYCODE_KP0 && scummKeyCode <= Common::KEYCODE_KP9)
405 return 0x60 + scummKeyCode - Common::KEYCODE_KP0;
406
407 switch (scummKeyCode) {
408 case Common::KEYCODE_BACKSPACE:
409 return 0x8;
410 case Common::KEYCODE_TAB:
411 return 0x9;
412 case Common::KEYCODE_CLEAR:
413 return 0xC;
414 case Common::KEYCODE_RETURN:
415 return 0xD;
416 case Common::KEYCODE_CAPSLOCK:
417 return 0x14;
418 case Common::KEYCODE_ESCAPE:
419 return 0x1B;
420 case Common::KEYCODE_SPACE:
421 return 0x20;
422 case Common::KEYCODE_PAGEUP:
423 return 0x21;
424 case Common::KEYCODE_PAGEDOWN:
425 return 0x22;
426 case Common::KEYCODE_END:
427 return 0x23;
428 case Common::KEYCODE_HOME:
429 return 0x24;
430 case Common::KEYCODE_LEFT:
431 return 0x25;
432 case Common::KEYCODE_UP:
433 return 0x26;
434 case Common::KEYCODE_RIGHT:
435 return 0x27;
436 case Common::KEYCODE_DOWN:
437 return 0x28;
438 case Common::KEYCODE_PRINT:
439 return 0x2A;
440 case Common::KEYCODE_INSERT:
441 return 0x2D;
442 case Common::KEYCODE_DELETE:
443 return 0x2E;
444 case Common::KEYCODE_HELP:
445 return 0x2F;
446 case Common::KEYCODE_KP_MULTIPLY:
447 return 0x6A;
448 case Common::KEYCODE_KP_PLUS:
449 return 0x6B;
450 case Common::KEYCODE_KP_MINUS:
451 return 0x6D;
452 case Common::KEYCODE_KP_PERIOD:
453 return 0x6E;
454 case Common::KEYCODE_KP_DIVIDE:
455 return 0x6F;
456 case Common::KEYCODE_NUMLOCK:
457 return 0x90;
458 case Common::KEYCODE_SCROLLOCK:
459 return 0x91;
460 case Common::KEYCODE_LSHIFT:
461 return 0xA0;
462 case Common::KEYCODE_RSHIFT:
463 return 0xA1;
464 case Common::KEYCODE_LCTRL:
465 return 0xA2;
466 case Common::KEYCODE_RCTRL:
467 return 0xA3;
468 case Common::KEYCODE_MENU:
469 return 0xA5;
470 case Common::KEYCODE_LEFTBRACKET:
471 return 0xDB;
472 case Common::KEYCODE_RIGHTBRACKET:
473 return 0xDD;
474 case Common::KEYCODE_SEMICOLON:
475 return 0xBA;
476 case Common::KEYCODE_BACKSLASH:
477 return 0xDC;
478 case Common::KEYCODE_QUOTE:
479 return 0xDE;
480 case Common::KEYCODE_SLASH:
481 return 0xBF;
482 case Common::KEYCODE_TILDE:
483 return 0xC0;
484 case Common::KEYCODE_COMMA:
485 return 0xBC;
486 case Common::KEYCODE_PERIOD:
487 return 0xBE;
488 case Common::KEYCODE_MINUS:
489 return 0xBD;
490 case Common::KEYCODE_PLUS:
491 return 0xBB;
492 default:
493 return 0;
494 }
495
496 return 0;
497 }
498
ifQuit()499 bool ZVision::ifQuit() {
500 if (_renderManager->askQuestion(_stringManager->getTextLine(StringManager::ZVISION_STR_EXITPROMT))) {
501 quitGame();
502 return true;
503 }
504 return false;
505 }
506
507 } // End of namespace ZVision
508