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 /*
24 * This file is based on WME Lite.
25 * http://dead-code.org/redir.php?target=wmelite
26 * Copyright (c) 2011 Jan Nedoma
27 */
28
29 #include "engines/wintermute/ad/ad_actor.h"
30 #include "engines/wintermute/ad/ad_game.h"
31 #include "engines/wintermute/ad/ad_scene.h"
32 #include "engines/wintermute/ad/ad_entity.h"
33 #include "engines/wintermute/ad/ad_sprite_set.h"
34 #include "engines/wintermute/ad/ad_waypoint_group.h"
35 #include "engines/wintermute/ad/ad_path.h"
36 #include "engines/wintermute/ad/ad_sentence.h"
37 #include "engines/wintermute/base/base_frame.h"
38 #include "engines/wintermute/base/base_parser.h"
39 #include "engines/wintermute/base/sound/base_sound.h"
40 #include "engines/wintermute/base/base_region.h"
41 #include "engines/wintermute/base/base_file_manager.h"
42 #include "engines/wintermute/base/base_sprite.h"
43 #include "engines/wintermute/base/scriptables/script.h"
44 #include "engines/wintermute/base/scriptables/script_value.h"
45 #include "engines/wintermute/base/scriptables/script_stack.h"
46 #include "engines/wintermute/base/particles/part_emitter.h"
47 #include "engines/wintermute/base/base_engine.h"
48
49 namespace Wintermute {
50
IMPLEMENT_PERSISTENT(AdActor,false)51 IMPLEMENT_PERSISTENT(AdActor, false)
52
53
54 //////////////////////////////////////////////////////////////////////////
55 AdActor::AdActor(BaseGame *inGame) : AdTalkHolder(inGame) {
56 _path = new AdPath(_gameRef);
57
58 _type = OBJECT_ACTOR;
59 _dir = DI_LEFT;
60
61 _walkSprite = nullptr;
62 _standSprite = nullptr;
63 _turnLeftSprite = nullptr;
64 _turnRightSprite = nullptr;
65
66 _targetPoint = new BasePoint;
67 _afterWalkDir = DI_NONE;
68
69 _animSprite2 = nullptr;
70
71 setDefaultAnimNames();
72 }
73
74 //////////////////////////////////////////////////////////////////////////
setDefaultAnimNames()75 bool AdActor::setDefaultAnimNames() {
76 _talkAnimName = "talk";
77 _idleAnimName = "idle";
78 _walkAnimName = "walk";
79 _turnLeftAnimName = "turnleft";
80 _turnRightAnimName = "turnright";
81 return STATUS_OK;
82 }
83
84 //////////////////////////////////////////////////////////////////////////
~AdActor()85 AdActor::~AdActor() {
86 delete _path;
87 delete _targetPoint;
88 _path = nullptr;
89 _targetPoint = nullptr;
90
91 delete _walkSprite;
92 delete _standSprite;
93 delete _turnLeftSprite;
94 delete _turnRightSprite;
95 _walkSprite = nullptr;
96 _standSprite = nullptr;
97 _turnLeftSprite = nullptr;
98 _turnRightSprite = nullptr;
99
100 _animSprite2 = nullptr; // ref only
101
102 for (uint32 i = 0; i < _talkSprites.size(); i++) {
103 delete _talkSprites[i];
104 }
105 _talkSprites.clear();
106
107 for (uint32 i = 0; i < _talkSpritesEx.size(); i++) {
108 delete _talkSpritesEx[i];
109 }
110 _talkSpritesEx.clear();
111
112 for (uint32 i = 0; i < _anims.size(); i++) {
113 delete _anims[i];
114 _anims[i] = nullptr;
115 }
116 _anims.clear();
117
118 }
119
120
121 //////////////////////////////////////////////////////////////////////////
loadFile(const char * filename)122 bool AdActor::loadFile(const char *filename) {
123 char *buffer = (char *)BaseFileManager::getEngineInstance()->readWholeFile(filename);
124 if (buffer == nullptr) {
125 _gameRef->LOG(0, "AdActor::LoadFile failed for file '%s'", filename);
126 return STATUS_FAILED;
127 }
128
129 bool ret;
130
131 setFilename(filename);
132
133 if (DID_FAIL(ret = loadBuffer(buffer, true))) {
134 _gameRef->LOG(0, "Error parsing ACTOR file '%s'", filename);
135 }
136
137
138 delete[] buffer;
139
140 return ret;
141 }
142
143
144 TOKEN_DEF_START
TOKEN_DEF(ACTOR)145 TOKEN_DEF(ACTOR)
146 TOKEN_DEF(X)
147 TOKEN_DEF(Y)
148 TOKEN_DEF(TEMPLATE)
149 TOKEN_DEF(NAME)
150 TOKEN_DEF(SCALABLE)
151 TOKEN_DEF(REGISTRABLE)
152 TOKEN_DEF(INTERACTIVE)
153 TOKEN_DEF(SHADOWABLE)
154 TOKEN_DEF(COLORABLE)
155 TOKEN_DEF(ACTIVE)
156 TOKEN_DEF(WALK)
157 TOKEN_DEF(STAND)
158 TOKEN_DEF(TALK_SPECIAL)
159 TOKEN_DEF(TALK)
160 TOKEN_DEF(TURN_LEFT)
161 TOKEN_DEF(TURN_RIGHT)
162 TOKEN_DEF(EVENTS)
163 TOKEN_DEF(FONT)
164 TOKEN_DEF(CURSOR)
165 TOKEN_DEF(SCRIPT)
166 TOKEN_DEF(SOUND_VOLUME)
167 TOKEN_DEF(SOUND_PANNING)
168 TOKEN_DEF(CAPTION)
169 TOKEN_DEF(PROPERTY)
170 TOKEN_DEF(BLOCKED_REGION)
171 TOKEN_DEF(WAYPOINTS)
172 TOKEN_DEF(IGNORE_ITEMS)
173 TOKEN_DEF(ROTABLE)
174 TOKEN_DEF(ROTATABLE)
175 TOKEN_DEF(ALPHA_COLOR)
176 TOKEN_DEF(SCALE)
177 TOKEN_DEF(RELATIVE_SCALE)
178 TOKEN_DEF(ALPHA)
179 TOKEN_DEF(EDITOR_PROPERTY)
180 TOKEN_DEF(ANIMATION)
181 TOKEN_DEF_END
182 //////////////////////////////////////////////////////////////////////////
183 bool AdActor::loadBuffer(char *buffer, bool complete) {
184 TOKEN_TABLE_START(commands)
185 TOKEN_TABLE(ACTOR)
186 TOKEN_TABLE(X)
187 TOKEN_TABLE(Y)
188 TOKEN_TABLE(TEMPLATE)
189 TOKEN_TABLE(NAME)
190 TOKEN_TABLE(SCALABLE)
191 TOKEN_TABLE(REGISTRABLE)
192 TOKEN_TABLE(INTERACTIVE)
193 TOKEN_TABLE(SHADOWABLE)
194 TOKEN_TABLE(COLORABLE)
195 TOKEN_TABLE(ACTIVE)
196 TOKEN_TABLE(WALK)
197 TOKEN_TABLE(STAND)
198 TOKEN_TABLE(TALK_SPECIAL)
199 TOKEN_TABLE(TALK)
200 TOKEN_TABLE(TURN_LEFT)
201 TOKEN_TABLE(TURN_RIGHT)
202 TOKEN_TABLE(EVENTS)
203 TOKEN_TABLE(FONT)
204 TOKEN_TABLE(CURSOR)
205 TOKEN_TABLE(SCRIPT)
206 TOKEN_TABLE(SOUND_VOLUME)
207 TOKEN_TABLE(SOUND_PANNING)
208 TOKEN_TABLE(CAPTION)
209 TOKEN_TABLE(PROPERTY)
210 TOKEN_TABLE(BLOCKED_REGION)
211 TOKEN_TABLE(WAYPOINTS)
212 TOKEN_TABLE(IGNORE_ITEMS)
213 TOKEN_TABLE(ROTABLE)
214 TOKEN_TABLE(ROTATABLE)
215 TOKEN_TABLE(ALPHA_COLOR)
216 TOKEN_TABLE(SCALE)
217 TOKEN_TABLE(RELATIVE_SCALE)
218 TOKEN_TABLE(ALPHA)
219 TOKEN_TABLE(EDITOR_PROPERTY)
220 TOKEN_TABLE(ANIMATION)
221 TOKEN_TABLE_END
222
223 char *params;
224 int cmd;
225 BaseParser parser;
226
227 if (complete) {
228 if (parser.getCommand(&buffer, commands, ¶ms) != TOKEN_ACTOR) {
229 _gameRef->LOG(0, "'ACTOR' keyword expected.");
230 return STATUS_FAILED;
231 }
232 buffer = params;
233 }
234
235 AdGame *adGame = (AdGame *)_gameRef;
236 AdSpriteSet *spr = nullptr;
237 int ar = 0, ag = 0, ab = 0, alpha = 0;
238 while ((cmd = parser.getCommand(&buffer, commands, ¶ms)) > 0) {
239 switch (cmd) {
240 case TOKEN_TEMPLATE:
241 if (DID_FAIL(loadFile(params))) {
242 cmd = PARSERR_GENERIC;
243 }
244 break;
245
246 case TOKEN_X:
247 parser.scanStr(params, "%d", &_posX);
248 break;
249
250 case TOKEN_Y:
251 parser.scanStr(params, "%d", &_posY);
252 break;
253
254 case TOKEN_NAME:
255 setName(params);
256 break;
257
258 case TOKEN_CAPTION:
259 setCaption(params);
260 break;
261
262 case TOKEN_FONT:
263 setFont(params);
264 break;
265
266 case TOKEN_SCALABLE:
267 parser.scanStr(params, "%b", &_zoomable);
268 break;
269
270 case TOKEN_ROTABLE:
271 case TOKEN_ROTATABLE:
272 parser.scanStr(params, "%b", &_rotatable);
273 break;
274
275 case TOKEN_REGISTRABLE:
276 case TOKEN_INTERACTIVE:
277 parser.scanStr(params, "%b", &_registrable);
278 break;
279
280 case TOKEN_SHADOWABLE:
281 case TOKEN_COLORABLE:
282 parser.scanStr(params, "%b", &_shadowable);
283 break;
284
285 case TOKEN_ACTIVE:
286 parser.scanStr(params, "%b", &_active);
287 break;
288
289 case TOKEN_WALK:
290 delete _walkSprite;
291 _walkSprite = nullptr;
292 spr = new AdSpriteSet(_gameRef, this);
293 if (!spr || DID_FAIL(spr->loadBuffer(params, true, adGame->_texWalkLifeTime, CACHE_HALF))) {
294 cmd = PARSERR_GENERIC;
295 } else {
296 _walkSprite = spr;
297 }
298 break;
299
300 case TOKEN_TALK:
301 spr = new AdSpriteSet(_gameRef, this);
302 if (!spr || DID_FAIL(spr->loadBuffer(params, true, adGame->_texTalkLifeTime))) {
303 cmd = PARSERR_GENERIC;
304 } else {
305 _talkSprites.add(spr);
306 }
307 break;
308
309 case TOKEN_TALK_SPECIAL:
310 spr = new AdSpriteSet(_gameRef, this);
311 if (!spr || DID_FAIL(spr->loadBuffer(params, true, adGame->_texTalkLifeTime))) {
312 cmd = PARSERR_GENERIC;
313 } else {
314 _talkSpritesEx.add(spr);
315 }
316 break;
317
318 case TOKEN_STAND:
319 delete _standSprite;
320 _standSprite = nullptr;
321 spr = new AdSpriteSet(_gameRef, this);
322 if (!spr || DID_FAIL(spr->loadBuffer(params, true, adGame->_texStandLifeTime))) {
323 cmd = PARSERR_GENERIC;
324 } else {
325 _standSprite = spr;
326 }
327 break;
328
329 case TOKEN_TURN_LEFT:
330 delete _turnLeftSprite;
331 _turnLeftSprite = nullptr;
332 spr = new AdSpriteSet(_gameRef, this);
333 if (!spr || DID_FAIL(spr->loadBuffer(params, true))) {
334 cmd = PARSERR_GENERIC;
335 } else {
336 _turnLeftSprite = spr;
337 }
338 break;
339
340 case TOKEN_TURN_RIGHT:
341 delete _turnRightSprite;
342 _turnRightSprite = nullptr;
343 spr = new AdSpriteSet(_gameRef, this);
344 if (!spr || DID_FAIL(spr->loadBuffer(params, true))) {
345 cmd = PARSERR_GENERIC;
346 } else {
347 _turnRightSprite = spr;
348 }
349 break;
350
351 case TOKEN_SCRIPT:
352 addScript(params);
353 break;
354
355 case TOKEN_CURSOR:
356 delete _cursor;
357 _cursor = new BaseSprite(_gameRef);
358 if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) {
359 delete _cursor;
360 _cursor = nullptr;
361 cmd = PARSERR_GENERIC;
362 }
363 break;
364
365 case TOKEN_SOUND_VOLUME:
366 parser.scanStr(params, "%d", &_sFXVolume);
367 break;
368
369 case TOKEN_SCALE: {
370 int s;
371 parser.scanStr(params, "%d", &s);
372 _scale = (float)s;
373
374 }
375 break;
376
377 case TOKEN_RELATIVE_SCALE: {
378 int s;
379 parser.scanStr(params, "%d", &s);
380 _relativeScale = (float)s;
381
382 }
383 break;
384
385 case TOKEN_SOUND_PANNING:
386 parser.scanStr(params, "%b", &_autoSoundPanning);
387 break;
388
389 case TOKEN_PROPERTY:
390 parseProperty(params, false);
391 break;
392
393 case TOKEN_BLOCKED_REGION: {
394 delete _blockRegion;
395 delete _currentBlockRegion;
396 _blockRegion = nullptr;
397 _currentBlockRegion = nullptr;
398 BaseRegion *rgn = new BaseRegion(_gameRef);
399 BaseRegion *crgn = new BaseRegion(_gameRef);
400 if (!rgn || !crgn || DID_FAIL(rgn->loadBuffer(params, false))) {
401 delete _blockRegion;
402 delete _currentBlockRegion;
403 _blockRegion = nullptr;
404 _currentBlockRegion = nullptr;
405 cmd = PARSERR_GENERIC;
406 } else {
407 _blockRegion = rgn;
408 _currentBlockRegion = crgn;
409 _currentBlockRegion->mimic(_blockRegion);
410 }
411 }
412 break;
413
414 case TOKEN_WAYPOINTS: {
415 delete _wptGroup;
416 delete _currentWptGroup;
417 _wptGroup = nullptr;
418 _currentWptGroup = nullptr;
419 AdWaypointGroup *wpt = new AdWaypointGroup(_gameRef);
420 AdWaypointGroup *cwpt = new AdWaypointGroup(_gameRef);
421 if (!wpt || !cwpt || DID_FAIL(wpt->loadBuffer(params, false))) {
422 delete _wptGroup;
423 delete _currentWptGroup;
424 _wptGroup = nullptr;
425 _currentWptGroup = nullptr;
426 cmd = PARSERR_GENERIC;
427 } else {
428 _wptGroup = wpt;
429 _currentWptGroup = cwpt;
430 _currentWptGroup->mimic(_wptGroup);
431 }
432 }
433 break;
434
435 case TOKEN_IGNORE_ITEMS:
436 parser.scanStr(params, "%b", &_ignoreItems);
437 break;
438
439 case TOKEN_ALPHA_COLOR:
440 parser.scanStr(params, "%d,%d,%d", &ar, &ag, &ab);
441 break;
442
443 case TOKEN_ALPHA:
444 parser.scanStr(params, "%d", &alpha);
445 break;
446
447 case TOKEN_EDITOR_PROPERTY:
448 parseEditorProperty(params, false);
449 break;
450
451 case TOKEN_ANIMATION: {
452 AdSpriteSet *anim = new AdSpriteSet(_gameRef, this);
453 if (!anim || DID_FAIL(anim->loadBuffer(params, false))) {
454 cmd = PARSERR_GENERIC;
455 } else {
456 _anims.add(anim);
457 }
458 }
459 break;
460
461 default:
462 break;
463 }
464 }
465 if (cmd == PARSERR_TOKENNOTFOUND) {
466 _gameRef->LOG(0, "Syntax error in ACTOR definition");
467 return STATUS_FAILED;
468 }
469 if (cmd == PARSERR_GENERIC) {
470 if (spr) {
471 delete spr;
472 }
473 _gameRef->LOG(0, "Error loading ACTOR definition");
474 return STATUS_FAILED;
475 }
476
477 if (alpha != 0 && ar == 0 && ag == 0 && ab == 0) {
478 ar = ag = ab = 255;
479 }
480 _alphaColor = BYTETORGBA(ar, ag, ab, alpha);
481 _state = _nextState = STATE_READY;
482
483 return STATUS_OK;
484 }
485
486
487 //////////////////////////////////////////////////////////////////////////
turnTo(TDirection dir)488 void AdActor::turnTo(TDirection dir) {
489 int delta1, delta2, delta3, delta;
490
491 delta1 = dir - _dir;
492 delta2 = dir + NUM_DIRECTIONS - _dir;
493 delta3 = dir - NUM_DIRECTIONS - _dir;
494
495 delta1 = (abs(delta1) <= abs(delta2)) ? delta1 : delta2;
496 delta = (abs(delta1) <= abs(delta3)) ? delta1 : delta3;
497
498 // already there?
499 if (abs(delta) < 2) {
500 _dir = dir;
501 _state = _nextState;
502 _nextState = STATE_READY;
503 return;
504 }
505
506 _targetDir = dir;
507 _state = delta < 0 ? STATE_TURNING_LEFT : STATE_TURNING_RIGHT;
508
509 _tempSprite2 = nullptr;
510 }
511
512
513 //////////////////////////////////////////////////////////////////////////
goTo(int x,int y,TDirection afterWalkDir)514 void AdActor::goTo(int x, int y, TDirection afterWalkDir) {
515 _afterWalkDir = afterWalkDir;
516 if (x == _targetPoint->x && y == _targetPoint->y && _state == STATE_FOLLOWING_PATH) {
517 return;
518 }
519
520 _path->reset();
521 _path->setReady(false);
522
523 _targetPoint->x = x;
524 _targetPoint->y = y;
525
526 ((AdGame *)_gameRef)->_scene->correctTargetPoint(_posX, _posY, &_targetPoint->x, &_targetPoint->y, true, this);
527
528 _state = STATE_SEARCHING_PATH;
529
530 }
531
532
533 //////////////////////////////////////////////////////////////////////////
display()534 bool AdActor::display() {
535 if (_active) {
536 updateSounds();
537 }
538
539 uint32 alpha;
540 if (_alphaColor != 0) {
541 alpha = _alphaColor;
542 } else {
543 alpha = _shadowable ? ((AdGame *)_gameRef)->_scene->getAlphaAt(_posX, _posY, true) : 0xFFFFFFFF;
544 }
545
546 float scaleX, scaleY;
547 getScale(&scaleX, &scaleY);
548
549
550 float rotate;
551 if (_rotatable) {
552 if (_rotateValid) {
553 rotate = _rotate;
554 } else {
555 rotate = ((AdGame *)_gameRef)->_scene->getRotationAt(_posX, _posY) + _relativeRotate;
556 }
557 } else {
558 rotate = 0.0f;
559 }
560
561 if (_active) {
562 displaySpriteAttachments(true);
563 }
564
565 if (_currentSprite && _active) {
566 bool reg = _registrable;
567 if (_ignoreItems && ((AdGame *)_gameRef)->_selectedItem) {
568 reg = false;
569 }
570
571 _currentSprite->display(_posX,
572 _posY,
573 reg ? _registerAlias : nullptr,
574 scaleX,
575 scaleY,
576 alpha,
577 rotate,
578 _blendMode);
579
580 }
581
582 if (_active) {
583 displaySpriteAttachments(false);
584 }
585 if (_active && _partEmitter) {
586 _partEmitter->display();
587 }
588
589
590 return STATUS_OK;
591 }
592
593
594 //////////////////////////////////////////////////////////////////////////
update()595 bool AdActor::update() {
596 _currentSprite = nullptr;
597
598 if (_state == STATE_READY) {
599 if (_animSprite) {
600 delete _animSprite;
601 _animSprite = nullptr;
602 }
603 if (_animSprite2) {
604 _animSprite2 = nullptr;
605 }
606 }
607
608 // finished playing animation?
609 if (_state == STATE_PLAYING_ANIM && _animSprite != nullptr && _animSprite->isFinished()) {
610 _state = _nextState;
611 _nextState = STATE_READY;
612 _currentSprite = _animSprite;
613 }
614
615 if (_state == STATE_PLAYING_ANIM_SET && _animSprite2 != nullptr && _animSprite2->isFinished()) {
616 _state = _nextState;
617 _nextState = STATE_READY;
618 _currentSprite = _animSprite2;
619 }
620
621 if (_sentence && _state != STATE_TALKING) {
622 _sentence->finish();
623 }
624
625 // default: stand animation
626 if (!_currentSprite) {
627 if (_sprite) {
628 _currentSprite = _sprite;
629 } else {
630 if (_standSprite) {
631 _currentSprite = _standSprite->getSprite(_dir);
632 } else {
633 AdSpriteSet *anim = getAnimByName(_idleAnimName);
634 if (anim) {
635 _currentSprite = anim->getSprite(_dir);
636 }
637 }
638 }
639 }
640
641 bool already_moved = false;
642
643 switch (_state) {
644 //////////////////////////////////////////////////////////////////////////
645 case STATE_PLAYING_ANIM:
646 _currentSprite = _animSprite;
647 break;
648
649 //////////////////////////////////////////////////////////////////////////
650 case STATE_PLAYING_ANIM_SET:
651 _currentSprite = _animSprite2;
652 break;
653
654 //////////////////////////////////////////////////////////////////////////
655 case STATE_TURNING_LEFT:
656 if (_tempSprite2 == nullptr || _tempSprite2->isFinished()) {
657 if (_dir > 0) {
658 _dir = (TDirection)(_dir - 1);
659 } else {
660 _dir = (TDirection)(NUM_DIRECTIONS - 1);
661 }
662
663 if (_dir == _targetDir) {
664 _tempSprite2 = nullptr;
665 _state = _nextState;
666 _nextState = STATE_READY;
667 } else {
668 if (_turnLeftSprite) {
669 _tempSprite2 = _turnLeftSprite->getSprite(_dir);
670 } else {
671 AdSpriteSet *anim = getAnimByName(_turnLeftAnimName);
672 if (anim) {
673 _tempSprite2 = anim->getSprite(_dir);
674 }
675 }
676
677 if (_tempSprite2) {
678 _tempSprite2->reset();
679 if (_tempSprite2->_looping) {
680 _tempSprite2->_looping = false;
681 }
682 }
683 _currentSprite = _tempSprite2;
684 }
685 } else {
686 _currentSprite = _tempSprite2;
687 }
688 break;
689
690
691 //////////////////////////////////////////////////////////////////////////
692 case STATE_TURNING_RIGHT:
693 if (_tempSprite2 == nullptr || _tempSprite2->isFinished()) {
694 _dir = (TDirection)(_dir + 1);
695
696 if ((int)_dir >= (int)NUM_DIRECTIONS) {
697 _dir = (TDirection)(0);
698 }
699
700 if (_dir == _targetDir) {
701 _tempSprite2 = nullptr;
702 _state = _nextState;
703 _nextState = STATE_READY;
704 } else {
705 if (_turnRightSprite) {
706 _tempSprite2 = _turnRightSprite->getSprite(_dir);
707 } else {
708 AdSpriteSet *anim = getAnimByName(_turnRightAnimName);
709 if (anim) {
710 _tempSprite2 = anim->getSprite(_dir);
711 }
712 }
713
714 if (_tempSprite2) {
715 _tempSprite2->reset();
716 if (_tempSprite2->_looping) {
717 _tempSprite2->_looping = false;
718 }
719 }
720 _currentSprite = _tempSprite2;
721 }
722 } else {
723 _currentSprite = _tempSprite2;
724 }
725 break;
726
727
728 //////////////////////////////////////////////////////////////////////////
729 case STATE_SEARCHING_PATH:
730 // keep asking scene for the path
731 if (((AdGame *)_gameRef)->_scene->getPath(BasePoint(_posX, _posY), *_targetPoint, _path, this)) {
732 _state = STATE_WAITING_PATH;
733 }
734 break;
735
736
737 //////////////////////////////////////////////////////////////////////////
738 case STATE_WAITING_PATH:
739 // wait until the scene finished the path
740 if (_path->_ready) {
741 followPath();
742 }
743 break;
744
745
746 //////////////////////////////////////////////////////////////////////////
747 case STATE_FOLLOWING_PATH:
748 getNextStep();
749 already_moved = true;
750 break;
751
752 //////////////////////////////////////////////////////////////////////////
753 case STATE_TALKING: {
754 _sentence->update(_dir);
755 if (_sentence->_currentSprite) {
756 _tempSprite2 = _sentence->_currentSprite;
757 }
758
759 bool timeIsUp = (_sentence->_sound && _sentence->_soundStarted && (!_sentence->_sound->isPlaying() && !_sentence->_sound->isPaused())) || (!_sentence->_sound && _sentence->_duration <= _gameRef->getTimer()->getTime() - _sentence->_startTime);
760 if (_tempSprite2 == nullptr || _tempSprite2->isFinished() || (/*_tempSprite2->_looping &&*/ timeIsUp)) {
761 if (timeIsUp) {
762 _sentence->finish();
763 _tempSprite2 = nullptr;
764 _state = _nextState;
765 _nextState = STATE_READY;
766 } else {
767 _tempSprite2 = getTalkStance(_sentence->getNextStance());
768 if (_tempSprite2) {
769 _tempSprite2->reset();
770 _currentSprite = _tempSprite2;
771 ((AdGame *)_gameRef)->addSentence(_sentence);
772 }
773 }
774 } else {
775 _currentSprite = _tempSprite2;
776 ((AdGame *)_gameRef)->addSentence(_sentence);
777 }
778 }
779 break;
780
781 //////////////////////////////////////////////////////////////////////////
782 case STATE_READY:
783 if (!_animSprite && !_animSprite2) {
784 if (_sprite) {
785 _currentSprite = _sprite;
786 } else {
787 if (_standSprite) {
788 _currentSprite = _standSprite->getSprite(_dir);
789 } else {
790 AdSpriteSet *anim = getAnimByName(_idleAnimName);
791 if (anim) {
792 _currentSprite = anim->getSprite(_dir);
793 }
794 }
795 }
796 }
797 break;
798 default:
799 error("AdActor::Update - Unhandled enum");
800 }
801
802
803 if (_currentSprite && !already_moved) {
804 _currentSprite->getCurrentFrame(_zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100, _zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100);
805 if (_currentSprite->isChanged()) {
806 _posX += _currentSprite->_moveX;
807 _posY += _currentSprite->_moveY;
808 afterMove();
809 }
810 }
811
812 //_gameRef->QuickMessageForm("%s", _currentSprite->_filename);
813
814 updateBlockRegion();
815 _ready = (_state == STATE_READY);
816
817 updatePartEmitter();
818 updateSpriteAttachments();
819
820 return STATUS_OK;
821 }
822
823
824 //////////////////////////////////////////////////////////////////////////
followPath()825 void AdActor::followPath() {
826 // skip current position
827 _path->getFirst();
828 while (_path->getCurrent() != nullptr) {
829 if (_path->getCurrent()->x != _posX || _path->getCurrent()->y != _posY) {
830 break;
831 }
832 _path->getNext();
833 }
834
835 // are there points to follow?
836 if (_path->getCurrent() != nullptr) {
837 _state = STATE_FOLLOWING_PATH;
838 initLine(BasePoint(_posX, _posY), *_path->getCurrent());
839 } else {
840 if (_afterWalkDir != DI_NONE) {
841 turnTo(_afterWalkDir);
842 } else {
843 _state = STATE_READY;
844 }
845 }
846 }
847
848
849 //////////////////////////////////////////////////////////////////////////
getNextStep()850 void AdActor::getNextStep() {
851 if (_walkSprite) {
852 _currentSprite = _walkSprite->getSprite(_dir);
853 } else {
854 AdSpriteSet *anim = getAnimByName(_walkAnimName);
855 if (anim) {
856 _currentSprite = anim->getSprite(_dir);
857 }
858 }
859
860 if (!_currentSprite) {
861 return;
862 }
863
864 _currentSprite->getCurrentFrame(_zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100, _zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100);
865 if (!_currentSprite->isChanged()) {
866 return;
867 }
868
869
870 int maxStepX, maxStepY;
871 maxStepX = abs(_currentSprite->_moveX);
872 maxStepY = abs(_currentSprite->_moveY);
873
874 maxStepX = MAX(maxStepX, maxStepY);
875 maxStepX = MAX(maxStepX, 1);
876
877 while (_pFCount > 0 && maxStepX >= 0) {
878 _pFX += _pFStepX;
879 _pFY += _pFStepY;
880
881 _pFCount--;
882 maxStepX--;
883 }
884
885 if (((AdGame *)_gameRef)->_scene->isBlockedAt((int)_pFX, (int)_pFY, true, this)) {
886 if (_pFCount == 0) {
887 _state = _nextState;
888 _nextState = STATE_READY;
889 return;
890 }
891 goTo(_targetPoint->x, _targetPoint->y);
892 return;
893 }
894
895
896 _posX = (int)_pFX;
897 _posY = (int)_pFY;
898
899 afterMove();
900
901
902 if (_pFCount == 0) {
903 if (_path->getNext() == nullptr) {
904 _posX = _targetPoint->x;
905 _posY = _targetPoint->y;
906
907 _path->reset();
908 if (_afterWalkDir != DI_NONE) {
909 turnTo(_afterWalkDir);
910 } else {
911 _state = _nextState;
912 _nextState = STATE_READY;
913 }
914 } else {
915 initLine(BasePoint(_posX, _posY), *_path->getCurrent());
916 }
917 }
918 }
919
920
921 //////////////////////////////////////////////////////////////////////////
initLine(const BasePoint & startPt,const BasePoint & endPt)922 void AdActor::initLine(const BasePoint &startPt, const BasePoint &endPt) {
923 _pFCount = MAX((abs(endPt.x - startPt.x)), (abs(endPt.y - startPt.y)));
924
925 _pFStepX = (double)(endPt.x - startPt.x) / _pFCount;
926 _pFStepY = (double)(endPt.y - startPt.y) / _pFCount;
927
928 _pFX = startPt.x;
929 _pFY = startPt.y;
930
931 int angle = (int)(atan2((double)(endPt.y - startPt.y), (double)(endPt.x - startPt.x)) * (180 / 3.14));
932
933 _nextState = STATE_FOLLOWING_PATH;
934
935 turnTo(angleToDirection(angle));
936 }
937
938
939 //////////////////////////////////////////////////////////////////////////
940 // high level scripting interface
941 //////////////////////////////////////////////////////////////////////////
scCallMethod(ScScript * script,ScStack * stack,ScStack * thisStack,const char * name)942 bool AdActor::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
943 //////////////////////////////////////////////////////////////////////////
944 // GoTo / GoToAsync
945 //////////////////////////////////////////////////////////////////////////
946 if (strcmp(name, "GoTo") == 0 || strcmp(name, "GoToAsync") == 0) {
947 stack->correctParams(2);
948 int x = stack->pop()->getInt();
949 int y = stack->pop()->getInt();
950 goTo(x, y);
951 if (strcmp(name, "GoToAsync") != 0) {
952 script->waitForExclusive(this);
953 }
954 stack->pushNULL();
955 return STATUS_OK;
956 }
957
958 //////////////////////////////////////////////////////////////////////////
959 // GoToObject / GoToObjectAsync
960 //////////////////////////////////////////////////////////////////////////
961 else if (strcmp(name, "GoToObject") == 0 || strcmp(name, "GoToObjectAsync") == 0) {
962 stack->correctParams(1);
963 ScValue *val = stack->pop();
964 if (!val->isNative()) {
965 script->runtimeError("actor.%s method accepts an entity reference only", name);
966 stack->pushNULL();
967 return STATUS_OK;
968 }
969 AdObject *obj = (AdObject *)val->getNative();
970 if (!obj || obj->getType() != OBJECT_ENTITY) {
971 script->runtimeError("actor.%s method accepts an entity reference only", name);
972 stack->pushNULL();
973 return STATUS_OK;
974 }
975 AdEntity *ent = (AdEntity *)obj;
976 if (ent->getWalkToX() == 0 && ent->getWalkToY() == 0) {
977 goTo(ent->_posX, ent->_posY);
978 } else {
979 goTo(ent->getWalkToX(), ent->getWalkToY(), ent->getWalkToDir());
980 }
981 if (strcmp(name, "GoToObjectAsync") != 0) {
982 script->waitForExclusive(this);
983 }
984 stack->pushNULL();
985 return STATUS_OK;
986 }
987
988 //////////////////////////////////////////////////////////////////////////
989 // TurnTo / TurnToAsync
990 //////////////////////////////////////////////////////////////////////////
991 else if (strcmp(name, "TurnTo") == 0 || strcmp(name, "TurnToAsync") == 0) {
992 stack->correctParams(1);
993 int dir;
994 ScValue *val = stack->pop();
995
996 // turn to object?
997 if (val->isNative() && _gameRef->validObject((BaseObject *)val->getNative())) {
998 BaseObject *obj = (BaseObject *)val->getNative();
999 int angle = (int)(atan2((double)(obj->_posY - _posY), (double)(obj->_posX - _posX)) * (180 / 3.14));
1000 dir = (int)angleToDirection(angle);
1001 }
1002 // otherwise turn to direction
1003 else {
1004 dir = val->getInt();
1005 }
1006
1007 if (dir >= 0 && dir < NUM_DIRECTIONS) {
1008 turnTo((TDirection)dir);
1009 if (strcmp(name, "TurnToAsync") != 0) {
1010 script->waitForExclusive(this);
1011 }
1012 }
1013 stack->pushNULL();
1014 return STATUS_OK;
1015 }
1016
1017 //////////////////////////////////////////////////////////////////////////
1018 // IsWalking
1019 //////////////////////////////////////////////////////////////////////////
1020 else if (strcmp(name, "IsWalking") == 0) {
1021 stack->correctParams(0);
1022 stack->pushBool(_state == STATE_FOLLOWING_PATH);
1023 return STATUS_OK;
1024 }
1025
1026 #ifdef ENABLE_FOXTAIL
1027 //////////////////////////////////////////////////////////////////////////
1028 // [FoxTail] StopWalking
1029 // Used to stop Leah in one scene only at rabbit_run.script in action()
1030 // Let's just call turnTo() for current direction to finalize movement
1031 // Return value is never used
1032 //////////////////////////////////////////////////////////////////////////
1033 else if (strcmp(name, "StopWalking") == 0) {
1034 stack->correctParams(0);
1035 turnTo(_dir);
1036 stack->pushNULL();
1037
1038 return STATUS_OK;
1039 }
1040
1041 //////////////////////////////////////////////////////////////////////////
1042 // [FoxTail] SetSpeedWalkAnim
1043 // Used to set Leah speed at leah.script in SetSpeed()
1044 // Modifies walking animations interframe delays
1045 // Takes integer parameter:
1046 // 10 on state.ultra_super_mega_fast_walk cheat code
1047 // 40 on "Fast" settings
1048 // 70 on "Normal" settings
1049 // 90 on "Slow" settings
1050 // Return value is never used
1051 //////////////////////////////////////////////////////////////////////////
1052 else if (strcmp(name, "SetSpeedWalkAnim") == 0) {
1053 stack->correctParams(1);
1054 int speedWalk = stack->pop()->getInt();
1055 for (uint32 dir = 0; dir < NUM_DIRECTIONS; dir++) {
1056 AdSpriteSet *anim = getAnimByName(_walkAnimName);
1057 if (anim != nullptr) {
1058 BaseSprite *item = anim->getSprite((TDirection)dir);
1059 if (item != nullptr) {
1060 for (uint32 i = 0; i < item->_frames.size(); i++) {
1061 BaseFrame *frame = item->_frames[i];
1062 if (frame != nullptr) {
1063 frame->_delay = speedWalk;
1064 }
1065 }
1066 }
1067 }
1068 }
1069 stack->pushNULL();
1070
1071 return STATUS_OK;
1072 }
1073 #endif
1074
1075 //////////////////////////////////////////////////////////////////////////
1076 // MergeAnims
1077 //////////////////////////////////////////////////////////////////////////
1078 else if (strcmp(name, "MergeAnims") == 0) {
1079 stack->correctParams(1);
1080 stack->pushBool(DID_SUCCEED(mergeAnims(stack->pop()->getString())));
1081 return STATUS_OK;
1082 }
1083
1084 //////////////////////////////////////////////////////////////////////////
1085 // UnloadAnim
1086 //////////////////////////////////////////////////////////////////////////
1087 else if (strcmp(name, "UnloadAnim") == 0) {
1088 stack->correctParams(1);
1089 const char *animName = stack->pop()->getString();
1090
1091 bool found = false;
1092 for (uint32 i = 0; i < _anims.size(); i++) {
1093 if (scumm_stricmp(_anims[i]->getName(), animName) == 0) {
1094 // invalidate sprites in use
1095 if (_anims[i]->containsSprite(_tempSprite2)) {
1096 _tempSprite2 = nullptr;
1097 }
1098 if (_anims[i]->containsSprite(_currentSprite)) {
1099 _currentSprite = nullptr;
1100 }
1101 if (_anims[i]->containsSprite(_animSprite2)) {
1102 _animSprite2 = nullptr;
1103 }
1104
1105 delete _anims[i];
1106 _anims[i] = nullptr;
1107 _anims.remove_at(i);
1108 i--;
1109 found = true;
1110 }
1111 }
1112 stack->pushBool(found);
1113 return STATUS_OK;
1114 }
1115
1116 //////////////////////////////////////////////////////////////////////////
1117 // HasAnim
1118 //////////////////////////////////////////////////////////////////////////
1119 else if (strcmp(name, "HasAnim") == 0) {
1120 stack->correctParams(1);
1121 const char *animName = stack->pop()->getString();
1122 stack->pushBool(getAnimByName(animName) != nullptr);
1123 return STATUS_OK;
1124 } else {
1125 return AdTalkHolder::scCallMethod(script, stack, thisStack, name);
1126 }
1127 }
1128
1129
1130 //////////////////////////////////////////////////////////////////////////
scGetProperty(const Common::String & name)1131 ScValue *AdActor::scGetProperty(const Common::String &name) {
1132 _scValue->setNULL();
1133
1134 //////////////////////////////////////////////////////////////////////////
1135 // Direction
1136 //////////////////////////////////////////////////////////////////////////
1137 if (name == "Direction") {
1138 _scValue->setInt(_dir);
1139 return _scValue;
1140 }
1141 //////////////////////////////////////////////////////////////////////////
1142 // Type
1143 //////////////////////////////////////////////////////////////////////////
1144 else if (name == "Type") {
1145 _scValue->setString("actor");
1146 return _scValue;
1147 }
1148 //////////////////////////////////////////////////////////////////////////
1149 // TalkAnimName
1150 //////////////////////////////////////////////////////////////////////////
1151 else if (name == "TalkAnimName") {
1152 _scValue->setString(_talkAnimName);
1153 return _scValue;
1154 }
1155
1156 //////////////////////////////////////////////////////////////////////////
1157 // WalkAnimName
1158 //////////////////////////////////////////////////////////////////////////
1159 else if (name == "WalkAnimName") {
1160 _scValue->setString(_walkAnimName);
1161 return _scValue;
1162 }
1163
1164 //////////////////////////////////////////////////////////////////////////
1165 // IdleAnimName
1166 //////////////////////////////////////////////////////////////////////////
1167 else if (name == "IdleAnimName") {
1168 _scValue->setString(_idleAnimName);
1169 return _scValue;
1170 }
1171
1172 //////////////////////////////////////////////////////////////////////////
1173 // TurnLeftAnimName
1174 //////////////////////////////////////////////////////////////////////////
1175 else if (name == "TurnLeftAnimName") {
1176 _scValue->setString(_turnLeftAnimName);
1177 return _scValue;
1178 }
1179
1180 //////////////////////////////////////////////////////////////////////////
1181 // TurnRightAnimName
1182 //////////////////////////////////////////////////////////////////////////
1183 else if (name == "TurnRightAnimName") {
1184 _scValue->setString(_turnRightAnimName);
1185 return _scValue;
1186 } else {
1187 return AdTalkHolder::scGetProperty(name);
1188 }
1189 }
1190
1191
1192 //////////////////////////////////////////////////////////////////////////
scSetProperty(const char * name,ScValue * value)1193 bool AdActor::scSetProperty(const char *name, ScValue *value) {
1194 //////////////////////////////////////////////////////////////////////////
1195 // Direction
1196 //////////////////////////////////////////////////////////////////////////
1197 if (strcmp(name, "Direction") == 0) {
1198 int dir = value->getInt();
1199 if (dir >= 0 && dir < NUM_DIRECTIONS) {
1200 _dir = (TDirection)dir;
1201 }
1202 return STATUS_OK;
1203 }
1204
1205 //////////////////////////////////////////////////////////////////////////
1206 // TalkAnimName
1207 //////////////////////////////////////////////////////////////////////////
1208 else if (strcmp(name, "TalkAnimName") == 0) {
1209 if (value->isNULL()) {
1210 _talkAnimName = "talk";
1211 } else {
1212 _talkAnimName = value->getString();
1213 }
1214 return STATUS_OK;
1215 }
1216
1217 //////////////////////////////////////////////////////////////////////////
1218 // WalkAnimName
1219 //////////////////////////////////////////////////////////////////////////
1220 else if (strcmp(name, "WalkAnimName") == 0) {
1221 if (value->isNULL()) {
1222 _walkAnimName = "walk";
1223 } else {
1224 _walkAnimName = value->getString();
1225 }
1226 return STATUS_OK;
1227 }
1228
1229 //////////////////////////////////////////////////////////////////////////
1230 // IdleAnimName
1231 //////////////////////////////////////////////////////////////////////////
1232 else if (strcmp(name, "IdleAnimName") == 0) {
1233 if (value->isNULL()) {
1234 _idleAnimName = "idle";
1235 } else {
1236 _idleAnimName = value->getString();
1237 }
1238 return STATUS_OK;
1239 }
1240
1241 //////////////////////////////////////////////////////////////////////////
1242 // TurnLeftAnimName
1243 //////////////////////////////////////////////////////////////////////////
1244 else if (strcmp(name, "TurnLeftAnimName") == 0) {
1245 if (value->isNULL()) {
1246 _turnLeftAnimName = "turnleft";
1247 } else {
1248 _turnLeftAnimName = value->getString();
1249 }
1250 return STATUS_OK;
1251 }
1252
1253 //////////////////////////////////////////////////////////////////////////
1254 // TurnRightAnimName
1255 //////////////////////////////////////////////////////////////////////////
1256 else if (strcmp(name, "TurnRightAnimName") == 0) {
1257 if (value->isNULL()) {
1258 _turnRightAnimName = "turnright";
1259 } else {
1260 _turnRightAnimName = value->getString();
1261 }
1262 return STATUS_OK;
1263 } else {
1264 return AdTalkHolder::scSetProperty(name, value);
1265 }
1266 }
1267
1268
1269 //////////////////////////////////////////////////////////////////////////
scToString()1270 const char *AdActor::scToString() {
1271 return "[actor object]";
1272 }
1273
1274
1275 //////////////////////////////////////////////////////////////////////////
getTalkStance(const char * stance)1276 BaseSprite *AdActor::getTalkStance(const char *stance) {
1277 // forced stance?
1278 if (_forcedTalkAnimName && !_forcedTalkAnimUsed) {
1279 _forcedTalkAnimUsed = true;
1280 delete _animSprite;
1281 _animSprite = new BaseSprite(_gameRef, this);
1282 if (_animSprite) {
1283 bool res = _animSprite->loadFile(_forcedTalkAnimName);
1284 if (DID_FAIL(res)) {
1285 _gameRef->LOG(res, "AdActor::GetTalkStance: error loading talk sprite (object:\"%s\" sprite:\"%s\")", getName(), _forcedTalkAnimName);
1286 delete _animSprite;
1287 _animSprite = nullptr;
1288 } else {
1289 return _animSprite;
1290 }
1291 }
1292 }
1293
1294 // old way
1295 if (_talkSprites.size() > 0 || _talkSpritesEx.size() > 0) {
1296 return getTalkStanceOld(stance);
1297 }
1298
1299 // new way
1300 BaseSprite *ret = nullptr;
1301
1302 // do we have an animation with this name?
1303 AdSpriteSet *anim = getAnimByName(stance);
1304 if (anim) {
1305 ret = anim->getSprite(_dir);
1306 }
1307
1308 // not - get a random talk
1309 if (!ret) {
1310 BaseArray<AdSpriteSet *> talkAnims;
1311 for (uint32 i = 0; i < _anims.size(); i++) {
1312 if (_talkAnimName.compareToIgnoreCase(_anims[i]->getName()) == 0) {
1313 talkAnims.add(_anims[i]);
1314 }
1315 }
1316
1317 if (talkAnims.size() > 0) {
1318 int rnd = BaseEngine::instance().randInt(0, talkAnims.size() - 1);
1319 ret = talkAnims[rnd]->getSprite(_dir);
1320 } else {
1321 if (_standSprite) {
1322 ret = _standSprite->getSprite(_dir);
1323 } else {
1324 anim = getAnimByName(_idleAnimName);
1325 if (anim) {
1326 ret = anim->getSprite(_dir);
1327 }
1328 }
1329 }
1330 }
1331 return ret;
1332 }
1333
1334 //////////////////////////////////////////////////////////////////////////
getTalkStanceOld(const char * stance)1335 BaseSprite *AdActor::getTalkStanceOld(const char *stance) {
1336 BaseSprite *ret = nullptr;
1337
1338 if (stance != nullptr) {
1339 // search special stances
1340 for (uint32 i = 0; i < _talkSpritesEx.size(); i++) {
1341 if (scumm_stricmp(_talkSpritesEx[i]->getName(), stance) == 0) {
1342 ret = _talkSpritesEx[i]->getSprite(_dir);
1343 break;
1344 }
1345 }
1346 if (ret == nullptr) {
1347 // search generic stances
1348 for (uint32 i = 0; i < _talkSprites.size(); i++) {
1349 if (scumm_stricmp(_talkSprites[i]->getName(), stance) == 0) {
1350 ret = _talkSprites[i]->getSprite(_dir);
1351 break;
1352 }
1353 }
1354 }
1355 }
1356
1357 // not a valid stance? get a random one
1358 if (ret == nullptr) {
1359 if (_talkSprites.size() < 1) {
1360 ret = _standSprite->getSprite(_dir);
1361 } else {
1362 // TODO: remember last
1363 int rnd = BaseEngine::instance().randInt(0, _talkSprites.size() - 1);
1364 ret = _talkSprites[rnd]->getSprite(_dir);
1365 }
1366 }
1367
1368 return ret;
1369 }
1370
1371 //////////////////////////////////////////////////////////////////////////
persist(BasePersistenceManager * persistMgr)1372 bool AdActor::persist(BasePersistenceManager *persistMgr) {
1373 AdTalkHolder::persist(persistMgr);
1374
1375 persistMgr->transferSint32(TMEMBER_INT(_dir));
1376 persistMgr->transferPtr(TMEMBER_PTR(_path));
1377 persistMgr->transferSint32(TMEMBER(_pFCount));
1378 persistMgr->transferDouble(TMEMBER(_pFStepX));
1379 persistMgr->transferDouble(TMEMBER(_pFStepY));
1380 persistMgr->transferDouble(TMEMBER(_pFX));
1381 persistMgr->transferDouble(TMEMBER(_pFY));
1382 persistMgr->transferPtr(TMEMBER_PTR(_standSprite));
1383 _talkSprites.persist(persistMgr);
1384 _talkSpritesEx.persist(persistMgr);
1385 persistMgr->transferSint32(TMEMBER_INT(_targetDir));
1386 persistMgr->transferSint32(TMEMBER_INT(_afterWalkDir));
1387 persistMgr->transferPtr(TMEMBER_PTR(_targetPoint));
1388 persistMgr->transferPtr(TMEMBER_PTR(_turnLeftSprite));
1389 persistMgr->transferPtr(TMEMBER_PTR(_turnRightSprite));
1390 persistMgr->transferPtr(TMEMBER_PTR(_walkSprite));
1391
1392 persistMgr->transferPtr(TMEMBER_PTR(_animSprite2));
1393 persistMgr->transferString(TMEMBER(_talkAnimName));
1394 persistMgr->transferString(TMEMBER(_idleAnimName));
1395 persistMgr->transferString(TMEMBER(_walkAnimName));
1396 persistMgr->transferString(TMEMBER(_turnLeftAnimName));
1397 persistMgr->transferString(TMEMBER(_turnRightAnimName));
1398
1399 _anims.persist(persistMgr);
1400
1401 return STATUS_OK;
1402 }
1403
1404
1405 //////////////////////////////////////////////////////////////////////////
angleToDirection(int angle)1406 TDirection AdActor::angleToDirection(int angle) {
1407 TDirection ret = DI_DOWN;
1408
1409 if (angle > -112 && angle <= -67) {
1410 ret = DI_UP;
1411 } else if (angle > -67 && angle <= -22) {
1412 ret = DI_UPRIGHT;
1413 } else if (angle > -22 && angle <= 22) {
1414 ret = DI_RIGHT;
1415 } else if (angle > 22 && angle <= 67) {
1416 ret = DI_DOWNRIGHT;
1417 } else if (angle > 67 && angle <= 112) {
1418 ret = DI_DOWN;
1419 } else if (angle > 112 && angle <= 157) {
1420 ret = DI_DOWNLEFT;
1421 } else if ((angle > 157 && angle <= 180) || (angle >= -180 && angle <= -157)) {
1422 ret = DI_LEFT;
1423 } else if (angle > -157 && angle <= -112) {
1424 ret = DI_UPLEFT;
1425 }
1426
1427 return ret;
1428 }
1429
1430
1431 //////////////////////////////////////////////////////////////////////////
getHeight()1432 int32 AdActor::getHeight() {
1433 // if no current sprite is set, set some
1434 if (_currentSprite == nullptr) {
1435 if (_standSprite) {
1436 _currentSprite = _standSprite->getSprite(_dir);
1437 } else {
1438 AdSpriteSet *anim = getAnimByName(_idleAnimName);
1439 if (anim) {
1440 _currentSprite = anim->getSprite(_dir);
1441 }
1442 }
1443 }
1444 // and get height
1445 return AdTalkHolder::getHeight();
1446 }
1447
1448
1449 //////////////////////////////////////////////////////////////////////////
getAnimByName(const Common::String & animName)1450 AdSpriteSet *AdActor::getAnimByName(const Common::String &animName) {
1451 for (uint32 i = 0; i < _anims.size(); i++) {
1452 if (animName.compareToIgnoreCase(_anims[i]->getName()) == 0) {
1453 return _anims[i];
1454 }
1455 }
1456 return nullptr;
1457 }
1458
1459 //////////////////////////////////////////////////////////////////////////
mergeAnims(const char * animsFilename)1460 bool AdActor::mergeAnims(const char *animsFilename) {
1461 TOKEN_TABLE_START(commands)
1462 TOKEN_TABLE(ANIMATION)
1463 TOKEN_TABLE_END
1464
1465
1466 char *fileBuffer = (char *)BaseFileManager::getEngineInstance()->readWholeFile(animsFilename);
1467 if (fileBuffer == nullptr) {
1468 _gameRef->LOG(0, "AdActor::MergeAnims failed for file '%s'", animsFilename);
1469 return STATUS_FAILED;
1470 }
1471
1472 char *buffer = fileBuffer;
1473 char *params;
1474 int cmd;
1475 BaseParser parser;
1476
1477 bool ret = STATUS_OK;
1478
1479 while ((cmd = parser.getCommand(&buffer, commands, ¶ms)) > 0) {
1480 switch (cmd) {
1481 case TOKEN_ANIMATION: {
1482 AdSpriteSet *anim = new AdSpriteSet(_gameRef, this);
1483 if (!anim || DID_FAIL(anim->loadBuffer(params, false))) {
1484 cmd = PARSERR_GENERIC;
1485 ret = STATUS_FAILED;
1486 } else {
1487 _anims.add(anim);
1488 }
1489 }
1490 break;
1491
1492 default:
1493 break;
1494 }
1495 }
1496 delete[] fileBuffer;
1497 return ret;
1498 }
1499
1500 //////////////////////////////////////////////////////////////////////////
playAnim(const char * filename)1501 bool AdActor::playAnim(const char *filename) {
1502 // if we have an anim with this name, use it
1503 AdSpriteSet *anim = getAnimByName(filename);
1504 if (anim) {
1505 _animSprite2 = anim->getSprite(_dir);
1506 if (_animSprite2) {
1507 _animSprite2->reset();
1508 _state = STATE_PLAYING_ANIM_SET;
1509 return STATUS_OK;
1510 }
1511 }
1512 // otherwise call the standard handler
1513 return AdTalkHolder::playAnim(filename);
1514 }
1515
1516 } // End of namespace Wintermute
1517