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 //
24 // Game update procedure
25 //
26 
27 #include "ags/lib/std/math.h"
28 #include "ags/shared/ac/common.h"
29 #include "ags/engine/ac/character.h"
30 #include "ags/engine/ac/character_extras.h"
31 #include "ags/engine/ac/draw.h"
32 #include "ags/engine/ac/game_state.h"
33 #include "ags/shared/ac/game_setup_struct.h"
34 #include "ags/engine/ac/global_character.h"
35 #include "ags/engine/ac/lip_sync.h"
36 #include "ags/engine/ac/overlay.h"
37 #include "ags/engine/ac/sys_events.h"
38 #include "ags/engine/ac/room_object.h"
39 #include "ags/engine/ac/room_status.h"
40 #include "ags/engine/main/update.h"
41 #include "ags/engine/ac/screen_overlay.h"
42 #include "ags/engine/ac/view_frame.h"
43 #include "ags/engine/ac/walkable_area.h"
44 #include "ags/shared/gfx/bitmap.h"
45 #include "ags/engine/gfx/graphics_driver.h"
46 #include "ags/engine/media/audio/audio_system.h"
47 #include "ags/engine/ac/timer.h"
48 #include "ags/engine/main/game_run.h"
49 #include "ags/engine/ac/move_list.h"
50 #include "ags/globals.h"
51 
52 namespace AGS3 {
53 
54 using namespace AGS::Shared;
55 using namespace AGS::Engine;
56 
do_movelist_move(short * mlnum,int * xx,int * yy)57 int do_movelist_move(short *mlnum, int *xx, int *yy) {
58 	int need_to_fix_sprite = 0;
59 	if (mlnum[0] < 1) quit("movelist_move: attempted to move on a non-exist movelist");
60 	MoveList *cmls;
61 	cmls = &_G(mls)[mlnum[0]];
62 	fixed xpermove = cmls->xpermove[cmls->onstage], ypermove = cmls->ypermove[cmls->onstage];
63 
64 	short targetx = short((cmls->pos[cmls->onstage + 1] >> 16) & 0x00ffff);
65 	short targety = short(cmls->pos[cmls->onstage + 1] & 0x00ffff);
66 	int xps = xx[0], yps = yy[0];
67 	if (cmls->doneflag & 1) {
68 		// if the X-movement has finished, and the Y-per-move is < 1, finish
69 		// This can cause jump at the end, but without it the character will
70 		// walk on the spot for a while if the Y-per-move is for example 0.2
71 		//    if ((ypermove & 0xfffff000) == 0) cmls->doneflag|=2;
72 		//    int ypmm=(ypermove >> 16) & 0x0000ffff;
73 
74 		// NEW 2.15 SR-1 plan: if X-movement has finished, and Y-per-move is < 1,
75 		// allow it to finish more easily by moving target zone
76 
77 		int adjAmnt = 3;
78 		// 2.70: if the X permove is also <=1, don't do the skipping
79 		if (((xpermove & 0xffff0000) == 0xffff0000) ||
80 		        ((xpermove & 0xffff0000) == 0x00000000))
81 			adjAmnt = 2;
82 
83 		// 2.61 RC1: correct this to work with > -1 as well as < 1
84 		if (ypermove == 0) {
85 		}
86 		// Y per move is < 1, so finish the move
87 		else if ((ypermove & 0xffff0000) == 0)
88 			targety -= adjAmnt;
89 		// Y per move is -1 exactly, don't snap to finish
90 		else if (ypermove == (fixed)0xffff0000) {
91 		}
92 		// Y per move is > -1, so finish the move
93 		else if ((ypermove & 0xffff0000) == 0xffff0000)
94 			targety += adjAmnt;
95 	} else xps = cmls->fromx + (int)(fixtof(xpermove) * (float)cmls->onpart);
96 
97 	if (cmls->doneflag & 2) {
98 		// Y-movement has finished
99 
100 		int adjAmnt = 3;
101 
102 		// if the Y permove is also <=1, don't skip as far
103 		if (((ypermove & 0xffff0000) == 0xffff0000) ||
104 		        ((ypermove & 0xffff0000) == 0x00000000))
105 			adjAmnt = 2;
106 
107 		if (xpermove == 0) {
108 		}
109 		// Y per move is < 1, so finish the move
110 		else if ((xpermove & 0xffff0000) == 0)
111 			targetx -= adjAmnt;
112 		// X per move is -1 exactly, don't snap to finish
113 		else if (xpermove == (fixed)0xffff0000) {
114 		}
115 		// X per move is > -1, so finish the move
116 		else if ((xpermove & 0xffff0000) == 0xffff0000)
117 			targetx += adjAmnt;
118 
119 		/*    int xpmm=(xpermove >> 16) & 0x0000ffff;
120 		//    if ((xpmm==0) | (xpmm==0xffff)) cmls->doneflag|=1;
121 		    if (xpmm==0) cmls->doneflag|=1;*/
122 	} else yps = cmls->fromy + (int)(fixtof(ypermove) * (float)cmls->onpart);
123 	// check if finished horizontal movement
124 	if (((xpermove > 0) && (xps >= targetx)) ||
125 	        ((xpermove < 0) && (xps <= targetx))) {
126 		cmls->doneflag |= 1;
127 		xps = targetx;
128 		// if the Y is almost there too, finish it
129 		// this is new in v2.40
130 		// removed in 2.70
131 		/*if (abs(yps - targety) <= 2)
132 		  yps = targety;*/
133 	} else if (xpermove == 0)
134 		cmls->doneflag |= 1;
135 	// check if finished vertical movement
136 	if ((ypermove > 0) & (yps >= targety)) {
137 		cmls->doneflag |= 2;
138 		yps = targety;
139 	} else if ((ypermove < 0) & (yps <= targety)) {
140 		cmls->doneflag |= 2;
141 		yps = targety;
142 	} else if (ypermove == 0)
143 		cmls->doneflag |= 2;
144 
145 	if ((cmls->doneflag & 0x03) == 3) {
146 		// this stage is done, go on to the next stage
147 		// signed shorts to ensure that numbers like -20 do not become 65515
148 		cmls->fromx = (signed short)((cmls->pos[cmls->onstage + 1] >> 16) & 0x000ffff);
149 		cmls->fromy = (signed short)(cmls->pos[cmls->onstage + 1] & 0x000ffff);
150 		if ((cmls->fromx > 65000) || (cmls->fromy > 65000))
151 			quit("do_movelist: int to short rounding error");
152 
153 		cmls->onstage++;
154 		cmls->onpart = -1;
155 		cmls->doneflag &= 0xf0;
156 		cmls->lastx = -1;
157 		if (cmls->onstage < cmls->numstage) {
158 			xps = cmls->fromx;
159 			yps = cmls->fromy;
160 		}
161 		if (cmls->onstage >= cmls->numstage - 1) {  // last stage is just dest pos
162 			cmls->numstage = 0;
163 			mlnum[0] = 0;
164 			need_to_fix_sprite = 1;
165 		} else need_to_fix_sprite = 2;
166 	}
167 	cmls->onpart++;
168 	xx[0] = xps;
169 	yy[0] = yps;
170 	return need_to_fix_sprite;
171 }
172 
173 
update_script_timers()174 void update_script_timers() {
175 	if (_GP(play).gscript_timer > 0) _GP(play).gscript_timer--;
176 	for (int aa = 0; aa < MAX_TIMERS; aa++) {
177 		if (_GP(play).script_timers[aa] > 1) _GP(play).script_timers[aa]--;
178 	}
179 }
180 
update_cycling_views()181 void update_cycling_views() {
182 	// update graphics for object if cycling view
183 	for (int i = 0; i < _G(croom)->numobj; ++i) {
184 		_G(objs)[i].UpdateCyclingView(i);
185 	}
186 }
187 
update_shadow_areas()188 void update_shadow_areas() {
189 	// shadow areas
190 	int onwalkarea = get_walkable_area_at_character(_GP(game).playercharacter);
191 	if (onwalkarea < 0);
192 	else if (_G(playerchar)->flags & CHF_FIXVIEW);
193 	else {
194 		onwalkarea = _GP(thisroom).WalkAreas[onwalkarea].Light;
195 		if (onwalkarea > 0) _G(playerchar)->view = onwalkarea - 1;
196 		else if (_GP(thisroom).Options.PlayerView == 0) _G(playerchar)->view = _G(playerchar)->defview;
197 		else _G(playerchar)->view = _GP(thisroom).Options.PlayerView - 1;
198 	}
199 }
200 
update_character_move_and_anim(int & numSheep,int * followingAsSheep)201 void update_character_move_and_anim(int &numSheep, int *followingAsSheep) {
202 	// move & animate characters
203 	for (int aa = 0; aa < _GP(game).numcharacters; aa++) {
204 		if (_GP(game).chars[aa].on != 1) continue;
205 
206 		CharacterInfo *chi = &_GP(game).chars[aa];
207 		CharacterExtras *chex = &_G(charextra)[aa];
208 
209 		chi->UpdateMoveAndAnim(aa, chex, numSheep, followingAsSheep);
210 	}
211 }
212 
update_following_exactly_characters(int & numSheep,int * followingAsSheep)213 void update_following_exactly_characters(int &numSheep, int *followingAsSheep) {
214 	// update location of all following_exactly characters
215 	for (int aa = 0; aa < numSheep; aa++) {
216 		CharacterInfo *chi = &_GP(game).chars[followingAsSheep[aa]];
217 
218 		chi->UpdateFollowingExactlyCharacter();
219 	}
220 }
221 
update_overlay_timers()222 void update_overlay_timers() {
223 	// update overlay timers
224 	for (int aa = 0; aa < _G(numscreenover); aa++) {
225 		if (_G(screenover)[aa].timeout > 0) {
226 			_G(screenover)[aa].timeout--;
227 			if (_G(screenover)[aa].timeout == 0)
228 				remove_screen_overlay(_G(screenover)[aa].type);
229 		}
230 	}
231 }
232 
update_speech_and_messages()233 void update_speech_and_messages() {
234 	bool is_voice_playing = false;
235 	if (_GP(play).speech_has_voice) {
236 		AudioChannelsLock lock;
237 		auto *ch = lock.GetChannel(SCHAN_SPEECH);
238 		is_voice_playing = ch && ch->is_playing();
239 	}
240 	// determine if speech text should be removed
241 	if (_GP(play).messagetime >= 0) {
242 		_GP(play).messagetime--;
243 		// extend life of text if the voice hasn't finished yet
244 		if (_GP(play).speech_has_voice && !_GP(play).speech_in_post_state) {
245 			if ((is_voice_playing) && (_GP(play).fast_forward == 0)) {
246 				if (_GP(play).messagetime <= 1)
247 					_GP(play).messagetime = 1;
248 			} else  // if the voice has finished, remove the speech
249 				_GP(play).messagetime = 0;
250 		}
251 
252 		if (_GP(play).messagetime < 1 && _GP(play).speech_display_post_time_ms > 0 &&
253 		        _GP(play).fast_forward == 0) {
254 			if (!_GP(play).speech_in_post_state) {
255 				_GP(play).messagetime = ::lround(_GP(play).speech_display_post_time_ms * get_current_fps() / 1000.0f);
256 			}
257 			_GP(play).speech_in_post_state = !_GP(play).speech_in_post_state;
258 		}
259 
260 		if (_GP(play).messagetime < 1) {
261 			if (_GP(play).fast_forward > 0) {
262 				remove_screen_overlay(_GP(play).text_overlay_on);
263 				_GP(play).SetWaitSkipResult(SKIP_AUTOTIMER);
264 			} else if (_GP(play).cant_skip_speech & SKIP_AUTOTIMER) {
265 				remove_screen_overlay(_GP(play).text_overlay_on);
266 				_GP(play).SetWaitSkipResult(SKIP_AUTOTIMER);
267 				_GP(play).SetIgnoreInput(_GP(play).ignore_user_input_after_text_timeout_ms);
268 			}
269 		}
270 	}
271 }
272 
273 // update sierra-style speech
update_sierra_speech()274 void update_sierra_speech() {
275 	int voice_pos_ms = -1;
276 	if (_GP(play).speech_has_voice) {
277 		AudioChannelsLock lock;
278 		auto *ch = lock.GetChannel(SCHAN_SPEECH);
279 		voice_pos_ms = ch ? ch->get_pos_ms() : -1;
280 	}
281 	if ((_G(face_talking) >= 0) && (_GP(play).fast_forward == 0)) {
282 		int updatedFrame = 0;
283 
284 		if ((_G(facetalkchar)->blinkview > 0) && (_G(facetalkAllowBlink))) {
285 			if (_G(facetalkchar)->blinktimer > 0) {
286 				// countdown to playing blink anim
287 				_G(facetalkchar)->blinktimer--;
288 				if (_G(facetalkchar)->blinktimer == 0) {
289 					_G(facetalkchar)->blinkframe = 0;
290 					_G(facetalkchar)->blinktimer = -1;
291 					updatedFrame = 2;
292 				}
293 			} else if (_G(facetalkchar)->blinktimer < 0) {
294 				// currently playing blink anim
295 				if (_G(facetalkchar)->blinktimer < ((0 - 6) - _G(views)[_G(facetalkchar)->blinkview].loops[_G(facetalkBlinkLoop)].frames[_G(facetalkchar)->blinkframe].speed)) {
296 					// time to advance to next frame
297 					_G(facetalkchar)->blinktimer = -1;
298 					_G(facetalkchar)->blinkframe++;
299 					updatedFrame = 2;
300 					if (_G(facetalkchar)->blinkframe >= _G(views)[_G(facetalkchar)->blinkview].loops[_G(facetalkBlinkLoop)].numFrames) {
301 						_G(facetalkchar)->blinkframe = 0;
302 						_G(facetalkchar)->blinktimer = _G(facetalkchar)->blinkinterval;
303 					}
304 				} else
305 					_G(facetalkchar)->blinktimer--;
306 			}
307 
308 		}
309 
310 		if (_G(curLipLine) >= 0) {
311 			// check voice lip sync
312 			if (_G(curLipLinePhoneme) >= _G(splipsync)[_G(curLipLine)].numPhonemes) {
313 				// the lip-sync has finished, so just stay idle
314 			} else {
315 				while ((_G(curLipLinePhoneme) < _G(splipsync)[_G(curLipLine)].numPhonemes) &&
316 				        ((_G(curLipLinePhoneme) < 0) || (voice_pos_ms >= _G(splipsync)[_G(curLipLine)].endtimeoffs[_G(curLipLinePhoneme)]))) {
317 					_G(curLipLinePhoneme)++;
318 					if (_G(curLipLinePhoneme) >= _G(splipsync)[_G(curLipLine)].numPhonemes)
319 						_G(facetalkframe) = _GP(game).default_lipsync_frame;
320 					else
321 						_G(facetalkframe) = _G(splipsync)[_G(curLipLine)].frame[_G(curLipLinePhoneme)];
322 
323 					if (_G(facetalkframe) >= _G(views)[_G(facetalkview)].loops[_G(facetalkloop)].numFrames)
324 						_G(facetalkframe) = 0;
325 
326 					updatedFrame |= 1;
327 				}
328 			}
329 		} else if (_G(facetalkwait) > 0) _G(facetalkwait)--;
330 		// don't animate if the speech has finished
331 		else if ((_GP(play).messagetime < 1) && (_G(facetalkframe) == 0) &&
332 		         // if _GP(play).close_mouth_speech_time = 0, this means animation should play till
333 		         // the speech ends; but this should not work in voice mode, and also if the
334 		         // speech is in the "post" state
335 		         (_GP(play).speech_has_voice || _GP(play).speech_in_post_state || _GP(play).close_mouth_speech_time > 0))
336 			;
337 		else {
338 			// Close mouth at end of sentence: if speech has entered the "post" state,
339 			// or if this is a text only mode and close_mouth_speech_time is set
340 			if (_GP(play).speech_in_post_state ||
341 			        (!_GP(play).speech_has_voice &&
342 			         (_GP(play).messagetime < _GP(play).close_mouth_speech_time) &&
343 			         (_GP(play).close_mouth_speech_time > 0))) {
344 				_G(facetalkframe) = 0;
345 				_G(facetalkwait) = _GP(play).messagetime;
346 			} else if ((_GP(game).options[OPT_LIPSYNCTEXT]) && (_G(facetalkrepeat) > 0)) {
347 				// lip-sync speech (and not a thought)
348 				_G(facetalkwait) = update_lip_sync(_G(facetalkview), _G(facetalkloop), &_G(facetalkframe));
349 				// It is actually displayed for _G(facetalkwait)+1 loops
350 				// (because when it's 1, it gets --'d then wait for next time)
351 				_G(facetalkwait)--;
352 			} else {
353 				// normal non-lip-sync
354 				_G(facetalkframe)++;
355 				if ((_G(facetalkframe) >= _G(views)[_G(facetalkview)].loops[_G(facetalkloop)].numFrames) ||
356 				        (!_GP(play).speech_has_voice && (_GP(play).messagetime < 1) && (_GP(play).close_mouth_speech_time > 0))) {
357 
358 					if ((_G(facetalkframe) >= _G(views)[_G(facetalkview)].loops[_G(facetalkloop)].numFrames) &&
359 					        (_G(views)[_G(facetalkview)].loops[_G(facetalkloop)].RunNextLoop())) {
360 						_G(facetalkloop)++;
361 					} else {
362 						_G(facetalkloop) = 0;
363 					}
364 					_G(facetalkframe) = 0;
365 					if (!_G(facetalkrepeat))
366 						_G(facetalkwait) = 999999;
367 				}
368 				if ((_G(facetalkframe) != 0) || (_G(facetalkrepeat) == 1))
369 					_G(facetalkwait) = _G(views)[_G(facetalkview)].loops[_G(facetalkloop)].frames[_G(facetalkframe)].speed + GetCharacterSpeechAnimationDelay(_G(facetalkchar));
370 			}
371 			updatedFrame |= 1;
372 		}
373 
374 		// _G(is_text_overlay) might be 0 if it was only just destroyed this loop
375 		if ((updatedFrame) && (_GP(play).text_overlay_on > 0)) {
376 
377 			if (updatedFrame & 1)
378 				CheckViewFrame(_G(facetalkview), _G(facetalkloop), _G(facetalkframe));
379 			if (updatedFrame & 2)
380 				CheckViewFrame(_G(facetalkchar)->blinkview, _G(facetalkBlinkLoop), _G(facetalkchar)->blinkframe);
381 
382 			int thisPic = _G(views)[_G(facetalkview)].loops[_G(facetalkloop)].frames[_G(facetalkframe)].pic;
383 			int view_frame_x = 0;
384 			int view_frame_y = 0;
385 
386 			if (_GP(game).options[OPT_SPEECHTYPE] == 3) {
387 				// QFG4-style fullscreen dialog
388 				if (_G(facetalk_qfg4_override_placement_x)) {
389 					view_frame_x = _GP(play).speech_portrait_x;
390 				}
391 				if (_G(facetalk_qfg4_override_placement_y)) {
392 					view_frame_y = _GP(play).speech_portrait_y;
393 				} else {
394 					view_frame_y = (_G(screenover)[_G(face_talking)].pic->GetHeight() / 2) - (_GP(game).SpriteInfos[thisPic].Height / 2);
395 				}
396 				_G(screenover)[_G(face_talking)].pic->Clear(0);
397 			} else {
398 				_G(screenover)[_G(face_talking)].pic->ClearTransparent();
399 			}
400 
401 			Bitmap *frame_pic = _G(screenover)[_G(face_talking)].pic;
402 			const ViewFrame *face_vf = &_G(views)[_G(facetalkview)].loops[_G(facetalkloop)].frames[_G(facetalkframe)];
403 			bool face_has_alpha = (_GP(game).SpriteInfos[face_vf->pic].Flags & SPF_ALPHACHANNEL) != 0;
404 			DrawViewFrame(frame_pic, face_vf, view_frame_x, view_frame_y);
405 
406 			if ((_G(facetalkchar)->blinkview > 0) && (_G(facetalkchar)->blinktimer < 0)) {
407 				ViewFrame *blink_vf = &_G(views)[_G(facetalkchar)->blinkview].loops[_G(facetalkBlinkLoop)].frames[_G(facetalkchar)->blinkframe];
408 				face_has_alpha |= (_GP(game).SpriteInfos[blink_vf->pic].Flags & SPF_ALPHACHANNEL) != 0;
409 				// draw the blinking sprite on top
410 				DrawViewFrame(frame_pic, blink_vf, view_frame_x, view_frame_y, face_has_alpha);
411 			}
412 
413 			_G(gfxDriver)->UpdateDDBFromBitmap(_G(screenover)[_G(face_talking)].bmp, _G(screenover)[_G(face_talking)].pic, face_has_alpha);
414 		}  // end if updatedFrame
415 	}
416 }
417 
418 // update_stuff: moves and animates objects, executes repeat scripts, and
419 // the like.
update_stuff()420 void update_stuff() {
421 
422 	_G(our_eip) = 20;
423 
424 	update_script_timers();
425 
426 	update_cycling_views();
427 
428 	_G(our_eip) = 21;
429 
430 	update_shadow_areas();
431 
432 	_G(our_eip) = 22;
433 
434 	int numSheep = 0;
435 	int followingAsSheep[MAX_SHEEP];
436 
437 	update_character_move_and_anim(numSheep, followingAsSheep);
438 
439 	update_following_exactly_characters(numSheep, followingAsSheep);
440 
441 	_G(our_eip) = 23;
442 
443 	update_overlay_timers();
444 
445 	update_speech_and_messages();
446 
447 	_G(our_eip) = 24;
448 
449 	update_sierra_speech();
450 
451 	_G(our_eip) = 25;
452 }
453 
454 } // namespace AGS3
455