1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "twine/scene/animations.h"
24 #include "common/endian.h"
25 #include "common/memstream.h"
26 #include "common/stream.h"
27 #include "common/util.h"
28 #include "twine/audio/sound.h"
29 #include "twine/debugger/debug_scene.h"
30 #include "twine/parser/anim.h"
31 #include "twine/parser/entity.h"
32 #include "twine/renderer/renderer.h"
33 #include "twine/resources/resources.h"
34 #include "twine/scene/collision.h"
35 #include "twine/scene/extra.h"
36 #include "twine/scene/gamestate.h"
37 #include "twine/scene/grid.h"
38 #include "twine/scene/movements.h"
39 #include "twine/scene/scene.h"
40 #include "twine/shared.h"
41 
42 namespace TwinE {
43 
44 static const int32 magicLevelStrengthOfHit[] = {
45 	MagicballStrengthType::kNoBallStrength,
46 	MagicballStrengthType::kYellowBallStrength,
47 	MagicballStrengthType::kGreenBallStrength,
48 	MagicballStrengthType::kRedBallStrength,
49 	MagicballStrengthType::kFireBallStrength,
50 	0};
51 
Animations(TwinEEngine * engine)52 Animations::Animations(TwinEEngine *engine) : _engine(engine) {
53 }
54 
getBodyAnimIndex(AnimationTypes animIdx,int32 actorIdx)55 int32 Animations::getBodyAnimIndex(AnimationTypes animIdx, int32 actorIdx) {
56 	ActorStruct *actor = _engine->_scene->getActor(actorIdx);
57 	const int32 bodyAnimIndex = actor->_entityDataPtr->getAnimIndex(animIdx);
58 	if (bodyAnimIndex != -1) {
59 		_currentActorAnimExtraPtr = animIdx;
60 	}
61 	return bodyAnimIndex;
62 }
63 
applyAnimStepRotation(int32 deltaTime,int32 keyFrameLength,int16 newAngle1,int16 lastAngle1) const64 int16 Animations::applyAnimStepRotation(int32 deltaTime, int32 keyFrameLength, int16 newAngle1, int16 lastAngle1) const {
65 	const int16 lastAngle = ClampAngle(lastAngle1);
66 	const int16 newAngle = ClampAngle(newAngle1);
67 
68 	int16 angleDiff = newAngle - lastAngle;
69 
70 	int16 computedAngle;
71 	if (angleDiff) {
72 		if (angleDiff < -ANGLE_180) {
73 			angleDiff += ANGLE_360;
74 		} else if (angleDiff > ANGLE_180) {
75 			angleDiff -= ANGLE_360;
76 		}
77 
78 		computedAngle = lastAngle + (angleDiff * deltaTime) / keyFrameLength;
79 	} else {
80 		computedAngle = lastAngle;
81 	}
82 
83 	return ClampAngle(computedAngle);
84 }
85 
applyAnimStepTranslation(int32 deltaTime,int32 keyFrameLength,int16 newPos,int16 lastPos) const86 int16 Animations::applyAnimStepTranslation(int32 deltaTime, int32 keyFrameLength, int16 newPos, int16 lastPos) const {
87 	int16 distance = newPos - lastPos;
88 
89 	int16 computedPos;
90 	if (distance) {
91 		computedPos = lastPos + (distance * deltaTime) / keyFrameLength;
92 	} else {
93 		computedPos = lastPos;
94 	}
95 
96 	return computedPos;
97 }
98 
setModelAnimation(int32 keyframeIdx,const AnimData & animData,BodyData & bodyData,AnimTimerDataStruct * animTimerDataPtr)99 bool Animations::setModelAnimation(int32 keyframeIdx, const AnimData &animData, BodyData &bodyData, AnimTimerDataStruct *animTimerDataPtr) {
100 	if (!bodyData.isAnimated()) {
101 		return false;
102 	}
103 	const KeyFrame *keyFrame = animData.getKeyframe(keyframeIdx);
104 
105 	_currentStep.x = keyFrame->x;
106 	_currentStep.y = keyFrame->y;
107 	_currentStep.z = keyFrame->z;
108 
109 	_processRotationByAnim = keyFrame->boneframes[0].type;
110 	_processLastRotationAngle = ToAngle(keyFrame->boneframes[0].y);
111 
112 	const int16 numBones = bodyData.getNumBones();
113 
114 	int32 numOfBonesInAnim = animData.getNumBoneframes();
115 	if (numOfBonesInAnim > numBones) {
116 		numOfBonesInAnim = numBones;
117 	}
118 	const int32 keyFrameLength = keyFrame->length;
119 
120 	const KeyFrame *lastKeyFramePtr = animTimerDataPtr->ptr;
121 	int32 remainingFrameTime = animTimerDataPtr->time;
122 	if (lastKeyFramePtr == nullptr) {
123 		lastKeyFramePtr = keyFrame;
124 		remainingFrameTime = keyFrameLength;
125 	}
126 	const int32 deltaTime = _engine->_lbaTime - remainingFrameTime;
127 	if (deltaTime >= keyFrameLength) {
128 		copyKeyFrameToState(keyFrame, bodyData, numOfBonesInAnim);
129 		animTimerDataPtr->ptr = keyFrame;
130 		animTimerDataPtr->time = _engine->_lbaTime;
131 		return true;
132 	}
133 
134 	_processLastRotationAngle = (_processLastRotationAngle * deltaTime) / keyFrameLength;
135 
136 	if (numOfBonesInAnim <= 1) {
137 		return false;
138 	}
139 
140 	int16 boneIdx = 1;
141 	int16 tmpNumOfPoints = MIN<int16>(lastKeyFramePtr->boneframes.size() - 1, numOfBonesInAnim - 1);
142 	do {
143 		BoneFrame *boneState = bodyData.getBoneState(boneIdx);
144 		const BoneFrame &boneFrame = keyFrame->boneframes[boneIdx];
145 		const BoneFrame &lastBoneFrame = lastKeyFramePtr->boneframes[boneIdx];
146 
147 		boneState->type = boneFrame.type;
148 		switch (boneFrame.type) {
149 		case 0:
150 			boneState->x = applyAnimStepRotation(deltaTime, keyFrameLength, boneFrame.x, lastBoneFrame.x);
151 			boneState->y = applyAnimStepRotation(deltaTime, keyFrameLength, boneFrame.y, lastBoneFrame.y);
152 			boneState->z = applyAnimStepRotation(deltaTime, keyFrameLength, boneFrame.z, lastBoneFrame.z);
153 			break;
154 		case 1:
155 		case 2:
156 			boneState->x = applyAnimStepTranslation(deltaTime, keyFrameLength, boneFrame.x, lastBoneFrame.x);
157 			boneState->y = applyAnimStepTranslation(deltaTime, keyFrameLength, boneFrame.y, lastBoneFrame.y);
158 			boneState->z = applyAnimStepTranslation(deltaTime, keyFrameLength, boneFrame.z, lastBoneFrame.z);
159 			break;
160 		default:
161 			error("Unsupported animation rotation mode %d", boneFrame.type);
162 		}
163 
164 		++boneIdx;
165 	} while (--tmpNumOfPoints);
166 
167 	return false;
168 }
169 
setAnimAtKeyframe(int32 keyframeIdx,const AnimData & animData,BodyData & bodyData,AnimTimerDataStruct * animTimerDataPtr)170 void Animations::setAnimAtKeyframe(int32 keyframeIdx, const AnimData &animData, BodyData &bodyData, AnimTimerDataStruct *animTimerDataPtr) {
171 	if (!bodyData.isAnimated()) {
172 		return;
173 	}
174 
175 	const int32 numOfKeyframeInAnim = animData.getKeyframes().size();
176 	if (keyframeIdx < 0 || keyframeIdx >= numOfKeyframeInAnim) {
177 		return;
178 	}
179 
180 	const KeyFrame *keyFrame = animData.getKeyframe(keyframeIdx);
181 
182 	_currentStep.x = keyFrame->x;
183 	_currentStep.y = keyFrame->y;
184 	_currentStep.z = keyFrame->z;
185 
186 	_processRotationByAnim = keyFrame->boneframes[0].type;
187 	_processLastRotationAngle = ToAngle(keyFrame->boneframes[0].y);
188 
189 	animTimerDataPtr->ptr = animData.getKeyframe(keyframeIdx);
190 	animTimerDataPtr->time = _engine->_lbaTime;
191 
192 	const int16 numBones = bodyData.getNumBones();
193 
194 	int16 numOfBonesInAnim = animData.getNumBoneframes();
195 	if (numOfBonesInAnim > numBones) {
196 		numOfBonesInAnim = numBones;
197 	}
198 
199 	copyKeyFrameToState(keyFrame, bodyData, numOfBonesInAnim);
200 }
201 
stockAnimation(const BodyData & bodyData,AnimTimerDataStruct * animTimerDataPtr)202 void Animations::stockAnimation(const BodyData &bodyData, AnimTimerDataStruct *animTimerDataPtr) {
203 	if (!bodyData.isAnimated()) {
204 		return;
205 	}
206 
207 	if (_animKeyframeBufIdx >= ARRAYSIZE(_animKeyframeBuf)) {
208 		_animKeyframeBufIdx = 0;
209 	}
210 	animTimerDataPtr->time = _engine->_lbaTime;
211 	KeyFrame *keyframe = &_animKeyframeBuf[_animKeyframeBufIdx++];
212 	animTimerDataPtr->ptr = keyframe;
213 	copyStateToKeyFrame(keyframe, bodyData);
214 }
215 
copyStateToKeyFrame(KeyFrame * keyframe,const BodyData & bodyData) const216 void Animations::copyStateToKeyFrame(KeyFrame *keyframe, const BodyData &bodyData) const {
217 	const int32 numBones = bodyData.getNumBones();
218 	keyframe->boneframes.clear();
219 	keyframe->boneframes.reserve(numBones);
220 	for (int32 i = 0; i < numBones; ++i) {
221 		const BoneFrame *boneState = bodyData.getBoneState(i);
222 		keyframe->boneframes.push_back(*boneState);
223 	}
224 }
225 
copyKeyFrameToState(const KeyFrame * keyframe,BodyData & bodyData,int32 numBones) const226 void Animations::copyKeyFrameToState(const KeyFrame *keyframe, BodyData &bodyData, int32 numBones) const {
227 	for (int32 i = 0; i < numBones; ++i) {
228 		BoneFrame *boneState = bodyData.getBoneState(i);
229 		*boneState = keyframe->boneframes[i];
230 	}
231 }
232 
verifyAnimAtKeyframe(int32 keyframeIdx,const AnimData & animData,AnimTimerDataStruct * animTimerDataPtr)233 bool Animations::verifyAnimAtKeyframe(int32 keyframeIdx, const AnimData &animData, AnimTimerDataStruct *animTimerDataPtr) {
234 	const KeyFrame *keyFrame = animData.getKeyframe(keyframeIdx);
235 	const int32 keyFrameLength = keyFrame->length;
236 
237 	int32 remainingFrameTime = animTimerDataPtr->time;
238 	if (animTimerDataPtr->ptr == nullptr) {
239 		remainingFrameTime = keyFrameLength;
240 	}
241 
242 	const int32 deltaTime = _engine->_lbaTime - remainingFrameTime;
243 
244 	_currentStep.x = keyFrame->x;
245 	_currentStep.y = keyFrame->y;
246 	_currentStep.z = keyFrame->z;
247 
248 	const BoneFrame &boneFrame = keyFrame->boneframes[0];
249 	_processRotationByAnim = boneFrame.type;
250 	_processLastRotationAngle = ToAngle(boneFrame.y);
251 
252 	if (deltaTime >= keyFrameLength) {
253 		animTimerDataPtr->ptr = animData.getKeyframe(keyframeIdx);
254 		animTimerDataPtr->time = _engine->_lbaTime;
255 		return true;
256 	}
257 
258 	_processLastRotationAngle = (_processLastRotationAngle * deltaTime) / keyFrameLength;
259 	_currentStep.x = (_currentStep.x * deltaTime) / keyFrameLength;
260 	_currentStep.y = (_currentStep.y * deltaTime) / keyFrameLength;
261 	_currentStep.z = (_currentStep.z * deltaTime) / keyFrameLength;
262 
263 	return false;
264 }
265 
processAnimActions(int32 actorIdx)266 void Animations::processAnimActions(int32 actorIdx) {
267 	ActorStruct *actor = _engine->_scene->getActor(actorIdx);
268 	if (actor->_entityDataPtr == nullptr || actor->_animExtraPtr == AnimationTypes::kAnimNone) {
269 		return;
270 	}
271 
272 	const Common::Array<EntityAnim::Action> *actions = actor->_entityDataPtr->getActions(actor->_animExtraPtr);
273 	if (actions == nullptr) {
274 		return;
275 	}
276 	for (const EntityAnim::Action &action : *actions) {
277 		switch (action.type) {
278 		case ActionType::ACTION_HITTING:
279 			if (action.animFrame - 1 == actor->_animPosition) {
280 				actor->_strengthOfHit = action.strength;
281 				actor->_dynamicFlags.bIsHitting = 1;
282 			}
283 			break;
284 		case ActionType::ACTION_SAMPLE:
285 		case ActionType::ACTION_SAMPLE_FREQ:
286 			if (action.animFrame == actor->_animPosition) {
287 				_engine->_sound->playSample(action.sampleIndex, 1, actor->pos(), actorIdx);
288 			}
289 			break;
290 		case ActionType::ACTION_THROW_EXTRA_BONUS:
291 			if (action.animFrame == actor->_animPosition) {
292 				_engine->_extra->addExtraThrow(actorIdx, actor->_pos.x, actor->_pos.y + action.yHeight, actor->_pos.z, action.spriteIndex, action.xAngle, action.yAngle, action.xRotPoint, action.extraAngle, action.strength);
293 			}
294 			break;
295 		case ActionType::ACTION_THROW_MAGIC_BALL:
296 			if (_engine->_gameState->_magicBallIdx == -1 && action.animFrame == actor->_animPosition) {
297 				_engine->_extra->addExtraThrowMagicball(actor->_pos.x, actor->_pos.y + action.yHeight, actor->_pos.z, action.xAngle, actor->_angle + action.yAngle, action.xRotPoint, action.extraAngle);
298 			}
299 			break;
300 		case ActionType::ACTION_SAMPLE_REPEAT:
301 			if (action.animFrame == actor->_animPosition) {
302 				_engine->_sound->playSample(action.sampleIndex, action.repeat, actor->pos(), actorIdx);
303 			}
304 			break;
305 		case ActionType::ACTION_THROW_SEARCH:
306 			if (action.animFrame == actor->_animPosition) {
307 				_engine->_extra->addExtraAiming(actorIdx, actor->_pos.x, actor->_pos.y + action.yHeight, actor->_pos.z, action.spriteIndex, action.targetActor, action.finalAngle, action.strength);
308 			}
309 			break;
310 		case ActionType::ACTION_THROW_ALPHA:
311 			if (action.animFrame == actor->_animPosition) {
312 				_engine->_extra->addExtraThrow(actorIdx, actor->_pos.x, actor->_pos.y + action.yHeight, actor->_pos.z, action.spriteIndex, action.xAngle, actor->_angle + action.yAngle, action.xRotPoint, action.extraAngle, action.strength);
313 			}
314 			break;
315 		case ActionType::ACTION_SAMPLE_STOP:
316 			if (action.animFrame == actor->_animPosition) {
317 				_engine->_sound->stopSample(action.sampleIndex);
318 			}
319 			break;
320 		case ActionType::ACTION_LEFT_STEP:
321 			if (action.animFrame == actor->_animPosition && (actor->_brickSound & 0xF0U) != 0xF0U) {
322 				const int16 sampleIdx = (actor->_brickSound & 0x0FU) + Samples::WalkFloorBegin;
323 				_engine->_sound->playSample(sampleIdx, 1, actor->pos(), actorIdx);
324 			}
325 			break;
326 		case ActionType::ACTION_RIGHT_STEP:
327 			if (action.animFrame == actor->_animPosition && (actor->_brickSound & 0xF0U) != 0xF0U) {
328 				const int16 sampleIdx = (actor->_brickSound & 0x0FU) + Samples::WalkFloorRightBegin;
329 				_engine->_sound->playSample(sampleIdx, 1, actor->pos(), actorIdx);
330 			}
331 			break;
332 		case ActionType::ACTION_HERO_HITTING:
333 			if (action.animFrame - 1 == actor->_animPosition) {
334 				actor->_strengthOfHit = magicLevelStrengthOfHit[_engine->_gameState->_magicLevelIdx];
335 				actor->_dynamicFlags.bIsHitting = 1;
336 			}
337 			break;
338 		case ActionType::ACTION_THROW_3D:
339 			if (action.animFrame == actor->_animPosition) {
340 				const IVec3 &destPos = _engine->_movements->rotateActor(action.distanceX, action.distanceZ, actor->_angle);
341 
342 				const int32 throwX = destPos.x + actor->_pos.x;
343 				const int32 throwY = action.distanceY + actor->_pos.y;
344 				const int32 throwZ = destPos.z + actor->_pos.z;
345 
346 				_engine->_extra->addExtraThrow(actorIdx, throwX, throwY, throwZ, action.spriteIndex,
347 				                               action.xAngle, action.yAngle + actor->_angle, action.xRotPoint, action.extraAngle, action.strength);
348 			}
349 			break;
350 		case ActionType::ACTION_THROW_3D_ALPHA:
351 			if (action.animFrame == actor->_animPosition) {
352 				const int32 distance = getDistance2D(actor->pos(), _engine->_scene->_sceneHero->pos());
353 				const int32 newAngle = _engine->_movements->getAngleAndSetTargetActorDistance(actor->_pos.y, 0, _engine->_scene->_sceneHero->_pos.y, distance);
354 
355 				const IVec3 &destPos = _engine->_movements->rotateActor(action.distanceX, action.distanceZ, actor->_angle);
356 
357 				const int32 throwX = destPos.x + actor->_pos.x;
358 				const int32 throwY = action.distanceY + actor->_pos.y;
359 				const int32 throwZ = destPos.z + actor->_pos.z;
360 
361 				_engine->_extra->addExtraThrow(actorIdx, throwX, throwY, throwZ, action.spriteIndex,
362 				                               action.xAngle + newAngle, action.yAngle + actor->_angle, action.xRotPoint, action.extraAngle, action.strength);
363 			}
364 			break;
365 		case ActionType::ACTION_THROW_3D_SEARCH:
366 			if (action.animFrame == actor->_animPosition) {
367 				const IVec3 &destPos = _engine->_movements->rotateActor(action.distanceX, action.distanceZ, actor->_angle);
368 				const int32 x = actor->_pos.x + destPos.x;
369 				const int32 y = actor->_pos.y + action.distanceY;
370 				const int32 z = actor->_pos.z + destPos.z;
371 				_engine->_extra->addExtraAiming(actorIdx, x, y, z, action.spriteIndex,
372 				                                action.targetActor, action.finalAngle, action.strength);
373 			}
374 			break;
375 		case ActionType::ACTION_THROW_3D_MAGIC:
376 			if (_engine->_gameState->_magicBallIdx == -1 && action.animFrame == actor->_animPosition) {
377 				const IVec3 &destPos = _engine->_movements->rotateActor(action.distanceX, action.distanceZ, actor->_angle);
378 				const int32 x = actor->_pos.x + destPos.x;
379 				const int32 y = actor->_pos.y + action.distanceY;
380 				const int32 z = actor->_pos.z + destPos.z;
381 				_engine->_extra->addExtraThrowMagicball(x, y, z, action.xAngle, actor->_angle, action.yAngle, action.finalAngle);
382 			}
383 			break;
384 		case ActionType::ACTION_ZV:
385 		default:
386 			break;
387 		}
388 	}
389 }
390 
initAnim(AnimationTypes newAnim,AnimType animType,AnimationTypes animExtra,int32 actorIdx)391 bool Animations::initAnim(AnimationTypes newAnim, AnimType animType, AnimationTypes animExtra, int32 actorIdx) {
392 	ActorStruct *actor = _engine->_scene->getActor(actorIdx);
393 	if (actor->_entity == -1) {
394 		return false;
395 	}
396 
397 	if (actor->_staticFlags.bIsSpriteActor) {
398 		return false;
399 	}
400 
401 	if (newAnim == actor->_anim && actor->_previousAnimIdx != -1) {
402 		return true;
403 	}
404 
405 	if (animExtra == AnimationTypes::kAnimInvalid && actor->_animType != AnimType::kAnimationAllThen) {
406 		animExtra = actor->_anim;
407 	}
408 
409 	int32 animIndex = getBodyAnimIndex(newAnim, actorIdx);
410 
411 	if (animIndex == -1) {
412 		animIndex = getBodyAnimIndex(AnimationTypes::kStanding, actorIdx);
413 	}
414 
415 	if (animType != AnimType::kAnimationSet && actor->_animType == AnimType::kAnimationAllThen) {
416 		actor->_animExtra = newAnim;
417 		return false;
418 	}
419 
420 	if (animType == AnimType::kAnimationInsert) {
421 		animType = AnimType::kAnimationAllThen;
422 
423 		animExtra = actor->_anim;
424 
425 		if (animExtra == AnimationTypes::kThrowBall || animExtra == AnimationTypes::kFall || animExtra == AnimationTypes::kLanding || animExtra == AnimationTypes::kLandingHit) {
426 			animExtra = AnimationTypes::kStanding;
427 		}
428 	}
429 
430 	if (animType == AnimType::kAnimationSet) {
431 		animType = AnimType::kAnimationAllThen;
432 	}
433 
434 	if (actor->_previousAnimIdx == -1) {
435 		// if no previous animation
436 		setAnimAtKeyframe(0, _engine->_resources->_animData[animIndex], _engine->_resources->_bodyData[actor->_entity], &actor->_animTimerData);
437 	} else {
438 		// interpolation between animations
439 		stockAnimation(_engine->_resources->_bodyData[actor->_entity], &actor->_animTimerData);
440 	}
441 
442 	actor->_previousAnimIdx = animIndex;
443 	actor->_anim = newAnim;
444 	actor->_animExtra = animExtra;
445 	actor->_animExtraPtr = _currentActorAnimExtraPtr;
446 	actor->_animType = animType;
447 	actor->_animPosition = 0;
448 	actor->_dynamicFlags.bIsHitting = 0;
449 	actor->_dynamicFlags.bAnimEnded = 0;
450 	actor->_dynamicFlags.bAnimFrameReached = 1;
451 
452 	processAnimActions(actorIdx);
453 
454 	actor->_lastRotationAngle = ANGLE_0;
455 	actor->_lastPos = IVec3();
456 
457 	return true;
458 }
459 
processActorAnimations(int32 actorIdx)460 void Animations::processActorAnimations(int32 actorIdx) {
461 	ActorStruct *actor = _engine->_scene->getActor(actorIdx);
462 
463 	_currentlyProcessedActorIdx = actorIdx;
464 	_engine->_actor->_processActorPtr = actor;
465 
466 	if (actor->_entity == -1) {
467 		return;
468 	}
469 
470 	IVec3 &previousActor = _engine->_movements->_previousActor;
471 	previousActor = actor->_collisionPos;
472 
473 	IVec3 &processActor = _engine->_movements->_processActor;
474 	if (actor->_staticFlags.bIsSpriteActor) {
475 		if (actor->_strengthOfHit) {
476 			actor->_dynamicFlags.bIsHitting = 1;
477 		}
478 
479 		processActor = actor->pos();
480 
481 		if (!actor->_dynamicFlags.bIsFalling) {
482 			if (actor->_speed) {
483 				int32 xAxisRotation = actor->_move.getRealValue(_engine->_lbaTime);
484 				if (!xAxisRotation) {
485 					if (actor->_move.to > 0) {
486 						xAxisRotation = 1;
487 					} else {
488 						xAxisRotation = -1;
489 					}
490 				}
491 
492 				IVec3 destPos = _engine->_movements->rotateActor(xAxisRotation, 0, actor->_spriteActorRotation);
493 
494 				processActor.y = actor->_pos.y - destPos.z;
495 
496 				destPos = _engine->_movements->rotateActor(0, destPos.x, actor->_angle);
497 
498 				processActor.x = actor->_pos.x + destPos.x;
499 				processActor.z = actor->_pos.z + destPos.z;
500 
501 				_engine->_movements->setActorAngle(ANGLE_0, actor->_speed, ANGLE_17, &actor->_move);
502 
503 				if (actor->_dynamicFlags.bIsSpriteMoving) {
504 					if (actor->_doorStatus) { // open door
505 						if (getDistance2D(processActor.x, processActor.z, actor->_lastPos.x, actor->_lastPos.z) >= actor->_doorStatus) {
506 							if (actor->_angle == ANGLE_0) {
507 								processActor.z = actor->_lastPos.z + actor->_doorStatus;
508 							} else if (actor->_angle == ANGLE_90) {
509 								processActor.x = actor->_lastPos.x + actor->_doorStatus;
510 							} else if (actor->_angle == ANGLE_180) {
511 								processActor.z = actor->_lastPos.z - actor->_doorStatus;
512 							} else if (actor->_angle == ANGLE_270) {
513 								processActor.x = actor->_lastPos.x - actor->_doorStatus;
514 							}
515 
516 							actor->_dynamicFlags.bIsSpriteMoving = 0;
517 							actor->_speed = 0;
518 						}
519 					} else { // close door
520 						bool updatePos = false;
521 
522 						if (actor->_angle == ANGLE_0) {
523 							if (processActor.z <= actor->_lastPos.z) {
524 								updatePos = true;
525 							}
526 						} else if (actor->_angle == ANGLE_90) {
527 							if (processActor.x <= actor->_lastPos.x) {
528 								updatePos = true;
529 							}
530 						} else if (actor->_angle == ANGLE_180) {
531 							if (processActor.z >= actor->_lastPos.z) {
532 								updatePos = true;
533 							}
534 						} else if (actor->_angle == ANGLE_270) {
535 							if (processActor.x >= actor->_lastPos.x) {
536 								updatePos = true;
537 							}
538 						}
539 
540 						if (updatePos) {
541 							processActor = actor->_lastPos;
542 
543 							actor->_dynamicFlags.bIsSpriteMoving = 0;
544 							actor->_speed = 0;
545 						}
546 					}
547 				}
548 			}
549 
550 			if (actor->_staticFlags.bCanBePushed) {
551 				processActor += actor->_lastPos;
552 
553 				if (actor->_staticFlags.bUseMiniZv) {
554 					processActor.x = ((processActor.x / 128) * 128);
555 					processActor.z = ((processActor.z / 128) * 128);
556 				}
557 
558 				actor->_lastPos = IVec3();
559 			}
560 		}
561 	} else { // 3D actor
562 		if (actor->_previousAnimIdx != -1) {
563 			const AnimData &animData = _engine->_resources->_animData[actor->_previousAnimIdx];
564 
565 			bool keyFramePassed = false;
566 			if (_engine->_resources->_bodyData[actor->_entity].isAnimated()) {
567 				keyFramePassed = verifyAnimAtKeyframe(actor->_animPosition, animData, &actor->_animTimerData);
568 			}
569 
570 			if (_processRotationByAnim) {
571 				actor->_dynamicFlags.bIsRotationByAnim = 1;
572 			} else {
573 				actor->_dynamicFlags.bIsRotationByAnim = 0;
574 			}
575 
576 			actor->_angle = ClampAngle(actor->_angle + _processLastRotationAngle - actor->_lastRotationAngle);
577 			actor->_lastRotationAngle = _processLastRotationAngle;
578 
579 			const IVec3 &destPos = _engine->_movements->rotateActor(_currentStep.x, _currentStep.z, actor->_angle);
580 
581 			_currentStep.x = destPos.x;
582 			_currentStep.z = destPos.z;
583 
584 			processActor = actor->pos() + _currentStep - actor->_lastPos;
585 
586 			actor->_lastPos = _currentStep;
587 
588 			actor->_dynamicFlags.bAnimEnded = 0;
589 			actor->_dynamicFlags.bAnimFrameReached = 0;
590 
591 			if (keyFramePassed) {
592 				actor->_animPosition++;
593 
594 				// if actor have animation actions to process
595 				processAnimActions(actorIdx);
596 
597 				int16 numKeyframe = actor->_animPosition;
598 				if (numKeyframe == (int16)animData.getNumKeyframes()) {
599 					actor->_dynamicFlags.bIsHitting = 0;
600 
601 					if (actor->_animType == AnimType::kAnimationTypeLoop) {
602 						actor->_animPosition = animData.getLoopFrame();
603 					} else {
604 						actor->_anim = actor->_animExtra;
605 						actor->_previousAnimIdx = getBodyAnimIndex(actor->_anim, actorIdx);
606 
607 						if (actor->_previousAnimIdx == -1) {
608 							actor->_previousAnimIdx = getBodyAnimIndex(AnimationTypes::kStanding, actorIdx);
609 							actor->_anim = AnimationTypes::kStanding;
610 						}
611 
612 						actor->_animExtraPtr = _currentActorAnimExtraPtr;
613 
614 						actor->_animType = AnimType::kAnimationTypeLoop;
615 						actor->_animPosition = 0;
616 						actor->_strengthOfHit = 0;
617 					}
618 
619 					processAnimActions(actorIdx);
620 
621 					actor->_dynamicFlags.bAnimEnded = 1;
622 				}
623 
624 				actor->_lastRotationAngle = ANGLE_0;
625 
626 				actor->_lastPos = IVec3();
627 			}
628 		}
629 	}
630 
631 	// actor standing on another actor
632 	if (actor->_standOn != -1) {
633 		const ActorStruct *standOnActor = _engine->_scene->getActor(actor->_standOn);
634 		processActor -= standOnActor->_collisionPos;
635 		processActor += standOnActor->pos();
636 
637 		if (!_engine->_collision->standingOnActor(actorIdx, actor->_standOn)) {
638 			actor->_standOn = -1; // no longer standing on other actor
639 		}
640 	}
641 
642 	// actor falling Y speed
643 	if (actor->_dynamicFlags.bIsFalling) {
644 		processActor = previousActor;
645 		processActor.y += _engine->_loopActorStep; // add step to fall
646 	}
647 
648 	// actor collisions with bricks
649 	if (actor->_staticFlags.bComputeCollisionWithBricks) {
650 		_engine->_collision->_collision.y = 0;
651 
652 		ShapeType brickShape = _engine->_grid->getBrickShape(previousActor);
653 
654 		if (brickShape != ShapeType::kNone) {
655 			if (brickShape != ShapeType::kSolid) {
656 				_engine->_collision->reajustActorPosition(brickShape);
657 			} /*else { // this shouldn't happen (collision should avoid it)
658 				actor->y = processActor.y = (processActor.y / BRICK_HEIGHT) * BRICK_HEIGHT + BRICK_HEIGHT; // go upper
659 			}*/
660 		}
661 
662 		if (actor->_staticFlags.bComputeCollisionWithObj) {
663 			_engine->_collision->checkCollisionWithActors(actorIdx);
664 		}
665 
666 		if (actor->_standOn != -1 && actor->_dynamicFlags.bIsFalling) {
667 			_engine->_collision->stopFalling();
668 		}
669 
670 		_engine->_collision->_causeActorDamage = 0;
671 
672 		_engine->_collision->_processCollision = processActor;
673 
674 		if (IS_HERO(actorIdx) && !actor->_staticFlags.bComputeLowCollision) {
675 			// check hero collisions with bricks
676 			_engine->_collision->checkHeroCollisionWithBricks(actor->_boudingBox.mins.x, actor->_boudingBox.mins.y, actor->_boudingBox.mins.z, 1);
677 			_engine->_collision->checkHeroCollisionWithBricks(actor->_boudingBox.maxs.x, actor->_boudingBox.mins.y, actor->_boudingBox.mins.z, 2);
678 			_engine->_collision->checkHeroCollisionWithBricks(actor->_boudingBox.maxs.x, actor->_boudingBox.mins.y, actor->_boudingBox.maxs.z, 4);
679 			_engine->_collision->checkHeroCollisionWithBricks(actor->_boudingBox.mins.x, actor->_boudingBox.mins.y, actor->_boudingBox.maxs.z, 8);
680 		} else {
681 			// check other actors collisions with bricks
682 			_engine->_collision->checkActorCollisionWithBricks(actor->_boudingBox.mins.x, actor->_boudingBox.mins.y, actor->_boudingBox.mins.z, 1);
683 			_engine->_collision->checkActorCollisionWithBricks(actor->_boudingBox.maxs.x, actor->_boudingBox.mins.y, actor->_boudingBox.mins.z, 2);
684 			_engine->_collision->checkActorCollisionWithBricks(actor->_boudingBox.maxs.x, actor->_boudingBox.mins.y, actor->_boudingBox.maxs.z, 4);
685 			_engine->_collision->checkActorCollisionWithBricks(actor->_boudingBox.mins.x, actor->_boudingBox.mins.y, actor->_boudingBox.maxs.z, 8);
686 		}
687 
688 		// process wall hit while running
689 		if (_engine->_collision->_causeActorDamage && !actor->_dynamicFlags.bIsFalling && !_currentlyProcessedActorIdx && _engine->_actor->_heroBehaviour == HeroBehaviourType::kAthletic && actor->_anim == AnimationTypes::kForward) {
690 			IVec3 destPos = _engine->_movements->rotateActor(actor->_boudingBox.mins.x, actor->_boudingBox.mins.z, actor->_angle + ANGLE_360 + ANGLE_135);
691 
692 			destPos.x += processActor.x;
693 			destPos.z += processActor.z;
694 
695 			if (destPos.x >= 0 && destPos.z >= 0 && destPos.x <= 0x7E00 && destPos.z <= 0x7E00) { // SCENE_SIZE_MAX
696 				if (_engine->_grid->getBrickShape(destPos.x, processActor.y + BRICK_HEIGHT, destPos.z) != ShapeType::kNone && _engine->_cfgfile.WallCollision) { // avoid wall hit damage
697 					_engine->_extra->addExtraSpecial(actor->_pos.x, actor->_pos.y + 1000, actor->_pos.z, ExtraSpecialType::kHitStars);
698 					initAnim(AnimationTypes::kBigHit, AnimType::kAnimationAllThen, AnimationTypes::kStanding, _currentlyProcessedActorIdx);
699 
700 					if (IS_HERO(_currentlyProcessedActorIdx)) {
701 						_engine->_movements->_heroMoved = true;
702 					}
703 
704 					actor->addLife(-1);
705 				}
706 			}
707 		}
708 
709 		brickShape = _engine->_grid->getBrickShape(processActor);
710 		actor->setBrickShape(brickShape);
711 
712 		if (brickShape != ShapeType::kNone) {
713 			if (brickShape == ShapeType::kSolid) {
714 				if (actor->_dynamicFlags.bIsFalling) {
715 					_engine->_collision->stopFalling();
716 					processActor.y = (_engine->_collision->_collision.y * BRICK_HEIGHT) + BRICK_HEIGHT;
717 				} else {
718 					if (IS_HERO(actorIdx) && _engine->_actor->_heroBehaviour == HeroBehaviourType::kAthletic && actor->_anim == AnimationTypes::kForward && _engine->_cfgfile.WallCollision) { // avoid wall hit damage
719 						_engine->_extra->addExtraSpecial(actor->_pos.x, actor->_pos.y + 1000, actor->_pos.z, ExtraSpecialType::kHitStars);
720 						initAnim(AnimationTypes::kBigHit, AnimType::kAnimationAllThen, AnimationTypes::kStanding, _currentlyProcessedActorIdx);
721 						_engine->_movements->_heroMoved = true;
722 						actor->addLife(-1);
723 					}
724 
725 					// no Z coordinate issue
726 					if (_engine->_grid->getBrickShape(processActor.x, processActor.y, previousActor.z) == ShapeType::kNone) {
727 						processActor.z = previousActor.z;
728 					}
729 
730 					// no X coordinate issue
731 					if (_engine->_grid->getBrickShape(previousActor.x, processActor.y, processActor.z) == ShapeType::kNone) {
732 						processActor.x = previousActor.x;
733 					}
734 
735 					// X and Z with issue, no move
736 					if (_engine->_grid->getBrickShape(processActor.x, processActor.y, previousActor.z) != ShapeType::kNone &&
737 					    _engine->_grid->getBrickShape(previousActor.x, processActor.y, processActor.z) != ShapeType::kNone) {
738 						return;
739 					}
740 				}
741 			} else {
742 				if (actor->_dynamicFlags.bIsFalling) {
743 					_engine->_collision->stopFalling();
744 				}
745 
746 				_engine->_collision->reajustActorPosition(brickShape);
747 			}
748 
749 			actor->_dynamicFlags.bIsFalling = 0;
750 		} else {
751 			if (actor->_staticFlags.bCanFall && actor->_standOn == -1) {
752 				brickShape = _engine->_grid->getBrickShape(processActor.x, processActor.y - 1, processActor.z);
753 
754 				if (brickShape != ShapeType::kNone) {
755 					if (actor->_dynamicFlags.bIsFalling) {
756 						_engine->_collision->stopFalling();
757 					}
758 
759 					_engine->_collision->reajustActorPosition(brickShape);
760 				} else {
761 					if (!actor->_dynamicFlags.bIsRotationByAnim) {
762 						actor->_dynamicFlags.bIsFalling = 1;
763 
764 						if (IS_HERO(actorIdx) && _engine->_scene->_heroYBeforeFall == 0) {
765 							_engine->_scene->_heroYBeforeFall = processActor.y;
766 						}
767 
768 						initAnim(AnimationTypes::kFall, AnimType::kAnimationTypeLoop, AnimationTypes::kAnimInvalid, actorIdx);
769 					}
770 				}
771 			}
772 		}
773 
774 		// if under the map, than die
775 		if (_engine->_collision->_collision.y == -1) {
776 			actor->setLife(0);
777 		}
778 	} else {
779 		if (actor->_staticFlags.bComputeCollisionWithObj) {
780 			_engine->_collision->checkCollisionWithActors(actorIdx);
781 		}
782 	}
783 
784 	if (_engine->_collision->_causeActorDamage) {
785 		actor->setBrickCausesDamage();
786 	}
787 
788 	// check and fix actor bounding position
789 	if (processActor.x < 0) {
790 		processActor.x = 0;
791 	}
792 
793 	if (processActor.y < 0) {
794 		processActor.y = 0;
795 	}
796 
797 	if (processActor.z < 0) {
798 		processActor.z = 0;
799 	}
800 
801 	if (processActor.x > 0x7E00) { // SCENE_SIZE_MAX
802 		processActor.x = 0x7E00;
803 	}
804 
805 	if (processActor.z > 0x7E00) { // SCENE_SIZE_MAX
806 		processActor.z = 0x7E00;
807 	}
808 
809 	actor->_pos = processActor;
810 }
811 
812 } // namespace TwinE
813