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/stream.h"
24 #include "common/memstream.h"
25
26 #include "startrek/iwfile.h"
27 #include "startrek/resource.h"
28 #include "startrek/room.h"
29 #include "startrek/startrek.h"
30
31 namespace StarTrek {
32
initActors()33 void StarTrekEngine::initActors() {
34 for (int i = 0; i < NUM_ACTORS; i++)
35 _actorList[i] = Actor();
36
37 for (int i = 0; i < MAX_BAN_FILES; i++) {
38 delete _banFiles[i];
39 _banFiles[i] = nullptr;
40 }
41
42 _kirkActor->animationString = "kstnd";
43 _spockActor->animationString = "sstnd";
44 _mccoyActor->animationString = "mstnd";
45 _redshirtActor->animationString = "rstnd";
46 }
47
loadActorAnim(int actorIndex,const Common::String & animName,int16 x,int16 y,Fixed8 scale)48 int StarTrekEngine::loadActorAnim(int actorIndex, const Common::String &animName, int16 x, int16 y, Fixed8 scale) {
49 debugC(6, kDebugGraphics, "Load animation '%s' on actor %d", animName.c_str(), actorIndex);
50
51 if (actorIndex == -1) {
52 bool foundSlot = false;
53
54 for (int i = 8; i < NUM_ACTORS; i++) {
55 if (_actorList[i].spriteDrawn == 0) {
56 actorIndex = i;
57 foundSlot = true;
58 break;
59 }
60 }
61
62 if (!foundSlot) {
63 error("All animations are in use");
64 }
65 }
66
67 Actor *actor = &_actorList[actorIndex];
68
69 if (actor->spriteDrawn) {
70 releaseAnim(actor);
71 drawActorToScreen(actor, animName, x, y, scale, false);
72 } else {
73 drawActorToScreen(actor, animName, x, y, scale, true);
74 }
75
76 actor->triggerActionWhenAnimFinished = false;
77 actor->finishedAnimActionParam = 0;
78
79 return actorIndex;
80 }
81
loadBanFile(const Common::String & name)82 void StarTrekEngine::loadBanFile(const Common::String &name) {
83 debugC(kDebugGeneral, 7, "Load BAN file: %s.ban", name.c_str());
84 for (int i = 0; i < MAX_BAN_FILES; i++) {
85 if (!_banFiles[i]) {
86 _banFiles[i] = _resource->loadFile(name + ".ban");
87 _banFileOffsets[i] = 0;
88 return;
89 }
90 }
91
92 warning("Couldn't load .BAN file \"%s.ban\"", name.c_str());
93 }
94
actorWalkToPosition(int actorIndex,const Common::String & animFile,int16 srcX,int16 srcY,int16 destX,int16 destY)95 bool StarTrekEngine::actorWalkToPosition(int actorIndex, const Common::String &animFile, int16 srcX, int16 srcY, int16 destX, int16 destY) {
96 debugC(6, "Obj %d: walk from (%d,%d) to (%d,%d)", actorIndex, srcX, srcY, destX, destY);
97
98 Actor *actor = &_actorList[actorIndex];
99
100 actor->triggerActionWhenAnimFinished = false;
101 if (isPositionSolid(destX, destY))
102 return false;
103
104 if (actor->spriteDrawn)
105 releaseAnim(actor);
106 else
107 _gfx->addSprite(&actor->sprite);
108
109 actor->spriteDrawn = true;
110 actor->animType = 1;
111 actor->frameToStartNextAnim = _frameIndex + 1;
112 actor->animationString2 = animFile;
113
114 actor->dest.x = destX;
115 actor->dest.y = destY;
116 actor->field92 = 0;
117 actor->triggerActionWhenAnimFinished = false;
118
119 actor->iwDestPosition = -1;
120 actor->iwSrcPosition = -1;
121
122 if (directPathExists(srcX, srcY, destX, destY)) {
123 chooseActorDirectionForWalking(actor, srcX, srcY, destX, destY);
124 updateActorPositionWhileWalking(actor, (actor->granularPosX + 0.5).toInt(), (actor->granularPosY + 0.5).toInt());
125 return true;
126 } else {
127 actor->iwSrcPosition = _iwFile->getClosestKeyPosition(srcX, srcY);
128 actor->iwDestPosition = _iwFile->getClosestKeyPosition(destX, destY);
129
130 if (actor->iwSrcPosition == -1 || actor->iwDestPosition == -1) {
131 // No path exists; face south by default.
132 actor->animationString2 += "S";
133 actor->direction = 'S';
134
135 updateActorPositionWhileWalking(actor, srcX, srcY);
136 initStandAnim(actorIndex);
137
138 return false;
139 } else {
140 Common::Point iwSrc = _iwFile->_keyPositions[actor->iwSrcPosition];
141 chooseActorDirectionForWalking(actor, srcX, srcY, iwSrc.x, iwSrc.y);
142 updateActorPositionWhileWalking(actor, (actor->granularPosX + 0.5).toInt(), (actor->granularPosY + 0.5).toInt());
143 return true;
144 }
145 }
146 }
147
updateActorAnimations()148 void StarTrekEngine::updateActorAnimations() {
149 for (int i = 0; i < NUM_ACTORS; i++) {
150 Actor *actor = &_actorList[i];
151 if (!actor->spriteDrawn)
152 continue;
153
154 switch (actor->animType) {
155 case 0: // Not walking?
156 case 2:
157 if (_frameIndex >= actor->frameToStartNextAnim) {
158 int nextAnimIndex = getRandomWord() & 3;
159 actor->animFile->seek(18 + nextAnimIndex + actor->animFrame * 22, SEEK_SET);
160 byte nextAnimFrame = actor->animFile->readByte();
161
162 if (actor->animFrame != nextAnimFrame) {
163 if (nextAnimFrame == actor->numAnimFrames - 1) {
164 actor->field62++;
165 if (actor->triggerActionWhenAnimFinished) {
166 addAction(ACTION_FINISHED_ANIMATION, actor->finishedAnimActionParam, 0, 0);
167 }
168 }
169 }
170
171 actor->animFrame = nextAnimFrame;
172 if (actor->animFrame >= actor->numAnimFrames) {
173 if (actor->animationString.empty())
174 removeActorFromScreen(i);
175 else
176 initStandAnim(i);
177 } else {
178 Sprite *sprite = &actor->sprite;
179
180 actor->animFile->seek(actor->animFrame * 22, SEEK_SET);
181 char animFrameFilename[16];
182 actor->animFile->read(animFrameFilename, 16);
183 actor->bitmapFilename = animFrameFilename;
184 actor->bitmapFilename.trim();
185 if (actor->bitmapFilename.contains(' '))
186 actor->bitmapFilename = actor->bitmapFilename.substr(0, actor->bitmapFilename.find(' '));
187
188 sprite->setBitmap(loadAnimationFrame(actor->bitmapFilename, actor->scale));
189
190 actor->animFile->seek(10 + actor->animFrame * 22, SEEK_SET);
191 uint16 xOffset = actor->animFile->readUint16();
192 uint16 yOffset = actor->animFile->readUint16();
193 uint16 basePriority = actor->animFile->readUint16();
194 uint16 frames = actor->animFile->readUint16();
195
196 sprite->pos.x = xOffset + actor->pos.x;
197 sprite->pos.y = yOffset + actor->pos.y;
198 sprite->drawPriority = _gfx->getPriValue(0, yOffset + actor->pos.y) + basePriority;
199 sprite->bitmapChanged = true;
200
201 actor->frameToStartNextAnim = frames + _frameIndex;
202 }
203 }
204 break;
205 case 1: // Walking
206 if (_frameIndex < actor->frameToStartNextAnim)
207 break;
208 if (i == 0) // Kirk only
209 checkTouchedLoadingZone(actor->pos.x, actor->pos.y);
210 if (actor->field90 != 0) {
211 Sprite *sprite = &actor->sprite;
212 int loops;
213 if (getActorScaleAtPosition((actor->granularPosY + 0.5).toInt()) < 0.625)
214 loops = 1;
215 else
216 loops = 2;
217 for (int k = 0; k < loops; k++) {
218 if (actor->field90 == 0)
219 break;
220 actor->field90--;
221 Fixed16 newX = actor->granularPosX + actor->speedX;
222 Fixed16 newY = actor->granularPosY + actor->speedY;
223 if ((actor->field90 & 3) == 0) {
224 delete sprite->bitmap;
225 sprite->bitmap = nullptr;
226 updateActorPositionWhileWalking(actor, (newX + 0.5).toInt(), (newY + 0.5).toInt());
227 actor->field92++;
228 }
229
230 actor->granularPosX = newX;
231 actor->granularPosY = newY;
232 actor->frameToStartNextAnim = _frameIndex;
233 }
234 } else { // actor->field90 == 0
235 if (actor->iwSrcPosition == -1) {
236 if (actor->triggerActionWhenAnimFinished) {
237 actor->triggerActionWhenAnimFinished = false;
238 addAction(ACTION_FINISHED_WALKING, actor->finishedAnimActionParam & 0xff, 0, 0);
239 }
240
241 delete actor->sprite.bitmap;
242 actor->sprite.bitmap = nullptr;
243 updateActorPositionWhileWalking(actor, (actor->granularPosX + 0.5).toInt(), (actor->granularPosY + 0.5).toInt());
244 initStandAnim(i);
245 } else { // actor->iwSrcPosition != -1
246 if (actor->iwSrcPosition == actor->iwDestPosition) {
247 actor->animationString2.deleteLastChar();
248 actor->iwDestPosition = -1;
249 actor->iwSrcPosition = -1;
250 chooseActorDirectionForWalking(actor, actor->pos.x, actor->pos.y, actor->dest.x, actor->dest.y);
251 } else {
252 int index = _iwFile->_iwEntries[actor->iwSrcPosition][actor->iwDestPosition];
253 actor->iwSrcPosition = index;
254 Common::Point dest = _iwFile->_keyPositions[actor->iwSrcPosition];
255 actor->animationString2.deleteLastChar();
256 chooseActorDirectionForWalking(actor, actor->pos.x, actor->pos.y, dest.x, dest.y);
257 }
258 }
259 }
260 break;
261 default:
262 error("Invalid anim type.");
263 break;
264 }
265 }
266 }
267
renderBanBelowSprites()268 void StarTrekEngine::renderBanBelowSprites() {
269 if ((_frameIndex & 3) != 0)
270 return;
271
272 byte *screenPixels = _gfx->lockScreenPixels();
273 byte *bgPixels = _gfx->getBackgroundPixels();
274
275 for (int i = 0; i < MAX_BAN_FILES; i++) {
276 if (!_banFiles[i])
277 continue;
278
279 // TODO: video modes other than VGA
280
281 _banFiles[i]->seek(_banFileOffsets[i], SEEK_SET);
282 uint16 offset = _banFiles[i]->readUint16();
283
284 if (offset == 0xffff) {
285 _banFileOffsets[i] = 0;
286 _banFiles[i]->seek(0, SEEK_SET);
287 offset = _banFiles[i]->readSint16();
288 }
289
290 int16 size = _banFiles[i]->readSint16();
291 if (size != 0)
292 renderBan(screenPixels, bgPixels, i);
293 }
294
295 _gfx->unlockScreenPixels();
296 }
297
renderBan(byte * screenPixels,byte * bgPixels,int banFileIndex)298 void StarTrekEngine::renderBan(byte *screenPixels, byte *bgPixels, int banFileIndex) {
299 Common::MemoryReadStreamEndian *banFile = _banFiles[banFileIndex];
300 banFile->seek(_banFileOffsets[banFileIndex], SEEK_SET);
301
302 uint16 offset = banFile->readUint16();
303 int32 size = banFile->readUint16();
304
305 byte *dest1 = screenPixels + offset;
306 byte *dest2 = bgPixels + offset;
307
308 // Skip 8 bytes (rectangle encompassing the area being drawn to)
309 banFile->skip(8);
310
311 while (--size >= 0) {
312 assert(dest1 >= screenPixels && dest1 < screenPixels + SCREEN_WIDTH * SCREEN_HEIGHT);
313 assert(dest2 >= bgPixels && dest2 < bgPixels + SCREEN_WIDTH * SCREEN_HEIGHT);
314
315 int8 b = banFile->readByte();
316
317 if (b == -128) { // Add value to destination (usually jumping to next row)
318 uint16 skip = banFile->readUint16();
319 dest1 += skip;
320 dest2 += skip;
321 } else if (b < 0) { // Repeated byte
322 byte c = banFile->readByte();
323 if (c == 0) {
324 dest1 += (-b) + 1;
325 dest2 += (-b) + 1;
326 } else {
327 for (int j = 0; j < (-b) + 1; j++) {
328 (*dest1++) = c;
329 (*dest2++) = c;
330 }
331 }
332 } else { // List of bytes
333 b++;
334 while (b-- != 0) {
335 byte c = banFile->readByte();
336 if (c == 0) {
337 dest1++;
338 dest2++;
339 } else {
340 *(dest1++) = c;
341 *(dest2++) = c;
342 }
343 }
344 }
345 }
346 }
347
renderBanAboveSprites()348 void StarTrekEngine::renderBanAboveSprites() {
349 if ((_frameIndex & 3) != 0)
350 return;
351
352 for (int i = 0; i < MAX_BAN_FILES; i++) {
353 if (!_banFiles[i])
354 continue;
355
356 _banFiles[i]->seek(_banFileOffsets[i], SEEK_SET);
357 uint16 offset = _banFiles[i]->readUint16();
358
359 if (offset == 0xffff) {
360 _banFileOffsets[i] = 0;
361 _banFiles[i]->seek(0, SEEK_SET);
362 offset = _banFiles[i]->readSint16();
363 }
364
365 int16 size = _banFiles[i]->readSint16();
366 if (size != 0) {
367 Common::Rect rect;
368 rect.left = _banFiles[i]->readSint16();
369 rect.top = _banFiles[i]->readSint16();
370 rect.right = _banFiles[i]->readSint16() + 1;
371 rect.bottom = _banFiles[i]->readSint16() + 1;
372
373 // Draw all sprites in this rectangle to a custom surface, and only update the
374 // specific pixels that were updated by the BAN file this frame.
375 // Rationale behind this is that, since the background may not have been
376 // redrawn, the transparent sprites (ie. textboxes) would further darken any
377 // pixels behind them that haven't been updated this frame. So, we can't just
378 // update everything in this rectangle.
379 // FIXME: This copies the entire screen surface for temporary drawing, which
380 // is somewhat wasteful. Original game had one more graphics layer it drew to
381 // before the screen was updated...
382 ::Graphics::Surface surface;
383 _gfx->drawAllSpritesInRectToSurface(rect, &surface);
384
385 byte *destPixels = _gfx->lockScreenPixels();
386 byte *src = (byte *)surface.getPixels() + offset;
387 byte *dest = destPixels + offset;
388
389 // This is similar to renderBan(), except it copies pixels from the surface
390 // above instead of drawing directly to it. (Important since sprites may be
391 // drawn on top.)
392 while (--size >= 0) {
393 assert(dest >= destPixels && dest < destPixels + SCREEN_WIDTH * SCREEN_HEIGHT);
394 int8 b = _banFiles[i]->readByte();
395 if (b == -128) {
396 uint16 skip = _banFiles[i]->readUint16();
397 dest += skip;
398 src += skip;
399 } else if (b < 0) {
400 byte c = _banFiles[i]->readByte();
401 if (c == 0) {
402 dest += (-b) + 1;
403 src += (-b) + 1;
404 }
405 else {
406 for (int j = 0; j < (-b) + 1; j++)
407 *(dest++) = *(src++);
408 }
409 } else {
410 b++;
411 while (b-- != 0) {
412 byte c = _banFiles[i]->readByte();
413 if (c == 0) {
414 dest++;
415 src++;
416 } else
417 *(dest++) = *(src++);
418 }
419 }
420 }
421
422 _gfx->unlockScreenPixels();
423 surface.free();
424
425 _banFileOffsets[i] = _banFiles[i]->pos();
426 }
427 }
428 }
429
removeActorFromScreen(int actorIndex)430 void StarTrekEngine::removeActorFromScreen(int actorIndex) {
431 Actor *actor = &_actorList[actorIndex];
432
433 if (actor->spriteDrawn != 1)
434 return;
435
436 debugC(6, kDebugGraphics, "Stop drawing actor %d", actorIndex);
437
438 Sprite *sprite = &actor->sprite;
439 sprite->field16 = true;
440 sprite->bitmapChanged = true;
441 _gfx->drawAllSprites();
442 _gfx->delSprite(sprite);
443 releaseAnim(actor);
444 }
445
removeDrawnActorsFromScreen()446 void StarTrekEngine::removeDrawnActorsFromScreen() {
447 for (int i = 0; i < NUM_ACTORS; i++) {
448 if (_actorList[i].spriteDrawn == 1) {
449 removeActorFromScreen(i);
450 }
451 }
452
453 for (int i = 0; i < MAX_BAN_FILES; i++) {
454 delete _banFiles[i];
455 _banFiles[i] = nullptr;
456 }
457 }
458
drawActorToScreen(Actor * actor,const Common::String & _animName,int16 x,int16 y,Fixed8 scale,bool addSprite)459 void StarTrekEngine::drawActorToScreen(Actor *actor, const Common::String &_animName, int16 x, int16 y, Fixed8 scale, bool addSprite) {
460 Common::String animFilename = _animName;
461 if (_animName.hasPrefixIgnoreCase("stnd") /* && word_45d20 == -1 */) // TODO
462 animFilename += 'j';
463
464 actor->animFilename = _animName;
465 actor->animType = 2;
466 actor->animFile = SharedPtr<Common::MemoryReadStreamEndian>(_resource->loadFile(animFilename + ".anm"));
467 actor->numAnimFrames = actor->animFile->size() / 22;
468 actor->animFrame = 0;
469 actor->pos.x = x;
470 actor->pos.y = y;
471 actor->field62 = 0;
472 actor->scale = scale;
473
474 actor->animFile->seek(16, SEEK_SET);
475 actor->frameToStartNextAnim = actor->animFile->readUint16() + _frameIndex;
476
477 char firstFrameFilename[11];
478 actor->animFile->seek(0, SEEK_SET);
479 actor->animFile->read(firstFrameFilename, 10);
480 firstFrameFilename[10] = '\0';
481
482 Sprite *sprite = &actor->sprite;
483 if (addSprite)
484 _gfx->addSprite(sprite);
485
486 actor->bitmapFilename = firstFrameFilename;
487 actor->bitmapFilename.trim();
488 sprite->setBitmap(loadAnimationFrame(actor->bitmapFilename, scale));
489 actor->scale = scale;
490 actor->animFile->seek(10, SEEK_SET);
491
492 uint16 xOffset = actor->animFile->readUint16();
493 uint16 yOffset = actor->animFile->readUint16();
494 uint16 basePriority = actor->animFile->readUint16();
495
496 sprite->pos.x = xOffset + actor->pos.x;
497 sprite->pos.y = yOffset + actor->pos.y;
498 sprite->drawPriority = _gfx->getPriValue(0, yOffset + actor->pos.y) + basePriority;
499 sprite->bitmapChanged = true;
500
501 actor->spriteDrawn = 1;
502 }
503
releaseAnim(Actor * actor)504 void StarTrekEngine::releaseAnim(Actor *actor) {
505 switch (actor->animType) {
506 case 0:
507 case 2:
508 actor->animFile.reset();
509 // Fall through
510 case 1:
511 delete actor->sprite.bitmap;
512 actor->sprite.bitmap = nullptr;
513 break;
514 default:
515 error("Invalid anim type");
516 break;
517 }
518
519 actor->spriteDrawn = 0;
520 }
521
initStandAnim(int actorIndex)522 void StarTrekEngine::initStandAnim(int actorIndex) {
523 Actor *actor = &_actorList[actorIndex];
524
525 if (!actor->spriteDrawn)
526 error("initStandAnim: dead anim");
527
528 ////////////////////
529 // sub_239d2
530 const char *directions = "nsew";
531
532 if (actorIndex >= 0 && actorIndex <= 3) {
533 int8 dir = _awayMission.crewDirectionsAfterWalk[actorIndex];
534 if (dir != -1) {
535 actor->direction = directions[dir];
536 _awayMission.crewDirectionsAfterWalk[actorIndex] = -1;
537 }
538 }
539 // end of sub_239d2
540 ////////////////////
541
542 Common::String animName;
543 if (actor->direction != 0)
544 animName = actor->animationString + (char)actor->direction;
545 else // Default to facing south
546 animName = actor->animationString + 's';
547
548 Fixed8 scale = getActorScaleAtPosition(actor->pos.y);
549 loadActorAnim(actorIndex, animName, actor->pos.x, actor->pos.y, scale);
550 actor->animType = 0;
551 }
552
updateActorPositionWhileWalking(Actor * actor,int16 x,int16 y)553 void StarTrekEngine::updateActorPositionWhileWalking(Actor *actor, int16 x, int16 y) {
554 actor->scale = getActorScaleAtPosition(y);
555 Common::String animName = Common::String::format("%s%02d", actor->animationString2.c_str(), actor->field92 & 7);
556 actor->sprite.setBitmap(loadAnimationFrame(animName, actor->scale));
557 actor->bitmapFilename = animName;
558
559 Sprite *sprite = &actor->sprite;
560 sprite->drawPriority = _gfx->getPriValue(0, y);
561 sprite->pos.x = x;
562 sprite->pos.y = y;
563 sprite->bitmapChanged = true;
564
565 actor->frameToStartNextAnim = _frameIndex;
566 actor->pos.x = x;
567 actor->pos.y = y;
568 }
569
chooseActorDirectionForWalking(Actor * actor,int16 srcX,int16 srcY,int16 destX,int16 destY)570 void StarTrekEngine::chooseActorDirectionForWalking(Actor *actor, int16 srcX, int16 srcY, int16 destX, int16 destY) {
571 actor->granularPosX = srcX;
572 actor->granularPosY = srcY;
573
574 int16 distX = destX - srcX;
575 int16 distY = destY - srcY;
576 int16 absDistX = abs(distX);
577 int16 absDistY = abs(distY);
578
579 if (absDistX > absDistY) {
580 char d;
581 if (distX > 0)
582 d = 'E';
583 else
584 d = 'W';
585
586 // Append direction to animation string
587 actor->animationString2 += d;
588
589 actor->direction = d;
590 actor->field90 = absDistX;
591
592 if (distX != 0) {
593 if (distX > 0)
594 actor->speedX = 1.0;
595 else
596 actor->speedX = -1.0;
597
598 actor->speedY = Fixed16(distY) / absDistX;
599 }
600 } else {
601 char d;
602 if (distY > 0)
603 d = 'S';
604 else
605 d = 'N';
606
607 // Append direction to animation string
608 actor->animationString2 += d;
609
610 actor->direction = d;
611 actor->field90 = absDistY;
612
613 if (distY != 0) {
614 if (distY > 0)
615 actor->speedY = 1.0;
616 else
617 actor->speedY = -1.0;
618
619 actor->speedX = Fixed16(distX) / absDistY;
620 }
621 }
622 }
623
directPathExists(int16 srcX,int16 srcY,int16 destX,int16 destY)624 bool StarTrekEngine::directPathExists(int16 srcX, int16 srcY, int16 destX, int16 destY) {
625 int32 distX = destX - srcX;
626 int32 distY = destY - srcY;
627
628 int32 absDistX = abs(distX);
629 int32 absDistY = abs(distY);
630
631 int32 distCounter;
632 Fixed16 speedX, speedY;
633
634 if (absDistX > absDistY) {
635 distCounter = absDistX;
636
637 if (distCounter == 0)
638 return true;
639
640 speedY = Fixed16(distY) / absDistX;
641
642 if (distX > 0)
643 speedX = 1.0;
644 else
645 speedX = -1.0;
646 } else { // absDistX <= absDistY
647 distCounter = absDistY;
648
649 if (distCounter == 0)
650 return true;
651
652 speedX = Fixed16(distX) / absDistY;
653
654 if (distY > 0)
655 speedY = 1.0;
656 else
657 speedY = -1.0;
658 }
659
660 Fixed16 fixedX = srcX;
661 Fixed16 fixedY = srcY;
662
663 if (isPositionSolid((fixedX + 0.5).toInt(), (fixedY + 0.5).toInt()))
664 return false;
665
666 while (distCounter-- > 0) {
667 fixedX += speedX;
668 fixedY += speedY;
669
670 if (isPositionSolid((fixedX + 0.5).toInt(), (fixedY + 0.5).toInt()))
671 return false;
672 }
673
674 return true;
675 }
676
findObjectAt(int x,int y)677 int StarTrekEngine::findObjectAt(int x, int y) {
678 Sprite *sprite = _gfx->getSpriteAt(x, y);
679
680 if (sprite != nullptr) {
681 if (sprite == &_inventoryIconSprite)
682 return OBJECT_INVENTORY_ICON;
683 else if (sprite == &_itemIconSprite)
684 return _awayMission.activeObject;
685
686 for (int i = 0; i < NUM_ACTORS; i++) {
687 Actor *actor = &_actorList[i];
688 if (sprite == &actor->sprite)
689 return i;
690 }
691
692 error("findObject: Clicked on an unknown sprite");
693 }
694
695 _objectHasWalkPosition = false;
696 int actionBit = 1 << (_awayMission.activeAction - 1);
697 int offset = _room->getFirstHotspot();
698
699 while (offset != _room->getHotspotEnd()) {
700 uint16 word = _room->readRdfWord(offset);
701 if (word & 0x8000) {
702 if ((word & actionBit) && _room->isPointInPolygon(offset + 6, x, y)) {
703 int actorIndex = _room->readRdfWord(offset + 6);
704 _objectHasWalkPosition = true;
705 _objectWalkPosition.x = _room->readRdfWord(offset + 2);
706 _objectWalkPosition.y = _room->readRdfWord(offset + 4);
707 return actorIndex;
708 }
709
710 int numVertices = _room->readRdfWord(offset + 8);
711 offset = offset + 10 + numVertices * 4;
712 } else {
713 if (_room->isPointInPolygon(offset, x, y)) {
714 int actorIndex = _room->readRdfWord(offset);
715 return actorIndex;
716 }
717
718 int numVertices = _room->readRdfWord(offset + 2);
719 offset = offset + 4 + numVertices * 4;
720 }
721 }
722
723 return -1;
724 }
725
loadAnimationFrame(const Common::String & filename,Fixed8 scale)726 Bitmap *StarTrekEngine::loadAnimationFrame(const Common::String &filename, Fixed8 scale) {
727 Bitmap *bitmapToReturn = nullptr;
728 bool isDemo = getFeatures() & GF_DEMO;
729
730 char basename[5];
731 strncpy(basename, filename.c_str() + 1, 4);
732 basename[4] = '\0';
733
734 char mcCoyChar = !isDemo ? 'm' : 'b';
735
736 char c = filename[0];
737 if ((strcmp(basename, "stnd") == 0 || strcmp(basename, "tele") == 0)
738 && (c == mcCoyChar || c == 's' || c == 'k' || c == 'r')) {
739 if (c == mcCoyChar) {
740 // Mccoy has the "base" animations for all crewmen
741 bitmapToReturn = new Bitmap(_resource->loadBitmapFile(filename));
742 } else {
743 // All crewman other than mccoy copy the animation frames from mccoy, change
744 // the colors of the uniforms, and load an "xor" file to redraw the face.
745
746 // TODO: The ".$bm" extension is a "virtual file"? Caches the changes to the
747 // file made here?
748 // bitmapToReturn = new Bitmap(loadBitmapFile(filename + ".$bm"));
749
750 if (bitmapToReturn == nullptr) {
751 Common::String mccoyFilename = filename;
752 mccoyFilename.setChar(mcCoyChar, 0);
753 if (isDemo && mccoyFilename.hasPrefix("bstnds"))
754 mccoyFilename.setChar('m', 0);
755 Bitmap *bitmap = new Bitmap(_resource->loadBitmapFile(mccoyFilename));
756
757 uint16 width = bitmap->width;
758 uint16 height = bitmap->height;
759
760 bitmapToReturn = new Bitmap(width, height);
761 bitmapToReturn->xoffset = bitmap->xoffset;
762 bitmapToReturn->yoffset = bitmap->yoffset;
763
764 // Change uniform color
765 int16 colorShift;
766 switch (c) {
767 case 'k': // Kirk
768 colorShift = 8;
769 break;
770 case 'r': // Redshirt
771 colorShift = -8;
772 break;
773 case 's': // Spock
774 colorShift = 0;
775 break;
776 case 'm': // McCoy
777 case 'b': // McCoy (demo)
778 colorShift = 0;
779 break;
780 default:
781 colorShift = 0;
782 break;
783 }
784
785 if (colorShift == 0) {
786 memcpy(bitmapToReturn->pixels, bitmap->pixels, width * height);
787 } else {
788 byte *src = bitmap->pixels;
789 byte *dest = bitmapToReturn->pixels;
790 byte baseUniformColor = 0xa8;
791
792 for (int i = 0; i < width * height; i++) {
793 byte b = *src++;
794 if (b >= baseUniformColor && b < baseUniformColor + 8)
795 *dest++ = b + colorShift;
796 else
797 *dest++ = b;
798 }
799 }
800
801 // Redraw face with XOR file
802 if (!isDemo) {
803 Common::MemoryReadStreamEndian *xorFile = _resource->loadFile(filename + ".xor");
804 xorFile->seek(0, SEEK_SET);
805 uint16 xoffset = bitmap->xoffset - xorFile->readUint16();
806 uint16 yoffset = bitmap->yoffset - xorFile->readUint16();
807 uint16 xorWidth = xorFile->readUint16();
808 uint16 xorHeight = xorFile->readUint16();
809
810 byte *dest = bitmapToReturn->pixels + yoffset * bitmap->width + xoffset;
811
812 for (int i = 0; i < xorHeight; i++) {
813 for (int j = 0; j < xorWidth; j++)
814 *dest++ ^= xorFile->readByte();
815 dest += (bitmap->width - xorWidth);
816 }
817
818 delete xorFile;
819 }
820
821 delete bitmap;
822 }
823 }
824 } else {
825 // TODO: when loading a bitmap, it passes a different argument than is standard to
826 // the "file loading with cache" function...
827 bitmapToReturn = new Bitmap(_resource->loadBitmapFile(filename));
828 }
829
830 if (scale != 1.0) {
831 bitmapToReturn = scaleBitmap(bitmapToReturn, scale);
832 }
833
834 return bitmapToReturn;
835 }
836
837
selectObjectForUseAction()838 int StarTrekEngine::selectObjectForUseAction() {
839 while (true) {
840 if (!(_awayMission.crewDownBitset & (1 << OBJECT_KIRK)))
841 showInventoryIcons(false);
842
843 TrekEvent event;
844
845 while (true) {
846 if (!getNextEvent(&event))
847 continue;
848
849 if (event.type == TREKEVENT_TICK) {
850 updateMouseBitmap();
851 _gfx->drawAllSprites();
852 _sound->checkLoopMusic();
853 } else if (event.type == TREKEVENT_LBUTTONDOWN) {
854 removeNextEvent();
855 break;
856 } else if (event.type == TREKEVENT_MOUSEMOVE) {
857 } else if (event.type == TREKEVENT_RBUTTONDOWN) {
858 // Allow this to be processed by main away mission loop
859 break;
860 } else if (event.type == TREKEVENT_KEYDOWN) {
861 if (event.kbd.keycode == Common::KEYCODE_ESCAPE
862 || event.kbd.keycode == Common::KEYCODE_w
863 || event.kbd.keycode == Common::KEYCODE_t
864 || event.kbd.keycode == Common::KEYCODE_u
865 || event.kbd.keycode == Common::KEYCODE_g
866 || event.kbd.keycode == Common::KEYCODE_l
867 || event.kbd.keycode == Common::KEYCODE_SPACE
868 || event.kbd.keycode == Common::KEYCODE_F2) {
869 // Allow these buttons to be processed by main away mission loop
870 break;
871 } else if (event.kbd.keycode == Common::KEYCODE_i) {
872 removeNextEvent();
873 break;
874 } else if (event.kbd.keycode == Common::KEYCODE_RETURN || event.kbd.keycode == Common::KEYCODE_KP_ENTER || event.kbd.keycode == Common::KEYCODE_F1) {
875 // Simulate left-click
876 removeNextEvent();
877 event.type = TREKEVENT_LBUTTONDOWN;
878 break;
879 }
880 }
881
882 removeNextEvent();
883 }
884
885 if (event.type == TREKEVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_i) {
886 hideInventoryIcons();
887 int clickedObject = showInventoryMenu(50, 50, true);
888 if (clickedObject == -1)
889 continue;
890 return clickedObject;
891 } else if (event.type == TREKEVENT_LBUTTONDOWN) {
892 int clickedObject = findObjectAt(_gfx->getMousePos());
893 hideInventoryIcons();
894
895 if (clickedObject == -1)
896 continue;
897 else if (isObjectUnusable(clickedObject, ACTION_USE))
898 continue;
899 else if (clickedObject == OBJECT_INVENTORY_ICON) {
900 clickedObject = showInventoryMenu(50, 50, false);
901 if (clickedObject == -1)
902 continue;
903 else
904 return clickedObject;
905 } else if (clickedObject <= OBJECT_REDSHIRT)
906 return clickedObject;
907 else if (isObjectUnusable(OBJECT_KIRK, ACTION_USE))
908 continue;
909 else if (_room->actionHasCode(ACTION_USE, OBJECT_KIRK, clickedObject, 0)
910 || _room->actionHasCode(ACTION_GET, clickedObject, 0, 0)
911 || _room->actionHasCode(ACTION_WALK, clickedObject, 0, 0)) {
912 _awayMission.activeObject = OBJECT_KIRK;
913 _awayMission.passiveObject = clickedObject;
914 _awayMission.activeAction = ACTION_USE;
915 clickedObject = OBJECT_KIRK;
916 if (!walkActiveObjectToHotspot())
917 addAction(_awayMission.activeAction, _awayMission.activeObject, _awayMission.passiveObject, 0);
918 return clickedObject;
919 } else
920 continue;
921 } else {
922 hideInventoryIcons();
923 return -1;
924 }
925 }
926 }
927
getCrewmanAnimFilename(int actorIndex,const Common::String & basename)928 Common::String StarTrekEngine::getCrewmanAnimFilename(int actorIndex, const Common::String &basename) {
929 bool isDemo = getFeatures() & GF_DEMO;
930 const char *crewmanChars = !isDemo ? "ksmr" : "ksbr"; // Kirk, Spock, McCoy (Bones), RedShirt
931 assert(actorIndex >= 0 && actorIndex < 4);
932 return crewmanChars[actorIndex] + basename;
933 }
934
updateMouseBitmap()935 void StarTrekEngine::updateMouseBitmap() {
936 const bool worksOnCrewmen[] = { // True if the action reacts with crewmen
937 false, // ACTION_WALK
938 true, // ACTION_USE
939 false, // ACTION_GET
940 true, // ACTION_LOOK
941 true // ACTION_TALK
942 };
943 const bool worksOnActors[] = { // True if the action reacts with other objects
944 false, // ACTION_WALK
945 true, // ACTION_USE
946 true, // ACTION_GET
947 true, // ACTION_LOOK
948 true // ACTION_TALK
949 };
950 const bool worksOnHotspots[] = { // True if the action reacts with hotspots
951 false, // ACTION_WALK
952 true, // ACTION_USE
953 true, // ACTION_GET
954 true, // ACTION_LOOK
955 false // ACTION_TALK
956 };
957
958 Common::Point mousePos = _gfx->getMousePos();
959 int selected = findObjectAt(mousePos.x, mousePos.y);
960 int action = _awayMission.activeAction;
961 assert(action >= 1 && action <= 5);
962
963 bool withRedOutline;
964
965 if (selected >= 0 && selected <= 3 && worksOnCrewmen[action - 1])
966 withRedOutline = true;
967 else if (selected > 3 && selected < NUM_ACTORS && worksOnActors[action - 1])
968 withRedOutline = true;
969 else if (selected >= NUM_ACTORS && selected < HOTSPOTS_END && worksOnHotspots[action - 1])
970 withRedOutline = true;
971 else
972 withRedOutline = false;
973
974 chooseMouseBitmapForAction(action, withRedOutline);
975 }
976
walkActiveObjectToHotspot()977 bool StarTrekEngine::walkActiveObjectToHotspot() {
978 if (!_objectHasWalkPosition)
979 return false;
980
981 int objectIndex;
982 if (_awayMission.activeAction != ACTION_USE)
983 objectIndex = OBJECT_KIRK;
984 else if (_awayMission.activeObject <= OBJECT_REDSHIRT)
985 objectIndex = _awayMission.activeObject;
986 else if (_awayMission.activeObject >= ITEMS_START && _awayMission.activeObject <= ITEMS_END) { // FIXME: "<= ITEMS_END" doesn't make sense?
987 if (_awayMission.activeObject == OBJECT_ISTRICOR)
988 objectIndex = OBJECT_SPOCK;
989 else if (_awayMission.activeObject == OBJECT_IMTRICOR)
990 objectIndex = OBJECT_MCCOY;
991 else
992 objectIndex = OBJECT_KIRK;
993 } else // This is the original error message...
994 error("Jay didn't think about pmcheck");
995
996 byte finishedAnimActionParam = false;
997 bool walk = false;
998
999 if (_awayMission.activeAction == ACTION_WALK)
1000 walk = true;
1001 else {
1002 // If this action has code defined for it in this room, buffer the action to be
1003 // done after the object finished walking there.
1004 Action action = {static_cast<int8>(_awayMission.activeAction), _awayMission.activeObject, 0, 0};
1005 if (_awayMission.activeAction == ACTION_USE)
1006 action.b2 = _awayMission.passiveObject;
1007
1008 if (_room->actionHasCode(action)) {
1009 for (int i = 0; i < MAX_BUFFERED_WALK_ACTIONS; i++) {
1010 if (!_actionOnWalkCompletionInUse[i]) {
1011 finishedAnimActionParam = i + 0xe0;
1012 _actionOnWalkCompletionInUse[i] = true;
1013 _actionOnWalkCompletion[i] = action;
1014 walk = true;
1015 break;
1016 }
1017 }
1018 }
1019 }
1020
1021 if (walk) {
1022 Actor *actor = &_actorList[objectIndex];
1023 Common::String anim = getCrewmanAnimFilename(objectIndex, "walk");
1024 actorWalkToPosition(objectIndex, anim, actor->pos.x, actor->pos.y, _objectWalkPosition.x, _objectWalkPosition.y);
1025 if (finishedAnimActionParam != 0) {
1026 actor->triggerActionWhenAnimFinished = true;
1027 actor->finishedAnimActionParam = finishedAnimActionParam;
1028 }
1029 _objectHasWalkPosition = false;
1030 return true;
1031 } else {
1032 _objectHasWalkPosition = false;
1033 return false;
1034 }
1035 }
1036
showInventoryIcons(bool showItem)1037 void StarTrekEngine::showInventoryIcons(bool showItem) {
1038 const char *crewmanFilenames[] = {
1039 "ikirk",
1040 "ispock",
1041 "imccoy",
1042 "iredshir"
1043 };
1044
1045 Common::String itemFilename;
1046
1047 if (showItem) {
1048 int i = _awayMission.activeObject;
1049 if (i >= OBJECT_KIRK && i <= OBJECT_REDSHIRT)
1050 itemFilename = crewmanFilenames[i];
1051 else {
1052 assert(i >= ITEMS_START && i < ITEMS_END);
1053 Item *item = &_itemList[i - ITEMS_START];
1054 itemFilename = item->name;
1055 }
1056 }
1057
1058 if (itemFilename.empty())
1059 _inventoryIconSprite.pos.x = 10;
1060 else {
1061 _gfx->addSprite(&_itemIconSprite);
1062 _itemIconSprite.drawMode = 2;
1063 _itemIconSprite.pos.x = 10;
1064 _itemIconSprite.pos.y = 10;
1065 _itemIconSprite.drawPriority = 15;
1066 _itemIconSprite.drawPriority2 = 8;
1067 _itemIconSprite.setBitmap(_resource->loadBitmapFile(itemFilename));
1068
1069 _inventoryIconSprite.pos.x = 46;
1070 }
1071
1072 _gfx->addSprite(&_inventoryIconSprite);
1073
1074 _inventoryIconSprite.pos.y = 10;
1075 _inventoryIconSprite.drawMode = 2;
1076 _inventoryIconSprite.drawPriority = 15;
1077 _inventoryIconSprite.drawPriority2 = 8;
1078 _inventoryIconSprite.setBitmap(_resource->loadBitmapFile("inv00"));
1079 }
1080
isObjectUnusable(int object,int action)1081 bool StarTrekEngine::isObjectUnusable(int object, int action) {
1082 if (action == ACTION_LOOK)
1083 return false;
1084 if (object == OBJECT_REDSHIRT && _awayMission.redshirtDead)
1085 return true;
1086 if (object >= OBJECT_KIRK && object <= OBJECT_REDSHIRT && (_awayMission.crewDownBitset & (1 << object)))
1087 return true;
1088 if (object == OBJECT_IMTRICOR && (_awayMission.crewDownBitset & (1 << OBJECT_MCCOY)))
1089 return true;
1090 if (object == OBJECT_ISTRICOR && (_awayMission.crewDownBitset & (1 << OBJECT_SPOCK)))
1091 return true;
1092 return false;
1093 }
1094
hideInventoryIcons()1095 void StarTrekEngine::hideInventoryIcons() {
1096 // Clear these sprites from the screen
1097 if (_itemIconSprite.drawMode == 2)
1098 _itemIconSprite.dontDrawNextFrame();
1099 if (_inventoryIconSprite.drawMode == 2)
1100 _inventoryIconSprite.dontDrawNextFrame();
1101
1102 _gfx->drawAllSprites();
1103
1104 if (_itemIconSprite.drawMode == 2) {
1105 _gfx->delSprite(&_itemIconSprite);
1106 _itemIconSprite.drawMode = 0;
1107 delete _itemIconSprite.bitmap;
1108 _itemIconSprite.bitmap = nullptr;
1109 }
1110
1111 if (_inventoryIconSprite.drawMode == 2) {
1112 _gfx->delSprite(&_inventoryIconSprite);
1113 _inventoryIconSprite.drawMode = 0;
1114 delete _inventoryIconSprite.bitmap;
1115 _inventoryIconSprite.bitmap = nullptr;
1116 }
1117 }
1118
updateCrewmanGetupTimers()1119 void StarTrekEngine::updateCrewmanGetupTimers() {
1120 if (_awayMission.crewDownBitset == 0)
1121 return;
1122 for (int i = OBJECT_KIRK; i <= OBJECT_REDSHIRT; i++) {
1123 Actor *actor = &_actorList[i];
1124
1125 if (!(_awayMission.crewDownBitset & (1 << i)))
1126 continue;
1127
1128 _awayMission.crewGetupTimers[i]--;
1129 if (_awayMission.crewGetupTimers[i] <= 0) {
1130 Common::String anim = getCrewmanAnimFilename(i, "getu");
1131 int8 dir = _awayMission.crewDirectionsAfterWalk[i];
1132 char d;
1133 if (dir == -1) {
1134 d = actor->direction;
1135 } else {
1136 const char *dirs = "nsew";
1137 Fixed8 scale = getActorScaleAtPosition(actor->sprite.pos.y);
1138 d = dirs[dir];
1139
1140 int16 xOffset = 0, yOffset = 0;
1141 if (d == 'n') {
1142 xOffset = -24;
1143 yOffset = -8;
1144 } else if (d == 'w') {
1145 xOffset = -35;
1146 yOffset = -12;
1147 }
1148 actor->sprite.pos.x += scale.multToInt(xOffset);
1149 actor->sprite.pos.y += scale.multToInt(yOffset);
1150 }
1151
1152 anim += (char)d;
1153 loadActorAnimWithRoomScaling(i, anim, actor->sprite.pos.x, actor->sprite.pos.y);
1154 _awayMission.crewDownBitset &= ~(1 << i);
1155 }
1156 }
1157 }
1158
showInventoryMenu(int x,int y,bool restoreMouse)1159 int StarTrekEngine::showInventoryMenu(int x, int y, bool restoreMouse) {
1160 const int ITEMS_PER_ROW = 5;
1161
1162 Common::Point oldMousePos = _gfx->getMousePos();
1163 bool keyboardControlledMouse = _keyboardControlsMouse;
1164 _keyboardControlsMouse = false;
1165
1166 int itemIndex = 0;
1167 int numItems = 0;
1168
1169 char itemNames[NUM_OBJECTS][10];
1170 Common::Point itemPositions[NUM_OBJECTS];
1171 int16 itemIndices[NUM_OBJECTS];
1172
1173 while (itemIndex < NUM_OBJECTS) {
1174 if (_itemList[itemIndex].have) {
1175 strcpy(itemNames[numItems], _itemList[itemIndex].name);
1176
1177 int16 itemX = (numItems % ITEMS_PER_ROW) * 32 + x;
1178 int16 itemY = (numItems / ITEMS_PER_ROW) * 32 + y;
1179 itemPositions[numItems] = Common::Point(itemX, itemY);
1180 itemIndices[numItems] = _itemList[itemIndex].field2;
1181
1182 numItems++;
1183 }
1184 itemIndex++;
1185 }
1186
1187 Sprite itemSprites[NUM_OBJECTS];
1188
1189 for (int i = 0; i < numItems; i++) {
1190 _gfx->addSprite(&itemSprites[i]);
1191
1192 itemSprites[i].drawMode = 2;
1193 itemSprites[i].pos.x = itemPositions[i].x;
1194 itemSprites[i].pos.y = itemPositions[i].y;
1195 itemSprites[i].drawPriority = 15;
1196 itemSprites[i].drawPriority2 = 8;
1197 itemSprites[i].setBitmap(_resource->loadBitmapFile(itemNames[i]));
1198 }
1199
1200 chooseMousePositionFromSprites(itemSprites, numItems, -1, 4);
1201 bool displayMenu = true;
1202 int lastItemIndex = -1;
1203
1204 while (displayMenu) {
1205 _sound->checkLoopMusic();
1206
1207 TrekEvent event;
1208 if (!getNextEvent(&event))
1209 continue;
1210
1211 switch (event.type) {
1212 case TREKEVENT_TICK: {
1213 Common::Point mousePos = _gfx->getMousePos();
1214 itemIndex = getMenuButtonAt(itemSprites, numItems, mousePos.x, mousePos.y);
1215 if (itemIndex != lastItemIndex) {
1216 if (lastItemIndex != -1) {
1217 drawMenuButtonOutline(itemSprites[lastItemIndex].bitmap, 0);
1218 itemSprites[lastItemIndex].bitmapChanged = true;
1219 }
1220 if (itemIndex != -1) {
1221 drawMenuButtonOutline(itemSprites[itemIndex].bitmap, 15);
1222 itemSprites[itemIndex].bitmapChanged = true;
1223 }
1224 lastItemIndex = itemIndex;
1225 }
1226 _gfx->drawAllSprites();
1227 break;
1228 }
1229
1230 case TREKEVENT_LBUTTONDOWN:
1231 displayMenu = false;
1232 break;
1233
1234 case TREKEVENT_RBUTTONDOWN:
1235 displayMenu = false;
1236 lastItemIndex = -1;
1237 break;
1238
1239 case TREKEVENT_KEYDOWN:
1240 switch (event.kbd.keycode) {
1241 case Common::KEYCODE_ESCAPE:
1242 case Common::KEYCODE_F2:
1243 displayMenu = false;
1244 lastItemIndex = -1;
1245 break;
1246
1247 case Common::KEYCODE_RETURN:
1248 case Common::KEYCODE_KP_ENTER:
1249 case Common::KEYCODE_F1:
1250 displayMenu = false;
1251 break;
1252
1253 case Common::KEYCODE_HOME:
1254 case Common::KEYCODE_KP7:
1255 chooseMousePositionFromSprites(itemSprites, numItems, lastItemIndex, 4);
1256 break;
1257
1258 case Common::KEYCODE_UP:
1259 case Common::KEYCODE_KP8:
1260 case Common::KEYCODE_PAGEUP:
1261 case Common::KEYCODE_KP9:
1262 chooseMousePositionFromSprites(itemSprites, numItems, lastItemIndex, 2);
1263 break;
1264
1265 case Common::KEYCODE_LEFT:
1266 case Common::KEYCODE_KP4:
1267 chooseMousePositionFromSprites(itemSprites, numItems, lastItemIndex, 1);
1268 break;
1269
1270 case Common::KEYCODE_RIGHT:
1271 case Common::KEYCODE_KP6:
1272 chooseMousePositionFromSprites(itemSprites, numItems, lastItemIndex, 0);
1273 break;
1274
1275 case Common::KEYCODE_END:
1276 case Common::KEYCODE_KP1:
1277 chooseMousePositionFromSprites(itemSprites, numItems, lastItemIndex, 5);
1278 break;
1279
1280 case Common::KEYCODE_DOWN:
1281 case Common::KEYCODE_KP2:
1282 case Common::KEYCODE_PAGEDOWN:
1283 case Common::KEYCODE_KP3:
1284 chooseMousePositionFromSprites(itemSprites, numItems, lastItemIndex, 3);
1285 break;
1286
1287 default:
1288 break;
1289 }
1290 break;
1291
1292 default:
1293 break;
1294 }
1295
1296 removeNextEvent();
1297 }
1298
1299 _sound->playSoundEffectIndex(0x10);
1300 if (lastItemIndex >= 0)
1301 drawMenuButtonOutline(itemSprites[lastItemIndex].bitmap, 0);
1302
1303 for (int i = 0; i < numItems; i++)
1304 itemSprites[i].dontDrawNextFrame();
1305
1306 _gfx->drawAllSprites();
1307
1308 for (int i = 0; i < numItems; i++) {
1309 delete itemSprites[i].bitmap;
1310 itemSprites[i].bitmap = nullptr;
1311 _gfx->delSprite(&itemSprites[i]);
1312 }
1313
1314 if (lastItemIndex >= 0) {
1315 lastItemIndex = itemIndices[lastItemIndex];
1316 }
1317
1318 if (restoreMouse)
1319 _gfx->warpMouse(oldMousePos.x, oldMousePos.y);
1320
1321 _keyboardControlsMouse = keyboardControlledMouse;
1322 return lastItemIndex;
1323 }
1324
initStarfieldSprite(Sprite * sprite,Bitmap * bitmap,const Common::Rect & rect)1325 void StarTrekEngine::initStarfieldSprite(Sprite *sprite, Bitmap *bitmap, const Common::Rect &rect) {
1326 sprite->setXYAndPriority(rect.left, rect.top, 0);
1327 sprite->setBitmap(bitmap);
1328 bitmap->xoffset = 0;
1329 bitmap->yoffset = 0;
1330 bitmap->width = rect.width();
1331 bitmap->height = rect.height();
1332 _gfx->addSprite(sprite);
1333 sprite->drawMode = 1;
1334 }
1335
scaleBitmap(Bitmap * bitmap,Fixed8 scale)1336 Bitmap *StarTrekEngine::scaleBitmap(Bitmap *bitmap, Fixed8 scale) {
1337 int scaledWidth = scale.multToInt(bitmap->width);
1338 int scaledHeight = scale.multToInt(bitmap->height);
1339 int origWidth = bitmap->width;
1340 int origHeight = bitmap->height;
1341
1342 if (scaledWidth < 1)
1343 scaledWidth = 1;
1344 if (scaledHeight < 1)
1345 scaledHeight = 1;
1346
1347 Bitmap *scaledBitmap = new Bitmap(scaledWidth, scaledHeight);
1348 scaledBitmap->xoffset = scale.multToInt(bitmap->xoffset);
1349 scaledBitmap->yoffset = scale.multToInt(bitmap->yoffset);
1350
1351 // sub_344a5(scaledWidth, origWidth);
1352
1353 origHeight--;
1354 scaledHeight--;
1355
1356 byte *src = bitmap->pixels;
1357 byte *dest = scaledBitmap->pixels;
1358
1359 if (scale <= 1.0) {
1360 int16 var2e = 0;
1361 uint16 var30 = scaledHeight << 1;
1362 uint16 var32 = (scaledHeight - origHeight) << 1;
1363 uint16 origRow = 0;
1364
1365 while (origRow <= origHeight) {
1366 if (var2e < 0) {
1367 var2e += var30;
1368 } else {
1369 var2e += var32;
1370 scaleBitmapRow(src, dest, origWidth, scaledWidth);
1371 dest += scaledWidth;
1372 }
1373
1374 src += bitmap->width;
1375 origRow++;
1376 }
1377 } else {
1378 int16 var2e = (origHeight << 1) - scaledHeight;
1379 uint16 var30 = origHeight << 1;
1380 uint16 var32 = (origHeight - scaledHeight) << 1;
1381 uint16 srcRowChanged = true;
1382 origWidth = bitmap->width;
1383 uint16 scaledRow = 0;
1384 byte *rowData = new byte[scaledWidth];
1385
1386 while (scaledRow++ <= scaledHeight) {
1387 if (srcRowChanged) {
1388 scaleBitmapRow(src, rowData, origWidth, scaledWidth);
1389 srcRowChanged = false;
1390 }
1391
1392 memcpy(dest, rowData, scaledWidth);
1393 dest += scaledWidth;
1394
1395 if (var2e < 0) {
1396 var2e += var30;
1397 } else {
1398 var2e += var32;
1399 src += origWidth;
1400 srcRowChanged = true;
1401 }
1402 }
1403
1404 delete[] rowData;
1405 }
1406
1407 delete bitmap;
1408
1409 return scaledBitmap;
1410 }
1411
scaleBitmapRow(byte * src,byte * dest,uint16 origWidth,uint16 scaledWidth)1412 void StarTrekEngine::scaleBitmapRow(byte *src, byte *dest, uint16 origWidth, uint16 scaledWidth) {
1413 if (origWidth >= scaledWidth) {
1414 int16 var2 = (scaledWidth << 1) - origWidth;
1415 uint16 var4 = scaledWidth << 1;
1416 uint16 var6 = (scaledWidth - origWidth) << 1;
1417 uint16 varE = 0;
1418 uint16 varA = 0;
1419 uint16 var8 = origWidth;
1420 uint16 di = 0;
1421
1422 while (var8-- != 0) {
1423 if (var2 < 0) {
1424 var2 += var4;
1425 } else {
1426 var2 += var6;
1427 if (di != 0) {
1428 if (varE != 0) {
1429 *(dest - 1) = *src++;
1430 varE = 0;
1431 di--;
1432 }
1433 src += di;
1434 di = 0;
1435 }
1436 *dest++ = *src;
1437 varE = 1;
1438 }
1439
1440 di++;
1441 varA++;
1442 }
1443 } else {
1444 int16 var2 = ((origWidth - 1) << 1) - (scaledWidth - 1);
1445 uint16 var4 = (origWidth - 1) << 1;
1446 uint16 var6 = ((origWidth - 1) - (scaledWidth - 1)) << 1;
1447 uint16 varA = 0;
1448 uint16 var8 = scaledWidth;
1449 uint16 di = 0;
1450
1451 while (var8-- != 0) {
1452 if (di != 0) {
1453 src += di;
1454 di = 0;
1455 }
1456 *dest++ = *src;
1457
1458 if (var2 < 0)
1459 var2 += var4;
1460 else {
1461 var2 += var6;
1462 di++;
1463 }
1464
1465 varA++;
1466 }
1467 }
1468 }
1469
1470 } // End of namespace StarTrek
1471