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