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 "asylum/resources/actor.h"
24
25 #include "asylum/resources/encounters.h"
26 #include "asylum/resources/object.h"
27 #include "asylum/resources/polygons.h"
28 #include "asylum/resources/special.h"
29 #include "asylum/resources/script.h"
30 #include "asylum/resources/worldstats.h"
31
32 #include "asylum/system/cursor.h"
33 #include "asylum/system/graphics.h"
34 #include "asylum/system/screen.h"
35 #include "asylum/system/speech.h"
36 #include "asylum/system/text.h"
37
38 #include "asylum/views/scene.h"
39
40 #include "asylum/asylum.h"
41 #include "asylum/staticres.h"
42
43
44 namespace Asylum {
45
46 #define DIR(val) (ActorDirection)((val) & 7)
47
Actor(AsylumEngine * engine,ActorIndex index)48 Actor::Actor(AsylumEngine *engine, ActorIndex index) : _vm(engine), _index(index), inventory(engine, _numberValue01) {
49 // Init all variables
50 _resourceId = kResourceNone;
51 _objectIndex = 0;
52 _frameIndex = 0;
53 _frameCount = 0;
54 _direction = kDirectionN;
55 _field_3C = 0;
56 _status = kActorStatusNone;
57 _field_44 = 0;
58 _priority = 0;
59 flags = 0;
60 _field_50 = 0;
61 _field_54 = 0;
62 _field_58 = 0;
63 _field_5C = 0;
64 _field_60 = 0;
65 _actionIdx3 = 0;
66 // TODO field_68 till field_617
67 _walkingSound1 = 0;
68 _walkingSound2 = 0;
69 _walkingSound3 = 0;
70 _walkingSound4 = 0;
71 _field_64C = 0;
72 _field_650 = 0;
73 memset(_graphicResourceIds, 0 , sizeof(_graphicResourceIds));
74 memset(&_name, 0, sizeof(_name));
75 memset(&_distancesEO, 0, sizeof(_distancesEO));
76 memset(&_distancesNS, 0, sizeof(_distancesNS));
77 memset(&_distancesNSEO, 0, sizeof(_distancesNSEO));
78 _actionIdx2 = 0;
79 _field_924 = 0;
80 _lastScreenUpdate = 0;
81 _scriptIndex = 0;
82 actionType = 0;
83 _field_934 = 0;
84 _field_938 = 0;
85 _soundResourceId = kResourceNone;
86 _numberValue01 = 0;
87 _field_944 = 0;
88 _field_948 = 0;
89 _field_94C = 0;
90 _numberFlag01 = 0;
91 _numberStringWidth = 0;
92 memset(&_numberString01, 0, 8);
93 _field_968 = 0;
94 _transparency = 0;
95 _processNewDirection = false;
96 _invertPriority = false;
97 _nextDirection = kDirectionN;
98 _nextActionIndex = 0;
99 _nextActorIndex = kActorMax;
100 _field_994 = 0;
101 _field_998 = 0;
102 _field_99C = 0;
103 _field_9A0 = 0;
104
105 // Instance data
106 _tickCount = -1;
107 _updateCounter = 0;
108
109 // Path finding
110 _frameNumber = 0;
111 }
112
113 /////////////////////////////////////////////////////////////////////////
114 // Loading
115 //////////////////////////////////////////////////////////////////////////
load(Common::SeekableReadStream * stream)116 void Actor::load(Common::SeekableReadStream *stream) {
117 if (!stream)
118 error("[Actor::load] invalid stream");
119
120 _point.x = (int16)stream->readSint32LE();
121 _point.y = (int16)stream->readSint32LE();
122 _resourceId = (ResourceId)stream->readSint32LE();
123 _objectIndex = stream->readSint32LE();
124 _frameIndex = stream->readUint32LE();
125 _frameCount = stream->readUint32LE();
126 _point1.x = (int16)stream->readSint32LE();
127 _point1.y = (int16)stream->readSint32LE();
128 _point2.x = (int16)stream->readSint32LE();
129 _point2.y = (int16)stream->readSint32LE();
130
131 _boundingRect.left = (int16)(stream->readSint32LE() & 0xFFFF);
132 _boundingRect.top = (int16)(stream->readSint32LE() & 0xFFFF);
133 _boundingRect.right = (int16)(stream->readSint32LE() & 0xFFFF);
134 _boundingRect.bottom = (int16)(stream->readSint32LE() & 0xFFFF);
135
136 _direction = (ActorDirection)stream->readSint32LE();
137 _field_3C = stream->readSint32LE();
138 _status = (ActorStatus)stream->readSint32LE();
139 _field_44 = stream->readSint32LE();
140 _priority = stream->readSint32LE();
141 flags = stream->readSint32LE();
142 _field_50 = stream->readSint32LE();
143 _field_54 = stream->readSint32LE();
144 _field_58 = stream->readSint32LE();
145 _field_5C = stream->readSint32LE();
146 _field_60 = stream->readSint32LE();
147 _actionIdx3 = stream->readSint32LE();
148
149 // TODO skip field_68 till field_617
150 stream->skip(0x5B0);
151
152 inventory.load(stream);
153 _walkingSound1 = stream->readSint32LE();
154 _walkingSound2 = stream->readSint32LE();
155 _walkingSound3 = stream->readSint32LE();
156 _walkingSound4 = stream->readSint32LE();
157 _field_64C = stream->readUint32LE();
158 _field_650 = stream->readUint32LE();
159
160 for (int32 i = 0; i < 55; i++)
161 _graphicResourceIds[i] = (ResourceId)stream->readSint32LE();
162
163 stream->read(_name, sizeof(_name));
164
165 for (int32 i = 0; i < 20; i++)
166 _distancesEO[i] = stream->readSint32LE();
167
168 for (int32 i = 0; i < 20; i++)
169 _distancesNS[i] = stream->readSint32LE();
170
171 for (int32 i = 0; i < 20; i++)
172 _distancesNSEO[i] = stream->readSint32LE();
173
174 _actionIdx2 = stream->readSint32LE();
175 _field_924 = stream->readSint32LE();
176 _lastScreenUpdate = stream->readUint32LE();
177 _scriptIndex = stream->readSint32LE();
178 actionType = stream->readSint32LE();
179 _field_934 = stream->readSint32LE();
180 _field_938 = stream->readSint32LE();
181 _soundResourceId = (ResourceId)stream->readSint32LE();
182 _numberValue01 = stream->readSint32LE();
183 _field_944 = stream->readSint32LE();
184 _field_948 = stream->readSint32LE();
185 _field_94C = stream->readSint32LE();
186 _numberFlag01 = stream->readSint32LE();
187 _numberStringWidth = (int16)stream->readSint32LE();
188 _numberPoint.x = (int16)stream->readSint32LE();
189 _numberPoint.y = (int16)stream->readSint32LE();
190 stream->read(_numberString01, sizeof(_numberString01));
191 _field_968 = stream->readSint32LE();
192 _transparency = stream->readSint32LE();
193 _processNewDirection = (bool)stream->readSint32LE();
194 _invertPriority = (bool)stream->readSint32LE();
195 _nextDirection = (ActorDirection)stream->readSint32LE();
196 _nextActionIndex = stream->readSint32LE();
197 _nextActorIndex = stream->readSint32LE();
198 _nextPositionOffset.x = (int16)stream->readSint32LE();
199 _nextPositionOffset.y = (int16)stream->readSint32LE();
200 _nextPosition.x = (int16)stream->readSint32LE();
201 _nextPosition.y = (int16)stream->readSint32LE();
202 _field_994 = stream->readSint32LE();
203 _field_998 = stream->readSint32LE();
204 _field_99C = stream->readSint32LE();
205 _field_9A0 = stream->readSint32LE();
206 }
207
saveLoadWithSerializer(Common::Serializer & s)208 void Actor::saveLoadWithSerializer(Common::Serializer &s) {
209 s.syncAsSint32LE(_point.x);
210 s.syncAsSint32LE(_point.y);
211 s.syncAsSint32LE(_resourceId);
212 s.syncAsSint32LE(_objectIndex);
213 s.syncAsUint32LE(_frameIndex);
214 s.syncAsUint32LE(_frameCount);
215 s.syncAsSint32LE(_point1.x);
216 s.syncAsSint32LE(_point1.y);
217 s.syncAsSint32LE(_point2.x);
218 s.syncAsSint32LE(_point2.y);
219
220 s.syncAsSint32LE(_boundingRect.left);
221 s.syncAsSint32LE(_boundingRect.top);
222 s.syncAsSint32LE(_boundingRect.right);
223 s.syncAsSint32LE(_boundingRect.bottom);
224
225 s.syncAsSint32LE(_direction);
226 s.syncAsSint32LE(_field_3C);
227 s.syncAsSint32LE(_status);
228 s.syncAsSint32LE(_field_44);
229 s.syncAsSint32LE(_priority);
230 s.syncAsSint32LE(flags);
231 s.syncAsSint32LE(_field_50);
232 s.syncAsSint32LE(_field_54);
233 s.syncAsSint32LE(_field_58);
234 s.syncAsSint32LE(_field_5C);
235 s.syncAsSint32LE(_field_60);
236 s.syncAsSint32LE(_actionIdx3);
237
238 // TODO skip field_68 till field_617
239 s.skip(0x5B0);
240
241 inventory.saveLoadWithSerializer(s);
242
243 s.syncAsSint32LE(_walkingSound1);
244 s.syncAsSint32LE(_walkingSound2);
245 s.syncAsSint32LE(_walkingSound3);
246 s.syncAsSint32LE(_walkingSound4);
247 s.syncAsUint32LE(_field_64C);
248 s.syncAsUint32LE(_field_650);
249
250 for (int32 i = 0; i < ARRAYSIZE(_graphicResourceIds); i++)
251 s.syncAsSint32LE(_graphicResourceIds[i]);
252
253 s.syncBytes((byte *)&_name, sizeof(_name));
254
255 for (int32 i = 0; i < 20; i++)
256 s.syncAsSint32LE(_distancesEO[i]);
257
258 for (int32 i = 0; i < 20; i++)
259 s.syncAsSint32LE(_distancesNS[i]);
260
261 for (int32 i = 0; i < 20; i++)
262 s.syncAsSint32LE(_distancesNSEO[i]);
263
264 s.syncAsSint32LE(_actionIdx2);
265 s.syncAsSint32LE(_field_924);
266 s.syncAsSint32LE(_lastScreenUpdate);
267 s.syncAsSint32LE(_scriptIndex);
268 s.syncAsSint32LE(actionType);
269 s.syncAsSint32LE(_field_934);
270 s.syncAsSint32LE(_field_938);
271 s.syncAsSint32LE(_soundResourceId);
272 s.syncAsSint32LE(_numberValue01);
273 s.syncAsSint32LE(_field_944);
274 s.syncAsSint32LE(_field_948);
275 s.syncAsSint32LE(_field_94C);
276 s.syncAsSint32LE(_numberFlag01);
277 s.syncAsSint32LE(_numberStringWidth);
278 s.syncAsSint32LE(_numberPoint.x);
279 s.syncAsSint32LE(_numberPoint.y);
280 s.syncBytes((byte *)&_numberString01, sizeof(_numberString01));
281 s.syncAsSint32LE(_field_968);
282 s.syncAsSint32LE(_transparency);
283 s.syncAsSint32LE(_processNewDirection);
284 s.syncAsSint32LE(_invertPriority);
285 s.syncAsSint32LE(_nextDirection);
286 s.syncAsSint32LE(_nextActionIndex);
287 s.syncAsSint32LE(_nextActorIndex);
288 s.syncAsSint32LE(_nextPositionOffset.x);
289 s.syncAsSint32LE(_nextPositionOffset.y);
290 s.syncAsSint32LE(_nextPosition.x);
291 s.syncAsSint32LE(_nextPosition.y);
292 s.syncAsSint32LE(_field_994);
293 s.syncAsSint32LE(_field_998);
294 s.syncAsSint32LE(_field_99C);
295 s.syncAsSint32LE(_field_9A0);
296 }
297
298 /////////////////////////////////////////////////////////////////////////
299 // Update & status
300 //////////////////////////////////////////////////////////////////////////
draw()301 void Actor::draw() {
302 if (!isVisible())
303 return;
304
305 // Draw the actor
306 Common::Point point;
307 adjustCoordinates(&point);
308 point.x += _point.x;
309 point.y += _point.y;
310
311 // Compute frame index
312 uint32 frameIndex = _frameIndex;
313 if (_frameIndex >= _frameCount)
314 frameIndex = 2 * _frameCount - (_frameIndex + 1);
315
316 if (flags & kActorFlagMasked) {
317 Object *object = getWorld()->objects[_objectIndex];
318 Common::Point objPoint;
319 object->adjustCoordinates(&objPoint);
320
321 getScreen()->addGraphicToQueueMasked(_resourceId, frameIndex, point, object->getResourceId(), objPoint, getGraphicsFlags(), _priority);
322
323 // Update flags
324 flags &= ~kActorFlagMasked;
325 } else {
326 getScreen()->addGraphicToQueue(_resourceId, frameIndex, point, getGraphicsFlags(), _transparency, _priority);
327 }
328 }
329
drawNumber()330 void Actor::drawNumber() {
331 if (!_numberFlag01)
332 return;
333
334 getText()->loadFont(getWorld()->font1);
335 getText()->drawCentered(_numberPoint, _numberStringWidth, (char *)&_numberString01);
336 }
337
update()338 void Actor::update() {
339 if (!isVisible())
340 return;
341
342 switch (_status) {
343 default:
344 break;
345
346 case kActorStatusGettingHurt:
347 if (getWorld()->chapter == kChapter2) {
348 MaxGetsSome();
349 } else if (getWorld()->chapter == kChapter11 && _index == getSharedData()->getPlayerIndex()) {
350 SarahGetsSome();
351 }
352 break;
353
354 case kActorStatusRestarting:
355 if (getWorld()->chapter == kChapter2) {
356 if (_index > 12) {
357 if (_frameIndex <= _frameCount - 1) {
358 ++_frameIndex;
359 } else {
360 hide();
361 getScene()->getActor(_index + 9)->hide();
362 }
363 }
364
365 if (_index == 11) {
366 if (_frameIndex <= _frameCount - 1) {
367 // Looks like a simple check using the counter, since it doesn't seem to be used anywhere else
368 if (_updateCounter == 0) {
369 ++_updateCounter;
370 } else {
371 _updateCounter = 0;
372 ++_frameIndex;
373 }
374 } else {
375 if (_vm->isGameFlagSet(kGameFlag556)) {
376 Actor *player = getScene()->getActor();
377
378 getSpeech()->playPlayer(453);
379 getScene()->getActor(11)->hide();
380
381 player->changeStatus(kActorStatusInteracting);
382 player->setResourceId(player->getResourcesId(35));
383 player->setDirection(kDirectionS);
384 player->setFrameCount(GraphicResource::getFrameCount(_vm, player->getResourceId()));
385
386 getCursor()->hide();
387 getScene()->getActor(0)->changeDirection(kDirectionS);
388
389 // Queue script
390 getScript()->queueScript(getWorld()->getActionAreaById(2696)->scriptIndex, getSharedData()->getPlayerIndex());
391
392 _vm->setGameFlag(kGameFlag279);
393 _vm->clearGameFlag(kGameFlag368);
394
395 player->setFrameIndex(0);
396 getScene()->getActor(0)->setLastScreenUpdate(_vm->getTick());
397
398 getSound()->playMusic(MAKE_RESOURCE(kResourcePackMusic, 1));
399
400 getWorld()->musicCurrentResourceIndex = 1;
401
402 if (getSound()->isPlaying(getWorld()->soundResourceIds[7]))
403 getSound()->stop(getWorld()->soundResourceIds[7]);
404
405 if (getSound()->isPlaying(getWorld()->soundResourceIds[6]))
406 getSound()->stop(getWorld()->soundResourceIds[6]);
407
408 if (getSound()->isPlaying(getWorld()->soundResourceIds[5]))
409 getSound()->stop(getWorld()->soundResourceIds[5]);
410
411 _vm->setGameFlag(kGameFlag1131);
412 } else {
413 getScene()->getActor(11)->updateGraphicData(25);
414 _vm->setGameFlag(kGameFlag556);
415 }
416 }
417 }
418
419 if (_index == getSharedData()->getPlayerIndex()) {
420 if (_frameIndex <= _frameCount - 1) {
421 ++_frameIndex;
422 } else {
423 _vm->clearGameFlag(kGameFlag239);
424 getScene()->getActor(10)->changeStatus(kActorStatusEnabled2);
425 hide();
426 _vm->setGameFlag(kGameFlag238);
427
428 // Queue script
429 getScript()->queueScript(getWorld()->getActionAreaById(1000)->scriptIndex, getSharedData()->getPlayerIndex());
430 }
431 }
432
433 } else if (getWorld()->chapter == kChapter11) {
434 if (_index == getSharedData()->getPlayerIndex()) {
435 if (_frameIndex <= _frameCount - 1)
436 ++_frameIndex;
437 else
438 SarahDies();
439 }
440
441 if (_index >= 10)
442 TentacleDies();
443 }
444 break;
445
446 case kActorStatusAttacking:
447 if (getWorld()->chapter == kChapter2) {
448 if (_index > 12)
449 CrowDives();
450
451 if (_index == getSharedData()->getPlayerIndex())
452 MaxAttacks();
453
454 if (_index == 11)
455 ScareCrowAttacks();
456
457 } else if (getWorld()->chapter == kChapter11) {
458 if (_index >= 10 && _index < 16)
459 TentacleWhips();
460
461 if (_index == getSharedData()->getPlayerIndex())
462 SarahAttacks();
463 }
464 break;
465
466 case kActorStatus18:
467 if (getWorld()->chapter == kChapter2) {
468 if (_index > 12)
469 CrowSwoops();
470
471 if (_index == 11)
472 ScareCrowRetreats();
473 }
474 break;
475
476 case kActorStatusDisabled:
477 _frameIndex = (_frameIndex + 1) % _frameCount;
478
479 if (_vm->screenUpdateCount - _lastScreenUpdate > 300) {
480 if (_vm->getRandom(100) < 50) {
481 if (!getSpeech()->getSoundResourceId() || !getSound()->isPlaying(getSpeech()->getSoundResourceId())) {
482 if (canChangeStatus(10))
483 changeStatus(kActorStatusFidget);
484 }
485 }
486 _lastScreenUpdate = _vm->screenUpdateCount;
487 }
488 break;
489
490 case kActorStatusWalking2:
491 if (getWorld()->chapter == kChapter2) {
492 if (_index > 12) {
493 CrowClosesIn();
494 return;
495 }
496
497 if (_index == 11) {
498 ScareCrowClosesIn();
499 return;
500 }
501 } else if (getWorld()->chapter == kChapter11) {
502 switch (_index) {
503 default:
504 break;
505
506 case 1:
507 getSpecial()->run(NULL, _index);
508 return;
509
510 case 10:
511 case 11:
512 case 12:
513 case 13:
514 case 14:
515 case 15:
516 TentacleRises();
517 return;
518
519 case 2:
520 case 3:
521 case 4:
522 case 5:
523 case 6:
524 case 7:
525 case 8:
526 return;
527 }
528 }
529 // fallthrough
530
531 case kActorStatusWalking: {
532 uint32 index = (_frameIndex >= _frameCount) ? 2 * _frameCount - (_frameIndex + 1) : _frameIndex;
533
534 uint32 dist = (uint32)abs((double)getWalkIncrement(_direction, index));
535 Common::Point point = _point1 + _point2;
536
537 if (canMove(&point, _direction, dist, false)) {
538 move(_direction, dist);
539 } else if (canMove(&point, DIR(_direction + 1), dist, false)) {
540 move(DIR(_direction + 1), dist);
541 } else if (canMove(&point, DIR(_direction + 7), dist, false)) {
542 move(DIR(_direction + 7), dist);
543 } else if (canMove(&point, DIR(_direction + 2), dist, false)) {
544 move(DIR(_direction + 2), dist);
545 } else if (canMove(&point, DIR(_direction + 6), dist, false)) {
546 move(DIR(_direction + 6), dist);
547 }
548
549 // Finish
550 if (_soundResourceId != kResourceNone && getSound()->isPlaying(_soundResourceId))
551 setVolume();
552
553 if (_index != getSharedData()->getPlayerIndex() && getWorld()->chapter != kChapter9)
554 getSpecial()->run(NULL, _index);
555
556 updateReflectionData();
557
558 if (_field_944 != 5)
559 actionAreaCheck();
560
561 }
562 break;
563
564 case kActorStatusWalkingTo:
565 case kActorStatusWalkingTo2: {
566 uint32 index = (_frameIndex >= _frameCount) ? 2 * _frameCount - (_frameIndex + 1) : _frameIndex;
567
568 int32 dist = (int32)abs((double)getWalkIncrement(_direction, index));
569
570 Common::Point point = _point1 + _point2;
571 Common::Point current = _data.points[_data.current];
572
573 if (point.x < (int16)(current.x - (dist + 1))
574 || point.x > (int16)(current.x + (dist + 1))
575 || point.y < (int16)(current.y - (dist + 1))
576 || point.y > (int16)(current.y + (dist + 1))) {
577 if (canMove(&point, _direction, (uint32)dist, false)) {
578 move(_direction, (uint32)dist);
579 } else {
580 stopWalking();
581 }
582 } else {
583 int32 area = getScene()->findActionArea(kActionAreaType1, current);
584 if (_field_944 == 1 || _field_944 == 4)
585 area = 1;
586
587 if (area == -1 && _data.current >= (int32)(_data.count - 1)) {
588 stopWalking();
589 } else {
590 _frameIndex = (_frameIndex + 1) % _frameCount;
591
592 if (testActorCollision(¤t, _direction)) {
593 _point1.x = current.x - _point2.x;
594 _point1.y = current.y - _point2.y;
595
596 if (_data.current < (int32)(_data.count - 1)) {
597 _data.current++;
598
599 changeDirection(_data.directions[_data.current]);
600 } else {
601 stopWalking();
602 }
603 } else {
604 stopWalking();
605 }
606 }
607 }
608
609 // Finish
610 if (_soundResourceId != kResourceNone && getSound()->isPlaying(_soundResourceId))
611 setVolume();
612
613 if (_index != getSharedData()->getPlayerIndex() && getWorld()->chapter != kChapter9)
614 getSpecial()->run(NULL, _index);
615
616 updateReflectionData();
617
618 if (_field_944 != 5)
619 actionAreaCheck();
620
621 }
622 break;
623
624 case kActorStatusInteracting:
625 case kActorStatusHittingPumpkin:
626 updateStatusInteracting();
627 break;
628
629 case kActorStatusStoppedInteracting:
630 if (getSharedData()->actorEnableForStatus7) {
631 getSharedData()->actorEnableForStatus7 = false;
632 enable();
633 }
634 break;
635
636 case kActorStatusEnabled:
637 if (_field_944 != 5)
638 updateStatusEnabled();
639 break;
640
641 case kActorStatusEnabled2:
642 updateStatusEnabled2();
643 break;
644
645 case kActorStatusMorphingInto:
646 updateStatusMorphing();
647 break;
648
649 case kActorStatusFidget:
650 updateStatusBored();
651 break;
652
653 case kActorStatusShowingInventory:
654 case kActorStatus10:
655 _frameIndex = (_frameIndex + 1) % _frameCount;
656 break;
657
658 case kActorStatus8:
659 if (getSharedData()->getFlag(kFlagIsEncounterRunning)
660 || !_soundResourceId
661 || getSound()->isPlaying(_soundResourceId)) {
662 _frameIndex = (_frameIndex + 1) % _frameCount;
663 } else {
664 enable();
665 _soundResourceId = kResourceNone;
666 }
667 break;
668 }
669
670 if (_soundResourceId && getSound()->isPlaying(_soundResourceId))
671 setVolume();
672
673 if (_index != getSharedData()->getPlayerIndex() && getWorld()->chapter != kChapter9)
674 getSpecial()->run(NULL, _index);
675
676 updateReflectionData();
677
678 if (_field_944 != 5)
679 actionAreaCheck();
680 }
681
changeStatus(ActorStatus actorStatus)682 void Actor::changeStatus(ActorStatus actorStatus) {
683 debugC(kDebugLevelActor, "[changeStatus] %d point1(%d:%d) point2(%d:%d)", actorStatus, _point1.x, _point1.y, _point2.x, _point2.y);
684
685 switch (actorStatus) {
686 default:
687 break;
688
689 case kActorStatusWalking:
690 case kActorStatusWalking2:
691 if ((getWorld()->chapter == kChapter2
692 && _index == getSharedData()->getPlayerIndex() && (_status == kActorStatus18 || _status == kActorStatusGettingHurt || _status == kActorStatusRestarting))
693 || (_status != kActorStatusEnabled && _status != kActorStatusFidget && _status != kActorStatusEnabled2 && _status != kActorStatusAttacking && _status != kActorStatus18))
694 return;
695
696 updateGraphicData(0);
697
698 // Force status in some cases
699 if (_status == kActorStatusEnabled2 || _status == kActorStatusAttacking || _status == kActorStatus18) {
700 _status = kActorStatusWalking2;
701 return;
702 }
703 break;
704
705 case kActorStatusWalkingTo:
706 case kActorStatusWalkingTo2:
707 updateGraphicData(0);
708 break;
709
710 case kActorStatusInteracting:
711 case kActorStatusHittingPumpkin:
712 if (!strcmp(_name, "Big Crow"))
713 _status = kActorStatusEnabled;
714 break;
715
716 case kActorStatusEnabled:
717 case kActorStatusShowingInventory:
718 case kActorStatusEnabled2:
719 updateGraphicData(5);
720 break;
721
722 case kActorStatusDisabled:
723 updateGraphicData(5);
724 _resourceId = _graphicResourceIds[(_direction > kDirectionS ? kDirection8 - _direction : _direction) + 5];
725
726 getScreen()->setFlag(-1);
727 break;
728
729 case kActorStatusStoppedInteracting:
730 if (getWorld()->chapter == kChapter2 && _index == 10 && _vm->isGameFlagSet(kGameFlag279)) {
731 Actor *actor = getScene()->getActor(0);
732 actor->getPoint1()->x = _point2.x + _point1.x - actor->getPoint2()->x;
733 actor->getPoint1()->y = _point2.y + _point1.y - actor->getPoint2()->y;
734 actor->setDirection(kDirectionS);
735
736 getSharedData()->setPlayerIndex(0);
737
738 // Hide this actor and the show the other one
739 hide();
740 actor->show();
741
742 _vm->clearGameFlag(kGameFlag279);
743
744 getCursor()->show();
745 }
746 break;
747
748 case kActorStatus8:
749 case kActorStatus10:
750 case kActorStatusRestarting:
751 updateGraphicData(20);
752 break;
753
754 case kActorStatusFidget:
755 if (getSharedData()->getFlag(kFlagIsEncounterRunning))
756 return;
757
758 if (_vm->getRandomBit() == 1 && canChangeStatus(15))
759 updateGraphicData(15);
760 else
761 updateGraphicData(10);
762 break;
763
764 case kActorStatusAttacking:
765 case kActorStatusGettingHurt:
766 updateGraphicData(actorStatus == kActorStatusAttacking ? 10 : 15);
767 break;
768
769 case kActorStatus18:
770 if (getWorld()->chapter == kChapter2) {
771 GraphicResource *resource = new GraphicResource(_vm);
772 _frameIndex = 0;
773
774 if (_index > 12)
775 _resourceId = _graphicResourceIds[_direction + 30];
776
777 if (getSharedData()->getPlayerIndex() == _index) {
778 resource->load(_resourceId);
779 _frameIndex = resource->count() - 1;
780 }
781
782 if (_index == 11)
783 _resourceId = _graphicResourceIds[getSharedData()->globalDirection > 4 ? 8 - getSharedData()->globalDirection : getSharedData()->globalDirection];
784
785 // Reload the graphic resource if the resource ID has changed
786 if (resource->getResourceId() != _resourceId)
787 resource->load(_resourceId);
788
789 _frameCount = resource->count();
790
791 delete resource;
792 }
793 break;
794 }
795
796 _status = actorStatus;
797 }
798
799 /////////////////////////////////////////////////////////////////////////
800 // Direction & position
801 //////////////////////////////////////////////////////////////////////////
802
updateReflectionData()803 void Actor::updateReflectionData() {
804 if (!_processNewDirection)
805 return;
806
807 Common::Point sum = _point1 + _point2;
808 if (_nextActionIndex != -1 && !getScene()->polygons()->get(getWorld()->actions[_nextActionIndex]->polygonIndex).contains(sum))
809 return;
810
811 ActorDirection direction = kDirectionN;
812 Common::Point position = sum;
813 ResourceId resourceId;
814 switch (_nextDirection) {
815 default:
816 // position is unchanged
817 break;
818
819 case kDirectionN:
820 case kDirectionS:
821 position.x = _nextPosition.x + sum.x;
822 position.y = _nextPosition.y + sum.y;
823 position.y += (int16)((_nextDirection == kDirectionN ? -1 : 1) * 2 * abs(sum.y - _nextPositionOffset.y));
824
825 switch (_direction) {
826 default:
827 break;
828
829 case kDirectionN:
830 direction = kDirectionS;
831 break;
832
833 case kDirectionS:
834 direction = kDirectionN;
835 break;
836
837 case kDirectionNE:
838 direction = kDirectionSE;
839 break;
840
841 case kDirectionSE:
842 direction = kDirectionNE;
843 break;
844
845 case kDirectionNW:
846 direction = kDirectionSW;
847 break;
848
849 case kDirectionSW:
850 direction = kDirectionNW;
851 break;
852 }
853 break;
854
855 case kDirectionW:
856 case kDirectionE:
857 position.x = _nextPosition.x + sum.x;
858 position.x += (int16)((_nextDirection == kDirectionW ? -1 : 1) * 2 * abs(sum.x - _nextPositionOffset.x));
859 position.y = _nextPosition.y + sum.y;
860
861 switch (_direction) {
862 default:
863 break;
864
865 case kDirectionW:
866 direction = kDirectionE;
867 break;
868
869 case kDirectionE:
870 direction = kDirectionW;
871 break;
872
873 case kDirectionSW:
874 direction = kDirectionSE;
875 break;
876
877 case kDirectionNW:
878 direction = kDirectionNE;
879 break;
880
881 case kDirectionSE:
882 direction = kDirectionSW;
883 break;
884
885 case kDirectionNE:
886 direction = kDirectionNW;
887 break;
888 }
889 break;
890
891 case kDirectionNW:
892 case kDirectionSE:
893 position.x = _nextPosition.x + _nextPositionOffset.x;
894 position.y = _nextPosition.y + _nextPositionOffset.y;
895 position.x += (int16)((_nextDirection == kDirectionNW ? -1 : 1) * abs(sum.y - _nextPositionOffset.y));
896 position.y += (int16)((_nextDirection == kDirectionNW ? -1 : 1) * abs(sum.x - _nextPositionOffset.x));
897
898 switch (_direction) {
899 default:
900 break;
901
902 case kDirectionN:
903 direction = kDirectionE;
904 break;
905
906 case kDirectionS:
907 direction = kDirectionW;
908 break;
909
910 case kDirectionW:
911 direction = kDirectionS;
912 break;
913
914 case kDirectionE:
915 direction = kDirectionN;
916 break;
917
918 case kDirectionNW:
919 direction = kDirectionSE;
920 break;
921
922 case kDirectionNE:
923 direction = kDirectionNE;
924 break;
925
926 case kDirectionSW:
927 direction = kDirectionSW;
928 break;
929
930 case kDirectionSE:
931 direction = kDirectionNW;
932 break;
933 }
934 break;
935
936 case kDirectionSW:
937 case kDirectionNE:
938 if (_nextDirection == kDirectionSW) {
939 position.x = (int16)(_nextPosition.x + _nextPositionOffset.x - abs(sum.y - _nextPositionOffset.y));
940 position.y = (int16)(_nextPosition.y + _nextPositionOffset.y + abs(sum.x - _nextPositionOffset.x));
941 } else {
942 double deltaX = sum.x * -0.56666666;
943 double deltaY = (419 - (sum.y + deltaX)) * 0.87613291;
944 position.x = (int16)(sum.x + 2 * (_nextPositionOffset.x - deltaY));
945 position.y = (int16)(sum.y + 2 * (_nextPositionOffset.y - (sum.y + deltaX + (deltaY * 0.56666666))));
946 }
947
948 switch (_direction) {
949 default:
950 break;
951
952 case kDirectionS:
953 direction = kDirectionE;
954 break;
955
956 case kDirectionN:
957 direction = kDirectionW;
958 break;
959
960 case kDirectionE:
961 direction = kDirectionS;
962 break;
963
964 case kDirectionW:
965 direction = kDirectionN;
966 break;
967
968 case kDirectionSE:
969 direction = kDirectionSE;
970 break;
971
972 case kDirectionSW:
973 direction = kDirectionNE;
974 break;
975
976 case kDirectionNE:
977 direction = kDirectionSW;
978 break;
979
980 case kDirectionNW:
981 direction = kDirectionNW;
982 break;
983 }
984 break;
985
986 case kDirection8:
987 position = _nextPosition + sum;
988 break;
989 }
990
991 // Get the next resource index offset
992 uint32 index = 0;
993 while (_graphicResourceIds[index] != _resourceId) {
994 index++;
995
996 if (index >= 55)
997 error("[Actor::updateReflectionData] Invalid resource id index");
998 }
999
1000 // Compute resource id and adjust frame count
1001 Actor *nextActor = getScene()->getActor(_nextActorIndex);
1002
1003 resourceId = nextActor->getResourcesId((index - index % 5) + (direction > kDirectionS ? 8 - direction : direction));
1004 nextActor->setFrameCount(GraphicResource::getFrameCount(_vm, resourceId));
1005
1006 // Adjust graphic resource and position
1007 uint32 frameIndex = _frameIndex % nextActor->getFrameCount();
1008 nextActor->setPosition(position.x, position.y, direction, frameIndex);
1009 nextActor->setFrameIndex(frameIndex);
1010 nextActor->setResourceId(resourceId);
1011 }
1012
changeDirection(ActorDirection actorDirection)1013 void Actor::changeDirection(ActorDirection actorDirection) {
1014 _direction = actorDirection;
1015
1016 if (_field_944 == 5)
1017 return;
1018
1019 switch (_status) {
1020 default:
1021 break;
1022
1023 case kActorStatusDisabled:
1024 case kActorStatusEnabled:
1025 case kActorStatusEnabled2:
1026 _resourceId = _graphicResourceIds[(actorDirection > kDirectionS ? kDirection8 - actorDirection : actorDirection) + 5];
1027 _frameCount = GraphicResource::getFrameCount(_vm, _resourceId);
1028 break;
1029
1030 case kActorStatus18:
1031 if (getWorld()->chapter == kChapter2)
1032 if (_index == 11) // we are actor 11
1033 _resourceId = _graphicResourceIds[(actorDirection > kDirectionS) ? kDirection8 - actorDirection : actorDirection];
1034 break;
1035
1036 case kActorStatusWalking:
1037 case kActorStatusWalkingTo:
1038 case kActorStatusWalking2:
1039 _resourceId = _graphicResourceIds[(actorDirection > kDirectionS ? kDirection8 - actorDirection : actorDirection)];
1040 _frameCount = GraphicResource::getFrameCount(_vm, _resourceId);
1041 break;
1042
1043 case kActorStatus8:
1044 _resourceId = _graphicResourceIds[(actorDirection > kDirectionS ? kDirection8 - actorDirection : actorDirection) + 20];
1045 break;
1046 }
1047 }
1048
faceTarget(uint32 target,DirectionFrom from)1049 void Actor::faceTarget(uint32 target, DirectionFrom from) {
1050 debugC(kDebugLevelActor, "[Actor] Facing target %d using direction from %d", target, from);
1051
1052 Common::Point point;
1053
1054 switch (from) {
1055 default:
1056 error("[Actor::faceTarget] Invalid direction input: %d (should be 0-3)", from);
1057
1058 case kDirectionFromObject: {
1059 Object *object = getWorld()->getObjectById((ObjectId)target);
1060 if (!object) {
1061 warning("[Actor::faceTarget] No Object found for id %d", target);
1062 return;
1063 }
1064
1065 Common::Rect frameRect = GraphicResource::getFrameRect(_vm, object->getResourceId(), object->getFrameIndex());
1066
1067 point.x = (int16)Common::Rational(frameRect.width(), 2).toInt() + object->x;
1068 point.y = (int16)Common::Rational(frameRect.height(), 2).toInt() + object->y;
1069 }
1070 break;
1071
1072 case kDirectionFromPolygons: {
1073 int32 actionIndex = getWorld()->getActionAreaIndexById(target);
1074 if (actionIndex == -1) {
1075 warning("[Actor::faceTarget] No ActionArea found for id %d", target);
1076 return;
1077 }
1078
1079 Polygon polygon = getScene()->polygons()->get(getWorld()->actions[actionIndex]->polygonIndex);
1080
1081 point.x = polygon.boundingRect.left + (polygon.boundingRect.right - polygon.boundingRect.left) / 2;
1082 point.y = polygon.boundingRect.top + (polygon.boundingRect.bottom - polygon.boundingRect.top) / 2;
1083 }
1084 break;
1085
1086 case kDirectionFromActor: {
1087 Actor *actor = getScene()->getActor((ActorIndex)target);
1088 point = *actor->getPoint1() + *actor->getPoint2();
1089 }
1090 break;
1091
1092 case kDirectionFromParameters:
1093 point.x = point.y = (int16)target;
1094 break;
1095 }
1096
1097 Common::Point mid(_point1.x + _point2.x, _point1.y + _point2.y);
1098 changeDirection(getAngle(mid, point));
1099 }
1100
setPosition(int16 newX,int16 newY,ActorDirection newDirection,uint32 frame)1101 void Actor::setPosition(int16 newX, int16 newY, ActorDirection newDirection, uint32 frame) {
1102 _point1.x = newX - _point2.x;
1103 _point1.y = newY - _point2.y;
1104
1105 if (_direction != kDirection8)
1106 changeDirection(newDirection);
1107
1108 if (frame > 0)
1109 _frameIndex = frame;
1110 }
1111
1112 /////////////////////////////////////////////////////////////////////////
1113 // Misc
1114 //////////////////////////////////////////////////////////////////////////
stopSound()1115 void Actor::stopSound() {
1116 if (_soundResourceId && getSound()->isPlaying(_soundResourceId))
1117 getSound()->stop(_soundResourceId);
1118 }
1119
toString(bool shortString)1120 Common::String Actor::toString(bool shortString) {
1121 Common::String output;
1122
1123 output += Common::String::format("Actor %d: %s\n", _index, _name);
1124 if (!shortString) {
1125 output += Common::String::format("resourceId: %d (0x%X): \n", _resourceId, _resourceId);
1126 output += Common::String::format("objectIndex: %d: \n", _objectIndex);
1127 output += Common::String::format("frameIndex: %d: \n", _frameIndex);
1128 output += Common::String::format("frameCount: %d: \n", _frameCount);
1129 output += Common::String::format("(x, y): (%d , %d): \n", _point.x, _point.y);
1130 output += Common::String::format("(x1, y1): (%d , %d): \n", _point1.x, _point1.y);
1131 output += Common::String::format("(x2, y2): (%d , %d): \n", _point2.x, _point2.y);
1132 output += Common::String::format("flags: %d: \n", flags);
1133 output += Common::String::format("actionType: %d: \n", actionType);
1134 output += Common::String::format("boundingRect: top[%d] left[%d] right[%d] bottom[%d]: \n", _boundingRect.top, _boundingRect.left, _boundingRect.right, _boundingRect.bottom);
1135 output += Common::String::format("direction: %d: \n", _direction);
1136 output += Common::String::format("field_3C: %d: \n", _field_3C);
1137 output += Common::String::format("status: %d: \n", _status);
1138 output += Common::String::format("field_44: %d: \n", _field_44);
1139 output += Common::String::format("priority: %d: \n", _priority);
1140 }
1141
1142 return output;
1143 }
1144
1145 //////////////////////////////////////////////////////////////////////////
1146 // Unknown methods
1147 //////////////////////////////////////////////////////////////////////////
1148
clearFields()1149 void Actor::clearFields() {
1150 _processNewDirection = false;
1151 _invertPriority = false;
1152 _nextDirection = kDirectionN;
1153 _nextActionIndex = 0;
1154 _nextActorIndex = 0;
1155 _nextPositionOffset = Common::Point(0, 0);
1156 _nextPosition = Common::Point(0, 0);
1157 _field_994 = 0;
1158 _field_998 = 0;
1159 _field_99C = 0;
1160 _field_9A0 = 0;
1161 }
1162
checkBoredStatus() const1163 bool Actor::checkBoredStatus() const {
1164 if (_status != kActorStatusFidget)
1165 return false;
1166
1167 int index;
1168 for (index = 10; index < 20; index++) {
1169 if (_graphicResourceIds[index] == _resourceId)
1170 break;
1171 }
1172
1173 return (index >= 15);
1174 }
1175
canReach(const Common::Point & point)1176 bool Actor::canReach(const Common::Point &point) {
1177 // Compute point and delta
1178 Common::Point sum(_point1.x + _point2.x, _point1.y + _point2.y);
1179 Common::Point delta = point - sum;
1180
1181 // Compute modifiers
1182 int16 a1 = 0;
1183 int16 a2 = 0;
1184 int16 a3 = 0;
1185
1186 if (delta.x <= 0) {
1187 if (delta.y >= 0) {
1188 a1 = -1;
1189 a2 = 1;
1190 a3 = 3;
1191 } else {
1192 a1 = -1;
1193 a2 = -1;
1194 a3 = 0;
1195 }
1196 } else {
1197 if (delta.y >= 0) {
1198 a1 = 1;
1199 a2 = 1;
1200 a3 = 2;
1201 } else {
1202 a1 = 1;
1203 a2 = -1;
1204 a3 = 1;
1205 }
1206 }
1207
1208 if (point == sum) {
1209 if (canMove(&sum, a3 >= 2 ? kDirectionS : kDirectionN, (uint32)abs(delta.y), false)) {
1210 _data.points[0] = point;
1211 _data.current = 0;
1212 _data.count = 1;
1213
1214 return true;
1215 }
1216 }
1217
1218 if (point.x == sum.x) {
1219 ActorDirection actorDir = a3 >= 2 ? kDirectionS : kDirectionN;
1220 if (canMove(&sum, actorDir, (uint32)abs(delta.y), false)) {
1221 _data.points[0] = point;
1222 _data.current = 0;
1223 _data.count = 1;
1224
1225 changeDirection(actorDir);
1226
1227 return true;
1228 }
1229
1230 return false;
1231 }
1232
1233 if (point.y == sum.y) {
1234 ActorDirection actorDir = (a3 != 0 && a3 != 3) ? kDirectionE : kDirectionW;
1235
1236 if (canMove(&sum, actorDir, (uint32)abs(delta.x), true)) {
1237 _data.points[0] = point;
1238 _data.current = 0;
1239 _data.count = 1;
1240
1241 changeDirection(actorDir);
1242
1243 return true;
1244 }
1245
1246 return false;
1247 }
1248
1249 if (abs(delta.x) != abs(delta.y)) {
1250 Common::Array<int> actions;
1251 Common::Point point1;
1252 Common::Point point2;
1253 uint32 count1 = 0;
1254 uint32 count2 = 0;
1255 ActorDirection direction1 = kDirectionInvalid;
1256 ActorDirection direction2 = kDirectionInvalid;
1257
1258 // Compute coordinates, directions and counts
1259 if (abs(delta.x) < abs(delta.y)) {
1260 point1 = Common::Point((int16)(sum.x + abs(delta.x) * a1), (int16)(sum.y + abs(delta.x) * a2));
1261 point2 = Common::Point(sum.x , (int16)(sum.y + abs(abs(delta.x) - abs(delta.y)) * a2));
1262 count1 = (uint32)abs(point1.x - sum.x);
1263 count2 = (uint32)abs(point1.y - point.y);
1264
1265 switch (a3) {
1266 default:
1267 error("[Actor::canReach] Invalid value for a3");
1268 break;
1269
1270 case 0:
1271 direction1 = kDirectionNW;
1272 direction2 = kDirectionN;
1273 break;
1274
1275 case 1:
1276 direction1 = kDirectionNE;
1277 direction2 = kDirectionN;
1278 break;
1279
1280 case 2:
1281 direction1 = kDirectionSE;
1282 direction2 = kDirectionS;
1283 break;
1284
1285 case 3:
1286 direction1 = kDirectionSW;
1287 direction2 = kDirectionS;
1288 break;
1289 }
1290 } else {
1291 point1 = Common::Point((int16)(sum.x + abs(delta.y) * a1), (int16)(sum.y + abs(delta.y) * a2));
1292 point2 = Common::Point((int16)(sum.x + abs(abs(delta.y) - abs(delta.x)) * a1), sum.y);
1293 count1 = (uint32)abs(abs(delta.y) * a2);
1294 count2 = (uint32)abs(point1.x - point.x);
1295
1296 switch (a3) {
1297 default:
1298 error("[Actor::canReach] Invalid value for a3");
1299 break;
1300
1301 case 0:
1302 direction1 = kDirectionNW;
1303 direction2 = kDirectionW;
1304 break;
1305
1306 case 1:
1307 direction1 = kDirectionNE;
1308 direction2 = kDirectionE;
1309 break;
1310
1311 case 2:
1312 direction1 = kDirectionSE;
1313 direction2 = kDirectionE;
1314 break;
1315
1316 case 3:
1317 direction1 = kDirectionSW;
1318 direction2 = kDirectionW;
1319 break;
1320 }
1321 }
1322
1323 // Check scene rects
1324 if (getWorld()->chapter != kChapter2 || strcmp(_name, "Big Crow")) {
1325 Common::Rect currentRect = getWorld()->sceneRects[getWorld()->sceneRectIdx];
1326
1327 if (!currentRect.contains(point1) || !currentRect.contains(point2))
1328 return false;
1329 }
1330
1331 if (canMove(&sum, direction1, count1, true)
1332 && canMove(&point1, direction2, count2, true)) {
1333 _data.points[0] = point1;
1334 _data.points[1] = point;
1335 _data.current = 0;
1336 _data.count = 2;
1337
1338 changeDirection(direction1);
1339
1340 _data.directions[1] = direction2;
1341
1342 return true;
1343 }
1344
1345 if (canMove(&sum, direction2, count2, true)
1346 && canMove(&point2, direction1, count1, true)) {
1347 _data.points[0] = point2;
1348 _data.points[1] = point;
1349 _data.current = 0;
1350 _data.count = 2;
1351
1352 changeDirection(direction2);
1353
1354 _data.directions[1] = direction1;
1355
1356 return true;
1357 }
1358
1359 // Compute actor actions
1360 int actorActions[5];
1361 ActionArea *actorArea = getWorld()->actions[_actionIdx3];
1362 for (uint32 i = 0; i < 5; i++) {
1363 if (actorArea->paths[i])
1364 actorActions[i] = actorArea->paths[i];
1365 else
1366 actorActions[i] = -1;
1367 }
1368
1369 if (actorActions[0] == -1)
1370 actions.push_back(_actionIdx3);
1371
1372 // Process all scene actions
1373 for (uint32 i = 0; i < getWorld()->actions.size(); i++) {
1374 // Check each area action against each actor action
1375 ActionArea *area = getWorld()->actions[i];
1376 for (uint32 j = 0; j < 5; j++) {
1377 for (uint32 k = 0; k < 5; k++) {
1378 if (area->paths[j] == actorActions[k])
1379 actions.push_back(i);
1380 }
1381 }
1382 }
1383
1384 //////////////////////////////////////////////////////////////////////////
1385 // Process actions
1386 _frameNumber = 0;
1387
1388 if (abs(sum.x - point.x) > abs(sum.y - point.y)) {
1389 if (sum.x <= point.x) {
1390 if (!findLeftPath(sum, point, &actions))
1391 return false;
1392 } else {
1393 if (!findRightPath(sum, point, &actions))
1394 return false;
1395 }
1396
1397 changeDirection(_data.directions[0]);
1398
1399 return true;
1400 }
1401
1402 if (sum.y > point.y) {
1403 if (!findUpPath(sum, point, &actions))
1404 return false;
1405
1406 changeDirection(_data.directions[0]);
1407
1408 return true;
1409 }
1410
1411 // last case: sum.y < point.y
1412 if (!findDownPath(sum, point, &actions))
1413 return false;
1414
1415 changeDirection(_data.directions[0]);
1416
1417 return true;
1418 }
1419
1420 //////////////////////////////////////////////////////////////////////////
1421 // Last case: abs(delta.x) == abs(delta.y)
1422
1423 // Compute direction
1424 ActorDirection actorDir = kDirectionSW;
1425 switch (a3) {
1426 default:
1427 break;
1428
1429 case 0:
1430 actorDir = kDirectionNW;
1431 break;
1432
1433 case 1:
1434 actorDir = kDirectionNE;
1435 break;
1436
1437 case 2:
1438 actorDir = kDirectionSE;
1439 break;
1440 }
1441
1442 if (!canMove(&sum, actorDir, (uint32)abs(delta.y), true))
1443 return false;
1444
1445 // Update actor data
1446 _data.points[0] = point;
1447 _data.current = 0;
1448 _data.count = 1;
1449
1450 // Update actor from direction
1451 changeDirection(actorDir);
1452
1453 return true;
1454 }
1455
forceTo(int16 actorX,int16 actorY,bool doSpeech)1456 void Actor::forceTo(int16 actorX, int16 actorY, bool doSpeech) {
1457 if (canReach(Common::Point(actorX, actorY))) {
1458 if (_status <= kActorStatus11)
1459 changeStatus(kActorStatusWalkingTo);
1460 else
1461 changeStatus(kActorStatusWalkingTo2);
1462 } else if (doSpeech) {
1463 getSpeech()->playIndexed(1);
1464 }
1465 }
1466
setupReflectionData(ActorIndex nextActor,int32 actionAreaId,ActorDirection nextDirection,const Common::Point & nextPosition,bool invertPriority,const Common::Point & nextPositionOffset)1467 void Actor::setupReflectionData(ActorIndex nextActor, int32 actionAreaId, ActorDirection nextDirection, const Common::Point &nextPosition, bool invertPriority, const Common::Point &nextPositionOffset) {
1468 _nextActorIndex = nextActor;
1469 _nextActionIndex = (actionAreaId != -1) ? getWorld()->getActionAreaIndexById(actionAreaId) : -1;
1470 _nextDirection = nextDirection;
1471 _nextPosition = nextPosition;
1472 _invertPriority = invertPriority;
1473
1474 Common::Point offset;
1475 if (actionAreaId != -1) {
1476 if (nextPositionOffset.x) {
1477 offset = nextPositionOffset;
1478 } else {
1479 Polygon polygon = getScene()->polygons()->get((uint32)_nextActionIndex);
1480
1481 offset = polygon.points[0];
1482
1483 // Iterate through points
1484 if (polygon.count() > 1) {
1485 for (uint i = 1; i < polygon.count() - 1; i++) {
1486 Common::Point point = polygon.points[i];
1487
1488 switch (nextDirection) {
1489 default:
1490 break;
1491
1492 case kDirectionN:
1493 if (offset.y > point.y)
1494 offset.y = point.y;
1495 break;
1496
1497 case kDirectionNW:
1498 if (offset.y > point.y)
1499 offset.y = point.y;
1500
1501 if (offset.x > point.x)
1502 offset.x = point.x;
1503 break;
1504
1505 case kDirectionW:
1506 if (offset.x > point.x)
1507 offset.x = point.x;
1508 break;
1509
1510 case kDirectionSW:
1511 if (offset.y < point.y)
1512 offset.y = point.y;
1513
1514 if (offset.x > point.x)
1515 offset.x = point.x;
1516 break;
1517
1518 case kDirectionS:
1519 if (offset.y < point.y)
1520 offset.y = point.y;
1521 break;
1522
1523 case kDirectionSE:
1524 if (offset.y < point.y)
1525 offset.y = point.y;
1526
1527 if (offset.x < point.x)
1528 offset.x = point.x;
1529 break;
1530
1531 case kDirectionE:
1532 if (offset.x < point.x)
1533 offset.x = point.x;
1534 break;
1535
1536 case kDirectionNE:
1537 if (offset.y > point.y)
1538 offset.y = point.y;
1539
1540 if (offset.x < point.x)
1541 offset.x = point.x;
1542 break;
1543
1544 case kDirection8:
1545 offset = Common::Point(0, 0);
1546 break;
1547 }
1548 }
1549 }
1550 }
1551 }
1552
1553 _nextPositionOffset = offset;
1554
1555 int16 cosValue = (int16)(cos(0.523598775) * 1000.0);
1556 int16 sinValue = (int16)(sin(0.523598775) * 1000.0);
1557
1558 _field_994 = offset.x - cosValue;
1559 _field_998 = offset.y + sinValue;
1560 _field_99C = offset.x + cosValue;
1561 _field_9A0 = offset.y - sinValue;
1562
1563 _processNewDirection = true;
1564
1565 updateReflectionData();
1566 }
1567
aNicePlaceToTalk(Common::Point * point,int32 * param)1568 bool Actor::aNicePlaceToTalk(Common::Point *point, int32* param) {
1569 Actor *player = getScene()->getActor();
1570
1571 int16 offset = 65;
1572 if (getWorld()->chapter != kChapter2 || _index != 8)
1573 offset = 40;
1574
1575 int32 parameter;
1576 Common::Point actorPoint = _point1 + _point2, pt = actorPoint;
1577
1578 if (getWorld()->chapter != kChapter2 || _index != 1) {
1579 Common::Point diff = *player->getPoint1() - _point1;
1580
1581 if (abs(diff.y) <= abs(diff.x)) {
1582
1583 if (diff.x < 0) {
1584
1585 parameter = 2;
1586 pt.x -= offset;
1587 if (getScene()->findActionArea(kActionAreaType2, pt) != -1)
1588 goto processActor;
1589
1590 parameter = 3;
1591 pt = actorPoint;
1592 pt.x -= offset;
1593 pt.y += offset;
1594 if (getScene()->findActionArea(kActionAreaType2, pt) != -1)
1595 goto processActor;
1596
1597 parameter = 1;
1598 pt = actorPoint;
1599 pt.x -= offset;
1600 pt.y -= offset;
1601
1602 } else {
1603
1604 parameter = 6;
1605 pt.x += offset;
1606 if (getScene()->findActionArea(kActionAreaType2, pt) != -1)
1607 goto processActor;
1608
1609 parameter = 5;
1610 pt = actorPoint;
1611 pt.x += offset;
1612 pt.y += offset;
1613 if (getScene()->findActionArea(kActionAreaType2, pt) != -1)
1614 goto processActor;
1615
1616 parameter = 7;
1617 pt = actorPoint;
1618 pt.x += offset;
1619 pt.y -= offset;
1620 }
1621
1622 } else {
1623
1624 if (diff.y >= 0) {
1625
1626 parameter = 4;
1627 pt.y += offset;
1628 if (getScene()->findActionArea(kActionAreaType2, pt) != -1)
1629 goto processActor;
1630
1631 parameter = 3;
1632 pt = actorPoint;
1633 pt.x -= offset;
1634 pt.y += offset;
1635 if (getScene()->findActionArea(kActionAreaType2, pt) != -1)
1636 goto processActor;
1637
1638 parameter = 5;
1639 pt = actorPoint;
1640 pt.x += offset;
1641 pt.y += offset;
1642
1643 } else {
1644
1645 parameter = 0;
1646 pt.y -= offset;
1647 if (getScene()->findActionArea(kActionAreaType2, pt) != -1)
1648 goto processActor;
1649
1650 parameter = 1;
1651 pt = actorPoint;
1652 pt.x -= offset;
1653 pt.y -= offset;
1654 if (getScene()->findActionArea(kActionAreaType2, pt) != -1)
1655 goto processActor;
1656
1657 parameter = 7;
1658 pt = actorPoint;
1659 pt.x += offset;
1660 pt.y -= offset;
1661 }
1662 }
1663 } else {
1664 parameter = 5;
1665 pt.x += offset;
1666 pt.y += offset;
1667 }
1668
1669 if (getScene()->findActionArea(kActionAreaType2, pt) == -1)
1670 return false;
1671
1672 processActor:
1673 if (!player->canReach(pt))
1674 return false;
1675
1676 *point = pt;
1677 *param = abs(parameter + 4) & 7;
1678
1679 return true;
1680 }
1681
canMove(Common::Point * point,ActorDirection direction,uint32 distance,bool hasDelta)1682 bool Actor::canMove(Common::Point *point, ActorDirection direction, uint32 distance, bool hasDelta) {
1683 if (_field_944 == 1 || _field_944 == 4)
1684 return true;
1685
1686 int16 x = (hasDelta ? point->x : point->x + deltaPointsArray[direction][0]);
1687 int16 y = (hasDelta ? point->y : point->y + deltaPointsArray[direction][1]);
1688
1689 // Check scene rect
1690 if (!_field_944) {
1691 Common::Rect rct = getWorld()->sceneRects[getWorld()->sceneRectIdx];
1692
1693 if (x > rct.right)
1694 return false;
1695
1696 if (x < rct.left)
1697 return false;
1698
1699 if (y < rct.top)
1700 return false;
1701
1702 if (y > rct.bottom)
1703 return false;
1704
1705 if (!testActorCollision(point, direction))
1706 return false;
1707 }
1708
1709 if (distance > 0) {
1710 uint32 allowed = 0;
1711
1712 while (getScene()->findActionArea(kActionAreaType1, Common::Point(x, y), true) != -1) {
1713 x += deltaPointsArray[direction][0];
1714 y += deltaPointsArray[direction][1];
1715
1716 ++allowed;
1717
1718 if (allowed >= distance)
1719 return true;
1720 }
1721
1722 return false;
1723 }
1724
1725 return true;
1726 }
1727
move(ActorDirection actorDir,uint32 dist)1728 void Actor::move(ActorDirection actorDir, uint32 dist) {
1729 if (_frameCount == 0)
1730 error("[Actor::move] Invalid frame count (cannot be 0)");
1731
1732 _lastScreenUpdate = _vm->screenUpdateCount;
1733
1734 Common::Point sum(_point1.x + _point2.x, _point1.y + _point2.y);
1735 int32 panning = getSound()->calculatePanningAtPoint(sum);
1736
1737 switch (_status) {
1738 default:
1739 break;
1740
1741 case kActorStatusWalking:
1742 case kActorStatusWalkingTo:
1743 case kActorStatusWalking2:
1744 case kActorStatusWalkingTo2:
1745 incPosition(actorDir, (int16)dist, &_point1);
1746
1747 _frameIndex = (_frameIndex + 1) % _frameCount;
1748
1749 if (_walkingSound1 != kResourceNone) {
1750
1751 // Compute volume
1752 int32 vol = (int32)sqrt((double)-Config.sfxVolume);
1753 if (_index != getSharedData()->getPlayerIndex())
1754 vol += (int32)sqrt((double)abs(getSound()->calculateVolumeAdjustement(sum, 10, 0)));
1755
1756 int32 volume = (Config.sfxVolume + vol) * (Config.sfxVolume + vol);
1757 if (volume > 10000)
1758 volume = 10000;
1759
1760 if (_field_944 != 1 && _field_944 != 4) {
1761 // Compute resource Id
1762 ResourceId resourceId = kResourceNone;
1763 if (getWorld()->actions[_actionIdx3]->soundResourceIdFrame != kResourceNone && strcmp((char *)&_name, "Crow") && strcmp((char *)&_name, "Big Crow")) {
1764 if (_frameIndex == _field_64C)
1765 resourceId = (ResourceId)(getWorld()->actions[_actionIdx3]->soundResourceIdFrame + (int)rnd(2));
1766 else if (_frameIndex == _field_650)
1767 resourceId = (ResourceId)(getWorld()->actions[_actionIdx3]->soundResourceId + (int)rnd(2));
1768 } else {
1769 if (_frameIndex == _field_64C)
1770 resourceId = (ResourceId)(_walkingSound1 + (int)rnd(2));
1771 else if (_frameIndex == _field_650)
1772 resourceId = (ResourceId)(_walkingSound3 + (int)rnd(2));
1773 }
1774
1775 // Play sound
1776 getSound()->playSound(resourceId, false, -volume, panning);
1777 }
1778 }
1779 break;
1780
1781 case kActorStatus18:
1782 if (getWorld()->chapter == kChapter2) {
1783 incPosition(actorDir, (int16)dist, &_point1);
1784
1785 if (_walkingSound1 == kResourceNone)
1786 break;
1787
1788 // Compute volume
1789 int32 vol = getWorld()->actions[_actionIdx3]->volume;
1790 if (_index != getSharedData()->getPlayerIndex())
1791 vol += (int32)sqrt((double)abs(getSound()->calculateVolumeAdjustement(sum, 10, 0)));
1792
1793 int32 volume = (Config.sfxVolume + vol) * (Config.sfxVolume + vol);
1794 if (volume > 10000)
1795 volume = 10000;
1796
1797 // Compute resource Id
1798 ResourceId resourceId = kResourceNone;
1799 if (getWorld()->actions[_actionIdx3]->soundResourceIdFrame != kResourceNone && strcmp((char *)&_name, "Crow") && strcmp((char *)&_name, "Big Crow")) {
1800 if (_frameIndex == _field_64C)
1801 resourceId = (ResourceId)(getWorld()->actions[_actionIdx3]->soundResourceIdFrame + (int)rnd(2));
1802 else if (_frameIndex == _field_650)
1803 resourceId = (ResourceId)(getWorld()->actions[_actionIdx3]->soundResourceId + (int)rnd(2));
1804 } else {
1805 if (_frameIndex == _field_64C)
1806 resourceId = (ResourceId)(_walkingSound1 + (int)rnd(2));
1807 else if (_frameIndex == _field_650)
1808 resourceId = (ResourceId)(_walkingSound3 + (int)rnd(2));
1809 }
1810
1811 // Play sound
1812 getSound()->playSound(resourceId, false, -volume, panning);
1813 }
1814 break;
1815 }
1816 }
1817
testActorCollision(Common::Point * point,ActorDirection dir)1818 bool Actor::testActorCollision(Common::Point *point, ActorDirection dir) {
1819 int32 dist = getStride(dir, (_frameIndex >= _frameCount) ? 2 * _frameCount - (_frameIndex + 1) : _frameIndex);
1820
1821 int32 x = point->x + deltaPointsArray[dir][0] * dist - (_field_948 + 10);
1822 int32 y = point->y + deltaPointsArray[dir][1] * dist - (_field_94C + 10);
1823 int32 x1 = x + 2 * _field_948 + 20;
1824 int32 y1 = y + 2 * _field_94C + 20;
1825
1826 for (int32 i = 0; i < (int32)getWorld()->actors.size(); i++) {
1827 if (i == _index)
1828 continue;
1829
1830 Actor *actor = getScene()->getActor(i);
1831
1832 if (!actor->isOnScreen())
1833 continue;
1834
1835 if (actor->_field_944)
1836 continue;
1837
1838 int32 x2 = actor->getPoint1()->x + actor->getPoint2()->x - (actor->getField948() + 15);
1839 int32 y2 = actor->getPoint1()->y + actor->getPoint2()->y - (actor->getField94C() + 10);
1840 int32 x3 = actor->getPoint1()->x + actor->getPoint2()->x + 2 * actor->getField948() + 15;
1841 int32 y3 = actor->getPoint1()->y + actor->getPoint2()->y + 2 * actor->getField94C() + 10;
1842
1843 if (i == getSharedData()->getPlayerIndex() && getWorld()->chapter != kChapter11) {
1844 x2 -= 10;
1845 y2 -= 10;
1846 x3 += 10;
1847 y3 += 10;
1848 }
1849
1850 if (getScene()->rectIntersect(x, y, x1, y1, x2, y2, x3, y3)) {
1851 if (i)
1852 return false;
1853
1854 int32 x4 = x2 + 10;
1855 int32 y4 = y2 + 10;
1856 int32 x5 = x3 - 10;
1857 int32 y5 = y3 - 10;
1858
1859 switch (_direction) {
1860 default:
1861 break;
1862
1863 case kDirectionNW:
1864 if (x4 >= x)
1865 break;
1866 // fallthrough
1867
1868 case kDirectionN:
1869 if (y4 >= y)
1870 break;
1871
1872 return false;
1873
1874 case kDirectionW:
1875 if (x4 < x)
1876 return false;
1877
1878 break;
1879
1880 case kDirectionSW:
1881 if (x4 < x && y4 > y)
1882 return false;
1883
1884 break;
1885
1886 case kDirectionS:
1887 if (y5 > y1)
1888 return false;
1889
1890 break;
1891
1892 case kDirectionE:
1893 if (x5 > x1)
1894 return false;
1895
1896 break;
1897
1898 case kDirectionNE:
1899 if (x5 > x1 && y4 < y)
1900 return false;
1901
1902 break;
1903 }
1904
1905 if (getScene()->rectIntersect(x, y, x1, y1, x4, y4, x5, y5))
1906 return false;
1907 }
1908 }
1909
1910 return true;
1911 }
1912
drawInventory()1913 void Actor::drawInventory() {
1914 Actor *player = getScene()->getActor();
1915 Common::Point mouse = getCursor()->position();
1916 bool keepField = false;
1917
1918 uint count = inventory.find();
1919 for (uint32 i = 0; i < count; i++) {
1920 // Compute points
1921 Common::Point coords;
1922 adjustCoordinates(&coords);
1923
1924 Common::Point ringPoint = Inventory::getInventoryRingPoint(_vm, count, i);
1925 Common::Point point = coords + Common::Point(player->getPoint2()->x + ringPoint.x, player->getPoint2()->y / 2 - ringPoint.y);
1926
1927 if (mouse.x < point.x || mouse.x > (point.x + 40) || mouse.y < point.y || mouse.y > (point.y + 40)) {
1928 getScreen()->addGraphicToQueue(getWorld()->inventoryIconsNormal[inventory[i] - 1],
1929 0,
1930 point,
1931 kDrawFlagNone,
1932 0,
1933 1);
1934 } else {
1935 if (getWorld()->field_120 != (int32)(i + 1)) {
1936 getSound()->playSound(MAKE_RESOURCE(kResourcePackSound, 3));
1937 Inventory::describe(_vm, inventory[i] - 1);
1938 }
1939
1940 getWorld()->field_120 = i + 1;
1941 keepField = true;
1942
1943 getScreen()->addGraphicToQueue(getWorld()->inventoryIconsActive[inventory[i] - 1],
1944 0,
1945 point,
1946 kDrawFlagNone,
1947 0,
1948 1);
1949 }
1950
1951 if (getWorld()->chapter == kChapter4)
1952 updateNumbers(inventory[i] - 1, point);
1953 }
1954
1955 if (!keepField)
1956 getWorld()->field_120 = 0;
1957 }
1958
stopWalking()1959 void Actor::stopWalking() {
1960 changeStatus(_status <= 11 ? kActorStatusEnabled : kActorStatusEnabled2);
1961 _data.current = 0;
1962 }
1963
1964 //////////////////////////////////////////////////////////////////////////
1965 // Static update methods
1966 //////////////////////////////////////////////////////////////////////////
crowsReturn(AsylumEngine * engine)1967 void Actor::crowsReturn(AsylumEngine *engine) {
1968 engine->clearGameFlag(kGameFlag438);
1969 engine->clearGameFlag(kGameFlag439);
1970 engine->clearGameFlag(kGameFlag440);
1971 engine->clearGameFlag(kGameFlag441);
1972 engine->clearGameFlag(kGameFlag442);
1973
1974 // Reset shared data
1975 engine->data()->resetChapter2Data();
1976
1977 engine->scene()->getActor(13)->enable();
1978 engine->scene()->getActor(13)->forceTo(2300, 71, false);
1979
1980 engine->scene()->getActor(14)->enable();
1981 engine->scene()->getActor(14)->forceTo(2600, 1300, false);
1982
1983 engine->scene()->getActor(15)->enable();
1984 engine->scene()->getActor(15)->forceTo(2742, 615, false);
1985
1986 engine->scene()->getActor(16)->enable();
1987 engine->scene()->getActor(16)->forceTo(2700, 1200, false);
1988
1989 engine->scene()->getActor(17)->enable();
1990 engine->scene()->getActor(17)->forceTo(2751, 347, false);
1991
1992 engine->scene()->getActor(18)->enable();
1993 engine->scene()->getActor(18)->forceTo(2420, 284, false);
1994
1995 engine->scene()->getActor(19)->enable();
1996 engine->scene()->getActor(19)->forceTo(2800, 370, false);
1997
1998 engine->scene()->getActor(20)->enable();
1999 engine->scene()->getActor(20)->forceTo(1973, 1, false);
2000
2001 engine->scene()->getActor(21)->enable();
2002 engine->scene()->getActor(21)->forceTo(2541, 40, false);
2003 }
2004
morphInto(AsylumEngine * engine,int nextPlayer)2005 void Actor::morphInto(AsylumEngine *engine, int nextPlayer) {
2006 WorldStats *world = engine->scene()->worldstats();
2007 if (world->chapter != kChapter9)
2008 return;
2009
2010 Actor *player = engine->scene()->getActor();
2011 world->nextPlayer = nextPlayer;
2012
2013 switch (engine->data()->getPlayerIndex()) {
2014 default:
2015 break;
2016
2017 case 1:
2018 if (nextPlayer == 2) {
2019 player->setResourceId(world->graphicResourceIds[7]);
2020
2021 uint32 frameCount = GraphicResource::getFrameCount(engine, player->getResourceId());
2022 player->setFrameCount(frameCount);
2023 player->setFrameIndex(frameCount - 1);
2024
2025 } else if (nextPlayer == 3) {
2026 player->setResourceId(world->graphicResourceIds[8]);
2027
2028 uint32 frameCount = GraphicResource::getFrameCount(engine, player->getResourceId());
2029 player->setFrameCount(frameCount);
2030 player->setFrameIndex(frameCount - 1);
2031 }
2032 break;
2033
2034 case 2:
2035 if (nextPlayer == 1) {
2036 player->setResourceId(world->graphicResourceIds[4]);
2037
2038 uint32 frameCount = GraphicResource::getFrameCount(engine, player->getResourceId());
2039 player->setFrameCount(frameCount);
2040 player->setFrameIndex(0);
2041
2042 } else if (nextPlayer == 3) {
2043 player->setResourceId(world->graphicResourceIds[3]);
2044
2045 uint32 frameCount = GraphicResource::getFrameCount(engine, player->getResourceId());
2046 player->setFrameCount(frameCount);
2047 player->setFrameIndex(0);
2048 }
2049 break;
2050
2051 case 3:
2052 if (nextPlayer == 1) {
2053 player->setResourceId(world->graphicResourceIds[5]);
2054
2055 uint32 frameCount = GraphicResource::getFrameCount(engine, player->getResourceId());
2056 player->setFrameCount(frameCount);
2057 player->setFrameIndex(0);
2058
2059 } else if (nextPlayer == 2) {
2060 player->setResourceId(world->graphicResourceIds[6]);
2061
2062 uint32 frameCount = GraphicResource::getFrameCount(engine, player->getResourceId());
2063 player->setFrameCount(frameCount);
2064 player->setFrameIndex(frameCount - 1);
2065 }
2066 break;
2067 }
2068
2069 player->changeStatus(kActorStatusMorphingInto);
2070 }
2071
2072 //////////////////////////////////////////////////////////////////////////
2073 // Update methods
2074 //////////////////////////////////////////////////////////////////////////
2075
updateStatusInteracting()2076 void Actor::updateStatusInteracting() {
2077 if (getWorld()->chapter != kChapter2 || _frameIndex != 6 || _status == kActorStatusInteracting) { /* Original check: _status <= kActorStatus11 */
2078 if (_frameIndex < _frameCount - 1) {
2079 ++_frameIndex;
2080 } else {
2081 if (_status == kActorStatusInteracting)
2082 changeStatus(kActorStatusStoppedInteracting);
2083 else
2084 changeStatus(kActorStatusStoppedHitting);
2085 }
2086 } else {
2087 if (_index == getSharedData()->getPlayerIndex())
2088 checkPumpkinDeath();
2089
2090 ++_frameIndex;
2091 }
2092 }
2093
checkPumpkinDeath()2094 void Actor::checkPumpkinDeath() {
2095 updatePumpkin(kGameFlag263, kGameFlag270, kObjectPumpkin2Dies, kObjectPumpkin2Loop);
2096 updatePumpkin(kGameFlag264, kGameFlag271, kObjectPumpkin3Dies, kObjectPumpkin3Loop);
2097 updatePumpkin(kGameFlag265, kGameFlag272, kObjectPumpkin4Dies, kObjectPumpkin4Loop);
2098 updatePumpkin(kGameFlag266, kGameFlag273, kObjectPumpkin5Dies, kObjectPumpkin5Loop);
2099 updatePumpkin(kGameFlag267, kGameFlag274, kObjectPumpkin6Dies, kObjectPumpkin6Loop);
2100 updatePumpkin(kGameFlag268, kGameFlag275, kObjectPumpkin7Dies, kObjectPumpkin7Loop);
2101 updatePumpkin(kGameFlag269, kGameFlag276, kObjectPumpkin1Dies, kObjectPumpkin1Loop);
2102 }
2103
updatePumpkin(GameFlag flagToCheck,GameFlag flagToSet,ObjectId objectToUpdate,ObjectId objectToDisable)2104 void Actor::updatePumpkin(GameFlag flagToCheck, GameFlag flagToSet, ObjectId objectToUpdate, ObjectId objectToDisable) {
2105 if (_vm->isGameFlagSet(flagToCheck)) {
2106 _vm->setGameFlag(flagToSet);
2107 _vm->clearGameFlag(flagToCheck);
2108
2109 getSharedData()->setChapter2Counter(5, getSharedData()->getChapter2Counter(5) + 1);
2110
2111 getWorld()->getObjectById(objectToUpdate)->setNextFrame(8);
2112 getSound()->playSound(getWorld()->soundResourceIds[17], false, Config.sfxVolume - 10);
2113 getWorld()->getObjectById(objectToDisable)->disable();
2114 }
2115 }
2116
updateStatusEnabled()2117 void Actor::updateStatusEnabled() {
2118 if (_frameCount == 0)
2119 error("[Actor::updateStatusEnabled] Actor has no frame!");
2120
2121 _frameIndex = (_frameIndex + 1) % _frameCount;
2122
2123 if (_vm->screenUpdateCount - _lastScreenUpdate > 300) {
2124 // All actors except Crow and Armed Max
2125 if (strcmp((char *)&_name, "Crow") && strcmp((char *)_name, "Armed Max")) {
2126 if (_vm->getRandom(100) < 50
2127 && (!getSpeech()->getSoundResourceId() || !getSound()->isPlaying(getSpeech()->getSoundResourceId()))
2128 && canChangeStatus(10))
2129 changeStatus(kActorStatusFidget);
2130
2131 _lastScreenUpdate = _vm->screenUpdateCount;
2132 }
2133 }
2134
2135 // Actor: Player
2136 if (_index == getSharedData()->getPlayerIndex()) {
2137 if (_vm->lastScreenUpdate && (_vm->screenUpdateCount - _vm->lastScreenUpdate) > 500) {
2138
2139 if (_vm->isGameFlagNotSet(kGameFlagScriptProcessing)
2140 && isVisible()
2141 && !getSharedData()->getFlag(kFlagIsEncounterRunning)
2142 && !getSpeech()->getSoundResourceId()) {
2143 if (_vm->getRandom(100) < 50) {
2144 if (getWorld()->chapter == kChapter13)
2145 getSpeech()->playPlayer(507);
2146 else
2147 getSpeech()->playIndexed(4);
2148 }
2149 }
2150 _lastScreenUpdate = _vm->screenUpdateCount;
2151 _vm->lastScreenUpdate = _vm->screenUpdateCount;
2152 }
2153
2154 return;
2155 }
2156
2157 // Actor:: BigCrow
2158 if (!strcmp(_name, "Big Crow")) {
2159 if (_vm->getRandom(10) < 5) {
2160 switch (_vm->getRandom(4)) {
2161 default:
2162 break;
2163
2164 case 0:
2165 setPosition(10, 1350, kDirectionN, 0);
2166 forceTo(1460, -100, false);
2167 break;
2168
2169 case 1:
2170 setPosition(300, 0, kDirectionN, 0);
2171 forceTo(1700, 1400, false);
2172 break;
2173
2174 case 2:
2175 setPosition(1560, -100, kDirectionN, 0);
2176 forceTo(-300, 1470, false);
2177 break;
2178
2179 case 3:
2180 setPosition(1150, 1400, kDirectionN, 0);
2181 forceTo(-250, 0, false);
2182 break;
2183 }
2184 }
2185
2186 return;
2187 }
2188
2189 // All other actors
2190 if (getWorld()->chapter != kChapter2 || _index != 8) {
2191 if (_field_944 == 4) {
2192 Common::Rect frameRect = GraphicResource::getFrameRect(_vm, getWorld()->backgroundImage, 0);
2193 forceTo((int16)rnd(frameRect.width() + 200) - 100, (int16)rnd(frameRect.height() + 200) - 100, false);
2194 } else {
2195 // Actor: Crow
2196 if (rnd(1000) < 5 || !strcmp(_name, "Crow")) {
2197 if (_actionIdx2 != -1) {
2198
2199 // Process action area
2200 int32 areaIndex = getWorld()->getRandomActionAreaIndexById(_actionIdx2);
2201 if (areaIndex != -1) {
2202
2203 ActionArea *area = getWorld()->actions[areaIndex];
2204 Polygon poly = getScene()->polygons()->get(area->polygonIndex);
2205
2206 Common::Point pt(poly.boundingRect.left + (int16)rnd((uint16)poly.boundingRect.width()),
2207 poly.boundingRect.top + (int16)rnd((uint16)poly.boundingRect.height()));
2208
2209 if (!getSharedData()->getFlag(kFlagActorUpdateEnabledCheck)) {
2210 if (!isInActionArea(pt, area)) {
2211 Common::Point polyPoint = poly.points[rnd(poly.count())];
2212 forceTo(polyPoint.x, polyPoint.y, false);
2213 } else {
2214 forceTo(pt.x, pt.y, false);
2215 }
2216 }
2217 }
2218 }
2219 }
2220 }
2221 } else {
2222 switch (getSharedData()->getActorUpdateStatusEnabledCounter()) {
2223 default:
2224 break;
2225
2226 case 0:
2227 updateStatusEnabledProcessStatus(1055, 989, 1, 1088, 956);
2228 break;
2229
2230 case 1:
2231 updateStatusEnabledProcessStatus(1088, 956, 2, _point1.x + _point2.x, 900);
2232 break;
2233
2234 case 2:
2235 updateStatusEnabledProcessStatus(1088, 900, 3, 1018, 830);
2236 break;
2237
2238 case 3:
2239 updateStatusEnabledProcessStatus(1018, 830, 4, 970, 830);
2240 break;
2241
2242 case 4:
2243 updateStatusEnabledProcessStatus(970, 830, 5, 912, 936);
2244 break;
2245
2246 case 5:
2247 updateStatusEnabledProcessStatus(912, 936, 0, 1055, 989);
2248 break;
2249 }
2250 }
2251 }
2252
updateStatusEnabledProcessStatus(int16 testX,int16 testY,uint32 counter,int16 setX,int16 setY)2253 void Actor::updateStatusEnabledProcessStatus(int16 testX, int16 testY, uint32 counter, int16 setX, int16 setY) {
2254 int32 xsum = _point1.x + _point2.x;
2255 int32 ysum = _point1.y + _point2.y;
2256
2257 if (xsum != testX || ysum != testY) {
2258 if (rnd(1000) < 5)
2259 forceTo(testX, testY, false);
2260 } else {
2261 getSharedData()->setActorUpdateStatusEnabledCounter(counter);
2262
2263 if (rnd(1000) < 5)
2264 forceTo(setX, setY, false);
2265 }
2266 }
2267
updateStatusBored()2268 void Actor::updateStatusBored() {
2269 if (_index == getSharedData()->getPlayerIndex()
2270 && getWorld()->chapter != kChapter9
2271 && getWorld()->actorType == 0
2272 && _frameIndex == 0
2273 && checkBoredStatus()) {
2274 if (!getSpeech()->getSoundResourceId() || !getSound()->isPlaying(getSpeech()->getSoundResourceId()))
2275 getSpeech()->playPlayer(13);
2276 }
2277
2278 ++_frameIndex;
2279 if (_frameIndex == _frameCount) {
2280 enable();
2281 _lastScreenUpdate = _vm->screenUpdateCount;
2282 }
2283 }
2284
CrowClosesIn()2285 void Actor::CrowClosesIn() {
2286 // Compute distance
2287 uint32 frameIndex = _frameIndex;
2288 if (_frameIndex >= _frameCount)
2289 frameIndex = 2 * _frameCount - (_frameIndex + 1);
2290
2291 uint32 distance = (uint32)abs((double)getWalkIncrement(_direction, frameIndex));
2292
2293 // Face actor
2294 faceTarget((uint32)getSharedData()->getPlayerIndex(), kDirectionFromActor);
2295
2296 int32 data = getSharedData()->crowsData[_index + 25];
2297 if (data > 0) {
2298 _direction = DIR(_direction + 4);
2299 getSharedData()->crowsData[_index + 25] = data - 1;
2300 }
2301
2302 // Compute coordinates and distance
2303 Actor *player = getScene()->getActor();
2304 Common::Point sumPlayer = *player->getPoint1() + *player->getPoint2();
2305 Common::Point sum = _point1 + _point2;
2306
2307 uint32 absX = (uint32)abs(sum.x - sumPlayer.x);
2308 uint32 absY = (uint32)abs(sum.y - sumPlayer.y);
2309
2310 // Adjust distance
2311 if (absX <= absY)
2312 absX = absY;
2313
2314 if (sum.y < sumPlayer.y)
2315 absX += 20;
2316
2317 if (absX >= 50) {
2318 move(_direction, distance);
2319 } else {
2320 _frameIndex = 0;
2321
2322 getSharedData()->crowsData[2 * _index + 30] = player->getPoint1()->x - _point1.x;
2323 getSharedData()->crowsData[2 * _index + 31] = player->getPoint1()->y - _point1.y;
2324
2325 changeStatus(kActorStatus18);
2326 }
2327 }
2328
ScareCrowClosesIn()2329 void Actor::ScareCrowClosesIn() {
2330 bool processEnd = true;
2331 Actor *player = getScene()->getActor();
2332 Common::Point sum = _point1 + _point2;
2333 Common::Point sumPlayer = *player->getPoint1() + *player->getPoint2();
2334
2335 // Compute distance
2336 uint32 frameIndex = _frameIndex;
2337 if (_frameIndex >= _frameCount)
2338 frameIndex = 2 * _frameCount - (_frameIndex + 1);
2339
2340 uint32 distance = (uint32)abs((double)getWalkIncrement(_direction, frameIndex));
2341
2342 // Update status
2343 if (player->getStatus() == kActorStatusRestarting || !getScene()->getActor(10)->isVisible()) {
2344 changeStatus(kActorStatusEnabled);
2345 getSharedData()->crowsData[_index - 11] = 160;
2346 }
2347
2348 // Face player
2349 faceTarget((uint32)getSharedData()->getPlayerIndex(), kDirectionFromActor);
2350
2351 // Compute coordinates
2352 Common::Point delta = Common::Point((sumPlayer.x + sum.x) / 2, (sumPlayer.y + sum.y) / 2);
2353 Common::Point point1 = Common::Point((delta.x + sum.x) / 2, (delta.y + sum.y) / 2);
2354 Common::Point point2 = Common::Point((delta.x + sumPlayer.x) / 2, (delta.y + sumPlayer.y) / 2);
2355
2356 if (getScene()->findActionArea(kActionAreaType1, delta) == -1
2357 || getScene()->findActionArea(kActionAreaType1, point1) == -1
2358 || getScene()->findActionArea(kActionAreaType1, point2) == -1) {
2359 processEnd = false;
2360
2361 if (determineLeftOrRight(sum, sumPlayer) == false) {
2362 if (canMove(&sum, DIR(_direction + 1), distance, false)) {
2363 move(DIR(_direction + 1), distance);
2364 } else if (canMove(&sum, DIR(_direction + 2), distance, false)) {
2365 move(DIR(_direction + 2), distance);
2366 } else if (canMove(&sum, DIR(_direction + 3), distance, false)) {
2367 move(DIR(_direction + 3), distance);
2368 } else if (canMove(&sum, DIR(_direction + 4), distance, false)) {
2369 move(DIR(_direction + 4), distance);
2370 }
2371 } else {
2372 if (canMove(&sum, DIR(_direction + 7), distance, false)) {
2373 move(DIR(_direction + 7), distance);
2374 } else if (canMove(&sum, DIR(_direction + 6), distance, false)) {
2375 move(DIR(_direction + 6), distance);
2376 } else if (canMove(&sum, DIR(_direction + 5), distance, false)) {
2377 move(DIR(_direction + 5), distance);
2378 } else if (canMove(&sum, DIR(_direction + 4), distance, false)) {
2379 move(DIR(_direction + 4), distance);
2380 }
2381 }
2382 } else {
2383 if (canMove(&sum, DIR(_direction + 1), distance, false)) {
2384 move(DIR(_direction + 1), distance);
2385 } else if (canMove(&sum, DIR(_direction + 2), distance, false)) {
2386 move(DIR(_direction + 2), distance);
2387 } else if (canMove(&sum, DIR(_direction + 7), distance, false)) {
2388 move(DIR(_direction + 7), distance);
2389 } else if (canMove(&sum, DIR(_direction + 6), distance, false)) {
2390 move(DIR(_direction + 6), distance);
2391 }
2392 }
2393
2394 if (processEnd) {
2395 if (player->getStatus() != kActorStatusRestarting && player->getStatus() != kActorStatusGettingHurt) {
2396 if (sqrt((double)((sum.y - sumPlayer.y) * (sum.y - sumPlayer.y) + (sum.x - sumPlayer.x) * (sum.x - sumPlayer.x))) < 80.0) {
2397 _frameIndex = 0;
2398 faceTarget((uint32)getSharedData()->getPlayerIndex(), kDirectionFromActor);
2399 changeStatus(kActorStatusAttacking);
2400 }
2401 }
2402 }
2403 }
2404
TentacleRises()2405 void Actor::TentacleRises() {
2406 if (!_frameIndex)
2407 getSound()->playSound(getWorld()->soundResourceIds[6]);
2408
2409 ++_frameIndex;
2410
2411 if (_frameIndex >= _frameCount) {
2412 _frameIndex = 0;
2413 changeStatus(kActorStatusEnabled2);
2414 getWorld()->tickValueArray[_index] = rnd(4000) + _vm->getTick();
2415 }
2416
2417 Actor *actor0 = getScene()->getActor(0);
2418
2419 getSharedData()->vector1.x = actor0->getPoint1()->x + actor0->getPoint2()->x;
2420 getSharedData()->vector1.y = actor0->getPoint1()->y + actor0->getPoint2()->y - 5;
2421
2422 getSharedData()->vector2.x = _point1.x + _point2.x;
2423 getSharedData()->vector2.y = _point1.y + _point2.y;
2424
2425 TentacleBlocksSarah(getSharedData()->vector1, getSharedData()->vector2);
2426 }
2427
updateStatusEnabled2()2428 void Actor::updateStatusEnabled2() {
2429 if (_frameCount == 0)
2430 error("[Actor::updateStatusEnabled2] Invalid frame count (cannot be 0)");
2431
2432 _frameIndex = (_frameIndex + 1) % _frameCount;
2433 _lastScreenUpdate = _vm->screenUpdateCount;
2434
2435 switch (getWorld()->chapter) {
2436 default:
2437 break;
2438
2439 case kChapter2:
2440 if (_index == 11)
2441 changeStatus(kActorStatusWalking2);
2442 else if (_index > 12)
2443 CrowStatusQuo();
2444 break;
2445
2446 case kChapter11:
2447 if (_index >= 10 && _index < 16)
2448 TentacleWigglesForSarah();
2449 break;
2450 }
2451 }
2452
CrowStatusQuo()2453 void Actor::CrowStatusQuo() {
2454 if (getSharedData()->crowsData[_index + 61])
2455 CrowHoveringBeforeKill();
2456 else
2457 changeStatus(kActorStatusWalking2);
2458 }
2459
CrowHoveringBeforeKill()2460 void Actor::CrowHoveringBeforeKill() {
2461 // Original calls getWalkIncrement but does not seem to do anything with the results
2462
2463 Actor *player = getScene()->getActor();
2464 ActorStatus playerStatus = player->getStatus();
2465
2466 if (playerStatus == kActorStatusRestarting || !getScene()->getActor(10)->isVisible()) {
2467 changeStatus(kActorStatusEnabled);
2468 getSharedData()->crowsData[_index - 11] = 160;
2469 }
2470
2471 if (playerStatus != kActorStatusGettingHurt) {
2472 _point1.x = player->getPoint1()->x - (int16)getSharedData()->crowsData[2 * _index + 30];
2473 _point1.y = player->getPoint1()->y - (int16)getSharedData()->crowsData[2 * _index + 31] + 54;
2474 }
2475
2476 if (_frameIndex == _frameCount - 1) {
2477 _frameIndex = 0;
2478 if (getSharedData()->crowsData[_index + 29] <= 1 || playerStatus == kActorStatusGettingHurt || playerStatus == kActorStatusRestarting) {
2479 getSharedData()->crowsData[_index + 29]++;
2480 } else {
2481 changeStatus(kActorStatusAttacking);
2482 _point1.y -= 54;
2483 getSharedData()->crowsData[_index + 29] = 0;
2484 getSharedData()->crowsData[_index - 2] += 54;
2485 }
2486 }
2487
2488 if (playerStatus == kActorStatusRestarting && getSharedData()->crowsData[_index + 17] < 100) {
2489 _point1.y -= 6;
2490 getSharedData()->crowsData[_index + 29] = 100;
2491 getSharedData()->crowsData[_index - 2] += 6;
2492 }
2493
2494 if (getSharedData()->crowsData[_index + 17] > 99) {
2495 _point1.y -= 6;
2496 getSharedData()->crowsData[_index + 17]++;
2497 getSharedData()->crowsData[_index - 2] += 6;
2498
2499 if (getSharedData()->crowsData[_index + 17] > 108) {
2500 getSharedData()->crowsData[_index + 29] = 0;
2501
2502 changeStatus(kActorStatusEnabled);
2503
2504 switch (_index) {
2505 default:
2506 error("Invalid actor index (was: %d)", _index);
2507 break;
2508
2509 case 13:
2510 forceTo(2300, 671, false);
2511 break;
2512
2513 case 14:
2514 forceTo(2600, 1300, false);
2515 break;
2516
2517 case 15:
2518 forceTo(2742, 615, false);
2519 break;
2520
2521 case 16:
2522 forceTo(2700, 1400, false);
2523 break;
2524
2525 case 17:
2526 forceTo(2751, 347, false);
2527 break;
2528
2529 case 18:
2530 forceTo(2420, 284, false);
2531 break;
2532
2533 case 19:
2534 forceTo(2800, 370, false);
2535 break;
2536
2537 case 20:
2538 forceTo(1973, 1, false);
2539 break;
2540 }
2541 }
2542 }
2543 }
2544
TentacleWigglesForSarah()2545 void Actor::TentacleWigglesForSarah() {
2546 Actor *actor0 = getScene()->getActor(0);
2547
2548 getSharedData()->vector1.x = actor0->getPoint1()->x + actor0->getPoint2()->x;
2549 getSharedData()->vector1.y = actor0->getPoint1()->y + actor0->getPoint2()->y - 5;
2550
2551 getSharedData()->vector2.x = _point1.x + _point2.x;
2552 getSharedData()->vector2.y = _point1.y + _point2.y;
2553
2554 if (getWorld()->tickValueArray[_index] == -666)
2555 getWorld()->tickValueArray[_index] = rnd(4000) + _vm->getTick();
2556
2557 faceTarget(kActorMax, kDirectionFromActor);
2558 TentacleBlocksSarah(getSharedData()->vector1, getSharedData()->vector2);
2559
2560 if (getWorld()->tickValueArray[_index] < (int)_vm->getTick()) {
2561 if (euclidianDistance(getSharedData()->vector1, getSharedData()->vector2) >= 75) {
2562 getWorld()->tickValueArray[_index] = rnd(1000) + 2000 + _vm->getTick();
2563 } else {
2564 if (actor0->getStatus() == kActorStatusWalking2 || actor0->getStatus() == kActorStatusEnabled2 || actor0->getStatus() == kActorStatusAttacking)
2565 changeStatus(kActorStatusAttacking);
2566
2567 getWorld()->tickValueArray[_index] = -666;
2568 }
2569 }
2570 }
2571
CrowDives()2572 void Actor::CrowDives() {
2573 Actor *player = getScene()->getActor();
2574
2575 Common::Point sum = _point1 + _point2;
2576 Common::Point sumPlayer = *player->getPoint1() + *player->getPoint2();
2577
2578 if (getScene()->getActor(10)->getStatus() == kActorStatusRestarting || !getScene()->getActor(10)->isVisible()) {
2579 changeStatus(kActorStatusEnabled);
2580 getSharedData()->crowsData[_index - 11] = 160;
2581 }
2582
2583 if (_frameIndex == 1)
2584 getSound()->playSound(getWorld()->soundResourceIds[1], false, Config.sfxVolume - 10);
2585
2586 if (player->getStatus() == kActorStatusRestarting && _frameIndex < 6)
2587 changeStatus(kActorStatusEnabled);
2588
2589 uint32 dist = euclidianDistance(sumPlayer, sum);
2590 int16 offset = (dist <= 10) ? 7 : 12;
2591 if (dist > 20) {
2592 faceTarget((uint32)getSharedData()->getPlayerIndex(), kDirectionFromActor);
2593 getScene()->getActor(_index + 9)->setDirection(_direction);
2594 }
2595
2596 if (_frameIndex < 5 || !getSharedData()->crowsData[_index + 61])
2597 _frameIndex++;
2598
2599 if (sumPlayer.x > sum.x)
2600 _point1.x += offset;
2601 else if (sumPlayer.x < sum.x)
2602 _point1.x -= offset;
2603
2604 if (sumPlayer.y > sum.y)
2605 _point1.y += offset;
2606 else if (sumPlayer.y < sum.y)
2607 _point1.y -= offset;
2608
2609 if ((int32)dist < (offset + 1)) {
2610 if (player->getStatus() != kActorStatusGettingHurt && player->getStatus() != kActorStatusRestarting && player->getFrameIndex() < 6) {
2611 _point1 = sumPlayer - _point2;
2612
2613 MaxGetsHit();
2614 getSpeech()->playPlayer(51);
2615 _vm->setGameFlag(kGameFlag219);
2616
2617 player->changeDirection(DIR(_direction + 4));
2618 player->changeStatus(kActorStatusGettingHurt);
2619
2620 getSharedData()->crowsData[_index + 61] = 0;
2621 }
2622 }
2623
2624 if (_frameIndex > _frameCount - 1) {
2625 switch (rnd(4)) {
2626 default:
2627 case 0:
2628 sum.y -= 200;
2629 break;
2630
2631 case 1:
2632 sum.y += 200;
2633 break;
2634
2635 case 2:
2636 sum.x -= 200;
2637 break;
2638
2639 case 3:
2640 sum.x += 200;
2641 break;
2642 }
2643
2644 _frameIndex = 0;
2645
2646 if (getSharedData()->getChapter2Counter(6) <= 2)
2647 forceTo(sum.x, sum.y, false);
2648 else
2649 changeStatus(kActorStatusEnabled);
2650
2651 getSharedData()->crowsData[_index + 61] = 0;
2652 }
2653 }
2654
MaxGetsHit()2655 void Actor::MaxGetsHit() {
2656 Actor *actor39 = getScene()->getActor(39);
2657
2658 actor39->setFrameIndex(0);
2659 *actor39->getPoint1() = *getScene()->getActor()->getPoint1();
2660
2661 if (_vm->isGameFlagSet(kGameFlag169))
2662 actor39->getPoint1()->y += 80;
2663
2664 switch (getSharedData()->getChapter2Counter(6)) {
2665 default:
2666 break;
2667
2668 case 0:
2669 _vm->setGameFlag(kGameFlag369);
2670
2671 if (getSound()->isPlaying(getWorld()->soundResourceIds[5]))
2672 getSound()->stop(getWorld()->soundResourceIds[5]);
2673
2674 if (!getSound()->isPlaying(getWorld()->soundResourceIds[6]))
2675 getSound()->playSound(getWorld()->soundResourceIds[6], true, Config.sfxVolume - 10);
2676 break;
2677
2678 case 1:
2679 _vm->setGameFlag(kGameFlag370);
2680
2681 if (getSound()->isPlaying(getWorld()->soundResourceIds[6]))
2682 getSound()->stop(getWorld()->soundResourceIds[6]);
2683
2684 if (!getSound()->isPlaying(getWorld()->soundResourceIds[7]))
2685 getSound()->playSound(getWorld()->soundResourceIds[7], true, Config.sfxVolume - 10);
2686 break;
2687
2688 case 2:
2689 if (getSound()->isPlaying(getWorld()->soundResourceIds[7]))
2690 getSound()->stop(getWorld()->soundResourceIds[7]);
2691 break;
2692 }
2693
2694 getSharedData()->setChapter2Counter(6, getSharedData()->getChapter2Counter(6) + 1);
2695
2696 switch (getSharedData()->getChapter2Counter(6)) {
2697 default:
2698 crowsReturn(_vm);
2699 getCursor()->hide();
2700 break;
2701
2702 case 0:
2703 break;
2704
2705 case 1:
2706 _vm->setGameFlag(kGameFlag369);
2707 break;
2708
2709 case 2:
2710 _vm->setGameFlag(kGameFlag370);
2711 break;
2712 }
2713 }
2714
MaxAttacks()2715 void Actor::MaxAttacks() {
2716 if (_index != getSharedData()->getPlayerIndex())
2717 error("[Actor::MaxAttacks] Function is only available for the current player");
2718
2719 // Update frame index and process
2720 _frameIndex++;
2721
2722 if (_frameIndex == 1)
2723 getSound()->playSound(getWorld()->soundResourceIds[3], false, Config.sfxVolume - 10);
2724
2725 ActorIndex actorIndex = getSharedData()->getChapter2ActorIndex();
2726 if (_frameIndex == 3) {
2727
2728 if (actorIndex > 12) {
2729
2730 Actor *otherActor = getScene()->getActor(actorIndex);
2731
2732 if (otherActor->getStatus() == kActorStatusEnabled2) {
2733 // FIXME: this is a bit strange, but it looks like the original does exactly that
2734 // this might be dead code (the actor 38 never exists and thus setting values has no effect)
2735 Actor* actor38 = getScene()->getActor(38);
2736 actor38->setFrameIndex(0);
2737 *actor38->getPoint1() = *otherActor->getPoint1();
2738
2739 switch (actorIndex) {
2740 default:
2741 break;
2742
2743 case 13:
2744 _vm->setGameFlag(kGameFlag319);
2745 _vm->clearGameFlag(kGameFlag235);
2746 break;
2747
2748 case 14:
2749 _vm->setGameFlag(kGameFlag320);
2750 _vm->clearGameFlag(kGameFlag235);
2751 break;
2752
2753 case 15:
2754 if (getScene()->getActor(16)->checkCrowDeath()) {
2755 _vm->setGameFlag(kGameFlag321);
2756 _vm->clearGameFlag(kGameFlag235);
2757 }
2758 break;
2759
2760 case 16:
2761 if (getScene()->getActor(15)->checkCrowDeath()) {
2762 _vm->setGameFlag(kGameFlag321);
2763 _vm->clearGameFlag(kGameFlag235);
2764 }
2765 break;
2766
2767 case 17:
2768 if (getScene()->getActor(21)->checkCrowDeath()) {
2769 _vm->setGameFlag(kGameFlag322);
2770 _vm->clearGameFlag(kGameFlag235);
2771 }
2772 break;
2773
2774 case 18:
2775 if (getScene()->getActor(19)->checkCrowDeath() && getScene()->getActor(20)->checkCrowDeath()) {
2776 _vm->setGameFlag(kGameFlag323);
2777 _vm->clearGameFlag(kGameFlag235);
2778 }
2779 break;
2780
2781 case 19:
2782 if (getScene()->getActor(18)->checkCrowDeath() && getScene()->getActor(20)->checkCrowDeath()) {
2783 _vm->setGameFlag(kGameFlag323);
2784 _vm->clearGameFlag(kGameFlag235);
2785 }
2786 break;
2787
2788 case 20:
2789 if (getScene()->getActor(19)->checkCrowDeath() && getScene()->getActor(18)->checkCrowDeath()) {
2790 _vm->setGameFlag(kGameFlag323);
2791 _vm->clearGameFlag(kGameFlag235);
2792 }
2793 break;
2794
2795 case 21:
2796 if (getScene()->getActor(17)->checkCrowDeath()) {
2797 _vm->setGameFlag(kGameFlag322);
2798 _vm->clearGameFlag(kGameFlag235);
2799 }
2800 break;
2801 }
2802
2803 otherActor->changeStatus(kActorStatusRestarting);
2804 getSound()->playSound(getWorld()->soundResourceIds[2], false, Config.sfxVolume - 10);
2805 }
2806 }
2807
2808 if (actorIndex == 11)
2809 checkScareCrowDeath();
2810 }
2811
2812 if (_frameIndex >= _frameCount) {
2813 _frameIndex = 0;
2814 changeStatus(kActorStatusEnabled2);
2815 }
2816 }
2817
checkScareCrowDeath()2818 void Actor::checkScareCrowDeath() {
2819 // we are the current player
2820 Actor *actor11 = getScene()->getActor(11);
2821 Actor *actor40 = getScene()->getActor(40);
2822
2823 Common::Point point(_point1.x + _point2.x, _point1.y + _point2.y);
2824 Common::Point point11(actor11->getPoint1()->x + actor11->getPoint2()->x, actor11->getPoint1()->y + actor11->getPoint2()->y);
2825
2826 if (actor11->getStatus() == kActorStatusAttacking && euclidianDistance(point, point11) < 100) {
2827 Actor *actor = getScene()->getActor(getSharedData()->getChapter2ActorIndex());
2828
2829 actor40->show();
2830 actor40->setFrameIndex(0);
2831 actor40->getPoint1()->x = actor->getPoint1()->x;
2832 actor40->getPoint1()->y = actor->getPoint1()->y;
2833
2834 if (actor11->getFrameIndex() <= 7) {
2835 getSound()->playSound(getWorld()->soundResourceIds[9], false, Config.sfxVolume - 10);
2836 } else if (getSharedData()->getChapter2Counter(5) <= 6) {
2837 getSound()->playSound(getWorld()->soundResourceIds[9], false, Config.sfxVolume - 10);
2838 } else {
2839 getScene()->getActor(11)->changeStatus(kActorStatusRestarting);
2840 getSound()->playSound(getWorld()->soundResourceIds[10], false, Config.sfxVolume - 10);
2841 }
2842 }
2843 }
2844
checkCrowDeath()2845 bool Actor::checkCrowDeath() {
2846 return (!isVisible() || _status == kActorStatusRestarting);
2847 }
2848
ScareCrowAttacks()2849 void Actor::ScareCrowAttacks() {
2850 Actor *player = getScene()->getActor();
2851
2852 Common::Point sum = _point1 + _point2;
2853 Common::Point sumPlayer = *player->getPoint1() + *player->getPoint2();
2854
2855 Common::Rect rect;
2856 getCrowStrikeZone(&rect, _direction, sum);
2857
2858 switch (_frameIndex) {
2859 default:
2860 break;
2861
2862 case 1:
2863 getSound()->playSound(getWorld()->soundResourceIds[4], false, Config.sfxVolume - 10);
2864 break;
2865
2866 case 9:
2867 getSharedData()->setChapter2FrameIndexOffset(1);
2868 getSharedData()->setChapter2Counter(8, getSharedData()->getChapter2Counter(8) + 1);
2869 break;
2870
2871 case 11:
2872 if (getSharedData()->getChapter2Counter(8) >= 3)
2873 getSharedData()->setChapter2Counter(8, 0);
2874 else
2875 getSharedData()->setChapter2FrameIndexOffset(-1);
2876 break;
2877 }
2878
2879 _frameIndex += getSharedData()->getChapter2FrameIndexOffset();
2880
2881 Common::Point actionPoint = sum;
2882 actionPoint.x += pointInRectXAdjust(rect, sumPlayer);
2883 actionPoint.y += pointInRectYAdjust(rect, sumPlayer);
2884
2885 if (getScene()->getActor(11)->getFrameIndex() < 8
2886 && getScene()->findActionArea(kActionAreaType2, actionPoint) != -1
2887 && !actorsIntersect(10, 11))
2888 _point1 = actionPoint - _point2;
2889
2890 if (_frameIndex != 8 || _status == kActorStatusGettingHurt) { /* FIXME the status test seems useless */
2891 if (_frameIndex > _frameCount - 1) {
2892 _frameIndex = 0;
2893
2894 if (!getSharedData()->getFlag(kFlagActorUpdateStatus15Check)) {
2895 changeStatus(kActorStatusWalking2);
2896 } else {
2897 getSharedData()->setFlag(kFlagActorUpdateStatus15Check, false);
2898 getScene()->getActor(11)->changeStatus(kActorStatus18);
2899 }
2900 }
2901 } else {
2902 if (rect.contains(sumPlayer)) {
2903
2904 _vm->clearGameFlag(kGameFlag263);
2905 _vm->clearGameFlag(kGameFlag264);
2906 _vm->clearGameFlag(kGameFlag265);
2907 _vm->clearGameFlag(kGameFlag266);
2908 _vm->clearGameFlag(kGameFlag267);
2909 _vm->clearGameFlag(kGameFlag268);
2910 _vm->clearGameFlag(kGameFlag269);
2911
2912 player->stopWalking();
2913 player->changeStatus(kActorStatusGettingHurt);
2914 MaxGetsHit();
2915
2916 getSpeech()->playPlayer(52);
2917
2918 _vm->setGameFlag(kGameFlag219);
2919
2920 } else {
2921 if ((abs(sum.y - sumPlayer.y) + abs(sum.x - sumPlayer.x)) < 100) {
2922 getSharedData()->setChapter2Counter(7, 5);
2923 getSharedData()->setFlag(kFlagActorUpdateStatus15Check, true);
2924 }
2925 }
2926 }
2927 }
2928
actorsIntersect(ActorIndex actorIndex1,ActorIndex actorIndex2)2929 bool Actor::actorsIntersect(ActorIndex actorIndex1, ActorIndex actorIndex2) {
2930 Actor *actor1 = getScene()->getActor(actorIndex1);
2931 Actor *actor2 = getScene()->getActor(actorIndex2);
2932
2933 if (actor1->getField944())
2934 return false;
2935
2936 if (actor2->getField944())
2937 return false;
2938
2939 int16 actor2_x = actor2->getPoint1()->x + actor2->getPoint2()->x;
2940 int16 actor2_y = actor2->getPoint1()->y + actor2->getPoint2()->y;
2941
2942 Common::Point pt1((int16)(actor2_x - (actor1->getField948() + 10)), (int16)(actor2_y - (actor1->getField94C() + 10)));
2943 Common::Point pt2((int16)(actor2_x + actor1->getField948() + 10), (int16)(actor2_y + actor1->getField94C() + 10));
2944 Common::Point pt3((int16)(actor2_x - (actor2->getField948() + 25)), (int16)(actor2_y - (actor2->getField94C() + 20)));
2945 Common::Point pt4((int16)(actor2_x + 2 * actor2->getField948() + 25), (int16)(actor2_y + 2 * actor2->getField94C() + 20));
2946
2947 return getScene()->rectIntersect(pt1.x, pt1.y, pt2.x, pt2.y, pt3.x, pt3.y, pt4.x, pt4.y);
2948 }
2949
TentacleWhips()2950 void Actor::TentacleWhips() {
2951 Actor *actor0 = getScene()->getActor(0);
2952
2953 // Update vectors
2954 getSharedData()->vector1.x = actor0->getPoint1()->x + actor0->getPoint2()->x;
2955 getSharedData()->vector1.y = actor0->getPoint1()->y + actor0->getPoint2()->y - 5;
2956
2957 getSharedData()->vector2.x = getPoint1()->x + getPoint2()->x;
2958 getSharedData()->vector2.y = getPoint1()->y + getPoint2()->y;
2959
2960 TentacleBlocksSarah(getSharedData()->vector1, getSharedData()->vector2);
2961
2962 ++_frameIndex;
2963 if (_frameIndex >= _frameCount)
2964 changeStatus(kActorStatusEnabled2);
2965
2966 if (_frameIndex == 14) {
2967 if (Actor::euclidianDistance(getSharedData()->vector1, getSharedData()->vector2) < 75) {
2968
2969 actor0->changeStatus(kActorStatusGettingHurt);
2970 ++getWorld()->field_E848C;
2971
2972 getSound()->stop(getWorld()->soundResourceIds[3]);
2973 getSound()->stop(getWorld()->soundResourceIds[4]);
2974 getSound()->stop(getWorld()->soundResourceIds[5]);
2975
2976 getSpeech()->playPlayer(131);
2977 }
2978 }
2979 }
2980
SarahAttacks()2981 void Actor::SarahAttacks() {
2982 _frameIndex++;
2983
2984 if (_frameIndex == 17) {
2985 getSpeech()->playPlayer(130);
2986
2987 if (getWorld()->field_E849C >= 666) {
2988 if (_vm->isGameFlagSet(kGameFlag583)) {
2989 _vm->setGameFlag(kGameFlag582);
2990 _vm->clearGameFlag(kGameFlag565);
2991 ++getWorld()->field_E8518;
2992 getSound()->playSound(getWorld()->soundResourceIds[2]);
2993 }
2994 } else {
2995 Actor *actor2 = getScene()->getActor(getWorld()->field_E849C);
2996
2997 double diffX = (actor2->getPoint1()->x + actor2->getPoint2()->x) - (_point1.x + _point2.x);
2998 double diffY = (actor2->getPoint1()->y + actor2->getPoint2()->y) - (_point1.y + _point2.y);
2999
3000 if (sqrt(diffX * diffX + diffY * diffY) < 75.0f
3001 && (actor2->getStatus() == kActorStatusEnabled2 || actor2->getStatus() == kActorStatusAttacking)) {
3002 getSound()->playSound(getWorld()->soundResourceIds[2]);
3003
3004 switch (getWorld()->field_E849C) {
3005 default:
3006 break;
3007
3008 case 10:
3009 _vm->setGameFlag(kGameFlag563);
3010 break;
3011
3012 case 11:
3013 _vm->setGameFlag(kGameFlag724);
3014 break;
3015
3016 case 12:
3017 _vm->setGameFlag(kGameFlag727);
3018 break;
3019
3020 case 13:
3021 _vm->setGameFlag(kGameFlag730);
3022 break;
3023 }
3024
3025 actor2->changeStatus(kActorStatusRestarting);
3026 }
3027 }
3028 }
3029
3030 if (_frameIndex >= _frameCount) {
3031 getCursor()->show();
3032 getSharedData()->setFlag(kFlag1, false);
3033 _frameIndex = 0;
3034 changeStatus(kActorStatusEnabled2);
3035 }
3036 }
3037
MaxGetsSome()3038 void Actor::MaxGetsSome() {
3039 Actor *player = getScene()->getActor();
3040
3041 player->setFrameIndex(player->getFrameIndex() + 1);
3042
3043 if (player->getFrameIndex() > (player->getFrameCount() - 1)) {
3044 if (getSharedData()->getChapter2Counter(6) <= 2) {
3045 player->setFrameIndex(0);
3046 player->changeStatus(kActorStatusEnabled2);
3047 } else {
3048 _vm->clearGameFlag(kGameFlag438);
3049 _vm->clearGameFlag(kGameFlag439);
3050 _vm->clearGameFlag(kGameFlag440);
3051 _vm->clearGameFlag(kGameFlag441);
3052 _vm->clearGameFlag(kGameFlag442);
3053
3054 getSpeech()->playPlayer(53);
3055
3056 _vm->setGameFlag(kGameFlag219);
3057
3058 player->setFrameIndex(0);
3059 player->changeStatus(kActorStatusRestarting);
3060
3061 _vm->clearGameFlag(kGameFlag369);
3062 _vm->clearGameFlag(kGameFlag370);
3063
3064 if (getSound()->isPlaying(getWorld()->soundResourceIds[5]))
3065 getSound()->stop(getWorld()->soundResourceIds[5]);
3066
3067 if (getSound()->isPlaying(getWorld()->soundResourceIds[6]))
3068 getSound()->stop(getWorld()->soundResourceIds[6]);
3069
3070 if (getSound()->isPlaying(getWorld()->soundResourceIds[7]))
3071 getSound()->stop(getWorld()->soundResourceIds[7]);
3072
3073 if (_vm->isGameFlagSet(kGameFlag235)) {
3074 Actor::crowsReturn(_vm);
3075 _vm->clearGameFlag(kGameFlag235);
3076 }
3077 }
3078 }
3079 }
3080
SarahGetsSome()3081 void Actor::SarahGetsSome() {
3082 // We are sure to be the current player
3083 getCursor()->show();
3084 getSharedData()->setFlag(kFlag1, false);
3085
3086 if (_frameIndex != 5 || _vm->isGameFlagNotSet(kGameFlag570))
3087 ++_frameIndex;
3088
3089 if (_frameIndex > _frameCount - 1) {
3090 if (getWorld()->field_E848C >= 3) {
3091 _frameIndex = 0;
3092
3093 getScene()->getActor(0)->changeStatus(kActorStatusRestarting);
3094 getScene()->getActor(1)->setTickCount(_vm->getTick() + 2000);
3095 } else {
3096 getScene()->getActor(0)->changeStatus(kActorStatusEnabled2);
3097 }
3098 }
3099 }
3100
TentacleDies()3101 void Actor::TentacleDies() {
3102 ++_frameIndex;
3103
3104 if (_frameIndex >= _frameCount) {
3105 _frameIndex = 0;
3106 changeStatus(kActorStatusEnabled2);
3107 hide();
3108
3109 if (_vm->getRandomBit() == 1) {
3110 _vm->setGameFlag(kGameFlag219);
3111 getSpeech()->playPlayer(133);
3112 }
3113 }
3114 }
3115
CrowSwoops()3116 void Actor::CrowSwoops() {
3117 Actor *player = getScene()->getActor();
3118
3119 _point1.x = player->getPoint1()->x - (int16)getSharedData()->crowsData[2 * _index + 30];
3120 _point1.y = player->getPoint1()->y - (int16)getSharedData()->crowsData[2 * _index + 31];
3121
3122 _frameIndex++;
3123
3124 if (_frameIndex > _frameCount - 1) {
3125 getSharedData()->crowsData[_index + 61] = 1;
3126 changeStatus(kActorStatusEnabled2);
3127
3128 _point1.y += 54;
3129 getSound()->playSound(getWorld()->soundResourceIds[1], false, Config.sfxVolume - 10);
3130
3131 getSharedData()->crowsData[_index - 2] -= 54;
3132 }
3133 }
3134
ScareCrowRetreats()3135 void Actor::ScareCrowRetreats() {
3136 int32 frameIndex = (int32)_frameIndex;
3137 uint32 distance = (uint32)abs((double)getWalkIncrement(_direction, (_frameIndex < _frameCount) ? _frameIndex : 2 * _frameCount - (_frameIndex + 1)));
3138
3139 getSharedData()->setChapter2Counter(7, getSharedData()->getChapter2Counter(7) + 1);
3140 if (getSharedData()->getChapter2Counter(7) > 14) {
3141 getSharedData()->setChapter2Counter(7, 0);
3142 changeStatus(kActorStatusWalking2);
3143 }
3144
3145 faceTarget((uint32)getSharedData()->getPlayerIndex(), kDirectionFromActor);
3146
3147 Common::Point sum = _point1 + _point2;
3148 if (canMove(&sum, DIR(_direction + 4), distance, false)) {
3149 move(DIR(_direction + 4), distance);
3150 --frameIndex;
3151 } else if (canMove(&sum, DIR(_direction + 5), distance, false)) {
3152 move(DIR(_direction + 5), distance);
3153 --frameIndex;
3154 } else if (canMove(&sum, DIR(_direction + 3), distance, false)) {
3155 move(DIR(_direction + 3), distance);
3156 --frameIndex;
3157 } else if (canMove(&sum, DIR(_direction + 6), distance, false)) {
3158 move(DIR(_direction + 6), distance);
3159 --frameIndex;
3160 } else if (canMove(&sum, DIR(_direction + 2), distance, false)) {
3161 move(DIR(_direction + 2), distance);
3162 --frameIndex;
3163 }
3164
3165 if (frameIndex < 0)
3166 _frameIndex = _frameCount - 1;
3167 else
3168 _frameIndex = (uint32)frameIndex;
3169 }
3170
updateStatusMorphing()3171 void Actor::updateStatusMorphing() {
3172 if (_resourceId == getWorld()->graphicResourceIds[3] || _resourceId == getWorld()->graphicResourceIds[4] || _resourceId == getWorld()->graphicResourceIds[5]) {
3173 if (_frameIndex < _frameCount - 1) {
3174 ++_frameIndex;
3175
3176 if (_frameIndex == _frameCount / 2) {
3177 getWorld()->currentPaletteId = getWorld()->graphicResourceIds[getWorld()->nextPlayer - 1];
3178 getScreen()->setPalette(getWorld()->currentPaletteId);
3179 getScreen()->setGammaLevel(getWorld()->currentPaletteId);
3180 }
3181
3182 return;
3183 }
3184 } else {
3185 if (_frameIndex > 0) {
3186 --_frameIndex;
3187
3188 if (_frameIndex == _frameCount / 2)
3189 getScreen()->setPalette(getWorld()->graphicResourceIds[getWorld()->nextPlayer - 1]);
3190
3191 getWorld()->currentPaletteId = getWorld()->graphicResourceIds[getWorld()->nextPlayer - 1];
3192 getScreen()->setGammaLevel(getWorld()->currentPaletteId);
3193 return;
3194 }
3195 }
3196
3197 getScene()->changePlayer(getWorld()->nextPlayer);
3198 changeStatus(kActorStatusEnabled);
3199 getWorld()->nextPlayer = kActorInvalid;
3200 }
3201
actionAreaCheck()3202 void Actor::actionAreaCheck() {
3203 if (_field_944 == 4 || !isVisible())
3204 return;
3205
3206 int32 areaIndex = getScene()->findActionArea(kActionAreaType1, Common::Point((int16)(_point1.x + _point2.x), (int16)(_point1.y + _point2.y)));
3207 if (areaIndex == _actionIdx3 || areaIndex == -1)
3208 return;
3209
3210 ActionArea *area = getWorld()->actions[areaIndex];
3211 ActionArea *actorArea = getWorld()->actions[_actionIdx3];
3212
3213 if ((area->flags & 1) && !getSharedData()->getFlag(kFlagSkipScriptProcessing)) {
3214 debugC(kDebugLevelScripts, "[Script] Entered ActionArea (idx: %d, name: %s)", areaIndex, area->name);
3215 debugC(kDebugLevelScripts, "[Script] Queuing Script #1 (idx: %d) for Actor (idx: %d)", actorArea->scriptIndex2, _index);
3216 getScript()->queueScript(actorArea->scriptIndex2, _index);
3217 debugC(kDebugLevelScripts, "[Script] Queuing Script #2 (idx: %d) for Actor (idx: %d)", area->scriptIndex, _index);
3218 getScript()->queueScript(area->scriptIndex, _index);
3219 }
3220
3221 if (!area->paletteResourceId || area->paletteResourceId == actorArea->paletteResourceId || _index) {
3222 if (area->paletteResourceId != actorArea->paletteResourceId && !_index)
3223 _vm->screen()->startPaletteFade(getWorld()->currentPaletteId, 100, 3);
3224
3225 _actionIdx3 = areaIndex;
3226 } else {
3227 _vm->screen()->startPaletteFade(area->paletteResourceId, 50, 3);
3228 _actionIdx3 = areaIndex;
3229 }
3230 }
3231
TentacleBlocksSarah(const Common::Point & vec1,Common::Point vec2)3232 void Actor::TentacleBlocksSarah(const Common::Point &vec1, Common::Point vec2) {
3233 if (getScene()->getActor(1)->isVisible())
3234 return;
3235
3236 uint32 diffY = (uint32)abs(vec2.y - vec1.y);
3237 if (diffY > 5)
3238 diffY = 5;
3239
3240 if (diffY == 0)
3241 return;
3242
3243 ActorDirection dir = (vec1.y > vec2.y) ? kDirectionS : kDirectionN;
3244
3245 if (canMove(&vec2, dir, diffY + 3, false))
3246 incPosition(dir, (int16)(diffY - 1), &_point1);
3247 }
3248
SarahDies()3249 void Actor::SarahDies() {
3250 getCursor()->hide();
3251 getScene()->getActor(0)->hide();
3252 getScene()->getActor(1)->setFrameIndex(0);
3253
3254 getWorld()->tickCount1 = _vm->getTick() + 3000;
3255 }
3256
updateNumbers(uint item,const Common::Point & point)3257 void Actor::updateNumbers(uint item, const Common::Point &point) {
3258 if (item != 1)
3259 return;
3260
3261 _numberPoint.x = point.x;
3262 _numberPoint.y = point.y + 8;
3263 _numberStringWidth = 40;
3264 snprintf(_numberString01, sizeof(_numberString01), "%d", _numberValue01);
3265
3266 _numberFlag01 = 1;
3267 }
3268
3269 //////////////////////////////////////////////////////////////////////////
3270 // Path finding functions
3271 //////////////////////////////////////////////////////////////////////////
findLeftPath(Common::Point source,const Common::Point & destination,Common::Array<int> * actions)3272 bool Actor::findLeftPath(Common::Point source, const Common::Point &destination, Common::Array<int> *actions) {
3273 // Reset pathfinding data
3274 _data.count = 0;
3275 _data.current = 0;
3276
3277 bool flag = false;
3278 Common::Point src = source;
3279
3280 for (uint32 i = 0; i < 60; i++) {
3281
3282 // Note: this is handled differently from other tryDirection functions
3283 // as we break instead of checking the other actors
3284 if (!tryDirection(source, actions, &src, kDirectionE, destination, &flag)
3285 && !tryDirection(source, actions, &src, kDirectionNE, destination, &flag)
3286 && !tryDirection(source, actions, &src, kDirectionSE, destination, &flag)
3287 && !tryDirection(source, actions, &src, kDirectionN, destination, &flag)
3288 && !tryDirection(source, actions, &src, kDirectionS, destination, &flag))
3289 break;
3290
3291 // Update source point after all processing
3292 source = src;
3293
3294 if (flag)
3295 return true;
3296 }
3297
3298 return false;
3299 }
3300
findRightPath(Common::Point source,const Common::Point & destination,Common::Array<int> * actions)3301 bool Actor::findRightPath(Common::Point source, const Common::Point &destination, Common::Array<int> *actions) {
3302 // Reset pathfinding data
3303 _data.count = 0;
3304 _data.current = 0;
3305
3306 bool flag = false;
3307 Common::Point src = source;
3308
3309 for (uint32 i = 0; i < 60; i++) {
3310 if (!tryDirection(source, actions, &src, kDirectionW, destination, &flag)
3311 && !tryDirection(source, actions, &src, kDirectionNW, destination, &flag)
3312 && !tryDirection(source, actions, &src, kDirectionSW, destination, &flag)) {
3313 if (src.y <= destination.y) {
3314 if (!tryDirection(source, actions, &src, kDirectionS, destination, &flag)
3315 && !tryDirection(source, actions, &src, kDirectionN, destination, &flag)
3316 && !tryDirection(source, actions, &src, kDirectionSE, destination, &flag)
3317 && !tryDirection(source, actions, &src, kDirectionE, destination, &flag)
3318 && !tryDirection(source, actions, &src, kDirectionNE, destination, &flag))
3319 continue;
3320 } else {
3321 if (!tryDirection(source, actions, &src, kDirectionN, destination, &flag)
3322 && !tryDirection(source, actions, &src, kDirectionS, destination, &flag)
3323 && !tryDirection(source, actions, &src, kDirectionNE, destination, &flag)
3324 && !tryDirection(source, actions, &src, kDirectionE, destination, &flag)
3325 && !tryDirection(source, actions, &src, kDirectionSE, destination, &flag))
3326 continue;
3327 }
3328 }
3329
3330 // Update source point after all processing
3331 source = src;
3332
3333 if (flag)
3334 return true;
3335 }
3336
3337 return false;
3338 }
3339
findUpPath(Common::Point source,const Common::Point & destination,Common::Array<int> * actions)3340 bool Actor::findUpPath(Common::Point source, const Common::Point &destination, Common::Array<int> *actions) {
3341 // Reset pathfinding data
3342 _data.count = 0;
3343 _data.current = 0;
3344
3345 bool flag = false;
3346 Common::Point src = source;
3347
3348 for (uint32 i = 0; i < 60; i++) {
3349 if (!tryDirection(source, actions, &src, kDirectionN, destination, &flag)) {
3350 if (src.x >= destination.x) {
3351 if (!tryDirection(source, actions, &src, kDirectionNW, destination, &flag)
3352 && !tryDirection(source, actions, &src, kDirectionW, destination, &flag)
3353 && !tryDirection(source, actions, &src, kDirectionNE, destination, &flag)
3354 && !tryDirection(source, actions, &src, kDirectionE, destination, &flag))
3355 continue;
3356 } else {
3357 if (!tryDirection(source, actions, &src, kDirectionNE, destination, &flag)
3358 && !tryDirection(source, actions, &src, kDirectionE, destination, &flag)
3359 && !tryDirection(source, actions, &src, kDirectionNW, destination, &flag)
3360 && !tryDirection(source, actions, &src, kDirectionW, destination, &flag))
3361 continue;
3362 }
3363 }
3364
3365 // Update source point after all processing
3366 source = src;
3367
3368 if (flag)
3369 return true;
3370 }
3371
3372 return false;
3373 }
3374
findDownPath(Common::Point source,const Common::Point & destination,Common::Array<int> * actions)3375 bool Actor::findDownPath(Common::Point source, const Common::Point &destination, Common::Array<int> *actions) {
3376 // Reset pathfinding data
3377 _data.count = 0;
3378 _data.current = 0;
3379
3380 bool flag = false;
3381 Common::Point src = source;
3382
3383 for (uint32 i = 0; i < 60; i++) {
3384 if (!tryDirection(source, actions, &src, kDirectionS, destination, &flag)
3385 && !tryDirection(source, actions, &src, kDirectionSE, destination, &flag)
3386 && !tryDirection(source, actions, &src, kDirectionSW, destination, &flag)
3387 && !tryDirection(source, actions, &src, kDirectionE, destination, &flag)
3388 && !tryDirection(source, actions, &src, kDirectionW, destination, &flag))
3389 continue;
3390
3391 // Update source point after all processing
3392 source = src;
3393
3394 if (flag)
3395 return true;
3396 }
3397
3398 return false;
3399 }
3400
tryDirection(const Common::Point & source,Common::Array<int> * actions,Common::Point * point,ActorDirection direction,const Common::Point & destination,bool * flag)3401 bool Actor::tryDirection(const Common::Point &source, Common::Array<int> *actions, Common::Point *point, ActorDirection direction, const Common::Point &destination, bool *flag) {
3402 Common::Point sign;
3403 Common::Point src = source;
3404 uint32 frameNumber = _frameNumber;
3405
3406 switch (direction) {
3407 default:
3408 return false;
3409
3410 case kDirectionN:
3411 sign.y = -1;
3412 break;
3413
3414 case kDirectionNW:
3415 sign.x = -1;
3416 sign.y = -1;
3417 break;
3418
3419 case kDirectionW:
3420 sign.x = -1;
3421 break;
3422
3423 case kDirectionSW:
3424 sign.x = -1;
3425 sign.y = 1;
3426 break;
3427
3428 case kDirectionS:
3429 sign.y = 1;
3430 break;
3431
3432 case kDirectionSE:
3433 sign.x = 1;
3434 sign.y = 1;
3435 break;
3436
3437 case kDirectionE:
3438 sign.x = 1;
3439 break;
3440
3441 case kDirectionNE:
3442 sign.x = 1;
3443 sign.y = -1;
3444 break;
3445 }
3446
3447 for (int i = 0; i < 10; i++) {
3448 if (!testPolyInLink(src, actions))
3449 break;
3450
3451 int32 dist = getStride(direction, frameNumber);
3452 src.x += (int16)(sign.x * dist);
3453 src.y += (int16)(sign.y * dist);
3454
3455 if (abs(src.x - destination.x) >= getStride(kDirectionW, frameNumber)) {
3456 if (abs(src.y - destination.y) < getStride(kDirectionN, frameNumber)) {
3457
3458 if (src.x >= destination.x) {
3459 if (canGetToDest(actions, src, kDirectionW, src.x - destination.x)) {
3460 *flag = true;
3461 *point = src;
3462
3463 _data.points[_data.count] = src;
3464 _data.directions[_data.count] = direction;
3465 _data.count++;
3466
3467 _data.points[_data.count] = destination;
3468 _data.directions[_data.count] = kDirectionW;
3469 _data.count++;
3470
3471 return true;
3472 }
3473 } else {
3474 if (canGetToDest(actions, src, kDirectionE, destination.x - src.x)) {
3475 *flag = true;
3476 *point = src;
3477
3478 _data.points[_data.count] = src;
3479 _data.directions[_data.count] = direction;
3480 _data.count++;
3481
3482 _data.points[_data.count] = destination;
3483 _data.directions[_data.count] = kDirectionE;
3484 _data.count++;
3485
3486 return true;
3487 }
3488 }
3489 }
3490 } else {
3491 if (src.y >= destination.y) {
3492 if (canGetToDest(actions, src, kDirectionN, src.y - destination.y)) {
3493 *flag = true;
3494 *point = src;
3495
3496 _data.points[_data.count] = src;
3497 _data.directions[_data.count] = direction;
3498 _data.count++;
3499
3500 _data.points[_data.count] = destination;
3501 _data.directions[_data.count] = kDirectionN;
3502 _data.count++;
3503
3504 return true;
3505 }
3506 } else {
3507 if (canGetToDest(actions, src, kDirectionS, destination.y - src.y)) {
3508 *flag = true;
3509 *point = src;
3510
3511 _data.points[_data.count] = src;
3512 _data.directions[_data.count] = direction;
3513 _data.count++;
3514
3515 _data.points[_data.count] = destination;
3516 _data.directions[_data.count] = kDirectionS;
3517 _data.count++;
3518
3519 return true;
3520 }
3521 }
3522 }
3523
3524 // Proceed to next frame
3525 ++frameNumber;
3526
3527 if (frameNumber >= _frameCount)
3528 frameNumber = 0;
3529 }
3530
3531 // Check if we need to process
3532 int32 distance = getStride(direction, frameNumber);
3533 if (source.x == (src.x - sign.x * distance) && source.y == (src.y - sign.y * distance))
3534 return false;
3535
3536 if (!getWorld()->sceneRects[getWorld()->sceneRectIdx].contains(src))
3537 return false;
3538
3539 // Update frame and setup pathfinding
3540 _frameNumber = frameNumber;
3541
3542 if (_frameNumber == 0)
3543 distance = getStride(direction, _frameCount - 1);
3544 else
3545 distance = getStride(direction, _frameNumber - 1);
3546
3547 src.x -= (int16)(sign.x * distance);
3548 src.y -= (int16)(sign.y * distance);
3549
3550 *point = src;
3551 _data.points[_data.count] = src;
3552 _data.directions[_data.count] = direction;
3553 _data.count++;
3554
3555 return true;
3556 }
3557
canGetToDest(Common::Array<int> * actions,const Common::Point & point,ActorDirection direction,int16 loopcount)3558 bool Actor::canGetToDest(Common::Array<int> *actions, const Common::Point &point, ActorDirection direction, int16 loopcount) {
3559 if (loopcount <= 1)
3560 return true;
3561
3562 // Initialize base coordinates
3563 Common::Point delta = Common::Point(deltaPointsArray[direction][0], deltaPointsArray[direction][1]);
3564 Common::Point basePoint = delta + point;
3565 Common::Rect rect = getWorld()->sceneRects[getWorld()->sceneRectIdx];
3566
3567 for (int16 i = 1; i < loopcount; i++) {
3568 if (!testPolyInLink(basePoint, actions))
3569 return false;
3570
3571 if (!rect.contains(basePoint))
3572 return false;
3573
3574 basePoint += delta;
3575 }
3576
3577 return true;
3578 }
3579
testPolyInLink(const Common::Point & pt,Common::Array<int> * actions)3580 bool Actor::testPolyInLink(const Common::Point &pt, Common::Array<int> *actions) {
3581 if (actions->size() == 0)
3582 return false;
3583
3584 for (Common::Array<int>::iterator it = actions->begin(); it != actions->end(); it++) {
3585 if (isInActionArea(pt, getWorld()->actions[*it]))
3586 return true;
3587 }
3588
3589 return false;
3590
3591 }
3592
isInActionArea(const Common::Point & pt,ActionArea * area)3593 bool Actor::isInActionArea(const Common::Point &pt, ActionArea *area) {
3594 Common::Rect sceneRect = getWorld()->sceneRects[getWorld()->sceneRectIdx];
3595
3596 if (!sceneRect.contains(pt))
3597 return false;
3598
3599 if (!(area->flags & 1))
3600 return false;
3601
3602 // Check flags
3603 bool found = false;
3604 for (uint32 i = 0; i < 10; i++) {
3605 int32 flag = area->flagNums[i];
3606 bool state = (flag <= 0) ? _vm->isGameFlagNotSet((GameFlag)-flag) : _vm->isGameFlagSet((GameFlag)flag);
3607
3608 if (!state) {
3609 found = true;
3610 break;
3611 }
3612 }
3613
3614 if (found)
3615 return false;
3616
3617 Polygon poly = getScene()->polygons()->get(area->polygonIndex);
3618 if (!poly.contains(pt))
3619 return false;
3620
3621 return true;
3622 }
3623
3624 //////////////////////////////////////////////////////////////////////////
3625 // Misc
3626 //////////////////////////////////////////////////////////////////////////
3627
setVisible(bool value)3628 void Actor::setVisible(bool value) {
3629 if (value)
3630 flags |= kActorFlagVisible;
3631 else
3632 flags &= ~kActorFlagVisible;
3633
3634 stopSound();
3635 }
3636
isOnScreen()3637 bool Actor::isOnScreen() {
3638 Common::Rect scene(getWorld()->xLeft, getWorld()->yTop, getWorld()->xLeft + 640, getWorld()->yTop + 480);
3639 Common::Rect actor(_boundingRect);
3640 actor.translate(_point1.x, _point1.y);
3641
3642 return isVisible() && scene.intersects(actor);
3643 }
3644
setVolume()3645 void Actor::setVolume() {
3646 if (!_soundResourceId || !getSound()->isPlaying(_soundResourceId))
3647 return;
3648
3649 // Compute volume
3650 int32 volume = Config.voiceVolume + getSound()->calculateVolumeAdjustement(_point1 + _point2, _field_968, 0);
3651 if (volume < -10000)
3652 volume = -10000;
3653
3654 getSound()->setVolume(_soundResourceId, volume);
3655 }
3656
3657 //////////////////////////////////////////////////////////////////////////
3658 // Helper methods
3659 //////////////////////////////////////////////////////////////////////////
3660
getAngle(const Common::Point & vec1,const Common::Point & vec2)3661 ActorDirection Actor::getAngle(const Common::Point &vec1, const Common::Point &vec2) {
3662 int32 diffX = (vec2.x << 16) - (vec1.x << 16);
3663 int32 diffY = (vec1.y << 16) - (vec2.y << 16);
3664 int32 adjust = 0;
3665
3666 if (diffX < 0) {
3667 adjust = 2;
3668 diffX = -diffX;
3669 }
3670
3671 if (diffY < 0) {
3672 adjust |= 1;
3673 diffY = -diffY;
3674 }
3675
3676 int32 dirAngle = -1;
3677
3678 if (diffX) {
3679 uint32 index = (uint32)((diffY * 256) / diffX);
3680
3681 if (index < 256)
3682 dirAngle = angleTable01[index];
3683 else if (index < 4096)
3684 dirAngle = angleTable02[index / 16];
3685 else if (index < 65536)
3686 dirAngle = angleTable03[index / 256];
3687 else
3688 dirAngle = 90;
3689 } else {
3690 dirAngle = 90;
3691 }
3692
3693 switch (adjust) {
3694 default:
3695 break;
3696
3697 case 1:
3698 dirAngle = 360 - dirAngle;
3699 break;
3700
3701 case 2:
3702 dirAngle = 180 - dirAngle;
3703 break;
3704
3705 case 3:
3706 dirAngle += 180;
3707 break;
3708 }
3709
3710 if (dirAngle >= 360)
3711 dirAngle -= 360;
3712
3713 ActorDirection dir;
3714
3715 if (dirAngle < 157 || dirAngle >= 202) {
3716 if (dirAngle < 112 || dirAngle >= 157) {
3717 if (dirAngle < 67 || dirAngle >= 112) {
3718 if (dirAngle < 22 || dirAngle >= 67) {
3719 if ((dirAngle < 0 || dirAngle >= 22) && (dirAngle < 337 || dirAngle > 359)) {
3720 if (dirAngle < 292 || dirAngle >= 337) {
3721 if (dirAngle < 247 || dirAngle >= 292) {
3722 if (dirAngle < 202 || dirAngle >= 247) {
3723 error("[Actor::direction] got a bad direction angle: %d!", dirAngle);
3724 } else {
3725 dir = kDirectionSW;
3726 }
3727 } else {
3728 dir = kDirectionS;
3729 }
3730 } else {
3731 dir = kDirectionSE;
3732 }
3733 } else {
3734 dir = kDirectionE;
3735 }
3736 } else {
3737 dir = kDirectionNE;
3738 }
3739 } else {
3740 dir = kDirectionN;
3741 }
3742 } else {
3743 dir = kDirectionNW;
3744 }
3745 } else {
3746 dir = kDirectionW;
3747 }
3748
3749 return dir;
3750 }
3751
updateGraphicData(uint32 offset)3752 void Actor::updateGraphicData(uint32 offset) {
3753 int32 index = ((_direction > kDirectionS) ? kDirection8 - _direction : _direction) + (int32)offset;
3754 _resourceId = _graphicResourceIds[index];
3755 _frameCount = GraphicResource::getFrameCount(_vm, _resourceId);
3756 _frameIndex = 0;
3757 }
3758
canChangeStatus(int index) const3759 bool Actor::canChangeStatus(int index) const {
3760 return _graphicResourceIds[index] != _graphicResourceIds[5];
3761 }
3762
adjustCoordinates(Common::Point * point)3763 void Actor::adjustCoordinates(Common::Point *point) {
3764 if (!point)
3765 error("[Actor::adjustCoordinates] Invalid point parameter!");
3766
3767 point->x = _point1.x - getWorld()->xLeft;
3768 point->y = _point1.y - getWorld()->yTop;
3769 }
3770
getGraphicsFlags()3771 DrawFlags Actor::getGraphicsFlags() {
3772 if (getWorld()->chapter == kChapter11) {
3773 int res = strcmp((char *)&_name, "Dead Sarah");
3774
3775 if (res == 0)
3776 return kDrawFlagNone;
3777 }
3778
3779 if (_direction < kDirectionSE)
3780 return kDrawFlagNone;
3781
3782 return kDrawFlagMirrorLeftRight;
3783 }
3784
getStride(ActorDirection dir,uint32 frameIndex) const3785 int32 Actor::getStride(ActorDirection dir, uint32 frameIndex) const {
3786 if (frameIndex >= ARRAYSIZE(_distancesNS))
3787 debugC(kDebugLevelMain, "[Actor::getStride] Invalid frame index (was: %d, max: %d)", _frameIndex, ARRAYSIZE(_distancesNS) - 1);
3788
3789 switch (dir) {
3790 default:
3791 error("[Actor::getStride] Invalid direction");
3792
3793 case kDirectionN:
3794 case kDirectionS:
3795 // XXX it seems that the original allows frameIndex to be out of range
3796 return (frameIndex < ARRAYSIZE(_distancesNS) ? _distancesNS[frameIndex] : _distancesNSEO[frameIndex % ARRAYSIZE(_distancesNS)]);
3797
3798 case kDirectionNW:
3799 case kDirectionSW:
3800 case kDirectionSE:
3801 case kDirectionNE:
3802 return _distancesNSEO[frameIndex];
3803
3804 case kDirectionW:
3805 case kDirectionE:
3806 return _distancesEO[frameIndex];
3807 }
3808 }
3809
getWalkIncrement(ActorDirection dir,uint32 frameIndex) const3810 int32 Actor::getWalkIncrement(ActorDirection dir, uint32 frameIndex) const {
3811 if (frameIndex >= ARRAYSIZE(_distancesNS))
3812 error("[Actor::getWalkIncrement] Invalid frame index (was: %d, max: %d)", _frameIndex, ARRAYSIZE(_distancesNS) - 1);
3813
3814 switch (dir) {
3815 default:
3816 error("[Actor::getWalkIncrement] Invalid direction");
3817
3818 case kDirectionN:
3819 return -_distancesNS[frameIndex];
3820
3821 case kDirectionS:
3822 return _distancesNS[frameIndex];
3823
3824 case kDirectionNW:
3825 return -_distancesNSEO[frameIndex];
3826
3827 case kDirectionNE:
3828 return -_distancesNSEO[frameIndex];
3829
3830 case kDirectionSW:
3831 return _distancesNSEO[frameIndex];
3832
3833 case kDirectionSE:
3834 return _distancesNSEO[frameIndex];
3835
3836 case kDirectionW:
3837 return -_distancesEO[frameIndex];
3838
3839 case kDirectionE:
3840 return _distancesEO[frameIndex];
3841 }
3842 }
3843
incPosition(ActorDirection direction,int16 delta,Common::Point * point)3844 void Actor::incPosition(ActorDirection direction, int16 delta, Common::Point *point) {
3845 if (!point)
3846 error("[Actor::incPosition] Invalid point (NULL)!");
3847
3848 switch (direction) {
3849 default:
3850 break;
3851
3852 case kDirectionN:
3853 point->y -= delta;
3854 break;
3855
3856 case kDirectionNW:
3857 point->x -= delta;
3858 point->y -= delta;
3859 break;
3860
3861 case kDirectionW:
3862 point->x -= delta;
3863 break;
3864
3865 case kDirectionSW:
3866 point->x -= delta;
3867 point->y += delta;
3868 break;
3869
3870 case kDirectionS:
3871 point->y += delta;
3872 break;
3873
3874 case kDirectionSE:
3875 point->x += delta;
3876 point->y += delta;
3877 break;
3878
3879 case kDirectionE:
3880 point->x += delta;
3881 break;
3882
3883 case kDirectionNE:
3884 point->x += delta;
3885 point->y -= delta;
3886 break;
3887 }
3888 }
3889
euclidianDistance(const Common::Point & point1,const Common::Point & point2)3890 uint32 Actor::euclidianDistance(const Common::Point &point1, const Common::Point &point2) {
3891 return (uint32)sqrt(pow((double)(point2.y - point1.y), 2) + pow((double)(point2.x - point1.x), 2));
3892 }
3893
getAngleOfVector(const Common::Point & vec1,const Common::Point & vec2)3894 int32 Actor::getAngleOfVector(const Common::Point &vec1, const Common::Point &vec2) {
3895 int32 result = (int32)(((long)(180 - acos((double)(vec2.y - vec1.y) / euclidianDistance(vec1, vec2)) * 180 / -M_PI)) % 360);
3896
3897 if (vec1.x < vec2.x)
3898 return 360 - result;
3899
3900 return result;
3901 }
3902
getCrowStrikeZone(Common::Rect * rect,ActorDirection direction,const Common::Point & point)3903 void Actor::getCrowStrikeZone(Common::Rect *rect, ActorDirection direction, const Common::Point &point) {
3904 if (!rect)
3905 error("[Actor::rect] Invalid rect (NULL)!");
3906
3907 switch (direction) {
3908 default:
3909 rect->top = 0;
3910 rect->left = 0;
3911 rect->bottom = 0;
3912 rect->right = 0;
3913 return;
3914
3915 case kDirectionN:
3916 rect->top = point.y - 84;
3917 rect->left = point.x - 9;
3918 break;
3919
3920 case kDirectionNW:
3921 rect->top = point.y - 55;
3922 rect->left = point.x - 84;
3923 break;
3924
3925 case kDirectionW:
3926 rect->top = point.y - 34;
3927 rect->left = point.x - 93;
3928 break;
3929
3930 case kDirectionSW:
3931 rect->top = point.y + 27;
3932 rect->left = point.x - 94;
3933 break;
3934
3935 case kDirectionS:
3936 rect->top = point.y + 41;
3937 rect->left = point.x - 9;
3938 break;
3939
3940 case kDirectionSE:
3941 rect->top = point.y + 27;
3942 rect->left = point.x + 54;
3943 break;
3944
3945 case kDirectionE:
3946 rect->top = point.y - 34;
3947 rect->left = point.x + 53;
3948 break;
3949
3950 case kDirectionNE:
3951 rect->top = point.y - 55;
3952 rect->left = point.x + 44;
3953 break;
3954 }
3955
3956 rect->setWidth(40);
3957 rect->setHeight(40);
3958 }
3959
determineLeftOrRight(const Common::Point & vec1,const Common::Point & vec2)3960 bool Actor::determineLeftOrRight(const Common::Point &vec1, const Common::Point &vec2) {
3961 Common::Point vec3(2289, 171);
3962
3963 int32 diff = getAngleOfVector(vec1, vec3) - getAngleOfVector(vec1, vec2);
3964
3965 if (diff < 0)
3966 diff += 359;
3967
3968 return (diff > 180);
3969 }
3970
pointInRectXAdjust(const Common::Rect & rect,const Common::Point & point)3971 int16 Actor::pointInRectXAdjust(const Common::Rect &rect, const Common::Point &point) {
3972 if (point.x > rect.right)
3973 return 3;
3974
3975 if (point.x < rect.left)
3976 return -3;
3977 else
3978 return 0;
3979 }
3980
pointInRectYAdjust(const Common::Rect & rect,const Common::Point & point)3981 int16 Actor::pointInRectYAdjust(const Common::Rect &rect, const Common::Point &point) {
3982 if (point.y > rect.bottom)
3983 return 3;
3984
3985 if (point.y < rect.top)
3986 return -3;
3987 else
3988 return 0;
3989 }
3990
3991
3992 } // end of namespace Asylum
3993