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/movements.h"
24 #include "common/textconsole.h"
25 #include "twine/input.h"
26 #include "twine/renderer/renderer.h"
27 #include "twine/renderer/shadeangletab.h"
28 #include "twine/scene/actor.h"
29 #include "twine/scene/animations.h"
30 #include "twine/scene/collision.h"
31 #include "twine/scene/gamestate.h"
32 #include "twine/scene/grid.h"
33 #include "twine/scene/scene.h"
34 #include "twine/text.h"
35 #include "twine/twine.h"
36
37 namespace TwinE {
38
Movements(TwinEEngine * engine)39 Movements::Movements(TwinEEngine *engine) : _engine(engine) {}
40
getShadowPosition(const IVec3 & pos)41 void Movements::getShadowPosition(const IVec3 &pos) {
42 const uint8 *ptr = _engine->_grid->getBlockBufferGround(pos, _processActor.y);
43 _processActor.x = pos.x;
44 _processActor.z = pos.z;
45
46 ShapeType shadowCollisionType;
47 const int32 blockIdx = *ptr;
48 if (blockIdx) {
49 const int32 brickIdx = *(ptr + 1);
50 const BlockDataEntry *blockPtr = _engine->_grid->getBlockPointer(blockIdx, brickIdx);
51 shadowCollisionType = (ShapeType)blockPtr->brickShape;
52 } else {
53 shadowCollisionType = ShapeType::kNone;
54 }
55 _engine->_collision->reajustActorPosition(shadowCollisionType);
56
57 _engine->_actor->_shadowCoord = _processActor;
58 }
59
setActorAngleSafe(int16 startAngle,int16 endAngle,int16 stepAngle,ActorMoveStruct * movePtr)60 void Movements::setActorAngleSafe(int16 startAngle, int16 endAngle, int16 stepAngle, ActorMoveStruct *movePtr) {
61 movePtr->from = ClampAngle(startAngle);
62 movePtr->to = ClampAngle(endAngle);
63 movePtr->numOfStep = ClampAngle(stepAngle);
64 movePtr->timeOfChange = _engine->_lbaTime;
65 }
66
clearRealAngle(ActorStruct * actorPtr)67 void Movements::clearRealAngle(ActorStruct *actorPtr) {
68 setActorAngleSafe(actorPtr->_angle, actorPtr->_angle, ANGLE_0, &actorPtr->_move);
69 }
70
setActorAngle(int16 startAngle,int16 endAngle,int16 stepAngle,ActorMoveStruct * movePtr)71 void Movements::setActorAngle(int16 startAngle, int16 endAngle, int16 stepAngle, ActorMoveStruct *movePtr) {
72 movePtr->from = startAngle;
73 movePtr->to = endAngle;
74 movePtr->numOfStep = stepAngle;
75 movePtr->timeOfChange = _engine->_lbaTime;
76 }
77
getAngleAndSetTargetActorDistance(int32 x1,int32 z1,int32 x2,int32 z2)78 int32 Movements::getAngleAndSetTargetActorDistance(int32 x1, int32 z1, int32 x2, int32 z2) {
79 /*
80 //Pythagoras
81 targetActorDistance = (int32)sqrt((float)(((z2 - z1)*(z2 - z1) + (x2 - x1)*(x2 - x1))));
82
83 if (targetActorDistance == 0)
84 return 0;
85
86 //given two points, we calculate its arc-tangent in radians
87 //Then we convert from radians (360 degrees == 2*M_PI) to a 10bit value (360 degrees == 1024) and invert the rotation direction
88 //Then we add an offset of 90 degrees (256) and limit it to the 10bit value range.
89 return (256 + ((int32)floor((-1024 * atan2((float)(z2-z1), (int32)(x2-x1))) / (2*M_PI)))) % 1024;
90 */
91
92 int32 difZ = z2 - z1;
93 const int32 newZ = difZ * difZ;
94
95 int32 difX = x2 - x1;
96 const int32 newX = difX * difX;
97
98 bool flag;
99 // Exchange X and Z
100 if (newX < newZ) {
101 const int32 tmpEx = difX;
102 difX = difZ;
103 difZ = tmpEx;
104
105 flag = true;
106 } else {
107 flag = false;
108 }
109
110 _targetActorDistance = (int32)sqrt((float)(newX + newZ));
111
112 if (!_targetActorDistance) {
113 return 0;
114 }
115
116 const int32 destAngle = (difZ * SCENE_SIZE_HALF) / _targetActorDistance;
117
118 int32 startAngle = ANGLE_0;
119 // stopAngle = ANGLE_90;
120 const int16 *shadeAngleTab3(&shadeAngleTable[ANGLE_135]);
121 while (shadeAngleTab3[startAngle] > destAngle) {
122 startAngle++;
123 }
124
125 if (shadeAngleTab3[startAngle] != destAngle) {
126 if ((shadeAngleTab3[startAngle - 1] + shadeAngleTab3[startAngle]) / 2 <= destAngle) {
127 startAngle--;
128 }
129 }
130
131 int32 finalAngle = ANGLE_45 + startAngle;
132
133 if (difX <= 0) {
134 finalAngle = -finalAngle;
135 }
136
137 if (flag) {
138 finalAngle = -finalAngle + ANGLE_90;
139 }
140
141 return ClampAngle(finalAngle);
142 }
143
rotateActor(int32 x,int32 z,int32 angle)144 IVec3 Movements::rotateActor(int32 x, int32 z, int32 angle) {
145 const double radians = AngleToRadians(angle);
146 const int32 vx = (int32)(x * cos(radians) + z * sin(radians));
147 const int32 vz = (int32)(-x * sin(radians) + z * cos(radians));
148 return IVec3(vx, 0, vz);
149 }
150
moveActor(int32 angleFrom,int32 angleTo,int32 speed,ActorMoveStruct * movePtr) const151 void Movements::moveActor(int32 angleFrom, int32 angleTo, int32 speed, ActorMoveStruct *movePtr) const { // ManualRealAngle
152 const int16 from = ClampAngle(angleFrom);
153 const int16 to = ClampAngle(angleTo);
154
155 movePtr->from = from;
156 movePtr->to = to;
157
158 const int16 numOfStep = (from - to) * 64;
159 int32 numOfStepInt = ABS(numOfStep);
160 numOfStepInt /= 64;
161
162 numOfStepInt *= speed;
163 numOfStepInt /= 256;
164
165 movePtr->numOfStep = (int16)numOfStepInt;
166 movePtr->timeOfChange = _engine->_lbaTime;
167 }
168
update(TwinEEngine * engine)169 void Movements::ChangedCursorKeys::update(TwinEEngine *engine) {
170 if (engine->_input->isActionActive(TwinEActionType::TurnLeft)) {
171 leftChange = leftDown == 0;
172 leftDown = 1;
173 } else {
174 leftChange = leftDown;
175 leftDown = 0;
176 }
177
178 if (engine->_input->isActionActive(TwinEActionType::TurnRight)) {
179 rightChange = rightDown == 0;
180 rightDown = 1;
181 } else {
182 rightChange = rightDown;
183 rightDown = 0;
184 }
185
186 if (engine->_input->isActionActive(TwinEActionType::MoveBackward)) {
187 backwardChange = backwardDown == 0;
188 backwardDown = 1;
189 } else {
190 backwardChange = backwardDown;
191 backwardDown = 0;
192 }
193
194 if (engine->_input->isActionActive(TwinEActionType::MoveForward)) {
195 forwardChange = forwardDown == 0;
196 forwardDown = 1;
197 } else {
198 forwardChange = forwardDown;
199 forwardDown = 0;
200 }
201 }
202
update()203 void Movements::update() {
204 _previousChangedCursorKeys = _changedCursorKeys;
205 _previousLoopActionKey = _heroActionKey;
206
207 _heroActionKey = _engine->_input->isHeroActionActive();
208 _changedCursorKeys.update(_engine);
209 }
210
processBehaviourExecution(int actorIdx)211 bool Movements::processBehaviourExecution(int actorIdx) {
212 bool executeAction = false;
213 if (_engine->_input->toggleActionIfActive(TwinEActionType::SpecialAction)) {
214 executeAction = true;
215 }
216 switch (_engine->_actor->_heroBehaviour) {
217 case HeroBehaviourType::kNormal:
218 executeAction = true;
219 break;
220 case HeroBehaviourType::kAthletic:
221 _engine->_animations->initAnim(AnimationTypes::kJump, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
222 break;
223 case HeroBehaviourType::kAggressive:
224 if (_engine->_actor->_autoAggressive) {
225 ActorStruct *actor = _engine->_scene->getActor(actorIdx);
226 _heroMoved = true;
227 actor->_angle = actor->_move.getRealAngle(_engine->_lbaTime);
228 // TODO: previousLoopActionKey must be handled properly
229 if (!_previousLoopActionKey || actor->_anim == AnimationTypes::kStanding) {
230 const int32 aggresiveMode = _engine->getRandomNumber(3);
231
232 switch (aggresiveMode) {
233 case 0:
234 _engine->_animations->initAnim(AnimationTypes::kKick, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
235 break;
236 case 1:
237 _engine->_animations->initAnim(AnimationTypes::kRightPunch, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
238 break;
239 case 2:
240 _engine->_animations->initAnim(AnimationTypes::kLeftPunch, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
241 break;
242 }
243 }
244 } else {
245 if (_engine->_input->isActionActive(TwinEActionType::TurnLeft)) {
246 _engine->_animations->initAnim(AnimationTypes::kLeftPunch, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
247 _heroMoved = true;
248 } else if (_engine->_input->isActionActive(TwinEActionType::TurnRight)) {
249 _engine->_animations->initAnim(AnimationTypes::kRightPunch, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
250 _heroMoved = true;
251 } else if (_engine->_input->isActionActive(TwinEActionType::MoveForward)) {
252 _engine->_animations->initAnim(AnimationTypes::kKick, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
253 _heroMoved = true;
254 }
255 }
256 break;
257 case HeroBehaviourType::kDiscrete:
258 _engine->_animations->initAnim(AnimationTypes::kHide, AnimType::kAnimationTypeLoop, AnimationTypes::kAnimInvalid, actorIdx);
259 break;
260 case HeroBehaviourType::kProtoPack:
261 break;
262 }
263 return executeAction;
264 }
265
processAttackExecution(int actorIdx)266 bool Movements::processAttackExecution(int actorIdx) {
267 ActorStruct *actor = _engine->_scene->getActor(actorIdx);
268 if (!_engine->_gameState->_usingSabre) {
269 // Use Magic Ball
270 if (_engine->_gameState->hasItem(InventoryItems::kiMagicBall)) {
271 if (_engine->_gameState->_magicBallIdx == -1) {
272 _engine->_animations->initAnim(AnimationTypes::kThrowBall, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
273 }
274
275 actor->_angle = actor->_move.getRealAngle(_engine->_lbaTime);
276 return true;
277 }
278 } else if (_engine->_gameState->hasItem(InventoryItems::kiUseSabre)) {
279 if (actor->_body != BodyType::btSabre) {
280 _engine->_actor->initModelActor(BodyType::btSabre, actorIdx);
281 }
282
283 _engine->_animations->initAnim(AnimationTypes::kSabreAttack, AnimType::kAnimationThen, AnimationTypes::kStanding, actorIdx);
284
285 actor->_angle = actor->_move.getRealAngle(_engine->_lbaTime);
286 return true;
287 }
288 return false;
289 }
290
processManualMovementExecution(int actorIdx)291 void Movements::processManualMovementExecution(int actorIdx) {
292 ActorStruct *actor = _engine->_scene->getActor(actorIdx);
293 if (actor->isAttackAnimationActive()) {
294 return;
295 }
296 if (actor->isJumpAnimationActive()) {
297 return;
298 }
299 if (actor->isAttackWeaponAnimationActive()) {
300 return;
301 }
302 if (!_changedCursorKeys || _heroAction) {
303 // if walking should get stopped
304 if (!_engine->_input->isActionActive(TwinEActionType::MoveForward) && !_engine->_input->isActionActive(TwinEActionType::MoveBackward)) {
305 if (_heroMoved && (_heroActionKey != _previousLoopActionKey || _changedCursorKeys != _previousChangedCursorKeys)) {
306 _engine->_animations->initAnim(AnimationTypes::kStanding, AnimType::kAnimationTypeLoop, AnimationTypes::kAnimInvalid, actorIdx);
307 }
308 }
309
310 _heroMoved = false;
311
312 if (_engine->_input->isActionActive(TwinEActionType::MoveForward)) {
313 if (!_engine->_scene->_currentActorInZone) {
314 _engine->_animations->initAnim(AnimationTypes::kForward, AnimType::kAnimationTypeLoop, AnimationTypes::kAnimInvalid, actorIdx);
315 }
316 _heroMoved = true;
317 } else if (_engine->_input->isActionActive(TwinEActionType::MoveBackward)) {
318 _engine->_animations->initAnim(AnimationTypes::kBackward, AnimType::kAnimationTypeLoop, AnimationTypes::kAnimInvalid, actorIdx);
319 _heroMoved = true;
320 }
321
322 if (_engine->_input->isActionActive(TwinEActionType::TurnLeft)) {
323 if (actor->_anim == AnimationTypes::kStanding) {
324 _engine->_animations->initAnim(AnimationTypes::kTurnLeft, AnimType::kAnimationTypeLoop, AnimationTypes::kAnimInvalid, actorIdx);
325 } else {
326 if (!actor->_dynamicFlags.bIsRotationByAnim) {
327 actor->_angle = actor->_move.getRealAngle(_engine->_lbaTime);
328 }
329 }
330 _heroMoved = true;
331 } else if (_engine->_input->isActionActive(TwinEActionType::TurnRight)) {
332 if (actor->_anim == AnimationTypes::kStanding) {
333 _engine->_animations->initAnim(AnimationTypes::kTurnRight, AnimType::kAnimationTypeLoop, AnimationTypes::kAnimInvalid, actorIdx);
334 } else {
335 if (!actor->_dynamicFlags.bIsRotationByAnim) {
336 actor->_angle = actor->_move.getRealAngle(_engine->_lbaTime);
337 }
338 }
339 _heroMoved = true;
340 }
341 }
342 }
343
processManualRotationExecution(int actorIdx)344 void Movements::processManualRotationExecution(int actorIdx) {
345 ActorStruct *actor = _engine->_scene->getActor(actorIdx);
346 if (!_engine->_actor->_autoAggressive && actor->isAttackAnimationActive()) {
347 // it is allowed to rotate in auto aggressive mode - but not in manual mode.
348 return;
349 }
350 if (actor->isJumpAnimationActive()) {
351 return;
352 }
353 int16 tempAngle;
354 if (_engine->_input->isActionActive(TwinEActionType::TurnLeft)) {
355 tempAngle = ANGLE_90;
356 } else if (_engine->_input->isActionActive(TwinEActionType::TurnRight)) {
357 tempAngle = -ANGLE_90;
358 } else {
359 tempAngle = ANGLE_0;
360 }
361
362 moveActor(actor->_angle, actor->_angle + tempAngle, actor->_speed, &actor->_move);
363 }
364
processManualAction(int actorIdx)365 void Movements::processManualAction(int actorIdx) {
366 if (IS_HERO(actorIdx)) {
367 _heroAction = false;
368 if (_engine->_input->isHeroActionActive()) {
369 _heroAction = processBehaviourExecution(actorIdx);
370 }
371 }
372
373 if (_engine->_input->isActionActive(TwinEActionType::ThrowMagicBall) && !_engine->_gameState->inventoryDisabled()) {
374 if (processAttackExecution(actorIdx)) {
375 _heroMoved = true;
376 }
377 }
378
379 processManualMovementExecution(actorIdx);
380 processManualRotationExecution(actorIdx);
381 }
382
processFollowAction(int actorIdx)383 void Movements::processFollowAction(int actorIdx) {
384 ActorStruct *actor = _engine->_scene->getActor(actorIdx);
385 const ActorStruct *followedActor = _engine->_scene->getActor(actor->_followedActor);
386 int32 newAngle = getAngleAndSetTargetActorDistance(actor->pos(), followedActor->pos());
387 if (actor->_staticFlags.bIsSpriteActor) {
388 actor->_angle = newAngle;
389 } else {
390 moveActor(actor->_angle, newAngle, actor->_speed, &actor->_move);
391 }
392 }
393
processRandomAction(int actorIdx)394 void Movements::processRandomAction(int actorIdx) {
395 ActorStruct *actor = _engine->_scene->getActor(actorIdx);
396 if (actor->_dynamicFlags.bIsRotationByAnim) {
397 return;
398 }
399
400 if (actor->brickCausesDamage()) {
401 moveActor(actor->_angle, ClampAngle((_engine->getRandomNumber() & ANGLE_90) + (actor->_angle - ANGLE_90)), actor->_speed, &actor->_move);
402 actor->_delayInMillis = _engine->getRandomNumber(300) + _engine->_lbaTime + 300;
403 _engine->_animations->initAnim(AnimationTypes::kStanding, AnimType::kAnimationTypeLoop, AnimationTypes::kAnimInvalid, actorIdx);
404 }
405
406 if (!actor->_move.numOfStep) {
407 _engine->_animations->initAnim(AnimationTypes::kForward, AnimType::kAnimationTypeLoop, AnimationTypes::kAnimInvalid, actorIdx);
408 if (_engine->_lbaTime > actor->_delayInMillis) {
409 moveActor(actor->_angle, ClampAngle((_engine->getRandomNumber() & ANGLE_90) + (actor->_angle - ANGLE_90)), actor->_speed, &actor->_move);
410 actor->_delayInMillis = _engine->getRandomNumber(300) + _engine->_lbaTime + 300;
411 }
412 }
413 }
414
processTrackAction(int actorIdx)415 void Movements::processTrackAction(int actorIdx) {
416 ActorStruct *actor = _engine->_scene->getActor(actorIdx);
417 if (actor->_positionInMoveScript == -1) {
418 actor->_positionInMoveScript = 0;
419 }
420 }
421
processSameXZAction(int actorIdx)422 void Movements::processSameXZAction(int actorIdx) {
423 ActorStruct *actor = _engine->_scene->getActor(actorIdx);
424 const ActorStruct *followedActor = _engine->_scene->getActor(actor->_followedActor);
425 actor->_pos.x = followedActor->_pos.x;
426 actor->_pos.z = followedActor->_pos.z;
427 }
428
processActorMovements(int32 actorIdx)429 void Movements::processActorMovements(int32 actorIdx) {
430 ActorStruct *actor = _engine->_scene->getActor(actorIdx);
431 if (actor->_entity == -1) {
432 return;
433 }
434
435 if (actor->_dynamicFlags.bIsFalling) {
436 if (actor->_controlMode != ControlMode::kManual) {
437 return;
438 }
439
440 int16 tempAngle = ANGLE_0;
441 if (_engine->_input->isActionActive(TwinEActionType::TurnLeft)) {
442 tempAngle = ANGLE_90;
443 } else if (_engine->_input->isActionActive(TwinEActionType::TurnRight)) {
444 tempAngle = -ANGLE_90;
445 }
446
447 moveActor(actor->_angle, actor->_angle + tempAngle, actor->_speed, &actor->_move);
448 return;
449 }
450 if (!actor->_staticFlags.bIsSpriteActor) {
451 if (actor->_controlMode != ControlMode::kManual) {
452 actor->_angle = actor->_move.getRealAngle(_engine->_lbaTime);
453 }
454 }
455
456 switch (actor->_controlMode) {
457 /**
458 * The Actor's Track Script is stopped. Track Script execution may be started with Life Script of
459 * the Actor or other Actors (with SET_TRACK(_OBJ) command). This mode does not mean the Actor
460 * will literally not move, but rather that it's Track Script (also called Move Script) is
461 * initially stopped. The Actor may move if it is assigned a moving animation.
462 */
463 case ControlMode::kNoMove:
464 case ControlMode::kFollow2: // unused
465 case ControlMode::kTrackAttack: // unused
466 break;
467 case ControlMode::kManual:
468 processManualAction(actorIdx);
469 break;
470 case ControlMode::kFollow:
471 processFollowAction(actorIdx);
472 break;
473 case ControlMode::kTrack:
474 processTrackAction(actorIdx);
475 break;
476 case ControlMode::kSameXZ:
477 // TODO: see lSET_DIRMODE and lSET_DIRMODE_OBJ opcodes
478 processSameXZAction(actorIdx);
479 break;
480 case ControlMode::kRandom:
481 processRandomAction(actorIdx);
482 break;
483 default:
484 warning("Unknown control mode %d", (int)actor->_controlMode);
485 break;
486 }
487 }
488
489 } // namespace TwinE
490