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