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 "agi/preagi.h"
24 #include "agi/preagi_winnie.h"
25 #include "agi/graphics.h"
26 
27 #include "graphics/cursorman.h"
28 
29 #include "common/events.h"
30 #include "common/memstream.h"
31 #include "common/savefile.h"
32 #include "common/textconsole.h"
33 
34 #include "audio/mididrv.h"
35 
36 namespace Agi {
37 
parseRoomHeader(WTP_ROOM_HDR * roomHdr,byte * buffer,int len)38 void WinnieEngine::parseRoomHeader(WTP_ROOM_HDR *roomHdr, byte *buffer, int len) {
39 	int i;
40 
41 	Common::MemoryReadStreamEndian readS(buffer, len, _isBigEndian);
42 
43 	roomHdr->roomNumber = readS.readByte();
44 	roomHdr->objId = readS.readByte();
45 	roomHdr->ofsPic = readS.readUint16();
46 	roomHdr->fileLen = readS.readUint16();
47 	roomHdr->reserved0 = readS.readUint16();
48 
49 	for (i = 0; i < IDI_WTP_MAX_DIR; i++)
50 		roomHdr->roomNew[i] = readS.readByte();
51 
52 	roomHdr->objX = readS.readByte();
53 	roomHdr->objY = readS.readByte();
54 
55 	roomHdr->reserved1 = readS.readUint16();
56 
57 	for (i = 0; i < IDI_WTP_MAX_BLOCK; i++)
58 		roomHdr->ofsDesc[i] = readS.readUint16();
59 
60 	for (i = 0; i < IDI_WTP_MAX_BLOCK; i++)
61 		roomHdr->ofsBlock[i] = readS.readUint16();
62 
63 	for (i = 0; i < IDI_WTP_MAX_STR; i++)
64 		roomHdr->ofsStr[i] = readS.readUint16();
65 
66 	roomHdr->reserved2 = readS.readUint32();
67 
68 	for (i = 0; i < IDI_WTP_MAX_BLOCK; i++)
69 		for (byte j = 0; j < IDI_WTP_MAX_BLOCK; j++)
70 			roomHdr->opt[i].ofsOpt[j] = readS.readUint16();
71 }
72 
parseObjHeader(WTP_OBJ_HDR * objHdr,byte * buffer,int len)73 void WinnieEngine::parseObjHeader(WTP_OBJ_HDR *objHdr, byte *buffer, int len) {
74 	int i;
75 
76 	Common::MemoryReadStreamEndian readS(buffer, len, _isBigEndian);
77 
78 	objHdr->fileLen = readS.readUint16();
79 	objHdr->objId = readS.readUint16();
80 
81 	for (i = 0; i < IDI_WTP_MAX_OBJ_STR_END; i++)
82 		objHdr->ofsEndStr[i] = readS.readUint16();
83 
84 	for (i = 0; i < IDI_WTP_MAX_OBJ_STR; i++)
85 		objHdr->ofsStr[i] = readS.readUint16();
86 
87 	objHdr->ofsPic = readS.readUint16();
88 }
89 
readRoom(int iRoom,uint8 * buffer,WTP_ROOM_HDR & roomHdr)90 uint32 WinnieEngine::readRoom(int iRoom, uint8 *buffer, WTP_ROOM_HDR &roomHdr) {
91 	Common::String fileName;
92 
93 	if (getPlatform() == Common::kPlatformDOS)
94 		fileName = Common::String::format(IDS_WTP_ROOM_DOS, iRoom);
95 	else if (getPlatform() == Common::kPlatformAmiga)
96 		fileName = Common::String::format(IDS_WTP_ROOM_AMIGA, iRoom);
97 	else if (getPlatform() == Common::kPlatformC64)
98 		fileName = Common::String::format(IDS_WTP_ROOM_C64, iRoom);
99 	else if (getPlatform() == Common::kPlatformApple2GS)
100 		fileName = Common::String::format(IDS_WTP_ROOM_APPLE, iRoom);
101 
102 	Common::File file;
103 	if (!file.open(fileName)) {
104 		warning("Could not open file \'%s\'", fileName.c_str());
105 		return 0;
106 	}
107 
108 	uint32 filelen = file.size();
109 	if (getPlatform() == Common::kPlatformC64) { // Skip the loading address
110 		filelen -= 2;
111 		file.seek(2, SEEK_CUR);
112 	}
113 
114 	memset(buffer, 0, 4096);
115 	file.read(buffer, filelen);
116 	file.close();
117 
118 	parseRoomHeader(&roomHdr, buffer, filelen);
119 
120 	return filelen;
121 }
122 
readObj(int iObj,uint8 * buffer)123 uint32 WinnieEngine::readObj(int iObj, uint8 *buffer) {
124 	Common::String fileName;
125 
126 	if (getPlatform() == Common::kPlatformDOS)
127 		fileName = Common::String::format(IDS_WTP_OBJ_DOS, iObj);
128 	else if (getPlatform() == Common::kPlatformAmiga)
129 		fileName = Common::String::format(IDS_WTP_OBJ_AMIGA, iObj);
130 	else if (getPlatform() == Common::kPlatformC64)
131 		fileName = Common::String::format(IDS_WTP_OBJ_C64, iObj);
132 	else if (getPlatform() == Common::kPlatformApple2GS)
133 		fileName = Common::String::format(IDS_WTP_OBJ_APPLE, iObj);
134 
135 	Common::File file;
136 	if (!file.open(fileName)) {
137 		warning("Could not open file \'%s\'", fileName.c_str());
138 		return 0;
139 	}
140 
141 	uint32 filelen = file.size();
142 	if (getPlatform() == Common::kPlatformC64) { // Skip the loading address
143 		filelen -= 2;
144 		file.seek(2, SEEK_CUR);
145 	}
146 
147 	memset(buffer, 0, 2048);
148 	file.read(buffer, filelen);
149 	file.close();
150 	return filelen;
151 }
152 
randomize()153 void WinnieEngine::randomize() {
154 	int iObj = 0;
155 	int iRoom = 0;
156 	bool done;
157 
158 	for (int i = 0; i < IDI_WTP_MAX_OBJ_MISSING; i++) {
159 		done = false;
160 
161 		while (!done) {
162 			iObj = rnd(IDI_WTP_MAX_OBJ - 1);
163 			done = true;
164 
165 			for (int j = 0; j < IDI_WTP_MAX_OBJ_MISSING; j++) {
166 				if (_gameStateWinnie.iUsedObj[j] == iObj) {
167 					done = false;
168 					break;
169 				}
170 			}
171 		}
172 
173 		_gameStateWinnie.iUsedObj[i] = iObj;
174 
175 		done = false;
176 		while (!done) {
177 			iRoom = rnd(IDI_WTP_MAX_ROOM_NORMAL);
178 			done = true;
179 
180 			for (int j = 0; j < IDI_WTP_MAX_ROOM_OBJ; j++) {
181 				if (_gameStateWinnie.iObjRoom[j] == iRoom) {
182 					done = false;
183 					break;
184 				}
185 			}
186 		}
187 
188 		_gameStateWinnie.iObjRoom[iObj] = iRoom;
189 	}
190 }
191 
intro()192 void WinnieEngine::intro() {
193 	drawPic(IDS_WTP_FILE_LOGO);
194 	printStr(IDS_WTP_INTRO_0);
195 	g_system->updateScreen();
196 	_system->delayMillis(0x640);
197 
198 	if (getPlatform() == Common::kPlatformAmiga)
199 		_gfx->clearDisplay(0);
200 
201 	drawPic(IDS_WTP_FILE_TITLE);
202 
203 	printStr(IDS_WTP_INTRO_1);
204 	g_system->updateScreen();
205 	_system->delayMillis(0x640);
206 
207 	if (!playSound(IDI_WTP_SND_POOH_0))
208 		return;
209 
210 	if (!playSound(IDI_WTP_SND_POOH_1))
211 		return;
212 
213 	if (!playSound(IDI_WTP_SND_POOH_2))
214 		return;
215 }
216 
getObjInRoom(int iRoom)217 int WinnieEngine::getObjInRoom(int iRoom) {
218 	for (int iObj = 1; iObj < IDI_WTP_MAX_ROOM_OBJ; iObj++)
219 		if (_gameStateWinnie.iObjRoom[iObj] == iRoom)
220 			return iObj;
221 	return 0;
222 }
223 
setTakeDrop(int fCanSel[])224 void WinnieEngine::setTakeDrop(int fCanSel[]) {
225 	fCanSel[IDI_WTP_SEL_TAKE] = getObjInRoom(_room);
226 	fCanSel[IDI_WTP_SEL_DROP] = _gameStateWinnie.iObjHave;
227 }
228 
setWinnieFlag(int iFlag)229 void WinnieEngine::setWinnieFlag(int iFlag) {
230 	_gameStateWinnie.fGame[iFlag] = 1;
231 }
232 
clearWinnieFlag(int iFlag)233 void WinnieEngine::clearWinnieFlag(int iFlag) {
234 	_gameStateWinnie.fGame[iFlag] = 0;
235 }
236 
parser(int pc,int index,uint8 * buffer)237 int WinnieEngine::parser(int pc, int index, uint8 *buffer) {
238 	WTP_ROOM_HDR hdr;
239 	int startpc = pc;
240 	int8 opcode;
241 	int iNewRoom = 0;
242 
243 	int iSel, iDir, iBlock;
244 	int fCanSel[IDI_WTP_SEL_LAST + 1];
245 	char szMenu[121] = {0};
246 	bool done;
247 	int fBlock;
248 
249 	// extract header from buffer
250 	parseRoomHeader(&hdr, buffer, sizeof(WTP_ROOM_HDR));
251 
252 	while (!shouldQuit()) {
253 		pc = startpc;
254 
255 		// check if block is to be run
256 
257 		iBlock = *(buffer + pc++);
258 		if (iBlock == 0)
259 			return IDI_WTP_PAR_OK;
260 
261 		fBlock = *(buffer + pc++);
262 		if (_gameStateWinnie.fGame[iBlock] != fBlock)
263 			return IDI_WTP_PAR_OK;
264 
265 		// extract text from block
266 
267 		opcode = *(buffer + pc);
268 		switch (opcode) {
269 		case 0:
270 		case IDO_WTP_OPTION_0:
271 		case IDO_WTP_OPTION_1:
272 		case IDO_WTP_OPTION_2:
273 			// clear fCanSel block
274 			memset(fCanSel, 0, sizeof(fCanSel));
275 
276 			// check if NSEW directions should be displayed
277 			if (hdr.roomNew[0]) {
278 				fCanSel[IDI_WTP_SEL_NORTH] = fCanSel[IDI_WTP_SEL_SOUTH] =
279 				fCanSel[IDI_WTP_SEL_EAST] = fCanSel[IDI_WTP_SEL_WEST] = true;
280 			}
281 
282 			// check if object in room or player carrying one
283 			setTakeDrop(fCanSel);
284 
285 			// check which rows have a menu option
286 			for (iSel = 0; iSel < IDI_WTP_MAX_OPTION; iSel++) {
287 				opcode = *(buffer + pc++);
288 				if (opcode) {
289 					fCanSel[opcode - IDO_WTP_OPTION_0] = true;
290 					fCanSel[iSel + IDI_WTP_SEL_REAL_OPT_1] = opcode - 0x14;
291 				}
292 			}
293 
294 			// extract menu string
295 			Common::strlcpy(szMenu, (char *)(buffer + pc), 121);
296 			XOR80(szMenu);
297 			break;
298 		default:
299 			// print description
300 			printStrWinnie((char *)(buffer + pc));
301 			if (getSelection(kSelBackspace) == 1)
302 				return IDI_WTP_PAR_OK;
303 			else
304 				return IDI_WTP_PAR_BACK;
305 		}
306 
307 		// input handler
308 
309 		done = false;
310 		while (!done) {
311 			// run wind if it's time
312 			if (_doWind)
313 				wind();
314 
315 			// get menu selection
316 			getMenuSel(szMenu, &iSel, fCanSel);
317 
318 			if (++_gameStateWinnie.nMoves == IDI_WTP_MAX_MOVES_UNTIL_WIND)
319 				_doWind = true;
320 
321 			if (_winnieEvent && (_room <= IDI_WTP_MAX_ROOM_TELEPORT)) {
322 				if (!_tiggerMist) {
323 					_tiggerMist = 1;
324 					tigger();
325 				} else {
326 					_tiggerMist = 0;
327 					mist();
328 				}
329 				_winnieEvent = false;
330 				return IDI_WTP_PAR_GOTO;
331 			}
332 
333 			// process selection
334 			switch (iSel) {
335 			case IDI_WTP_SEL_HOME:
336 				switch (_room) {
337 				case IDI_WTP_ROOM_HOME:
338 				case IDI_WTP_ROOM_MIST:
339 				case IDI_WTP_ROOM_TIGGER:
340 					break;
341 				default:
342 					_room = IDI_WTP_ROOM_HOME;
343 					return IDI_WTP_PAR_GOTO;
344 				}
345 				break;
346 			case IDI_WTP_SEL_BACK:
347 				return IDI_WTP_PAR_BACK;
348 			case IDI_WTP_SEL_OPT_1:
349 			case IDI_WTP_SEL_OPT_2:
350 			case IDI_WTP_SEL_OPT_3:
351 				done = true;
352 				break;
353 			case IDI_WTP_SEL_NORTH:
354 			case IDI_WTP_SEL_SOUTH:
355 			case IDI_WTP_SEL_EAST:
356 			case IDI_WTP_SEL_WEST:
357 				iDir = iSel - IDI_WTP_SEL_NORTH;
358 
359 				if (hdr.roomNew[iDir] == IDI_WTP_ROOM_NONE) {
360 					printStr(IDS_WTP_CANT_GO);
361 					getSelection(kSelAnyKey);
362 				} else {
363 					_room = hdr.roomNew[iDir];
364 					return IDI_WTP_PAR_GOTO;
365 				}
366 				break;
367 			case IDI_WTP_SEL_TAKE:
368 				takeObj(_room);
369 				setTakeDrop(fCanSel);
370 				break;
371 			case IDI_WTP_SEL_DROP:
372 				dropObj(_room);
373 				setTakeDrop(fCanSel);
374 				break;
375 			}
376 		}
377 
378 		// jump to the script block of the selected option
379 		pc = hdr.opt[index].ofsOpt[iSel] - _roomOffset;
380 
381 		opcode = *(buffer + pc);
382 		if (!opcode) pc++;
383 
384 		// process script
385 		do {
386 			opcode = *(buffer + pc++);
387 			switch (opcode) {
388 			case IDO_WTP_GOTO_ROOM:
389 				opcode = *(buffer + pc++);
390 				iNewRoom = opcode;
391 				break;
392 			case IDO_WTP_PRINT_MSG:
393 				opcode = *(buffer + pc++);
394 				printRoomStr(_room, opcode);
395 				getSelection(kSelAnyKey);
396 				break;
397 			case IDO_WTP_PRINT_STR:
398 				opcode = *(buffer + pc++);
399 				printRoomStr(_room, opcode);
400 				break;
401 			case IDO_WTP_DROP_OBJ:
402 				opcode = *(buffer + pc++);
403 				opcode = -1;
404 				dropObjRnd();
405 				break;
406 			case IDO_WTP_FLAG_CLEAR:
407 				opcode = *(buffer + pc++);
408 				clearWinnieFlag(opcode);
409 				break;
410 			case IDO_WTP_FLAG_SET:
411 				opcode = *(buffer + pc++);
412 				setWinnieFlag(opcode);
413 				break;
414 			case IDO_WTP_GAME_OVER:
415 				gameOver();
416 				break;
417 			case IDO_WTP_WALK_MIST:
418 				_mist--;
419 				if (!_mist) {
420 					_room = rnd(IDI_WTP_MAX_ROOM_TELEPORT) + 1;
421 					return IDI_WTP_PAR_GOTO;
422 				}
423 				break;
424 			case IDO_WTP_PLAY_SOUND:
425 				opcode = *(buffer + pc++);
426 				playSound((ENUM_WTP_SOUND)opcode);
427 				break;
428 			case IDO_WTP_SAVE_GAME:
429 				saveGame();
430 				_room = IDI_WTP_ROOM_HOME;
431 				return IDI_WTP_PAR_GOTO;
432 			case IDO_WTP_LOAD_GAME:
433 				loadGame();
434 				_room = IDI_WTP_ROOM_HOME;
435 				return IDI_WTP_PAR_GOTO;
436 			case IDO_WTP_OWL_HELP:
437 				opcode = *(buffer + pc++);
438 				showOwlHelp();
439 				break;
440 			case IDO_WTP_GOTO_RND:
441 				_room = rnd(IDI_WTP_MAX_ROOM_TELEPORT) + 1;
442 				return IDI_WTP_PAR_GOTO;
443 			default:
444 				opcode = 0;
445 				break;
446 			}
447 		} while (opcode && !shouldQuit());
448 
449 		if (iNewRoom) {
450 			_room = iNewRoom;
451 			return IDI_WTP_PAR_GOTO;
452 		}
453 
454 		if (iBlock == 1)
455 			return IDI_WTP_PAR_OK;
456 		g_system->updateScreen();
457 	}
458 
459 	return IDI_WTP_PAR_OK;
460 }
461 
keyHelp()462 void WinnieEngine::keyHelp() {
463 	playSound(IDI_WTP_SND_KEYHELP);
464 	printStr(IDS_WTP_HELP_0);
465 	getSelection(kSelAnyKey);
466 	printStr(IDS_WTP_HELP_1);
467 	getSelection(kSelAnyKey);
468 }
469 
inventory()470 void WinnieEngine::inventory() {
471 	if (_gameStateWinnie.iObjHave)
472 		printObjStr(_gameStateWinnie.iObjHave, IDI_WTP_OBJ_TAKE);
473 	else {
474 		clearTextArea();
475 		drawStr(IDI_WTP_ROW_MENU, IDI_WTP_COL_MENU, IDA_DEFAULT, IDS_WTP_INVENTORY_0);
476 	}
477 
478 	Common::String missing = Common::String::format(IDS_WTP_INVENTORY_1, _gameStateWinnie.nObjMiss);
479 
480 	drawStr(IDI_WTP_ROW_OPTION_4, IDI_WTP_COL_MENU, IDA_DEFAULT, missing.c_str());
481 	g_system->updateScreen();
482 	getSelection(kSelAnyKey);
483 }
484 
printObjStr(int iObj,int iStr)485 void WinnieEngine::printObjStr(int iObj, int iStr) {
486 	WTP_OBJ_HDR hdr;
487 	uint8 *buffer = (uint8 *)malloc(2048);
488 
489 	readObj(iObj, buffer);
490 	parseObjHeader(&hdr, buffer, sizeof(hdr));
491 
492 	printStrWinnie((char *)(buffer + hdr.ofsStr[iStr] - _objOffset));
493 	free(buffer);
494 }
495 
isRightObj(int iRoom,int iObj,int * iCode)496 bool WinnieEngine::isRightObj(int iRoom, int iObj, int *iCode) {
497 	WTP_ROOM_HDR roomhdr;
498 	WTP_OBJ_HDR objhdr;
499 	uint8 *roomdata = (uint8 *)malloc(4096);
500 	uint8 *objdata = (uint8 *)malloc(2048);
501 
502 	readRoom(iRoom, roomdata, roomhdr);
503 	readObj(iObj, objdata);
504 	parseObjHeader(&objhdr, objdata, sizeof(WTP_OBJ_HDR));
505 
506 	free(roomdata);
507 	free(objdata);
508 
509 	*iCode = objhdr.objId;
510 
511 	if (objhdr.objId == 11) objhdr.objId = 34;
512 
513 	if (roomhdr.objId == objhdr.objId)
514 		return true;
515 	else
516 		return false;
517 }
518 
takeObj(int iRoom)519 void WinnieEngine::takeObj(int iRoom) {
520 	if (_gameStateWinnie.iObjHave) {
521 		// player is already carrying an object, can't take
522 		printStr(IDS_WTP_CANT_TAKE);
523 		getSelection(kSelAnyKey);
524 	} else {
525 		// take object
526 		int iObj = getObjInRoom(iRoom);
527 
528 		_gameStateWinnie.iObjHave = iObj;
529 		_gameStateWinnie.iObjRoom[iObj] = 0;
530 
531 		printStr(IDS_WTP_OK);
532 		playSound(IDI_WTP_SND_TAKE);
533 
534 		drawRoomPic();
535 
536 		// print object "take" string
537 		printObjStr(_gameStateWinnie.iObjHave, IDI_WTP_OBJ_TAKE);
538 		getSelection(kSelAnyKey);
539 
540 		// HACK WARNING
541 		if (iObj == 18) {
542 			_gameStateWinnie.fGame[0x0d] = 1;
543 		}
544 	}
545 }
546 
dropObj(int iRoom)547 void WinnieEngine::dropObj(int iRoom) {
548 	int iCode;
549 
550 	if (getObjInRoom(iRoom)) {
551 		// there already is an object in the room, can't drop
552 		printStr(IDS_WTP_CANT_DROP);
553 		getSelection(kSelAnyKey);
554 	} else {
555 		// HACK WARNING
556 		if (_gameStateWinnie.iObjHave == 18) {
557 			_gameStateWinnie.fGame[0x0d] = 0;
558 		}
559 
560 		if (isRightObj(iRoom, _gameStateWinnie.iObjHave, &iCode)) {
561 			// object has been dropped in the right place
562 			printStr(IDS_WTP_OK);
563 			getSelection(kSelAnyKey);
564 			playSound(IDI_WTP_SND_DROP_OK);
565 			printObjStr(_gameStateWinnie.iObjHave, IDI_WTP_OBJ_DROP);
566 			getSelection(kSelAnyKey);
567 
568 			// increase amount of objects returned, decrease amount of objects missing
569 			_gameStateWinnie.nObjMiss--;
570 			_gameStateWinnie.nObjRet++;
571 
572 			// xor the dropped object with 0x80 to signify it has been dropped in the right place
573 			for (int i = 0; i < IDI_WTP_MAX_OBJ_MISSING; i++) {
574 				if (_gameStateWinnie.iUsedObj[i] == _gameStateWinnie.iObjHave) {
575 					_gameStateWinnie.iUsedObj[i] ^= 0x80;
576 					break;
577 				}
578 			}
579 
580 			// set flag according to dropped object's id
581 			_gameStateWinnie.fGame[iCode] = 1;
582 
583 			// player is carrying nothing
584 			_gameStateWinnie.iObjHave = 0;
585 
586 			if (!_gameStateWinnie.nObjMiss) {
587 				// all objects returned, tell player to find party
588 				playSound(IDI_WTP_SND_FANFARE);
589 				printStr(IDS_WTP_GAME_OVER_0);
590 				getSelection(kSelAnyKey);
591 				printStr(IDS_WTP_GAME_OVER_1);
592 				getSelection(kSelAnyKey);
593 			}
594 		} else {
595 			// drop object in the given room
596 			_gameStateWinnie.iObjRoom[_gameStateWinnie.iObjHave] = iRoom;
597 
598 			// object has been dropped in the wrong place
599 			printStr(IDS_WTP_WRONG_PLACE);
600 			getSelection(kSelAnyKey);
601 
602 			playSound(IDI_WTP_SND_DROP);
603 			drawRoomPic();
604 
605 			printStr(IDS_WTP_WRONG_PLACE);
606 			getSelection(kSelAnyKey);
607 
608 			// print object description
609 			printObjStr(_gameStateWinnie.iObjHave, IDI_WTP_OBJ_DESC);
610 			getSelection(kSelAnyKey);
611 
612 			_gameStateWinnie.iObjHave = 0;
613 		}
614 	}
615 }
616 
dropObjRnd()617 void WinnieEngine::dropObjRnd() {
618 	if (!_gameStateWinnie.iObjHave)
619 		return;
620 
621 	int iRoom = 0;
622 	bool done = false;
623 
624 	while (!done) {
625 		iRoom = rnd(IDI_WTP_MAX_ROOM_NORMAL);
626 		done = true;
627 		if (iRoom == _room)
628 			done = false;
629 		for (int j = 0; j < IDI_WTP_MAX_ROOM_OBJ; j++) {
630 			if (_gameStateWinnie.iObjRoom[j] == iRoom) {
631 				done = false;
632 			}
633 		}
634 	}
635 
636 	_gameStateWinnie.iObjRoom[_gameStateWinnie.iObjHave] = iRoom;
637 	_gameStateWinnie.iObjHave = 0;
638 }
639 
wind()640 void WinnieEngine::wind() {
641 	int iRoom = 0;
642 	bool done;
643 
644 	_doWind = 0;
645 	_gameStateWinnie.nMoves = 0;
646 	if (!_gameStateWinnie.nObjMiss)
647 		return;
648 
649 	printStr(IDS_WTP_WIND_0);
650 	playSound(IDI_WTP_SND_WIND_0);
651 	getSelection(kSelAnyKey);
652 
653 	printStr(IDS_WTP_WIND_1);
654 	playSound(IDI_WTP_SND_WIND_0);
655 	getSelection(kSelAnyKey);
656 
657 	dropObjRnd();
658 
659 	// randomize positions of objects at large
660 	for (int i = 0; i < IDI_WTP_MAX_OBJ_MISSING; i++) {
661 		if (!(_gameStateWinnie.iUsedObj[i] & IDI_XOR_KEY)) {
662 			done = false;
663 			while (!done) {
664 				iRoom = rnd(IDI_WTP_MAX_ROOM_NORMAL);
665 				done = true;
666 
667 				for (int j = 0; j < IDI_WTP_MAX_ROOM_OBJ; j++) {
668 					if (_gameStateWinnie.iObjRoom[j] == iRoom) {
669 						done = false;
670 					}
671 				}
672 			}
673 			_gameStateWinnie.iObjRoom[_gameStateWinnie.iUsedObj[i]] = iRoom;
674 		}
675 	}
676 }
677 
mist()678 void WinnieEngine::mist() {
679 	// mist length in turns is (2-5)
680 	_mist = rnd(4) + 2;
681 
682 	_room = IDI_WTP_ROOM_MIST;
683 	drawRoomPic();
684 
685 	printStr(IDS_WTP_MIST);
686 }
687 
tigger()688 void WinnieEngine::tigger() {
689 	_room = IDI_WTP_ROOM_TIGGER;
690 
691 	drawRoomPic();
692 	printStr(IDS_WTP_TIGGER);
693 
694 	dropObjRnd();
695 }
696 
showOwlHelp()697 void WinnieEngine::showOwlHelp() {
698 	if (_gameStateWinnie.iObjHave) {
699 		printStr(IDS_WTP_OWL_0);
700 		getSelection(kSelAnyKey);
701 		printObjStr(_gameStateWinnie.iObjHave, IDI_WTP_OBJ_HELP);
702 		getSelection(kSelAnyKey);
703 	}
704 	if (getObjInRoom(_room)) {
705 		printStr(IDS_WTP_OWL_0);
706 		getSelection(kSelAnyKey);
707 		printObjStr(getObjInRoom(_room), IDI_WTP_OBJ_HELP);
708 		getSelection(kSelAnyKey);
709 	}
710 }
711 
712 
drawMenu(char * szMenu,int iSel,int fCanSel[])713 void WinnieEngine::drawMenu(char *szMenu, int iSel, int fCanSel[]) {
714 	int iRow = 0, iCol = 0;
715 
716 	clearTextArea();
717 	drawStr(IDI_WTP_ROW_MENU, IDI_WTP_COL_MENU, IDA_DEFAULT, szMenu);
718 
719 	if (fCanSel[IDI_WTP_SEL_NORTH])
720 		drawStr(IDI_WTP_ROW_OPTION_4, IDI_WTP_COL_NSEW, IDA_DEFAULT, IDS_WTP_NSEW);
721 	if (fCanSel[IDI_WTP_SEL_TAKE])
722 		drawStr(IDI_WTP_ROW_OPTION_4, IDI_WTP_COL_TAKE, IDA_DEFAULT, IDS_WTP_TAKE);
723 	if (fCanSel[IDI_WTP_SEL_DROP])
724 		drawStr(IDI_WTP_ROW_OPTION_4, IDI_WTP_COL_DROP, IDA_DEFAULT, IDS_WTP_DROP);
725 
726 	switch (iSel) {
727 	case IDI_WTP_SEL_OPT_1:
728 	case IDI_WTP_SEL_OPT_2:
729 	case IDI_WTP_SEL_OPT_3:
730 		iRow = IDI_WTP_ROW_OPTION_1 + iSel;
731 		iCol = IDI_WTP_COL_OPTION;
732 		break;
733 	case IDI_WTP_SEL_NORTH:
734 		iRow = IDI_WTP_ROW_OPTION_4;
735 		iCol = IDI_WTP_COL_NORTH;
736 		break;
737 	case IDI_WTP_SEL_SOUTH:
738 		iRow = IDI_WTP_ROW_OPTION_4;
739 		iCol = IDI_WTP_COL_SOUTH;
740 		break;
741 	case IDI_WTP_SEL_EAST:
742 		iRow = IDI_WTP_ROW_OPTION_4;
743 		iCol = IDI_WTP_COL_EAST;
744 		break;
745 	case IDI_WTP_SEL_WEST:
746 		iRow = IDI_WTP_ROW_OPTION_4;
747 		iCol = IDI_WTP_COL_WEST;
748 		break;
749 	case IDI_WTP_SEL_TAKE:
750 		iRow = IDI_WTP_ROW_OPTION_4;
751 		iCol = IDI_WTP_COL_TAKE;
752 		break;
753 	case IDI_WTP_SEL_DROP:
754 		iRow = IDI_WTP_ROW_OPTION_4;
755 		iCol = IDI_WTP_COL_DROP;
756 		break;
757 	}
758 	drawStr(iRow, iCol - 1, IDA_DEFAULT, ">");
759 	g_system->updateScreen();
760 }
761 
incMenuSel(int * iSel,int fCanSel[])762 void WinnieEngine::incMenuSel(int *iSel, int fCanSel[]) {
763 	do {
764 		*iSel += 1;
765 		if (*iSel > IDI_WTP_SEL_DROP) *iSel = IDI_WTP_SEL_OPT_1;
766 	} while (!fCanSel[*iSel]);
767 }
768 
decMenuSel(int * iSel,int fCanSel[])769 void WinnieEngine::decMenuSel(int *iSel, int fCanSel[]) {
770 	do {
771 		*iSel -= 1;
772 		if (*iSel < IDI_WTP_SEL_OPT_1) *iSel = IDI_WTP_SEL_DROP;
773 	} while (!fCanSel[*iSel]);
774 }
775 
getMenuMouseSel(int * iSel,int fCanSel[],int x,int y)776 void WinnieEngine::getMenuMouseSel(int *iSel, int fCanSel[], int x, int y) {
777 	switch (y) {
778 	case IDI_WTP_ROW_OPTION_1:
779 	case IDI_WTP_ROW_OPTION_2:
780 	case IDI_WTP_ROW_OPTION_3:
781 		if (fCanSel[y - IDI_WTP_ROW_OPTION_1])  *iSel = y - IDI_WTP_ROW_OPTION_1;
782 		break;
783 	case IDI_WTP_ROW_OPTION_4:
784 		if (fCanSel[IDI_WTP_SEL_NORTH] && (x > IDI_WTP_COL_NORTH - 1) && (x < 6)) *iSel = IDI_WTP_SEL_NORTH;
785 		if (fCanSel[IDI_WTP_SEL_SOUTH] && (x > IDI_WTP_COL_SOUTH - 1) && (x < 13)) *iSel = IDI_WTP_SEL_SOUTH;
786 		if (fCanSel[IDI_WTP_SEL_EAST] && (x > IDI_WTP_COL_EAST - 1) && (x < 19)) *iSel = IDI_WTP_SEL_EAST;
787 		if (fCanSel[IDI_WTP_SEL_WEST] && (x > IDI_WTP_COL_WEST - 1) && (x < 25)) *iSel = IDI_WTP_SEL_WEST;
788 		if (fCanSel[IDI_WTP_SEL_TAKE] && (x > IDI_WTP_COL_TAKE - 1) && (x < 33)) *iSel = IDI_WTP_SEL_TAKE;
789 		if (fCanSel[IDI_WTP_SEL_DROP] && (x > IDI_WTP_COL_DROP - 1) && (x < 39)) *iSel = IDI_WTP_SEL_DROP;
790 		break;
791 	}
792 }
793 
makeSel(int * iSel,int fCanSel[])794 void WinnieEngine::makeSel(int *iSel, int fCanSel[]) {
795 	if (fCanSel[*iSel])
796 		return;
797 
798 	keyHelp();
799 	clrMenuSel(iSel, fCanSel);
800 }
801 
getMenuSel(char * szMenu,int * iSel,int fCanSel[])802 void WinnieEngine::getMenuSel(char *szMenu, int *iSel, int fCanSel[]) {
803 	Common::Event event;
804 	int x, y;
805 
806 	clrMenuSel(iSel, fCanSel);
807 	drawMenu(szMenu, *iSel, fCanSel);
808 
809 	// Show the mouse cursor for the menu
810 	CursorMan.showMouse(true);
811 
812 	while (!shouldQuit()) {
813 		while (_system->getEventManager()->pollEvent(event)) {
814 			switch (event.type) {
815 			case Common::EVENT_RTL:
816 			case Common::EVENT_QUIT:
817 				return;
818 			case Common::EVENT_MOUSEMOVE:
819 				x = event.mouse.x / 8;
820 				y = event.mouse.y / 8;
821 				getMenuMouseSel(iSel, fCanSel, x, y);
822 
823 				// Change cursor
824 				if (fCanSel[IDI_WTP_SEL_NORTH] && hotspotNorth.contains(event.mouse.x, event.mouse.y)) {
825 					//_gfx->setCursorPalette(true);
826 					// ????
827 				} else if (fCanSel[IDI_WTP_SEL_SOUTH] && hotspotSouth.contains(event.mouse.x, event.mouse.y)) {
828 					//_gfx->setCursorPalette(true);
829 				} else if (fCanSel[IDI_WTP_SEL_WEST] && hotspotWest.contains(event.mouse.x, event.mouse.y)) {
830 					//_gfx->setCursorPalette(true);
831 				} else if (fCanSel[IDI_WTP_SEL_EAST] && hotspotEast.contains(event.mouse.x, event.mouse.y)) {
832 					//_gfx->setCursorPalette(true);
833 				} else {
834 					//_gfx->setCursorPalette(false);
835 				}
836 
837 				break;
838 			case Common::EVENT_LBUTTONUP:
839 				// Click to move
840 				if (fCanSel[IDI_WTP_SEL_NORTH] && hotspotNorth.contains(event.mouse.x, event.mouse.y)) {
841 					*iSel = IDI_WTP_SEL_NORTH;
842 					makeSel(iSel, fCanSel);
843 					//_gfx->setCursorPalette(false);
844 					// TODO???
845 					return;
846 				} else if (fCanSel[IDI_WTP_SEL_SOUTH] && hotspotSouth.contains(event.mouse.x, event.mouse.y)) {
847 					*iSel = IDI_WTP_SEL_SOUTH;
848 					makeSel(iSel, fCanSel);
849 					//_gfx->setCursorPalette(false);
850 					// TODO???
851 					return;
852 				} else if (fCanSel[IDI_WTP_SEL_WEST] && hotspotWest.contains(event.mouse.x, event.mouse.y)) {
853 					*iSel = IDI_WTP_SEL_WEST;
854 					makeSel(iSel, fCanSel);
855 					//_gfx->setCursorPalette(false);
856 					// TODO???
857 					return;
858 				} else if (fCanSel[IDI_WTP_SEL_EAST] && hotspotEast.contains(event.mouse.x, event.mouse.y)) {
859 					*iSel = IDI_WTP_SEL_EAST;
860 					makeSel(iSel, fCanSel);
861 					//_gfx->setCursorPalette(false);
862 					// TODO???
863 					return;
864 				} else {
865 					//_gfx->setCursorPalette(false);
866 					// TODO???
867 				}
868 
869 				switch (*iSel) {
870 				case IDI_WTP_SEL_OPT_1:
871 				case IDI_WTP_SEL_OPT_2:
872 				case IDI_WTP_SEL_OPT_3:
873 					for (int iSel2 = 0; iSel2 < IDI_WTP_MAX_OPTION; iSel2++) {
874 						if (*iSel == (fCanSel[iSel2 + IDI_WTP_SEL_REAL_OPT_1] - 1)) {
875 							*iSel = iSel2;
876 							// Menu selection made, hide the mouse cursor
877 							CursorMan.showMouse(false);
878 							return;
879 						}
880 					}
881 					break;
882 				default:
883 					if (fCanSel[*iSel]) {
884 						// Menu selection made, hide the mouse cursor
885 						CursorMan.showMouse(false);
886 						return;
887 					}
888 					break;
889 				}
890 				break;
891 			case Common::EVENT_RBUTTONUP:
892 				*iSel = IDI_WTP_SEL_BACK;
893 				// Menu selection made, hide the mouse cursor
894 				CursorMan.showMouse(false);
895 				return;
896 			case Common::EVENT_WHEELUP:
897 				decMenuSel(iSel, fCanSel);
898 				break;
899 			case Common::EVENT_WHEELDOWN:
900 				incMenuSel(iSel, fCanSel);
901 				break;
902 			case Common::EVENT_KEYDOWN:
903 				if (event.kbd.keycode == Common::KEYCODE_d && (event.kbd.flags & Common::KBD_CTRL) && _console) {
904 					_console->attach();
905 					_console->onFrame();
906 					continue;
907 				}
908 
909 				switch (event.kbd.keycode) {
910 				case Common::KEYCODE_ESCAPE:
911 					*iSel = IDI_WTP_SEL_HOME;
912 					// Menu selection made, hide the mouse cursor
913 					CursorMan.showMouse(false);
914 					return;
915 				case Common::KEYCODE_BACKSPACE:
916 					*iSel = IDI_WTP_SEL_BACK;
917 					// Menu selection made, hide the mouse cursor
918 					CursorMan.showMouse(false);
919 					return;
920 				case Common::KEYCODE_c:
921 					inventory();
922 					break;
923 				case Common::KEYCODE_SPACE:
924 				case Common::KEYCODE_RIGHT:
925 				case Common::KEYCODE_DOWN:
926 					incMenuSel(iSel, fCanSel);
927 					break;
928 				case Common::KEYCODE_LEFT:
929 				case Common::KEYCODE_UP:
930 					decMenuSel(iSel, fCanSel);
931 					break;
932 				case Common::KEYCODE_1:
933 				case Common::KEYCODE_2:
934 				case Common::KEYCODE_3:
935 					*iSel = event.kbd.keycode - Common::KEYCODE_1;
936 					if (fCanSel[*iSel + IDI_WTP_SEL_REAL_OPT_1]) {
937 						// Menu selection made, hide the mouse cursor
938 						CursorMan.showMouse(false);
939 						return;
940 					} else {
941 						keyHelp();
942 						clrMenuSel(iSel, fCanSel);
943 					}
944 					break;
945 				case Common::KEYCODE_n:
946 					*iSel = IDI_WTP_SEL_NORTH;
947 					makeSel(iSel, fCanSel);
948 					break;
949 				case Common::KEYCODE_s:
950 					if (event.kbd.flags & Common::KBD_CTRL) {
951 						flipFlag(VM_FLAG_SOUND_ON);
952 					} else {
953 						*iSel = IDI_WTP_SEL_SOUTH;
954 						makeSel(iSel, fCanSel);
955 					}
956 					break;
957 				case Common::KEYCODE_e:
958 					*iSel = IDI_WTP_SEL_EAST;
959 					makeSel(iSel, fCanSel);
960 					break;
961 				case Common::KEYCODE_w:
962 					*iSel = IDI_WTP_SEL_WEST;
963 					makeSel(iSel, fCanSel);
964 					break;
965 				case Common::KEYCODE_t:
966 					*iSel = IDI_WTP_SEL_TAKE;
967 					makeSel(iSel, fCanSel);
968 					break;
969 				case Common::KEYCODE_d:
970 					*iSel = IDI_WTP_SEL_DROP;
971 					makeSel(iSel, fCanSel);
972 					break;
973 				case Common::KEYCODE_RETURN:
974 					switch (*iSel) {
975 					case IDI_WTP_SEL_OPT_1:
976 					case IDI_WTP_SEL_OPT_2:
977 					case IDI_WTP_SEL_OPT_3:
978 						for (int iSel2 = 0; iSel2 < IDI_WTP_MAX_OPTION; iSel2++) {
979 							if (*iSel == (fCanSel[iSel2 + IDI_WTP_SEL_REAL_OPT_1] - 1)) {
980 								*iSel = iSel2;
981 								// Menu selection made, hide the mouse cursor
982 								CursorMan.showMouse(false);
983 								return;
984 							}
985 						}
986 						break;
987 					default:
988 						if (fCanSel[*iSel]) {
989 							// Menu selection made, hide the mouse cursor
990 							CursorMan.showMouse(false);
991 							return;
992 						}
993 						break;
994 					}
995 					break;
996 				default:
997 					if (!event.kbd.flags) { // if the control/alt/shift keys are not pressed
998 						keyHelp();
999 						clrMenuSel(iSel, fCanSel);
1000 					}
1001 					break;
1002 				}
1003 				break;
1004 			default:
1005 				break;
1006 			}
1007 
1008 			drawMenu(szMenu, *iSel, fCanSel);
1009 		}
1010 	}
1011 }
1012 
gameLoop()1013 void WinnieEngine::gameLoop() {
1014 	WTP_ROOM_HDR hdr;
1015 	uint8 *roomdata = (uint8 *)malloc(4096);
1016 	int iBlock;
1017 	uint8 decodePhase = 0;
1018 
1019 	while (!shouldQuit()) {
1020 		if (decodePhase == 0) {
1021 			if (!_gameStateWinnie.nObjMiss && (_room == IDI_WTP_ROOM_PICNIC))
1022 				_room = IDI_WTP_ROOM_PARTY;
1023 
1024 			readRoom(_room, roomdata, hdr);
1025 			drawRoomPic();
1026 			g_system->updateScreen();
1027 			decodePhase = 1;
1028 		}
1029 
1030 		if (decodePhase == 1) {
1031 			if (getObjInRoom(_room)) {
1032 				printObjStr(getObjInRoom(_room), IDI_WTP_OBJ_DESC);
1033 				getSelection(kSelAnyKey);
1034 			}
1035 			decodePhase = 2;
1036 		}
1037 
1038 		if (decodePhase == 2) {
1039 			for (iBlock = 0; iBlock < IDI_WTP_MAX_BLOCK; iBlock++) {
1040 				if (parser(hdr.ofsDesc[iBlock] - _roomOffset, iBlock, roomdata) == IDI_WTP_PAR_BACK) {
1041 					decodePhase = 1;
1042 					break;
1043 				}
1044 			}
1045 			if (decodePhase == 2)
1046 				decodePhase = 3;
1047 		}
1048 
1049 		if (decodePhase == 3) {
1050 			for (iBlock = 0; iBlock < IDI_WTP_MAX_BLOCK; iBlock++) {
1051 				if (parser(hdr.ofsBlock[iBlock] - _roomOffset, iBlock, roomdata) == IDI_WTP_PAR_GOTO) {
1052 					decodePhase = 0;
1053 					break;
1054 				} else if (parser(hdr.ofsBlock[iBlock] - _roomOffset, iBlock, roomdata) == IDI_WTP_PAR_BACK) {
1055 					decodePhase = 2;
1056 					break;
1057 				}
1058 			}
1059 		}
1060 	}
1061 
1062 	free(roomdata);
1063 }
1064 
drawPic(const char * szName)1065 void WinnieEngine::drawPic(const char *szName) {
1066 	Common::String fileName = szName;
1067 
1068 	if (getPlatform() != Common::kPlatformAmiga)
1069 		fileName += ".pic";
1070 
1071 	Common::File file;
1072 
1073 	if (!file.open(fileName)) {
1074 		warning("Could not open file \'%s\'", fileName.c_str());
1075 		return;
1076 	}
1077 
1078 	uint8 *buffer = (uint8 *)malloc(4096);
1079 	uint32 size = file.size();
1080 	file.read(buffer, size);
1081 	file.close();
1082 
1083 	_picture->decodePicture(buffer, size, 1, IDI_WTP_PIC_WIDTH, IDI_WTP_PIC_HEIGHT);
1084 	_picture->showPic(IDI_WTP_PIC_X0, IDI_WTP_PIC_Y0, IDI_WTP_PIC_WIDTH, IDI_WTP_PIC_HEIGHT);
1085 
1086 	free(buffer);
1087 }
1088 
drawObjPic(int iObj,int x0,int y0)1089 void WinnieEngine::drawObjPic(int iObj, int x0, int y0) {
1090 	if (!iObj)
1091 		return;
1092 
1093 	WTP_OBJ_HDR objhdr;
1094 	uint8 *buffer = (uint8 *)malloc(2048);
1095 	uint32 objSize = readObj(iObj, buffer);
1096 	parseObjHeader(&objhdr, buffer, sizeof(WTP_OBJ_HDR));
1097 
1098 	_picture->setOffset(x0, y0);
1099 	_picture->decodePicture(buffer + objhdr.ofsPic - _objOffset, objSize, 0, IDI_WTP_PIC_WIDTH, IDI_WTP_PIC_HEIGHT);
1100 	_picture->setOffset(0, 0);
1101 	_picture->showPic(10, 0, IDI_WTP_PIC_WIDTH, IDI_WTP_PIC_HEIGHT);
1102 
1103 	free(buffer);
1104 }
1105 
drawRoomPic()1106 void WinnieEngine::drawRoomPic() {
1107 	WTP_ROOM_HDR roomhdr;
1108 	uint8 *buffer = (uint8 *)malloc(4096);
1109 	int iObj = getObjInRoom(_room);
1110 
1111 	// clear gfx screen
1112 	_gfx->clearDisplay(0);
1113 
1114 	// read room picture
1115 	readRoom(_room, buffer, roomhdr);
1116 
1117 	// draw room picture
1118 	_picture->decodePicture(buffer + roomhdr.ofsPic - _roomOffset, 4096, 1, IDI_WTP_PIC_WIDTH, IDI_WTP_PIC_HEIGHT);
1119 	_picture->showPic(IDI_WTP_PIC_X0, IDI_WTP_PIC_Y0, IDI_WTP_PIC_WIDTH, IDI_WTP_PIC_HEIGHT);
1120 
1121 	// draw object picture
1122 	drawObjPic(iObj, IDI_WTP_PIC_X0 + roomhdr.objX, IDI_WTP_PIC_Y0 + roomhdr.objY);
1123 
1124 	free(buffer);
1125 }
1126 
playSound(ENUM_WTP_SOUND iSound)1127 bool WinnieEngine::playSound(ENUM_WTP_SOUND iSound) {
1128 	// TODO: Only DOS sound is supported, currently
1129 	if (getPlatform() != Common::kPlatformDOS) {
1130 		warning("STUB: playSound(%d)", iSound);
1131 		return false;
1132 	}
1133 
1134 	Common::String fileName = Common::String::format(IDS_WTP_SND_DOS, iSound);
1135 
1136 	Common::File file;
1137 	if (!file.open(fileName))
1138 		return false;
1139 
1140 	uint32 size = file.size();
1141 	byte *data = new byte[size];
1142 	file.read(data, size);
1143 	file.close();
1144 
1145 	_game.sounds[0] = AgiSound::createFromRawResource(data, size, 0, _soundemu);
1146 	_sound->startSound(0, 0);
1147 
1148 	bool cursorShowing = CursorMan.showMouse(false);
1149 	_system->updateScreen();
1150 
1151 	// Loop until the sound is done
1152 	bool skippedSound = false;
1153 	while (!shouldQuit() && _game.sounds[0]->isPlaying()) {
1154 		Common::Event event;
1155 		while (_system->getEventManager()->pollEvent(event)) {
1156 			switch (event.type) {
1157 			case Common::EVENT_KEYDOWN:
1158 				_sound->stopSound();
1159 				skippedSound = true;
1160 				break;
1161 			default:
1162 				break;
1163 			}
1164 		}
1165 
1166 		_system->delayMillis(10);
1167 	}
1168 
1169 	if (cursorShowing) {
1170 		CursorMan.showMouse(true);
1171 		_system->updateScreen();
1172 	}
1173 
1174 	delete _game.sounds[0];
1175 	_game.sounds[0] = 0;
1176 
1177 	return !shouldQuit() && !skippedSound;
1178 }
1179 
clrMenuSel(int * iSel,int fCanSel[])1180 void WinnieEngine::clrMenuSel(int *iSel, int fCanSel[]) {
1181 	*iSel = IDI_WTP_SEL_OPT_1;
1182 	while (!fCanSel[*iSel]) {
1183 		*iSel += 1;
1184 	}
1185 	//_gfx->setCursorPalette(false);
1186 	// TODO???
1187 }
1188 
printRoomStr(int iRoom,int iStr)1189 void WinnieEngine::printRoomStr(int iRoom, int iStr) {
1190 	WTP_ROOM_HDR hdr;
1191 	uint8 *buffer = (uint8 *)malloc(4096);
1192 
1193 	readRoom(iRoom, buffer, hdr);
1194 	printStrWinnie((char *)(buffer + hdr.ofsStr[iStr - 1] - _roomOffset));
1195 
1196 	free(buffer);
1197 }
1198 
gameOver()1199 void WinnieEngine::gameOver() {
1200 	// sing the Pooh song forever
1201 	while (!shouldQuit()) {
1202 		printStr(IDS_WTP_SONG_0);
1203 		playSound(IDI_WTP_SND_POOH_0);
1204 		printStr(IDS_WTP_SONG_1);
1205 		playSound(IDI_WTP_SND_POOH_1);
1206 		printStr(IDS_WTP_SONG_2);
1207 		playSound(IDI_WTP_SND_POOH_2);
1208 		getSelection(kSelAnyKey);
1209 	}
1210 }
1211 
saveGame()1212 void WinnieEngine::saveGame() {
1213 	int i = 0;
1214 
1215 	Common::OutSaveFile *outfile = getSaveFileMan()->openForSaving(IDS_WTP_FILE_SAVEGAME);
1216 
1217 	if (!outfile)
1218 		return;
1219 
1220 	outfile->writeUint32BE(MKTAG('W', 'I', 'N', 'N')); // header
1221 	outfile->writeByte(WTP_SAVEGAME_VERSION);
1222 
1223 	outfile->writeByte(_gameStateWinnie.fSound);
1224 	outfile->writeByte(_gameStateWinnie.nMoves);
1225 	outfile->writeByte(_gameStateWinnie.nObjMiss);
1226 	outfile->writeByte(_gameStateWinnie.nObjRet);
1227 	outfile->writeByte(_gameStateWinnie.iObjHave);
1228 
1229 	for (i = 0; i < IDI_WTP_MAX_FLAG; i++)
1230 		outfile->writeByte(_gameStateWinnie.fGame[i]);
1231 
1232 	for (i = 0; i < IDI_WTP_MAX_OBJ_MISSING; i++)
1233 		outfile->writeByte(_gameStateWinnie.iUsedObj[i]);
1234 
1235 	for (i = 0; i < IDI_WTP_MAX_ROOM_OBJ; i++)
1236 		outfile->writeByte(_gameStateWinnie.iObjRoom[i]);
1237 
1238 	outfile->finalize();
1239 
1240 	if (outfile->err())
1241 		warning("Can't write file '%s'. (Disk full?)", IDS_WTP_FILE_SAVEGAME);
1242 
1243 	delete outfile;
1244 }
1245 
loadGame()1246 void WinnieEngine::loadGame() {
1247 	int saveVersion = 0;
1248 	int i = 0;
1249 
1250 	Common::InSaveFile *infile = getSaveFileMan()->openForLoading(IDS_WTP_FILE_SAVEGAME);
1251 
1252 	if (!infile)
1253 		return;
1254 
1255 	if (infile->readUint32BE() == MKTAG('W', 'I', 'N', 'N')) {
1256 		saveVersion = infile->readByte();
1257 		if (saveVersion != WTP_SAVEGAME_VERSION)
1258 			warning("Old save game version (%d, current version is %d). Will try and read anyway, but don't be surprised if bad things happen", saveVersion, WTP_SAVEGAME_VERSION);
1259 
1260 		_gameStateWinnie.fSound = infile->readByte();
1261 		_gameStateWinnie.nMoves = infile->readByte();
1262 		_gameStateWinnie.nObjMiss = infile->readByte();
1263 		_gameStateWinnie.nObjRet = infile->readByte();
1264 		_gameStateWinnie.iObjHave = infile->readByte();
1265 	} else {
1266 		// This is probably a save from the original interpreter, throw a warning and attempt
1267 		// to read it as LE
1268 		warning("No header found in save game, assuming it came from the original interpreter");
1269 		// Note that the original saves variables as 16-bit integers, but only 8 bits are used.
1270 		// Since we read the save file data as little-endian, we skip the first byte of each
1271 		// variable
1272 
1273 		infile->seek(0);                    // Jump back to the beginning of the file
1274 
1275 		infile->readUint16LE();             // skip unused field
1276 		infile->readByte();                 // first 8 bits of fSound
1277 		_gameStateWinnie.fSound = infile->readByte();
1278 		infile->readByte();                 // first 8 bits of nMoves
1279 		_gameStateWinnie.nMoves = infile->readByte();
1280 		infile->readByte();                 // first 8 bits of nObjMiss
1281 		_gameStateWinnie.nObjMiss = infile->readByte();
1282 		infile->readByte();                 // first 8 bits of nObjRet
1283 		_gameStateWinnie.nObjRet = infile->readByte();
1284 		infile->readUint16LE();             // skip unused field
1285 		infile->readUint16LE();             // skip unused field
1286 		infile->readUint16LE();             // skip unused field
1287 		infile->readByte();                 // first 8 bits of iObjHave
1288 		_gameStateWinnie.iObjHave = infile->readByte();
1289 		infile->readUint16LE();             // skip unused field
1290 		infile->readUint16LE();             // skip unused field
1291 		infile->readUint16LE();             // skip unused field
1292 	}
1293 
1294 	for (i = 0; i < IDI_WTP_MAX_FLAG; i++)
1295 		_gameStateWinnie.fGame[i] = infile->readByte();
1296 
1297 	for (i = 0; i < IDI_WTP_MAX_OBJ_MISSING; i++)
1298 		_gameStateWinnie.iUsedObj[i] = infile->readByte();
1299 
1300 	for (i = 0; i < IDI_WTP_MAX_ROOM_OBJ; i++)
1301 		_gameStateWinnie.iObjRoom[i] = infile->readByte();
1302 
1303 	// Note that saved games from the original interpreter have 2 more 16-bit fields here
1304 	// which are ignored
1305 
1306 	delete infile;
1307 }
1308 
printStrWinnie(char * szMsg)1309 void WinnieEngine::printStrWinnie(char *szMsg) {
1310 	if (getPlatform() != Common::kPlatformAmiga)
1311 		printStrXOR(szMsg);
1312 	else
1313 		printStr(szMsg);
1314 }
1315 
1316 // Console-related functions
1317 
debugCurRoom()1318 void WinnieEngine::debugCurRoom() {
1319 	_console->debugPrintf("Current Room = %d\n", _room);
1320 }
1321 
WinnieEngine(OSystem * syst,const AGIGameDescription * gameDesc)1322 WinnieEngine::WinnieEngine(OSystem *syst, const AGIGameDescription *gameDesc) : PreAgiEngine(syst, gameDesc) {
1323 	_console = new WinnieConsole(this);
1324 }
1325 
~WinnieEngine()1326 WinnieEngine::~WinnieEngine() {
1327 	delete _console;
1328 }
1329 
init()1330 void WinnieEngine::init() {
1331 	// Initialize sound
1332 
1333 	switch (MidiDriver::getMusicType(MidiDriver::detectDevice(MDT_PCSPK | MDT_PCJR))) {
1334 	case MT_PCSPK:
1335 		_soundemu = SOUND_EMU_PC;
1336 		break;
1337 	case MT_PCJR:
1338 		_soundemu = SOUND_EMU_PCJR;
1339 		break;
1340 	default:
1341 		_soundemu = SOUND_EMU_NONE;
1342 		break;
1343 	}
1344 
1345 	_sound = new SoundMgr(this, _mixer);
1346 	setFlag(VM_FLAG_SOUND_ON, true); // enable sound
1347 
1348 	memset(&_gameStateWinnie, 0, sizeof(_gameStateWinnie));
1349 	_gameStateWinnie.fSound = 1;
1350 	_gameStateWinnie.nObjMiss = IDI_WTP_MAX_OBJ_MISSING;
1351 	_gameStateWinnie.nObjRet = 0;
1352 	_gameStateWinnie.fGame[0] = 1;
1353 	_gameStateWinnie.fGame[1] = 1;
1354 	_room = IDI_WTP_ROOM_HOME;
1355 
1356 	_mist = -1;
1357 	_doWind = false;
1358 	_winnieEvent = false;
1359 
1360 	if (getPlatform() != Common::kPlatformAmiga) {
1361 		_isBigEndian = false;
1362 		_roomOffset = IDI_WTP_OFS_ROOM;
1363 		_objOffset = IDI_WTP_OFS_OBJ;
1364 	} else {
1365 		_isBigEndian = true;
1366 		_roomOffset = 0;
1367 		_objOffset = 0;
1368 	}
1369 
1370 	if (getPlatform() == Common::kPlatformC64 || getPlatform() == Common::kPlatformApple2GS)
1371 		_picture->setPictureVersion(AGIPIC_C64);
1372 
1373 	hotspotNorth = Common::Rect(20, 0, (IDI_WTP_PIC_WIDTH + 10) * 2, 10);
1374 	hotspotSouth = Common::Rect(20, IDI_WTP_PIC_HEIGHT - 10, (IDI_WTP_PIC_WIDTH + 10) * 2, IDI_WTP_PIC_HEIGHT);
1375 	hotspotEast  = Common::Rect(IDI_WTP_PIC_WIDTH * 2, 0, (IDI_WTP_PIC_WIDTH + 10) * 2, IDI_WTP_PIC_HEIGHT);
1376 	hotspotWest  = Common::Rect(20, 0, 30, IDI_WTP_PIC_HEIGHT);
1377 }
1378 
go()1379 Common::Error WinnieEngine::go() {
1380 	init();
1381 	randomize();
1382 
1383 	// The intro is not supported on these platforms yet
1384 	if (getPlatform() != Common::kPlatformC64 && getPlatform() != Common::kPlatformApple2GS)
1385 		intro();
1386 
1387 	gameLoop();
1388 
1389 	return Common::kNoError;
1390 }
1391 
1392 } // End of namespace AGI
1393