rnd-20030111-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)                 /* looping animation */
39   {
40     frame = (sync_frame % (delay * num_frames)) / delay;
41   }
42   else if (mode & ANIM_LINEAR)          /* linear (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)        /* oscillate (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)       /* oscillate (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   else if (mode & ANIM_RANDOM)          /* play frames in random order */
64   {
65     /* note: expect different frames for the same delay cycle! */
66
67     frame = SimpleRND(num_frames);
68   }
69
70   if (mode & ANIM_REVERSE)              /* use reverse animation direction */
71     frame = num_frames - frame - 1;
72
73   return frame;
74 }
75
76
77 /* ========================================================================= */
78 /* toon animation functions                                                  */
79 /* ========================================================================= */
80
81 void InitToonScreen(Bitmap *save_buffer,
82                     void (*update_function)(void),
83                     void (*prepare_backbuffer_function)(void),
84                     boolean (*redraw_needed_function)(void),
85                     struct ToonInfo *toons, int num_toons,
86                     int startx, int starty,
87                     int width, int height,
88                     int frame_delay_value)
89 {
90   screen_info.save_buffer = save_buffer;
91   screen_info.update_function = update_function;
92   screen_info.prepare_backbuffer_function = prepare_backbuffer_function;
93   screen_info.redraw_needed_function = redraw_needed_function;
94   screen_info.toons = toons;
95   screen_info.num_toons = num_toons;
96   screen_info.startx = startx;
97   screen_info.starty = starty;
98   screen_info.width = width;
99   screen_info.height = height;
100   screen_info.frame_delay_value = frame_delay_value;
101 }
102
103 void DrawAnim(Bitmap *toon_bitmap, GC toon_clip_gc,
104               int src_x, int src_y, int width, int height,
105               int dest_x, int dest_y, int pad_x, int pad_y)
106 {
107   int buf_x = DOOR_GFX_PAGEX3, buf_y = DOOR_GFX_PAGEY1;
108
109 #if 1
110   /* special method to avoid flickering interference with BackToFront() */
111   BlitBitmap(backbuffer, screen_info.save_buffer, dest_x-pad_x, dest_y-pad_y,
112              width+2*pad_x, height+2*pad_y, buf_x, buf_y);
113   SetClipOrigin(toon_bitmap, toon_clip_gc, dest_x-src_x, dest_y-src_y);
114   BlitBitmapMasked(toon_bitmap, backbuffer,
115                    src_x, src_y, width, height, dest_x, dest_y);
116   BlitBitmap(backbuffer, window, dest_x-pad_x, dest_y-pad_y,
117              width+2*pad_x, height+2*pad_y, dest_x-pad_x, dest_y-pad_y);
118
119   screen_info.update_function();
120
121   BlitBitmap(screen_info.save_buffer, backbuffer, buf_x, buf_y,
122             width+2*pad_x, height+2*pad_y, dest_x-pad_x, dest_y-pad_y);
123 #else
124   /* normal method, causing flickering interference with BackToFront() */
125   BlitBitmap(backbuffer, screen_info.save_buffer, dest_x-pad_x, dest_y-pad_y,
126              width+2*pad_x, height+2*pad_y, buf_x, buf_y);
127   SetClipOrigin(toon_bitmap,toon_clip_gc, buf_x-src_x+pad_x,buf_y-src_y+pad_y);
128   BlitBitmapMasked(toon_bitmap, screen_info.save_buffer,
129                    src_x, src_y, width, height, buf_x+pad_x, buf_y+pad_y);
130   BlitBitmap(screen_info.save_buffer, window, buf_x, buf_y,
131              width+2*pad_x, height+2*pad_y, dest_x-pad_x, dest_y-pad_y);
132 #endif
133
134   FlushDisplay();
135 }
136
137 boolean AnimateToon(int toon_nr, boolean restart)
138 {
139   static unsigned long animation_frame_counter = 0;
140   static int pos_x = 0, pos_y = 0;
141   static int delta_x = 0, delta_y = 0;
142   static int frame = 0;
143   static boolean horiz_move, vert_move;
144   static unsigned long anim_delay = 0;
145   static unsigned long anim_delay_value = 0;
146   static int width,height;
147   static int pad_x,pad_y;
148   static int cut_x,cut_y;
149   static int src_x, src_y;
150   static int dest_x, dest_y;
151   struct ToonInfo *anim = &screen_info.toons[toon_nr];
152   Bitmap *anim_bitmap = screen_info.toons[toon_nr].bitmap;
153   GC anim_clip_gc = anim_bitmap->stored_clip_gc;
154
155   if (restart)
156   {
157     horiz_move = (anim->direction & (ANIMDIR_LEFT | ANIMDIR_RIGHT));
158     vert_move = (anim->direction & (ANIMDIR_UP | ANIMDIR_DOWN));
159     anim_delay_value = anim->move_delay * screen_info.frame_delay_value;
160
161     frame = getAnimationFrame(anim->anim_frames, anim->anim_delay,
162                               anim->anim_mode, anim->start_frame,
163                               animation_frame_counter++);
164
165     if (horiz_move)
166     {
167       if (anim->position == ANIMPOS_UP)
168         pos_y = 0;
169       else if (anim->position == ANIMPOS_DOWN)
170         pos_y = screen_info.height - anim->height;
171       else if (anim->position == ANIMPOS_UPPER)
172         pos_y = SimpleRND((screen_info.height - anim->height) / 2);
173       else
174         pos_y = SimpleRND(screen_info.height - anim->height);
175
176       if (anim->direction == ANIMDIR_RIGHT)
177       {
178         delta_x = anim->stepsize;
179         pos_x = -anim->width + delta_x;
180       }
181       else
182       {
183         delta_x = -anim->stepsize;
184         pos_x = screen_info.width + delta_x;
185       }
186       delta_y = 0;
187     }
188     else
189     {
190       if (anim->position == ANIMPOS_LEFT)
191         pos_x = 0;
192       else if (anim->position == ANIMPOS_RIGHT)
193         pos_x = screen_info.width - anim->width;
194       else
195         pos_x = SimpleRND(screen_info.width - anim->width);
196
197       if (anim->direction == ANIMDIR_DOWN)
198       {
199         delta_y = anim->stepsize;
200         pos_y = -anim->height + delta_y;
201       }
202       else
203       {
204         delta_y = -anim->stepsize;
205         pos_y = screen_info.width + delta_y;
206       }
207       delta_x = 0;
208     }
209   }
210
211   if (pos_x <= -anim->width        - anim->stepsize ||
212       pos_x >=  screen_info.width  + anim->stepsize ||
213       pos_y <= -anim->height       - anim->stepsize ||
214       pos_y >=  screen_info.height + anim->stepsize)
215     return TRUE;
216
217   if (!DelayReached(&anim_delay, anim_delay_value))
218   {
219     if (screen_info.redraw_needed_function() && !restart)
220       DrawAnim(anim_bitmap, anim_clip_gc,
221                src_x + cut_x, src_y + cut_y,
222                width, height,
223                screen_info.startx + dest_x,
224                screen_info.starty + dest_y,
225                pad_x, pad_y);
226
227     return FALSE;
228   }
229
230   if (pos_x < -anim->width)
231     pos_x = -anim->width;
232   else if (pos_x > screen_info.width)
233     pos_x = screen_info.width;
234   if (pos_y < -anim->height)
235     pos_y = -anim->height;
236   else if (pos_y > screen_info.height)
237     pos_y = screen_info.height;
238
239   pad_x = (horiz_move ? anim->stepsize : 0);
240   pad_y = (vert_move  ? anim->stepsize : 0);
241   src_x = anim->src_x + frame * anim->width;
242   src_y = anim->src_y;
243   dest_x = pos_x;
244   dest_y = pos_y;
245   cut_x = cut_y = 0;
246   width  = anim->width;
247   height = anim->height;
248
249   if (pos_x < 0)
250   {
251     dest_x = 0;
252     width += pos_x;
253     cut_x = -pos_x;
254   }
255   else if (pos_x > screen_info.width - anim->width)
256     width -= (pos_x - (screen_info.width - anim->width));
257
258   if (pos_y < 0)
259   {
260     dest_y = 0;
261     height += pos_y;
262     cut_y = -pos_y;
263   }
264   else if (pos_y > screen_info.height - anim->height)
265     height -= (pos_y - (screen_info.height - anim->height));
266
267   DrawAnim(anim_bitmap, anim_clip_gc,
268            src_x + cut_x, src_y + cut_y,
269            width, height,
270            screen_info.startx + dest_x,
271            screen_info.starty + dest_y,
272            pad_x, pad_y);
273
274   pos_x += delta_x;
275   pos_y += delta_y;
276
277   frame = getAnimationFrame(anim->anim_frames, anim->anim_delay,
278                             anim->anim_mode, anim->start_frame,
279                             animation_frame_counter++);
280
281   return FALSE;
282 }
283
284 void HandleAnimation(int mode)
285 {
286   static unsigned long animstart_delay = -1;
287   static unsigned long animstart_delay_value = 0;
288   static boolean anim_restart = TRUE;
289   static boolean reset_delay = TRUE;
290   static int toon_nr = 0;
291   int draw_mode;
292
293   if (!setup.toons)
294     return;
295
296   switch(mode)
297   {
298     case ANIM_START:
299       screen_info.prepare_backbuffer_function();
300       anim_restart = TRUE;
301       reset_delay = TRUE;
302
303       return;
304
305     case ANIM_CONTINUE:
306       break;
307
308     case ANIM_STOP:
309       redraw_mask |= (REDRAW_FIELD | REDRAW_FROM_BACKBUFFER);
310
311       /* Redraw background even when in direct drawing mode */
312       draw_mode = setup.direct_draw;
313       setup.direct_draw = FALSE;
314       screen_info.update_function();
315       setup.direct_draw = draw_mode;
316
317       return;
318
319     default:
320       break;
321   }
322
323   if (reset_delay)
324   {
325     animstart_delay = Counter();
326     animstart_delay_value = SimpleRND(3000);
327     reset_delay = FALSE;
328   }
329
330   if (anim_restart)
331   {
332     if (!DelayReached(&animstart_delay, animstart_delay_value))
333       return;
334
335     toon_nr = SimpleRND(screen_info.num_toons);
336   }
337
338   anim_restart = reset_delay = AnimateToon(toon_nr,anim_restart);
339 }
340
341 void InitAnimation()
342 {
343   HandleAnimation(ANIM_START);
344 }
345
346 void StopAnimation()
347 {
348   HandleAnimation(ANIM_STOP);
349 }
350
351 void DoAnimation()
352 {
353   HandleAnimation(ANIM_CONTINUE);
354 }