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