rnd-20100315-2-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 long mWidth, mHeight;
11 long mhWnd;
12 long mScrollX, mScrollY;
13 long mScrollX_last, mScrollY_last;
14 long mDestXOff, mDestYOff;
15
16 long ScreenBuffer[MAX_BUF_XSIZE][MAX_BUF_YSIZE];
17 boolean redraw[MAX_BUF_XSIZE][MAX_BUF_YSIZE];
18
19
20 void RestorePlayfield()
21 {
22   int x1 = mScrollX / TILEX - 2;
23   int y1 = mScrollY / TILEY - 2;
24   int x2 = mScrollX / TILEX + (SCR_FIELDX - 1) + 2;
25   int y2 = mScrollY / TILEY + (SCR_FIELDY - 1) + 2;
26   int x, y;
27
28   DrawFrameIfNeeded();
29
30   for (y = DisplayMinY; y <= DisplayMaxY; y++)
31   {
32     for (x = DisplayMinX; x <= DisplayMaxX; x++)
33     {
34       if (x >= x1 && x <= x2 && y >= y1 && y <= y2)
35       {
36         DrawFieldNoAnimated(x, y);
37         DrawFieldAnimated(x, y);
38       }
39     }
40   }
41 }
42
43 static void ScrollPlayfield(int dx, int dy)
44 {
45   int x1 = mScrollX_last / TILEX - 2;
46   int y1 = mScrollY_last / TILEY - 2;
47   int x2 = mScrollX_last / TILEX + (SCR_FIELDX - 1) + 2;
48   int y2 = mScrollY_last / TILEY + (SCR_FIELDY - 1) + 2;
49   int x, y;
50
51   BlitBitmap(screenBitmap, screenBitmap,
52              TILEX * (dx == -1),
53              TILEY * (dy == -1),
54              (MAX_BUF_XSIZE * TILEX) - TILEX * (dx != 0),
55              (MAX_BUF_YSIZE * TILEY) - TILEY * (dy != 0),
56              TILEX * (dx == 1),
57              TILEY * (dy == 1));
58
59   /* when scrolling the whole playfield, do not redraw single tiles */
60   for (x = 0; x < MAX_BUF_XSIZE; x++)
61     for (y = 0; y < MAX_BUF_YSIZE; y++)
62       redraw[x][y] = FALSE;
63   redraw_tiles = 0;
64
65   DrawFrameIfNeeded();
66
67   for (y = DisplayMinY; y <= DisplayMaxY; y++)
68   {
69     for (x = DisplayMinX; x <= DisplayMaxX; x++)
70     {
71       if (x >= x1 && x <= x2 && y >= y1 && y <= y2)
72       {
73         int sx = x - x1;
74         int sy = y - y1;
75         int tsi = GetSI(x, y);
76         long id = ((PlayField16[tsi]) |
77                    (PlayField8[tsi] << 16) |
78                    (DisPlayField[tsi] << 24));
79
80         if ((dx == -1 && x == x2) ||
81             (dx == +1 && x == x1) ||
82             (dy == -1 && y == y2) ||
83             (dy == +1 && y == y1))
84         {
85           DrawFieldNoAnimated(x, y);
86           DrawFieldAnimated(x, y);
87         }
88
89         ScreenBuffer[sx][sy] = id;
90       }
91     }
92   }
93 }
94
95 static void ScrollPlayfieldIfNeededExt(boolean reset)
96 {
97   if (reset)
98   {
99     mScrollX_last = -1;
100     mScrollY_last = -1;
101
102     return;
103   }
104
105   if (mScrollX_last == -1 || mScrollY_last == -1)
106   {
107     mScrollX_last = mScrollX;
108     mScrollY_last = mScrollY;
109
110     return;
111   }
112
113   /* check if scrolling the playfield requires redrawing the viewport bitmap */
114   if ((mScrollX != mScrollX_last ||
115        mScrollY != mScrollY_last) &&
116       (ABS(mScrollX - mScrollX_last) >= TILEX ||
117        ABS(mScrollY - mScrollY_last) >= TILEY))
118   {
119     int dx = (ABS(mScrollX - mScrollX_last) < TILEX ? 0 :
120               mScrollX < mScrollX_last ? 1 : mScrollX > mScrollX_last ? -1 : 0);
121     int dy = (ABS(mScrollY - mScrollY_last) < TILEY ? 0 :
122               mScrollY < mScrollY_last ? 1 : mScrollY > mScrollY_last ? -1 : 0);
123
124     mScrollX_last -= dx * TILEX;
125     mScrollY_last -= dy * TILEY;
126
127     ScrollPlayfield(dx, dy);
128   }
129 }
130
131 static void ScrollPlayfieldIfNeeded()
132 {
133   ScrollPlayfieldIfNeededExt(FALSE);
134 }
135
136 void InitScrollPlayfield()
137 {
138   ScrollPlayfieldIfNeededExt(TRUE);
139 }
140
141 void UpdatePlayfield(boolean force_redraw)
142 {
143   int x, y;
144 #if 1
145   int num_redrawn = 0;
146 #endif
147
148   for (y = DisplayMinY; y <= DisplayMaxY; y++)
149   {
150     for (x = DisplayMinX; x <= DisplayMaxX; x++)
151     {
152       int element = LowByte(PlayField16[GetSI(x, y)]);
153       int graphic = GfxGraphic[x][y];
154       int sync_frame = GfxFrame[x][y];
155       boolean redraw = force_redraw;
156
157 #if 0
158       redraw = TRUE;    // !!! TEST ONLY -- ALWAYS REDRAW !!!
159 #endif
160
161       if (graphic < 0)
162       {
163         GfxGraphicLast[x][y] = GfxGraphic[x][y];
164
165         continue;
166       }
167
168       if (element != GfxElementLast[x][y] &&
169           graphic == GfxGraphicLast[x][y])
170       {
171         /* element changed, but not graphic => disable updating graphic */
172
173         GfxElementLast[x][y] = element;
174         GfxGraphicLast[x][y] = GfxGraphic[x][y] = -1;
175
176         continue;
177       }
178
179       if (graphic != GfxGraphicLast[x][y])                      // new graphic
180       {
181         redraw = TRUE;
182
183         GfxElementLast[x][y] = element;
184         GfxGraphicLast[x][y] = GfxGraphic[x][y];
185         sync_frame = GfxFrame[x][y] = 0;
186       }
187       else if (isNextAnimationFrame_SP(graphic, sync_frame))    // new frame
188       {
189         redraw = TRUE;
190       }
191
192       if (redraw)
193       {
194         int sx = x * StretchWidth;
195         int sy = y * StretchWidth;
196
197 #if 0
198         printf("::: REDRAW (%d, %d): %d, %d\n", x, y, graphic, sync_frame);
199 #endif
200
201         StretchedSprites.BltImg(sx, sy, graphic, sync_frame);
202
203 #if 1
204         num_redrawn++;
205 #endif
206       }
207     }
208   }
209
210 #if 0
211   printf("::: FRAME %d: %d redrawn\n", FrameCounter, num_redrawn);
212 #endif
213 }
214
215 /* copy the entire screen to the window at the scroll position */
216
217 void BlitScreenToBitmap_SP(Bitmap *target_bitmap)
218 {
219   int px = 2 * TILEX + (mScrollX - mScrollX_last) % TILEX;
220   int py = 2 * TILEY + (mScrollY - mScrollY_last) % TILEY;
221   int sx, sy, sxsize, sysize;
222
223   int xsize = SXSIZE;
224   int ysize = SYSIZE;
225   int full_xsize = (FieldWidth  - (menBorder.Checked ? 0 : 1)) * TILEX;
226   int full_ysize = (FieldHeight - (menBorder.Checked ? 0 : 1)) * TILEY;
227
228   sxsize = (full_xsize < xsize ? full_xsize : xsize);
229   sysize = (full_ysize < ysize ? full_ysize : ysize);
230   sx = SX + (full_xsize < xsize ? (xsize - full_xsize) / 2 : 0);
231   sy = SY + (full_ysize < ysize ? (ysize - full_ysize) / 2 : 0);
232
233   if (!menBorder.Checked)
234   {
235     px += TILEX / 2;
236     py += TILEY / 2;
237   }
238
239   BlitBitmap(screenBitmap, target_bitmap, px, py, sxsize, sysize, sx, sy);
240 }
241
242 void BackToFront_SP(void)
243 {
244   static boolean scrolling_last = FALSE;
245   int left = mScrollX / TILEX;
246   int top  = mScrollY / TILEY;
247   boolean scrolling = (mScrollX % TILEX != 0 || mScrollY % TILEY != 0);
248   int x, y;
249
250   SyncDisplay();
251
252   if (1 ||
253       redraw_tiles > REDRAWTILES_THRESHOLD || scrolling || scrolling_last)
254   {
255     BlitScreenToBitmap_SP(window);
256   }
257   else
258   {
259     for (x = 0; x < SCR_FIELDX; x++)
260     {
261       for (y = 0; y < SCR_FIELDY; y++)
262       {
263         int xx = (left + x) % MAX_BUF_XSIZE;
264         int yy = (top  + y) % MAX_BUF_YSIZE;
265
266         if (redraw[xx][yy])
267           BlitBitmap(screenBitmap, window,
268                      xx * TILEX, yy * TILEY, TILEX, TILEY,
269                      SX + x * TILEX, SY + y * TILEY);
270       }
271     }
272   }
273
274   FlushDisplay();
275
276   for (x = 0; x < MAX_BUF_XSIZE; x++)
277     for (y = 0; y < MAX_BUF_YSIZE; y++)
278       redraw[x][y] = FALSE;
279   redraw_tiles = 0;
280
281   scrolling_last = scrolling;
282 }
283
284 void DDScrollBuffer_Blt()
285 {
286   BackToFront_SP();
287 }
288
289 void DDScrollBuffer_ScrollTo(int X, int Y)
290 {
291   if (NoDisplayFlag)
292     return;
293
294   X = X / Stretch;
295   Y = Y / Stretch;
296   mScrollX = X;
297   mScrollY = Y;
298   ScrollX = mScrollX;
299   ScrollY = mScrollY;
300
301   ScrollPlayfieldIfNeeded();
302 }
303
304 void DDScrollBuffer_ScrollTowards(int X, int Y, double Step)
305 {
306   double dx, dY, r;
307
308   if (NoDisplayFlag)
309     return;
310
311   X = X / Stretch;
312   Y = Y / Stretch;
313   dx = X - mScrollX;
314   dY = Y - mScrollY;
315
316   r = Sqr(dx * dx + dY * dY);
317   if (r == 0)   // we are there already
318     return;
319
320   if (Step < r)
321     r = Step / r;
322   else
323     r = 1;
324
325   mScrollX = mScrollX + dx * r;
326   mScrollY = mScrollY + dY * r;
327   ScrollX = mScrollX;
328   ScrollY = mScrollY;
329
330   ScrollPlayfieldIfNeeded();
331 }
332
333 void DDScrollBuffer_SoftScrollTo(int X, int Y, long TimeMS, int FPS)
334 {
335   double dx, dY;
336   long dT, StepCount;
337   double T, tStep;
338   long oldX, oldY, maxD;
339   static boolean AlreadyRunning = False;
340
341   if (NoDisplayFlag)
342     return;
343
344   if (AlreadyRunning)
345     return;
346
347   AlreadyRunning = True;
348   X = X / Stretch;
349   Y = Y / Stretch;
350   dx = X - mScrollX;
351   dY = Y - mScrollY;
352   maxD = (Abs(dx) < Abs(dY) ? Abs(dY) : Abs(dx));
353
354   StepCount = FPS * (TimeMS / (double)1000);
355   if (StepCount > maxD)
356     StepCount = maxD;
357
358   if (StepCount == 0)
359     StepCount = 1;
360
361   dT = 1000 / FPS;
362   tStep = (double)1 / StepCount;
363   oldX = mScrollX;
364   oldY = mScrollY;
365
366   for (T = (double)tStep; T <= (double)1; T += tStep)
367   {
368     if (UserDragFlag)
369       goto SoftScrollEH;
370
371     mScrollX = oldX + T * dx;
372     mScrollY = oldY + T * dY;
373     ScrollX = mScrollX;
374     ScrollY = mScrollY;
375   }
376
377   if (UserDragFlag)
378     goto SoftScrollEH;
379
380   mScrollX = X;
381   mScrollY = Y;
382   ScrollX = mScrollX;
383   ScrollY = mScrollY;
384
385 SoftScrollEH:
386   AlreadyRunning = False;
387
388   ScrollPlayfieldIfNeeded();
389 }