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 "scumm/scumm.h"
24 #include "scumm/actor.h"
25 #include "scumm/charset.h"
26 #include "scumm/scumm_v7.h"
27 
28 #include "common/util.h"
29 
30 namespace Scumm {
31 
setCameraAtEx(int at)32 void ScummEngine::setCameraAtEx(int at) {
33 	if (_game.version < 7) {
34 		camera._mode = kNormalCameraMode;
35 		camera._cur.x = at;
36 		setCameraAt(at, 0);
37 		camera._movingToActor = false;
38 	}
39 }
40 
setCameraAt(int pos_x,int pos_y)41 void ScummEngine::setCameraAt(int pos_x, int pos_y) {
42 	if (camera._mode != kFollowActorCameraMode || ABS(pos_x - camera._cur.x) > (_screenWidth / 2)) {
43 		camera._cur.x = pos_x;
44 	}
45 	camera._dest.x = pos_x;
46 
47 	if (VAR_CAMERA_MIN_X != 0xFF && camera._cur.x < VAR(VAR_CAMERA_MIN_X))
48 		camera._cur.x = (short) VAR(VAR_CAMERA_MIN_X);
49 
50 	if (VAR_CAMERA_MAX_X != 0xFF && camera._cur.x > VAR(VAR_CAMERA_MAX_X))
51 		camera._cur.x = (short) VAR(VAR_CAMERA_MAX_X);
52 
53 	if (VAR_SCROLL_SCRIPT != 0xFF && VAR(VAR_SCROLL_SCRIPT)) {
54 		VAR(VAR_CAMERA_POS_X) = camera._cur.x;
55 		runScript(VAR(VAR_SCROLL_SCRIPT), 0, 0, 0);
56 	}
57 
58 	// If the camera moved and text is visible, remove it
59 	if (camera._cur.x != camera._last.x && _charset->_hasMask && _game.version > 3)
60 		stopTalk();
61 }
62 
setCameraFollows(Actor * a,bool setCamera)63 void ScummEngine::setCameraFollows(Actor *a, bool setCamera) {
64 
65 	int t, i;
66 
67 	camera._mode = kFollowActorCameraMode;
68 	camera._follows = a->_number;
69 
70 	if (!a->isInCurrentRoom()) {
71 		startScene(a->getRoom(), 0, 0);
72 		camera._mode = kFollowActorCameraMode;
73 		camera._cur.x = a->getPos().x;
74 		setCameraAt(camera._cur.x, 0);
75 	}
76 
77 	t = a->getPos().x / 8 - _screenStartStrip;
78 
79 	if (t < camera._leftTrigger || t > camera._rightTrigger || setCamera == true)
80 		setCameraAt(a->getPos().x, 0);
81 
82 	for (i = 1; i < _numActors; i++) {
83 		if (_actors[i]->isInCurrentRoom())
84 			_actors[i]->_needRedraw = true;
85 	}
86 	runInventoryScript(0);
87 }
88 
clampCameraPos(Common::Point * pt)89 void ScummEngine::clampCameraPos(Common::Point *pt) {
90 	pt->x = CLIP<short>(pt->x, VAR(VAR_CAMERA_MIN_X), VAR(VAR_CAMERA_MAX_X));
91 	pt->y = CLIP<short>(pt->y, VAR(VAR_CAMERA_MIN_Y), VAR(VAR_CAMERA_MAX_Y));
92 }
93 
moveCamera()94 void ScummEngine::moveCamera() {
95 	int pos = camera._cur.x;
96 	int t;
97 	Actor *a = NULL;
98 	const bool snapToX = (_snapScroll || (VAR_CAMERA_FAST_X != 0xFF && VAR(VAR_CAMERA_FAST_X)));
99 
100 	camera._cur.x &= 0xFFF8;
101 
102 	if (VAR_CAMERA_MIN_X != 0xFF && camera._cur.x < VAR(VAR_CAMERA_MIN_X)) {
103 		if (snapToX)
104 			camera._cur.x = (short) VAR(VAR_CAMERA_MIN_X);
105 		else
106 			camera._cur.x += 8;
107 		cameraMoved();
108 		return;
109 	}
110 
111 	if (VAR_CAMERA_MAX_X != 0xFF && camera._cur.x > VAR(VAR_CAMERA_MAX_X)) {
112 		if (snapToX)
113 			camera._cur.x = (short) VAR(VAR_CAMERA_MAX_X);
114 		else
115 			camera._cur.x -= 8;
116 		cameraMoved();
117 		return;
118 	}
119 
120 	if (camera._mode == kFollowActorCameraMode) {
121 		a = derefActor(camera._follows, "moveCamera");
122 
123 		int actorx = a->getPos().x;
124 		t = actorx / 8 - _screenStartStrip;
125 
126 		if (t < camera._leftTrigger || t > camera._rightTrigger) {
127 			if (snapToX) {
128 				if (t > 40-5)
129 					camera._dest.x = actorx + 80;
130 				if (t < 5)
131 					camera._dest.x = actorx - 80;
132 			} else
133 				camera._movingToActor = true;
134 		}
135 	}
136 
137 	if (camera._movingToActor) {
138 		a = derefActor(camera._follows, "moveCamera(2)");
139 		camera._dest.x = a->getPos().x;
140 	}
141 
142 	if (VAR_CAMERA_MIN_X != 0xFF && camera._dest.x < VAR(VAR_CAMERA_MIN_X))
143 		camera._dest.x = (short) VAR(VAR_CAMERA_MIN_X);
144 
145 	if (VAR_CAMERA_MAX_X != 0xFF && camera._dest.x > VAR(VAR_CAMERA_MAX_X))
146 		camera._dest.x = (short) VAR(VAR_CAMERA_MAX_X);
147 
148 	if (snapToX) {
149 		camera._cur.x = camera._dest.x;
150 	} else {
151 		if (camera._cur.x < camera._dest.x)
152 			camera._cur.x += 8;
153 		if (camera._cur.x > camera._dest.x)
154 			camera._cur.x -= 8;
155 	}
156 
157 	/* Actor 'a' is set a bit above */
158 	if (camera._movingToActor && (camera._cur.x / 8) == (a->getPos().x / 8)) {
159 		camera._movingToActor = false;
160 	}
161 
162 	cameraMoved();
163 
164 	if (VAR_SCROLL_SCRIPT != 0xFF && VAR(VAR_SCROLL_SCRIPT) && pos != camera._cur.x) {
165 		VAR(VAR_CAMERA_POS_X) = camera._cur.x;
166 		runScript(VAR(VAR_SCROLL_SCRIPT), 0, 0, 0);
167 	}
168 }
169 
cameraMoved()170 void ScummEngine::cameraMoved() {
171 	int screenLeft;
172 	if (_game.version >= 7) {
173 		assert(camera._cur.x >= (_screenWidth / 2) && camera._cur.y >= (_screenHeight / 2));
174 	} else {
175 		if (camera._cur.x < (_screenWidth / 2)) {
176 			camera._cur.x = (_screenWidth / 2);
177 		} else if (camera._cur.x > _roomWidth - (_screenWidth / 2)) {
178 			camera._cur.x = _roomWidth - (_screenWidth / 2);
179 		}
180 	}
181 
182 	_screenStartStrip = camera._cur.x / 8 - _gdi->_numStrips / 2;
183 	_screenEndStrip = _screenStartStrip + _gdi->_numStrips - 1;
184 
185 	_screenTop = camera._cur.y - (_screenHeight / 2);
186 	if (_game.version >= 7) {
187 		screenLeft = camera._cur.x - (_screenWidth / 2);
188 	} else {
189 		screenLeft = _screenStartStrip * 8;
190 	}
191 
192 	_virtscr[kMainVirtScreen].xstart = screenLeft;
193 }
194 
panCameraTo(int x,int y)195 void ScummEngine::panCameraTo(int x, int y) {
196 	camera._dest.x = x;
197 	camera._mode = kPanningCameraMode;
198 	camera._movingToActor = false;
199 }
200 
actorFollowCamera(int act)201 void ScummEngine::actorFollowCamera(int act) {
202 	if (_game.version < 7) {
203 		int old;
204 
205 		old = camera._follows;
206 		setCameraFollows(derefActor(act, "actorFollowCamera"));
207 		if (camera._follows != old)
208 			runInventoryScript(0);
209 
210 		camera._movingToActor = false;
211 	}
212 }
213 
214 #ifdef ENABLE_SCUMM_7_8
setCameraAt(int pos_x,int pos_y)215 void ScummEngine_v7::setCameraAt(int pos_x, int pos_y) {
216 	Common::Point old;
217 
218 	old = camera._cur;
219 
220 	camera._cur.x = pos_x;
221 	camera._cur.y = pos_y;
222 
223 	clampCameraPos(&camera._cur);
224 
225 	camera._dest = camera._cur;
226 	VAR(VAR_CAMERA_DEST_X) = camera._dest.x;
227 	VAR(VAR_CAMERA_DEST_Y) = camera._dest.y;
228 
229 	assert(camera._cur.x >= (_screenWidth / 2) && camera._cur.y >= (_screenHeight / 2));
230 
231 	if (camera._cur.x != old.x || camera._cur.y != old.y) {
232 		if (VAR(VAR_SCROLL_SCRIPT)) {
233 			VAR(VAR_CAMERA_POS_X) = camera._cur.x;
234 			VAR(VAR_CAMERA_POS_Y) = camera._cur.y;
235 			runScript(VAR(VAR_SCROLL_SCRIPT), 0, 0, 0);
236 		}
237 
238 		// Even though cameraMoved() is called automatically, we may
239 		// need to know at once that the camera has moved, or text may
240 		// be printed at the wrong coordinates. See bugs #795938 and
241 		// #929242
242 		cameraMoved();
243 	}
244 }
245 
setCameraFollows(Actor * a,bool setCamera)246 void ScummEngine_v7::setCameraFollows(Actor *a, bool setCamera) {
247 
248 	byte oldfollow = camera._follows;
249 	int ax, ay;
250 
251 	camera._follows = a->_number;
252 	VAR(VAR_CAMERA_FOLLOWED_ACTOR) = a->_number;
253 
254 	if (!a->isInCurrentRoom()) {
255 		startScene(a->getRoom(), 0, 0);
256 	}
257 
258 	ax = ABS(a->getPos().x - camera._cur.x);
259 	ay = ABS(a->getPos().y - camera._cur.y);
260 
261 	if (ax > VAR(VAR_CAMERA_THRESHOLD_X) || ay > VAR(VAR_CAMERA_THRESHOLD_Y) || ax > (_screenWidth / 2) || ay > (_screenHeight / 2)) {
262 		setCameraAt(a->getPos().x, a->getPos().y);
263 	}
264 
265 	if (a->_number != oldfollow)
266 		runInventoryScript(0);
267 }
268 
moveCamera()269 void ScummEngine_v7::moveCamera() {
270 	Common::Point old = camera._cur;
271 	Actor *a = NULL;
272 
273 	if (camera._follows) {
274 		a = derefActor(camera._follows, "moveCamera");
275 		if (ABS(camera._cur.x - a->getPos().x) > VAR(VAR_CAMERA_THRESHOLD_X) ||
276 				ABS(camera._cur.y - a->getPos().y) > VAR(VAR_CAMERA_THRESHOLD_Y)) {
277 			camera._movingToActor = true;
278 			if (VAR(VAR_CAMERA_THRESHOLD_X) == 0)
279 				camera._cur.x = a->getPos().x;
280 			if (VAR(VAR_CAMERA_THRESHOLD_Y) == 0)
281 				camera._cur.y = a->getPos().y;
282 			clampCameraPos(&camera._cur);
283 		}
284 	} else {
285 		camera._movingToActor = false;
286 	}
287 
288 	if (camera._movingToActor) {
289 		VAR(VAR_CAMERA_DEST_X) = camera._dest.x = a->getPos().x;
290 		VAR(VAR_CAMERA_DEST_Y) = camera._dest.y = a->getPos().y;
291 	}
292 
293 	assert(camera._cur.x >= (_screenWidth / 2) && camera._cur.y >= (_screenHeight / 2));
294 
295 	clampCameraPos(&camera._dest);
296 
297 	if (camera._cur.x < camera._dest.x) {
298 		camera._cur.x += (short) VAR(VAR_CAMERA_SPEED_X);
299 		if (camera._cur.x > camera._dest.x)
300 			camera._cur.x = camera._dest.x;
301 	}
302 
303 	if (camera._cur.x > camera._dest.x) {
304 		camera._cur.x -= (short) VAR(VAR_CAMERA_SPEED_X);
305 		if (camera._cur.x < camera._dest.x)
306 			camera._cur.x = camera._dest.x;
307 	}
308 
309 	if (camera._cur.y < camera._dest.y) {
310 		camera._cur.y += (short) VAR(VAR_CAMERA_SPEED_Y);
311 		if (camera._cur.y > camera._dest.y)
312 			camera._cur.y = camera._dest.y;
313 	}
314 
315 	if (camera._cur.y > camera._dest.y) {
316 		camera._cur.y -= (short) VAR(VAR_CAMERA_SPEED_Y);
317 		if (camera._cur.y < camera._dest.y)
318 			camera._cur.y = camera._dest.y;
319 	}
320 
321 	if (camera._cur.x == camera._dest.x && camera._cur.y == camera._dest.y) {
322 
323 		camera._movingToActor = false;
324 		camera._accel.x = camera._accel.y = 0;
325 		VAR(VAR_CAMERA_SPEED_X) = VAR(VAR_CAMERA_SPEED_Y) = 0;
326 	} else {
327 
328 		camera._accel.x += (short) VAR(VAR_CAMERA_ACCEL_X);
329 		camera._accel.y += (short) VAR(VAR_CAMERA_ACCEL_Y);
330 
331 		VAR(VAR_CAMERA_SPEED_X) += camera._accel.x / 100;
332 		VAR(VAR_CAMERA_SPEED_Y) += camera._accel.y / 100;
333 
334 		if (VAR(VAR_CAMERA_SPEED_X) > 8)
335 			VAR(VAR_CAMERA_SPEED_X) = 8;
336 
337 		if (VAR(VAR_CAMERA_SPEED_Y) > 8)
338 			VAR(VAR_CAMERA_SPEED_Y) = 8;
339 
340 	}
341 
342 	cameraMoved();
343 
344 	if (camera._cur.x != old.x || camera._cur.y != old.y) {
345 		VAR(VAR_CAMERA_POS_X) = camera._cur.x;
346 		VAR(VAR_CAMERA_POS_Y) = camera._cur.y;
347 
348 		if (VAR(VAR_SCROLL_SCRIPT))
349 			runScript(VAR(VAR_SCROLL_SCRIPT), 0, 0, 0);
350 	}
351 }
352 
panCameraTo(int x,int y)353 void ScummEngine_v7::panCameraTo(int x, int y) {
354 	VAR(VAR_CAMERA_FOLLOWED_ACTOR) = camera._follows = 0;
355 	VAR(VAR_CAMERA_DEST_X) = camera._dest.x = x;
356 	VAR(VAR_CAMERA_DEST_Y) = camera._dest.y = y;
357 }
358 #endif
359 
360 } // End of namespace Scumm
361