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/debug.h"
24 #include "common/stream.h"
25
26 #include "petka/base.h"
27 #include "petka/big_dialogue.h"
28 #include "petka/interfaces/main.h"
29 #include "petka/interfaces/dialog_interface.h"
30 #include "petka/objects/heroes.h"
31 #include "petka/q_system.h"
32 #include "petka/petka.h"
33
34 namespace Petka {
35
36 const uint16 kFallback = 0xFFFE;
37
BigDialogue(PetkaEngine & vm)38 BigDialogue::BigDialogue(PetkaEngine &vm)
39 : _vm(vm) {
40 _currOp = nullptr;
41 _startOpIndex = 0;
42
43 Common::ScopedPtr<Common::SeekableReadStream> file(vm.openFile("dialogue.fix", true));
44 if (!file)
45 return;
46
47 _objDialogs.resize(file->readUint32LE());
48 for (uint i = 0; i < _objDialogs.size(); ++i) {
49 _objDialogs[i].objId = file->readUint32LE();
50 _objDialogs[i].handlers.resize(file->readUint32LE());
51 file->skip(4); // pointer
52 }
53 for (uint i = 0; i < _objDialogs.size(); ++i) {
54 for (uint j = 0; j < _objDialogs[i].handlers.size(); ++j) {
55 _objDialogs[i].handlers[j].opcode = file->readUint32LE();
56 _objDialogs[i].handlers[j].dialogs.resize(file->readUint32LE());
57 _objDialogs[i].handlers[j].startDialogIndex = file->readUint32LE();
58 file->skip(4); // pointer
59 }
60 for (uint j = 0; j < _objDialogs[i].handlers.size(); ++j) {
61 for (uint z = 0; z < _objDialogs[i].handlers[j].dialogs.size(); ++z) {
62 _objDialogs[i].handlers[j].dialogs[z].startOpIndex = file->readUint32LE();
63 file->skip(4 + 4); // opsCount + pointer
64 }
65 }
66 }
67
68 load(file.get());
69 }
70
loadSpeechesInfo()71 void BigDialogue::loadSpeechesInfo() {
72 if (!_speeches.empty())
73 return;
74
75 Common::ScopedPtr<Common::SeekableReadStream> file(_vm.openFile("dialogue.lod", true));
76 if (!file)
77 return;
78
79 _speeches.resize(file->readUint32LE());
80 for (uint i = 0; i < _speeches.size(); ++i) {
81 _speeches[i].speakerId = file->readUint32LE();
82 file->read(_speeches[i].soundName, sizeof(_speeches[i].soundName));
83 file->skip(4);
84 }
85
86 char *str = new char[file->size() - file->pos()];
87 char *curr = str;
88 file->read(str, file->size() - file->pos());
89 for (uint i = 0; i < _speeches.size(); ++i) {
90 _speeches[i].text = Common::convertToU32String(curr, Common::kWindows1251);
91 curr += strlen(curr) + 1;
92 }
93 delete[] str;
94 }
95
getSpeechInfo(int * talkerId,const char ** soundName,int choice)96 const Common::U32String *BigDialogue::getSpeechInfo(int *talkerId, const char **soundName, int choice) {
97 if (!_currOp)
98 return nullptr;
99 switch (_currOp->type) {
100 case kOperationMenu: {
101 Operation *menuOp = _currOp;
102 uint bit = 1;
103 if (_currOp->menu.bits <= choice || choice < 0) {
104 break;
105 }
106 while (true) {
107 _currOp += 1;
108 if (choice == 0 && (bit & menuOp->menu.bitField))
109 break;
110 if (_currOp->type == kOperationBreak) {
111 if (bit & menuOp->menu.bitField)
112 choice--;
113 bit *= 2;
114 }
115 }
116 if (_currOp->type != kOperationPlay)
117 next();
118 if (_currOp->type != kOperationPlay) {
119 _currOp = menuOp;
120 break;
121 }
122
123 uint index = _currOp->play.messageIndex;
124 _currOp = menuOp;
125 if (soundName)
126 *soundName = _speeches[index].soundName;
127 *talkerId = _speeches[index].speakerId;
128 return &_speeches[index].text;
129 }
130 case kOperationCircle:
131 circleMoveTo(_currOp->circle.curr);
132 assert(_currOp->type == kOperationPlay);
133 // fall through
134 case kOperationPlay:
135 if (soundName)
136 *soundName = _speeches[_currOp->play.messageIndex].soundName;
137 *talkerId = _speeches[_currOp->play.messageIndex].speakerId;
138 return &_speeches[_currOp->play.messageIndex].text;
139 default:
140 break;
141 }
142 return nullptr;
143 }
144
findHandler(uint objId,uint opcode,bool * fallback) const145 const DialogHandler *BigDialogue::findHandler(uint objId, uint opcode, bool *fallback) const {
146 if (opcode == kEnd || opcode == kHalf) {
147 return nullptr;
148 }
149 if (fallback) {
150 *fallback = false;
151 }
152 for (uint i = 0; i < _objDialogs.size(); ++i) {
153 if (_objDialogs[i].objId != objId) {
154 continue;
155 }
156
157 for (uint j = 0; j < _objDialogs[i].handlers.size(); ++j) {
158 if (_objDialogs[i].handlers[j].opcode == opcode) {
159 return &_objDialogs[i].handlers[j];
160 }
161 }
162
163 if ((uint16)opcode != kObjectUse) {
164 continue;
165 }
166
167 for (uint j = 0; j < _objDialogs[i].handlers.size(); ++j) {
168 if (_objDialogs[i].handlers[j].opcode == kFallback) {
169 if (fallback)
170 *fallback = true;
171 return &_objDialogs[i].handlers[j];
172 }
173 }
174 }
175 for (uint i = 0; i < _objDialogs.size(); ++i) {
176 if (_objDialogs[i].objId != kFallback)
177 continue;
178 for (uint j = 0; j < _objDialogs[i].handlers.size(); ++j) {
179 if (_objDialogs[i].handlers[j].opcode == opcode) {
180 if (fallback)
181 *fallback = true;
182 return &_objDialogs[i].handlers[j];
183 }
184 }
185 }
186 return nullptr;
187 }
188
setHandler(uint objId,uint opcode)189 void BigDialogue::setHandler(uint objId, uint opcode) {
190 loadSpeechesInfo();
191 const DialogHandler *h = findHandler(objId, opcode, nullptr);
192 if (h) {
193 _startOpIndex = h->dialogs[h->startDialogIndex].startOpIndex;
194 _currOp = &_ops[_startOpIndex];
195 }
196 }
197
opcode()198 uint BigDialogue::opcode() {
199 while (_currOp) {
200 switch (_currOp->type) {
201 case kOperationMenu:
202 if (choicesCount() > 1)
203 return kOpcodeMenu;
204 next(0);
205 break;
206 case kOperationReturn:
207 return kOpcodeEnd;
208 case kOperationPlay:
209 case kOperationCircle:
210 return kOpcodePlay;
211 case kOperationUserMessage:
212 return kOpcodeUserMessage;
213 default:
214 next();
215 break;
216 }
217 }
218 return kOpcodeEnd;
219 }
220
load(Common::ReadStream * s)221 void BigDialogue::load(Common::ReadStream *s) {
222 uint32 opsCount = s->readUint32LE();
223 _ops.resize(opsCount);
224 for (uint i = 0; i < opsCount; ++i) {
225 uint op = s->readUint32LE();
226 _ops[i].type = (byte)(op >> 24);
227 switch (_ops[i].type) {
228 case kOperationBreak:
229 break;
230 case kOperationMenu:
231 _ops[i].menu.bits = (byte)op;
232 _ops[i].menu.bitField = (uint16)(op >> 8);
233 break;
234 case kOperationGoTo:
235 _ops[i].goTo.opIndex = (uint16)(op);
236 break;
237 case kOperationDisableMenuItem:
238 _ops[i].disableMenuItem.opIndex = (uint16)op;
239 _ops[i].disableMenuItem.bit = (byte)(op >> 16);
240 break;
241 case kOperationEnableMenuItem:
242 _ops[i].enableMenuItem.opIndex = (uint16)op;
243 _ops[i].enableMenuItem.bit = (byte)(op >> 16);
244 break;
245 case kOperationReturn:
246 break;
247 case kOperationPlay:
248 _ops[i].play.messageIndex = (uint16)op;
249 break;
250 case kOperationCircle:
251 _ops[i].circle.count = (uint16)op;
252 _ops[i].circle.curr = (byte)(op >> 16);
253 break;
254 case kOperationUserMessage:
255 _ops[i].userMsg.arg = (uint16)op;
256 break;
257 default:
258 break;
259 }
260 }
261 }
262
save(Common::WriteStream * s)263 void BigDialogue::save(Common::WriteStream *s) {
264 s->writeUint32LE(_ops.size());
265 for (uint i = 0; i < _ops.size(); ++i) {
266 switch (_ops[i].type) {
267 case kOperationBreak:
268 s->writeUint32LE(MKTAG(kOperationBreak, 0, 0, 0));
269 break;
270 case kOperationMenu:
271 s->writeByte(_ops[i].menu.bits);
272 s->writeUint16LE(_ops[i].menu.bitField);
273 s->writeByte(kOperationMenu);
274 break;
275 case kOperationGoTo:
276 s->writeUint16LE(_ops[i].goTo.opIndex);
277 s->writeUint16LE(MKTAG16(kOperationGoTo, 0));
278 break;
279 case kOperationDisableMenuItem:
280 s->writeUint16LE(_ops[i].disableMenuItem.opIndex);
281 s->writeUint16LE(MKTAG16(kOperationDisableMenuItem, _ops[i].disableMenuItem.bit));
282 break;
283 case kOperationEnableMenuItem:
284 s->writeUint16LE(_ops[i].enableMenuItem.opIndex);
285 s->writeUint16LE(MKTAG16(kOperationEnableMenuItem, _ops[i].enableMenuItem.bit));
286 break;
287 case kOperationReturn:
288 s->writeUint32LE(MKTAG(kOperationReturn, 0, 0, 0));
289 break;
290 case kOperationPlay:
291 s->writeUint16LE(_ops[i].play.messageIndex);
292 s->writeUint16LE(MKTAG16(kOperationPlay, 0));
293 break;
294 case kOperationCircle:
295 s->writeUint16LE(_ops[i].circle.count);
296 s->writeUint16LE(MKTAG16(kOperationCircle, _ops[i].circle.curr));
297 break;
298 case kOperationUserMessage:
299 s->writeUint16LE(_ops[i].userMsg.arg);
300 s->writeUint16LE(MKTAG16(kOperationUserMessage, 0));
301 break;
302 default:
303 break;
304 }
305 }
306 }
307
next(int choice)308 void BigDialogue::next(int choice) {
309 bool processed = true;
310
311 if (!_currOp)
312 return;
313 if (choice != -1 && _currOp->type != kOperationMenu) {
314 choice = -1;
315 }
316
317 while (true) {
318 switch (_currOp->type) {
319 case kOperationBreak:
320 while (_currOp->type != kOperationMenu && _currOp->type != kOperationCircle) {
321 _currOp--;
322 }
323 next(choice);
324 return;
325 case kOperationMenu: {
326 if (!processed)
327 return;
328 if (choice == -1)
329 choice = 0;
330 if (_currOp->menu.bits <= choice)
331 choice = _currOp->menu.bits - 1;
332 uint16 bitField = _currOp->menu.bitField;
333 uint bits = _currOp->menu.bits;
334 uint bit = 1;
335 uint i = 0;
336 while (bits) {
337 if (_currOp->type == kOperationBreak) {
338 bits--;
339 if (!(bit & bitField) && i <= (uint)choice)
340 choice++;
341 bit *= 2;
342 i++;
343 }
344 _currOp += 1;
345 }
346 _currOp += choice;
347 processed = false;
348 break;
349 }
350 case kOperationGoTo: {
351 _currOp = &_ops[_currOp->goTo.opIndex];
352 processed = false;
353 break;
354 }
355 case kOperationDisableMenuItem:
356 _ops[_currOp->disableMenuItem.opIndex].menu.bitField &= ~(1 << _currOp->disableMenuItem.bit); // disable menu item
357 checkMenu(_startOpIndex);
358 _currOp += 1;
359 processed = false;
360 break;
361 case kOperationEnableMenuItem:
362 _ops[_currOp->enableMenuItem.opIndex].menu.bitField |= (1 << _currOp->enableMenuItem.bit);
363 _currOp += 1;
364 processed = false;
365 break;
366 case kOperationReturn:
367 return;
368 case kOperationPlay:
369 if (!processed)
370 return;
371 _currOp += 1;
372 processed = false;
373 break;
374 case kOperationCircle:
375 if (!processed)
376 return;
377 _currOp->circle.curr = (byte)((_currOp->circle.curr + 1) % _currOp->circle.count);
378 circleMoveTo(_currOp->circle.count);
379 processed = false;
380 break;
381 case kOperationUserMessage:
382 if (processed)
383 _currOp += 1;
384 else {
385 _vm.getQSystem()->_mainInterface->_dialog.startUserMsg(_currOp->userMsg.arg);
386 }
387 return;
388 default:
389 _currOp += 1;
390 processed = false;
391 break;
392 }
393 }
394
395 }
396
choicesCount()397 uint BigDialogue::choicesCount() {
398 if (!_currOp || _currOp->type != kOperationMenu)
399 return 0;
400 uint count = 0;
401 uint bit = 1;
402 for (uint i = 0; i < _currOp->menu.bits; ++i) {
403 if (_currOp->menu.bitField & bit) {
404 count++;
405 }
406 bit <<= 1;
407 }
408 return count;
409 }
410
findOperation(uint index,uint opType,uint * resIndex)411 bool BigDialogue::findOperation(uint index, uint opType, uint *resIndex) {
412 while (_ops[index].type != opType) {
413 switch(_ops[index].type) {
414 case kOperationGoTo:
415 if (index >= _ops[index].goTo.opIndex)
416 return false;
417 index = _ops[index].goTo.opIndex;
418 break;
419 case kOperationReturn:
420 return false;
421 default:
422 index++;
423 break;
424 }
425 }
426 *resIndex = index;
427 return true;
428 }
429
checkMenu(uint menuIndex)430 bool BigDialogue::checkMenu(uint menuIndex) {
431 if (_ops[menuIndex].type != kOperationMenu && !findOperation(menuIndex, kOperationMenu, &menuIndex)) {
432 return true;
433 }
434
435 uint count = 0;
436 uint bit = 1;
437 uint opIndex = menuIndex + 1;
438 for (uint i = 0; i < _ops[menuIndex].menu.bits; ++i) {
439 if (_ops[menuIndex].menu.bitField & bit) {
440 count++;
441 }
442 findOperation(opIndex, kOperationBreak, &opIndex);
443 opIndex++;
444 bit <<= 1;
445 }
446
447 if (!count)
448 return false;
449
450 bit = 1;
451 for (uint i = 0; i < _ops[menuIndex].menu.bits; ++i) {
452 uint subMenuIndex;
453 if ((_ops[menuIndex].menu.bitField & bit) && findOperation(_ops[opIndex + i].goTo.opIndex, kOperationMenu, &subMenuIndex) && !checkMenu(subMenuIndex)) {
454 _ops[menuIndex].menu.bitField &= ~bit;
455 count--;
456 if (count < 1)
457 return false;
458 }
459 bit <<= 1;
460 }
461 return true;
462 }
463
getMenuChoices(Common::Array<Common::U32String> & choices)464 void BigDialogue::getMenuChoices(Common::Array<Common::U32String> &choices) {
465 uint count = choicesCount();
466 for (uint i = 0; i < count; ++i) {
467 int id;
468 choices.push_back(*getSpeechInfo(&id, nullptr, i));
469 }
470 }
471
circleMoveTo(byte index)472 void BigDialogue::circleMoveTo(byte index) {
473 _currOp += 1;
474 for (uint i = 0; i < index; ++i) {
475 while (_currOp->type != kOperationBreak)
476 _currOp += 1;
477 _currOp += 1;
478 }
479 }
480
481 } // End of namespace Petka
482