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