rnd-20040505-1-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 #if 1
52     int max_anim_frames = (num_frames > 1 ? 2 * num_frames - 2 : 1);
53 #else
54     int max_anim_frames = 2 * num_frames - 2;
55 #endif
56
57     frame = (sync_frame % (delay * max_anim_frames)) / delay;
58     frame = (frame < num_frames ? frame : max_anim_frames - frame);
59   }
60   else if (mode & ANIM_PINGPONG2)       /* oscillate (border frames twice) */
61   {
62     int max_anim_frames = 2 * num_frames;
63
64     frame = (sync_frame % (delay * max_anim_frames)) / delay;
65     frame = (frame < num_frames ? frame : max_anim_frames - frame - 1);
66   }
67   else if (mode & ANIM_RANDOM)          /* play frames in random order */
68   {
69     /* note: expect different frames for the same delay cycle! */
70
71     if (gfx.anim_random_frame < 0)
72       frame = SimpleRND(num_frames);
73     else
74       frame = gfx.anim_random_frame % num_frames;
75   }
76
77   if (mode & ANIM_REVERSE)              /* use reverse animation direction */
78     frame = num_frames - frame - 1;
79
80   return frame;
81 }
82
83
84 /* ========================================================================= */
85 /* toon animation functions                                                  */
86 /* ========================================================================= */
87
88 static int get_toon_direction(char *direction_string_raw)
89 {
90   char *direction_string = getStringToLower(direction_string_raw);
91   int direction = (strcmp(direction_string, "left")  == 0 ? MV_LEFT :
92                    strcmp(direction_string, "right") == 0 ? MV_RIGHT :
93                    strcmp(direction_string, "up")    == 0 ? MV_UP :
94                    strcmp(direction_string, "down")  == 0 ? MV_DOWN :
95                    MV_NO_MOVING);
96
97   free(direction_string);
98
99   return direction;
100 }
101
102 void InitToonScreen(Bitmap *save_buffer,
103                     void (*update_function)(void),
104                     void (*prepare_backbuffer_function)(void),
105                     boolean (*redraw_needed_function)(void),
106                     struct ToonInfo *toons, int num_toons,
107                     int startx, int starty,
108                     int width, int height,
109                     int frame_delay_value)
110 {
111   screen_info.save_buffer = save_buffer;
112   screen_info.update_function = update_function;
113   screen_info.prepare_backbuffer_function = prepare_backbuffer_function;
114   screen_info.redraw_needed_function = redraw_needed_function;
115   screen_info.toons = toons;
116   screen_info.num_toons = num_toons;
117   screen_info.startx = startx;
118   screen_info.starty = starty;
119   screen_info.width = width;
120   screen_info.height = height;
121   screen_info.frame_delay_value = frame_delay_value;
122 }
123
124 void DrawAnim(Bitmap *toon_bitmap, GC toon_clip_gc,
125               int src_x, int src_y, int width, int height,
126               int dest_x, int dest_y, int pad_x, int pad_y)
127 {
128   int buf_x = DOOR_GFX_PAGEX3, buf_y = DOOR_GFX_PAGEY1;
129
130 #if 1
131   /* special method to avoid flickering interference with BackToFront() */
132   BlitBitmap(backbuffer, screen_info.save_buffer, dest_x-pad_x, dest_y-pad_y,
133              width+2*pad_x, height+2*pad_y, buf_x, buf_y);
134   SetClipOrigin(toon_bitmap, toon_clip_gc, dest_x-src_x, dest_y-src_y);
135   BlitBitmapMasked(toon_bitmap, backbuffer,
136                    src_x, src_y, width, height, dest_x, dest_y);
137   BlitBitmap(backbuffer, window, dest_x-pad_x, dest_y-pad_y,
138              width+2*pad_x, height+2*pad_y, dest_x-pad_x, dest_y-pad_y);
139
140   screen_info.update_function();
141
142   BlitBitmap(screen_info.save_buffer, backbuffer, buf_x, buf_y,
143             width+2*pad_x, height+2*pad_y, dest_x-pad_x, dest_y-pad_y);
144 #else
145   /* normal method, causing flickering interference with BackToFront() */
146   BlitBitmap(backbuffer, screen_info.save_buffer, dest_x-pad_x, dest_y-pad_y,
147              width+2*pad_x, height+2*pad_y, buf_x, buf_y);
148   SetClipOrigin(toon_bitmap,toon_clip_gc, buf_x-src_x+pad_x,buf_y-src_y+pad_y);
149   BlitBitmapMasked(toon_bitmap, screen_info.save_buffer,
150                    src_x, src_y, width, height, buf_x+pad_x, buf_y+pad_y);
151   BlitBitmap(screen_info.save_buffer, window, buf_x, buf_y,
152              width+2*pad_x, height+2*pad_y, dest_x-pad_x, dest_y-pad_y);
153 #endif
154
155   FlushDisplay();
156 }
157
158 boolean AnimateToon(int toon_nr, boolean restart)
159 {
160   static unsigned long animation_frame_counter = 0;
161   static int pos_x = 0, pos_y = 0;
162   static int delta_x = 0, delta_y = 0;
163   static int frame = 0;
164   static boolean horiz_move, vert_move;
165   static unsigned long anim_delay = 0;
166   static unsigned long anim_delay_value = 0;
167   static int width,height;
168   static int pad_x,pad_y;
169   static int cut_x,cut_y;
170   static int src_x, src_y;
171   static int dest_x, dest_y;
172   struct ToonInfo *anim = &screen_info.toons[toon_nr];
173   Bitmap *anim_bitmap = screen_info.toons[toon_nr].bitmap;
174   GC anim_clip_gc = anim_bitmap->stored_clip_gc;
175   int direction = get_toon_direction(anim->direction);
176
177   if (restart)
178   {
179     horiz_move = (direction & (MV_LEFT | MV_RIGHT));
180     vert_move = (direction & (MV_UP | MV_DOWN));
181     anim_delay_value = anim->step_delay * screen_info.frame_delay_value;
182
183     frame = getAnimationFrame(anim->anim_frames, anim->anim_delay,
184                               anim->anim_mode, anim->anim_start_frame,
185                               animation_frame_counter++);
186
187     if (horiz_move)
188     {
189       int pos_bottom = screen_info.height - anim->height;
190
191       if (strcmp(anim->position, "top") == 0)
192         pos_y = 0;
193       else if (strcmp(anim->position, "bottom") == 0)
194         pos_y = pos_bottom;
195       else if (strcmp(anim->position, "upper")  == 0)
196         pos_y = SimpleRND(pos_bottom / 2);
197       else if (strcmp(anim->position, "lower")  == 0)
198         pos_y = pos_bottom / 2 + SimpleRND(pos_bottom / 2);
199       else
200         pos_y = SimpleRND(pos_bottom);
201
202       if (direction == MV_RIGHT)
203       {
204         delta_x = anim->step_offset;
205         pos_x = -anim->width + delta_x;
206       }
207       else
208       {
209         delta_x = -anim->step_offset;
210         pos_x = screen_info.width + delta_x;
211       }
212
213       delta_y = 0;
214     }
215     else
216     {
217       int pos_right = screen_info.width - anim->width;
218
219       if (strcmp(anim->position, "left") == 0)
220         pos_x = 0;
221       else if (strcmp(anim->position, "right")  == 0)
222         pos_x = pos_right;
223       else
224         pos_x = SimpleRND(pos_right);
225
226       if (direction == MV_DOWN)
227       {
228         delta_y = anim->step_offset;
229         pos_y = -anim->height + delta_y;
230       }
231       else
232       {
233         delta_y = -anim->step_offset;
234         pos_y = screen_info.width + delta_y;
235       }
236
237       delta_x = 0;
238     }
239   }
240
241   if (pos_x <= -anim->width        - anim->step_offset ||
242       pos_x >=  screen_info.width  + anim->step_offset ||
243       pos_y <= -anim->height       - anim->step_offset ||
244       pos_y >=  screen_info.height + anim->step_offset)
245     return TRUE;
246
247   if (!DelayReached(&anim_delay, anim_delay_value))
248   {
249     if (screen_info.redraw_needed_function() && !restart)
250       DrawAnim(anim_bitmap, anim_clip_gc,
251                src_x + cut_x, src_y + cut_y,
252                width, height,
253                screen_info.startx + dest_x,
254                screen_info.starty + dest_y,
255                pad_x, pad_y);
256
257     return FALSE;
258   }
259
260   if (pos_x < -anim->width)
261     pos_x = -anim->width;
262   else if (pos_x > screen_info.width)
263     pos_x = screen_info.width;
264   if (pos_y < -anim->height)
265     pos_y = -anim->height;
266   else if (pos_y > screen_info.height)
267     pos_y = screen_info.height;
268
269   pad_x = (horiz_move ? anim->step_offset : 0);
270   pad_y = (vert_move  ? anim->step_offset : 0);
271   src_x = anim->src_x + frame * anim->width;
272   src_y = anim->src_y;
273   dest_x = pos_x;
274   dest_y = pos_y;
275   cut_x = cut_y = 0;
276   width  = anim->width;
277   height = anim->height;
278
279   if (pos_x < 0)
280   {
281     dest_x = 0;
282     width += pos_x;
283     cut_x = -pos_x;
284   }
285   else if (pos_x > screen_info.width - anim->width)
286     width -= (pos_x - (screen_info.width - anim->width));
287
288   if (pos_y < 0)
289   {
290     dest_y = 0;
291     height += pos_y;
292     cut_y = -pos_y;
293   }
294   else if (pos_y > screen_info.height - anim->height)
295     height -= (pos_y - (screen_info.height - anim->height));
296
297   DrawAnim(anim_bitmap, anim_clip_gc,
298            src_x + cut_x, src_y + cut_y,
299            width, height,
300            screen_info.startx + dest_x,
301            screen_info.starty + dest_y,
302            pad_x, pad_y);
303
304   pos_x += delta_x;
305   pos_y += delta_y;
306
307   frame = getAnimationFrame(anim->anim_frames, anim->anim_delay,
308                             anim->anim_mode, anim->anim_start_frame,
309                             animation_frame_counter++);
310
311   return FALSE;
312 }
313
314 void HandleAnimation(int mode)
315 {
316   static unsigned long animstart_delay = -1;
317   static unsigned long animstart_delay_value = 0;
318   static boolean anim_running = FALSE;
319   static boolean anim_restart = TRUE;
320   static boolean reset_delay = TRUE;
321   static int toon_nr = 0;
322   int draw_mode;
323
324   if (!setup.toons)
325     return;
326
327   /* this may happen after reloading graphics and redefining "num_toons" */
328   if (toon_nr >= screen_info.num_toons)
329     anim_restart = TRUE;
330
331   switch(mode)
332   {
333     case ANIM_START:
334       screen_info.prepare_backbuffer_function();
335
336       anim_running = TRUE;
337       anim_restart = TRUE;
338       reset_delay = TRUE;
339
340       return;
341
342     case ANIM_CONTINUE:
343       if (!anim_running)
344         return;
345
346       break;
347
348     case ANIM_STOP:
349       redraw_mask |= (REDRAW_FIELD | REDRAW_FROM_BACKBUFFER);
350
351       /* Redraw background even when in direct drawing mode */
352       draw_mode = setup.direct_draw;
353       setup.direct_draw = FALSE;
354       screen_info.update_function();
355       setup.direct_draw = draw_mode;
356
357       anim_running = FALSE;
358
359       return;
360
361     default:
362       break;
363   }
364
365   if (reset_delay)
366   {
367     animstart_delay = Counter();
368     animstart_delay_value = SimpleRND(3000);
369     reset_delay = FALSE;
370   }
371
372   if (anim_restart)
373   {
374     if (!DelayReached(&animstart_delay, animstart_delay_value))
375       return;
376
377     toon_nr = SimpleRND(screen_info.num_toons);
378   }
379
380   anim_restart = reset_delay = AnimateToon(toon_nr, anim_restart);
381 }
382
383 void InitAnimation()
384 {
385   HandleAnimation(ANIM_START);
386 }
387
388 void StopAnimation()
389 {
390   HandleAnimation(ANIM_STOP);
391 }
392
393 void DoAnimation()
394 {
395   HandleAnimation(ANIM_CONTINUE);
396 }