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