improved customizability of game controller configuration screen
[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 int ScreenBuffer[2 + MAX_PLAYFIELD_WIDTH + 2][2 + MAX_PLAYFIELD_HEIGHT + 2];
14
15
16 int getFieldbufferOffsetX_SP()
17 {
18   int px = 2 * TILEX + (mScrollX - mScrollX_last) % TILEX;
19
20   /* scroll correction for even number of visible tiles (half tile shifted) */
21   px += game_sp.scroll_xoffset;
22
23   if (ExplosionShakeMurphy != 0)
24     px += TILEX / 2 - GetSimpleRandom(TILEX + 1);
25
26   px = px * TILESIZE_VAR / TILESIZE;
27
28   return px;
29 }
30
31 int getFieldbufferOffsetY_SP()
32 {
33   int py = 2 * TILEY + (mScrollY - mScrollY_last) % TILEY;
34
35   /* scroll correction for even number of visible tiles (half tile shifted) */
36   py += game_sp.scroll_yoffset;
37
38   if (ExplosionShakeMurphy != 0)
39     py += TILEY / 2 - GetSimpleRandom(TILEX + 1);
40
41   py = py * TILESIZE_VAR / TILESIZE;
42
43   return py;
44 }
45
46 void RestorePlayfield()
47 {
48   int x1 = mScrollX / TILEX - 2;
49   int y1 = mScrollY / TILEY - 2;
50   int x2 = mScrollX / TILEX + (SCR_FIELDX - 1) + 2;
51   int y2 = mScrollY / TILEY + (SCR_FIELDY - 1) + 2;
52   int x, y;
53
54   DrawFrameIfNeeded();
55
56   for (y = DisplayMinY; y <= DisplayMaxY; y++)
57   {
58     for (x = DisplayMinX; x <= DisplayMaxX; x++)
59     {
60       if (x >= x1 && x <= x2 && y >= y1 && y <= y2)
61       {
62         DrawFieldNoAnimated(x, y);
63         DrawFieldAnimated(x, y);
64       }
65     }
66   }
67 }
68
69 static void ScrollPlayfield(int dx, int dy)
70 {
71   int x1 = mScrollX_last / TILEX - 2;
72   int y1 = mScrollY_last / TILEY - 2;
73   int x2 = mScrollX_last / TILEX + (SCR_FIELDX - 1) + 2;
74   int y2 = mScrollY_last / TILEY + (SCR_FIELDY - 1) + 2;
75   int x, y;
76
77   BlitBitmap(bitmap_db_field_sp, bitmap_db_field_sp,
78              TILEX_VAR * (dx == -1),
79              TILEY_VAR * (dy == -1),
80              (MAX_BUF_XSIZE * TILEX_VAR) - TILEX_VAR * (dx != 0),
81              (MAX_BUF_YSIZE * TILEY_VAR) - TILEY_VAR * (dy != 0),
82              TILEX_VAR * (dx == 1),
83              TILEY_VAR * (dy == 1));
84
85   DrawFrameIfNeeded();
86
87   for (y = DisplayMinY; y <= DisplayMaxY; y++)
88   {
89     for (x = DisplayMinX; x <= DisplayMaxX; x++)
90     {
91       if (x >= x1 && x <= x2 && y >= y1 && y <= y2)
92       {
93         int sx = x - x1;
94         int sy = y - y1;
95         int tsi = GetSI(x, y);
96         int id = ((PlayField16[tsi]) |
97                   (PlayField8[tsi] << 16) |
98                   (DisPlayField[tsi] << 24));
99
100         if ((dx == -1 && x == x2) ||
101             (dx == +1 && x == x1) ||
102             (dy == -1 && y == y2) ||
103             (dy == +1 && y == y1))
104         {
105           DrawFieldNoAnimated(x, y);
106           DrawFieldAnimated(x, y);
107         }
108
109         ScreenBuffer[sx][sy] = id;
110       }
111     }
112   }
113 }
114
115 static void ScrollPlayfieldIfNeededExt(boolean reset)
116 {
117   if (reset)
118   {
119     mScrollX_last = -1;
120     mScrollY_last = -1;
121
122     return;
123   }
124
125   if (mScrollX_last == -1 || mScrollY_last == -1)
126   {
127     mScrollX_last = (mScrollX / TILESIZE) * TILESIZE;
128     mScrollY_last = (mScrollY / TILESIZE) * TILESIZE;
129
130     return;
131   }
132
133   /* check if scrolling the playfield requires redrawing the viewport bitmap */
134   if ((mScrollX != mScrollX_last ||
135        mScrollY != mScrollY_last) &&
136       (ABS(mScrollX - mScrollX_last) >= TILEX ||
137        ABS(mScrollY - mScrollY_last) >= TILEY))
138   {
139     int dx = (ABS(mScrollX - mScrollX_last) < TILEX ? 0 :
140               mScrollX < mScrollX_last ? 1 : mScrollX > mScrollX_last ? -1 : 0);
141     int dy = (ABS(mScrollY - mScrollY_last) < TILEY ? 0 :
142               mScrollY < mScrollY_last ? 1 : mScrollY > mScrollY_last ? -1 : 0);
143
144     mScrollX_last -= dx * TILEX;
145     mScrollY_last -= dy * TILEY;
146
147     ScrollPlayfield(dx, dy);
148   }
149 }
150
151 static void ScrollPlayfieldIfNeeded()
152 {
153   ScrollPlayfieldIfNeededExt(FALSE);
154 }
155
156 void InitScrollPlayfield()
157 {
158   ScrollPlayfieldIfNeededExt(TRUE);
159 }
160
161 #define DEBUG_REDRAW    0
162
163 void UpdatePlayfield(boolean force_redraw)
164 {
165   int x, y;
166
167 #if DEBUG_REDRAW
168   int num_redrawn = 0;
169 #endif
170
171   for (y = DisplayMinY; y <= DisplayMaxY; y++)
172   {
173     for (x = DisplayMinX; x <= DisplayMaxX; x++)
174     {
175       int element = LowByte(PlayField16[GetSI(x, y)]);
176       int graphic = GfxGraphic[x][y];
177       int sync_frame = GfxFrame[x][y];
178       boolean redraw = force_redraw;
179
180       if (graphic < 0)
181       {
182         GfxGraphicLast[x][y] = GfxGraphic[x][y];
183
184         continue;
185       }
186
187       if (element != GfxElementLast[x][y] &&
188           graphic == GfxGraphicLast[x][y])
189       {
190         /* element changed, but not graphic => disable updating graphic */
191
192         GfxElementLast[x][y] = element;
193         GfxGraphicLast[x][y] = GfxGraphic[x][y] = -1;
194
195         continue;
196       }
197
198       if (graphic != GfxGraphicLast[x][y])                      // new graphic
199       {
200         redraw = TRUE;
201
202         GfxElementLast[x][y] = element;
203         GfxGraphicLast[x][y] = GfxGraphic[x][y];
204         sync_frame = GfxFrame[x][y] = 0;
205       }
206       else if (isNextAnimationFrame_SP(graphic, sync_frame))    // new frame
207       {
208         redraw = TRUE;
209       }
210
211       if (redraw)
212       {
213         int sx = x * StretchWidth;
214         int sy = y * StretchWidth;
215
216         DDSpriteBuffer_BltImg(sx, sy, graphic, sync_frame);
217
218 #if DEBUG_REDRAW
219         num_redrawn++;
220 #endif
221       }
222     }
223   }
224
225 #if DEBUG_REDRAW
226   printf("::: FRAME %d: %d redrawn\n", FrameCounter, num_redrawn);
227 #endif
228 }
229
230 void BlitScreenToBitmap_SP(Bitmap *target_bitmap)
231 {
232   /* copy playfield buffer to target bitmap at scroll position */
233
234   int px = getFieldbufferOffsetX_SP();
235   int py = getFieldbufferOffsetY_SP();
236   int xsize = SXSIZE;
237   int ysize = SYSIZE;
238   int full_xsize = (FieldWidth  - (menBorder ? 0 : 1)) * TILEX_VAR;
239   int full_ysize = (FieldHeight - (menBorder ? 0 : 1)) * TILEY_VAR;
240   int sx = SX + (full_xsize < xsize ? (xsize - full_xsize) / 2 : 0);
241   int sy = SY + (full_ysize < ysize ? (ysize - full_ysize) / 2 : 0);
242   int sxsize = (full_xsize < xsize ? full_xsize : xsize);
243   int sysize = (full_ysize < ysize ? full_ysize : ysize);
244
245   BlitBitmap(bitmap_db_field_sp, target_bitmap, px, py, sxsize, sysize, sx, sy);
246 }
247
248 void DDScrollBuffer_ScrollTo(int X, int Y)
249 {
250   if (NoDisplayFlag)
251     return;
252
253   ScrollX = mScrollX = X;
254   ScrollY = mScrollY = Y;
255
256   ScrollPlayfieldIfNeeded();
257 }
258
259 void DDScrollBuffer_ScrollTowards(int X, int Y, double Step)
260 {
261   double dx, dY, r;
262
263   if (NoDisplayFlag)
264     return;
265
266   dx = X - mScrollX;
267   dY = Y - mScrollY;
268
269   r = Sqr(dx * dx + dY * dY);
270   if (r == 0)   // we are there already
271     return;
272
273   if (Step < r)
274     r = Step / r;
275   else
276     r = 1;
277
278   ScrollX = mScrollX = mScrollX + dx * r;
279   ScrollY = mScrollY = mScrollY + dY * r;
280
281   ScrollPlayfieldIfNeeded();
282 }
283
284 void DDScrollBuffer_SoftScrollTo(int X, int Y, int TimeMS, int FPS)
285 {
286   double dx, dY;
287   int StepCount;
288   double T, tStep;
289   int oldX, oldY, maxD;
290   static boolean AlreadyRunning = False;
291
292   if (NoDisplayFlag)
293     return;
294
295   if (AlreadyRunning)
296     return;
297
298   AlreadyRunning = True;
299
300   dx = X - mScrollX;
301   dY = Y - mScrollY;
302   maxD = (Abs(dx) < Abs(dY) ? Abs(dY) : Abs(dx));
303
304   StepCount = FPS * (TimeMS / (double)1000);
305   if (StepCount > maxD)
306     StepCount = maxD;
307
308   if (StepCount == 0)
309     StepCount = 1;
310
311   tStep = (double)1 / StepCount;
312   oldX = mScrollX;
313   oldY = mScrollY;
314
315   for (T = (double)tStep; T <= (double)1; T += tStep)
316   {
317     ScrollX = mScrollX = oldX + T * dx;
318     ScrollY = mScrollY = oldY + T * dY;
319   }
320
321   ScrollX = mScrollX = X;
322   ScrollY = mScrollY = Y;
323
324   AlreadyRunning = False;
325
326   ScrollPlayfieldIfNeeded();
327 }