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  * aint32 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  * Based on the original sources
23  *   Faery Tale II -- The Halls of the Dead
24  *   (c) 1993-1996 The Wyrmkeep Entertainment Co.
25  */
26 
27 #include "saga2/saga2.h"
28 #include "saga2/dispnode.h"
29 #include "saga2/tile.h"
30 #include "saga2/motion.h"
31 #include "saga2/tilemode.h"
32 #include "saga2/magic.h"
33 #include "saga2/spellbuk.h"
34 #include "saga2/contain.h"
35 #include "saga2/intrface.h"
36 
37 namespace Saga2 {
38 
39 //  Turns on visual debugging aids
40 #define VISUAL1     0
41 
42 /* ===================================================================== *
43    Globals
44  * ===================================================================== */
45 
46 bool    interruptableMotionsPaused;
47 
48 /* ===================================================================== *
49    Test Functions
50  * ===================================================================== */
51 
52 bool unstickObject(GameObject *obj);
53 int32 currentGamePerformance(void);
54 
55 /* ===================================================================== *
56    Functions
57  * ===================================================================== */
58 
59 /*  Different motion types we want to simulate:
60 
61     Transient motions
62     ~~~~~~~~~~~~~~~~~
63 x       Walk to Point
64         Walk to Object (moving)
65 x       Run
66         Ballistic Motion
67             Jump up (vertical motion only)
68             Leap to X distance
69             Running Leap
70 x           Fall off of cliff or height
71             Object Ballistic Motion
72         Climb
73             Climb up ladder
74             Climb up Rope
75             Climb up Ledge
76         Talk & Gesture
77         Give Item (requires cooperation)
78         Stoop to pick up item
79         Stoop to dodge
80         Fight
81             Cast Magic spell
82             Swing High
83             Swing Low
84             Parry
85             Lunge
86             Shoot Bow
87         Consume Food
88 x       Cycle through arbitrary frames
89 x           Forward
90 x           Backward
91 x           Random
92 x           Ping-Pong
93 x           Looped
94 x           Single
95         Die
96 
97     Persistent Motions
98     ~~~~~~~~~~~~~~~~~~
99         Wait cycle / Twitch (not for FTA, but eventually)
100         Be Dead
101         Sleep
102         Sit
103 */
104 
105 
106 /* ===================================================================== *
107     Motion Constants
108  * ===================================================================== */
109 
110 const StaticTilePoint dirTable[8] = {
111 	{ 2,  2, 0},
112 	{ 0,  3, 0},
113 	{-2,  2, 0},
114 	{-3,  0, 0},
115 	{-2, -2, 0},
116 	{ 0, -3, 0},
117 	{ 2, -2, 0},
118 	{ 3,  0, 0}
119 };
120 
121 //  Incremental direction table
122 const StaticTilePoint incDirTable[8] = {
123 	{ 1,  1, 0},
124 	{ 0,  1, 0},
125 	{-1,  1, 0},
126 	{-1,  0, 0},
127 	{-1, -1, 0},
128 	{ 0, -1, 0},
129 	{ 1, -1, 0},
130 	{ 1,  0, 0}
131 };
132 
133 extern uint16   uMaxMasks[4],
134        uMinMasks[4],
135        vMaxMasks[4],
136        vMinMasks[4];
137 
138 extern SpellStuff *spellBook;
139 void fallingDamage(GameObject *obj, int16 speed);
140 
141 /* ===================================================================== *
142    PathMinder
143  * ===================================================================== */
144 
getPathFindIQ(GameObject * obj)145 int32 getPathFindIQ(GameObject *obj) {
146 	int32 pfIQ = 50;
147 	if (isActor(obj)) {
148 		Actor   *a = (Actor *)obj;
149 
150 		if (a == getCenterActor())
151 			pfIQ = 400;
152 		else if (isPlayerActor(a))
153 			pfIQ = 300;
154 		else {
155 			if (objRoofRipped(obj))
156 				pfIQ = 75;
157 			else if (a->_disposition == 1)
158 				pfIQ = 250;
159 			else
160 				pfIQ = 100;
161 			if (g_vm->_rnd->getRandomNumber(9) == 5)
162 				pfIQ += 200;
163 
164 		}
165 		int32 p = clamp(50, currentGamePerformance(), 200);
166 		pfIQ = (pfIQ * p) / 200;
167 	}
168 
169 
170 
171 	return pfIQ;
172 }
173 
174 /* ===================================================================== *
175    Utility functions
176  * ===================================================================== */
177 
178 //  This subroutine detects if the actor has landed on an active
179 //  tile, and checks to see if the active tile's script should
180 //  be triggered.
181 
setObjectSurface(GameObject * obj,StandingTileInfo & sti)182 void setObjectSurface(GameObject *obj, StandingTileInfo &sti) {
183 	ActiveItemID    tagID = sti.surfaceTAG != NULL
184 	                        ?   sti.surfaceTAG->thisID()
185 	                        :   NoActiveItem;
186 
187 	if (!(sti.surfaceRef.flags & trTileSensitive))
188 		tagID = NoActiveItem;
189 
190 	if (obj->_data.currentTAG != tagID) {
191 		ObjectID    objID = obj->thisID(),
192 		            enactorID = isActor(objID) ? objID : Nothing;
193 
194 		if (obj->_data.currentTAG != NoActiveItem) {
195 			ActiveItem  *oldTAG =
196 			    ActiveItem::activeItemAddress(obj->_data.currentTAG);
197 
198 			oldTAG->release(enactorID, objID);
199 
200 			obj->_data.currentTAG = NoActiveItem;
201 		}
202 
203 		if (tagID != NoActiveItem) {
204 			if (sti.surfaceTAG->trigger(enactorID, objID))
205 				obj->_data.currentTAG = tagID;
206 		}
207 	}
208 }
209 
spinLeft(int16 dir,int16 amt=1)210 inline int16 spinLeft(int16 dir, int16 amt = 1) {
211 	return (dir + amt) & 7;
212 }
spinRight(int16 dir,int16 amt=1)213 inline int16 spinRight(int16 dir, int16 amt = 1) {
214 	return (dir - amt) & 7;
215 }
216 
217 //  Special code to avoid actors sticking in walls, which occasionally
218 //  happens due to the point-sampled nature of the environment.
219 
unstickObject(GameObject * obj)220 bool unstickObject(GameObject *obj) {
221 	assert(isObject(obj) || isActor(obj));
222 
223 	TilePoint       pos;
224 	int16           mapNum;
225 	bool            outside;
226 
227 	mapNum = obj->getMapNum();
228 	outside = objRoofID(obj, mapNum, obj->getLocation()) == 0;
229 
230 	if (checkBlocked(obj, obj->getLocation()) == blockageNone)
231 		return false;
232 
233 #if 1
234 #if DEBUG
235 	WriteStatusF(9, "Unsticking");
236 #endif
237 	//  A stochastic unsticker, written by Talin
238 	//  Basically, it tightens the constraints each time a solution
239 	//  is found.
240 	int32           radius = 256;
241 	int16           objZ = obj->getLocation().z;
242 	TilePoint       bestPos;
243 
244 	for (int tries = 128; tries >= 0; tries--) {
245 		int32       dx = g_vm->_rnd->getRandomNumber(radius * 2) - radius,
246 		            dy = g_vm->_rnd->getRandomNumber(radius * 2) - radius,
247 		            dz = g_vm->_rnd->getRandomNumber(radius * 2) - radius;
248 		int16       tHeight;
249 
250 		//  Compute the actual _data.location of the new point
251 		pos = obj->getLocation() + TilePoint(dx, dy, dz);
252 
253 		//  Get the surface height at that point
254 		tHeight = tileSlopeHeight(pos, obj);
255 
256 		//  If the surface height is too far away from the sample
257 		//  height, then ignore it.
258 		if (tHeight > pos.z + kMaxStepHeight
259 		        ||  tHeight < pos.z - kMaxStepHeight * 4) continue;
260 
261 		//  Recompute the coordinate
262 		dz = tHeight - objZ;
263 
264 		//  If under the same roof, and no blockages...
265 
266 		if (outside == (objRoofID(obj, mapNum, pos) == 0)
267 		        &&  checkBlocked(obj, pos) == blockageNone) {
268 			int32   newRadius;
269 
270 			//  Then this is the best one found so far.
271 
272 			//  Set new radius to maximum of abs of the 3 coords, minus 1
273 			//  (Because we want solution to converge faster)
274 			newRadius = MAX(MAX(ABS(dx), ABS(dy)), ABS(dz)) - 1;
275 			if (newRadius < radius) {
276 				radius = newRadius;
277 
278 				//  Each time radius gets reduced, we try a few more times
279 				//  to find a better solution.
280 				tries = radius * 2 + 8;
281 			}
282 
283 			pos.z = tHeight;
284 			bestPos = pos;
285 		}
286 	}
287 
288 	if (radius < 128) {
289 #if DEBUG
290 		WriteStatusF(9, "Unstick Dist: %d", radius);
291 #endif
292 		obj->move(bestPos);
293 		return true;
294 	}
295 
296 #else
297 	for (dist = 4; dist < 64; dist += 4) {
298 		int         level;
299 
300 		for (level = 0; level < dist / 2; level++) {
301 			bool    up = (level & 1) == 0;
302 
303 			height = 8 * (up ? level >> 1 : -1 - (level >> 1));
304 
305 			for (dir = 0; dir < 8; dir++) {
306 				pos = obj->getLocation() + (dirTable[dir] * dist);
307 				pos.z += height;
308 
309 				if (outside == (objRoofID(obj, mapNum, pos) == 0)
310 				        &&  checkBlocked(obj, pos) == blockageNone) {
311 					int16       tHeight;
312 
313 					tHeight = tileSlopeHeight(pos, obj);
314 					if (tHeight <= pos.z + kMaxStepHeight
315 					        &&  tHeight >= pos.z - kMaxStepHeight * 4) {
316 						pos.z = tHeight;
317 						obj->move(pos);
318 						return true;
319 					}
320 				}
321 			}
322 		}
323 	}
324 #endif
325 #if DEBUG
326 	WriteStatusF(9, "Unstick Failed!");
327 #endif
328 	return true;
329 }
330 
331 //  Calculates the direction of a missile based upon the velocity vector
missileDir(const TilePoint & vector)332 uint8 missileDir(const TilePoint &vector) {
333 	return (((ptToAngle(vector.u, vector.v) + 8) >> 4) - 2) & 0xF;
334 }
335 
336 //  Computes the frames needed to turn from one direction to another
computeTurnFrames(Direction fromDir,Direction toDir)337 uint8 computeTurnFrames(Direction fromDir, Direction toDir) {
338 	Direction       relDir = (toDir - fromDir) & 0x7;
339 
340 	return relDir <= 4 ? relDir : 8 - relDir;
341 }
342 
343 /* ===================================================================== *
344    MotionTaskList member functions
345  * ===================================================================== */
346 
347 //-----------------------------------------------------------------------
348 //	Initialize the MotionTaskList
349 
MotionTaskList(void)350 MotionTaskList::MotionTaskList(void) {
351 	_nextMT = _list.end();
352 }
353 
MotionTaskList(Common::SeekableReadStream * stream)354 MotionTaskList::MotionTaskList(Common::SeekableReadStream *stream) {
355 	read(stream);
356 	_nextMT = _list.end();
357 }
358 
359 
read(Common::InSaveFile * in)360 void MotionTaskList::read(Common::InSaveFile *in) {
361 	int16 motionTaskCount;
362 	//  Retrieve the motion task count
363 	motionTaskCount = in->readSint16LE();
364 
365 	for (int i = 0; i < motionTaskCount; i++) {
366 		MotionTask *mt;
367 
368 		mt = new MotionTask;
369 		_list.push_back(mt);
370 
371 		mt->read(in);
372 	}
373 }
374 
375 //-----------------------------------------------------------------------
376 //	Return the number of bytes needed to archive the motion tasks
377 //	in a buffer
378 
archiveSize(void)379 int32 MotionTaskList::archiveSize(void) {
380 	//  Initilialize with sizeof motion task count
381 	int32       size = sizeof(int16);
382 
383 	for (Common::List<MotionTask *>::iterator it = _list.begin(); it != _list.end(); ++it)
384 		size += (*it)->archiveSize();
385 
386 	return size;
387 }
388 
write(Common::MemoryWriteStreamDynamic * out)389 void MotionTaskList::write(Common::MemoryWriteStreamDynamic *out) {
390 	int16 motionTaskCount = _list.size();
391 
392 	//  Store the motion task count
393 	out->writeSint16LE(motionTaskCount);
394 
395 	//  Archive the active motion tasks
396 	for (Common::List<MotionTask *>::iterator it = _list.begin(); it != _list.end(); ++it)
397 		(*it)->write(out);
398 }
399 
400 //-----------------------------------------------------------------------
401 //	Cleanup the motion tasks
402 
cleanup(void)403 void MotionTaskList::cleanup(void) {
404 	for (Common::List<MotionTask *>::iterator it = _list.begin(); it != _list.end(); ++it) {
405 		abortPathFind(*it);
406 		(*it)->pathFindTask = NULL;
407 
408 		delete *it;
409 	}
410 
411 	_list.clear();
412 }
413 
414 //-----------------------------------------------------------------------
415 //	Get a new motion task, if there is one available, and initialize it.
416 
newTask(GameObject * obj)417 MotionTask *MotionTaskList::newTask(GameObject *obj) {
418 	MotionTask *mt = nullptr;
419 
420 	//  Check see if there's already motion associated with this object.
421 	for (Common::List<MotionTask *>::iterator it = _list.begin(); it != _list.end(); ++it) {
422 
423 		if ((*it)->object == obj) {
424 			mt = *it;
425 			wakeUpThread(mt->thread, motionInterrupted);
426 			mt->thread = NoThread;
427 
428 			break;
429 		}
430 	}
431 
432 	if (mt == nullptr) {
433 		mt = new MotionTask;
434 
435 		mt->object = obj;
436 		mt->motionType = mt->prevMotionType = MotionTask::motionTypeNone;
437 		mt->pathFindTask = nullptr;
438 		mt->pathCount = -1;
439 		mt->flags = 0;
440 		mt->velocity = TilePoint(0, 0, 0);
441 		mt->immediateLocation = mt->finalTarget = obj->getLocation();
442 		mt->thread = NoThread;
443 
444 		mt->targetObj = nullptr;
445 		mt->targetTAG = nullptr;
446 		mt->spellObj = nullptr;
447 
448 		_list.push_back(mt);
449 
450 		if (isActor(obj))
451 			((Actor *)obj)->_moveTask = mt;
452 	}
453 
454 	obj->_data.objectFlags |= objectMoving;
455 
456 	return mt;
457 }
458 
459 /* ===================================================================== *
460    MotionTask member functions
461  * ===================================================================== */
462 
read(Common::InSaveFile * in)463 void MotionTask::read(Common::InSaveFile *in) {
464 	ObjectID    objectID;
465 
466 	//  Restore the motion type and previous motion type
467 	motionType = in->readByte();
468 	prevMotionType = in->readByte();
469 
470 	//  Restore the thread ID
471 	thread = in->readSint16LE();
472 
473 	//  Restore the motion flags
474 	flags = in->readUint16LE();
475 
476 	//  Get the object ID
477 	objectID = in->readUint16LE();
478 
479 	//  Convert the object ID to and object address
480 	object =    objectID != Nothing
481 	            ?   GameObject::objectAddress(objectID)
482 	            :   NULL;
483 
484 	//  If the object is an actor, plug this motion task into the actor
485 	if (object && isActor(object))
486 		((Actor *)object)->_moveTask = this;
487 
488 	if (motionType == motionTypeWalk
489 	        ||  prevMotionType == motionTypeWalk) {
490 		//  Restore the target _data.locations
491 		immediateLocation.load(in);
492 		finalTarget.load(in);
493 
494 		//  If there is a tether restore it
495 		if (flags & tethered) {
496 			tetherMinU = in->readSint16LE();
497 			tetherMinV = in->readSint16LE();
498 			tetherMaxU = in->readSint16LE();
499 			tetherMaxV = in->readSint16LE();
500 		}
501 
502 		//  Restore the direction
503 		direction = in->readByte();
504 
505 		//  Restore the path index and path count
506 		pathIndex = in->readSint16LE();
507 		pathCount = in->readSint16LE();
508 		runCount = in->readSint16LE();
509 
510 		//  Restore the action counter if needed
511 		if (flags & agitated)
512 			actionCounter = in->readSint16LE();
513 
514 		//  If there were valid path way points, restore those
515 		if (pathIndex >= 0 && pathIndex < pathCount) {
516 			int16 wayPointIndex = pathIndex;
517 
518 			while (wayPointIndex < pathCount) {
519 				pathList[wayPointIndex].load(in);
520 
521 				wayPointIndex++;
522 			}
523 		}
524 
525 		//  If this motion task previously had a path finding request
526 		//  it must be restarted
527 		pathFindTask = NULL;
528 	}
529 
530 	if (motionType == motionTypeThrown || motionType == motionTypeShot) {
531 		//  Restore the velocity
532 		velocity.load(in);
533 
534 		//  Restore other ballistic motion variables
535 		steps = in->readSint16LE();
536 		uFrac = in->readSint16LE();
537 		vFrac = in->readSint16LE();
538 		uErrorTerm = in->readSint16LE();
539 		vErrorTerm = in->readSint16LE();
540 
541 		if (motionType == motionTypeShot) {
542 			ObjectID targetObjID,
543 			         enactorID;
544 
545 			targetObjID = in->readUint16LE();
546 
547 			targetObj = targetObjID
548 			            ?   GameObject::objectAddress(targetObjID)
549 			            :   NULL;
550 
551 			enactorID = in->readUint16LE();
552 
553 			o.enactor = enactorID != Nothing
554 			            ? (Actor *)GameObject::objectAddress(enactorID)
555 			            :   NULL;
556 		}
557 	} else if (motionType == motionTypeClimbUp
558 	           ||  motionType == motionTypeClimbDown) {
559 		immediateLocation.load(in);
560 	} else if (motionType == motionTypeJump) {
561 		velocity.load(in);
562 	} else if (motionType == motionTypeTurn) {
563 		direction = in->readByte();
564 	} else if (motionType == motionTypeGive) {
565 		ObjectID id = in->readUint16LE();
566 		targetObj = id != Nothing
567 		            ?   GameObject::objectAddress(id)
568 		            :   NULL;
569 	} else if (motionType == motionTypeWait) {
570 		actionCounter = in->readSint16LE();
571 	} else if (motionType == motionTypeUseObject
572 	           ||  motionType == motionTypeUseObjectOnObject
573 	           ||  motionType == motionTypeUseObjectOnTAI
574 	           ||  motionType == motionTypeUseObjectOnLocation
575 	           ||  motionType == motionTypeDropObject
576 	           ||  motionType == motionTypeDropObjectOnObject
577 	           ||  motionType == motionTypeDropObjectOnTAI) {
578 	    ObjectID directObjID = in->readUint16LE();
579 		o.directObject = directObjID != Nothing
580 		                ?   GameObject::objectAddress(directObjID)
581 		                :   NULL;
582 
583 		direction = in->readByte();
584 
585 		if (motionType == motionTypeUseObjectOnObject
586 		        ||  motionType == motionTypeDropObjectOnObject) {
587 		    ObjectID indirectObjID = in->readUint16LE();
588 			o.indirectObject =  indirectObjID != Nothing
589 			                    ?   GameObject::objectAddress(indirectObjID)
590 			                    :   NULL;
591 		} else {
592 			if (motionType == motionTypeUseObjectOnTAI
593 			        ||  motionType == motionTypeDropObjectOnTAI) {
594 			    ActiveItemID tai(in->readSint16LE());
595 				o.TAI = tai != NoActiveItem
596 				        ?   ActiveItem::activeItemAddress(tai)
597 				        :   NULL;
598 			}
599 
600 			if (motionType == motionTypeUseObjectOnLocation
601 			        ||  motionType == motionTypeDropObject
602 			        ||  motionType == motionTypeDropObjectOnTAI) {
603 				targetLoc.load(in);
604 			}
605 		}
606 	} else if (motionType == motionTypeUseTAI) {
607 		ActiveItemID tai(in->readSint16LE());
608 		o.TAI = tai != NoActiveItem
609 		        ?   ActiveItem::activeItemAddress(tai)
610 		        :   NULL;
611 
612 		direction = in->readByte();
613 	} else if (motionType == motionTypeTwoHandedSwing
614 	           ||  motionType == motionTypeOneHandedSwing
615 	           ||  motionType == motionTypeFireBow
616 	           ||  motionType == motionTypeCastSpell
617 	           ||  motionType == motionTypeUseWand) {
618 		ObjectID    targetObjID;
619 
620 		//  Restore the direction
621 		direction = in->readByte();
622 
623 		//  Restore the combat motion type
624 		combatMotionType = in->readByte();
625 
626 		//  Get the target object ID
627 		targetObjID = in->readUint16LE();
628 
629 		//  Convert the target object ID to a pointer
630 		targetObj = targetObjID != Nothing
631 		            ?   GameObject::objectAddress(targetObjID)
632 		            :   NULL;
633 
634 		if (motionType == motionTypeCastSpell) {
635 			SpellID sid       ;
636 			ObjectID toid     ;
637 			ActiveItemID ttaid;
638 
639 			//  restore the spell prototype
640 			warning("MotionTask::read: Check SpellID size");
641 			sid = (SpellID)in->readUint32LE();
642 			spellObj = sid != nullSpell
643 			           ? skillProtoFromID(sid)
644 			           : NULL;
645 
646 			//  restore object target
647 			toid = in->readUint16LE();
648 			targetObj = toid != Nothing
649 			            ?   GameObject::objectAddress(toid)
650 			            :   NULL;
651 
652 			//  restore TAG target
653 			ttaid = in->readSint16LE();
654 			targetTAG = ttaid != NoActiveItem
655 			            ?  ActiveItem::activeItemAddress(ttaid)
656 			            :  NULL;
657 
658 			//  restore _data.location target
659 			targetLoc.load(in);
660 		}
661 
662 		//  Restore the action counter
663 		actionCounter = in->readSint16LE();
664 	} else if (motionType == motionTypeTwoHandedParry
665 	           ||  motionType == motionTypeOneHandedParry
666 	           ||  motionType == motionTypeShieldParry) {
667 		ObjectID attackerID,
668 		         defensiveObjID;
669 
670 		//  Restore the direction
671 		direction = in->readByte();
672 
673 		//  Get the attacker's and defensive object's IDs
674 		attackerID = in->readByte();
675 		defensiveObjID = in->readByte();
676 
677 		//  Convert IDs to pointers
678 		d.attacker = attackerID != Nothing
679 		            ? (Actor *)GameObject::objectAddress(attackerID)
680 		            :   NULL;
681 
682 		d.defensiveObj = defensiveObjID != Nothing
683 		                ?   GameObject::objectAddress(defensiveObjID)
684 		                :   NULL;
685 
686 		//  Restore the defense flags
687 		d.defenseFlags = in->readByte();
688 
689 		//  Restore the action counter
690 		actionCounter = in->readSint16LE();
691 
692 		if (motionType == motionTypeOneHandedParry) {
693 			//  Restore the combat sub-motion type
694 			combatMotionType = in->readByte();
695 		}
696 	} else if (motionType == motionTypeDodge
697 	           ||  motionType == motionTypeAcceptHit
698 	           ||  motionType == motionTypeFallDown) {
699 		ObjectID        attackerID;
700 
701 		//  Get the attacker's ID
702 		attackerID = in->readUint16LE();
703 
704 		//  Convert ID to pointer
705 		d.attacker = attackerID != Nothing
706 		            ? (Actor *)GameObject::objectAddress(attackerID)
707 		            :   NULL;
708 
709 		//  Restore the action counter
710 		actionCounter = in->readSint16LE();
711 	}
712 }
713 
714 //-----------------------------------------------------------------------
715 //	Return the number of bytes needed to archive this MotionTask
716 
archiveSize(void)717 int32 MotionTask::archiveSize(void) {
718 	int32       size = 0;
719 
720 	size =      sizeof(motionType)
721 	            +   sizeof(prevMotionType)
722 	            +   sizeof(thread)
723 	            +   sizeof(flags)
724 	            +   sizeof(ObjectID);            //  object
725 
726 	if (motionType == motionTypeWalk
727 	        ||  prevMotionType == motionTypeWalk) {
728 		size +=     sizeof(immediateLocation)
729 		            +   sizeof(finalTarget);
730 
731 		if (flags & tethered) {
732 			size +=     sizeof(tetherMinU)
733 			            +   sizeof(tetherMinV)
734 			            +   sizeof(tetherMaxU)
735 			            +   sizeof(tetherMaxV);
736 		}
737 
738 		size +=     sizeof(direction)
739 		            +   sizeof(pathIndex)
740 		            +   sizeof(pathCount)
741 		            +   sizeof(runCount);
742 
743 		if (flags & agitated)
744 			size += sizeof(actionCounter);
745 
746 		if (pathIndex >= 0 && pathIndex < pathCount)
747 			size += sizeof(TilePoint) * (pathCount - pathIndex);
748 	}
749 
750 	if (motionType == motionTypeThrown || motionType == motionTypeShot) {
751 		size +=     sizeof(velocity)
752 		            +   sizeof(steps)
753 		            +   sizeof(uFrac)
754 		            +   sizeof(vFrac)
755 		            +   sizeof(uErrorTerm)
756 		            +   sizeof(vErrorTerm);
757 
758 		if (motionType == motionTypeShot) {
759 			size +=     sizeof(ObjectID)         //  targetObj ID
760 			            +   sizeof(ObjectID);        //  enactor ID
761 		}
762 	} else if (motionType == motionTypeClimbUp
763 	           ||  motionType == motionTypeClimbDown) {
764 		size += sizeof(immediateLocation);
765 	} else if (motionType == motionTypeJump) {
766 		size += sizeof(velocity);
767 	} else if (motionType == motionTypeTurn) {
768 		size += sizeof(direction);
769 	} else if (motionType == motionTypeGive) {
770 		size += sizeof(ObjectID);        //  targetObj ID
771 	} else if (motionType == motionTypeUseObject
772 	           ||  motionType == motionTypeUseObjectOnObject
773 	           ||  motionType == motionTypeUseObjectOnTAI
774 	           ||  motionType == motionTypeUseObjectOnLocation
775 	           ||  motionType == motionTypeDropObject
776 	           ||  motionType == motionTypeDropObjectOnObject
777 	           ||  motionType == motionTypeDropObjectOnTAI) {
778 		size +=     sizeof(ObjectID)
779 		            +   sizeof(direction);
780 
781 		if (motionType == motionTypeUseObjectOnObject
782 		        ||  motionType == motionTypeDropObjectOnObject) {
783 			size += sizeof(ObjectID);
784 		} else {
785 			if (motionType == motionTypeUseObjectOnTAI
786 			        ||  motionType == motionTypeDropObjectOnTAI) {
787 				size += sizeof(ActiveItemID);
788 			}
789 
790 			if (motionType == motionTypeUseObjectOnLocation
791 			        ||  motionType == motionTypeDropObject
792 			        ||  motionType == motionTypeDropObjectOnTAI) {
793 				size += sizeof(targetLoc);
794 			}
795 		}
796 	} else if (motionType == motionTypeUseTAI) {
797 		size +=     sizeof(ActiveItemID)
798 		            +   sizeof(direction);
799 	} else if (motionType == motionTypeTwoHandedSwing
800 	           ||  motionType == motionTypeOneHandedSwing
801 	           ||  motionType == motionTypeFireBow
802 	           ||  motionType == motionTypeCastSpell
803 	           ||  motionType == motionTypeUseWand) {
804 		size +=     sizeof(direction)
805 		            +   sizeof(combatMotionType)
806 		            +   sizeof(ObjectID);            //  targetObj
807 
808 		if (motionType == motionTypeCastSpell) {
809 			size += sizeof(SpellID);             //  spellObj
810 			size += sizeof(ObjectID);            //  targetObj
811 			size += sizeof(ActiveItemID);        //  targetTAG
812 			size += sizeof(targetLoc);           //  targetLoc
813 		}
814 
815 		size +=     sizeof(actionCounter);
816 
817 	} else if (motionType == motionTypeTwoHandedParry
818 	           ||  motionType == motionTypeOneHandedParry
819 	           ||  motionType == motionTypeShieldParry) {
820 		size +=     sizeof(direction)
821 		            +   sizeof(ObjectID)             //  attacker ID
822 		            +   sizeof(ObjectID)             //  defensiveObj ID
823 		            +   sizeof(d.defenseFlags)
824 		            +   sizeof(actionCounter);
825 
826 		if (motionType == motionTypeOneHandedParry)
827 			size += sizeof(combatMotionType);
828 	} else if (motionType == motionTypeDodge
829 	           ||  motionType == motionTypeAcceptHit
830 	           ||  motionType == motionTypeFallDown) {
831 		size +=     sizeof(ObjectID)             //  attacker ID
832 		            +   sizeof(actionCounter);
833 	}
834 
835 	return size;
836 }
837 
write(Common::MemoryWriteStreamDynamic * out)838 void MotionTask::write(Common::MemoryWriteStreamDynamic *out) {
839 	ObjectID    objectID;
840 
841 	//  Store the motion type and previous motion type
842 	out->writeByte(motionType);
843 	out->writeByte(prevMotionType);
844 
845 	//  Store the thread ID
846 	out->writeSint16LE(thread);
847 
848 	//  Store the motion flags
849 	out->writeUint16LE(flags);
850 
851 	//  Convert the object pointer to an object ID
852 	objectID = object != NULL ? object->thisID() : Nothing;
853 
854 	//  Store the object ID
855 	out->writeUint16LE(objectID);
856 
857 	if (motionType == motionTypeWalk
858 	        ||  prevMotionType == motionTypeWalk) {
859 		//  Store the target _data.locations
860 		immediateLocation.write(out);
861 		finalTarget.write(out);
862 
863 		//  If there is a tether store it
864 		if (flags & tethered) {
865 			out->writeSint16LE(tetherMinU);
866 			out->writeSint16LE(tetherMinV);
867 			out->writeSint16LE(tetherMaxU);
868 			out->writeSint16LE(tetherMaxV);
869 		}
870 
871 		//  Store the direction
872 		out->writeByte(direction);
873 
874 		//  Store the path index and path count
875 		out->writeSint16LE(pathIndex);
876 		out->writeSint16LE(pathCount);
877 		out->writeSint16LE(runCount);
878 
879 		//  Store the action counter if needed
880 		if (flags & agitated)
881 			out->writeSint16LE(actionCounter);
882 
883 		//  If there are valid path way points, store them
884 		if (pathIndex >= 0 && pathIndex < pathCount) {
885 			int16   wayPointIndex = pathIndex;
886 
887 			while (wayPointIndex < pathCount) {
888 				pathList[wayPointIndex].write(out);
889 
890 				wayPointIndex++;
891 			}
892 		}
893 	}
894 
895 	if (motionType == motionTypeThrown || motionType == motionTypeShot) {
896 		//  Store the velocity
897 		velocity.write(out);
898 
899 		//  Store other ballistic motion variables
900 		out->writeSint16LE(steps);
901 		out->writeSint16LE(uFrac);
902 		out->writeSint16LE(vFrac);
903 		out->writeSint16LE(uErrorTerm);
904 		out->writeSint16LE(vErrorTerm);
905 
906 		if (motionType == motionTypeShot) {
907 			ObjectID        targetObjID,
908 			                enactorID;
909 
910 			targetObjID =   targetObj != NULL
911 			                ?   targetObj->thisID()
912 			                :   Nothing;
913 
914 			out->writeUint16LE(targetObjID);
915 
916 			enactorID = o.enactor != NULL
917 			            ?   o.enactor->thisID()
918 			            :   Nothing;
919 
920 			out->writeUint16LE(enactorID);
921 		}
922 	} else if (motionType == motionTypeClimbUp
923 	           ||  motionType == motionTypeClimbDown) {
924 		immediateLocation.write(out);
925 	} else if (motionType == motionTypeJump) {
926 		velocity.write(out);
927 	} else if (motionType == motionTypeTurn) {
928 		out->writeByte(direction);
929 	} else if (motionType == motionTypeGive) {
930 		if (targetObj != NULL)
931 			out->writeUint16LE(targetObj->thisID());
932 		else
933 			out->writeUint16LE(Nothing);
934 	} else if (motionType == motionTypeUseObject
935 	           ||  motionType == motionTypeUseObjectOnObject
936 	           ||  motionType == motionTypeUseObjectOnTAI
937 	           ||  motionType == motionTypeUseObjectOnLocation
938 	           ||  motionType == motionTypeDropObject
939 	           ||  motionType == motionTypeDropObjectOnObject
940 	           ||  motionType == motionTypeDropObjectOnTAI) {
941 		if (o.directObject != NULL)
942 			out->writeUint16LE(o.directObject->thisID());
943 		else
944 			out->writeUint16LE(Nothing);
945 
946 		out->writeByte(direction);
947 
948 		if (motionType == motionTypeUseObjectOnObject
949 		        ||  motionType == motionTypeDropObjectOnObject) {
950 			if (o.indirectObject != NULL)
951 				out->writeUint16LE(o.indirectObject->thisID());
952 			else
953 				out->writeUint16LE(Nothing);
954 		} else {
955 			if (motionType == motionTypeUseObjectOnTAI
956 			        ||  motionType == motionTypeDropObjectOnTAI) {
957 				if (o.TAI != NULL)
958 					out->writeSint16LE(o.TAI->thisID());
959 				else
960 					out->writeSint16LE(NoActiveItem.val);
961 			}
962 
963 			if (motionType == motionTypeUseObjectOnLocation
964 			        ||  motionType == motionTypeDropObject
965 			        ||  motionType == motionTypeDropObjectOnTAI) {
966 				targetLoc.write(out);
967 			}
968 		}
969 	} else if (motionType == motionTypeUseTAI) {
970 		if (o.TAI != NULL)
971 			out->writeSint16LE(o.TAI->thisID());
972 		else
973 			out->writeSint16LE(NoActiveItem.val);
974 
975 		out->writeByte(direction);
976 	} else if (motionType == motionTypeTwoHandedSwing
977 	           ||  motionType == motionTypeOneHandedSwing
978 	           ||  motionType == motionTypeFireBow
979 	           ||  motionType == motionTypeCastSpell
980 	           ||  motionType == motionTypeUseWand) {
981 		ObjectID    targetObjID;
982 
983 		//  Store the direction
984 		out->writeByte(direction);
985 
986 		//  Store the combat motion type
987 		out->writeByte(combatMotionType);
988 
989 		//  Convert the target object pointer to an ID
990 		targetObjID = targetObj != NULL ? targetObj->thisID() : Nothing;
991 
992 		//  Store the target object ID
993 		out->writeUint16LE(targetObjID);
994 
995 		if (motionType == motionTypeCastSpell) {
996 			//  Convert the spell object pointer to an ID
997 
998 			SpellID sid         = spellObj != NULL
999 			                      ? spellObj->getSpellID()
1000 			                      : nullSpell;
1001 
1002 			ObjectID toid       = targetObj != NULL
1003 			                      ? targetObj->thisID()
1004 			                      : Nothing;
1005 
1006 			ActiveItemID ttaid  = targetTAG != NULL
1007 			                      ? targetTAG->thisID()
1008 			                      : NoActiveItem;
1009 
1010 			//  Store the spell prototype
1011 			warning("MotionTask::write: Check SpellID size");
1012 			out->writeUint32LE(sid);
1013 
1014 			//  Store object target
1015 			out->writeUint16LE(toid);
1016 
1017 			//  Store TAG target
1018 			out->writeSint16LE(ttaid.val);
1019 
1020 			//  Store _data.location target
1021 			targetLoc.write(out);
1022 		}
1023 
1024 		//  Store the action counter
1025 		out->writeSint16LE(actionCounter);
1026 
1027 	} else if (motionType == motionTypeTwoHandedParry
1028 	           ||  motionType == motionTypeOneHandedParry
1029 	           ||  motionType == motionTypeShieldParry) {
1030 		ObjectID        attackerID,
1031 		                defensiveObjID;
1032 
1033 		//  Store the direction
1034 		out->writeByte(direction);
1035 
1036 		attackerID = d.attacker != NULL ? d.attacker->thisID() : Nothing;
1037 		defensiveObjID = d.defensiveObj != NULL ? d.defensiveObj->thisID() : Nothing;
1038 
1039 		//  Store the attacker's and defensive object's IDs
1040 		out->writeUint16LE(attackerID);
1041 		out->writeUint16LE(defensiveObjID);
1042 
1043 		//  Store the defense flags
1044 		out->writeByte(d.defenseFlags);
1045 
1046 		//  Store the action counter
1047 		out->writeSint16LE(actionCounter);
1048 
1049 		if (motionType == motionTypeOneHandedParry) {
1050 			//  Store the combat sub-motion type
1051 			out->writeByte(combatMotionType);
1052 		}
1053 	} else if (motionType == motionTypeDodge
1054 	           ||  motionType == motionTypeAcceptHit
1055 	           ||  motionType == motionTypeFallDown) {
1056 		ObjectID        attackerID;
1057 
1058 		attackerID = d.attacker != NULL ? d.attacker->thisID() : Nothing;
1059 
1060 		//  Store the attacker's ID
1061 		out->writeUint16LE(attackerID);
1062 
1063 		//  Store the action counter
1064 		out->writeSint16LE(actionCounter);
1065 	}
1066 }
1067 
1068 //-----------------------------------------------------------------------
1069 //	When a motion task is finished, call this function to delete it.
1070 
remove(int16 returnVal)1071 void MotionTask::remove(int16 returnVal) {
1072 	if (g_vm->_mTaskList->_nextMT != g_vm->_mTaskList->_list.end() && *(g_vm->_mTaskList->_nextMT) == this)
1073 		++g_vm->_mTaskList->_nextMT;
1074 
1075 	object->_data.objectFlags &= ~objectMoving;
1076 	if (objObscured(object))
1077 		object->_data.objectFlags |= objectObscured;
1078 	else
1079 		object->_data.objectFlags &= ~objectObscured;
1080 
1081 	if (isActor(object)) {
1082 		Actor   *a = (Actor *)object;
1083 
1084 		a->_moveTask = NULL;
1085 		a->_cycleCount = g_vm->_rnd->getRandomNumber(19);
1086 
1087 		//  Make sure the actor is not left in a permanently
1088 		//  uninterruptable state with no motion task to reset it
1089 		if (a->isPermanentlyUninterruptable())
1090 			a->setInterruptablity(true);
1091 	}
1092 
1093 	g_vm->_mTaskList->_list.remove(this);
1094 
1095 	abortPathFind(this);
1096 	pathFindTask = NULL;
1097 
1098 	wakeUpThread(thread, returnVal);
1099 }
1100 
1101 //-----------------------------------------------------------------------
1102 //	Determine the immediate target _data.location
1103 
getImmediateTarget(void)1104 TilePoint MotionTask::getImmediateTarget(void) {
1105 	if (immediateLocation != Nowhere)
1106 		return immediateLocation;
1107 
1108 	Direction       dir;
1109 
1110 	//  If the wandering then simply go in the direction the actor is
1111 	//  facing, else if avoiding a block go in the previously selected
1112 	//  random direction
1113 	if (flags & agitated)
1114 		dir = direction;
1115 	else
1116 		dir = ((Actor *)object)->_currentFacing;
1117 
1118 	return  object->_data.location
1119 	        +   incDirTable[dir] * kTileUVSize;
1120 }
1121 
1122 //-----------------------------------------------------------------------
1123 //	This calculates the velocity for a ballistic motion
1124 
calcVelocity(const TilePoint & vector,int16 turns)1125 void MotionTask::calcVelocity(const TilePoint &vector,  int16 turns) {
1126 	TilePoint   veloc;
1127 
1128 	// Here is the formula for calculating the velocity Z vector
1129 
1130 	// Vz = - 1/2gt + 1/t(Dz - Sz)
1131 
1132 	// Vz = Velocity Z Coords
1133 	// g  = gravity
1134 	// t  = turns
1135 	// Dz = Destination Z Coords
1136 	// Sz = Source Z Coords
1137 
1138 	veloc.u = vector.u / turns;
1139 	veloc.v = vector.v / turns;
1140 
1141 	//  This is used in ballistic motion to make up for rounding
1142 
1143 	steps = turns;
1144 	uFrac = vector.u % turns;
1145 	vFrac = vector.v % turns;
1146 	uErrorTerm = 0;
1147 	vErrorTerm = 0;
1148 
1149 	veloc.z = ((gravity * turns) >> 1) + vector.z / turns;
1150 	velocity = veloc;
1151 }
1152 
1153 //-----------------------------------------------------------------------
1154 //	This initiates a motion task for turning an actor
1155 
turn(Actor & obj,Direction dir)1156 void MotionTask::turn(Actor &obj, Direction dir) {
1157 	assert(dir < 8);
1158 
1159 	MotionTask      *mt;
1160 
1161 	if ((mt = g_vm->_mTaskList->newTask(&obj)) != NULL) {
1162 		mt->direction = dir;
1163 		mt->motionType = motionTypeTurn;
1164 		mt->flags = reset;
1165 	}
1166 }
1167 
1168 //-----------------------------------------------------------------------
1169 //	This initiates a motion task for turning an actor
1170 
turnTowards(Actor & obj,const TilePoint & where)1171 void MotionTask::turnTowards(Actor &obj, const TilePoint &where) {
1172 	MotionTask      *mt;
1173 
1174 	if ((mt = g_vm->_mTaskList->newTask(&obj)) != NULL) {
1175 		mt->direction = (where - obj.getLocation()).quickDir();
1176 		mt->motionType = motionTypeTurn;
1177 		mt->flags = reset;
1178 	}
1179 }
1180 
1181 //-----------------------------------------------------------------------
1182 //	This initiates a motion task for going through the motions of giving
1183 //	an object to another actor
1184 
give(Actor & actor,Actor & givee)1185 void MotionTask::give(Actor &actor, Actor &givee) {
1186 	MotionTask      *mt;
1187 
1188 	if ((mt = g_vm->_mTaskList->newTask(&actor)) != NULL) {
1189 		mt->targetObj = &givee;
1190 		mt->motionType = motionTypeGive;
1191 		mt->flags = reset;
1192 	}
1193 }
1194 
1195 //-----------------------------------------------------------------------
1196 //	This initiates a motion task for throwing an object
1197 
throwObject(GameObject & obj,const TilePoint & velocity)1198 void MotionTask::throwObject(GameObject &obj, const TilePoint &velocity) {
1199 	MotionTask      *mt;
1200 
1201 	if ((mt = g_vm->_mTaskList->newTask(&obj)) != NULL) {
1202 		if (obj.isMissile()) obj._data.missileFacing = missileNoFacing;
1203 		mt->velocity = velocity;
1204 		mt->motionType = motionTypeThrown;
1205 	}
1206 }
1207 
1208 //-----------------------------------------------------------------------
1209 //	This function is intended to allow the character to throw an object
1210 //	to a specific point. It is in no way functional yet.
1211 
1212 //  REM: we need to know if we are indoors or outdoors!
1213 //  REM: we need to know celing height!!!
1214 
throwObjectTo(GameObject & obj,const TilePoint & where)1215 void MotionTask::throwObjectTo(GameObject &obj, const TilePoint &where) {
1216 	MotionTask      *mt;
1217 	const int16     turns = 15;
1218 
1219 	if ((mt = g_vm->_mTaskList->newTask(&obj)) != NULL) {
1220 		if (obj.isMissile()) obj._data.missileFacing = missileNoFacing;
1221 		mt->calcVelocity(where - obj.getLocation(), turns);
1222 		mt->motionType = motionTypeThrown;
1223 	}
1224 }
1225 
1226 //-----------------------------------------------------------------------
1227 //	This function initiates a ballistic motion towards a specified target
1228 //	_data.location at a specified horizontal speed.
1229 
shootObject(GameObject & obj,Actor & doer,GameObject & target,int16 speed)1230 void MotionTask::shootObject(
1231     GameObject &obj,
1232     Actor &doer,
1233     GameObject &target,
1234     int16 speed) {
1235 	MotionTask      *mt;
1236 
1237 	if ((mt = g_vm->_mTaskList->newTask(&obj)) != NULL) {
1238 		TilePoint   targetLoc = target.getLocation();
1239 
1240 		targetLoc.z += target.proto()->height / 2;
1241 
1242 		TilePoint   vector = targetLoc - obj.getLocation();
1243 		int16       turns = MAX(vector.quickHDistance() / speed, 1);
1244 
1245 		if (isActor(&target)) {
1246 			Actor       *targetActor = (Actor *)&target;
1247 
1248 			if (targetActor->_moveTask != NULL) {
1249 				MotionTask      *targetMotion = targetActor->_moveTask;
1250 
1251 				if (targetMotion->motionType == motionTypeWalk)
1252 					vector += targetMotion->velocity * turns;
1253 			}
1254 		}
1255 
1256 		mt->calcVelocity(vector, turns);
1257 
1258 		if (obj.isMissile())
1259 			obj._data.missileFacing = missileDir(mt->velocity);
1260 
1261 		mt->motionType = motionTypeShot;
1262 		mt->o.enactor = &doer;
1263 		mt->targetObj = &target;
1264 	}
1265 }
1266 
1267 //-----------------------------------------------------------------------
1268 //	Walk to a specific point, using pathfinding.
1269 
walkTo(Actor & actor,const TilePoint & target,bool run,bool canAgitate)1270 void MotionTask::walkTo(
1271     Actor           &actor,
1272     const TilePoint &target,
1273     bool            run,
1274     bool            canAgitate) {
1275 	MotionTask      *mt;
1276 
1277 	if ((mt = g_vm->_mTaskList->newTask(&actor)) != NULL) {
1278 		if (!mt->isReflex() && !actor.isImmobile()) {
1279 			unstickObject(&actor);
1280 			mt->finalTarget = mt->immediateLocation = target;
1281 			mt->motionType = mt->prevMotionType = motionTypeWalk;
1282 			mt->pathCount = mt->pathIndex = 0;
1283 			mt->flags = pathFind | reset;
1284 			mt->runCount = 12;          // # of frames until we can run
1285 
1286 			if (run && actor.isActionAvailable(actionRun))
1287 				mt->flags |= requestRun;
1288 			if (canAgitate)
1289 				mt->flags |= agitatable;
1290 
1291 			RequestPath(mt, getPathFindIQ(&actor));
1292 		}
1293 	}
1294 }
1295 
1296 //-----------------------------------------------------------------------
1297 //	Walk to a specific point without pathfinding
1298 
walkToDirect(Actor & actor,const TilePoint & target,bool run,bool canAgitate)1299 void MotionTask::walkToDirect(
1300     Actor           &actor,
1301     const TilePoint &target,
1302     bool            run,
1303     bool            canAgitate) {
1304 	MotionTask      *mt;
1305 
1306 	if ((mt = g_vm->_mTaskList->newTask(&actor)) != NULL) {
1307 		if (!mt->isReflex() && !actor.isImmobile()) {
1308 			//  Abort any pending path finding task
1309 			abortPathFind(mt);
1310 			mt->pathFindTask = NULL;
1311 
1312 			unstickObject(&actor);
1313 			mt->motionType = mt->prevMotionType = motionTypeWalk;
1314 			mt->finalTarget = mt->immediateLocation = target;
1315 			mt->pathCount = mt->pathIndex = 0;
1316 			mt->flags = reset;
1317 			mt->runCount = 12;
1318 
1319 			if (run && actor.isActionAvailable(actionRun))
1320 				mt->flags |= requestRun;
1321 			if (canAgitate)
1322 				mt->flags |= agitatable;
1323 		}
1324 	}
1325 }
1326 
1327 //-----------------------------------------------------------------------
1328 //	Wander around
1329 
wander(Actor & actor,bool run)1330 void MotionTask::wander(
1331     Actor       &actor,
1332     bool        run) {
1333 	MotionTask      *mt;
1334 
1335 	if ((mt = g_vm->_mTaskList->newTask(&actor)) != NULL) {
1336 		if (!mt->isReflex() && !actor.isImmobile()) {
1337 			//  Abort any pending path finding task
1338 			abortPathFind(mt);
1339 			mt->pathFindTask = NULL;
1340 
1341 			unstickObject(&actor);
1342 			mt->motionType = mt->prevMotionType = motionTypeWalk;
1343 			mt->immediateLocation = Nowhere;
1344 			mt->pathCount = mt->pathIndex = 0;
1345 			mt->flags = reset | wandering;
1346 			mt->runCount = 12;
1347 
1348 			if (run && actor.isActionAvailable(actionRun))
1349 				mt->flags |= requestRun;
1350 
1351 			RequestWanderPath(mt, getPathFindIQ(&actor));
1352 		}
1353 	}
1354 }
1355 
1356 //-----------------------------------------------------------------------
1357 //	Wander around within a tether region
1358 
tetheredWander(Actor & actor,const TileRegion & tetherReg,bool run)1359 void MotionTask::tetheredWander(
1360     Actor               &actor,
1361     const TileRegion    &tetherReg,
1362     bool                run) {
1363 	MotionTask          *mt;
1364 
1365 	if ((mt = g_vm->_mTaskList->newTask(&actor)) != NULL) {
1366 		if (!mt->isReflex() && !actor.isImmobile()) {
1367 			//  Abort any pending path finding task
1368 			abortPathFind(mt);
1369 			mt->pathFindTask = NULL;
1370 
1371 			unstickObject(&actor);
1372 			mt->motionType = mt->prevMotionType = motionTypeWalk;
1373 			mt->immediateLocation = Nowhere;
1374 			mt->tetherMinU = tetherReg.min.u;
1375 			mt->tetherMinV = tetherReg.min.v;
1376 			mt->tetherMaxU = tetherReg.max.u;
1377 			mt->tetherMaxV = tetherReg.max.v;
1378 			mt->pathCount = mt->pathIndex = 0;
1379 			mt->flags = reset | wandering | tethered;
1380 			mt->runCount = 12;
1381 
1382 			if (run && actor.isActionAvailable(actionRun))
1383 				mt->flags |= requestRun;
1384 
1385 			RequestWanderPath(mt, getPathFindIQ(&actor));
1386 		}
1387 	}
1388 }
1389 
1390 //-----------------------------------------------------------------------
1391 //	Create a climb up ladder motion task.
1392 
upLadder(Actor & actor)1393 void MotionTask::upLadder(Actor &actor) {
1394 	MotionTask      *mt;
1395 
1396 	if ((mt = g_vm->_mTaskList->newTask(&actor)) != NULL) {
1397 		if (mt->motionType != motionTypeClimbUp) {
1398 			mt->motionType = motionTypeClimbUp;
1399 			mt->flags = reset;
1400 		}
1401 	}
1402 }
1403 
1404 //-----------------------------------------------------------------------
1405 //	Create a climb down ladder motion task.
1406 
downLadder(Actor & actor)1407 void MotionTask::downLadder(Actor &actor) {
1408 	MotionTask      *mt;
1409 
1410 	if ((mt = g_vm->_mTaskList->newTask(&actor)) != NULL) {
1411 		if (mt->motionType != motionTypeClimbDown) {
1412 			mt->motionType = motionTypeClimbDown;
1413 			mt->flags = reset;
1414 		}
1415 	}
1416 }
1417 
1418 //-----------------------------------------------------------------------
1419 //	Create a talk motion task.
1420 
talk(Actor & actor)1421 void MotionTask::talk(Actor &actor) {
1422 	MotionTask      *mt;
1423 
1424 	if ((mt = g_vm->_mTaskList->newTask(&actor)) != NULL) {
1425 		if (mt->motionType != motionTypeTalk) {
1426 			mt->motionType = motionTypeTalk;
1427 			mt->flags = reset;
1428 		}
1429 	}
1430 }
1431 
1432 //-----------------------------------------------------------------------
1433 //	Begin a jump. REM: This should probably have a parameter for jumping
1434 //	forward, backward, etc.
1435 
jump(Actor & actor)1436 void MotionTask::jump(Actor &actor) {
1437 	MotionTask      *mt;
1438 
1439 	if ((mt = g_vm->_mTaskList->newTask(&actor)) != NULL) {
1440 		if (mt->motionType != motionTypeThrown) {
1441 			mt->velocity.z = 10;
1442 			mt->motionType = motionTypeJump;
1443 			mt->flags = reset;
1444 		}
1445 	}
1446 }
1447 
1448 
1449 //-----------------------------------------------------------------------
1450 //	Don't move -- simply eat some time
1451 
wait(Actor & a)1452 void MotionTask::wait(Actor &a) {
1453 	MotionTask      *mt;
1454 
1455 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1456 		if (mt->motionType != motionTypeWait) {
1457 			mt->motionType = motionTypeWait;
1458 			mt->flags = reset;
1459 		}
1460 	}
1461 }
1462 
1463 //-----------------------------------------------------------------------
1464 //	Use an object
1465 
useObject(Actor & a,GameObject & dObj)1466 void MotionTask::useObject(Actor &a, GameObject &dObj) {
1467 	MotionTask      *mt;
1468 
1469 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1470 		if (mt->motionType != motionTypeUseObject) {
1471 			mt->motionType = motionTypeUseObject;
1472 			mt->o.directObject = &dObj;
1473 			mt->flags = reset;
1474 			if (isPlayerActor(&a)) mt->flags |= privledged;
1475 		}
1476 	}
1477 }
1478 
1479 //-----------------------------------------------------------------------
1480 //	Use one object on another
1481 
useObjectOnObject(Actor & a,GameObject & dObj,GameObject & target)1482 void MotionTask::useObjectOnObject(
1483     Actor       &a,
1484     GameObject  &dObj,
1485     GameObject  &target) {
1486 	MotionTask      *mt;
1487 
1488 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1489 		if (mt->motionType != motionTypeUseObjectOnObject) {
1490 			mt->motionType = motionTypeUseObjectOnObject;
1491 			mt->o.directObject = &dObj;
1492 			mt->o.indirectObject = &target;
1493 			mt->flags = reset;
1494 			if (isPlayerActor(&a)) mt->flags |= privledged;
1495 		}
1496 	}
1497 }
1498 
1499 //-----------------------------------------------------------------------
1500 //	Use an object on a TAI
1501 
useObjectOnTAI(Actor & a,GameObject & dObj,ActiveItem & target)1502 void MotionTask::useObjectOnTAI(
1503     Actor       &a,
1504     GameObject  &dObj,
1505     ActiveItem  &target) {
1506 	MotionTask      *mt;
1507 
1508 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1509 		if (mt->motionType != motionTypeUseObjectOnTAI) {
1510 			mt->motionType = motionTypeUseObjectOnTAI;
1511 			mt->o.directObject = &dObj;
1512 			mt->o.TAI = &target;
1513 			mt->flags = reset;
1514 		}
1515 	}
1516 }
1517 
1518 //-----------------------------------------------------------------------
1519 //	Use on object on a TilePoint
1520 
useObjectOnLocation(Actor & a,GameObject & dObj,const Location & target)1521 void MotionTask::useObjectOnLocation(
1522     Actor           &a,
1523     GameObject      &dObj,
1524     const Location  &target) {
1525 	MotionTask      *mt;
1526 
1527 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1528 		if (mt->motionType != motionTypeUseObjectOnLocation) {
1529 			mt->motionType = motionTypeUseObjectOnLocation;
1530 			mt->o.directObject = &dObj;
1531 			mt->targetLoc = target;
1532 			mt->flags = reset;
1533 		}
1534 	}
1535 }
1536 
1537 //-----------------------------------------------------------------------
1538 //	Use a TAI
1539 
useTAI(Actor & a,ActiveItem & dTAI)1540 void MotionTask::useTAI(Actor &a, ActiveItem &dTAI) {
1541 	MotionTask      *mt;
1542 
1543 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1544 		if (mt->motionType != motionTypeUseTAI) {
1545 			mt->motionType = motionTypeUseTAI;
1546 			mt->o.TAI = &dTAI;
1547 			mt->flags = reset;
1548 		}
1549 	}
1550 }
1551 
1552 //-----------------------------------------------------------------------
1553 //	Drop an object
1554 
dropObject(Actor & a,GameObject & dObj,const Location & loc,int16 num)1555 void MotionTask::dropObject(Actor       &a,
1556                             GameObject &dObj,
1557                             const      Location &loc,
1558                             int16      num) {
1559 	MotionTask      *mt;
1560 
1561 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1562 		if (mt->motionType != motionTypeDropObject) {
1563 			mt->motionType = motionTypeDropObject;
1564 			mt->o.directObject = &dObj;
1565 			mt->targetLoc = loc;
1566 			mt->flags = reset;
1567 			mt->moveCount = num;
1568 		}
1569 	}
1570 }
1571 
1572 //-----------------------------------------------------------------------
1573 //	Drop one object on another
1574 
dropObjectOnObject(Actor & a,GameObject & dObj,GameObject & target,int16 num)1575 void MotionTask::dropObjectOnObject(
1576     Actor       &a,
1577     GameObject  &dObj,
1578     GameObject  &target,
1579     int16       num) {
1580 	MotionTask      *mt;
1581 
1582 	//  If actor is dropping object on himself, and object is already
1583 	//  in actor's container then consider it a "use" (if the object
1584 	//  is of the correct type).
1585 
1586 	if (isActor(&target)
1587 	        &&  isPlayerActor((Actor *)&target)
1588 	        &&  dObj.IDParent() == target.thisID()
1589 	        &&  !(dObj.proto()->containmentSet() & ProtoObj::isContainer)) {
1590 		useObject(a, dObj);
1591 		return;
1592 	}
1593 
1594 	//  Otherwise, drop it on the object
1595 
1596 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1597 		if (mt->motionType != motionTypeDropObjectOnObject) {
1598 			mt->motionType = motionTypeDropObjectOnObject;
1599 			mt->o.directObject = &dObj;
1600 			mt->o.indirectObject = &target;
1601 			mt->flags = reset;
1602 			mt->moveCount = num;
1603 		}
1604 	}
1605 }
1606 
1607 //-----------------------------------------------------------------------
1608 //	Drop an object on a TAI
1609 
dropObjectOnTAI(Actor & a,GameObject & dObj,ActiveItem & target,const Location & loc)1610 void MotionTask::dropObjectOnTAI(
1611     Actor           &a,
1612     GameObject      &dObj,
1613     ActiveItem      &target,
1614     const Location  &loc) {
1615 	MotionTask      *mt;
1616 
1617 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1618 		if (mt->motionType != motionTypeDropObjectOnTAI) {
1619 			mt->motionType = motionTypeDropObjectOnTAI;
1620 			mt->o.directObject = &dObj;
1621 			mt->o.TAI = &target;
1622 			mt->targetLoc = loc;
1623 			mt->flags = reset;
1624 		}
1625 	}
1626 }
1627 
1628 //-----------------------------------------------------------------------
1629 //	Determine if this MotionTask is a reflex ( motion over which an actor
1630 //	has no control )
1631 
isReflex(void)1632 bool MotionTask::isReflex(void) {
1633 	return      motionType == motionTypeThrown
1634 	            ||  motionType == motionTypeFall
1635 	            ||  motionType == motionTypeLand
1636 	            ||  motionType == motionTypeAcceptHit
1637 	            ||  motionType == motionTypeFallDown
1638 	            ||  motionType == motionTypeDie;
1639 }
1640 
1641 //  Offensive combat actions
1642 
1643 //-----------------------------------------------------------------------
1644 //	Initiate a two-handed swing
1645 
twoHandedSwing(Actor & a,GameObject & target)1646 void MotionTask::twoHandedSwing(Actor &a, GameObject &target) {
1647 	MotionTask      *mt;
1648 
1649 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1650 		if (mt->motionType != motionTypeTwoHandedSwing) {
1651 			mt->motionType = motionTypeTwoHandedSwing;
1652 			mt->targetObj = &target;
1653 			mt->flags = reset;
1654 		}
1655 	}
1656 }
1657 
1658 //-----------------------------------------------------------------------
1659 //	Initiate a one-handed swing
1660 
oneHandedSwing(Actor & a,GameObject & target)1661 void MotionTask::oneHandedSwing(Actor &a, GameObject &target) {
1662 	MotionTask      *mt;
1663 
1664 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1665 		if (mt->motionType != motionTypeOneHandedSwing) {
1666 			mt->motionType = motionTypeOneHandedSwing;
1667 			mt->targetObj = &target;
1668 			mt->flags = reset;
1669 		}
1670 	}
1671 }
1672 
1673 //-----------------------------------------------------------------------
1674 //	Initiate a fire bow motion
1675 
fireBow(Actor & a,GameObject & target)1676 void MotionTask::fireBow(Actor &a, GameObject &target) {
1677 	MotionTask      *mt;
1678 
1679 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1680 		if (mt->motionType != motionTypeFireBow) {
1681 			mt->motionType = motionTypeFireBow;
1682 			mt->targetObj = &target;
1683 			mt->flags = reset;
1684 		}
1685 	}
1686 }
1687 
1688 //-----------------------------------------------------------------------
1689 //	Initiate a cast spell motion
1690 
castSpell(Actor & a,SkillProto & spell,GameObject & target)1691 void MotionTask::castSpell(Actor &a, SkillProto &spell, GameObject &target) {
1692 	MotionTask      *mt;
1693 	motionTypes     type =
1694 	    (spellBook[spell.getSpellID()].getManaType() == sManaIDSkill) ?
1695 	    motionTypeGive :
1696 	    motionTypeCastSpell;
1697 
1698 
1699 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1700 		if (mt->motionType != type) {
1701 			mt->motionType = type;
1702 			mt->spellObj = &spell;
1703 			mt->targetObj = &target;
1704 			mt->flags = reset;
1705 			mt->direction = (mt->targetObj->getLocation() - a.getLocation()).quickDir();
1706 			if (isPlayerActor(&a)) mt->flags |= privledged;
1707 		}
1708 	}
1709 }
1710 
castSpell(Actor & a,SkillProto & spell,Location & target)1711 void MotionTask::castSpell(Actor &a, SkillProto &spell, Location &target) {
1712 	MotionTask      *mt;
1713 	motionTypes     type =
1714 	    (spellBook[spell.getSpellID()].getManaType() == sManaIDSkill) ?
1715 	    motionTypeGive :
1716 	    motionTypeCastSpell;
1717 
1718 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1719 		if (mt->motionType != type) {
1720 			mt->motionType = type;
1721 			mt->spellObj = &spell;
1722 			mt->targetLoc = target; //target;
1723 			mt->flags = reset | LocTarg;
1724 			mt->direction = (target - a.getLocation()).quickDir();
1725 			if (isPlayerActor(&a)) mt->flags |= privledged;
1726 		}
1727 	}
1728 }
1729 
castSpell(Actor & a,SkillProto & spell,ActiveItem & target)1730 void MotionTask::castSpell(Actor &a, SkillProto &spell, ActiveItem &target) {
1731 	MotionTask      *mt;
1732 	motionTypes     type =
1733 	    (spellBook[spell.getSpellID()].getManaType() == sManaIDSkill) ?
1734 	    motionTypeGive :
1735 	    motionTypeCastSpell;
1736 
1737 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1738 		if (mt->motionType != type) {
1739 			Location loc;
1740 			assert(target._data.itemType == activeTypeInstance);
1741 			mt->motionType = type;
1742 			mt->spellObj = &spell;
1743 			mt->targetTAG = &target;
1744 			loc = Location(
1745 			          target._data.instance.u << kTileUVShift,
1746 			          target._data.instance.v << kTileUVShift,
1747 			          target._data.instance.h,
1748 			          a.world()->thisID());
1749 			mt->targetLoc = loc; //target;
1750 			mt->flags = reset | TAGTarg;
1751 			mt->direction = (loc - a.getLocation()).quickDir();
1752 			if (isPlayerActor(&a)) mt->flags |= privledged;
1753 		}
1754 	}
1755 }
1756 
1757 //-----------------------------------------------------------------------
1758 //	Initiate a use wand motion
1759 
useWand(Actor & a,GameObject & target)1760 void MotionTask::useWand(Actor &a, GameObject &target) {
1761 	MotionTask      *mt;
1762 
1763 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1764 		if (mt->motionType != motionTypeUseWand) {
1765 			mt->motionType = motionTypeUseWand;
1766 			mt->targetObj = &target;
1767 			mt->flags = reset;
1768 		}
1769 	}
1770 }
1771 
1772 //  Defensive combat actions
1773 
1774 //-----------------------------------------------------------------------
1775 //	Initiate a two-handed parry
1776 
twoHandedParry(Actor & a,GameObject & weapon,Actor & opponent)1777 void MotionTask::twoHandedParry(
1778     Actor       &a,
1779     GameObject  &weapon,
1780     Actor       &opponent) {
1781 	MotionTask      *mt;
1782 
1783 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1784 		if (mt->motionType != motionTypeTwoHandedParry) {
1785 			mt->motionType = motionTypeTwoHandedParry;
1786 			mt->d.attacker = &opponent;
1787 			mt->d.defensiveObj = &weapon;
1788 		}
1789 		mt->flags = reset;
1790 		mt->d.defenseFlags = 0;
1791 	}
1792 }
1793 
1794 //-----------------------------------------------------------------------
1795 //	Initiate a one-handed parry
1796 
oneHandedParry(Actor & a,GameObject & weapon,Actor & opponent)1797 void MotionTask::oneHandedParry(
1798     Actor       &a,
1799     GameObject  &weapon,
1800     Actor       &opponent) {
1801 	MotionTask      *mt;
1802 
1803 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1804 		if (mt->motionType != motionTypeOneHandedParry) {
1805 			mt->motionType = motionTypeOneHandedParry;
1806 			mt->d.attacker = &opponent;
1807 			mt->d.defensiveObj = &weapon;
1808 		}
1809 		mt->flags = reset;
1810 		mt->d.defenseFlags = 0;
1811 	}
1812 }
1813 
1814 //-----------------------------------------------------------------------
1815 //	Initiate a shield parry
1816 
shieldParry(Actor & a,GameObject & shield,Actor & opponent)1817 void MotionTask::shieldParry(
1818     Actor       &a,
1819     GameObject  &shield,
1820     Actor       &opponent) {
1821 	MotionTask      *mt;
1822 
1823 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1824 		if (mt->motionType != motionTypeShieldParry) {
1825 			mt->motionType = motionTypeShieldParry;
1826 			mt->d.attacker = &opponent;
1827 			mt->d.defensiveObj = &shield;
1828 		}
1829 		mt->flags = reset;
1830 		mt->d.defenseFlags = 0;
1831 	}
1832 }
1833 
1834 //-----------------------------------------------------------------------
1835 //	Initiate a dodge
1836 
dodge(Actor & a,Actor & opponent)1837 void MotionTask::dodge(Actor &a, Actor &opponent) {
1838 	MotionTask      *mt;
1839 
1840 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1841 		if (mt->motionType != motionTypeDodge) {
1842 			mt->motionType = motionTypeDodge;
1843 			mt->d.attacker = &opponent;
1844 		}
1845 		mt->flags = reset;
1846 		mt->d.defenseFlags = 0;
1847 	}
1848 }
1849 
1850 //  Other combat actions
1851 
1852 //-----------------------------------------------------------------------
1853 //	Initiate an accept hit motion
1854 
acceptHit(Actor & a,Actor & opponent)1855 void MotionTask::acceptHit(Actor &a, Actor &opponent) {
1856 	MotionTask      *mt;
1857 
1858 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1859 		if (mt->motionType != motionTypeAcceptHit) {
1860 			mt->motionType = motionTypeAcceptHit;
1861 			mt->d.attacker = &opponent;
1862 			mt->flags = reset;
1863 		}
1864 	}
1865 }
1866 
1867 //-----------------------------------------------------------------------
1868 //	Initiate a fall down motion
1869 
fallDown(Actor & a,Actor & opponent)1870 void MotionTask::fallDown(Actor &a, Actor &opponent) {
1871 	MotionTask      *mt;
1872 
1873 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1874 		if (mt->motionType != motionTypeFallDown) {
1875 			mt->motionType = motionTypeFallDown;
1876 			mt->d.attacker = &opponent;
1877 			mt->flags = reset;
1878 		}
1879 	}
1880 }
1881 
1882 //-----------------------------------------------------------------------
1883 //	Initiate a die motion
1884 
die(Actor & a)1885 void MotionTask::die(Actor &a) {
1886 	MotionTask      *mt;
1887 
1888 	if ((mt = g_vm->_mTaskList->newTask(&a)) != NULL) {
1889 		if (mt->motionType != motionTypeDie) {
1890 			mt->motionType = motionTypeDie;
1891 			mt->flags = reset;
1892 		}
1893 	}
1894 }
1895 
1896 //-----------------------------------------------------------------------
1897 //	Determine if this MotionTask is a defensive motion
1898 
isDefense(void)1899 bool MotionTask::isDefense(void) {
1900 	return      motionType == motionTypeOneHandedParry
1901 	            ||  motionType == motionTypeTwoHandedParry
1902 	            ||  motionType == motionTypeShieldParry
1903 	            ||  motionType == motionTypeDodge;
1904 }
1905 
1906 //-----------------------------------------------------------------------
1907 //	Determine if this MotionTask is an offensive motion
1908 
isAttack(void)1909 bool MotionTask::isAttack(void) {
1910 	return      isMeleeAttack()
1911 	            ||  motionType == motionTypeFireBow
1912 	            ||  motionType == motionTypeCastSpell
1913 	            ||  motionType == motionTypeUseWand;
1914 }
1915 
1916 //-----------------------------------------------------------------------
1917 //	Determine if this MotionTask is an offensive melee motion
1918 
isMeleeAttack(void)1919 bool MotionTask::isMeleeAttack(void) {
1920 	return      motionType == motionTypeOneHandedSwing
1921 	            ||  motionType == motionTypeTwoHandedSwing;
1922 }
1923 
1924 //-----------------------------------------------------------------------
1925 //	Determine if this MotionTask is a walk motion
1926 
isWalk(void)1927 bool MotionTask::isWalk(void) {
1928 	return prevMotionType == motionTypeWalk;
1929 }
1930 
1931 //-----------------------------------------------------------------------
1932 //	Return the wandering tether region
1933 
getTether(void)1934 TileRegion MotionTask::getTether(void) {
1935 	TileRegion  reg;
1936 
1937 	if (flags & tethered) {
1938 		reg.min = TilePoint(tetherMinU, tetherMinV, 0);
1939 		reg.max = TilePoint(tetherMaxU, tetherMaxV, 0);
1940 	} else {
1941 		reg.min = Nowhere;
1942 		reg.max = Nowhere;
1943 	}
1944 
1945 	return reg;
1946 }
1947 
1948 //-----------------------------------------------------------------------
1949 //	If the target has changed position since the last path find started,
1950 //  then call this function.
1951 
changeTarget(const TilePoint & newPos,bool run)1952 void MotionTask::changeTarget(const TilePoint &newPos, bool run) {
1953 	if (prevMotionType == motionTypeWalk) {
1954 		uint16      oldFlags = flags;
1955 
1956 		abortPathFind(this);
1957 
1958 		finalTarget = immediateLocation = newPos;
1959 		pathCount = pathIndex = 0;
1960 
1961 		flags = pathFind | reset;
1962 		if (oldFlags & agitatable)
1963 			flags |= agitatable;
1964 
1965 		//  Set run flag if requested
1966 		if (run
1967 		        //  Check if actor capable of running...
1968 		        && ((Actor *)object)->isActionAvailable(actionRun))
1969 
1970 			flags |= requestRun;
1971 		else
1972 			flags &= ~requestRun;
1973 
1974 		RequestPath(this, getPathFindIQ(object));
1975 	}
1976 }
1977 
1978 //-----------------------------------------------------------------------
1979 //	If the target has changed position since the walk/run started, then
1980 //	call this function.
1981 
changeDirectTarget(const TilePoint & newPos,bool run)1982 void MotionTask::changeDirectTarget(const TilePoint &newPos, bool run) {
1983 	if (prevMotionType == motionTypeWalk) {
1984 		prevMotionType = motionTypeWalk;
1985 
1986 		finalTarget = immediateLocation = newPos;
1987 
1988 		//  Reset motion task
1989 		flags |= reset;
1990 		flags &= ~pathFind;
1991 
1992 		//  Set run flag if requested
1993 		if (run
1994 		        //  Check if actor capable of running...
1995 		        && ((Actor *)object)->isActionAvailable(actionRun))
1996 
1997 			flags |= requestRun;
1998 		else
1999 			flags &= ~requestRun;
2000 	}
2001 }
2002 
2003 //  Cancel actor movement if walking...
finishWalk(void)2004 void MotionTask::finishWalk(void) {
2005 	//  If the actor is in a running state
2006 	if (motionType == motionTypeWalk) {
2007 		remove();
2008 		//  If there is currently a path finding request, abort it.
2009 		/*      abortPathFind( this );
2010 
2011 		            //  Simply set actor's target _data.location to "here".
2012 		        finalTarget = immediateLocation = object->getLocation();
2013 		        pathList[0] = finalTarget;
2014 		        flags = reset;
2015 		        pathCount = 0;
2016 		        pathIndex = 0;*/
2017 	}
2018 }
2019 
2020 //  Cancel actor movement if talking...
finishTalking(void)2021 void MotionTask::finishTalking(void) {
2022 	if (motionType == motionTypeTalk) {
2023 		if (isActor(object)) {
2024 			Actor   *a = (Actor *)object;
2025 			if (a->_currentAnimation != actionStand)
2026 				a->setAction(actionStand, 0);
2027 		}
2028 		remove();
2029 	}
2030 }
2031 
2032 //-----------------------------------------------------------------------
2033 //	Handle actions for characters and objects in free-fall
2034 
ballisticAction(void)2035 void MotionTask::ballisticAction(void) {
2036 	TilePoint       totalVelocity,          // total velocity vector
2037 	                stepVelocity,           // sub-velocity vector
2038 	                location,
2039 	                newPos;
2040 
2041 	int16           minDim,
2042 	                vectorSteps;
2043 
2044 	GameObject      *obj = object;
2045 	ProtoObj        *proto = obj->proto();
2046 
2047 	if (isActor(obj)) {
2048 		//  Before anything else make sure the actor is in an
2049 		//  uninterruptable state.
2050 		((Actor *)obj)->setInterruptablity(false);
2051 	}
2052 
2053 
2054 	//  Add the force of gravity to the acceleration.
2055 
2056 	if (!(flags & inWater)) {
2057 		velocity.z -= gravity;
2058 	} else {
2059 		velocity.u = velocity.v = 0;
2060 		velocity.z = -gravity;
2061 	}
2062 	location = obj->getLocation();
2063 
2064 //	WriteStatusF( 6, "%d %d %d", _data.location.u, _data.location.v, _data.location.z );
2065 
2066 	//  Because we live in a point-sampled universe, we need to make
2067 	//  sure that objects which are moving extremely fast don't
2068 	//  undersample the terrain. We do this by breaking the velocity
2069 	//  vector into smaller vectors, and handling them individually.
2070 
2071 	totalVelocity = velocity;
2072 
2073 	//  Make Up For Rounding Errors In ThrowTo
2074 
2075 	if (uFrac) {
2076 		uErrorTerm += ABS(uFrac);
2077 
2078 		if (uErrorTerm >= steps) {
2079 			uErrorTerm -= steps;
2080 			if (uFrac > 0)
2081 				totalVelocity.u++;
2082 			else
2083 				totalVelocity.u--;
2084 		}
2085 	}
2086 
2087 	if (vFrac) {
2088 		vErrorTerm += ABS(vFrac);
2089 
2090 		if (vErrorTerm >= steps) {
2091 			vErrorTerm -= steps;
2092 			if (vFrac > 0)
2093 				totalVelocity.v++;
2094 			else
2095 				totalVelocity.v--;
2096 		}
2097 	}
2098 
2099 	//  Determine which dimension is smaller, width or height.
2100 	minDim = MAX<int16>(MIN<int16>(proto->height, proto->crossSection * 2), 1);
2101 
2102 	//  "vectorSteps" is the number of increments we are going to process
2103 	//  this vector.
2104 
2105 	vectorSteps = ((totalVelocity.magnitude() - 1) / minDim) + 1;
2106 
2107 	if (isActor(obj) && velocity.magnitude() > 16) {
2108 		Actor       *a = (Actor *)obj;
2109 
2110 		if (a->isActionAvailable(actionFreeFall))
2111 			a->setAction(actionFreeFall, 0);
2112 	}
2113 
2114 	for (int i = 0; i < vectorSteps; i++) {
2115 		int16       stepsLeft = vectorSteps - i;
2116 		GameObject  *collisionObject;
2117 
2118 		//  REM: This would be better as a rounded division...
2119 
2120 		//  Compute the small velocity vector for this increment,
2121 		//  and then subtract it from the total velocity.
2122 
2123 		stepVelocity = totalVelocity / stepsLeft;
2124 		totalVelocity -= stepVelocity;
2125 
2126 		//  Compute the new position of the object
2127 
2128 		newPos = location + stepVelocity;
2129 
2130 
2131 
2132 		//  See if the object ran into anything. If it didn't, then
2133 		//  update the coord and try again.
2134 
2135 		if (isActor(obj)) {
2136 			Actor   *a = (Actor *)obj;
2137 
2138 			if (a == getCenterActor() && checkLadder(a, newPos))
2139 				return;
2140 		}
2141 
2142 		if (checkContact(obj, newPos, &collisionObject) == false) {
2143 			location = newPos;
2144 		} else {
2145 			TilePoint       oldVelocity = velocity;
2146 
2147 			if (motionType == motionTypeShot && collisionObject != NULL) {
2148 				//  If this motion is for a shot arrow and we did not
2149 				//  collide with our target object just continue the
2150 				//  motion as if there was no collision.
2151 				if (collisionObject == targetObj) {
2152 					if (object->strike(
2153 					            o.enactor->thisID(),
2154 					            targetObj->thisID())) {
2155 						//  The arrow struck, so delete the arrow and
2156 						//  end this motion
2157 						remove();
2158 						object->deleteObject();
2159 						return;
2160 					} else {
2161 						//  If the arrow failed to strike continue the
2162 						//  arrows flight as if there was no collision.
2163 						targetObj = NULL;
2164 						location = newPos;
2165 						continue;
2166 					}
2167 				} else {
2168 					location = newPos;
2169 					continue;
2170 				}
2171 			}
2172 
2173 			if (unstickObject(obj)) return;
2174 
2175 			//  "probe" is a bitfield which will indicate which
2176 			//  directions the obstructions lie in.
2177 
2178 			int16       probe = 0;
2179 
2180 			//  Probe along each of the three coordinate axes
2181 
2182 			if (checkBlocked(obj,
2183 			                 TilePoint(newPos.u,
2184 			                           obj->_data.location.v,
2185 			                           obj->_data.location.z))) {
2186 				probe |= (1 << 0);
2187 			}
2188 
2189 			if (checkBlocked(obj,
2190 			                 TilePoint(obj->_data.location.u,
2191 			                           newPos.v,
2192 			                           obj->_data.location.z))) {
2193 				probe |= (1 << 1);
2194 			}
2195 
2196 			if (checkContact(obj,
2197 			                 TilePoint(obj->_data.location.u,
2198 			                           obj->_data.location.v,
2199 			                           newPos.z))) {
2200 				probe |= (1 << 2);
2201 			}
2202 
2203 			//  If there are no obstructions along the orthogonal
2204 			//  directions, then we must have hit a corner. In this
2205 			//  case, we just bounce directly backwards.
2206 
2207 			if (probe == 0) {
2208 				velocity = -velocity / 2;
2209 				totalVelocity = -totalVelocity / 2;
2210 			} else {
2211 				if (probe & (1 << 0)) {     // If struck wall in U direction
2212 					velocity.u = -velocity.u / 2;
2213 					totalVelocity.u = -totalVelocity.u / 2;
2214 				} else {
2215 					velocity.u = (velocity.u * 2) / 3;
2216 					totalVelocity.u = (totalVelocity.u * 2) / 3;
2217 				}
2218 
2219 				if (probe & (1 << 1)) {     // If struck wall in V direction
2220 					velocity.v = -velocity.v / 2;
2221 					totalVelocity.v = -totalVelocity.v / 2;
2222 				} else {
2223 					velocity.v = (velocity.v * 2) / 3;
2224 					totalVelocity.v = (totalVelocity.v * 2) / 3;
2225 				}
2226 
2227 				if (probe & (1 << 2)) {     // If struct wall in Z direction
2228 					velocity.z = -velocity.z / 2;
2229 					totalVelocity.z = -totalVelocity.z / 2;
2230 				} else {
2231 					velocity.z = (velocity.z * 2) / 3;
2232 					totalVelocity.z = (totalVelocity.z * 2) / 3;
2233 				}
2234 			}
2235 			uFrac = vFrac = 0;
2236 			if (motionType == motionTypeShot && obj->isMissile())
2237 				obj->_data.missileFacing = missileDir(velocity);
2238 
2239 			//  If the ballistic object is an actor hitting the
2240 			//  ground, then instead of bouncing, we'll just have
2241 			//  them absorb the impact
2242 
2243 			if (isActor(obj) && probe & (1 << 2)) {
2244 				StandingTileInfo    sti;
2245 
2246 				if (freeFall(location, sti) == false) {
2247 					int16       velocityMagnitude = oldVelocity.magnitude();
2248 
2249 					fallingDamage(obj, velocityMagnitude);
2250 					obj->move(location);
2251 					if (!((Actor *)obj)->isDead()) {
2252 						motionType =    velocityMagnitude <= 16
2253 						                ?   motionTypeLand
2254 						                :   motionTypeLandBadly;
2255 						flags |= reset;
2256 						setObjectSurface(obj, sti);
2257 					} else {
2258 						setObjectSurface(obj, sti);
2259 						remove();
2260 					}
2261 					return;
2262 				} else {
2263 					setObjectSurface(obj, sti);
2264 					//  If the object is falling, then
2265 					//  freeFall will have already modified the
2266 					//  object's _data.location
2267 					return;
2268 				}
2269 			} else if (velocity.u < 2 && velocity.u > -2
2270 			           &&  velocity.v < 2 && velocity.v > -2
2271 			           &&  velocity.z < 2 && velocity.z > -2) {
2272 				StandingTileInfo    sti;
2273 
2274 				//  If the reduced velocity after impact is
2275 				//  very small, then we'll assume that the object
2276 				//  has come to rest.
2277 
2278 				if (freeFall(location, sti) == false) {
2279 					obj->move(location);
2280 					remove();           // delete motion task
2281 					setObjectSurface(obj, sti);
2282 					return;
2283 				}
2284 				setObjectSurface(obj, sti);
2285 				return;
2286 			}
2287 			//  Otherwise, since we struck a wall at high velocity,
2288 			//  we just drop this small velocity vector from our
2289 			//  calculations, and continue with the next iteration
2290 			//  of the loop.
2291 		}
2292 	}
2293 
2294 	obj->move(location);
2295 }
2296 
2297 
2298 //-----------------------------------------------------------------------
2299 //	Get the coordinates of the next waypoint.
2300 
nextWayPoint(void)2301 bool MotionTask::nextWayPoint(void) {
2302 	//  If the pathfinder hasn't managed to determine waypoints
2303 	//  yet, then return failure.
2304 //	if ( ( flags & pathFind ) && pathCount < 0 ) return false;
2305 
2306 	//  If there are still waypoints in the path list, then
2307 	//  retrieve the next waypoint.
2308 	if ((flags & (pathFind | wandering)) && pathIndex < pathCount) {
2309 		TilePoint   wayPointVector(0, 0, 0);
2310 
2311 		if (pathIndex > 0)
2312 			wayPointVector = immediateLocation - object->_data.location;
2313 
2314 		if (wayPointVector.quickHDistance() == 0)
2315 			//  Next vertex in path polyline
2316 			immediateLocation = pathList[pathIndex++];
2317 		else
2318 			return false;
2319 	} else {
2320 		if (flags & wandering) {
2321 			immediateLocation = Nowhere;
2322 			if (pathFindTask == NULL)
2323 				RequestWanderPath(this, getPathFindIQ(object));
2324 		} else if (flags & agitated) {
2325 			immediateLocation = Nowhere;
2326 		} else {
2327 			//  If we've gone off the end of the path list,
2328 			//  and we're not at the target yet, request more waypoints then
2329 			//  use dumb pathfinding until the pathfinder finishes it's task.
2330 
2331 			if ((finalTarget - object->_data.location).quickHDistance() > 0
2332 			        ||  ABS(finalTarget.z - object->_data.location.z) > kMaxStepHeight) {
2333 				//  If no pathfind in progress
2334 				if ((flags & pathFind)
2335 				        &&  !(flags & finalPath)
2336 				        &&  pathFindTask == NULL)
2337 					RequestPath(this, getPathFindIQ(object));
2338 
2339 				//  Set the immediate target to the final target,
2340 				immediateLocation = finalTarget;
2341 			}
2342 			//  else we're close enough to call it quits.
2343 			else return false;
2344 		}
2345 	}
2346 
2347 	return true;
2348 }
2349 
2350 
2351 //-----------------------------------------------------------------------
2352 //	Test to see if actor can walk in a given direction
2353 
checkWalk(int16 dir,int16 speed,int16 stepUp,TilePoint & pos)2354 bool MotionTask::checkWalk(
2355     int16           dir,
2356     int16           speed,
2357     int16           stepUp,
2358     TilePoint       &pos) {
2359 	TilePoint       newPos;
2360 
2361 	//  Check the terrain in various directions.
2362 	//  Check in the forward direction first, at various heights
2363 
2364 	newPos      = object->_data.location + (dirTable[dir] * speed) / 2;
2365 	newPos.z    = object->_data.location.z + stepUp;
2366 
2367 	if (checkWalkable(object, newPos)) return false;
2368 
2369 //	movementDirection = direction;
2370 	pos = newPos;
2371 	return true;
2372 }
2373 
2374 //-----------------------------------------------------------------------
2375 //	Handle actions for characters walking and running
2376 
walkAction(void)2377 void MotionTask::walkAction(void) {
2378 	enum WalkType {
2379 		walkNormal  = 0,
2380 		walkSlow,
2381 		walkRun,
2382 		walkStairs
2383 	};
2384 
2385 	TilePoint       immediateTarget = getImmediateTarget(),
2386 	                newPos,
2387 	                targetVector;
2388 	int16           targetDist = 0;
2389 	int16           movementDirection,
2390 	                directionAngle;
2391 	int16           moveBlocked,
2392 	                speed = walkSpeed,
2393 	                speedScale = 2;
2394 	Actor           *a;
2395 	ActorAppearance *aa;
2396 	StandingTileInfo sti;
2397 
2398 	bool            moveTaskWaiting = false,
2399 	                moveTaskDone = false;
2400 	WalkType        walkType = walkNormal;
2401 
2402 	assert(isActor(object));
2403 	a = (Actor *)object;
2404 	aa = a->_appearance;
2405 
2406 	if (a->isImmobile()) {
2407 		remove(motionWalkBlocked);
2408 		return;
2409 	}
2410 
2411 	//  Make sure that the actor is interruptable
2412 	a->setInterruptablity(true);
2413 
2414 	//  Set the speed of movement based on whether we are walking
2415 	//  or running. Running only occurs after we have accelerated.
2416 	if (flags & requestRun
2417 	        &&  runCount == 0
2418 	        &&  !(flags & (inWater | onStairs))) {
2419 		speed = runSpeed;
2420 		speedScale = 4;
2421 		walkType = walkRun;
2422 
2423 		//  If we can see this actor, and the actor's run frames
2424 		//  have not been loaded, then downgrade this action to
2425 		//  a walk (but request the run frames).
2426 		if (aa && !aa->isBankLoaded(sprRunBankNum)) {
2427 			walkType = walkNormal;
2428 			aa->requestBank(sprRunBankNum);
2429 		}
2430 	}
2431 
2432 	//  If for some reason we cannot run at this time, then
2433 	//  set up for a walk instead.
2434 	if (walkType != walkRun) {
2435 		if (!(flags & onStairs)) {
2436 			if (!(flags & inWater)) {
2437 				speed = walkSpeed;
2438 				speedScale = 2;
2439 				walkType = walkNormal;
2440 			} else {
2441 				speed = slowWalkSpeed;
2442 				speedScale = 1;
2443 				walkType = walkSlow;
2444 
2445 				//  reset run count if actor walking slowly
2446 				runCount = MAX<int16>(runCount, 8);
2447 			}
2448 
2449 			//  If we can see this actor, and this actor's walk
2450 			//  frames have not been loaded, then downgrade this
2451 			//  action to a stand (i.e. do nothing).
2452 			if (aa && !aa->isBankLoaded(sprWalkBankNum)) {
2453 				aa->requestBank(sprWalkBankNum);
2454 				return;
2455 			}
2456 		} else {
2457 			speed = slowWalkSpeed;
2458 			speedScale = 1;
2459 			walkType = walkStairs;
2460 
2461 			//  reset run count if actor walking on stairs
2462 			runCount = MAX<int16>(runCount, 8);
2463 		}
2464 	}
2465 
2466 	if ((flags & agitated)
2467 	        &&  --actionCounter <= 0) {
2468 		flags &= ~agitated;
2469 		flags |= pathFind | reset;
2470 	}
2471 
2472 	for (;;) {
2473 		//  The "reset" flag indicates that the final target has
2474 		//  changed since the last time this routine was called.
2475 		if (!(flags & reset)) {
2476 			//  Compute the vector and distance of the current
2477 			//  position to the next "immediate" target.
2478 			targetVector = immediateTarget - object->_data.location;
2479 			targetDist = targetVector.quickHDistance();
2480 
2481 			//  If we're not already there, then proceed towards
2482 			//  the target.
2483 			if (targetDist > 0 || ABS(targetVector.z) > kMaxStepHeight)
2484 				break;
2485 		}
2486 
2487 		if (nextWayPoint() == false) {
2488 			//  If no waypoint could be found and this motion task has
2489 			//  a path find request, then go into "wait" mode.
2490 			if (pathFindTask)
2491 				moveTaskWaiting = true;
2492 			else moveTaskDone = true;
2493 			break;
2494 		} else {
2495 			flags &= ~reset;
2496 			immediateTarget = getImmediateTarget();
2497 		}
2498 	}
2499 
2500 #if VISUAL1
2501 	extern void ShowObjectSection(GameObject * obj);
2502 	extern void TPLine(const TilePoint & start, const TilePoint & stop);
2503 	{
2504 		TilePoint   curPt,
2505 		            wayPt,
2506 		            pt1,
2507 		            pt2;
2508 
2509 //		TPLine( a->getLocation(), immediateLocation );
2510 		curPt = a->getLocation();
2511 		wayPt = immediateTarget;
2512 
2513 		for (int i = pathIndex - 1; i < pathCount;) {
2514 			TPLine(curPt, wayPt);
2515 			pt1 = pt2 = wayPt;
2516 			pt1.u -= 2;
2517 			pt1.v -= 2;
2518 			pt2.u -= 2;
2519 			pt2.v += 2;
2520 			TPLine(pt1, pt2);
2521 			pt1.u += 4;
2522 			pt1.v += 4;
2523 			TPLine(pt1, pt2);
2524 			pt2.u += 4;
2525 			pt2.v -= 4;
2526 			TPLine(pt1, pt2);
2527 			pt1.u -= 4;
2528 			pt1.v -= 4;
2529 			TPLine(pt1, pt2);
2530 
2531 			curPt = wayPt;
2532 			wayPt = pathList[++i];
2533 		}
2534 
2535 		ShowObjectSection(a);
2536 	}
2537 #endif
2538 
2539 	moveBlocked = false;
2540 
2541 	if (moveTaskDone || moveTaskWaiting) {
2542 		movementDirection = a->_currentFacing;
2543 	} else if (targetDist == 0 && ABS(targetVector.z) > kMaxStepHeight) {
2544 		if (pathFindTask) {
2545 			movementDirection = a->_currentFacing;
2546 			moveTaskWaiting = true;
2547 		} else {
2548 			movementDirection = a->_currentFacing;
2549 			moveBlocked = true;
2550 		}
2551 	} else if (targetDist <= speed) {
2552 		int16       blockageType;
2553 
2554 		//  If we're near the target, then don't bother with
2555 		//  a smooth movement, just jump right there.
2556 		movementDirection = targetVector.quickDir();
2557 //		movementDirection = a->currentFacing;
2558 
2559 		//  Set the new _data.location to the character's _data.location.
2560 		newPos.u = immediateTarget.u;
2561 		newPos.v = immediateTarget.v;
2562 		newPos.z = object->_data.location.z;
2563 
2564 		//  Determine the direction the character must spin
2565 		//  to be at the correct movement angle.
2566 		directionAngle =
2567 		    (((movementDirection - a->_currentFacing) + 4) & 7) - 4;
2568 
2569 		//  Test terrain. Note that if the character is spinning more than 1
2570 		//  octant this frame, then they cannot move so a terrain test is unneeded.
2571 		if (directionAngle <= 1 && directionAngle >= -1) {
2572 			//  Test the terrain to see if we can go there.
2573 			if ((blockageType = checkWalkable(object, newPos)) != false) {
2574 				//  Try stepping up to a higher terrain too.
2575 				newPos.z = object->_data.location.z + kMaxStepHeight;
2576 				if (checkWalkable(object, newPos) != blockageNone) {
2577 					//  If there is a path find task pending, put the walk action
2578 					//  on hold until it finishes, else, abort the walk action.
2579 					if (pathFindTask)
2580 						moveTaskWaiting = true;
2581 					else {
2582 						movementDirection = a->_currentFacing;
2583 						moveBlocked = true;
2584 					}
2585 					/*                  if (!(flags & pathFind) || nextWayPoint() == false)
2586 					                    {
2587 					                        moveBlocked = true;
2588 					                        flags |= blocked;
2589 					                        newPos.z = object->_data.location.z;
2590 
2591 					                    }*/
2592 				}
2593 			}
2594 		}
2595 	} else {
2596 		int16       height;
2597 		bool        foundPath = false;
2598 
2599 		movementDirection = targetVector.quickDir();
2600 
2601 		//  Calculate new object position along direction vector.
2602 		TilePoint   pos = object->_data.location
2603 		                  + targetVector * speed / targetDist;
2604 
2605 #if DEBUG*0
2606 		TPLine(object->_data.location, pos);
2607 #endif
2608 
2609 		//  Check the terrain in various directions.
2610 		//  Check in the forward direction first, at various heights
2611 
2612 		for (height = 0; height <= kMaxStepHeight; height += kMaxSmoothStep) {
2613 			//  This code has him move along the exact direction
2614 			//  vector, even if it's not aligned with one of the
2615 			//  cardinal directions.
2616 
2617 			pos.z   = object->_data.location.z + height;
2618 
2619 			if (!checkWalkable(object, pos)) {
2620 				newPos = pos;
2621 				foundPath = true;
2622 				break;
2623 			}
2624 		}
2625 
2626 
2627 		//  Check left and right facings if a path was not found in
2628 		//  the forward direction.
2629 
2630 		if (foundPath == false) {
2631 			int16   leftDir = spinLeft(movementDirection),
2632 			        rightDir = spinRight(movementDirection);
2633 
2634 			for (height = 0; height <= kMaxStepHeight; height += 8) {
2635 				if (checkWalk(rightDir, speedScale, height, newPos)) {
2636 					movementDirection = rightDir;
2637 					foundPath = true;
2638 					break;
2639 				}
2640 
2641 				if (checkWalk(leftDir, speedScale, height, newPos)) {
2642 					movementDirection = leftDir;
2643 					foundPath = true;
2644 					break;
2645 				}
2646 			}
2647 		}
2648 
2649 		//  Let's try moving at a right angle to the current path to
2650 		//  get around this annoying obstacle...
2651 
2652 		if (foundPath == false) {
2653 			if (targetVector.u > speed / 2
2654 			        &&  checkWalk(dirUpRight, speedScale, 0, newPos)) {
2655 				movementDirection = dirUpRight;
2656 				foundPath = true;
2657 			} else if (-targetVector.u > speed / 2
2658 			           &&  checkWalk(dirDownLeft, speedScale, 0, newPos)) {
2659 				movementDirection = dirDownLeft;
2660 				foundPath = true;
2661 			} else if (targetVector.v > speed / 2
2662 			           &&  checkWalk(dirUpLeft, speedScale, 0, newPos)) {
2663 				movementDirection = dirUpLeft;
2664 				foundPath = true;
2665 			} else if (-targetVector.v > speed / 2
2666 			           &&  checkWalk(dirDownRight, speedScale, 0, newPos)) {
2667 				movementDirection = dirDownRight;
2668 				foundPath = true;
2669 			}
2670 		}
2671 
2672 		//  If we just couldn't find a valid path no matter how hard
2673 		//  we tried, then just give up and say that we were blocked.
2674 
2675 		if (foundPath == false) {
2676 
2677 			//  If there is a path find task pending, put the walk action
2678 			//  on hold until it finishes, else, abort the walk action.
2679 			if (pathFindTask)
2680 				moveTaskWaiting = true;
2681 			else {
2682 				movementDirection = a->_currentFacing;
2683 				moveBlocked = true;
2684 			}
2685 		}
2686 	}
2687 
2688 	//  REM: Test the terrain at the new spot.
2689 
2690 	if (movementDirection != a->_currentFacing) {
2691 		//  Determine the direction the character must spin
2692 		//  to be at the correct movement angle.
2693 		directionAngle =
2694 		    (((movementDirection - a->_currentFacing) + 4) & 7) - 4;
2695 
2696 		//  If the direction is at a right angle or behind
2697 		//  the character, then they cannot move.
2698 
2699 		if (directionAngle < 0) {
2700 			a->_currentFacing = spinRight(a->_currentFacing);
2701 		} else {
2702 			a->_currentFacing = spinLeft(a->_currentFacing);
2703 		}
2704 	}
2705 
2706 	if (moveTaskDone) {
2707 		remove(motionCompleted);
2708 	} else if (moveBlocked) {
2709 		a->setAction(actionStand, 0);
2710 		if (flags & agitatable) {
2711 			if (freeFall(object->_data.location, sti)) return;
2712 
2713 			//  When he starts running again, then have him walk only.
2714 			runCount = MAX<int16>(runCount, 8);
2715 
2716 			//  We're blocked so we're going to wander in a random
2717 			//  direction for a random duration
2718 			flags |= agitated | reset;
2719 
2720 			direction = g_vm->_rnd->getRandomNumber(7);
2721 			actionCounter = 8 + g_vm->_rnd->getRandomNumber(7);
2722 
2723 			//  Discard the path
2724 			if (flags & pathFind) {
2725 				flags &= ~finalPath;
2726 				pathIndex = pathCount = 0;
2727 			}
2728 		} else
2729 			remove(motionWalkBlocked);
2730 	} else if (moveTaskWaiting
2731 	           ||  movementDirection != a->_currentFacing) {
2732 		//  When he starts running again, then have him walk only.
2733 		runCount = MAX<int16>(runCount, 8);
2734 
2735 		a->setAction(actionStand, 0);
2736 		freeFall(object->_data.location, sti);
2737 	} else {
2738 		if (a == getCenterActor() && checkLadder(a, newPos)) return;
2739 
2740 		int16               tHeight;
2741 
2742 		flags &= ~blocked;
2743 
2744 		tHeight = tileSlopeHeight(newPos, object, &sti);
2745 
2746 
2747 		//  This is a kludge to keep the character from
2748 		//  "jumping" as he climbs up a small step.
2749 
2750 		if (tHeight >= object->_data.location.z - kMaxSmoothStep
2751 		        * ((sti.surfaceTile != NULL
2752 		            && (sti.surfaceTile->combinedTerrainMask() & terrainStair))
2753 		           ?   4
2754 		           :   1)
2755 		        &&  tHeight <  newPos.z)
2756 			newPos.z = tHeight;
2757 
2758 		if (freeFall(newPos, sti) == false) {
2759 			int16   newAction;
2760 
2761 			if (sti.surfaceTile != NULL
2762 			        && (sti.surfaceTile->combinedTerrainMask() & terrainStair)
2763 			        &&  a->isActionAvailable(actionSpecial7)) {
2764 				Direction   stairsDir;
2765 				uint8       *cornerHeight;
2766 
2767 				cornerHeight = sti.surfaceTile->attrs.cornerHeight;
2768 
2769 				if (cornerHeight[0] == 0 && cornerHeight[1] == 0)
2770 					stairsDir = 1;
2771 				else if (cornerHeight[1] == 0 && cornerHeight[2] == 0)
2772 					stairsDir = 3;
2773 				else if (cornerHeight[2] == 0 && cornerHeight[3] == 0)
2774 					stairsDir = 5;
2775 				else
2776 					stairsDir = 7;
2777 
2778 				if (a->_currentFacing == stairsDir) {
2779 					//  walk up stairs
2780 					newAction = actionSpecial7;
2781 					flags |= onStairs;
2782 				} else if (a->_currentFacing == ((stairsDir - 4) & 0x7)) {
2783 					//  walk down stairs
2784 					newAction = actionSpecial8;
2785 					flags |= onStairs;
2786 				} else {
2787 					flags &= ~onStairs;
2788 					if (walkType == walkStairs) walkType = walkNormal;
2789 					newAction = (walkType == walkRun) ? actionRun : actionWalk;
2790 				}
2791 			} else {
2792 				flags &= ~onStairs;
2793 				if (walkType == walkStairs) walkType = walkNormal;
2794 				newAction = (walkType == walkRun) ? actionRun : actionWalk;
2795 			}
2796 
2797 
2798 			object->move(newPos);
2799 
2800 			//  Determine if the new action is running
2801 			//  or walking.
2802 
2803 			if (a->_currentAnimation == newAction) {
2804 				//  If we are already doing that action, then
2805 				//  just continue doing it.
2806 				if (walkType != walkSlow)
2807 					a->nextAnimationFrame();
2808 				else {
2809 					if (flags & nextAnim)
2810 						a->nextAnimationFrame();
2811 					flags ^= nextAnim;
2812 				}
2813 			} else if (a->_currentAnimation == actionWalk
2814 			           ||  a->_currentAnimation == actionRun
2815 			           ||  a->_currentAnimation == actionSpecial7
2816 			           ||  a->_currentAnimation == actionSpecial8) {
2817 				//  If we are running instead of walking or
2818 				//  vice versa, then change to the new action
2819 				//  but don't break stride
2820 				a->setAction(newAction,
2821 				             animateRepeat | animateNoRestart);
2822 
2823 				if (walkType != walkSlow)
2824 					a->nextAnimationFrame();
2825 				else {
2826 					if (flags & nextAnim)
2827 						a->nextAnimationFrame();
2828 					flags ^= nextAnim;
2829 				}
2830 			} else {
2831 				// If we weren't walking or running before, then start
2832 				// walking/running and reset the sequence.
2833 				a->setAction(newAction, animateRepeat);
2834 				if (walkType == walkSlow) flags |= nextAnim;
2835 			}
2836 
2837 			if (runCount > 0) runCount--;
2838 			setObjectSurface(object, sti);
2839 		}
2840 	}
2841 }
2842 
2843 //-----------------------------------------------------------------------
2844 //	Climb up a ladder
2845 
upLadderAction(void)2846 void MotionTask::upLadderAction(void) {
2847 	Actor               *a = (Actor *)object;
2848 
2849 	if (flags & reset) {
2850 		a->setAction(actionClimbLadder, animateRepeat);
2851 		flags &= ~reset;
2852 	} else {
2853 		TilePoint           loc = a->getLocation();
2854 		uint8               crossSection = a->proto()->crossSection,
2855 		                    height = a->proto()->height;
2856 		int16               mapNum = a->getMapNum();
2857 		TileRegion          actorTileReg;
2858 		TileInfo            *ti;
2859 		TilePoint           tileLoc;
2860 		StandingTileInfo    sti = {nullptr, nullptr, {0, 0, 0}, 0};
2861 
2862 		loc.z += 6;
2863 
2864 		//  Determine the tile region which the actor overlays
2865 		actorTileReg.min.u = (loc.u - crossSection) >> kTileUVShift;
2866 		actorTileReg.min.v = (loc.v - crossSection) >> kTileUVShift;
2867 		actorTileReg.max.u =
2868 		    (loc.u + crossSection + kTileUVMask) >> kTileUVShift;
2869 		actorTileReg.max.v =
2870 		    (loc.v + crossSection + kTileUVMask) >> kTileUVShift;
2871 		actorTileReg.min.z = actorTileReg.max.z = 0;
2872 
2873 		TileIterator    iter(mapNum, actorTileReg);
2874 
2875 		//  Iterate through all the tiles in the actor's tile region
2876 		for (ti = iter.first(&tileLoc, &sti);
2877 		        ti != NULL;
2878 		        ti = iter.next(&tileLoc, &sti)) {
2879 			if (!(ti->combinedTerrainMask() & terrainLadder)) continue;
2880 
2881 			if (sti.surfaceHeight
2882 			        +   ti->attrs.terrainHeight
2883 			        <=      loc.z
2884 			        +   height
2885 			        ||  sti.surfaceHeight > loc.z + height)
2886 				continue;
2887 
2888 			uint16      footPrintMask = 0xFFFF,
2889 			            ladderMask;
2890 			TilePoint   subTileLoc(
2891 			    tileLoc.u << kTileSubShift,
2892 			    tileLoc.v << kTileSubShift,
2893 			    0);
2894 			TileRegion  actorSubTileReg;
2895 
2896 			actorSubTileReg.min.u = (loc.u - crossSection) >> kSubTileShift;
2897 			actorSubTileReg.min.v = (loc.v - crossSection) >> kSubTileShift;
2898 			actorSubTileReg.max.u =
2899 			    (loc.u + crossSection + kSubTileMask) >> kSubTileShift;
2900 			actorSubTileReg.max.v =
2901 			    (loc.v + crossSection + kSubTileMask) >> kSubTileShift;
2902 
2903 			if (actorSubTileReg.min.u >= subTileLoc.u)
2904 				footPrintMask &=
2905 				    uMinMasks[actorSubTileReg.min.u - subTileLoc.u];
2906 
2907 			if (actorSubTileReg.min.v >= subTileLoc.v)
2908 				footPrintMask &=
2909 				    vMinMasks[actorSubTileReg.min.v - subTileLoc.v];
2910 
2911 			if (actorSubTileReg.max.u < subTileLoc.u + kTileSubSize)
2912 				footPrintMask &=
2913 				    uMaxMasks[actorSubTileReg.max.u - subTileLoc.u];
2914 
2915 			if (actorSubTileReg.max.v < subTileLoc.v + kTileSubSize)
2916 				footPrintMask &=
2917 				    vMaxMasks[actorSubTileReg.max.v - subTileLoc.v];
2918 
2919 			ladderMask =    ti->attrs.fgdTerrain == terrNumLadder
2920 			                ?   ti->attrs.terrainMask
2921 			                :   ~ti->attrs.terrainMask;
2922 
2923 			if (footPrintMask & ladderMask) {
2924 				a->nextAnimationFrame();
2925 				a->move(loc);
2926 				return;
2927 			}
2928 		}
2929 
2930 		TilePoint   newLoc;
2931 
2932 		newLoc = loc + incDirTable[a->_currentFacing] * crossSection * 2;
2933 		newLoc.z = tileSlopeHeight(newLoc, a);
2934 
2935 		if (!checkBlocked(a, newLoc))
2936 			a->move(newLoc);
2937 		else {
2938 			newLoc =    loc
2939 			            +       incDirTable[(a->_currentFacing - 2) & 7]
2940 			            *   crossSection * 2;
2941 			newLoc.z = tileSlopeHeight(newLoc, a);
2942 
2943 			if (!checkBlocked(a, newLoc))
2944 				a->move(newLoc);
2945 			else {
2946 				newLoc =    loc
2947 				            +       incDirTable[(a->_currentFacing + 2) & 7]
2948 				            *   crossSection * 2;
2949 				newLoc.z = tileSlopeHeight(newLoc, a);
2950 
2951 				if (!checkBlocked(a, newLoc))
2952 					a->move(newLoc);
2953 				else {
2954 					newLoc =    loc
2955 					            +       incDirTable[a->_currentFacing]
2956 					            *   crossSection * 2;
2957 					newLoc.z = tileSlopeHeight(newLoc, a);
2958 					a->move(newLoc);
2959 					unstickObject(a);
2960 				}
2961 			}
2962 		}
2963 
2964 		a->setAction(actionStand, 0);
2965 
2966 		remove();
2967 	}
2968 }
2969 
2970 //-----------------------------------------------------------------------
2971 //	Climb down a ladder
2972 
downLadderAction(void)2973 void MotionTask::downLadderAction(void) {
2974 	Actor               *a = (Actor *)object;
2975 
2976 	if (flags & reset) {
2977 		a->setAction(actionClimbLadder, animateRepeat | animateReverse);
2978 		flags &= ~reset;
2979 	} else {
2980 		TilePoint           loc = a->getLocation();
2981 		uint8               crossSection = a->proto()->crossSection;
2982 		int16               mapNum = a->getMapNum();
2983 		TileRegion          actorTileReg;
2984 		TileInfo            *ti;
2985 		TilePoint           tileLoc;
2986 		StandingTileInfo    sti = {nullptr, nullptr, {0, 0, 0}, 0};
2987 
2988 		loc.z -= 6;
2989 
2990 		actorTileReg.min.u = (loc.u - crossSection) >> kTileUVShift;
2991 		actorTileReg.min.v = (loc.v - crossSection) >> kTileUVShift;
2992 		actorTileReg.max.u =
2993 		    (loc.u + crossSection + kTileUVMask) >> kTileUVShift;
2994 		actorTileReg.max.v =
2995 		    (loc.v + crossSection + kTileUVMask) >> kTileUVShift;
2996 		actorTileReg.min.z = actorTileReg.max.z = 0;
2997 
2998 		TileIterator    iter(mapNum, actorTileReg);
2999 
3000 		for (ti = iter.first(&tileLoc, &sti);
3001 		        ti != NULL;
3002 		        ti = iter.next(&tileLoc, &sti)) {
3003 			if (!(ti->combinedTerrainMask() & terrainLadder)) continue;
3004 
3005 			if (sti.surfaceHeight + ti->attrs.terrainHeight <= loc.z
3006 			        ||  sti.surfaceHeight > loc.z)
3007 				continue;
3008 
3009 			uint16      footPrintMask = 0xFFFF,
3010 			            ladderMask;
3011 			TilePoint   subTileLoc(
3012 			    tileLoc.u << kTileSubShift,
3013 			    tileLoc.v << kTileSubShift,
3014 			    0);
3015 			TileRegion  actorSubTileReg;
3016 
3017 			actorSubTileReg.min.u = (loc.u - crossSection) >> kSubTileShift;
3018 			actorSubTileReg.min.v = (loc.v - crossSection) >> kSubTileShift;
3019 			actorSubTileReg.max.u =
3020 			    (loc.u + crossSection + kSubTileMask) >> kSubTileShift;
3021 			actorSubTileReg.max.v =
3022 			    (loc.v + crossSection + kSubTileMask) >> kSubTileShift;
3023 
3024 			if (actorSubTileReg.min.u >= subTileLoc.u)
3025 				footPrintMask &=
3026 				    uMinMasks[actorSubTileReg.min.u - subTileLoc.u];
3027 
3028 			if (actorSubTileReg.min.v >= subTileLoc.v)
3029 				footPrintMask &=
3030 				    vMinMasks[actorSubTileReg.min.v - subTileLoc.v];
3031 
3032 			if (actorSubTileReg.max.u < subTileLoc.u + kTileSubSize)
3033 				footPrintMask &=
3034 				    uMaxMasks[actorSubTileReg.max.u - subTileLoc.u];
3035 
3036 			if (actorSubTileReg.max.v < subTileLoc.v + kTileSubSize)
3037 				footPrintMask &=
3038 				    vMaxMasks[actorSubTileReg.max.v - subTileLoc.v];
3039 
3040 			ladderMask =    ti->attrs.fgdTerrain == terrNumLadder
3041 			                ?   ti->attrs.terrainMask
3042 			                :   ~ti->attrs.terrainMask;
3043 
3044 			if (footPrintMask & ladderMask) {
3045 				a->nextAnimationFrame();
3046 				a->move(loc);
3047 				return;
3048 			}
3049 		}
3050 
3051 		TilePoint   newLoc;
3052 
3053 		newLoc = loc - incDirTable[a->_currentFacing] * kTileUVSize;
3054 		newLoc.z = tileSlopeHeight(newLoc, a);
3055 
3056 		if (!checkBlocked(a, newLoc))
3057 			a->move(newLoc);
3058 		else {
3059 			newLoc =    loc
3060 			            -       incDirTable[(a->_currentFacing - 2) & 7]
3061 			            *   kTileUVSize;
3062 			newLoc.z = tileSlopeHeight(newLoc, a);
3063 
3064 			if (!checkBlocked(a, newLoc))
3065 				a->move(newLoc);
3066 			else {
3067 				newLoc =    loc
3068 				            -       incDirTable[(a->_currentFacing + 2) & 7]
3069 				            *   kTileUVSize;
3070 				newLoc.z = tileSlopeHeight(newLoc, a);
3071 
3072 				if (!checkBlocked(a, newLoc))
3073 					a->move(newLoc);
3074 				else {
3075 					newLoc =    loc
3076 					            -       incDirTable[a->_currentFacing]
3077 					            *   kTileUVSize;
3078 					newLoc.z = tileSlopeHeight(newLoc, a);
3079 					a->move(newLoc);
3080 					unstickObject(a);
3081 				}
3082 			}
3083 		}
3084 
3085 		a->setAction(actionStand, 0);
3086 
3087 		remove();
3088 	}
3089 }
3090 
3091 //  Go through the giving motions
giveAction(void)3092 void MotionTask::giveAction(void) {
3093 	Actor       *a = (Actor *)object;
3094 	Direction   targetDir = (targetObj->getLocation()
3095 	                         -   a->getLocation()).quickDir();
3096 
3097 	if (flags & reset) {
3098 		a->setAction(actionGiveItem, 0);
3099 		flags &= ~reset;
3100 	}
3101 
3102 	if (a->_currentFacing != targetDir)
3103 		a->turn(targetDir);
3104 	else if (a->nextAnimationFrame())
3105 		remove(motionCompleted);
3106 }
3107 
3108 
3109 //  Set up specified animation and run through the frames
genericAnimationAction(uint8 actionType)3110 void MotionTask::genericAnimationAction(uint8 actionType) {
3111 	Actor *const   a = (Actor *)object;
3112 
3113 	if (flags & reset) {
3114 		a->setAction(actionType, 0);
3115 		flags &= ~reset;
3116 	} else if (a->nextAnimationFrame())
3117 		remove(motionCompleted);
3118 }
3119 
3120 //  This class is specifically designed to aid in the selection of
3121 //  of a combat motion type from a selected subset
3122 struct CombatMotionSet {
3123 	const uint8 *list;          //  Array of motion types
3124 	uint16      listSize;       //  Size of array
3125 
3126 	//  Select randome element from the array
selectRandomSaga2::CombatMotionSet3127 	uint8 selectRandom(void) const {
3128 		return list[g_vm->_rnd->getRandomNumber(listSize - 1)];
3129 	}
3130 };
3131 
3132 //  Offensive combat actions
3133 
3134 //  Construct a set of all two handed swing types
3135 const uint8 twoHandedSwingArray[] = {
3136 	MotionTask::twoHandedSwingHigh,
3137 	MotionTask::twoHandedSwingLow,
3138 	MotionTask::twoHandedSwingLeftHigh,
3139 	MotionTask::twoHandedSwingLeftLow,
3140 	MotionTask::twoHandedSwingRightHigh,
3141 	MotionTask::twoHandedSwingRightLow,
3142 };
3143 
3144 const CombatMotionSet twoHandedSwingSet = {
3145 	twoHandedSwingArray,
3146 	ARRAYSIZE(twoHandedSwingArray)
3147 };
3148 
3149 //  Construct a subset of all high two handed swing types
3150 const uint8 twoHandedHighSwingArray[] = {
3151 	MotionTask::twoHandedSwingHigh,
3152 	MotionTask::twoHandedSwingLeftHigh,
3153 	MotionTask::twoHandedSwingRightHigh,
3154 };
3155 
3156 const CombatMotionSet twoHandedHighSwingSet = {
3157 	twoHandedHighSwingArray,
3158 	ARRAYSIZE(twoHandedHighSwingArray)
3159 };
3160 
3161 //  Construct a subset of all low two handed swing types
3162 const uint8 twoHandedLowSwingArray[] = {
3163 	MotionTask::twoHandedSwingLow,
3164 	MotionTask::twoHandedSwingLeftLow,
3165 	MotionTask::twoHandedSwingRightLow,
3166 };
3167 
3168 const CombatMotionSet twoHandedLowSwingSet = {
3169 	twoHandedLowSwingArray,
3170 	ARRAYSIZE(twoHandedLowSwingArray)
3171 };
3172 
3173 //-----------------------------------------------------------------------
3174 //  Handle all two handed swing motions
3175 
twoHandedSwingAction(void)3176 void MotionTask::twoHandedSwingAction(void) {
3177 	//  If the reset flag is set, initialize the motion
3178 	if (flags & reset) {
3179 		//  Let the game engine know about this aggressive act
3180 		logAggressiveAct(object->thisID(), targetObj->thisID());
3181 
3182 		//  Notify the target actor that he is being attacked
3183 		if (isActor(targetObj))
3184 			((Actor *)targetObj)->evaluateMeleeAttack((Actor *)object);
3185 
3186 		//  Create an animation type lookup table
3187 		static const uint8  animationTypeArray[] = {
3188 			actionTwoHandSwingHigh,
3189 			actionTwoHandSwingLow,
3190 			actionTwoHandSwingLeftHigh,
3191 			actionTwoHandSwingLeftLow,
3192 			actionTwoHandSwingRightHigh,
3193 			actionTwoHandSwingRightLow,
3194 		};
3195 
3196 		Actor               *a = (Actor *)object;
3197 		uint8               actorAnimation;
3198 		int16               actorMidAltitude,
3199 		                    targetAltitude = targetObj->getLocation().z;
3200 
3201 		const CombatMotionSet   *availableSet;
3202 
3203 		//  Calculate the altitude of the actor's mid section
3204 		actorMidAltitude = a->getLocation().z + (a->proto()->height >> 1);
3205 
3206 		if (targetAltitude > actorMidAltitude)
3207 			//  The target is higher than the actor's midsection
3208 			availableSet = &twoHandedHighSwingSet;
3209 		else {
3210 			uint8           targetHeight = targetObj->proto()->height;
3211 
3212 			if (targetAltitude + targetHeight < actorMidAltitude)
3213 				//  The target is below the actor's midsection
3214 				availableSet = &twoHandedLowSwingSet;
3215 			else
3216 				//  The target is nearly the same altitude as the actor
3217 				availableSet = &twoHandedSwingSet;
3218 		}
3219 
3220 		//  Calculate the direction of the attack
3221 		direction = (targetObj->getLocation() - a->getLocation()).quickDir();
3222 
3223 		//  Randomly select a combat motion type from the available set
3224 		combatMotionType = availableSet->selectRandom();
3225 		actorAnimation = animationTypeArray[combatMotionType];
3226 
3227 		if (a->_appearance != NULL
3228 		        &&  a->isActionAvailable(actorAnimation)) {
3229 			//  Compute the number of frames in the animation before the
3230 			//  actual strike
3231 			actionCounter = a->animationFrames(actorAnimation, direction) - 2;
3232 
3233 			a->setAction(actorAnimation, 0);
3234 
3235 			//  Set this flag to indicate that the animation is actually
3236 			//  being played
3237 			flags |= nextAnim;
3238 		} else {
3239 			actionCounter = 2;
3240 
3241 			//  Clear this flag to indicate that the animation is not
3242 			//  being played
3243 			flags &= ~nextAnim;
3244 		}
3245 
3246 		a->setActionPoints(
3247 		    computeTurnFrames(a->_currentFacing, direction) + 10);
3248 
3249 		flags &= ~reset;
3250 	} else
3251 		//  Call the generic offensive melee function
3252 		offensiveMeleeAction();
3253 }
3254 
3255 
3256 //  Construct a set of all one handed swing types
3257 const uint8 oneHandedSwingArray[] = {
3258 	MotionTask::oneHandedSwingHigh,
3259 	MotionTask::oneHandedSwingLow,
3260 //				MotionTask::oneHandedThrust,
3261 };
3262 
3263 const CombatMotionSet oneHandedSwingSet = {
3264 	oneHandedSwingArray,
3265 	ARRAYSIZE(oneHandedSwingArray)
3266 };
3267 
3268 //  Construct a subset of all high one handed swing types
3269 const uint8 oneHandedHighSwingArray[] = {
3270 	MotionTask::oneHandedSwingHigh,
3271 };
3272 
3273 const CombatMotionSet oneHandedHighSwingSet = {
3274 	oneHandedHighSwingArray,
3275 	ARRAYSIZE(oneHandedHighSwingArray)
3276 };
3277 
3278 //  Construct a subset of all low one handed swing types
3279 const uint8 oneHandedLowSwingArray[] = {
3280 	MotionTask::oneHandedSwingLow,
3281 };
3282 
3283 const CombatMotionSet oneHandedLowSwingSet = {
3284 	oneHandedLowSwingArray,
3285 	ARRAYSIZE(oneHandedLowSwingArray)
3286 };
3287 
3288 //-----------------------------------------------------------------------
3289 //  Handle all one handed swing motions
3290 
oneHandedSwingAction(void)3291 void MotionTask::oneHandedSwingAction(void) {
3292 	if (flags & reset) {
3293 		//  Let the game engine know about this aggressive act
3294 		logAggressiveAct(object->thisID(), targetObj->thisID());
3295 
3296 		//  Notify the target actor that he is being attacked
3297 		if (isActor(targetObj))
3298 			((Actor *)targetObj)->evaluateMeleeAttack((Actor *)object);
3299 
3300 		//  Create an animation type lookup table
3301 		static const uint8  animationTypeArray[] = {
3302 			actionSwingHigh,
3303 			actionSwingLow,
3304 		};
3305 
3306 		Actor *const       a = (Actor *)object;
3307 		uint8               actorAnimation;
3308 		int16               actorMidAltitude,
3309 		                    targetAltitude = targetObj->getLocation().z;
3310 
3311 		const CombatMotionSet   *availableSet;
3312 
3313 		//  Calculate the altitude of the actor's mid section
3314 		actorMidAltitude = a->getLocation().z + (a->proto()->height >> 1);
3315 
3316 		if (targetAltitude > actorMidAltitude)
3317 			//  The target is higher than the actor's midsection
3318 			availableSet = &oneHandedHighSwingSet;
3319 		else {
3320 			uint8           targetHeight = targetObj->proto()->height;
3321 
3322 			if (targetAltitude + targetHeight < actorMidAltitude)
3323 				//  The target is below the actor's midsection
3324 				availableSet = &oneHandedLowSwingSet;
3325 			else
3326 				//  The target is nearly the same altitude as the actor
3327 				availableSet = &oneHandedSwingSet;
3328 		}
3329 
3330 		//  Calculate the direction of the attack
3331 		direction = (targetObj->getLocation() - a->getLocation()).quickDir();
3332 
3333 		//  Randomly select a combat motion type from the available set
3334 		combatMotionType = availableSet->selectRandom();
3335 
3336 		/*      if ( combatMotionType == oneHandedThrust )
3337 		        {
3338 		            //  Initialize the thrust motion
3339 		        }
3340 		        else*/
3341 		{
3342 			actorAnimation = animationTypeArray[combatMotionType];
3343 			if (a->_appearance != NULL
3344 			        &&  a->isActionAvailable(actorAnimation)) {
3345 				//  Compute the number of frames in the animation before the
3346 				//  actual strike
3347 				actionCounter = a->animationFrames(actorAnimation, direction) - 2;
3348 
3349 				a->setAction(actorAnimation, 0);
3350 
3351 				//  Set this flag to indicate that the animation is actually
3352 				//  being played
3353 				flags |= nextAnim;
3354 			} else {
3355 				actionCounter = 1;
3356 
3357 				//  Clear this flag to indicate that the animation is not
3358 				//  being played
3359 				flags &= ~nextAnim;
3360 			}
3361 
3362 		}
3363 
3364 		a->setActionPoints(actionCounter * 2);
3365 
3366 		a->setActionPoints(
3367 		    computeTurnFrames(a->_currentFacing, direction) + 10);
3368 
3369 		flags &= ~reset;
3370 	} else
3371 		//  Call the generic offensive melee function
3372 		offensiveMeleeAction();
3373 }
3374 
3375 //-----------------------------------------------------------------------
3376 //	Compute the number of frames before the actual strike in an
3377 //	offensive melee motion
3378 
framesUntilStrike(void)3379 uint16 MotionTask::framesUntilStrike(void) {
3380 	//  If the melee action has not been initialized, return a safe value
3381 	if (flags & reset) return maxuint16;
3382 
3383 	uint16          turnFrames;
3384 
3385 	turnFrames = (direction - ((Actor *)object)->_currentFacing) & 0x7;
3386 	if (turnFrames > 4) turnFrames = 8 - turnFrames;
3387 
3388 	return turnFrames + actionCounter;
3389 }
3390 
3391 //-----------------------------------------------------------------------
3392 //	Returns a pointer to the blocking object if it applicable to
3393 //	this motion task
3394 
blockingObject(Actor * thisAttacker)3395 GameObject *MotionTask::blockingObject(Actor *thisAttacker) {
3396 	return      isDefense()
3397 	            && (d.defenseFlags & blocking)
3398 	            &&  thisAttacker == d.attacker
3399 	            ?   d.defensiveObj
3400 	            :   NULL;
3401 }
3402 
3403 //-----------------------------------------------------------------------
3404 //  Handle bow firing motions
3405 
fireBowAction(void)3406 void MotionTask::fireBowAction(void) {
3407 	Actor       *a = (Actor *)object;
3408 
3409 	assert(a->_leftHandObject != Nothing);
3410 
3411 	//  Initialize the bow firing motion
3412 	if (flags & reset) {
3413 		//  Let the game engine know about this aggressive act
3414 		logAggressiveAct(object->thisID(), targetObj->thisID());
3415 
3416 		//  Compute the direction to the target
3417 		direction = (targetObj->getLocation() - a->getLocation()).quickDir();
3418 
3419 		if (a->_appearance != NULL
3420 		        &&  a->isActionAvailable(actionFireBow)) {
3421 			//  Calculate the number of frames in the animation before the
3422 			//  projectile is actually fired
3423 			actionCounter = a->animationFrames(actionFireBow, direction) - 1;
3424 			a->setAction(actionFireBow, 0);
3425 
3426 			//  Set this flag to indicate that the animation is actually
3427 			//  being played
3428 			flags |= nextAnim;
3429 		} else {
3430 			actionCounter = 1;
3431 
3432 			//  Clear this flag to indicate that the animation is not
3433 			//  being played
3434 			flags &= ~nextAnim;
3435 		}
3436 
3437 		a->setActionPoints(
3438 		    computeTurnFrames(a->_currentFacing, direction) + 10);
3439 
3440 		if (a->_currentFacing != direction)
3441 			a->turn(direction);
3442 
3443 		flags &= ~reset;
3444 	} else if (a->_currentFacing != direction)
3445 		a->turn(direction);
3446 	else {
3447 		//  If the actors appearance becomes NULL, make sure this action
3448 		//  no longer depends upon the animation
3449 		if ((flags & nextAnim) && a->_appearance == NULL)
3450 			flags &= ~nextAnim;
3451 
3452 		//  If the action counter has reached zero, get a projectile and
3453 		//  fire it
3454 		if (actionCounter == 0) {
3455 			GameObject  *missileWeapon;
3456 
3457 			missileWeapon = GameObject::objectAddress(a->_leftHandObject);
3458 			if (missileWeapon != NULL) {
3459 				GameObject  *proj;
3460 
3461 				//  Ask the missile weapon's prototype to get a projectile
3462 				proj =  missileWeapon->proto()->getProjectile(
3463 				            a->_leftHandObject,
3464 				            a->thisID());
3465 
3466 				//  Shoot the projectile
3467 				if (proj != NULL) {
3468 					TilePoint   actorLoc = a->getLocation();
3469 					uint8       actorCrossSection = a->proto()->crossSection,
3470 					            projCrossSection = proj->proto()->crossSection;
3471 					ObjectID    projID;
3472 
3473 					actorLoc.u +=       incDirTable[a->_currentFacing].u
3474 					                    * (actorCrossSection + projCrossSection);
3475 					actorLoc.v +=       incDirTable[a->_currentFacing].v
3476 					                    * (actorCrossSection + projCrossSection);
3477 					actorLoc.z += a->proto()->height * 7 / 8;
3478 
3479 					if ((projID =   proj->extractMerged(Location(actorLoc, a->IDParent()), 1)) !=  Nothing) {
3480 						g_vm->_cnm->setUpdate(a->thisID());
3481 						proj = GameObject::objectAddress(projID);
3482 						shootObject(*proj, *a, *targetObj, 16);
3483 					}
3484 				}
3485 			}
3486 		}
3487 
3488 		if (flags & nextAnim) {
3489 			//  Run through the animation frames
3490 			if (!a->nextAnimationFrame()) {
3491 				if (actionCounter >= 0) actionCounter--;
3492 			} else
3493 				remove();
3494 		} else {
3495 			if (actionCounter > 0)
3496 				actionCounter--;
3497 			else
3498 				remove();
3499 		}
3500 	}
3501 }
3502 
3503 //-----------------------------------------------------------------------
3504 //  Handle spell casting motions
3505 
castSpellAction(void)3506 void MotionTask::castSpellAction(void) {
3507 	Actor       *a = (Actor *)object;
3508 
3509 	//  Turn until facing the target
3510 	if (a->_currentFacing != direction)
3511 		a->turn(direction);
3512 	else {
3513 		if (flags & reset) {
3514 			if (a->_appearance != NULL
3515 			        &&  a->isActionAvailable(actionCastSpell)) {
3516 				//  Calculate the number of frames in the animation before the
3517 				//  spell is case
3518 				actionCounter = a->animationFrames(actionCastSpell, direction) - 1;
3519 				a->setAction(actionCastSpell, 0);
3520 
3521 				//  Set this flag to indicate that the animation is actually
3522 				//  being played
3523 				flags |= nextAnim;
3524 			} else {
3525 				actionCounter = 3;
3526 
3527 				//  Clear this flag to indicate that the animation is not
3528 				//  being played
3529 				flags &= ~nextAnim;
3530 			}
3531 
3532 			flags &= ~reset;
3533 		}
3534 
3535 		//  If the actors appearance becomes NULL, make sure this action
3536 		//  no longer depends upon the animation
3537 		if ((flags & nextAnim) && a->_appearance == NULL)
3538 			flags &= ~nextAnim;
3539 
3540 		if (actionCounter == 0) {
3541 			if (spellObj) {
3542 				if (flags & TAGTarg) {
3543 					assert(targetTAG->_data.itemType == activeTypeInstance);
3544 					spellObj->implementAction(spellObj->getSpellID(), a->thisID(), targetTAG);
3545 				} else if (flags & LocTarg) {
3546 					spellObj->implementAction(spellObj->getSpellID(), a->thisID(), targetLoc);
3547 				} else if (targetObj) {
3548 					spellObj->implementAction(spellObj->getSpellID(), a->thisID(), targetObj->thisID());
3549 				}
3550 			}
3551 		}
3552 
3553 		if (flags & nextAnim) {
3554 			//  Run through the animation frames
3555 			if (!a->nextAnimationFrame()) {
3556 				if (actionCounter >= 0) actionCounter--;
3557 			} else
3558 				remove();
3559 		} else {
3560 			if (actionCounter > 0)
3561 				actionCounter--;
3562 			else
3563 				remove();
3564 		}
3565 	}
3566 }
3567 
3568 //-----------------------------------------------------------------------
3569 //  Handle wand using motions
3570 
useWandAction(void)3571 void MotionTask::useWandAction(void) {
3572 	//  Initialize the wand using motion
3573 	if (flags & reset) {
3574 		//  Let the game engine know about this aggressive act
3575 		logAggressiveAct(object->thisID(), targetObj->thisID());
3576 
3577 		Actor       *a = (Actor *)object;
3578 
3579 		direction = (targetObj->getLocation() - a->getLocation()).quickDir();
3580 
3581 		if (a->_appearance != NULL
3582 		        &&  a->isActionAvailable(actionUseWand)) {
3583 			actionCounter = a->animationFrames(actionUseWand, direction) - 1;
3584 			a->setAction(actionUseWand, 0);
3585 
3586 			//  Set this flag to indicate that the animation is actually
3587 			//  being played
3588 			flags |= nextAnim;
3589 		} else {
3590 			actionCounter = 3;
3591 
3592 			//  Clear this flag to indicate that the animation is not
3593 			//  being played
3594 			flags &= ~nextAnim;
3595 		}
3596 
3597 		a->setActionPoints(
3598 		    computeTurnFrames(a->_currentFacing, direction) + 10);
3599 
3600 		flags &= ~reset;
3601 	}
3602 	useMagicWeaponAction();
3603 }
3604 
3605 //  Defensive combat actions
3606 //-----------------------------------------------------------------------
3607 //	Handle two handed parrying motions
3608 
twoHandedParryAction(void)3609 void MotionTask::twoHandedParryAction(void) {
3610 	if (flags & reset) {
3611 		Actor       *a = (Actor *)object;
3612 		int16       animationFrames;
3613 
3614 		direction = (d.attacker->getLocation() - a->getLocation()).quickDir();
3615 
3616 		if (a->_appearance != NULL
3617 		        &&  a->isActionAvailable(actionTwoHandParry)) {
3618 			a->setAction(actionTwoHandParry, 0);
3619 			animationFrames = a->animationFrames(actionTwoHandParry, direction);
3620 
3621 			//  Set this flag to indicate that the animation is actually
3622 			//  being played
3623 			flags |= nextAnim;
3624 		} else {
3625 			animationFrames = 2;
3626 
3627 			//  Clear this flag to indicate that the animation is not
3628 			//  being played
3629 			flags &= ~nextAnim;
3630 		}
3631 
3632 		a->setActionPoints(
3633 		    computeTurnFrames(a->_currentFacing, direction)
3634 		    +   animationFrames + 1);
3635 
3636 		flags &= ~reset;
3637 	}
3638 	defensiveMeleeAction();
3639 }
3640 
3641 //-----------------------------------------------------------------------
3642 //	Handle one handed parrying motions
3643 
oneHandedParryAction(void)3644 void MotionTask::oneHandedParryAction(void) {
3645 	if (flags & reset) {
3646 		Actor       *a = (Actor *)object;
3647 		int16       animationFrames;
3648 
3649 		direction = (d.attacker->getLocation() - a->getLocation()).quickDir();
3650 
3651 		combatMotionType = oneHandedParryHigh;
3652 		if (a->_appearance != NULL
3653 		        &&  a->isActionAvailable(actionParryHigh)) {
3654 			a->setAction(actionParryHigh, 0);
3655 			animationFrames = a->animationFrames(actionParryHigh, direction);
3656 
3657 			//  Set this flag to indicate that the animation is actually
3658 			//  being played
3659 			flags |= nextAnim;
3660 		} else {
3661 			animationFrames = 2;
3662 
3663 			//  Clear this flag to indicate that the animation is not
3664 			//  being played
3665 			flags &= ~nextAnim;
3666 		}
3667 
3668 		a->setActionPoints(
3669 		    computeTurnFrames(a->_currentFacing, direction)
3670 		    +   animationFrames + 1);
3671 
3672 		flags &= ~reset;
3673 	}
3674 	defensiveMeleeAction();
3675 }
3676 
3677 //-----------------------------------------------------------------------
3678 //	Handle shield parrying motions
3679 
shieldParryAction(void)3680 void MotionTask::shieldParryAction(void) {
3681 	if (flags & reset) {
3682 		Actor       *a = (Actor *)object;
3683 		int16       animationFrames;
3684 
3685 		direction = (d.attacker->getLocation() - a->getLocation()).quickDir();
3686 
3687 		if (a->_appearance != NULL
3688 		        &&  a->isActionAvailable(actionShieldParry)) {
3689 			a->setAction(actionShieldParry, 0);
3690 			animationFrames = a->animationFrames(actionShieldParry, direction);
3691 
3692 			//  Set this flag to indicate that the animation is actually
3693 			//  being played
3694 			flags |= nextAnim;
3695 		} else {
3696 			animationFrames = 1;
3697 
3698 			//  Clear this flag to indicate that the animation is not
3699 			//  being played
3700 			flags &= ~nextAnim;
3701 		}
3702 
3703 		a->setActionPoints(
3704 		    computeTurnFrames(a->_currentFacing, direction)
3705 		    +   animationFrames + 1);
3706 
3707 		flags &= ~reset;
3708 	}
3709 	defensiveMeleeAction();
3710 }
3711 
3712 //-----------------------------------------------------------------------
3713 //	Handle dodging motions
3714 
dodgeAction(void)3715 void MotionTask::dodgeAction(void) {
3716 	Actor           *a = (Actor *)object;
3717 	MotionTask      *attackerMotion = d.attacker->_moveTask;
3718 
3719 	if (flags & reset) {
3720 		//  If the attacker is not attacking, we're done
3721 		if (attackerMotion == NULL
3722 		        ||  !attackerMotion->isMeleeAttack()) {
3723 			a->setInterruptablity(true);
3724 			remove();
3725 			return;
3726 		}
3727 
3728 		//  If the strike is about to land start the dodging motion
3729 		if (attackerMotion->framesUntilStrike() <= 2) {
3730 			int16       animationFrames;
3731 
3732 			if (a->_appearance != NULL
3733 			        &&  a->isActionAvailable(actionJumpUp, a->_currentFacing)) {
3734 				a->setAction(actionJumpUp, 0);
3735 				animationFrames = a->animationFrames(actionJumpUp, a->_currentFacing);
3736 
3737 				//  Set this flag to indicate that the animation is actually
3738 				//  being played
3739 				flags |= nextAnim;
3740 			} else {
3741 				animationFrames = 3;
3742 
3743 				//  Clear this flag to indicate that the animation is not
3744 				//  being played
3745 				flags &= ~nextAnim;
3746 			}
3747 
3748 			actionCounter = animationFrames - 1;
3749 			a->setActionPoints(animationFrames + 1);
3750 
3751 			flags &= ~reset;
3752 		}
3753 	} else {
3754 		//  If the actors appearance becomes NULL, make sure this action
3755 		//  no longer depends upon the animation
3756 		if ((flags & nextAnim) && a->_appearance == NULL)
3757 			flags &= ~nextAnim;
3758 
3759 		if (flags & nextAnim) {
3760 			//  Run through the animation frames
3761 			if (!a->nextAnimationFrame()) {
3762 				if (actionCounter > 0) actionCounter--;
3763 			} else
3764 				remove();
3765 		} else {
3766 			if (actionCounter > 0)
3767 				actionCounter--;
3768 			else
3769 				remove();
3770 		}
3771 	}
3772 }
3773 
3774 //-----------------------------------------------------------------------
3775 //	Handle accept hit motions
3776 
acceptHitAction(void)3777 void MotionTask::acceptHitAction(void) {
3778 	Actor           *a = (Actor *)object;
3779 
3780 	if (flags & reset) {
3781 		TilePoint           newLoc = a->getLocation();
3782 		StandingTileInfo    sti;
3783 		int16               animationFrames;
3784 
3785 		a->_currentFacing =
3786 		    (d.attacker->getWorldLocation() - a->getLocation()).quickDir();
3787 
3788 		if (a->_appearance != NULL
3789 		        &&  a->isActionAvailable(actionHit, a->_currentFacing)) {
3790 			a->setAction(actionHit, 0);
3791 			animationFrames = a->animationFrames(actionHit, a->_currentFacing);
3792 
3793 			//  Set this flag to indicate that the animation is actually
3794 			//  being played
3795 			flags |= nextAnim;
3796 		} else {
3797 			animationFrames = 1;
3798 
3799 			//  Clear this flag to indicate that the animation is not
3800 			//  being played
3801 			flags &= ~nextAnim;
3802 		}
3803 
3804 		a->setActionPoints(animationFrames + 1);
3805 
3806 		if (g_vm->_rnd->getRandomNumber(1)) {
3807 			//  Calculate the new position to knock the actor back to
3808 			newLoc += dirTable[(a->_currentFacing - 4) & 0x7];
3809 
3810 			//  If the actor is not blocked, move him back
3811 			if (!checkBlocked(a, newLoc)) {
3812 				newLoc.z = tileSlopeHeight(newLoc, a, &sti);
3813 				a->move(newLoc);
3814 				setObjectSurface(a, sti);
3815 			}
3816 		}
3817 
3818 		flags &= ~reset;
3819 	} else {
3820 		//  If the actors appearance becomes NULL, make sure this action
3821 		//  no longer depends upon the animation
3822 		if ((flags & nextAnim) && a->_appearance == NULL)
3823 			flags &= ~nextAnim;
3824 
3825 		if (flags & nextAnim) {
3826 			if (a->nextAnimationFrame()) remove();
3827 		} else
3828 			remove();
3829 	}
3830 }
3831 
3832 //-----------------------------------------------------------------------
3833 //	Handle fall down motions
3834 
fallDownAction(void)3835 void MotionTask::fallDownAction(void) {
3836 	Actor           *a = (Actor *)object;
3837 
3838 	if (flags & reset) {
3839 		TilePoint           newLoc = a->getLocation();
3840 		StandingTileInfo    sti;
3841 		int16               animationFrames;
3842 
3843 		a->_currentFacing =
3844 		    (d.attacker->getWorldLocation() - a->getLocation()).quickDir();
3845 
3846 		if (a->_appearance != NULL
3847 		        &&  a->isActionAvailable(actionKnockedDown, a->_currentFacing)) {
3848 			a->setAction(actionKnockedDown, 0);
3849 			animationFrames =   a->animationFrames(
3850 			                        actionKnockedDown,
3851 			                        a->_currentFacing);
3852 
3853 			//  Set this flag to indicate that the animation is actually
3854 			//  being played
3855 			flags |= nextAnim;
3856 		} else {
3857 			animationFrames = 6;
3858 
3859 			//  Clear this flag to indicate that the animation is not
3860 			//  being played
3861 			flags &= ~nextAnim;
3862 		}
3863 
3864 		a->setActionPoints(animationFrames + 1);
3865 
3866 		if (g_vm->_rnd->getRandomNumber(1)) {
3867 			//  Calculate the new position to knock the actor back to
3868 			newLoc += dirTable[(a->_currentFacing - 4) & 0x7];
3869 			newLoc.z = tileSlopeHeight(newLoc, a, &sti);
3870 
3871 			//  If the actor is not blocked, move him back
3872 			if (!checkBlocked(a, newLoc)) {
3873 				a->move(newLoc);
3874 				setObjectSurface(a, sti);
3875 			}
3876 		}
3877 
3878 		flags &= ~reset;
3879 	} else {
3880 		//  If the actors appearance becomes NULL, make sure this action
3881 		//  no longer depends upon the animation
3882 		if ((flags & nextAnim) && a->_appearance == NULL)
3883 			flags &= ~nextAnim;
3884 
3885 		if (flags & nextAnim) {
3886 			if (a->nextAnimationFrame()) remove();
3887 		} else
3888 			remove();
3889 	}
3890 }
3891 
3892 //-----------------------------------------------------------------------
3893 //  Generic offensive melee code.  Called by twoHandedSwingAction()
3894 //	and oneHandedSwingAction()
3895 
offensiveMeleeAction(void)3896 void MotionTask::offensiveMeleeAction(void) {
3897 	Actor       *a = (Actor *)object;
3898 
3899 	//  Turn until facing the target
3900 	if (a->_currentFacing != direction)
3901 		a->turn(direction);
3902 	else {
3903 		//  If the actors appearance becomes NULL, make sure this action
3904 		//  no longer depends upon the animation
3905 		if ((flags & nextAnim) && a->_appearance == NULL)
3906 			flags &= ~nextAnim;
3907 
3908 		//  If the action counter has reached zero, use the weapon on
3909 		//  the target
3910 		if (actionCounter == 0) {
3911 			GameObject  *weapon;
3912 
3913 			weapon = a->offensiveObject();
3914 			if (weapon) weapon->strike(a->thisID(), targetObj->thisID());
3915 		}
3916 
3917 		if (flags & nextAnim) {
3918 			//  Run through the animation frames
3919 			if (!a->nextAnimationFrame()) {
3920 				if (actionCounter >= 0) actionCounter--;
3921 			} else
3922 				remove();
3923 		} else {
3924 			if (actionCounter > 0)
3925 				actionCounter--;
3926 			else
3927 				remove();
3928 		}
3929 	}
3930 }
3931 
3932 //-----------------------------------------------------------------------
3933 //  Generic magic weapon code.  Called by useWandAction().
3934 
useMagicWeaponAction(void)3935 void MotionTask::useMagicWeaponAction(void) {
3936 	Actor       *a = (Actor *)object;
3937 
3938 	//  Turn until facing the target
3939 	if (a->_currentFacing != direction)
3940 		a->turn(direction);
3941 	else {
3942 		//  If the actors appearance becomes NULL, make sure this action
3943 		//  no longer depends upon the animation
3944 		if ((flags & nextAnim) && a->_appearance == NULL)
3945 			flags &= ~nextAnim;
3946 
3947 		//  If the action counter has reached zero, get a spell and
3948 		//  use it
3949 		if (actionCounter == 0) {
3950 			GameObject  *magicWeapon;
3951 
3952 			magicWeapon = a->offensiveObject();
3953 
3954 			if (magicWeapon != NULL && magicWeapon->IDChild() != Nothing) {
3955 				GameObject      *spell;
3956 				SkillProto      *spellProto;
3957 
3958 				spell = GameObject::objectAddress(magicWeapon->IDChild());
3959 				spellProto = (SkillProto *)spell->proto();
3960 
3961 				assert(spellProto->containmentSet() & ProtoObj::isSkill);
3962 
3963 				//  use the spell
3964 				spellProto->implementAction(
3965 				    spellProto->getSpellID(),
3966 				    magicWeapon->thisID(),
3967 				    targetObj->thisID());
3968 			}
3969 		}
3970 
3971 		if (flags & nextAnim) {
3972 			//  Run through the animation frames
3973 			if (!a->nextAnimationFrame()) {
3974 				if (actionCounter >= 0) actionCounter--;
3975 			} else
3976 				remove();
3977 		} else {
3978 			if (actionCounter > 0)
3979 				actionCounter--;
3980 			else
3981 				remove();
3982 		}
3983 	}
3984 }
3985 
3986 //-----------------------------------------------------------------------
3987 //	Generic defensive melee code.  Called by twoHandedParryAction(),
3988 //	oneHandedParryAction() and shieldParryAction().
3989 
defensiveMeleeAction(void)3990 void MotionTask::defensiveMeleeAction(void) {
3991 	Actor           *a = (Actor *)object;
3992 	MotionTask      *attackerMotion = d.attacker->_moveTask;
3993 
3994 	//  Determine if the blocking action has been initiated
3995 	if (!(d.defenseFlags & blocking)) {
3996 		//  If the attacker is not attacking, we're done
3997 		if (attackerMotion == NULL
3998 		        ||  !attackerMotion->isMeleeAttack()) {
3999 			a->setInterruptablity(true);
4000 			remove();
4001 			return;
4002 		}
4003 
4004 		//  turn towards attacker
4005 		if (a->_currentFacing != direction)
4006 			a->turn(direction);
4007 
4008 		//  If the strike is about to land start the blocking motion
4009 		if (attackerMotion->framesUntilStrike() <= 1)
4010 			d.defenseFlags |= blocking;
4011 	} else {
4012 		//  If the actors appearance becomes NULL, make sure this action
4013 		//  no longer depends upon the animation
4014 		if ((flags & nextAnim) && a->_appearance == NULL)
4015 			flags &= ~nextAnim;
4016 
4017 		//  Run through the animation frames
4018 		if (!(flags & nextAnim) || a->nextAnimationFrame()) {
4019 			//  Wait for the attacker's attack
4020 			if (attackerMotion == NULL
4021 			        ||  !attackerMotion->isMeleeAttack()) {
4022 				a->setInterruptablity(true);
4023 				remove();
4024 			}
4025 		}
4026 	}
4027 }
4028 
4029 //-----------------------------------------------------------------------
4030 //	Routine to update positions of all moving objects using MotionTasks
4031 
updatePositions(void)4032 void MotionTask::updatePositions(void) {
4033 	TilePoint       targetVector;
4034 	TilePoint       fallVelocity, terminalVelocity(15, 15, 0);
4035 	TilePoint       curLoc;
4036 	int16           targetDist;
4037 	StandingTileInfo sti;
4038 
4039 	for (Common::List<MotionTask *>::iterator it = g_vm->_mTaskList->_list.begin(); it != g_vm->_mTaskList->_list.end(); it = g_vm->_mTaskList->_nextMT) {
4040 		MotionTask *mt = *it;
4041 		GameObject  *obj = mt->object;
4042 		ProtoObj    *proto = obj->proto();
4043 		Actor       *a = (Actor *)obj;
4044 		bool        moveTaskDone = false;
4045 
4046 		g_vm->_mTaskList->_nextMT = it;
4047 		g_vm->_mTaskList->_nextMT++;
4048 
4049 		if (!isWorld(obj->IDParent())) {
4050 			mt->remove();
4051 			continue;
4052 		}
4053 
4054 		//  Determine if this motion should be skipped
4055 		if (interruptableMotionsPaused
4056 		        &&  isActor(obj)
4057 		        &&  a->isInterruptable())
4058 			continue;
4059 
4060 		if (obj->_data.location.z < -(proto->height >> 2))
4061 			mt->flags |= inWater;
4062 		else
4063 			mt->flags &= ~inWater;
4064 
4065 		switch (mt->motionType) {
4066 		case motionTypeThrown:
4067 		case motionTypeShot:
4068 			mt->ballisticAction();
4069 			break;
4070 
4071 		case motionTypeWalk:
4072 			mt->walkAction();
4073 			break;
4074 
4075 		case motionTypeClimbUp:
4076 			mt->upLadderAction();
4077 			break;
4078 
4079 		case motionTypeClimbDown:
4080 			mt->downLadderAction();
4081 			break;
4082 
4083 		case motionTypeTalk:
4084 
4085 			if (mt->flags & reset) {
4086 				a->setAction(actionStand, 0);
4087 				a->_cycleCount = g_vm->_rnd->getRandomNumber(3);
4088 				mt->flags &= ~(reset | nextAnim);
4089 			}
4090 			if (a->_cycleCount == 0) {
4091 				a->setAction(actionTalk, 0);
4092 				mt->flags |= nextAnim;
4093 				a->_cycleCount = -1;
4094 			} else if (mt->flags & nextAnim) {
4095 				if (a->nextAnimationFrame()) {
4096 					a->setAction(actionStand, 0);
4097 					a->_cycleCount = g_vm->_rnd->getRandomNumber(3);
4098 					mt->flags &= ~nextAnim;
4099 				}
4100 			} else
4101 				a->_cycleCount--;
4102 			break;
4103 
4104 		case motionTypeLand:
4105 		case motionTypeLandBadly:
4106 
4107 			if (mt->flags & reset) {
4108 				int16   newAction = mt->motionType == motionTypeLand
4109 				                    ?   actionJumpUp
4110 				                    :   actionFallBadly;
4111 
4112 				if (!a->isActionAvailable(newAction)) {
4113 					if (mt->prevMotionType == motionTypeWalk) {
4114 						mt->motionType = mt->prevMotionType;
4115 						if (mt->flags & pathFind) {
4116 							mt->changeTarget(
4117 							    mt->finalTarget,
4118 							    (mt->flags & requestRun) != 0);
4119 						} else {
4120 							mt->changeDirectTarget(
4121 							    mt->finalTarget,
4122 							    (mt->flags & requestRun) != 0);
4123 						}
4124 						g_vm->_mTaskList->_nextMT = it;
4125 					}
4126 				} else {
4127 					a->setAction(newAction, 0);
4128 					a->setInterruptablity(false);
4129 					mt->flags &= ~reset;
4130 				}
4131 			} else if (a->nextAnimationFrame() || (mt->flags & inWater)) {
4132 				if (mt->prevMotionType == motionTypeWalk) {
4133 					mt->motionType = mt->prevMotionType;
4134 					if (mt->flags & pathFind) {
4135 						mt->changeTarget(
4136 						    mt->finalTarget,
4137 						    (mt->flags & requestRun) != 0);
4138 					} else {
4139 						mt->changeDirectTarget(
4140 						    mt->finalTarget,
4141 						    (mt->flags & requestRun) != 0);
4142 					}
4143 					g_vm->_mTaskList->_nextMT = it;
4144 				} else if (mt->freeFall(obj->_data.location, sti) == false)
4145 					moveTaskDone = true;
4146 			} else {
4147 				//  If actor was running, go through an abreviated
4148 				//  landing sequence by aborting the landing animation
4149 				//  after the first frame.
4150 				if (mt->prevMotionType == motionTypeWalk
4151 				        &&  mt->flags & requestRun
4152 				        &&  mt->runCount == 0
4153 				        &&  !(mt->flags & inWater)) {
4154 					mt->motionType = mt->prevMotionType;
4155 					if (mt->flags & pathFind) {
4156 						mt->changeTarget(
4157 						    mt->finalTarget,
4158 						    (mt->flags & requestRun) != 0);
4159 					} else {
4160 						mt->changeDirectTarget(
4161 						    mt->finalTarget,
4162 						    (mt->flags & requestRun) != 0);
4163 					}
4164 					g_vm->_mTaskList->_nextMT = it;
4165 				}
4166 			}
4167 			break;
4168 
4169 		case motionTypeJump:
4170 
4171 			if (mt->flags & reset) {
4172 				a->setAction(actionJumpUp, 0);
4173 				a->setInterruptablity(false);
4174 				mt->flags &= ~reset;
4175 			} else if (a->nextAnimationFrame()) {
4176 				mt->motionType = motionTypeThrown;
4177 				a->setAction(actionFreeFall, 0);
4178 			}
4179 			break;
4180 
4181 		case motionTypeTurn:
4182 
4183 			mt->turnAction();
4184 			break;
4185 
4186 		case motionTypeGive:
4187 
4188 			mt->giveAction();
4189 			break;
4190 
4191 		case motionTypeRise:
4192 
4193 			if (a->_data.location.z < mt->immediateLocation.z) {
4194 				a->_data.location.z++;
4195 				if (mt->flags & nextAnim)
4196 					a->nextAnimationFrame();
4197 				mt->flags ^= nextAnim;
4198 			} else {
4199 				targetVector = mt->finalTarget - obj->_data.location;
4200 				targetDist = targetVector.quickHDistance();
4201 
4202 				if (targetDist > kTileUVSize) {
4203 					mt->motionType = mt->prevMotionType;
4204 					mt->flags |= reset;
4205 					g_vm->_mTaskList->_nextMT = it;
4206 				} else
4207 					moveTaskDone = true;
4208 			}
4209 			break;
4210 
4211 		case motionTypeWait:
4212 
4213 			if (mt->flags & reset) {
4214 				mt->actionCounter = 5;
4215 				mt->flags &= ~reset;
4216 			} else if (--mt->actionCounter == 0)
4217 				moveTaskDone = true;
4218 			break;
4219 
4220 		case motionTypeUseObject:
4221 
4222 			//  This will be uninterrutable for 2 frames
4223 			a->setActionPoints(2);
4224 			mt->o.directObject->use(a->thisID());
4225 			//g_vm->_mTaskList->_nextMT=mt;
4226 			moveTaskDone = true;
4227 			break;
4228 
4229 		case motionTypeUseObjectOnObject:
4230 
4231 			if (isWorld(mt->o.indirectObject->IDParent())) {
4232 				if (
4233 				    1
4234 #ifdef THIS_SHOULD_BE_IN_TILEMODE
4235 				    a->inUseRange(
4236 				        mt->o.indirectObject->getLocation(),
4237 				        mt->o.directObject)
4238 #endif
4239 				) {
4240 					mt->direction = (mt->o.indirectObject->getLocation()
4241 					                 -   a->getLocation()).quickDir();
4242 					if (a->_currentFacing != mt->direction)
4243 						a->turn(mt->direction);
4244 					else {
4245 						//  The actor will now be uniterruptable
4246 						a->setActionPoints(2);
4247 						mt->o.directObject->useOn(
4248 						    a->thisID(),
4249 						    mt->o.indirectObject->thisID());
4250 						if (mt->motionType == motionTypeUseObjectOnObject)
4251 							moveTaskDone = true;
4252 						else
4253 							g_vm->_mTaskList->_nextMT = it;
4254 					}
4255 				}
4256 			} else {
4257 				//  The actor will now be uniterruptable
4258 				a->setActionPoints(2);
4259 				mt->o.directObject->useOn(
4260 				    a->thisID(),
4261 				    mt->o.indirectObject->thisID());
4262 				if (mt->motionType == motionTypeUseObjectOnObject)
4263 					moveTaskDone = true;
4264 				else
4265 					g_vm->_mTaskList->_nextMT = it;
4266 			}
4267 
4268 			break;
4269 
4270 		case motionTypeUseObjectOnTAI:
4271 
4272 			if (mt->flags & reset) {
4273 				TilePoint       actorLoc = a->getLocation(),
4274 				                TAILoc;
4275 				TileRegion      TAIReg;
4276 				ActiveItem      *TAG = mt->o.TAI->getGroup();
4277 
4278 				//  Compute in points the region of the TAI
4279 				TAIReg.min.u = mt->o.TAI->_data.instance.u << kTileUVShift;
4280 				TAIReg.min.v = mt->o.TAI->_data.instance.v << kTileUVShift;
4281 				TAIReg.max.u =      TAIReg.min.u
4282 				                    + (TAG->_data.group.uSize << kTileUVShift);
4283 				TAIReg.max.v =      TAIReg.min.v
4284 				                    + (TAG->_data.group.vSize << kTileUVShift);
4285 				TAIReg.min.z = TAIReg.max.z = 0;
4286 
4287 				//  Find the point on the TAI closest to the actor
4288 				TAILoc.u = clamp(TAIReg.min.u, actorLoc.u, TAIReg.max.u - 1);
4289 				TAILoc.v = clamp(TAIReg.min.v, actorLoc.v, TAIReg.max.v - 1);
4290 				TAILoc.z = actorLoc.z;
4291 
4292 				//  Compute the direction from the actor to the TAI
4293 				mt->direction = (TAILoc - actorLoc).quickDir();
4294 				mt->flags &= ~reset;
4295 			}
4296 
4297 			if (a->_currentFacing != mt->direction)
4298 				a->turn(mt->direction);
4299 			else {
4300 				//  The actor will now be uniterruptable
4301 				a->setActionPoints(2);
4302 				mt->o.directObject->useOn(a->thisID(), mt->o.TAI);
4303 				if (mt->motionType == motionTypeUseObjectOnTAI)
4304 					moveTaskDone = true;
4305 				else
4306 					g_vm->_mTaskList->_nextMT = it;
4307 			}
4308 			break;
4309 
4310 		case motionTypeUseObjectOnLocation:
4311 
4312 			if (mt->flags & reset) {
4313 				mt->direction = (mt->targetLoc - a->getLocation()).quickDir();
4314 				mt->flags &= ~reset;
4315 			}
4316 
4317 			if (a->_currentFacing != mt->direction)
4318 				a->turn(mt->direction);
4319 			else {
4320 				//  The actor will now be uniterruptable
4321 				a->setActionPoints(2);
4322 				mt->o.directObject->useOn(a->thisID(), mt->targetLoc);
4323 				if (mt->motionType == motionTypeUseObjectOnLocation)
4324 					moveTaskDone = true;
4325 				else
4326 					g_vm->_mTaskList->_nextMT = it;
4327 			}
4328 			break;
4329 
4330 		case motionTypeUseTAI:
4331 
4332 			if (mt->flags & reset) {
4333 				TilePoint       actorLoc = a->getLocation(),
4334 				                TAILoc;
4335 				TileRegion      TAIReg;
4336 				ActiveItem      *TAG = mt->o.TAI->getGroup();
4337 
4338 				//  Compute in points the region of the TAI
4339 				TAIReg.min.u = mt->o.TAI->_data.instance.u << kTileUVShift;
4340 				TAIReg.min.v = mt->o.TAI->_data.instance.v << kTileUVShift;
4341 				TAIReg.max.u =      TAIReg.min.u
4342 				                    + (TAG->_data.group.uSize << kTileUVShift);
4343 				TAIReg.max.v =      TAIReg.min.v
4344 				                    + (TAG->_data.group.vSize << kTileUVShift);
4345 				TAIReg.min.z = TAIReg.max.z = 0;
4346 
4347 				//  Find the point on the TAI closest to the actor
4348 				TAILoc.u = clamp(TAIReg.min.u, actorLoc.u, TAIReg.max.u - 1);
4349 				TAILoc.v = clamp(TAIReg.min.v, actorLoc.v, TAIReg.max.v - 1);
4350 				TAILoc.z = actorLoc.z;
4351 
4352 				//  Compute the direction from the actor to the TAI
4353 				mt->direction = (TAILoc - actorLoc).quickDir();
4354 				mt->flags &= ~reset;
4355 			}
4356 
4357 			if (a->_currentFacing != mt->direction)
4358 				a->turn(mt->direction);
4359 			else {
4360 				//  The actor will now be uniterruptable
4361 				a->setActionPoints(2);
4362 				mt->o.TAI->use(a->thisID());
4363 				moveTaskDone = true;
4364 			}
4365 			break;
4366 
4367 		case motionTypeDropObject:
4368 
4369 			if (isWorld(mt->targetLoc.context)) {
4370 				if (mt->flags & reset) {
4371 					mt->direction = (mt->targetLoc - a->getLocation()).quickDir();
4372 					mt->flags &= ~reset;
4373 				}
4374 
4375 				if (a->_currentFacing != mt->direction)
4376 					a->turn(mt->direction);
4377 				else {
4378 					//  The actor will now be uniterruptable
4379 					a->setActionPoints(2);
4380 					mt->o.directObject->drop(a->thisID(),
4381 					                       mt->targetLoc,
4382 					                       mt->moveCount);
4383 					if (mt->motionType == motionTypeDropObject)
4384 						moveTaskDone = true;
4385 					else
4386 						g_vm->_mTaskList->_nextMT = it;
4387 				}
4388 			} else {
4389 				//  The actor will now be uniterruptable
4390 				a->setActionPoints(2);
4391 				mt->o.directObject->drop(a->thisID(),
4392 				                       mt->targetLoc,
4393 				                       mt->moveCount);
4394 				if (mt->motionType == motionTypeDropObject)
4395 					moveTaskDone = true;
4396 				else
4397 					g_vm->_mTaskList->_nextMT = it;
4398 			}
4399 
4400 			CMassWeightIndicator::bRedraw = true;   // tell the mass/weight indicators to refresh
4401 
4402 			break;
4403 
4404 		case motionTypeDropObjectOnObject:
4405 
4406 			if (isWorld(mt->o.indirectObject->IDParent())) {
4407 				mt->direction = (mt->o.indirectObject->getLocation()
4408 				                 -   a->getLocation()).quickDir();
4409 				if (a->_currentFacing != mt->direction)
4410 					a->turn(mt->direction);
4411 				else {
4412 					//  The actor will now be uniterruptable
4413 					a->setActionPoints(2);
4414 					mt->o.directObject->dropOn(
4415 					    a->thisID(),
4416 					    mt->o.indirectObject->thisID(),
4417 					    mt->moveCount);
4418 					if (mt->motionType == motionTypeDropObjectOnObject)
4419 						moveTaskDone = true;
4420 					else
4421 						g_vm->_mTaskList->_nextMT = it;
4422 				}
4423 			} else {
4424 				//  The actor will now be uniterruptable
4425 				a->setActionPoints(2);
4426 				mt->o.directObject->dropOn(
4427 				    a->thisID(),
4428 				    mt->o.indirectObject->thisID(),
4429 				    mt->moveCount);
4430 				if (mt->motionType == motionTypeDropObjectOnObject)
4431 					moveTaskDone = true;
4432 				else
4433 					g_vm->_mTaskList->_nextMT = it;
4434 			}
4435 
4436 			CMassWeightIndicator::bRedraw = true;   // tell the mass/weight indicators to refresh
4437 
4438 			break;
4439 
4440 		case motionTypeDropObjectOnTAI:
4441 
4442 			if (mt->flags & reset) {
4443 				mt->direction = (mt->targetLoc - a->getLocation()).quickDir();
4444 				mt->flags &= ~reset;
4445 			}
4446 
4447 			if (a->_currentFacing != mt->direction)
4448 				a->turn(mt->direction);
4449 			else {
4450 				//  The actor will now be uniterruptable
4451 				a->setActionPoints(2);
4452 				mt->o.directObject->dropOn(
4453 				    a->thisID(),
4454 				    mt->o.TAI,
4455 				    mt->targetLoc);
4456 				if (mt->motionType == motionTypeDropObjectOnTAI)
4457 					moveTaskDone = true;
4458 				else
4459 					g_vm->_mTaskList->_nextMT = it;
4460 			}
4461 			break;
4462 
4463 		case motionTypeTwoHandedSwing:
4464 			mt->twoHandedSwingAction();
4465 			break;
4466 
4467 		case motionTypeOneHandedSwing:
4468 			mt->oneHandedSwingAction();
4469 			break;
4470 
4471 		case motionTypeFireBow:
4472 			mt->fireBowAction();
4473 			break;
4474 
4475 		case motionTypeCastSpell:
4476 			mt->castSpellAction();
4477 			break;
4478 
4479 		case motionTypeUseWand:
4480 			mt->useWandAction();
4481 			break;
4482 
4483 		case motionTypeTwoHandedParry:
4484 			mt->twoHandedParryAction();
4485 			break;
4486 
4487 		case motionTypeOneHandedParry:
4488 			mt->oneHandedParryAction();
4489 			break;
4490 
4491 		case motionTypeShieldParry:
4492 			mt->shieldParryAction();
4493 			break;
4494 
4495 		case motionTypeDodge:
4496 			mt->dodgeAction();
4497 			break;
4498 
4499 		case motionTypeAcceptHit:
4500 			mt->acceptHitAction();
4501 			break;
4502 
4503 		case motionTypeFallDown:
4504 			mt->fallDownAction();
4505 			break;
4506 
4507 		case motionTypeDie:
4508 			if (mt->flags & reset) {
4509 				if (a->isActionAvailable(actionDie)) {
4510 					a->setAction(actionDie, 0);
4511 					a->setInterruptablity(false);
4512 					mt->flags &= ~reset;
4513 				} else {
4514 					moveTaskDone = true;
4515 					a->setInterruptablity(true);
4516 					if (!a->hasEffect(actorDisappearOnDeath)) {
4517 						a->setAction(actionDead, 0);
4518 						a->die();
4519 					} else {
4520 						a->die();
4521 						a->dropInventory();
4522 						a->deleteObjectRecursive();
4523 					}
4524 				}
4525 			} else if (a->nextAnimationFrame()) {
4526 				moveTaskDone = true;
4527 				a->setInterruptablity(true);
4528 				if (!a->hasEffect(actorDisappearOnDeath)) {
4529 					a->setAction(actionDead, 0);
4530 					a->die();
4531 				} else {
4532 					a->die();
4533 					a->dropInventory();
4534 					a->deleteObjectRecursive();
4535 				}
4536 			}
4537 
4538 			break;
4539 		}
4540 		if (moveTaskDone) mt->remove();
4541 	}
4542 }
4543 
4544 //-----------------------------------------------------------------------
4545 //	Manages any object which has no supporting surface.
4546 //	Returns true if object is still falling.
4547 
freeFall(TilePoint & newPos,StandingTileInfo & sti)4548 bool MotionTask::freeFall(TilePoint &newPos, StandingTileInfo &sti) {
4549 	int16           tHeight;
4550 	TilePoint       tPos;
4551 	uint8           objCrossSection;
4552 
4553 	tHeight = tileSlopeHeight(newPos, object, &sti);
4554 
4555 	if (object->_data.objectFlags & objectFloating) return false;
4556 
4557 	velocity.u = (newPos.u - object->_data.location.u) * 2 / 3;
4558 	velocity.v = (newPos.v - object->_data.location.v) * 2 / 3;
4559 	velocity.z = (newPos.z - object->_data.location.z) * 2 / 3;
4560 //	velocity.z = 0;
4561 
4562 	//  If terrain is HIGHER (or even sligtly lower) than we are
4563 	//  currently at, then try climbing it.
4564 
4565 	if (tHeight >= newPos.z - gravity * 4) {
4566 supported:
4567 		if (motionType != motionTypeWalk
4568 		        ||  tHeight <= newPos.z
4569 		        ||  !(flags & inWater)) {
4570 			if (tHeight > newPos.z + kMaxStepHeight) {
4571 				unstickObject(object);
4572 				tHeight = tileSlopeHeight(newPos, object, &sti);
4573 			}
4574 			newPos.z = tHeight;
4575 //			setObjectSurface( object, sti );
4576 			return false;
4577 		} else {
4578 			motionType = motionTypeRise;
4579 			immediateLocation.z = tHeight;
4580 			object->move(newPos);
4581 			return true;
4582 		}
4583 
4584 	}
4585 
4586 	//  Otherwise, begin a fall sequence...
4587 	tPos = newPos;
4588 
4589 	//  Attempt to solve cases where he gets stuck in falling,
4590 	//  by checking the contact of what he's about to fall on.
4591 	if (tPos.z > tHeight) tPos.z--;
4592 	//  See if we fell on something.
4593 	if (checkContact(object, tPos) == blockageNone) {
4594 falling:
4595 		if (motionType != motionTypeWalk
4596 				||  newPos.z > gravity * 4
4597 				||  tHeight >= 0) {
4598 			motionType = motionTypeThrown;
4599 
4600 //			newPos = tPos;
4601 			object->move(tPos);
4602 			return true;
4603 		} else {
4604 			newPos = tPos;
4605 			return false;
4606 		}
4607 	}
4608 
4609 	//  If we fall on something, reduce velocity due to impact.
4610 	//  Try a couple of probes to see if we can fall in
4611 	//  other directions.
4612 	objCrossSection = object->proto()->crossSection;
4613 
4614 	tPos.u += objCrossSection;
4615 	if (!checkBlocked(object, tPos)
4616 			&&  !checkContact(object, tPos))
4617 		goto falling;
4618 
4619 	tPos.u -= objCrossSection * 2;
4620 	if (!checkBlocked(object, tPos)
4621 			&&  !checkContact(object, tPos))
4622 		goto falling;
4623 
4624 	tPos.u += objCrossSection;
4625 	tPos.v += objCrossSection;
4626 	if (!checkBlocked(object, tPos)
4627 			&&  !checkContact(object, tPos))
4628 		goto falling;
4629 
4630 	tPos.v -= objCrossSection * 2;
4631 	if (!checkBlocked(object, tPos)
4632 			&&  !checkContact(object, tPos))
4633 		goto falling;
4634 
4635 	//  There is no support for the object and there is no place to fall
4636 	//  so cheat and pretend this whole mess never happened.
4637 	tPos = newPos;
4638 
4639 	tPos.u += objCrossSection;
4640 	tHeight = tileSlopeHeight(tPos, object, &sti);
4641 	if (tHeight <= tPos.z + kMaxStepHeight
4642 			&&  tHeight >= tPos.z - gravity * 4) {
4643 		newPos = tPos;
4644 		goto supported;
4645 	}
4646 
4647 	tPos.u -= objCrossSection * 2;
4648 	tHeight = tileSlopeHeight(tPos, object, &sti);
4649 	if (tHeight <= tPos.z + kMaxStepHeight
4650 			&&  tHeight >= tPos.z - gravity * 4) {
4651 		newPos = tPos;
4652 		goto supported;
4653 	}
4654 
4655 	tPos.u += objCrossSection;
4656 	tPos.v += objCrossSection;
4657 	tHeight = tileSlopeHeight(tPos, object, &sti);
4658 	if (tHeight <= tPos.z + kMaxStepHeight
4659 			&&  tHeight >= tPos.z - gravity * 4) {
4660 		newPos = tPos;
4661 		goto supported;
4662 	}
4663 
4664 	tPos.v -= objCrossSection * 2;
4665 	tHeight = tileSlopeHeight(tPos, object, &sti);
4666 	if (tHeight <= tPos.z + kMaxStepHeight
4667 			&&  tHeight >= tPos.z - gravity * 4) {
4668 		newPos = tPos;
4669 		goto supported;
4670 	}
4671 
4672 	//  If we STILL cannot find support for the object, change its
4673 	//  position and try again.  This should be very rare.
4674 	newPos.z--;
4675 	object->move(newPos);
4676 	unstickObject(object);
4677 	newPos = object->getLocation();
4678 	return true;
4679 }
4680 
4681 //-----------------------------------------------------------------------
4682 //	Calls the handling routine for each active motion task
4683 
moveActors(int32 deltaTime)4684 void moveActors(int32 deltaTime) {
4685 	MotionTask::updatePositions();
4686 }
4687 
4688 //-----------------------------------------------------------------------
4689 //	Check the actor's area to see if he is intersecting ladder terrain, and
4690 //	if so, make him climb it.
4691 
checkLadder(Actor * a,const TilePoint & loc)4692 bool checkLadder(Actor *a, const TilePoint &loc) {
4693 	TileRegion          actorTileReg;
4694 	uint8               crossSection = a->proto()->crossSection,
4695 	                    height = a->proto()->height;
4696 	int16               mapNum = a->getMapNum();
4697 	TileInfo            *ti;
4698 	TilePoint           tileLoc;
4699 	StandingTileInfo    sti = {nullptr, nullptr, {0, 0, 0}, 0};
4700 
4701 	actorTileReg.min.u = (loc.u - crossSection) >> kTileUVShift;
4702 	actorTileReg.min.v = (loc.v - crossSection) >> kTileUVShift;
4703 	actorTileReg.max.u = (loc.u + crossSection + kTileUVMask) >> kTileUVShift;
4704 	actorTileReg.max.v = (loc.v + crossSection + kTileUVMask) >> kTileUVShift;
4705 	actorTileReg.min.z = actorTileReg.max.z = 0;
4706 
4707 	TileIterator    iter(mapNum, actorTileReg);
4708 
4709 	for (ti = iter.first(&tileLoc, &sti);
4710 	        ti != NULL;
4711 	        ti = iter.next(&tileLoc, &sti)) {
4712 		if (!(ti->combinedTerrainMask() & terrainLadder)) continue;
4713 
4714 		if (sti.surfaceHeight + ti->attrs.terrainHeight < loc.z
4715 		        ||  sti.surfaceHeight > loc.z + height)
4716 			continue;
4717 
4718 		uint16      footPrintMask = 0xFFFF,
4719 		            ladderMask;
4720 		TilePoint   subTileLoc(
4721 		    tileLoc.u << kTileSubShift,
4722 		    tileLoc.v << kTileSubShift,
4723 		    0);
4724 		TileRegion  actorSubTileReg;
4725 
4726 		actorSubTileReg.min.u = (loc.u - crossSection) >> kSubTileShift;
4727 		actorSubTileReg.min.v = (loc.v - crossSection) >> kSubTileShift;
4728 		actorSubTileReg.max.u =
4729 		    (loc.u + crossSection + kSubTileMask) >> kSubTileShift;
4730 		actorSubTileReg.max.v =
4731 		    (loc.v + crossSection + kSubTileMask) >> kSubTileShift;
4732 
4733 		if (actorSubTileReg.min.u >= subTileLoc.u)
4734 			footPrintMask &=
4735 			    uMinMasks[actorSubTileReg.min.u - subTileLoc.u];
4736 
4737 		if (actorSubTileReg.min.v >= subTileLoc.v)
4738 			footPrintMask &=
4739 			    vMinMasks[actorSubTileReg.min.v - subTileLoc.v];
4740 
4741 		if (actorSubTileReg.max.u < subTileLoc.u + kTileSubSize)
4742 			footPrintMask &=
4743 			    uMaxMasks[actorSubTileReg.max.u - subTileLoc.u];
4744 
4745 		if (actorSubTileReg.max.v < subTileLoc.v + kTileSubSize)
4746 			footPrintMask &=
4747 			    vMaxMasks[actorSubTileReg.max.v - subTileLoc.v];
4748 
4749 		ladderMask =    ti->attrs.fgdTerrain == terrNumLadder
4750 		                ?   ti->attrs.terrainMask
4751 		                :   ~ti->attrs.terrainMask;
4752 
4753 		if (footPrintMask & ladderMask) {
4754 			if (!(~ladderMask & 0xF000)) {
4755 				a->_currentFacing = 7;
4756 				a->move(
4757 				    TilePoint(
4758 				        (tileLoc.u << kTileUVShift)
4759 				        +   kTileUVSize
4760 				        -   crossSection,
4761 				        (tileLoc.v << kTileUVShift) + kTileUVSize / 2,
4762 				        loc.z));
4763 			} else if (!(~ladderMask & 0x000F)) {
4764 				a->_currentFacing = 3;
4765 				a->move(
4766 				    TilePoint(
4767 				        (tileLoc.u << kTileUVShift) + crossSection,
4768 				        (tileLoc.v << kTileUVShift) + kTileUVSize / 2,
4769 				        loc.z));
4770 			} else if (!(~ladderMask & 0x8888)) {
4771 				a->_currentFacing = 1;
4772 				a->move(
4773 				    TilePoint(
4774 				        (tileLoc.u << kTileUVShift) + kTileUVSize / 2,
4775 				        (tileLoc.v << kTileUVShift)
4776 				        +   kTileUVSize
4777 				        -   crossSection,
4778 				        loc.z));
4779 			} else {
4780 				a->_currentFacing = 3;
4781 				a->move(
4782 				    TilePoint(
4783 				        (tileLoc.u << kTileUVShift) + kTileUVSize / 2,
4784 				        (tileLoc.v << kTileUVShift) + crossSection,
4785 				        loc.z));
4786 			}
4787 
4788 			if (loc.z
4789 			        <   tileSlopeHeight(a->getLocation(), a) + kMaxStepHeight)
4790 				MotionTask::upLadder(*a);
4791 			else
4792 				MotionTask::downLadder(*a);
4793 
4794 			return true;
4795 		}
4796 	}
4797 
4798 	return false;
4799 }
4800 
4801 
pauseInterruptableMotions(void)4802 void pauseInterruptableMotions(void) {
4803 	interruptableMotionsPaused = true;
4804 }
4805 
resumeInterruptableMotions(void)4806 void resumeInterruptableMotions(void) {
4807 	interruptableMotionsPaused = false;
4808 }
4809 
4810 /* ===================================================================== *
4811    MotionTask list management functions
4812  * ===================================================================== */
4813 
4814 //-----------------------------------------------------------------------
4815 //	Initialize the motion task list
4816 
initMotionTasks(void)4817 void initMotionTasks(void) {
4818 	//  Simply call the default MotionTaskList constructor
4819 	//new (g_vm->_mTaskList) MotionTaskList;
4820 }
4821 
saveMotionTasks(Common::OutSaveFile * outS)4822 void saveMotionTasks(Common::OutSaveFile *outS) {
4823 	debugC(2, kDebugSaveload, "Saving MotionTasks");
4824 
4825 	outS->write("MOTN", 4);
4826 	CHUNK_BEGIN;
4827 	g_vm->_mTaskList->write(out);
4828 	CHUNK_END;
4829 }
4830 
loadMotionTasks(Common::InSaveFile * in,int32 chunkSize)4831 void loadMotionTasks(Common::InSaveFile *in, int32 chunkSize) {
4832 	debugC(2, kDebugSaveload, "Loading MotionTasks");
4833 
4834 	//  If there is no saved data, simply call the default constructor
4835 	if (chunkSize == 0) {
4836 		//new (g_vm->_mTaskList) MotionTaskList;
4837 		return;
4838 	}
4839 
4840 	//  Reconstruct g_vm->_mTaskList from archived data
4841 	g_vm->_mTaskList->read(in);
4842 }
4843 
4844 //-----------------------------------------------------------------------
4845 //	Cleanup the motion task list
4846 
cleanupMotionTasks(void)4847 void cleanupMotionTasks(void) {
4848 	//  Simply call stackList's cleanup
4849 	g_vm->_mTaskList->cleanup();
4850 }
4851 
4852 } // end of namespace Saga2
4853