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 "saga/saga.h"
24 
25 #include "saga/actor.h"
26 #include "saga/console.h"
27 #include "saga/events.h"
28 #include "saga/isomap.h"
29 #include "saga/objectmap.h"
30 #include "saga/script.h"
31 #include "saga/sound.h"
32 #include "saga/scene.h"
33 
34 namespace Saga {
35 
36 static const int angleLUT[16][2] = {
37 	{    0, -256 },
38 	{   98, -237 },
39 	{  181, -181 },
40 	{  237,  -98 },
41 	{  256,    0 },
42 	{  237,	  98 },
43 	{  181,  181 },
44 	{   98,  237 },
45 	{    0,  256 },
46 	{  -98,  237 },
47 	{ -181,  181 },
48 	{ -237,   98 },
49 	{ -256,    0 },
50 	{ -237,  -98 },
51 	{ -181, -181 },
52 	{  -98, -237 }
53 };
54 
55 static const int directionLUT[8][2] = {
56 	{  0 * 2, -2 * 2 },
57 	{  2 * 2, -1 * 2 },
58 	{  3 * 2,  0 * 2 },
59 	{  2 * 2,  1 * 2 },
60 	{  0 * 2,  2 * 2 },
61 	{ -2 * 2,  1 * 2 },
62 	{ -4 * 2,  0 * 2 },
63 	{ -2 * 2, -1 * 2 }
64 };
65 
66 static const int tileDirectionLUT[8][2] = {
67 	{  1,  1 },
68 	{  2,  0 },
69 	{  1, -1 },
70 	{  0, -2 },
71 	{ -1, -1 },
72 	{ -2,  0 },
73 	{ -1,  1 },
74 	{  0,  2 }
75 };
76 
77 struct DragonMove {
78 	uint16 baseFrame;
79 	int16 offset[4][2];
80 };
81 
82 static const DragonMove dragonMoveTable[12] = {
83 	{  0, { {  0,  0 }, {  0,  0 }, {   0,   0 }, {   0,   0 } } },
84 	{  0, { {  0,  0 }, {  0,  0 }, {   0,   0 }, {   0,   0 } } },
85 	{  0, { {  0,  0 }, {  0,  0 }, {   0,   0 }, {   0,   0 } } },
86 	{  0, { {  0,  0 }, {  0,  0 }, {   0,   0 }, {   0,   0 } } },
87 	{ 28, { { -0,  0 }, { -1,  6 }, {  -5,  11 }, { -10,  15 } } },
88 	{ 56, { {  0,  0 }, {  1,  6 }, {   5,  11 }, {  10,  15 } } },
89 	{ 40, { {  0,  0 }, {  6,  1 }, {  11,   5 }, {  15,  10 } } },
90 	{ 44, { {  0,  0 }, {  6, -1 }, {  11,  -5 }, {  15, -10 } } },
91 	{ 32, { { -0, -0 }, { -6, -1 }, { -11,  -5 }, { -15, -10 } } },
92 	{ 52, { { -0,  0 }, { -6,  1 }, { -11,   5 }, { -15,  10 } } },
93 	{ 36, { {  0, -0 }, {  1, -6 }, {   5, -11 }, {  10, -15 } } },
94 	{ 48, { { -0, -0 }, { -1, -6 }, {  -5, -11 }, { -10, -15 } } }
95 };
96 
validFollowerLocation(const Location & location)97 bool Actor::validFollowerLocation(const Location &location) {
98 	Point point;
99 	location.toScreenPointXY(point);
100 
101 	if ((point.x < 5) || (point.x >= _vm->getDisplayInfo().width - 5) ||
102 		(point.y < 0) || (point.y > _vm->_scene->getHeight())) {
103 		return false;
104 	}
105 
106 	return (_vm->_scene->canWalk(point));
107 }
108 
realLocation(Location & location,uint16 objectId,uint16 walkFlags)109 void Actor::realLocation(Location &location, uint16 objectId, uint16 walkFlags) {
110 	int angle;
111 	int distance;
112 	ActorData *actor;
113 	ObjectData *obj;
114 	debug (8, "Actor::realLocation objectId=%i", objectId);
115 	if (walkFlags & kWalkUseAngle) {
116 		if (_vm->_scene->getFlags() & kSceneFlagISO) {
117 			angle = (location.x + 2) & 15;
118 			distance = location.y;
119 
120 			location.u() = (angleLUT[angle][0] * distance) >> 8;
121 			location.v() = -(angleLUT[angle][1] * distance) >> 8;
122 		} else {
123 			angle = location.x & 15;
124 			distance = location.y;
125 
126 			location.x = (angleLUT[angle][0] * distance) >> 6;
127 			location.y = (angleLUT[angle][1] * distance) >> 6;
128 		}
129 	}
130 
131 	if (objectId != ID_NOTHING) {
132 		if (validActorId(objectId)) {
133 			actor = getActor(objectId);
134 			location.addXY(actor->_location);
135 		} else if (validObjId(objectId)) {
136 			obj = getObj(objectId);
137 			location.addXY(obj->_location);
138 		}
139 	}
140 }
141 
actorFaceTowardsObject(uint16 actorId,uint16 objectId)142 void Actor::actorFaceTowardsObject(uint16 actorId, uint16 objectId) {
143 	ActorData *actor;
144 	ObjectData *obj;
145 
146 	if (validActorId(objectId)) {
147 		actor = getActor(objectId);
148 		actorFaceTowardsPoint(actorId, actor->_location);
149 	} else if (validObjId(objectId)) {
150 		obj = getObj(objectId);
151 		actorFaceTowardsPoint(actorId, obj->_location);
152 	}
153 }
154 
actorFaceTowardsPoint(uint16 actorId,const Location & toLocation)155 void Actor::actorFaceTowardsPoint(uint16 actorId, const Location &toLocation) {
156 	ActorData *actor;
157 	Location delta;
158 	//debug (8, "Actor::actorFaceTowardsPoint actorId=%i", actorId);
159 	actor = getActor(actorId);
160 
161 	toLocation.delta(actor->_location, delta);
162 
163 	if (_vm->_scene->getFlags() & kSceneFlagISO) {
164 		if (delta.u() > 0) {
165 			actor->_facingDirection = (delta.v() > 0) ? kDirUp : kDirRight;
166 		} else {
167 			actor->_facingDirection = (delta.v() > 0) ? kDirLeft : kDirDown;
168 		}
169 	} else {
170 		if (ABS(delta.y) > ABS(delta.x * 2)) {
171 			actor->_facingDirection = (delta.y > 0) ? kDirDown : kDirUp;
172 		} else {
173 			actor->_facingDirection = (delta.x > 0) ? kDirRight : kDirLeft;
174 		}
175 	}
176 }
177 
updateActorsScene(int actorsEntrance)178 void Actor::updateActorsScene(int actorsEntrance) {
179 	int j;
180 	int followerDirection;
181 	Location tempLocation;
182 	Location possibleLocation;
183 	Point delta;
184 	const SceneEntry *sceneEntry;
185 
186 	if (_vm->_scene->currentSceneNumber() == 0) {
187 		error("Actor::updateActorsScene _vm->_scene->currentSceneNumber() == 0");
188 	}
189 
190 	_vm->_sound->stopVoice();
191 	_activeSpeech.stringsCount = 0;
192 	_activeSpeech.playing = false;
193 	_protagonist = NULL;
194 
195 	for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
196 		actor->_inScene = false;
197 		actor->_spriteList.clear();
198 		if ((actor->_flags & (kProtagonist | kFollower)) || (actor->_index == 0)) {
199 			if (actor->_flags & kProtagonist) {
200 				actor->_finalTarget = actor->_location;
201 				_centerActor = _protagonist = actor;
202 			} else if (_vm->getGameId() == GID_ITE &&
203 					   _vm->_scene->currentSceneResourceId() == ITE_SCENE_OVERMAP) {
204 				continue;
205 			}
206 
207 			actor->_sceneNumber = _vm->_scene->currentSceneNumber();
208 		}
209 		if (actor->_sceneNumber == _vm->_scene->currentSceneNumber()) {
210 			actor->_inScene = true;
211 			actor->_actionCycle = (_vm->_rnd.getRandomNumber(7) & 0x7) * 4; // 1/8th chance
212 			if (actor->_currentAction != kActionFreeze) {
213 				actor->_currentAction = kActionWait;
214 			}
215 		}
216 	}
217 
218 	// _protagonist can be null while loading a game from the command line
219 	if (_protagonist == NULL)
220 		return;
221 
222 	if ((actorsEntrance >= 0) && (!_vm->_scene->_entryList.empty())) {
223 		if (_vm->_scene->_entryList.size() <= uint(actorsEntrance)) {
224 			actorsEntrance = 0; //OCEAN bug
225 		}
226 
227 		sceneEntry = &_vm->_scene->_entryList[actorsEntrance];
228 		if (_vm->_scene->getFlags() & kSceneFlagISO) {
229 			_protagonist->_location = sceneEntry->location;
230 		} else {
231 			_protagonist->_location.x = sceneEntry->location.x * ACTOR_LMULT;
232 			_protagonist->_location.y = sceneEntry->location.y * ACTOR_LMULT;
233 			_protagonist->_location.z = sceneEntry->location.z * ACTOR_LMULT;
234 		}
235 		// Workaround for bug #1328045:
236 		// "When entering any of the houses at the start of the
237 		// game if you click on anything inside the building you
238 		// start walking through the door, turn around and leave."
239 		//
240 		// After stepping on an action zone, Rif is trying to exit.
241 		// Shift Rif's entry position to a non action zone area.
242 		if (_vm->getGameId() == GID_ITE) {
243 			if ((_vm->_scene->currentSceneNumber() >= 53) && (_vm->_scene->currentSceneNumber() <= 66))
244 				_protagonist->_location.y += 10;
245 		}
246 
247 		_protagonist->_facingDirection = _protagonist->_actionDirection = sceneEntry->facing;
248 	}
249 
250 	_protagonist->_currentAction = kActionWait;
251 
252 	if (_vm->_scene->getFlags() & kSceneFlagISO) {
253 		//nothing?
254 	} else {
255 		_vm->_scene->initDoorsState(); //TODO: move to _scene
256 	}
257 
258 	followerDirection = _protagonist->_facingDirection + 3;
259 	calcScreenPosition(_protagonist);
260 
261 	for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
262 		if (actor->_flags & (kFollower)) {
263 			actor->_facingDirection = actor->_actionDirection = _protagonist->_facingDirection;
264 			actor->_currentAction = kActionWait;
265 			actor->_walkStepsCount = actor->_walkStepIndex = 0;
266 			actor->_location.z = _protagonist->_location.z;
267 
268 
269 			if (_vm->_scene->getFlags() & kSceneFlagISO) {
270 				_vm->_isoMap->placeOnTileMap(_protagonist->_location, actor->_location, 3, followerDirection & 0x07);
271 			} else {
272 				followerDirection &= 0x07;
273 
274 				possibleLocation = _protagonist->_location;
275 
276 				delta.x = directionLUT[followerDirection][0];
277 				delta.y = directionLUT[followerDirection][1];
278 
279 				for (j = 0; j < 30; j++) {
280 					tempLocation = possibleLocation;
281 					tempLocation.x += delta.x;
282 					tempLocation.y += delta.y;
283 
284 					if (validFollowerLocation(tempLocation)) {
285 						possibleLocation = tempLocation;
286 					} else {
287 						tempLocation = possibleLocation;
288 						tempLocation.x += delta.x;
289 						if (validFollowerLocation(tempLocation)) {
290 							possibleLocation = tempLocation;
291 						} else {
292 							tempLocation = possibleLocation;
293 							tempLocation.y += delta.y;
294 							if (validFollowerLocation(tempLocation)) {
295 								possibleLocation = tempLocation;
296 							} else {
297 								break;
298 							}
299 						}
300 					}
301 				}
302 
303 				actor->_location = possibleLocation;
304 			}
305 			followerDirection += 2;
306 		}
307 
308 	}
309 
310 	handleActions(0, true);
311 	if (_vm->_scene->getFlags() & kSceneFlagISO) {
312 		_vm->_isoMap->adjustScroll(true);
313 	}
314 }
315 
handleActions(int msec,bool setup)316 void Actor::handleActions(int msec, bool setup) {
317 	ActorFrameRange *frameRange;
318 	int state;
319 	int speed;
320 	int32 framesLeft;
321 	Location delta;
322 	Location addDelta;
323 	int hitZoneIndex;
324 	const HitZone *hitZone;
325 	Point hitPoint;
326 	Location pickLocation;
327 
328 	for (ActorDataArray::iterator actor = _actors.begin(); actor != _actors.end(); ++actor) {
329 		if (!actor->_inScene)
330 			continue;
331 
332 		if ((_vm->getGameId() == GID_ITE) && (actor->_index == ACTOR_DRAGON_INDEX)) {
333 			moveDragon(actor);
334 			continue;
335 		}
336 
337 		switch (actor->_currentAction) {
338 		case kActionWait:
339 			if (!setup && (actor->_flags & kFollower)) {
340 				followProtagonist(actor);
341 				if (actor->_currentAction != kActionWait)
342 					break;
343 			}
344 
345 			if (actor->_targetObject != ID_NOTHING) {
346 				actorFaceTowardsObject(actor->_id, actor->_targetObject);
347 			}
348 
349 			if (actor->_flags & kCycle) {
350 				frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameStand));
351 				if (frameRange->frameCount > 0) {
352 					actor->_actionCycle++;
353 					actor->_actionCycle = (actor->_actionCycle) % frameRange->frameCount;
354 				} else {
355 					actor->_actionCycle = 0;
356 				}
357 				actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle;
358 				break;
359 			}
360 
361 			if ((actor->_actionCycle & 3) == 0) {
362 				actor->cycleWrap(100);
363 
364 				frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameWait));
365 				if ((frameRange->frameCount < 1 || actor->_actionCycle > 33))
366 					frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameStand));
367 
368 				if (frameRange->frameCount) {
369 					actor->_frameNumber = frameRange->frameIndex + (uint16)_vm->_rnd.getRandomNumber(frameRange->frameCount - 1);
370 				} else {
371 					actor->_frameNumber = frameRange->frameIndex;
372 				}
373 			}
374 			actor->_actionCycle++;
375 			break;
376 
377 		case kActionWalkToPoint:
378 		case kActionWalkToLink:
379 			if (_vm->_scene->getFlags() & kSceneFlagISO) {
380 				actor->_partialTarget.delta(actor->_location, delta);
381 
382 				while ((delta.u() == 0) && (delta.v() == 0)) {
383 
384 					if ((actor == _protagonist) && (_vm->mouseButtonPressed())) {
385 						_vm->_isoMap->screenPointToTileCoords(_vm->mousePos(), pickLocation);
386 
387 						if (!actorWalkTo(_protagonist->_id, pickLocation)) {
388 							break;
389 						}
390 					} else if (!_vm->_isoMap->nextTileTarget(actor) && !actorEndWalk(actor->_id, true)) {
391 						break;
392 					}
393 
394 					actor->_partialTarget.delta(actor->_location, delta);
395 					actor->_partialTarget.z = 0;
396 				}
397 
398 				if (actor->_flags & kFastest) {
399 					speed = 8;
400 				} else if (actor->_flags & kFaster) {
401 					speed = 6;
402 				} else {
403 					speed = 4;
404 				}
405 
406 				if (_vm->_scene->currentSceneResourceId() == ITE_SCENE_OVERMAP) {
407 					speed = 2;
408 				}
409 
410 				if ((actor->_actionDirection == 2) || (actor->_actionDirection == 6)) {
411 					speed = speed / 2;
412 				}
413 
414 				if (ABS(delta.v()) > ABS(delta.u())) {
415 					addDelta.v() = CLIP<int>(delta.v(), -speed, speed);
416 					if (addDelta.v() == delta.v()) {
417 						addDelta.u() = delta.u();
418 					} else {
419 						addDelta.u() = delta.u() * addDelta.v();
420 						addDelta.u() += (addDelta.u() > 0) ? (delta.v() / 2) : (-delta.v() / 2);
421 						addDelta.u() /= delta.v();
422 					}
423 				} else {
424 					addDelta.u() = CLIP<int>(delta.u(), -speed, speed);
425 					if (addDelta.u() == delta.u()) {
426 						addDelta.v() = delta.v();
427 					} else {
428 						addDelta.v() = delta.v() * addDelta.u();
429 						addDelta.v() += (addDelta.v() > 0) ? (delta.u() / 2) : (-delta.u() / 2);
430 						addDelta.v() /= delta.u();
431 					}
432 				}
433 
434 				actor->_location.add(addDelta);
435 			} else {
436 				actor->_partialTarget.delta(actor->_location, delta);
437 
438 				while ((delta.x == 0) && (delta.y == 0)) {
439 
440 					if (actor->_walkStepIndex >= actor->_walkStepsCount) {
441 						actorEndWalk(actor->_id, true);
442 						return;		// break out of select case
443 					}
444 
445 					actor->_partialTarget.fromScreenPoint(actor->_walkStepsPoints[actor->_walkStepIndex++]);
446 					if (_vm->getGameId() == GID_ITE) {
447 						if (actor->_partialTarget.x > 224 * 2 * ACTOR_LMULT) {
448 							actor->_partialTarget.x -= 256 * 2 * ACTOR_LMULT;
449 						}
450 					} else {
451 						if (actor->_partialTarget.x > 224 * 4 * ACTOR_LMULT) {
452 							actor->_partialTarget.x -= 256 * 4 * ACTOR_LMULT;
453 						}
454 					}
455 
456 					actor->_partialTarget.delta(actor->_location, delta);
457 
458 					if (ABS(delta.y) > ABS(delta.x)) {
459 						actor->_actionDirection = delta.y > 0 ? kDirDown : kDirUp;
460 					} else {
461 						actor->_actionDirection = delta.x > 0 ? kDirRight : kDirLeft;
462 					}
463 				}
464 
465 				if (_vm->getGameId() == GID_ITE)
466 					speed = (ACTOR_LMULT * 2 * actor->_screenScale + 63) / 256;
467 				else
468 					speed = (ACTOR_SPEED * actor->_screenScale + 128) >> 8;
469 
470 				if (speed < 1)
471 					speed = 1;
472 
473 				if (_vm->getGameId() == GID_IHNM)
474 					speed = speed / 2;
475 
476 				if ((actor->_actionDirection == kDirUp) || (actor->_actionDirection == kDirDown)) {
477 					addDelta.y = CLIP<int>(delta.y, -speed, speed);
478 					if (addDelta.y == delta.y) {
479 						addDelta.x = delta.x;
480 					} else {
481 						addDelta.x = delta.x * addDelta.y;
482 						addDelta.x += (addDelta.x > 0) ? (delta.y / 2) : (-delta.y / 2);
483 						addDelta.x /= delta.y;
484 						actor->_facingDirection = actor->_actionDirection;
485 					}
486 				} else {
487 					addDelta.x = CLIP<int>(delta.x, -2 * speed, 2 * speed);
488 					if (addDelta.x == delta.x) {
489 						addDelta.y = delta.y;
490 					} else {
491 						addDelta.y = delta.y * addDelta.x;
492 						addDelta.y += (addDelta.y > 0) ? (delta.x / 2) : (-delta.x / 2);
493 						addDelta.y /= delta.x;
494 						actor->_facingDirection = actor->_actionDirection;
495 					}
496 				}
497 
498 				actor->_location.add(addDelta);
499 			}
500 
501 			if (actor->_actorFlags & kActorBackwards) {
502 				actor->_facingDirection = (actor->_actionDirection + 4) & 7;
503 				actor->_actionCycle--;
504 			} else {
505 				actor->_actionCycle++;
506 			}
507 
508 			frameRange = getActorFrameRange(actor->_id, actor->_walkFrameSequence);
509 
510 			if (actor->_actionCycle < 0) {
511 				actor->_actionCycle = frameRange->frameCount - 1;
512 			} else if (actor->_actionCycle >= frameRange->frameCount) {
513 				actor->_actionCycle = 0;
514 			}
515 
516 			actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle;
517 			break;
518 
519 		case kActionWalkDir:
520 			if (_vm->_scene->getFlags() & kSceneFlagISO) {
521 				actor->_location.u() += tileDirectionLUT[actor->_actionDirection][0];
522 				actor->_location.v() += tileDirectionLUT[actor->_actionDirection][1];
523 
524 				frameRange = getActorFrameRange(actor->_id, actor->_walkFrameSequence);
525 
526 				actor->_actionCycle++;
527 				actor->cycleWrap(frameRange->frameCount);
528 				actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle;
529 			} else {
530 				if (_vm->getGameId() == GID_ITE) {
531 					actor->_location.x += directionLUT[actor->_actionDirection][0] * 2;
532 					actor->_location.y += directionLUT[actor->_actionDirection][1] * 2;
533 				} else {
534 					// FIXME: The original does not multiply by 8 here, but we do
535 					actor->_location.x += (directionLUT[actor->_actionDirection][0] * 8 * actor->_screenScale + 128) >> 8;
536 					actor->_location.y += (directionLUT[actor->_actionDirection][1] * 8 * actor->_screenScale + 128) >> 8;
537 				}
538 
539 				frameRange = getActorFrameRange(actor->_id, actor->_walkFrameSequence);
540 				actor->_actionCycle++;
541 				actor->cycleWrap(frameRange->frameCount);
542 				actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle;
543 			}
544 			break;
545 
546 		case kActionSpeak:
547 			actor->_actionCycle++;
548 			actor->cycleWrap(64);
549 
550 			frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameGesture));
551 			if (actor->_actionCycle >= frameRange->frameCount) {
552 				if (actor->_actionCycle & 1)
553 					break;
554 				frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameSpeak));
555 
556 				state = (uint16)_vm->_rnd.getRandomNumber(frameRange->frameCount);
557 
558 				if (state == 0) {
559 					frameRange = getActorFrameRange(actor->_id, getFrameType(kFrameStand));
560 				} else {
561 					state--;
562 				}
563 			} else {
564 				state = actor->_actionCycle;
565 			}
566 
567 			actor->_frameNumber = frameRange->frameIndex + state;
568 			break;
569 
570 		case kActionAccept:
571 		case kActionStoop:
572 			break;
573 
574 		case kActionCycleFrames:
575 		case kActionPongFrames:
576 			if (actor->_cycleTimeCount > 0) {
577 				actor->_cycleTimeCount--;
578 				break;
579 			}
580 
581 			actor->_cycleTimeCount = actor->_cycleDelay;
582 			actor->_actionCycle++;
583 
584 			frameRange = getActorFrameRange(actor->_id, actor->_cycleFrameSequence);
585 
586 			if (actor->_currentAction == kActionPongFrames) {
587 				if (actor->_actionCycle >= frameRange->frameCount * 2 - 2) {
588 					if (actor->_actorFlags & kActorContinuous) {
589 						actor->_actionCycle = 0;
590 					} else {
591 						actor->_currentAction = kActionFreeze;
592 						break;
593 					}
594 				}
595 
596 				state = actor->_actionCycle;
597 				if (state >= frameRange->frameCount) {
598 					state = frameRange->frameCount * 2 - 2 - state;
599 				}
600 			} else {
601 				if (actor->_actionCycle >= frameRange->frameCount) {
602 					if (actor->_actorFlags & kActorContinuous) {
603 						actor->_actionCycle = 0;
604 					} else {
605 						actor->_currentAction = kActionFreeze;
606 						break;
607 					}
608 				}
609 				state = actor->_actionCycle;
610 			}
611 
612 			if (frameRange->frameCount && (actor->_actorFlags & kActorRandom)) {
613 				state = _vm->_rnd.getRandomNumber(frameRange->frameCount - 1);
614 			}
615 
616 			if (actor->_actorFlags & kActorBackwards) {
617 				actor->_frameNumber = frameRange->frameIndex + frameRange->frameCount - 1 - state;
618 			} else {
619 				actor->_frameNumber = frameRange->frameIndex + state;
620 			}
621 			break;
622 
623 		case kActionFall:
624 			if (actor->_actionCycle > 0) {
625 				framesLeft = actor->_actionCycle--;
626 				actor->_finalTarget.delta(actor->_location, delta);
627 				delta.x /= framesLeft;
628 				delta.y /= framesLeft;
629 				actor->_location.addXY(delta);
630 				actor->_fallVelocity += actor->_fallAcceleration;
631 				actor->_fallPosition += actor->_fallVelocity;
632 				actor->_location.z = actor->_fallPosition >> 4;
633 			} else {
634 				actor->_location = actor->_finalTarget;
635 				actor->_currentAction = kActionFreeze;
636 				_vm->_script->wakeUpActorThread(kWaitTypeWalk, actor);
637 			}
638 			break;
639 
640 		case kActionClimb:
641 			actor->_cycleDelay++;
642 			if (actor->_cycleDelay & 3) {
643 				break;
644 			}
645 
646 			if (actor->_location.z >= actor->_finalTarget.z + ACTOR_CLIMB_SPEED) {
647 				actor->_location.z -= ACTOR_CLIMB_SPEED;
648 				actor->_actionCycle--;
649 			} else if (actor->_location.z <= actor->_finalTarget.z - ACTOR_CLIMB_SPEED) {
650 				actor->_location.z += ACTOR_CLIMB_SPEED;
651 				actor->_actionCycle++;
652 			} else {
653 				actor->_location.z = actor->_finalTarget.z;
654 				actor->_currentAction = kActionFreeze;
655 				_vm->_script->wakeUpActorThread(kWaitTypeWalk, actor);
656 			}
657 
658 			frameRange = getActorFrameRange(actor->_id, actor->_cycleFrameSequence);
659 
660 			if (actor->_actionCycle < 0) {
661 				actor->_actionCycle = frameRange->frameCount - 1;
662 			}
663 			actor->cycleWrap(frameRange->frameCount);
664 			actor->_frameNumber = frameRange->frameIndex + actor->_actionCycle;
665 			break;
666 		}
667 
668 		if ((actor->_currentAction >= kActionWalkToPoint) && (actor->_currentAction <= kActionWalkDir)) {
669 			hitZone = NULL;
670 
671 			if (_vm->_scene->getFlags() & kSceneFlagISO) {
672 				actor->_location.toScreenPointUV(hitPoint);
673 			} else {
674 				actor->_location.toScreenPointXY(hitPoint);
675 			}
676 			hitZoneIndex = _vm->_scene->_actionMap->hitTest(hitPoint);
677 			if (hitZoneIndex != -1) {
678 				hitZone = _vm->_scene->_actionMap->getHitZone(hitZoneIndex);
679 			}
680 
681 			// WORKAROUND for an incorrect hitzone which exists in IHNM
682 			// In Gorrister's chapter, in the toilet screen, the hitzone of the exit is
683 			// placed over the place where Gorrister sits to examine the graffiti on the wall
684 			// to the left, which makes him exit the screen when the graffiti is examined.
685 			// We effectively change the left side of the hitzone here so that it starts from
686 			// pixel 301 onwards. The same workaround is applied in Script::whichObject
687 			if (_vm->getGameId() == GID_IHNM) {
688 				if (_vm->_scene->currentChapterNumber() == 1 && _vm->_scene->currentSceneNumber() == 22)
689 					if (hitPoint.x <= 300)
690 						hitZone = NULL;
691 			}
692 
693 			if (hitZone != actor->_lastZone) {
694 				if (actor->_lastZone)
695 					stepZoneAction(actor, actor->_lastZone, true, false);
696 				actor->_lastZone = hitZone;
697 				// WORKAROUND for graphics glitch in the rat caves. Don't do this step zone action in the rat caves
698 				// (room 51) for hitzone 24577 (the door with the copy protection) to avoid the glitch. This glitch
699 				// happens because the copy protection is supposed to kick in at this point, but it's bypassed
700 				// (with permission from Wyrmkeep Entertainment)
701 				if (hitZone && !(_vm->getGameId() == GID_ITE && _vm->_scene->currentSceneNumber() == 51 && hitZone->getHitZoneId() == 24577)) {
702 					stepZoneAction(actor, hitZone, false, false);
703 				}
704 			}
705 		}
706 	}
707 	// Update frameCount for sfWaitFrames in IHNM
708 	_vm->_frameCount++;
709 }
710 
direct(int msec)711 void Actor::direct(int msec) {
712 
713 	if (_vm->_scene->_entryList.empty()) {
714 		return;
715 	}
716 
717 	if (_vm->_interface->_statusTextInput) {
718 		return;
719 	}
720 
721 	// FIXME: HACK. This should be turned into cycle event.
722 	_lastTickMsec += msec;
723 
724 	if (_lastTickMsec > 1000 / _handleActionDiv) {
725 		_lastTickMsec = 0;
726 		//process actions
727 		handleActions(msec, false);
728 	}
729 
730 //process speech
731 	handleSpeech(msec);
732 }
733 
followProtagonist(ActorData * actor)734 bool Actor::followProtagonist(ActorData *actor) {
735 	Location protagonistLocation;
736 	Location newLocation;
737 	Location delta;
738 	int protagonistBGMaskType;
739 	Point prefer1;
740 	Point prefer2;
741 	Point prefer3;
742 	int16 prefU;
743 	int16 prefV;
744 	int16 newU;
745 	int16 newV;
746 
747 	assert(_protagonist);
748 
749 	actor->_flags &= ~(kFaster | kFastest);
750 	protagonistLocation = _protagonist->_location;
751 	calcScreenPosition(_protagonist);
752 
753 	if (_vm->_scene->getFlags() & kSceneFlagISO) {
754 		prefU = 60;
755 		prefV = 60;
756 
757 
758 		actor->_location.delta(protagonistLocation, delta);
759 
760 		if (actor->_id == actorIndexToId(2)) {
761 			prefU = prefV = 48;
762 		}
763 
764 		if ((delta.u() > prefU) || (delta.u() < -prefU) || (delta.v() > prefV) || (delta.v() < -prefV)) {
765 
766 			if ((delta.u() > prefU * 2) || (delta.u() < -prefU * 2) || (delta.v() > prefV * 2) || (delta.v() < -prefV * 2)) {
767 				actor->_flags |= kFaster;
768 
769 				if ((delta.u() > prefU * 3) || (delta.u() < -prefU*3) || (delta.v() > prefV * 3) || (delta.v() < -prefV * 3)) {
770 					actor->_flags |= kFastest;
771 				}
772 			}
773 
774 			prefU /= 2;
775 			prefV /= 2;
776 
777 			newU = CLIP<int32>(delta.u(), -prefU, prefU) + protagonistLocation.u();
778 			newV = CLIP<int32>(delta.v(), -prefV, prefV) + protagonistLocation.v();
779 
780 			newLocation.u() = newU + _vm->_rnd.getRandomNumber(prefU - 1) - prefU / 2;
781 			newLocation.v() = newV + _vm->_rnd.getRandomNumber(prefV - 1) - prefV / 2;
782 			newLocation.z = 0;
783 
784 			return actorWalkTo(actor->_id, newLocation);
785 		}
786 
787 	} else {
788 		prefer1.x = (100 * _protagonist->_screenScale) >> 8;
789 		prefer1.y = (50 * _protagonist->_screenScale) >> 8;
790 
791 		if (_protagonist->_currentAction == kActionWalkDir) {
792 			prefer1.x /= 2;
793 		}
794 
795 		if (prefer1.x < 8) {
796 			prefer1.x = 8;
797 		}
798 
799 		if (prefer1.y < 8) {
800 			prefer1.y = 8;
801 		}
802 
803 		prefer2.x = prefer1.x * 2;
804 		prefer2.y = prefer1.y * 2;
805 		prefer3.x = prefer1.x + prefer1.x / 2;
806 		prefer3.y = prefer1.y + prefer1.y / 2;
807 
808 		actor->_location.delta(protagonistLocation, delta);
809 
810 		protagonistBGMaskType = 0;
811 		if (_vm->_scene->isBGMaskPresent() && _vm->_scene->validBGMaskPoint(_protagonist->_screenPosition)) {
812 			protagonistBGMaskType = _vm->_scene->getBGMaskType(_protagonist->_screenPosition);
813 		}
814 
815 		if ((_vm->_rnd.getRandomNumber(7) & 0x7) == 0) // 1/8th chance
816 			actor->_actorFlags &= ~kActorNoFollow;
817 
818 		if (actor->_actorFlags & kActorNoFollow) {
819 			return false;
820 		}
821 
822 		if ((delta.x > prefer2.x) || (delta.x < -prefer2.x) ||
823 			(delta.y > prefer2.y) || (delta.y < -prefer2.y) ||
824 			((_protagonist->_currentAction == kActionWait) &&
825 			(delta.x * 2 < prefer1.x) && (delta.x * 2 > -prefer1.x) &&
826 			(delta.y < prefer1.y) && (delta.y > -prefer1.y))) {
827 
828 				if (ABS(delta.x) > ABS(delta.y)) {
829 
830 					delta.x = (delta.x > 0) ? prefer3.x : -prefer3.x;
831 
832 					newLocation.x = delta.x + protagonistLocation.x;
833 					newLocation.y = CLIP<int32>(delta.y, -prefer2.y, prefer2.y) + protagonistLocation.y;
834 				} else {
835 					delta.y = (delta.y > 0) ? prefer3.y : -prefer3.y;
836 
837 					newLocation.x = CLIP<int32>(delta.x, -prefer2.x, prefer2.x) + protagonistLocation.x;
838 					newLocation.y = delta.y + protagonistLocation.y;
839 				}
840 				newLocation.z = 0;
841 
842 				if (protagonistBGMaskType != 3) {
843 					newLocation.x += _vm->_rnd.getRandomNumber(prefer1.x - 1) - prefer1.x / 2;
844 					newLocation.y += _vm->_rnd.getRandomNumber(prefer1.y - 1) - prefer1.y / 2;
845 				}
846 
847 				newLocation.x = CLIP<int>(newLocation.x, -31 * 4, (_vm->getDisplayInfo().width + 31) * 4);
848 
849 				return actorWalkTo(actor->_id, newLocation);
850 			}
851 	}
852 	return false;
853 }
854 
actorWalkTo(uint16 actorId,const Location & toLocation)855 bool Actor::actorWalkTo(uint16 actorId, const Location &toLocation) {
856 	ActorData *actor;
857 
858 	Rect testBox;
859 	Rect testBox2;
860 	Point anotherActorScreenPosition;
861 	Point collision;
862 	Point pointFrom, pointTo, pointBest, pointAdd;
863 	Point delta, bestDelta;
864 	Point tempPoint;
865 	bool extraStartNode;
866 	bool extraEndNode;
867 
868 	actor = getActor(actorId);
869 
870 	if (actor == _protagonist) {
871 		_vm->_scene->setDoorState(2, 0xff);		// closed
872 		_vm->_scene->setDoorState(3, 0);		// open
873 	} else {
874 		_vm->_scene->setDoorState(2, 0);		// open
875 		_vm->_scene->setDoorState(3, 0xff);		// closed
876 	}
877 
878 	if (_vm->_scene->getFlags() & kSceneFlagISO) {
879 
880 		if ((_vm->getGameId() == GID_ITE) && (actor->_index == ACTOR_DRAGON_INDEX)) {
881 			return false;
882 		}
883 
884 		actor->_finalTarget = toLocation;
885 		actor->_walkStepsCount = 0;
886 		_vm->_isoMap->findTilePath(actor, actor->_location, toLocation);
887 
888 
889 		if ((actor->_walkStepsCount == 0) && (actor->_flags & kProtagonist)) {
890 			actor->_actorFlags |= kActorNoCollide;
891 			_vm->_isoMap->findTilePath(actor, actor->_location, toLocation);
892 		}
893 
894 		actor->_walkStepIndex = 0;
895 		if (_vm->_isoMap->nextTileTarget(actor)) {
896 			actor->_currentAction = kActionWalkToPoint;
897 			actor->_walkFrameSequence = getFrameType(kFrameWalk);
898 		} else {
899 			actorEndWalk(actorId, false);
900 			return false;
901 		}
902 	} else {
903 
904 		actor->_location.toScreenPointXY(pointFrom);
905 		// FIXME: why is the following line needed?
906 		pointFrom.x &= ~1;	// set last bit to 0
907 
908 		extraStartNode = _vm->_scene->offscreenPath(pointFrom);
909 
910 		toLocation.toScreenPointXY(pointTo);
911 		// FIXME: why is the following line needed?
912 		pointTo.x &= ~1;	// set last bit to 0
913 
914 		// Are we already where we want to go?
915 		if (pointFrom.x == pointTo.x && pointFrom.y == pointTo.y) {
916 			actor->_walkStepsCount = 0;
917 			actorEndWalk(actorId, false);
918 			return false;
919 		}
920 
921 		extraEndNode = _vm->_scene->offscreenPath(pointTo);
922 
923 		if (_vm->_scene->isBGMaskPresent()) {
924 
925 			if (
926 				((actor->_currentAction >= kActionWalkToPoint && actor->_currentAction <= kActionWalkDir) ||
927 				(_vm->getGameId() == GID_ITE && actor == _protagonist)) &&
928 				!_vm->_scene->canWalk(pointFrom)) {
929 
930 				int max = _vm->getGameId() == GID_ITE ? 8 : 4;
931 
932 				for (int i = 1; i < max; i++) {
933 					pointAdd = pointFrom;
934 					pointAdd.y += i;
935 					if (_vm->_scene->canWalk(pointAdd)) {
936 						pointFrom = pointAdd;
937 						break;
938 					}
939 					pointAdd = pointFrom;
940 					pointAdd.y -= i;
941 					if (_vm->_scene->canWalk(pointAdd)) {
942 						pointFrom = pointAdd;
943 						break;
944 					}
945 					if (_vm->getGameId() == GID_ITE) {
946 						pointAdd = pointFrom;
947 						pointAdd.x += i;
948 						if (_vm->_scene->canWalk(pointAdd)) {
949 							pointFrom = pointAdd;
950 							break;
951 						}
952 						pointAdd = pointFrom;
953 						pointAdd.x -= i;
954 						if (_vm->_scene->canWalk(pointAdd)) {
955 							pointFrom = pointAdd;
956 							break;
957 						}
958 					}
959 				}
960 			}
961 
962 			_barrierCount = 0;
963 			if (!(actor->_actorFlags & kActorNoCollide)) {
964 				collision.x = ACTOR_COLLISION_WIDTH * actor->_screenScale / (256 * 2);
965 				collision.y = ACTOR_COLLISION_HEIGHT * actor->_screenScale / (256 * 2);
966 
967 				for (ActorDataArray::iterator anotherActor = _actors.begin(); (anotherActor != _actors.end()) && (_barrierCount < ACTOR_BARRIERS_MAX); ++anotherActor) {
968 					if (!anotherActor->_inScene)
969 						continue;
970 					if (anotherActor == actor)
971 						continue;
972 
973 					anotherActorScreenPosition = anotherActor->_screenPosition;
974 					testBox.left = (anotherActorScreenPosition.x - collision.x) & ~1;
975 					testBox.right = (anotherActorScreenPosition.x + collision.x) & ~1;
976 					testBox.top = anotherActorScreenPosition.y - collision.y;
977 					testBox.bottom = anotherActorScreenPosition.y + collision.y;
978 					testBox2 = testBox;
979 					testBox2.left -= 2;
980 					testBox2.right += 2;
981 					testBox2.top -= 1;
982 					testBox2.bottom += 1;
983 
984 					if (testBox2.contains(pointFrom)) {
985 						if (pointFrom.x > anotherActorScreenPosition.x + 4) {
986 							testBox.right = pointFrom.x - 2;
987 						} else if (pointFrom.x < anotherActorScreenPosition.x - 4) {
988 							testBox.left = pointFrom.x + 2;
989 						} else if (pointFrom.y > anotherActorScreenPosition.y) {
990 							testBox.bottom = pointFrom.y - 1;
991 						} else {
992 							testBox.top = pointFrom.y + 1;
993 						}
994 					}
995 
996 					if ((testBox.width() > 0) && (testBox.height() > 0)) {
997 						_barrierList[_barrierCount++] = testBox;
998 					}
999 				}
1000 			}
1001 
1002 
1003 			pointBest = pointTo;
1004 			actor->_walkStepsCount = 0;
1005 			findActorPath(actor, pointFrom, pointTo);
1006 
1007 			if (actor->_walkStepsCount == 0) {
1008 				error("actor->_walkStepsCount == 0");
1009 			}
1010 
1011 			if (extraStartNode) {
1012 				actor->_walkStepIndex = 0;
1013 			} else {
1014 				// FIXME: Why is this needed?
1015 				actor->_walkStepIndex = 1;
1016 			}
1017 
1018 			if (extraEndNode) {
1019 				toLocation.toScreenPointXY(tempPoint);
1020 				actor->_walkStepsCount--;
1021 				actor->addWalkStepPoint(tempPoint);
1022 			}
1023 
1024 
1025 			pointBest = actor->_walkStepsPoints[actor->_walkStepsCount - 1];
1026 
1027 			pointBest.x &= ~1;
1028 			delta.x = ABS(pointFrom.x - pointTo.x);
1029 			delta.y = ABS(pointFrom.y - pointTo.y);
1030 
1031 			bestDelta.x = ABS(pointBest.x - pointTo.x);
1032 			bestDelta.y = ABS(pointBest.y - pointTo.y);
1033 
1034 			if ((delta.x + delta.y <= bestDelta.x + bestDelta.y) && (actor->_flags & kFollower)) {
1035 				actor->_actorFlags |= kActorNoFollow;
1036 			}
1037 
1038 			if (pointBest == pointFrom) {
1039 				actor->_walkStepsCount = 0;
1040 			}
1041 		} else {
1042 			actor->_walkStepsCount = 0;
1043 			actor->addWalkStepPoint(pointTo);
1044 			actor->_walkStepIndex = 0;
1045 		}
1046 
1047 		actor->_partialTarget = actor->_location;
1048 		actor->_finalTarget = toLocation;
1049 		if (actor->_walkStepsCount == 0) {
1050 			actorEndWalk(actorId, false);
1051 			return false;
1052 		} else {
1053 			if (actor->_flags & kProtagonist) {
1054 				_actors[1]._actorFlags &= ~kActorNoFollow; // TODO: mark all actors with kFollower flag, not only 1 and 2
1055 				_actors[2]._actorFlags &= ~kActorNoFollow;
1056 			}
1057 			actor->_currentAction = (actor->_walkStepsCount >= ACTOR_MAX_STEPS_COUNT) ? kActionWalkToLink : kActionWalkToPoint;
1058 			actor->_walkFrameSequence = getFrameType(kFrameWalk);
1059 		}
1060 	}
1061 	return true;
1062 }
1063 
actorEndWalk(uint16 actorId,bool recurse)1064 bool Actor::actorEndWalk(uint16 actorId, bool recurse) {
1065 	bool walkMore = false;
1066 	ActorData *actor;
1067 	const HitZone *hitZone;
1068 	int hitZoneIndex;
1069 	Point testPoint;
1070 
1071 	actor = getActor(actorId);
1072 	actor->_actorFlags &= ~kActorBackwards;
1073 
1074 	if (_vm->getGameId() == GID_ITE) {
1075 
1076 		if (actor->_location.distance(actor->_finalTarget) > 8 && (actor->_flags & kProtagonist) && recurse && !(actor->_actorFlags & kActorNoCollide)) {
1077 			actor->_actorFlags |= kActorNoCollide;
1078 			return actorWalkTo(actorId, actor->_finalTarget);
1079 		}
1080 	}
1081 
1082 	actor->_currentAction = kActionWait;
1083 	actor->_actionCycle = 0;
1084 	if (actor->_actorFlags & kActorFinalFace) {
1085 		actor->_facingDirection = actor->_actionDirection = (actor->_actorFlags >> 6) & 0x07; //?
1086 	}
1087 
1088 	actor->_actorFlags &= ~(kActorNoCollide | kActorCollided | kActorFinalFace | kActorFacingMask);
1089 	actor->_flags &= ~(kFaster | kFastest);
1090 
1091 	if (actor == _protagonist) {
1092 		_vm->_script->wakeUpActorThread(kWaitTypeWalk, actor);
1093 		if (_vm->_script->_pendingVerb == _vm->_script->getVerbType(kVerbWalkTo)) {
1094 			if (_vm->getGameId() == GID_ITE)
1095 				actor->_location.toScreenPointUV(testPoint); // it's wrong calculation, but it is used in ITE
1096 			else
1097 				actor->_location.toScreenPointXY(testPoint);
1098 
1099 			hitZoneIndex = _vm->_scene->_actionMap->hitTest(testPoint);
1100 			if (hitZoneIndex != -1) {
1101 				hitZone = _vm->_scene->_actionMap->getHitZone(hitZoneIndex);
1102 				stepZoneAction(actor, hitZone, false, true);
1103 			} else {
1104 				_vm->_script->setNoPendingVerb();
1105 			}
1106 		} else if (_vm->_script->_pendingVerb != _vm->_script->getVerbType(kVerbNone)) {
1107 			_vm->_script->doVerb();
1108 		}
1109 	} else {
1110 		if (recurse && (actor->_flags & kFollower))
1111 			walkMore = followProtagonist(actor);
1112 
1113 		_vm->_script->wakeUpActorThread(kWaitTypeWalk, actor);
1114 	}
1115 	return walkMore;
1116 }
1117 
moveDragon(ActorData * actor)1118 void Actor::moveDragon(ActorData *actor) {
1119 	int16 dir0, dir1, dir2, dir3;
1120 	int16 moveType;
1121 	Event event;
1122 	const DragonMove *dragonMove;
1123 
1124 	if ((actor->_actionCycle < 0) ||
1125 		((actor->_actionCycle == 0) && (actor->_dragonMoveType >= ACTOR_DRAGON_TURN_MOVES))) {
1126 
1127 		moveType = kDragonMoveInvalid;
1128 		if (actor->_location.distance(_protagonist->_location) < 24) {
1129 			if (_dragonHunt && (_protagonist->_currentAction != kActionFall)) {
1130 				event.type = kEvTOneshot;
1131 				event.code = kScriptEvent;
1132 				event.op = kEventExecNonBlocking;
1133 				event.time = 0;
1134 				event.param = _vm->_scene->getScriptModuleNumber(); // module number
1135 				event.param2 = ACTOR_EXP_KNOCK_RIF;			// script entry point number
1136 				event.param3 = -1;		// Action
1137 				event.param4 = -1;		// Object
1138 				event.param5 = -1;		// With Object
1139 				event.param6 = -1;		// Actor
1140 				_vm->_events->queue(event);
1141 
1142 				_dragonHunt = false;
1143 			}
1144 		} else {
1145 			_dragonHunt = true;
1146 		}
1147 
1148 		if (actor->_walkStepIndex + 2 > actor->_walkStepsCount) {
1149 
1150 			_vm->_isoMap->findDragonTilePath(actor, actor->_location, _protagonist->_location, actor->_actionDirection);
1151 
1152 			if (actor->_walkStepsCount == 0) {
1153 				_vm->_isoMap->findDragonTilePath(actor, actor->_location, _protagonist->_location, 0);
1154 			}
1155 
1156 			if (actor->_walkStepsCount < 2) {
1157 				return;
1158 			}
1159 
1160 			actor->_partialTarget = actor->_location;
1161 			actor->_finalTarget = _protagonist->_location;
1162 			actor->_walkStepIndex = 0;
1163 		}
1164 
1165 		dir0 = actor->_actionDirection;
1166 		dir1 = actor->_tileDirections[actor->_walkStepIndex++];
1167 		dir2 = actor->_tileDirections[actor->_walkStepIndex];
1168 		// Fix for Bug #3324850 ("ITE (SAGA): crash in dog sewers")
1169 		// If there were more than two steps left, get the third (next) step.
1170 		// Otherwise, store the second step again so the anim looks right.
1171 		// (If you stop the move instead, Rif isn't automatically knocked into
1172 		// the Sewer.)
1173 		if (actor->_walkStepIndex + 1 < actor->_walkStepsCount)
1174 			dir3 = actor->_tileDirections[actor->_walkStepIndex + 1];
1175 		else
1176 			dir3 = dir2;
1177 
1178 		if (dir0 != dir1){
1179 			actor->_actionDirection = dir0 = dir1;
1180 		}
1181 
1182 		actor->_location = actor->_partialTarget;
1183 
1184 		if ((dir1 != dir2) && (dir1 == dir3)) {
1185 			switch (dir1) {
1186 			case kDirUpLeft:
1187 				actor->_partialTarget.v() += 16;
1188 				moveType = kDragonMoveUpLeft;
1189 				break;
1190 			case kDirDownLeft:
1191 				actor->_partialTarget.u() -= 16;
1192 				moveType = kDragonMoveDownLeft;
1193 				break;
1194 			case kDirDownRight:
1195 				actor->_partialTarget.v() -= 16;
1196 				moveType = kDragonMoveDownRight;
1197 				break;
1198 			case kDirUpRight:
1199 				actor->_partialTarget.u() += 16;
1200 				moveType = kDragonMoveUpRight;
1201 				break;
1202 			}
1203 
1204 			switch (dir2) {
1205 			case kDirUpLeft:
1206 				actor->_partialTarget.v() += 16;
1207 				break;
1208 			case kDirDownLeft:
1209 				actor->_partialTarget.u() -= 16;
1210 				break;
1211 			case kDirDownRight:
1212 				actor->_partialTarget.v() -= 16;
1213 				break;
1214 			case kDirUpRight:
1215 				actor->_partialTarget.u() += 16;
1216 				break;
1217 			}
1218 
1219 			actor->_walkStepIndex++;
1220 		} else {
1221 			switch (dir1) {
1222 			case kDirUpLeft:
1223 				actor->_partialTarget.v() += 16;
1224 				switch (dir2) {
1225 				case kDirDownLeft:
1226 					moveType = kDragonMoveUpLeft_Left;
1227 					actor->_partialTarget.u() -= 16;
1228 					break;
1229 				case kDirUpLeft:
1230 					moveType = kDragonMoveUpLeft;
1231 					break;
1232 				case kDirUpRight:
1233 					actor->_partialTarget.u() += 16;
1234 					moveType = kDragonMoveUpLeft_Right;
1235 					break;
1236 				default:
1237 					actor->_actionDirection = dir1;
1238 					actor->_walkStepsCount = 0;
1239 					break;
1240 				}
1241 				break;
1242 			case kDirDownLeft:
1243 				actor->_partialTarget.u() -= 16;
1244 				switch (dir2) {
1245 				case kDirDownRight:
1246 					moveType = kDragonMoveDownLeft_Left;
1247 					actor->_partialTarget.v() -= 16;
1248 					break;
1249 				case kDirDownLeft:
1250 					moveType = kDragonMoveDownLeft;
1251 					break;
1252 				case kDirUpLeft:
1253 					moveType = kDragonMoveDownLeft_Right;
1254 					actor->_partialTarget.v() += 16;
1255 					break;
1256 				default:
1257 					actor->_actionDirection = dir1;
1258 					actor->_walkStepsCount = 0;
1259 					break;
1260 				}
1261 				break;
1262 			case kDirDownRight:
1263 				actor->_partialTarget.v() -= 16;
1264 				switch (dir2) {
1265 				case kDirUpRight:
1266 					moveType = kDragonMoveDownRight_Left;
1267 					actor->_partialTarget.u() += 16;
1268 					break;
1269 				case kDirDownRight:
1270 					moveType = kDragonMoveDownRight;
1271 					break;
1272 				case kDirDownLeft:
1273 					moveType = kDragonMoveDownRight_Right;
1274 					actor->_partialTarget.u() -= 16;
1275 					break;
1276 				default:
1277 					actor->_actionDirection = dir1;
1278 					actor->_walkStepsCount = 0;
1279 					break;
1280 				}
1281 				break;
1282 			case kDirUpRight:
1283 				actor->_partialTarget.u() += 16;
1284 				switch (dir2) {
1285 				case kDirUpLeft:
1286 					moveType = kDragonMoveUpRight_Left;
1287 					actor->_partialTarget.v() += 16;
1288 					break;
1289 				case kDirUpRight:
1290 					moveType = kDragonMoveUpRight;
1291 					break;
1292 				case kDirDownRight:
1293 					moveType = kDragonMoveUpRight_Right;
1294 					actor->_partialTarget.v() -= 16;
1295 					break;
1296 				default:
1297 					actor->_actionDirection = dir1;
1298 					actor->_walkStepsCount = 0;
1299 					break;
1300 				}
1301 				break;
1302 
1303 			default:
1304 				actor->_actionDirection = dir1;
1305 				actor->_walkStepsCount = 0;
1306 				break;
1307 			}
1308 		}
1309 
1310 		actor->_dragonMoveType = moveType;
1311 
1312 		if (moveType >= ACTOR_DRAGON_TURN_MOVES) {
1313 			actor->_dragonStepCycle = 0;
1314 			actor->_actionCycle = 4;
1315 			actor->_walkStepIndex++;
1316 		} else {
1317 			actor->_actionCycle = 4;
1318 		}
1319 	}
1320 
1321 	actor->_actionCycle--;
1322 
1323 	if ((actor->_walkStepsCount < 1) || (actor->_actionCycle < 0)) {
1324 		return;
1325 	}
1326 
1327 	if (actor->_dragonMoveType < ACTOR_DRAGON_TURN_MOVES) {
1328 
1329 		actor->_dragonStepCycle++;
1330 		if (actor->_dragonStepCycle >= 7) {
1331 			actor->_dragonStepCycle = 0;
1332 		}
1333 
1334 		actor->_dragonBaseFrame = actor->_dragonMoveType * 7;
1335 
1336 		if (actor->_location.u() > actor->_partialTarget.u() + 3) {
1337 			actor->_location.u() -= 4;
1338 		} else if (actor->_location.u() < actor->_partialTarget.u() - 3) {
1339 			actor->_location.u() += 4;
1340 		} else {
1341 			actor->_location.u() = actor->_partialTarget.u();
1342 		}
1343 
1344 		if (actor->_location.v() > actor->_partialTarget.v() + 3) {
1345 			actor->_location.v() -= 4;
1346 		} else if (actor->_location.v() < actor->_partialTarget.v() - 3) {
1347 			actor->_location.v() += 4;
1348 		} else {
1349 			actor->_location.v() = actor->_partialTarget.v();
1350 		}
1351 	} else {
1352 		dragonMove = &dragonMoveTable[actor->_dragonMoveType];
1353 		actor->_dragonBaseFrame = dragonMove->baseFrame;
1354 
1355 
1356 		actor->_location.u() = actor->_partialTarget.u() - dragonMove->offset[actor->_actionCycle][0];
1357 		actor->_location.v() = actor->_partialTarget.v() - dragonMove->offset[actor->_actionCycle][1];
1358 
1359 		actor->_dragonStepCycle++;
1360 		if (actor->_dragonStepCycle >= 3) {
1361 			actor->_dragonStepCycle = 3;
1362 		}
1363 	}
1364 
1365 	actor->_frameNumber = actor->_dragonBaseFrame + actor->_dragonStepCycle;
1366 }
1367 
1368 // Console wrappers - must be safe to run
1369 
cmdActorWalkTo(int argc,const char ** argv)1370 void Actor::cmdActorWalkTo(int argc, const char **argv) {
1371 	uint16 actorId = (uint16) atoi(argv[1]);
1372 	Location location;
1373 	Point movePoint;
1374 
1375 	movePoint.x = atoi(argv[2]);
1376 	movePoint.y = atoi(argv[3]);
1377 
1378 	location.fromScreenPoint(movePoint);
1379 
1380 	if (!validActorId(actorId)) {
1381 		_vm->_console->debugPrintf("Actor::cmActorWalkTo Invalid actorId 0x%X.\n", actorId);
1382 		return;
1383 	}
1384 
1385 	actorWalkTo(actorId, location);
1386 }
1387 
1388 } // End of namespace Saga
1389