rnd-20021227-2-src
[rocksndiamonds.git] / src / libgame / toons.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * toons.c                                                  *
12 ***********************************************************/
13
14 #include "toons.h"
15 #include "misc.h"
16
17
18 /* values for toon animation */
19 #define ANIM_START      0
20 #define ANIM_CONTINUE   1
21 #define ANIM_STOP       2
22
23
24 static struct ToonScreenInfo screen_info;
25
26
27 /* ========================================================================= */
28 /* generic animation frame calculation                                       */
29 /* ========================================================================= */
30
31 int getAnimationFrame(int num_frames, int delay, int mode, int start_frame,
32                       int sync_frame)
33 {
34   int frame = 0;
35
36   sync_frame += start_frame * delay;
37
38   if (mode & ANIM_LOOP)                 /* normal, looping animation */
39   {
40     frame = (sync_frame % (delay * num_frames)) / delay;
41   }
42   else if (mode & ANIM_LINEAR)          /* normal, non-looping animation */
43   {
44     frame = sync_frame / delay;
45
46     if (frame > num_frames - 1)
47       frame = num_frames - 1;
48   }
49   else if (mode & ANIM_PINGPONG)        /* use border frames once */
50   {
51     int max_anim_frames = 2 * num_frames - 2;
52
53     frame = (sync_frame % (delay * max_anim_frames)) / delay;
54     frame = (frame < num_frames ? frame : max_anim_frames - frame);
55   }
56   else if (mode & ANIM_PINGPONG2)       /* use border frames twice */
57   {
58     int max_anim_frames = 2 * num_frames;
59
60     frame = (sync_frame % (delay * max_anim_frames)) / delay;
61     frame = (frame < num_frames ? frame : max_anim_frames - frame - 1);
62   }
63
64   if (mode & ANIM_REVERSE)              /* use reverse animation direction */
65     frame = num_frames - frame - 1;
66
67   return frame;
68 }
69
70
71 /* ========================================================================= */
72 /* toon animation functions                                                  */
73 /* ========================================================================= */
74
75 void InitToonScreen(Bitmap *save_buffer,
76                     void (*update_function)(void),
77                     void (*prepare_backbuffer_function)(void),
78                     boolean (*redraw_needed_function)(void),
79                     struct ToonInfo *toons, int num_toons,
80                     int startx, int starty,
81                     int width, int height,
82                     int frame_delay_value)
83 {
84   screen_info.save_buffer = save_buffer;
85   screen_info.update_function = update_function;
86   screen_info.prepare_backbuffer_function = prepare_backbuffer_function;
87   screen_info.redraw_needed_function = redraw_needed_function;
88   screen_info.toons = toons;
89   screen_info.num_toons = num_toons;
90   screen_info.startx = startx;
91   screen_info.starty = starty;
92   screen_info.width = width;
93   screen_info.height = height;
94   screen_info.frame_delay_value = frame_delay_value;
95 }
96
97 void DrawAnim(Bitmap *toon_bitmap, GC toon_clip_gc,
98               int src_x, int src_y, int width, int height,
99               int dest_x, int dest_y, int pad_x, int pad_y)
100 {
101   int buf_x = DOOR_GFX_PAGEX3, buf_y = DOOR_GFX_PAGEY1;
102
103 #if 1
104   /* special method to avoid flickering interference with BackToFront() */
105   BlitBitmap(backbuffer, screen_info.save_buffer, dest_x-pad_x, dest_y-pad_y,
106              width+2*pad_x, height+2*pad_y, buf_x, buf_y);
107   SetClipOrigin(toon_bitmap, toon_clip_gc, dest_x-src_x, dest_y-src_y);
108   BlitBitmapMasked(toon_bitmap, backbuffer,
109                    src_x, src_y, width, height, dest_x, dest_y);
110   BlitBitmap(backbuffer, window, dest_x-pad_x, dest_y-pad_y,
111              width+2*pad_x, height+2*pad_y, dest_x-pad_x, dest_y-pad_y);
112
113   screen_info.update_function();
114
115   BlitBitmap(screen_info.save_buffer, backbuffer, buf_x, buf_y,
116             width+2*pad_x, height+2*pad_y, dest_x-pad_x, dest_y-pad_y);
117 #else
118   /* normal method, causing flickering interference with BackToFront() */
119   BlitBitmap(backbuffer, screen_info.save_buffer, dest_x-pad_x, dest_y-pad_y,
120              width+2*pad_x, height+2*pad_y, buf_x, buf_y);
121   SetClipOrigin(toon_bitmap,toon_clip_gc, buf_x-src_x+pad_x,buf_y-src_y+pad_y);
122   BlitBitmapMasked(toon_bitmap, screen_info.save_buffer,
123                    src_x, src_y, width, height, buf_x+pad_x, buf_y+pad_y);
124   BlitBitmap(screen_info.save_buffer, window, buf_x, buf_y,
125              width+2*pad_x, height+2*pad_y, dest_x-pad_x, dest_y-pad_y);
126 #endif
127
128   FlushDisplay();
129 }
130
131 boolean AnimateToon(int toon_nr, boolean restart)
132 {
133   static unsigned long animation_frame_counter = 0;
134   static int pos_x = 0, pos_y = 0;
135   static int delta_x = 0, delta_y = 0;
136   static int frame = 0;
137   static boolean horiz_move, vert_move;
138   static unsigned long anim_delay = 0;
139   static unsigned long anim_delay_value = 0;
140   static int width,height;
141   static int pad_x,pad_y;
142   static int cut_x,cut_y;
143   static int src_x, src_y;
144   static int dest_x, dest_y;
145   struct ToonInfo *anim = &screen_info.toons[toon_nr];
146   Bitmap *anim_bitmap = screen_info.toons[toon_nr].bitmap;
147   GC anim_clip_gc = anim_bitmap->stored_clip_gc;
148
149   if (restart)
150   {
151     horiz_move = (anim->direction & (ANIMDIR_LEFT | ANIMDIR_RIGHT));
152     vert_move = (anim->direction & (ANIMDIR_UP | ANIMDIR_DOWN));
153     anim_delay_value = anim->move_delay * screen_info.frame_delay_value;
154
155     frame = getAnimationFrame(anim->anim_frames, anim->anim_delay,
156                               anim->anim_mode, anim->start_frame,
157                               animation_frame_counter++);
158
159     if (horiz_move)
160     {
161       if (anim->position == ANIMPOS_UP)
162         pos_y = 0;
163       else if (anim->position == ANIMPOS_DOWN)
164         pos_y = screen_info.height - anim->height;
165       else if (anim->position == ANIMPOS_UPPER)
166         pos_y = SimpleRND((screen_info.height - anim->height) / 2);
167       else
168         pos_y = SimpleRND(screen_info.height - anim->height);
169
170       if (anim->direction == ANIMDIR_RIGHT)
171       {
172         delta_x = anim->stepsize;
173         pos_x = -anim->width + delta_x;
174       }
175       else
176       {
177         delta_x = -anim->stepsize;
178         pos_x = screen_info.width + delta_x;
179       }
180       delta_y = 0;
181     }
182     else
183     {
184       if (anim->position == ANIMPOS_LEFT)
185         pos_x = 0;
186       else if (anim->position == ANIMPOS_RIGHT)
187         pos_x = screen_info.width - anim->width;
188       else
189         pos_x = SimpleRND(screen_info.width - anim->width);
190
191       if (anim->direction == ANIMDIR_DOWN)
192       {
193         delta_y = anim->stepsize;
194         pos_y = -anim->height + delta_y;
195       }
196       else
197       {
198         delta_y = -anim->stepsize;
199         pos_y = screen_info.width + delta_y;
200       }
201       delta_x = 0;
202     }
203   }
204
205   if (pos_x <= -anim->width        - anim->stepsize ||
206       pos_x >=  screen_info.width  + anim->stepsize ||
207       pos_y <= -anim->height       - anim->stepsize ||
208       pos_y >=  screen_info.height + anim->stepsize)
209     return TRUE;
210
211   if (!DelayReached(&anim_delay, anim_delay_value))
212   {
213     if (screen_info.redraw_needed_function() && !restart)
214       DrawAnim(anim_bitmap, anim_clip_gc,
215                src_x + cut_x, src_y + cut_y,
216                width, height,
217                screen_info.startx + dest_x,
218                screen_info.starty + dest_y,
219                pad_x, pad_y);
220
221     return FALSE;
222   }
223
224   if (pos_x < -anim->width)
225     pos_x = -anim->width;
226   else if (pos_x > screen_info.width)
227     pos_x = screen_info.width;
228   if (pos_y < -anim->height)
229     pos_y = -anim->height;
230   else if (pos_y > screen_info.height)
231     pos_y = screen_info.height;
232
233   pad_x = (horiz_move ? anim->stepsize : 0);
234   pad_y = (vert_move  ? anim->stepsize : 0);
235   src_x = anim->src_x + frame * anim->width;
236   src_y = anim->src_y;
237   dest_x = pos_x;
238   dest_y = pos_y;
239   cut_x = cut_y = 0;
240   width  = anim->width;
241   height = anim->height;
242
243   if (pos_x < 0)
244   {
245     dest_x = 0;
246     width += pos_x;
247     cut_x = -pos_x;
248   }
249   else if (pos_x > screen_info.width - anim->width)
250     width -= (pos_x - (screen_info.width - anim->width));
251
252   if (pos_y < 0)
253   {
254     dest_y = 0;
255     height += pos_y;
256     cut_y = -pos_y;
257   }
258   else if (pos_y > screen_info.height - anim->height)
259     height -= (pos_y - (screen_info.height - anim->height));
260
261   DrawAnim(anim_bitmap, anim_clip_gc,
262            src_x + cut_x, src_y + cut_y,
263            width, height,
264            screen_info.startx + dest_x,
265            screen_info.starty + dest_y,
266            pad_x, pad_y);
267
268   pos_x += delta_x;
269   pos_y += delta_y;
270
271   frame = getAnimationFrame(anim->anim_frames, anim->anim_delay,
272                             anim->anim_mode, anim->start_frame,
273                             animation_frame_counter++);
274
275   return FALSE;
276 }
277
278 void HandleAnimation(int mode)
279 {
280   static unsigned long animstart_delay = -1;
281   static unsigned long animstart_delay_value = 0;
282   static boolean anim_restart = TRUE;
283   static boolean reset_delay = TRUE;
284   static int toon_nr = 0;
285   int draw_mode;
286
287   if (!setup.toons)
288     return;
289
290   switch(mode)
291   {
292     case ANIM_START:
293       screen_info.prepare_backbuffer_function();
294       anim_restart = TRUE;
295       reset_delay = TRUE;
296
297       return;
298
299     case ANIM_CONTINUE:
300       break;
301
302     case ANIM_STOP:
303       redraw_mask |= (REDRAW_FIELD | REDRAW_FROM_BACKBUFFER);
304
305       /* Redraw background even when in direct drawing mode */
306       draw_mode = setup.direct_draw;
307       setup.direct_draw = FALSE;
308       screen_info.update_function();
309       setup.direct_draw = draw_mode;
310
311       return;
312
313     default:
314       break;
315   }
316
317   if (reset_delay)
318   {
319     animstart_delay = Counter();
320     animstart_delay_value = SimpleRND(3000);
321     reset_delay = FALSE;
322   }
323
324   if (anim_restart)
325   {
326     if (!DelayReached(&animstart_delay, animstart_delay_value))
327       return;
328
329     toon_nr = SimpleRND(screen_info.num_toons);
330   }
331
332   anim_restart = reset_delay = AnimateToon(toon_nr,anim_restart);
333 }
334
335 void InitAnimation()
336 {
337   HandleAnimation(ANIM_START);
338 }
339
340 void StopAnimation()
341 {
342   HandleAnimation(ANIM_STOP);
343 }
344
345 void DoAnimation()
346 {
347   HandleAnimation(ANIM_CONTINUE);
348 }