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