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, &params) != 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, &params)) > 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, &params)) > 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