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