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