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