rnd-20111007-1-src
[rocksndiamonds.git] / src / game_sp / DDScrollBuffer.c
1 // ----------------------------------------------------------------------------
2 // DDScrollBuffer.c
3 // ----------------------------------------------------------------------------
4
5 #include "DDScrollBuffer.h"
6
7 #include <math.h>
8
9
10 int mScrollX, mScrollY;
11 int mScrollX_last, mScrollY_last;
12
13 #if 1
14 int ScreenBuffer[2 + MAX_PLAYFIELD_WIDTH + 2][2 + MAX_PLAYFIELD_HEIGHT + 2];
15 boolean redraw[2 + MAX_PLAYFIELD_WIDTH + 2][2 + MAX_PLAYFIELD_HEIGHT + 2];
16 #else
17 int ScreenBuffer[MAX_BUF_XSIZE][MAX_BUF_YSIZE];
18 boolean redraw[MAX_BUF_XSIZE][MAX_BUF_YSIZE];
19 #endif
20
21
22 void RestorePlayfield()
23 {
24   int x1 = mScrollX / TILEX - 2;
25   int y1 = mScrollY / TILEY - 2;
26   int x2 = mScrollX / TILEX + (SCR_FIELDX - 1) + 2;
27   int y2 = mScrollY / TILEY + (SCR_FIELDY - 1) + 2;
28   int x, y;
29
30   DrawFrameIfNeeded();
31
32   for (y = DisplayMinY; y <= DisplayMaxY; y++)
33   {
34     for (x = DisplayMinX; x <= DisplayMaxX; x++)
35     {
36       if (x >= x1 && x <= x2 && y >= y1 && y <= y2)
37       {
38         DrawFieldNoAnimated(x, y);
39         DrawFieldAnimated(x, y);
40       }
41     }
42   }
43 }
44
45 static void ScrollPlayfield(int dx, int dy)
46 {
47   int x1 = mScrollX_last / TILEX - 2;
48   int y1 = mScrollY_last / TILEY - 2;
49   int x2 = mScrollX_last / TILEX + (SCR_FIELDX - 1) + 2;
50   int y2 = mScrollY_last / TILEY + (SCR_FIELDY - 1) + 2;
51   int x, y;
52
53 #if NEW_TILESIZE
54   BlitBitmap(bitmap_db_field_sp, bitmap_db_field_sp,
55              TILEX_VAR * (dx == -1),
56              TILEY_VAR * (dy == -1),
57              (MAX_BUF_XSIZE * TILEX_VAR) - TILEX_VAR * (dx != 0),
58              (MAX_BUF_YSIZE * TILEY_VAR) - TILEY_VAR * (dy != 0),
59              TILEX_VAR * (dx == 1),
60              TILEY_VAR * (dy == 1));
61 #else
62   BlitBitmap(bitmap_db_field_sp, bitmap_db_field_sp,
63              TILEX * (dx == -1),
64              TILEY * (dy == -1),
65              (MAX_BUF_XSIZE * TILEX) - TILEX * (dx != 0),
66              (MAX_BUF_YSIZE * TILEY) - TILEY * (dy != 0),
67              TILEX * (dx == 1),
68              TILEY * (dy == 1));
69 #endif
70
71   /* when scrolling the whole playfield, do not redraw single tiles */
72 #if 1
73   for (x = 0; x < 2 + MAX_PLAYFIELD_WIDTH + 2; x++)
74     for (y = 0; y < 2 + MAX_PLAYFIELD_HEIGHT + 2; y++)
75       redraw[x][y] = FALSE;
76 #else
77   for (x = 0; x < MAX_BUF_XSIZE; x++)
78     for (y = 0; y < MAX_BUF_YSIZE; y++)
79       redraw[x][y] = FALSE;
80 #endif
81   redraw_tiles = 0;
82
83   DrawFrameIfNeeded();
84
85   for (y = DisplayMinY; y <= DisplayMaxY; y++)
86   {
87     for (x = DisplayMinX; x <= DisplayMaxX; x++)
88     {
89       if (x >= x1 && x <= x2 && y >= y1 && y <= y2)
90       {
91         int sx = x - x1;
92         int sy = y - y1;
93         int tsi = GetSI(x, y);
94         int id = ((PlayField16[tsi]) |
95                   (PlayField8[tsi] << 16) |
96                   (DisPlayField[tsi] << 24));
97
98         if ((dx == -1 && x == x2) ||
99             (dx == +1 && x == x1) ||
100             (dy == -1 && y == y2) ||
101             (dy == +1 && y == y1))
102         {
103           DrawFieldNoAnimated(x, y);
104           DrawFieldAnimated(x, y);
105         }
106
107         ScreenBuffer[sx][sy] = id;
108       }
109     }
110   }
111 }
112
113 static void ScrollPlayfieldIfNeededExt(boolean reset)
114 {
115   if (reset)
116   {
117     mScrollX_last = -1;
118     mScrollY_last = -1;
119
120     return;
121   }
122
123   if (mScrollX_last == -1 || mScrollY_last == -1)
124   {
125 #if 1
126     mScrollX_last = (mScrollX / TILESIZE) * TILESIZE;
127     mScrollY_last = (mScrollY / TILESIZE) * TILESIZE;
128 #else
129     mScrollX_last = mScrollX;
130     mScrollY_last = mScrollY;
131 #endif
132
133     return;
134   }
135
136   /* check if scrolling the playfield requires redrawing the viewport bitmap */
137   if ((mScrollX != mScrollX_last ||
138        mScrollY != mScrollY_last) &&
139       (ABS(mScrollX - mScrollX_last) >= TILEX ||
140        ABS(mScrollY - mScrollY_last) >= TILEY))
141   {
142     int dx = (ABS(mScrollX - mScrollX_last) < TILEX ? 0 :
143               mScrollX < mScrollX_last ? 1 : mScrollX > mScrollX_last ? -1 : 0);
144     int dy = (ABS(mScrollY - mScrollY_last) < TILEY ? 0 :
145               mScrollY < mScrollY_last ? 1 : mScrollY > mScrollY_last ? -1 : 0);
146
147     mScrollX_last -= dx * TILEX;
148     mScrollY_last -= dy * TILEY;
149
150     ScrollPlayfield(dx, dy);
151   }
152 }
153
154 static void ScrollPlayfieldIfNeeded()
155 {
156   ScrollPlayfieldIfNeededExt(FALSE);
157 }
158
159 void InitScrollPlayfield()
160 {
161   ScrollPlayfieldIfNeededExt(TRUE);
162 }
163
164 #define DEBUG_REDRAW    0
165
166 void UpdatePlayfield(boolean force_redraw)
167 {
168   int x, y;
169
170 #if DEBUG_REDRAW
171   int num_redrawn = 0;
172 #endif
173
174   for (y = DisplayMinY; y <= DisplayMaxY; y++)
175   {
176     for (x = DisplayMinX; x <= DisplayMaxX; x++)
177     {
178       int element = LowByte(PlayField16[GetSI(x, y)]);
179       int graphic = GfxGraphic[x][y];
180       int sync_frame = GfxFrame[x][y];
181       boolean redraw = force_redraw;
182
183 #if DEBUG_REDRAW
184 #if 0
185       redraw = TRUE;    // !!! TEST ONLY -- ALWAYS REDRAW !!!
186 #endif
187 #endif
188
189       if (graphic < 0)
190       {
191         GfxGraphicLast[x][y] = GfxGraphic[x][y];
192
193         continue;
194       }
195
196       if (element != GfxElementLast[x][y] &&
197           graphic == GfxGraphicLast[x][y])
198       {
199         /* element changed, but not graphic => disable updating graphic */
200
201         GfxElementLast[x][y] = element;
202         GfxGraphicLast[x][y] = GfxGraphic[x][y] = -1;
203
204         continue;
205       }
206
207       if (graphic != GfxGraphicLast[x][y])                      // new graphic
208       {
209         redraw = TRUE;
210
211         GfxElementLast[x][y] = element;
212         GfxGraphicLast[x][y] = GfxGraphic[x][y];
213         sync_frame = GfxFrame[x][y] = 0;
214       }
215       else if (isNextAnimationFrame_SP(graphic, sync_frame))    // new frame
216       {
217         redraw = TRUE;
218       }
219
220       if (redraw)
221       {
222         int sx = x * StretchWidth;
223         int sy = y * StretchWidth;
224
225 #if DEBUG_REDRAW
226 #if 0
227         printf("::: REDRAW (%d, %d): %d, %d\n", x, y, graphic, sync_frame);
228 #endif
229 #endif
230
231         DDSpriteBuffer_BltImg(sx, sy, graphic, sync_frame);
232
233 #if DEBUG_REDRAW
234         num_redrawn++;
235 #endif
236       }
237     }
238   }
239
240 #if DEBUG_REDRAW
241   printf("::: FRAME %d: %d redrawn\n", FrameCounter, num_redrawn);
242 #endif
243 }
244
245 /* copy the entire screen to the window at the scroll position */
246
247 void BlitScreenToBitmap_SP(Bitmap *target_bitmap)
248 {
249   int px = 2 * TILEX + (mScrollX - mScrollX_last) % TILEX;
250   int py = 2 * TILEY + (mScrollY - mScrollY_last) % TILEY;
251   int sx, sy, sxsize, sysize;
252
253 #if 0
254   printf("::: %d, %d / %d, %d / %ld, %ld (%ld, %ld) / %d, %d\n",
255          MurphyScreenXPos, MurphyScreenYPos,
256          ScreenScrollXPos, ScreenScrollYPos,
257          mScrollX, mScrollY,
258          mScrollX_last, mScrollY_last,
259          px, py);
260 #endif
261
262   int xsize = SXSIZE;
263   int ysize = SYSIZE;
264 #if NEW_TILESIZE
265   int full_xsize = (FieldWidth  - (menBorder ? 0 : 1)) * TILEX_VAR;
266   int full_ysize = (FieldHeight - (menBorder ? 0 : 1)) * TILEY_VAR;
267 #else
268   int full_xsize = (FieldWidth  - (menBorder ? 0 : 1)) * TILEX;
269   int full_ysize = (FieldHeight - (menBorder ? 0 : 1)) * TILEY;
270 #endif
271
272 #if NEW_TILESIZE
273
274 #endif
275
276   sxsize = (full_xsize < xsize ? full_xsize : xsize);
277   sysize = (full_ysize < ysize ? full_ysize : ysize);
278   sx = SX + (full_xsize < xsize ? (xsize - full_xsize) / 2 : 0);
279   sy = SY + (full_ysize < ysize ? (ysize - full_ysize) / 2 : 0);
280
281   /* scroll correction for even number of visible tiles (half tile shifted) */
282   px += game_sp.scroll_xoffset;
283   py += game_sp.scroll_yoffset;
284
285 #if 1
286   if (ExplosionShakeMurphy != 0)
287   {
288     px += TILEX / 2 - GetSimpleRandom(TILEX + 1);
289     py += TILEY / 2 - GetSimpleRandom(TILEX + 1);
290   }
291 #endif
292
293 #if NEW_TILESIZE
294   px = px * TILESIZE_VAR / TILESIZE;
295   py = py * TILESIZE_VAR / TILESIZE;
296 #endif
297
298 #if 0
299   printf("::: (%d, %d) (%d, %d) (%d, %d) [%d / %d]\n",
300          px, py, sxsize, sysize, sx, sy,
301          FieldHeight, menBorder);
302 #endif
303
304 #if 0
305   printf("::: (%d, %d)\n",
306          bitmap_db_field_sp->width, bitmap_db_field_sp->height);
307 #endif
308
309   BlitBitmap(bitmap_db_field_sp, target_bitmap, px, py, sxsize, sysize, sx, sy);
310 }
311
312 void BackToFront_SP(void)
313 {
314   static int scroll_x_last = -1, scroll_y_last = -1;
315   static boolean scrolling_last = FALSE;
316   static boolean ExplosionShakeMurphy_last = -1;
317 #if 1
318   boolean scrolling = (mScrollX != scroll_x_last || mScrollY != scroll_y_last);
319   // boolean scrolling = (mScrollX != mScrollX_last || mScrollY != mScrollY_last);
320 #else
321   boolean scrolling = (mScrollX % TILEX != 0 || mScrollY % TILEY != 0);
322 #endif
323   int x, y;
324
325 #if 0
326   printf("::: %d, %d / %d, %d [%d, %d]\n",
327          mScrollX, mScrollY,
328          mScrollX_last, mScrollY_last,
329          game_sp.scroll_xoffset, game_sp.scroll_yoffset);
330 #endif
331
332   SyncDisplay();
333
334   if (0 ||
335       redraw_tiles > REDRAWTILES_THRESHOLD || scrolling || scrolling_last ||
336       ExplosionShakeMurphy != 0 || ExplosionShakeMurphy_last != 0)
337   {
338     BlitScreenToBitmap_SP(window);
339   }
340   else
341   {
342     int scroll_xoffset = mScrollX - mScrollX_last + game_sp.scroll_xoffset;
343     int scroll_yoffset = mScrollY - mScrollY_last + game_sp.scroll_yoffset;
344     int x1 = 0, x2 = SCR_FIELDX - (scroll_xoffset != 0 ? 0 : 1);
345     int y1 = 0, y2 = SCR_FIELDY - (scroll_yoffset != 0 ? 0 : 1);
346 #if NEW_TILESIZE
347     int full_xsize = (FieldWidth  - (menBorder ? 0 : 1)) * TILEX_VAR;
348     int full_ysize = (FieldHeight - (menBorder ? 0 : 1)) * TILEY_VAR;
349 #else
350     int full_xsize = (FieldWidth  - (menBorder ? 0 : 1)) * TILEX;
351     int full_ysize = (FieldHeight - (menBorder ? 0 : 1)) * TILEY;
352 #endif
353 #if 1
354     int xsize = SXSIZE;
355     int ysize = SYSIZE;
356     int sxsize = (full_xsize < xsize ? full_xsize : xsize);
357     int sysize = (full_ysize < ysize ? full_ysize : ysize);
358     int sx = SX + (full_xsize < xsize ? (xsize - full_xsize) / 2 : 0);
359     int sy = SY + (full_ysize < ysize ? (ysize - full_ysize) / 2 : 0);
360 #else
361     int sx = SX + (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
362     int sy = SY + (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
363 #endif
364
365 #if 1
366     InitGfxClipRegion(TRUE, sx, sy, sxsize, sysize);
367 #else
368     InitGfxClipRegion(TRUE, SX, SY, SXSIZE, SYSIZE);
369 #endif
370
371 #if NEW_TILESIZE
372     scroll_xoffset = scroll_xoffset * TILESIZE_VAR / TILESIZE;
373     scroll_yoffset = scroll_yoffset * TILESIZE_VAR / TILESIZE;
374 #endif
375
376     for (x = x1; x <= x2; x++)
377     {
378       for (y = y1; y <= y2; y++)
379       {
380         int xx = 2 + x;
381         int yy = 2 + y;
382
383 #if NEW_TILESIZE
384         if (redraw[xx][yy])
385           BlitBitmap(bitmap_db_field_sp, window,
386                      xx * TILEX_VAR, yy * TILEY_VAR, TILEX_VAR, TILEY_VAR,
387                      sx + x * TILEX_VAR - scroll_xoffset,
388                      sy + y * TILEY_VAR - scroll_yoffset);
389 #else
390         if (redraw[xx][yy])
391           BlitBitmap(bitmap_db_field_sp, window,
392                      xx * TILEX, yy * TILEY, TILEX, TILEY,
393                      sx + x * TILEX - scroll_xoffset,
394                      sy + y * TILEY - scroll_yoffset);
395 #endif
396       }
397     }
398
399     InitGfxClipRegion(FALSE, -1, -1, -1, -1);
400   }
401
402   FlushDisplay();
403
404 #if 1
405   for (x = 0; x < 2 + MAX_PLAYFIELD_WIDTH + 2; x++)
406     for (y = 0; y < 2 + MAX_PLAYFIELD_HEIGHT + 2; y++)
407       redraw[x][y] = FALSE;
408 #else
409   for (x = 0; x < MAX_BUF_XSIZE; x++)
410     for (y = 0; y < MAX_BUF_YSIZE; y++)
411       redraw[x][y] = FALSE;
412 #endif
413   redraw_tiles = 0;
414
415   scroll_x_last = mScrollX;
416   scroll_y_last = mScrollY;
417   scrolling_last = scrolling;
418   ExplosionShakeMurphy_last = ExplosionShakeMurphy;
419 }
420
421 void DDScrollBuffer_ScrollTo(int X, int Y)
422 {
423   if (NoDisplayFlag)
424     return;
425
426   ScrollX = mScrollX = X;
427   ScrollY = mScrollY = Y;
428
429   ScrollPlayfieldIfNeeded();
430 }
431
432 void DDScrollBuffer_ScrollTowards(int X, int Y, double Step)
433 {
434   double dx, dY, r;
435
436   if (NoDisplayFlag)
437     return;
438
439   dx = X - mScrollX;
440   dY = Y - mScrollY;
441
442   r = Sqr(dx * dx + dY * dY);
443   if (r == 0)   // we are there already
444     return;
445
446   if (Step < r)
447     r = Step / r;
448   else
449     r = 1;
450
451   ScrollX = mScrollX = mScrollX + dx * r;
452   ScrollY = mScrollY = mScrollY + dY * r;
453
454   ScrollPlayfieldIfNeeded();
455 }
456
457 void DDScrollBuffer_SoftScrollTo(int X, int Y, int TimeMS, int FPS)
458 {
459   double dx, dY;
460   int dT, StepCount;
461   double T, tStep;
462   int oldX, oldY, maxD;
463   static boolean AlreadyRunning = False;
464
465   if (NoDisplayFlag)
466     return;
467
468   if (AlreadyRunning)
469     return;
470
471   AlreadyRunning = True;
472
473   dx = X - mScrollX;
474   dY = Y - mScrollY;
475   maxD = (Abs(dx) < Abs(dY) ? Abs(dY) : Abs(dx));
476
477   StepCount = FPS * (TimeMS / (double)1000);
478   if (StepCount > maxD)
479     StepCount = maxD;
480
481   if (StepCount == 0)
482     StepCount = 1;
483
484   dT = 1000 / FPS;
485   tStep = (double)1 / StepCount;
486   oldX = mScrollX;
487   oldY = mScrollY;
488
489   for (T = (double)tStep; T <= (double)1; T += tStep)
490   {
491     ScrollX = mScrollX = oldX + T * dx;
492     ScrollY = mScrollY = oldY + T * dY;
493   }
494
495   ScrollX = mScrollX = X;
496   ScrollY = mScrollY = Y;
497
498   AlreadyRunning = False;
499
500   ScrollPlayfieldIfNeeded();
501 }