1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
12 #include "libgame/libgame.h"
27 #include "conf_e2g.c" // include auto-generated data structure definitions
28 #include "conf_esg.c" // include auto-generated data structure definitions
29 #include "conf_e2s.c" // include auto-generated data structure definitions
30 #include "conf_fnt.c" // include auto-generated data structure definitions
31 #include "conf_g2s.c" // include auto-generated data structure definitions
32 #include "conf_g2m.c" // include auto-generated data structure definitions
33 #include "conf_act.c" // include auto-generated data structure definitions
36 #define CONFIG_TOKEN_FONT_INITIAL "font.initial"
37 #define CONFIG_TOKEN_GLOBAL_BUSY "global.busy"
40 static struct FontBitmapInfo font_initial[NUM_INITIAL_FONTS];
41 static struct GraphicInfo anim_initial;
43 static int copy_properties[][5] =
47 EL_BUG_LEFT, EL_BUG_RIGHT,
48 EL_BUG_UP, EL_BUG_DOWN
52 EL_SPACESHIP_LEFT, EL_SPACESHIP_RIGHT,
53 EL_SPACESHIP_UP, EL_SPACESHIP_DOWN
57 EL_BD_BUTTERFLY_LEFT, EL_BD_BUTTERFLY_RIGHT,
58 EL_BD_BUTTERFLY_UP, EL_BD_BUTTERFLY_DOWN
62 EL_BD_FIREFLY_LEFT, EL_BD_FIREFLY_RIGHT,
63 EL_BD_FIREFLY_UP, EL_BD_FIREFLY_DOWN
67 EL_PACMAN_LEFT, EL_PACMAN_RIGHT,
68 EL_PACMAN_UP, EL_PACMAN_DOWN
72 EL_YAMYAM_LEFT, EL_YAMYAM_RIGHT,
73 EL_YAMYAM_UP, EL_YAMYAM_DOWN
77 EL_MOLE_LEFT, EL_MOLE_RIGHT,
78 EL_MOLE_UP, EL_MOLE_DOWN
82 EL_SPRING_LEFT, EL_SPRING_RIGHT,
83 EL_SPRING_LEFT, EL_SPRING_RIGHT, // (to match array size)
92 // forward declaration for internal use
93 static int get_graphic_parameter_value(char *, char *, int);
96 static void DrawInitAnim(void)
98 struct GraphicInfo *graphic_info_last = graphic_info;
100 static unsigned int action_delay = 0;
101 unsigned int action_delay_value = GameFrameDelay;
102 int sync_frame = FrameCounter;
105 // prevent OS (Windows) from complaining about program not responding
108 if (game_status != GAME_MODE_LOADING)
111 if (anim_initial.bitmap == NULL || window == NULL)
114 if (!DelayReached(&action_delay, action_delay_value))
117 if (init_last.busy.x == -1)
118 init_last.busy.x = WIN_XSIZE / 2;
119 if (init_last.busy.y == -1)
120 init_last.busy.y = WIN_YSIZE / 2;
122 x = ALIGNED_TEXT_XPOS(&init_last.busy);
123 y = ALIGNED_TEXT_YPOS(&init_last.busy);
125 graphic_info = &anim_initial; // graphic == 0 => anim_initial
127 if (sync_frame % anim_initial.anim_delay == 0)
131 int width = graphic_info[graphic].width;
132 int height = graphic_info[graphic].height;
133 int frame = getGraphicAnimationFrame(graphic, sync_frame);
135 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
136 BlitBitmap(src_bitmap, window, src_x, src_y, width, height, x, y);
139 graphic_info = graphic_info_last;
144 static void DrawProgramInfo(void)
146 int font1_nr = FC_YELLOW;
147 int font2_nr = FC_RED;
148 int font2_height = getFontHeight(font2_nr);
151 int ypos3 = WIN_YSIZE - 20 - font2_height;
153 DrawInitText(getProgramInitString(), ypos1, font1_nr);
154 DrawInitText(setup.internal.program_copyright, ypos2, font2_nr);
155 DrawInitText(setup.internal.program_website, ypos3, font2_nr);
158 static void FreeGadgets(void)
160 FreeLevelEditorGadgets();
167 void InitGadgets(void)
169 static boolean gadgets_initialized = FALSE;
171 if (gadgets_initialized)
174 CreateLevelEditorGadgets();
178 CreateScreenGadgets();
180 InitGadgetsSoundCallback(PlaySoundActivating, PlaySoundSelecting);
182 gadgets_initialized = TRUE;
185 static void InitElementSmallImagesScaledUp(int graphic)
187 struct GraphicInfo *g = &graphic_info[graphic];
189 // create small and game tile sized bitmaps (and scale up, if needed)
190 CreateImageWithSmallImages(graphic, g->scale_up_factor, g->tile_size);
193 static void InitElementSmallImages(void)
195 print_timestamp_init("InitElementSmallImages");
197 static int special_graphics[] =
211 IMG_EDITOR_ELEMENT_BORDER,
212 IMG_EDITOR_ELEMENT_BORDER_INPUT,
213 IMG_EDITOR_CASCADE_LIST,
214 IMG_EDITOR_CASCADE_LIST_ACTIVE,
217 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
218 int num_property_mappings = getImageListPropertyMappingSize();
221 print_timestamp_time("getImageListPropertyMapping/Size");
223 print_timestamp_init("InitElementSmallImagesScaledUp (1)");
224 // initialize normal element images from static configuration
225 for (i = 0; element_to_graphic[i].element > -1; i++)
226 InitElementSmallImagesScaledUp(element_to_graphic[i].graphic);
227 print_timestamp_done("InitElementSmallImagesScaledUp (1)");
229 // initialize special element images from static configuration
230 for (i = 0; element_to_special_graphic[i].element > -1; i++)
231 InitElementSmallImagesScaledUp(element_to_special_graphic[i].graphic);
232 print_timestamp_time("InitElementSmallImagesScaledUp (2)");
234 // initialize element images from dynamic configuration
235 for (i = 0; i < num_property_mappings; i++)
236 if (property_mapping[i].base_index < MAX_NUM_ELEMENTS)
237 InitElementSmallImagesScaledUp(property_mapping[i].artwork_index);
238 print_timestamp_time("InitElementSmallImagesScaledUp (3)");
240 // initialize special non-element images from above list
241 for (i = 0; special_graphics[i] > -1; i++)
242 InitElementSmallImagesScaledUp(special_graphics[i]);
243 print_timestamp_time("InitElementSmallImagesScaledUp (4)");
245 print_timestamp_done("InitElementSmallImages");
248 static void InitScaledImagesScaledUp(int graphic)
250 struct GraphicInfo *g = &graphic_info[graphic];
252 ScaleImage(graphic, g->scale_up_factor);
255 static void InitScaledImages(void)
257 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
258 int num_property_mappings = getImageListPropertyMappingSize();
261 // scale normal images from static configuration, if not already scaled
262 for (i = 0; i < NUM_IMAGE_FILES; i++)
263 InitScaledImagesScaledUp(i);
265 // scale images from dynamic configuration, if not already scaled
266 for (i = 0; i < num_property_mappings; i++)
267 InitScaledImagesScaledUp(property_mapping[i].artwork_index);
270 static void InitBitmapPointers(void)
272 int num_images = getImageListSize();
275 // standard size bitmap may have changed -- update default bitmap pointer
276 for (i = 0; i < num_images; i++)
277 if (graphic_info[i].bitmaps)
278 graphic_info[i].bitmap = graphic_info[i].bitmaps[IMG_BITMAP_STANDARD];
281 void InitImageTextures(void)
283 static int texture_graphics[] =
285 IMG_GFX_REQUEST_BUTTON_TOUCH_YES,
286 IMG_GFX_REQUEST_BUTTON_TOUCH_NO,
287 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM,
288 IMG_GFX_GAME_BUTTON_TOUCH_STOP,
289 IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,
290 IMG_MENU_BUTTON_TOUCH_BACK,
291 IMG_MENU_BUTTON_TOUCH_NEXT,
292 IMG_MENU_BUTTON_TOUCH_BACK2,
293 IMG_MENU_BUTTON_TOUCH_NEXT2,
298 FreeAllImageTextures();
300 for (i = IMG_GLOBAL_BORDER_FIRST; i <= IMG_GLOBAL_BORDER_LAST; i++)
301 CreateImageTextures(i);
303 for (i = 0; i < MAX_NUM_TOONS; i++)
304 CreateImageTextures(IMG_TOON_1 + i);
306 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
308 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
310 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
312 int graphic = global_anim_info[i].graphic[j][k];
314 if (graphic == IMG_UNDEFINED)
317 CreateImageTextures(graphic);
322 for (i = 0; texture_graphics[i] > -1; i++)
323 CreateImageTextures(texture_graphics[i]);
326 static int getFontBitmapID(int font_nr)
330 // (special case: do not use special font for GAME_MODE_LOADING)
331 if (game_status >= GAME_MODE_TITLE_INITIAL &&
332 game_status <= GAME_MODE_PSEUDO_PREVIEW)
333 special = game_status;
334 else if (game_status == GAME_MODE_PSEUDO_TYPENAME)
335 special = GFX_SPECIAL_ARG_MAIN;
336 else if (game_status == GAME_MODE_PSEUDO_TYPENAMES)
337 special = GFX_SPECIAL_ARG_NAMES;
340 return font_info[font_nr].special_bitmap_id[special];
345 static int getFontFromToken(char *token)
347 char *value = getHashEntry(font_token_hash, token);
352 // if font not found, use reliable default value
353 return FONT_INITIAL_1;
356 static void InitFontGraphicInfo(void)
358 static struct FontBitmapInfo *font_bitmap_info = NULL;
359 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
360 int num_property_mappings = getImageListPropertyMappingSize();
361 int num_font_bitmaps = NUM_FONTS;
364 if (graphic_info == NULL) // still at startup phase
366 InitFontInfo(font_initial, NUM_INITIAL_FONTS,
367 getFontBitmapID, getFontFromToken);
372 // ---------- initialize font graphic definitions ----------
374 // always start with reliable default values (normal font graphics)
375 for (i = 0; i < NUM_FONTS; i++)
376 font_info[i].graphic = IMG_FONT_INITIAL_1;
378 // initialize normal font/graphic mapping from static configuration
379 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
381 int font_nr = font_to_graphic[i].font_nr;
382 int special = font_to_graphic[i].special;
383 int graphic = font_to_graphic[i].graphic;
388 font_info[font_nr].graphic = graphic;
391 // always start with reliable default values (special font graphics)
392 for (i = 0; i < NUM_FONTS; i++)
394 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
396 font_info[i].special_graphic[j] = font_info[i].graphic;
397 font_info[i].special_bitmap_id[j] = i;
401 // initialize special font/graphic mapping from static configuration
402 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
404 int font_nr = font_to_graphic[i].font_nr;
405 int special = font_to_graphic[i].special;
406 int graphic = font_to_graphic[i].graphic;
407 int base_graphic = font2baseimg(font_nr);
409 if (IS_SPECIAL_GFX_ARG(special))
411 boolean base_redefined =
412 getImageListEntryFromImageID(base_graphic)->redefined;
413 boolean special_redefined =
414 getImageListEntryFromImageID(graphic)->redefined;
415 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
417 /* if the base font ("font.title_1", for example) has been redefined,
418 but not the special font ("font.title_1.LEVELS", for example), do not
419 use an existing (in this case considered obsolete) special font
420 anymore, but use the automatically determined default font */
421 /* special case: cloned special fonts must be explicitly redefined,
422 but are not automatically redefined by redefining base font */
423 if (base_redefined && !special_redefined && !special_cloned)
426 font_info[font_nr].special_graphic[special] = graphic;
427 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
432 // initialize special font/graphic mapping from dynamic configuration
433 for (i = 0; i < num_property_mappings; i++)
435 int font_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
436 int special = property_mapping[i].ext3_index;
437 int graphic = property_mapping[i].artwork_index;
439 if (font_nr < 0 || font_nr >= NUM_FONTS)
442 if (IS_SPECIAL_GFX_ARG(special))
444 font_info[font_nr].special_graphic[special] = graphic;
445 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
450 /* correct special font/graphic mapping for cloned fonts for downwards
451 compatibility of PREVIEW fonts -- this is only needed for implicit
452 redefinition of special font by redefined base font, and only if other
453 fonts are cloned from this special font (like in the "Zelda" level set) */
454 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
456 int font_nr = font_to_graphic[i].font_nr;
457 int special = font_to_graphic[i].special;
458 int graphic = font_to_graphic[i].graphic;
460 if (IS_SPECIAL_GFX_ARG(special))
462 boolean special_redefined =
463 getImageListEntryFromImageID(graphic)->redefined;
464 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
466 if (special_cloned && !special_redefined)
470 for (j = 0; font_to_graphic[j].font_nr > -1; j++)
472 int font_nr2 = font_to_graphic[j].font_nr;
473 int special2 = font_to_graphic[j].special;
474 int graphic2 = font_to_graphic[j].graphic;
476 if (IS_SPECIAL_GFX_ARG(special2) &&
477 graphic2 == graphic_info[graphic].clone_from)
479 font_info[font_nr].special_graphic[special] =
480 font_info[font_nr2].special_graphic[special2];
481 font_info[font_nr].special_bitmap_id[special] =
482 font_info[font_nr2].special_bitmap_id[special2];
489 // reset non-redefined ".active" font graphics if normal font is redefined
490 // (this different treatment is needed because normal and active fonts are
491 // independently defined ("active" is not a property of font definitions!)
492 for (i = 0; i < NUM_FONTS; i++)
494 int font_nr_base = i;
495 int font_nr_active = FONT_ACTIVE(font_nr_base);
497 // check only those fonts with exist as normal and ".active" variant
498 if (font_nr_base != font_nr_active)
500 int base_graphic = font_info[font_nr_base].graphic;
501 int active_graphic = font_info[font_nr_active].graphic;
502 boolean base_redefined =
503 getImageListEntryFromImageID(base_graphic)->redefined;
504 boolean active_redefined =
505 getImageListEntryFromImageID(active_graphic)->redefined;
507 /* if the base font ("font.menu_1", for example) has been redefined,
508 but not the active font ("font.menu_1.active", for example), do not
509 use an existing (in this case considered obsolete) active font
510 anymore, but use the automatically determined default font */
511 if (base_redefined && !active_redefined)
512 font_info[font_nr_active].graphic = base_graphic;
514 // now also check each "special" font (which may be the same as above)
515 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
517 int base_graphic = font_info[font_nr_base].special_graphic[j];
518 int active_graphic = font_info[font_nr_active].special_graphic[j];
519 boolean base_redefined =
520 getImageListEntryFromImageID(base_graphic)->redefined;
521 boolean active_redefined =
522 getImageListEntryFromImageID(active_graphic)->redefined;
524 // same as above, but check special graphic definitions, for example:
525 // redefined "font.menu_1.MAIN" invalidates "font.menu_1.active.MAIN"
526 if (base_redefined && !active_redefined)
528 font_info[font_nr_active].special_graphic[j] =
529 font_info[font_nr_base].special_graphic[j];
530 font_info[font_nr_active].special_bitmap_id[j] =
531 font_info[font_nr_base].special_bitmap_id[j];
537 // ---------- initialize font bitmap array ----------
539 if (font_bitmap_info != NULL)
540 FreeFontInfo(font_bitmap_info);
543 checked_calloc(num_font_bitmaps * sizeof(struct FontBitmapInfo));
545 // ---------- initialize font bitmap definitions ----------
547 for (i = 0; i < NUM_FONTS; i++)
549 if (i < NUM_INITIAL_FONTS)
551 font_bitmap_info[i] = font_initial[i];
555 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
557 int font_bitmap_id = font_info[i].special_bitmap_id[j];
558 int graphic = font_info[i].special_graphic[j];
560 // set 'graphic_info' for font entries, if uninitialized (guessed)
561 if (graphic_info[graphic].anim_frames < MIN_NUM_CHARS_PER_FONT)
563 graphic_info[graphic].anim_frames = DEFAULT_NUM_CHARS_PER_FONT;
564 graphic_info[graphic].anim_frames_per_line = DEFAULT_NUM_CHARS_PER_LINE;
567 // copy font relevant information from graphics information
568 font_bitmap_info[font_bitmap_id].bitmap = graphic_info[graphic].bitmap;
569 font_bitmap_info[font_bitmap_id].src_x = graphic_info[graphic].src_x;
570 font_bitmap_info[font_bitmap_id].src_y = graphic_info[graphic].src_y;
571 font_bitmap_info[font_bitmap_id].width = graphic_info[graphic].width;
572 font_bitmap_info[font_bitmap_id].height = graphic_info[graphic].height;
574 font_bitmap_info[font_bitmap_id].offset_x =
575 graphic_info[graphic].offset_x;
576 font_bitmap_info[font_bitmap_id].offset_y =
577 graphic_info[graphic].offset_y;
579 font_bitmap_info[font_bitmap_id].draw_xoffset =
580 graphic_info[graphic].draw_xoffset;
581 font_bitmap_info[font_bitmap_id].draw_yoffset =
582 graphic_info[graphic].draw_yoffset;
584 font_bitmap_info[font_bitmap_id].num_chars =
585 graphic_info[graphic].anim_frames;
586 font_bitmap_info[font_bitmap_id].num_chars_per_line =
587 graphic_info[graphic].anim_frames_per_line;
591 InitFontInfo(font_bitmap_info, num_font_bitmaps,
592 getFontBitmapID, getFontFromToken);
595 static void InitGlobalAnimGraphicInfo(void)
597 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
598 int num_property_mappings = getImageListPropertyMappingSize();
601 if (graphic_info == NULL) // still at startup phase
604 // always start with reliable default values (no global animations)
605 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
606 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
607 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
608 global_anim_info[i].graphic[j][k] = IMG_UNDEFINED;
610 // initialize global animation definitions from static configuration
611 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
613 int j = GLOBAL_ANIM_ID_PART_BASE;
614 int k = GFX_SPECIAL_ARG_DEFAULT;
616 global_anim_info[i].graphic[j][k] = IMG_GFX_GLOBAL_ANIM_1 + i;
619 // initialize global animation definitions from dynamic configuration
620 for (i = 0; i < num_property_mappings; i++)
622 int anim_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS - NUM_FONTS;
623 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
624 int special = property_mapping[i].ext3_index;
625 int graphic = property_mapping[i].artwork_index;
627 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
630 // set animation part to base part, if not specified
631 if (!IS_GLOBAL_ANIM_PART(part_nr))
632 part_nr = GLOBAL_ANIM_ID_PART_BASE;
634 // set animation screen to default, if not specified
635 if (!IS_SPECIAL_GFX_ARG(special))
636 special = GFX_SPECIAL_ARG_DEFAULT;
638 global_anim_info[anim_nr].graphic[part_nr][special] = graphic;
640 // fix default value for ".draw_masked" (for backward compatibility)
641 struct GraphicInfo *g = &graphic_info[graphic];
642 struct FileInfo *image = getImageListEntryFromImageID(graphic);
643 char **parameter_raw = image->parameter;
644 int p = GFX_ARG_DRAW_MASKED;
645 int draw_masked = get_graphic_parameter_value(parameter_raw[p],
646 image_config_suffix[p].token,
647 image_config_suffix[p].type);
649 // if ".draw_masked" parameter is undefined, use default value "TRUE"
650 if (draw_masked == ARG_UNDEFINED_VALUE)
651 g->draw_masked = TRUE;
655 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
656 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
657 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
658 if (global_anim_info[i].graphic[j][k] != IMG_UNDEFINED &&
659 graphic_info[global_anim_info[i].graphic[j][k]].bitmap != NULL)
660 Debug("init:InitGlobalAnimGraphicInfo",
661 "anim %d, part %d, mode %d => %d",
662 i, j, k, global_anim_info[i].graphic[j][k]);
666 static void InitGlobalAnimSoundInfo(void)
668 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
669 int num_property_mappings = getSoundListPropertyMappingSize();
672 // always start with reliable default values (no global animation sounds)
673 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
674 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
675 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
676 global_anim_info[i].sound[j][k] = SND_UNDEFINED;
678 // initialize global animation sound definitions from dynamic configuration
679 for (i = 0; i < num_property_mappings; i++)
681 int anim_nr = property_mapping[i].base_index - 2 * MAX_NUM_ELEMENTS;
682 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
683 int special = property_mapping[i].ext3_index;
684 int sound = property_mapping[i].artwork_index;
686 // sound uses control definition; map it to position of graphic (artwork)
687 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
689 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
692 // set animation part to base part, if not specified
693 if (!IS_GLOBAL_ANIM_PART(part_nr))
694 part_nr = GLOBAL_ANIM_ID_PART_BASE;
696 // set animation screen to default, if not specified
697 if (!IS_SPECIAL_GFX_ARG(special))
698 special = GFX_SPECIAL_ARG_DEFAULT;
700 global_anim_info[anim_nr].sound[part_nr][special] = sound;
704 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
705 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
706 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
707 if (global_anim_info[i].sound[j][k] != SND_UNDEFINED)
708 Debug("init:InitGlobalAnimSoundInfo",
709 "anim %d, part %d, mode %d => %d",
710 i, j, k, global_anim_info[i].sound[j][k]);
714 static void InitGlobalAnimMusicInfo(void)
716 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
717 int num_property_mappings = getMusicListPropertyMappingSize();
720 // always start with reliable default values (no global animation music)
721 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
722 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
723 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
724 global_anim_info[i].music[j][k] = MUS_UNDEFINED;
726 // initialize global animation music definitions from dynamic configuration
727 for (i = 0; i < num_property_mappings; i++)
729 int anim_nr = property_mapping[i].base_index - NUM_MUSIC_PREFIXES;
730 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
731 int special = property_mapping[i].ext2_index;
732 int music = property_mapping[i].artwork_index;
734 // music uses control definition; map it to position of graphic (artwork)
735 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
737 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
740 // set animation part to base part, if not specified
741 if (!IS_GLOBAL_ANIM_PART(part_nr))
742 part_nr = GLOBAL_ANIM_ID_PART_BASE;
744 // set animation screen to default, if not specified
745 if (!IS_SPECIAL_GFX_ARG(special))
746 special = GFX_SPECIAL_ARG_DEFAULT;
748 global_anim_info[anim_nr].music[part_nr][special] = music;
752 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
753 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
754 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
755 if (global_anim_info[i].music[j][k] != MUS_UNDEFINED)
756 Debug("init:InitGlobalAnimMusicInfo",
757 "anim %d, part %d, mode %d => %d",
758 i, j, k, global_anim_info[i].music[j][k]);
762 static void InitElementGraphicInfo(void)
764 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
765 int num_property_mappings = getImageListPropertyMappingSize();
768 if (graphic_info == NULL) // still at startup phase
771 // set values to -1 to identify later as "uninitialized" values
772 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
774 for (act = 0; act < NUM_ACTIONS; act++)
776 element_info[i].graphic[act] = -1;
777 element_info[i].crumbled[act] = -1;
779 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
781 element_info[i].direction_graphic[act][dir] = -1;
782 element_info[i].direction_crumbled[act][dir] = -1;
789 // initialize normal element/graphic mapping from static configuration
790 for (i = 0; element_to_graphic[i].element > -1; i++)
792 int element = element_to_graphic[i].element;
793 int action = element_to_graphic[i].action;
794 int direction = element_to_graphic[i].direction;
795 boolean crumbled = element_to_graphic[i].crumbled;
796 int graphic = element_to_graphic[i].graphic;
797 int base_graphic = el2baseimg(element);
799 if (graphic_info[graphic].bitmap == NULL)
802 if ((action > -1 || direction > -1 || crumbled == TRUE) &&
805 boolean base_redefined =
806 getImageListEntryFromImageID(base_graphic)->redefined;
807 boolean act_dir_redefined =
808 getImageListEntryFromImageID(graphic)->redefined;
810 /* if the base graphic ("emerald", for example) has been redefined,
811 but not the action graphic ("emerald.falling", for example), do not
812 use an existing (in this case considered obsolete) action graphic
813 anymore, but use the automatically determined default graphic */
814 if (base_redefined && !act_dir_redefined)
819 action = ACTION_DEFAULT;
824 element_info[element].direction_crumbled[action][direction] = graphic;
826 element_info[element].crumbled[action] = graphic;
831 element_info[element].direction_graphic[action][direction] = graphic;
833 element_info[element].graphic[action] = graphic;
837 // initialize normal element/graphic mapping from dynamic configuration
838 for (i = 0; i < num_property_mappings; i++)
840 int element = property_mapping[i].base_index;
841 int action = property_mapping[i].ext1_index;
842 int direction = property_mapping[i].ext2_index;
843 int special = property_mapping[i].ext3_index;
844 int graphic = property_mapping[i].artwork_index;
845 boolean crumbled = FALSE;
847 if (special == GFX_SPECIAL_ARG_CRUMBLED)
853 if (graphic_info[graphic].bitmap == NULL)
856 if (element >= MAX_NUM_ELEMENTS || special != -1)
860 action = ACTION_DEFAULT;
865 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
866 element_info[element].direction_crumbled[action][dir] = -1;
869 element_info[element].direction_crumbled[action][direction] = graphic;
871 element_info[element].crumbled[action] = graphic;
876 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
877 element_info[element].direction_graphic[action][dir] = -1;
880 element_info[element].direction_graphic[action][direction] = graphic;
882 element_info[element].graphic[action] = graphic;
886 // now copy all graphics that are defined to be cloned from other graphics
887 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
889 int graphic = element_info[i].graphic[ACTION_DEFAULT];
890 int crumbled_like, diggable_like;
895 crumbled_like = graphic_info[graphic].crumbled_like;
896 diggable_like = graphic_info[graphic].diggable_like;
898 if (crumbled_like != -1 && element_info[i].crumbled[ACTION_DEFAULT] == -1)
900 for (act = 0; act < NUM_ACTIONS; act++)
901 element_info[i].crumbled[act] =
902 element_info[crumbled_like].crumbled[act];
903 for (act = 0; act < NUM_ACTIONS; act++)
904 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
905 element_info[i].direction_crumbled[act][dir] =
906 element_info[crumbled_like].direction_crumbled[act][dir];
909 if (diggable_like != -1 && element_info[i].graphic[ACTION_DIGGING] == -1)
911 element_info[i].graphic[ACTION_DIGGING] =
912 element_info[diggable_like].graphic[ACTION_DIGGING];
913 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
914 element_info[i].direction_graphic[ACTION_DIGGING][dir] =
915 element_info[diggable_like].direction_graphic[ACTION_DIGGING][dir];
919 // set hardcoded definitions for some runtime elements without graphic
920 element_info[EL_AMOEBA_TO_DIAMOND].graphic[ACTION_DEFAULT] = IMG_AMOEBA_DEAD;
922 // set hardcoded definitions for some internal elements without graphic
923 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
925 if (IS_EDITOR_CASCADE_INACTIVE(i))
926 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST;
927 else if (IS_EDITOR_CASCADE_ACTIVE(i))
928 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST_ACTIVE;
931 // now set all undefined/invalid graphics to -1 to set to default after it
932 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
934 for (act = 0; act < NUM_ACTIONS; act++)
938 graphic = element_info[i].graphic[act];
939 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
940 element_info[i].graphic[act] = -1;
942 graphic = element_info[i].crumbled[act];
943 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
944 element_info[i].crumbled[act] = -1;
946 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
948 graphic = element_info[i].direction_graphic[act][dir];
949 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
950 element_info[i].direction_graphic[act][dir] = -1;
952 graphic = element_info[i].direction_crumbled[act][dir];
953 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
954 element_info[i].direction_crumbled[act][dir] = -1;
961 // adjust graphics with 2nd tile for movement according to direction
962 // (do this before correcting '-1' values to minimize calculations)
963 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
965 for (act = 0; act < NUM_ACTIONS; act++)
967 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
969 int graphic = element_info[i].direction_graphic[act][dir];
970 int move_dir = (act == ACTION_FALLING ? MV_BIT_DOWN : dir);
972 if (act == ACTION_FALLING) // special case
973 graphic = element_info[i].graphic[act];
976 graphic_info[graphic].double_movement &&
977 graphic_info[graphic].swap_double_tiles != 0)
979 struct GraphicInfo *g = &graphic_info[graphic];
980 int src_x_front = g->src_x;
981 int src_y_front = g->src_y;
982 int src_x_back = g->src_x + g->offset2_x;
983 int src_y_back = g->src_y + g->offset2_y;
984 boolean frames_are_ordered_diagonally = (g->offset_x != 0 &&
986 boolean front_is_left_or_upper = (src_x_front < src_x_back ||
987 src_y_front < src_y_back);
988 boolean swap_movement_tiles_always = (g->swap_double_tiles == 1);
989 boolean swap_movement_tiles_autodetected =
990 (!frames_are_ordered_diagonally &&
991 ((move_dir == MV_BIT_LEFT && !front_is_left_or_upper) ||
992 (move_dir == MV_BIT_UP && !front_is_left_or_upper) ||
993 (move_dir == MV_BIT_RIGHT && front_is_left_or_upper) ||
994 (move_dir == MV_BIT_DOWN && front_is_left_or_upper)));
996 // swap frontside and backside graphic tile coordinates, if needed
997 if (swap_movement_tiles_always || swap_movement_tiles_autodetected)
999 // get current (wrong) backside tile coordinates
1000 getGraphicSourceXY(graphic, 0, &src_x_back, &src_y_back, TRUE);
1002 // set frontside tile coordinates to backside tile coordinates
1003 g->src_x = src_x_back;
1004 g->src_y = src_y_back;
1006 // invert tile offset to point to new backside tile coordinates
1010 // do not swap front and backside tiles again after correction
1011 g->swap_double_tiles = 0;
1018 UPDATE_BUSY_STATE();
1020 // now set all '-1' values to element specific default values
1021 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1023 int default_graphic = element_info[i].graphic[ACTION_DEFAULT];
1024 int default_crumbled = element_info[i].crumbled[ACTION_DEFAULT];
1025 int default_direction_graphic[NUM_DIRECTIONS_FULL];
1026 int default_direction_crumbled[NUM_DIRECTIONS_FULL];
1028 if (default_graphic == -1)
1029 default_graphic = IMG_UNKNOWN;
1031 if (default_crumbled == -1)
1032 default_crumbled = default_graphic;
1034 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1036 default_direction_graphic[dir] =
1037 element_info[i].direction_graphic[ACTION_DEFAULT][dir];
1038 default_direction_crumbled[dir] =
1039 element_info[i].direction_crumbled[ACTION_DEFAULT][dir];
1041 if (default_direction_graphic[dir] == -1)
1042 default_direction_graphic[dir] = default_graphic;
1044 if (default_direction_crumbled[dir] == -1)
1045 default_direction_crumbled[dir] = default_direction_graphic[dir];
1048 for (act = 0; act < NUM_ACTIONS; act++)
1050 boolean act_remove = ((IS_DIGGABLE(i) && act == ACTION_DIGGING) ||
1051 (IS_SNAPPABLE(i) && act == ACTION_SNAPPING) ||
1052 (IS_COLLECTIBLE(i) && act == ACTION_COLLECTING));
1053 boolean act_turning = (act == ACTION_TURNING_FROM_LEFT ||
1054 act == ACTION_TURNING_FROM_RIGHT ||
1055 act == ACTION_TURNING_FROM_UP ||
1056 act == ACTION_TURNING_FROM_DOWN);
1058 // generic default action graphic (defined by "[default]" directive)
1059 int default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1060 int default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1061 int default_remove_graphic = IMG_EMPTY;
1063 if (act_remove && default_action_graphic != -1)
1064 default_remove_graphic = default_action_graphic;
1066 // look for special default action graphic (classic game specific)
1067 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].graphic[act] != -1)
1068 default_action_graphic = element_info[EL_BD_DEFAULT].graphic[act];
1069 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].graphic[act] != -1)
1070 default_action_graphic = element_info[EL_SP_DEFAULT].graphic[act];
1071 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].graphic[act] != -1)
1072 default_action_graphic = element_info[EL_SB_DEFAULT].graphic[act];
1073 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].graphic[act] != -1)
1074 default_action_graphic = element_info[EL_MM_DEFAULT].graphic[act];
1076 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].crumbled[act] != -1)
1077 default_action_crumbled = element_info[EL_BD_DEFAULT].crumbled[act];
1078 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].crumbled[act] != -1)
1079 default_action_crumbled = element_info[EL_SP_DEFAULT].crumbled[act];
1080 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].crumbled[act] != -1)
1081 default_action_crumbled = element_info[EL_SB_DEFAULT].crumbled[act];
1082 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].crumbled[act] != -1)
1083 default_action_crumbled = element_info[EL_MM_DEFAULT].crumbled[act];
1085 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
1086 // !!! make this better !!!
1087 if (i == EL_EMPTY_SPACE)
1089 default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1090 default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1093 if (default_action_graphic == -1)
1094 default_action_graphic = default_graphic;
1096 if (default_action_crumbled == -1)
1097 default_action_crumbled = default_action_graphic;
1099 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1101 // use action graphic as the default direction graphic, if undefined
1102 int default_action_direction_graphic = element_info[i].graphic[act];
1103 int default_action_direction_crumbled = element_info[i].crumbled[act];
1105 // no graphic for current action -- use default direction graphic
1106 if (default_action_direction_graphic == -1)
1107 default_action_direction_graphic =
1108 (act_remove ? default_remove_graphic :
1110 element_info[i].direction_graphic[ACTION_TURNING][dir] :
1111 default_action_graphic != default_graphic ?
1112 default_action_graphic :
1113 default_direction_graphic[dir]);
1115 if (element_info[i].direction_graphic[act][dir] == -1)
1116 element_info[i].direction_graphic[act][dir] =
1117 default_action_direction_graphic;
1119 if (default_action_direction_crumbled == -1)
1120 default_action_direction_crumbled =
1121 element_info[i].direction_graphic[act][dir];
1123 if (element_info[i].direction_crumbled[act][dir] == -1)
1124 element_info[i].direction_crumbled[act][dir] =
1125 default_action_direction_crumbled;
1128 // no graphic for this specific action -- use default action graphic
1129 if (element_info[i].graphic[act] == -1)
1130 element_info[i].graphic[act] =
1131 (act_remove ? default_remove_graphic :
1132 act_turning ? element_info[i].graphic[ACTION_TURNING] :
1133 default_action_graphic);
1135 if (element_info[i].crumbled[act] == -1)
1136 element_info[i].crumbled[act] = element_info[i].graphic[act];
1140 UPDATE_BUSY_STATE();
1143 static void InitElementSpecialGraphicInfo(void)
1145 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
1146 int num_property_mappings = getImageListPropertyMappingSize();
1149 // always start with reliable default values
1150 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1151 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1152 element_info[i].special_graphic[j] =
1153 element_info[i].graphic[ACTION_DEFAULT];
1155 // initialize special element/graphic mapping from static configuration
1156 for (i = 0; element_to_special_graphic[i].element > -1; i++)
1158 int element = element_to_special_graphic[i].element;
1159 int special = element_to_special_graphic[i].special;
1160 int graphic = element_to_special_graphic[i].graphic;
1161 int base_graphic = el2baseimg(element);
1162 boolean base_redefined =
1163 getImageListEntryFromImageID(base_graphic)->redefined;
1164 boolean special_redefined =
1165 getImageListEntryFromImageID(graphic)->redefined;
1167 /* if the base graphic ("emerald", for example) has been redefined,
1168 but not the special graphic ("emerald.EDITOR", for example), do not
1169 use an existing (in this case considered obsolete) special graphic
1170 anymore, but use the automatically created (down-scaled) graphic */
1171 if (base_redefined && !special_redefined)
1174 element_info[element].special_graphic[special] = graphic;
1177 // initialize special element/graphic mapping from dynamic configuration
1178 for (i = 0; i < num_property_mappings; i++)
1180 int element = property_mapping[i].base_index;
1181 int action = property_mapping[i].ext1_index;
1182 int direction = property_mapping[i].ext2_index;
1183 int special = property_mapping[i].ext3_index;
1184 int graphic = property_mapping[i].artwork_index;
1186 // for action ".active", replace element with active element, if exists
1187 if (action == ACTION_ACTIVE && element != ELEMENT_ACTIVE(element))
1189 element = ELEMENT_ACTIVE(element);
1193 if (element >= MAX_NUM_ELEMENTS)
1196 // do not change special graphic if action or direction was specified
1197 if (action != -1 || direction != -1)
1200 if (IS_SPECIAL_GFX_ARG(special))
1201 element_info[element].special_graphic[special] = graphic;
1204 // now set all undefined/invalid graphics to default
1205 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1206 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1207 if (graphic_info[element_info[i].special_graphic[j]].bitmap == NULL)
1208 element_info[i].special_graphic[j] =
1209 element_info[i].graphic[ACTION_DEFAULT];
1212 static int get_graphic_parameter_value(char *value_raw, char *suffix, int type)
1214 if (type != TYPE_ELEMENT && type != TYPE_GRAPHIC)
1215 return get_parameter_value(value_raw, suffix, type);
1217 if (strEqual(value_raw, ARG_UNDEFINED))
1218 return ARG_UNDEFINED_VALUE;
1220 if (type == TYPE_ELEMENT)
1222 char *value = getHashEntry(element_token_hash, value_raw);
1227 Warn("error found in config file:");
1228 Warn("- config file: '%s'", getImageConfigFilename());
1229 Warn("error: invalid element token '%s'", value_raw);
1230 Warn("custom graphic rejected for this element/action");
1231 Warn("fallback done to undefined element for this graphic");
1235 return (value != NULL ? atoi(value) : EL_UNDEFINED);
1237 else if (type == TYPE_GRAPHIC)
1239 char *value = getHashEntry(graphic_token_hash, value_raw);
1240 int fallback_graphic = IMG_CHAR_EXCLAM;
1245 Warn("error found in config file:");
1246 Warn("- config file: '%s'", getImageConfigFilename());
1247 Warn("error: invalid graphic token '%s'", value_raw);
1248 Warn("custom graphic rejected for this element/action");
1249 Warn("fallback done to 'char_exclam' for this graphic");
1253 return (value != NULL ? atoi(value) : fallback_graphic);
1259 static int get_scaled_graphic_width(int graphic)
1261 int original_width = getOriginalImageWidthFromImageID(graphic);
1262 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1264 return original_width * scale_up_factor;
1267 static int get_scaled_graphic_height(int graphic)
1269 int original_height = getOriginalImageHeightFromImageID(graphic);
1270 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1272 return original_height * scale_up_factor;
1275 static void set_graphic_parameters_ext(int graphic, int *parameter,
1276 Bitmap **src_bitmaps)
1278 struct GraphicInfo *g = &graphic_info[graphic];
1279 Bitmap *src_bitmap = (src_bitmaps ? src_bitmaps[IMG_BITMAP_STANDARD] : NULL);
1280 int anim_frames_per_row = 1, anim_frames_per_col = 1;
1281 int anim_frames_per_line = 1;
1283 // always start with reliable default values
1284 g->src_image_width = 0;
1285 g->src_image_height = 0;
1288 g->width = TILEX; // default for element graphics
1289 g->height = TILEY; // default for element graphics
1290 g->offset_x = 0; // one or both of these values ...
1291 g->offset_y = 0; // ... will be corrected later
1292 g->offset2_x = 0; // one or both of these values ...
1293 g->offset2_y = 0; // ... will be corrected later
1294 g->swap_double_tiles = -1; // auto-detect tile swapping
1295 g->crumbled_like = -1; // do not use clone element
1296 g->diggable_like = -1; // do not use clone element
1297 g->border_size = TILEX / 8; // "CRUMBLED" border size
1298 g->scale_up_factor = 1; // default: no scaling up
1299 g->tile_size = TILESIZE; // default: standard tile size
1300 g->clone_from = -1; // do not use clone graphic
1301 g->init_delay_fixed = 0;
1302 g->init_delay_random = 0;
1303 g->init_delay_action = -1;
1304 g->anim_delay_fixed = 0;
1305 g->anim_delay_random = 0;
1306 g->anim_delay_action = -1;
1307 g->post_delay_fixed = 0;
1308 g->post_delay_random = 0;
1309 g->post_delay_action = -1;
1310 g->init_event = ANIM_EVENT_UNDEFINED;
1311 g->anim_event = ANIM_EVENT_UNDEFINED;
1312 g->init_event_action = -1;
1313 g->anim_event_action = -1;
1314 g->draw_masked = FALSE;
1316 g->fade_mode = FADE_MODE_DEFAULT;
1320 g->auto_delay_unit = AUTO_DELAY_UNIT_DEFAULT;
1321 g->align = ALIGN_CENTER; // default for title screens
1322 g->valign = VALIGN_MIDDLE; // default for title screens
1323 g->sort_priority = 0; // default for title screens
1325 g->style = STYLE_DEFAULT;
1327 g->bitmaps = src_bitmaps;
1328 g->bitmap = src_bitmap;
1330 // optional zoom factor for scaling up the image to a larger size
1331 if (parameter[GFX_ARG_SCALE_UP_FACTOR] != ARG_UNDEFINED_VALUE)
1332 g->scale_up_factor = parameter[GFX_ARG_SCALE_UP_FACTOR];
1333 if (g->scale_up_factor < 1)
1334 g->scale_up_factor = 1; // no scaling
1336 // optional tile size for using non-standard image size
1337 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1339 g->tile_size = parameter[GFX_ARG_TILE_SIZE];
1342 // CHECK: should tile sizes less than standard tile size be allowed?
1343 if (g->tile_size < TILESIZE)
1344 g->tile_size = TILESIZE; // standard tile size
1347 // when setting tile size, also set width and height accordingly
1348 g->width = g->tile_size;
1349 g->height = g->tile_size;
1352 if (g->use_image_size)
1354 // set new default bitmap size (with scaling, but without small images)
1355 g->width = get_scaled_graphic_width(graphic);
1356 g->height = get_scaled_graphic_height(graphic);
1359 // optional width and height of each animation frame
1360 if (parameter[GFX_ARG_WIDTH] != ARG_UNDEFINED_VALUE)
1361 g->width = parameter[GFX_ARG_WIDTH];
1362 if (parameter[GFX_ARG_HEIGHT] != ARG_UNDEFINED_VALUE)
1363 g->height = parameter[GFX_ARG_HEIGHT];
1365 // optional x and y tile position of animation frame sequence
1366 if (parameter[GFX_ARG_XPOS] != ARG_UNDEFINED_VALUE)
1367 g->src_x = parameter[GFX_ARG_XPOS] * g->width;
1368 if (parameter[GFX_ARG_YPOS] != ARG_UNDEFINED_VALUE)
1369 g->src_y = parameter[GFX_ARG_YPOS] * g->height;
1371 // optional x and y pixel position of animation frame sequence
1372 if (parameter[GFX_ARG_X] != ARG_UNDEFINED_VALUE)
1373 g->src_x = parameter[GFX_ARG_X];
1374 if (parameter[GFX_ARG_Y] != ARG_UNDEFINED_VALUE)
1375 g->src_y = parameter[GFX_ARG_Y];
1382 Warn("invalid value %d for '%s.width' (fallback done to %d)",
1383 g->width, getTokenFromImageID(graphic), TILEX);
1386 g->width = TILEX; // will be checked to be inside bitmap later
1392 Warn("invalid value %d for '%s.height' (fallback done to %d)",
1393 g->height, getTokenFromImageID(graphic), TILEY);
1396 g->height = TILEY; // will be checked to be inside bitmap later
1402 // get final bitmap size (with scaling, but without small images)
1403 int src_image_width = get_scaled_graphic_width(graphic);
1404 int src_image_height = get_scaled_graphic_height(graphic);
1406 if (src_image_width == 0 || src_image_height == 0)
1408 // only happens when loaded outside artwork system (like "global.busy")
1409 src_image_width = src_bitmap->width;
1410 src_image_height = src_bitmap->height;
1413 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1415 anim_frames_per_row = MAX(1, src_image_width / g->tile_size);
1416 anim_frames_per_col = MAX(1, src_image_height / g->tile_size);
1420 anim_frames_per_row = MAX(1, src_image_width / g->width);
1421 anim_frames_per_col = MAX(1, src_image_height / g->height);
1424 g->src_image_width = src_image_width;
1425 g->src_image_height = src_image_height;
1428 // correct x or y offset dependent of vertical or horizontal frame order
1429 if (parameter[GFX_ARG_VERTICAL]) // frames are ordered vertically
1431 g->offset_y = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1432 parameter[GFX_ARG_OFFSET] : g->height);
1433 anim_frames_per_line = anim_frames_per_col;
1435 else // frames are ordered horizontally
1437 g->offset_x = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1438 parameter[GFX_ARG_OFFSET] : g->width);
1439 anim_frames_per_line = anim_frames_per_row;
1442 // optionally, the x and y offset of frames can be specified directly
1443 if (parameter[GFX_ARG_XOFFSET] != ARG_UNDEFINED_VALUE)
1444 g->offset_x = parameter[GFX_ARG_XOFFSET];
1445 if (parameter[GFX_ARG_YOFFSET] != ARG_UNDEFINED_VALUE)
1446 g->offset_y = parameter[GFX_ARG_YOFFSET];
1448 // optionally, moving animations may have separate start and end graphics
1449 g->double_movement = parameter[GFX_ARG_2ND_MOVEMENT_TILE];
1451 if (parameter[GFX_ARG_2ND_VERTICAL] == ARG_UNDEFINED_VALUE)
1452 parameter[GFX_ARG_2ND_VERTICAL] = !parameter[GFX_ARG_VERTICAL];
1454 // correct x or y offset2 dependent of vertical or horizontal frame order
1455 if (parameter[GFX_ARG_2ND_VERTICAL]) // frames are ordered vertically
1456 g->offset2_y = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1457 parameter[GFX_ARG_2ND_OFFSET] : g->height);
1458 else // frames are ordered horizontally
1459 g->offset2_x = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1460 parameter[GFX_ARG_2ND_OFFSET] : g->width);
1462 // optionally, the x and y offset of 2nd graphic can be specified directly
1463 if (parameter[GFX_ARG_2ND_XOFFSET] != ARG_UNDEFINED_VALUE)
1464 g->offset2_x = parameter[GFX_ARG_2ND_XOFFSET];
1465 if (parameter[GFX_ARG_2ND_YOFFSET] != ARG_UNDEFINED_VALUE)
1466 g->offset2_y = parameter[GFX_ARG_2ND_YOFFSET];
1468 // optionally, the second movement tile can be specified as start tile
1469 if (parameter[GFX_ARG_2ND_SWAP_TILES] != ARG_UNDEFINED_VALUE)
1470 g->swap_double_tiles= parameter[GFX_ARG_2ND_SWAP_TILES];
1472 // automatically determine correct number of frames, if not defined
1473 if (parameter[GFX_ARG_FRAMES] != ARG_UNDEFINED_VALUE)
1474 g->anim_frames = parameter[GFX_ARG_FRAMES];
1475 else if (parameter[GFX_ARG_XPOS] == 0 && !parameter[GFX_ARG_VERTICAL])
1476 g->anim_frames = anim_frames_per_row;
1477 else if (parameter[GFX_ARG_YPOS] == 0 && parameter[GFX_ARG_VERTICAL])
1478 g->anim_frames = anim_frames_per_col;
1482 if (g->anim_frames < 1) // frames must be at least 1
1485 g->anim_frames_per_line =
1486 (parameter[GFX_ARG_FRAMES_PER_LINE] != ARG_UNDEFINED_VALUE ?
1487 parameter[GFX_ARG_FRAMES_PER_LINE] : anim_frames_per_line);
1489 g->anim_delay = parameter[GFX_ARG_DELAY];
1490 if (g->anim_delay < 1) // delay must be at least 1
1493 g->anim_mode = parameter[GFX_ARG_ANIM_MODE];
1495 // automatically determine correct start frame, if not defined
1496 if (parameter[GFX_ARG_START_FRAME] == ARG_UNDEFINED_VALUE)
1497 g->anim_start_frame = 0;
1498 else if (g->anim_mode & ANIM_REVERSE)
1499 g->anim_start_frame = g->anim_frames - parameter[GFX_ARG_START_FRAME] - 1;
1501 g->anim_start_frame = parameter[GFX_ARG_START_FRAME];
1503 // animation synchronized with global frame counter, not move position
1504 g->anim_global_sync = parameter[GFX_ARG_GLOBAL_SYNC];
1506 // optional element for cloning crumble graphics
1507 if (parameter[GFX_ARG_CRUMBLED_LIKE] != ARG_UNDEFINED_VALUE)
1508 g->crumbled_like = parameter[GFX_ARG_CRUMBLED_LIKE];
1510 // optional element for cloning digging graphics
1511 if (parameter[GFX_ARG_DIGGABLE_LIKE] != ARG_UNDEFINED_VALUE)
1512 g->diggable_like = parameter[GFX_ARG_DIGGABLE_LIKE];
1514 // optional border size for "crumbling" diggable graphics
1515 if (parameter[GFX_ARG_BORDER_SIZE] != ARG_UNDEFINED_VALUE)
1516 g->border_size = parameter[GFX_ARG_BORDER_SIZE];
1518 // used for global animations and player "boring" and "sleeping" actions
1519 if (parameter[GFX_ARG_INIT_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1520 g->init_delay_fixed = parameter[GFX_ARG_INIT_DELAY_FIXED];
1521 if (parameter[GFX_ARG_INIT_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1522 g->init_delay_random = parameter[GFX_ARG_INIT_DELAY_RANDOM];
1523 if (parameter[GFX_ARG_ANIM_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1524 g->anim_delay_fixed = parameter[GFX_ARG_ANIM_DELAY_FIXED];
1525 if (parameter[GFX_ARG_ANIM_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1526 g->anim_delay_random = parameter[GFX_ARG_ANIM_DELAY_RANDOM];
1527 if (parameter[GFX_ARG_POST_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1528 g->post_delay_fixed = parameter[GFX_ARG_POST_DELAY_FIXED];
1529 if (parameter[GFX_ARG_POST_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1530 g->post_delay_random = parameter[GFX_ARG_POST_DELAY_RANDOM];
1532 // used for global animations
1533 if (parameter[GFX_ARG_INIT_EVENT] != ARG_UNDEFINED_VALUE)
1534 g->init_event = parameter[GFX_ARG_INIT_EVENT];
1535 if (parameter[GFX_ARG_ANIM_EVENT] != ARG_UNDEFINED_VALUE)
1536 g->anim_event = parameter[GFX_ARG_ANIM_EVENT];
1537 if (parameter[GFX_ARG_INIT_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1538 g->init_event_action = parameter[GFX_ARG_INIT_EVENT_ACTION];
1539 if (parameter[GFX_ARG_ANIM_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1540 g->anim_event_action = parameter[GFX_ARG_ANIM_EVENT_ACTION];
1541 if (parameter[GFX_ARG_INIT_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1542 g->init_delay_action = parameter[GFX_ARG_INIT_DELAY_ACTION];
1543 if (parameter[GFX_ARG_ANIM_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1544 g->anim_delay_action = parameter[GFX_ARG_ANIM_DELAY_ACTION];
1545 if (parameter[GFX_ARG_POST_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1546 g->post_delay_action = parameter[GFX_ARG_POST_DELAY_ACTION];
1548 // used for toon animations and global animations
1549 g->step_offset = parameter[GFX_ARG_STEP_OFFSET];
1550 g->step_xoffset = parameter[GFX_ARG_STEP_XOFFSET];
1551 g->step_yoffset = parameter[GFX_ARG_STEP_YOFFSET];
1552 g->step_delay = parameter[GFX_ARG_STEP_DELAY];
1553 g->direction = parameter[GFX_ARG_DIRECTION];
1554 g->position = parameter[GFX_ARG_POSITION];
1555 g->x = parameter[GFX_ARG_X]; // (may be uninitialized,
1556 g->y = parameter[GFX_ARG_Y]; // unlike src_x and src_y)
1558 if (g->step_delay < 1) // delay must be at least 1
1561 // this is only used for drawing font characters
1562 g->draw_xoffset = parameter[GFX_ARG_DRAW_XOFFSET];
1563 g->draw_yoffset = parameter[GFX_ARG_DRAW_YOFFSET];
1565 // use a different default value for global animations and toons
1566 if ((graphic >= IMG_GFX_GLOBAL_ANIM_1 && graphic <= IMG_GFX_GLOBAL_ANIM_8) ||
1567 (graphic >= IMG_TOON_1 && graphic <= IMG_TOON_20))
1568 g->draw_masked = TRUE;
1570 // this is used for drawing envelopes, global animations and toons
1571 if (parameter[GFX_ARG_DRAW_MASKED] != ARG_UNDEFINED_VALUE)
1572 g->draw_masked = parameter[GFX_ARG_DRAW_MASKED];
1574 // used for toon animations and global animations
1575 if (parameter[GFX_ARG_DRAW_ORDER] != ARG_UNDEFINED_VALUE)
1576 g->draw_order = parameter[GFX_ARG_DRAW_ORDER];
1578 // optional graphic for cloning all graphics settings
1579 if (parameter[GFX_ARG_CLONE_FROM] != ARG_UNDEFINED_VALUE)
1580 g->clone_from = parameter[GFX_ARG_CLONE_FROM];
1582 // optional settings for drawing title screens and title messages
1583 if (parameter[GFX_ARG_FADE_MODE] != ARG_UNDEFINED_VALUE)
1584 g->fade_mode = parameter[GFX_ARG_FADE_MODE];
1585 if (parameter[GFX_ARG_FADE_DELAY] != ARG_UNDEFINED_VALUE)
1586 g->fade_delay = parameter[GFX_ARG_FADE_DELAY];
1587 if (parameter[GFX_ARG_POST_DELAY] != ARG_UNDEFINED_VALUE)
1588 g->post_delay = parameter[GFX_ARG_POST_DELAY];
1589 if (parameter[GFX_ARG_AUTO_DELAY] != ARG_UNDEFINED_VALUE)
1590 g->auto_delay = parameter[GFX_ARG_AUTO_DELAY];
1591 if (parameter[GFX_ARG_AUTO_DELAY_UNIT] != ARG_UNDEFINED_VALUE)
1592 g->auto_delay_unit = parameter[GFX_ARG_AUTO_DELAY_UNIT];
1593 if (parameter[GFX_ARG_ALIGN] != ARG_UNDEFINED_VALUE)
1594 g->align = parameter[GFX_ARG_ALIGN];
1595 if (parameter[GFX_ARG_VALIGN] != ARG_UNDEFINED_VALUE)
1596 g->valign = parameter[GFX_ARG_VALIGN];
1597 if (parameter[GFX_ARG_SORT_PRIORITY] != ARG_UNDEFINED_VALUE)
1598 g->sort_priority = parameter[GFX_ARG_SORT_PRIORITY];
1600 if (parameter[GFX_ARG_CLASS] != ARG_UNDEFINED_VALUE)
1601 g->class = parameter[GFX_ARG_CLASS];
1602 if (parameter[GFX_ARG_STYLE] != ARG_UNDEFINED_VALUE)
1603 g->style = parameter[GFX_ARG_STYLE];
1605 // this is only used for drawing menu buttons and text
1606 g->active_xoffset = parameter[GFX_ARG_ACTIVE_XOFFSET];
1607 g->active_yoffset = parameter[GFX_ARG_ACTIVE_YOFFSET];
1608 g->pressed_xoffset = parameter[GFX_ARG_PRESSED_XOFFSET];
1609 g->pressed_yoffset = parameter[GFX_ARG_PRESSED_YOFFSET];
1612 static void set_graphic_parameters(int graphic)
1614 struct FileInfo *image = getImageListEntryFromImageID(graphic);
1615 char **parameter_raw = image->parameter;
1616 Bitmap **src_bitmaps = getBitmapsFromImageID(graphic);
1617 int parameter[NUM_GFX_ARGS];
1620 // if fallback to default artwork is done, also use the default parameters
1621 if (image->fallback_to_default)
1622 parameter_raw = image->default_parameter;
1624 // get integer values from string parameters
1625 for (i = 0; i < NUM_GFX_ARGS; i++)
1626 parameter[i] = get_graphic_parameter_value(parameter_raw[i],
1627 image_config_suffix[i].token,
1628 image_config_suffix[i].type);
1630 set_graphic_parameters_ext(graphic, parameter, src_bitmaps);
1632 UPDATE_BUSY_STATE();
1635 static void set_cloned_graphic_parameters(int graphic)
1637 int fallback_graphic = IMG_CHAR_EXCLAM;
1638 int max_num_images = getImageListSize();
1639 int clone_graphic = graphic_info[graphic].clone_from;
1640 int num_references_followed = 1;
1642 while (graphic_info[clone_graphic].clone_from != -1 &&
1643 num_references_followed < max_num_images)
1645 clone_graphic = graphic_info[clone_graphic].clone_from;
1647 num_references_followed++;
1650 if (num_references_followed >= max_num_images)
1653 Warn("error found in config file:");
1654 Warn("- config file: '%s'", getImageConfigFilename());
1655 Warn("- config token: '%s'", getTokenFromImageID(graphic));
1656 Warn("error: loop discovered when resolving cloned graphics");
1657 Warn("custom graphic rejected for this element/action");
1659 if (graphic == fallback_graphic)
1660 Fail("no fallback graphic available");
1662 Warn("fallback done to 'char_exclam' for this graphic");
1665 graphic_info[graphic] = graphic_info[fallback_graphic];
1669 graphic_info[graphic] = graphic_info[clone_graphic];
1670 graphic_info[graphic].clone_from = clone_graphic;
1674 static void InitGraphicInfo(void)
1676 int fallback_graphic = IMG_CHAR_EXCLAM;
1677 int num_images = getImageListSize();
1680 // use image size as default values for width and height for these images
1681 static int full_size_graphics[] =
1684 IMG_GLOBAL_BORDER_MAIN,
1685 IMG_GLOBAL_BORDER_SCORES,
1686 IMG_GLOBAL_BORDER_EDITOR,
1687 IMG_GLOBAL_BORDER_PLAYING,
1690 IMG_BACKGROUND_ENVELOPE_1,
1691 IMG_BACKGROUND_ENVELOPE_2,
1692 IMG_BACKGROUND_ENVELOPE_3,
1693 IMG_BACKGROUND_ENVELOPE_4,
1694 IMG_BACKGROUND_REQUEST,
1697 IMG_BACKGROUND_TITLE_INITIAL,
1698 IMG_BACKGROUND_TITLE,
1699 IMG_BACKGROUND_MAIN,
1700 IMG_BACKGROUND_NAMES,
1701 IMG_BACKGROUND_LEVELS,
1702 IMG_BACKGROUND_LEVELNR,
1703 IMG_BACKGROUND_SCORES,
1704 IMG_BACKGROUND_EDITOR,
1705 IMG_BACKGROUND_INFO,
1706 IMG_BACKGROUND_INFO_ELEMENTS,
1707 IMG_BACKGROUND_INFO_MUSIC,
1708 IMG_BACKGROUND_INFO_CREDITS,
1709 IMG_BACKGROUND_INFO_PROGRAM,
1710 IMG_BACKGROUND_INFO_VERSION,
1711 IMG_BACKGROUND_INFO_LEVELSET,
1712 IMG_BACKGROUND_SETUP,
1713 IMG_BACKGROUND_PLAYING,
1714 IMG_BACKGROUND_DOOR,
1715 IMG_BACKGROUND_TAPE,
1716 IMG_BACKGROUND_PANEL,
1717 IMG_BACKGROUND_PALETTE,
1718 IMG_BACKGROUND_TOOLBOX,
1720 IMG_TITLESCREEN_INITIAL_1,
1721 IMG_TITLESCREEN_INITIAL_2,
1722 IMG_TITLESCREEN_INITIAL_3,
1723 IMG_TITLESCREEN_INITIAL_4,
1724 IMG_TITLESCREEN_INITIAL_5,
1731 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_1,
1732 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_2,
1733 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_3,
1734 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_4,
1735 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_5,
1736 IMG_BACKGROUND_TITLEMESSAGE_1,
1737 IMG_BACKGROUND_TITLEMESSAGE_2,
1738 IMG_BACKGROUND_TITLEMESSAGE_3,
1739 IMG_BACKGROUND_TITLEMESSAGE_4,
1740 IMG_BACKGROUND_TITLEMESSAGE_5,
1745 FreeGlobalAnimEventInfo();
1747 checked_free(graphic_info);
1749 graphic_info = checked_calloc(num_images * sizeof(struct GraphicInfo));
1751 // initialize "use_image_size" flag with default value
1752 for (i = 0; i < num_images; i++)
1753 graphic_info[i].use_image_size = FALSE;
1755 // initialize "use_image_size" flag from static configuration above
1756 for (i = 0; full_size_graphics[i] != -1; i++)
1757 graphic_info[full_size_graphics[i]].use_image_size = TRUE;
1759 // first set all graphic paramaters ...
1760 for (i = 0; i < num_images; i++)
1761 set_graphic_parameters(i);
1763 // ... then copy these parameters for cloned graphics
1764 for (i = 0; i < num_images; i++)
1765 if (graphic_info[i].clone_from != -1)
1766 set_cloned_graphic_parameters(i);
1768 for (i = 0; i < num_images; i++)
1770 Bitmap *src_bitmap = graphic_info[i].bitmap;
1774 int src_bitmap_width, src_bitmap_height;
1776 // now check if no animation frames are outside of the loaded image
1778 if (graphic_info[i].bitmap == NULL)
1779 continue; // skip check for optional images that are undefined
1781 // get image size (this can differ from the standard element tile size!)
1782 width = graphic_info[i].width;
1783 height = graphic_info[i].height;
1785 // get final bitmap size (with scaling, but without small images)
1786 src_bitmap_width = graphic_info[i].src_image_width;
1787 src_bitmap_height = graphic_info[i].src_image_height;
1789 // check if first animation frame is inside specified bitmap
1791 // do not use getGraphicSourceXY() here to get position of first frame;
1792 // this avoids calculating wrong start position for out-of-bounds frame
1793 src_x = graphic_info[i].src_x;
1794 src_y = graphic_info[i].src_y;
1796 if (program.headless)
1799 if (src_x < 0 || src_y < 0 ||
1800 src_x + width > src_bitmap_width ||
1801 src_y + height > src_bitmap_height)
1804 Warn("error found in config file:");
1805 Warn("- config file: '%s'", getImageConfigFilename());
1806 Warn("- config token: '%s'", getTokenFromImageID(i));
1807 Warn("- image file: '%s'", src_bitmap->source_filename);
1808 Warn("- frame size: %d, %d", width, height);
1809 Warn("error: first animation frame out of bounds (%d, %d) [%d, %d]",
1810 src_x, src_y, src_bitmap_width, src_bitmap_height);
1811 Warn("custom graphic rejected for this element/action");
1813 if (i == fallback_graphic)
1814 Fail("no fallback graphic available");
1816 Warn("fallback done to 'char_exclam' for this graphic");
1819 graphic_info[i] = graphic_info[fallback_graphic];
1821 // if first frame out of bounds, do not check last frame anymore
1825 // check if last animation frame is inside specified bitmap
1827 last_frame = graphic_info[i].anim_frames - 1;
1828 getGraphicSourceXY(i, last_frame, &src_x, &src_y, FALSE);
1830 if (src_x < 0 || src_y < 0 ||
1831 src_x + width > src_bitmap_width ||
1832 src_y + height > src_bitmap_height)
1835 Warn("error found in config file:");
1836 Warn("- config file: '%s'", getImageConfigFilename());
1837 Warn("- config token: '%s'", getTokenFromImageID(i));
1838 Warn("- image file: '%s'", src_bitmap->source_filename);
1839 Warn("- frame size: %d, %d", width, height);
1840 Warn("error: last animation frame (%d) out of bounds (%d, %d) [%d, %d]",
1841 last_frame, src_x, src_y, src_bitmap_width, src_bitmap_height);
1842 Warn("custom graphic rejected for this element/action");
1844 if (i == fallback_graphic)
1845 Fail("no fallback graphic available");
1847 Warn("fallback done to 'char_exclam' for this graphic");
1850 graphic_info[i] = graphic_info[fallback_graphic];
1855 static void InitGraphicCompatibilityInfo(void)
1857 struct FileInfo *fi_global_door =
1858 getImageListEntryFromImageID(IMG_GLOBAL_DOOR);
1859 int num_images = getImageListSize();
1862 /* the following compatibility handling is needed for the following case:
1863 versions up to 3.3.0.0 used one large bitmap "global.door" for various
1864 graphics mainly used for door and panel graphics, like editor, tape and
1865 in-game buttons with hard-coded bitmap positions and button sizes; as
1866 these graphics now have individual definitions, redefining "global.door"
1867 to change all these graphics at once like before does not work anymore
1868 (because all those individual definitions still have their default values);
1869 to solve this, remap all those individual definitions that are not
1870 redefined to the new bitmap of "global.door" if it was redefined */
1872 // special compatibility handling if image "global.door" was redefined
1873 if (fi_global_door->redefined)
1875 for (i = 0; i < num_images; i++)
1877 struct FileInfo *fi = getImageListEntryFromImageID(i);
1879 // process only those images that still use the default settings
1882 // process all images which default to same image as "global.door"
1883 if (strEqual(fi->default_filename, fi_global_door->default_filename))
1886 Debug("init:InitGraphicCompatibilityInfo",
1887 "special treatment needed for token '%s'", fi->token);
1890 graphic_info[i].bitmaps = graphic_info[IMG_GLOBAL_DOOR].bitmaps;
1891 graphic_info[i].bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
1897 InitGraphicCompatibilityInfo_Doors();
1900 static void InitElementSoundInfo(void)
1902 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
1903 int num_property_mappings = getSoundListPropertyMappingSize();
1906 // set values to -1 to identify later as "uninitialized" values
1907 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1908 for (act = 0; act < NUM_ACTIONS; act++)
1909 element_info[i].sound[act] = -1;
1911 // initialize element/sound mapping from static configuration
1912 for (i = 0; element_to_sound[i].element > -1; i++)
1914 int element = element_to_sound[i].element;
1915 int action = element_to_sound[i].action;
1916 int sound = element_to_sound[i].sound;
1917 boolean is_class = element_to_sound[i].is_class;
1920 action = ACTION_DEFAULT;
1923 element_info[element].sound[action] = sound;
1925 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
1926 if (strEqual(element_info[j].class_name,
1927 element_info[element].class_name))
1928 element_info[j].sound[action] = sound;
1931 // initialize element class/sound mapping from dynamic configuration
1932 for (i = 0; i < num_property_mappings; i++)
1934 int element_class = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
1935 int action = property_mapping[i].ext1_index;
1936 int sound = property_mapping[i].artwork_index;
1938 if (element_class < 0 || element_class >= MAX_NUM_ELEMENTS)
1942 action = ACTION_DEFAULT;
1944 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
1945 if (strEqual(element_info[j].class_name,
1946 element_info[element_class].class_name))
1947 element_info[j].sound[action] = sound;
1950 // initialize element/sound mapping from dynamic configuration
1951 for (i = 0; i < num_property_mappings; i++)
1953 int element = property_mapping[i].base_index;
1954 int action = property_mapping[i].ext1_index;
1955 int sound = property_mapping[i].artwork_index;
1957 if (element >= MAX_NUM_ELEMENTS)
1961 action = ACTION_DEFAULT;
1963 element_info[element].sound[action] = sound;
1966 // now set all '-1' values to element specific default values
1967 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1969 for (act = 0; act < NUM_ACTIONS; act++)
1971 // generic default action sound (defined by "[default]" directive)
1972 int default_action_sound = element_info[EL_DEFAULT].sound[act];
1974 // look for special default action sound (classic game specific)
1975 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].sound[act] != -1)
1976 default_action_sound = element_info[EL_BD_DEFAULT].sound[act];
1977 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].sound[act] != -1)
1978 default_action_sound = element_info[EL_SP_DEFAULT].sound[act];
1979 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].sound[act] != -1)
1980 default_action_sound = element_info[EL_SB_DEFAULT].sound[act];
1981 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].sound[act] != -1)
1982 default_action_sound = element_info[EL_MM_DEFAULT].sound[act];
1984 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
1985 // !!! make this better !!!
1986 if (i == EL_EMPTY_SPACE)
1987 default_action_sound = element_info[EL_DEFAULT].sound[act];
1989 // no sound for this specific action -- use default action sound
1990 if (element_info[i].sound[act] == -1)
1991 element_info[i].sound[act] = default_action_sound;
1995 // copy sound settings to some elements that are only stored in level file
1996 // in native R'n'D levels, but are used by game engine in native EM levels
1997 for (i = 0; copy_properties[i][0] != -1; i++)
1998 for (j = 1; j <= 4; j++)
1999 for (act = 0; act < NUM_ACTIONS; act++)
2000 element_info[copy_properties[i][j]].sound[act] =
2001 element_info[copy_properties[i][0]].sound[act];
2004 static void InitGameModeSoundInfo(void)
2008 // set values to -1 to identify later as "uninitialized" values
2009 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2012 // initialize gamemode/sound mapping from static configuration
2013 for (i = 0; gamemode_to_sound[i].sound > -1; i++)
2015 int gamemode = gamemode_to_sound[i].gamemode;
2016 int sound = gamemode_to_sound[i].sound;
2019 gamemode = GAME_MODE_DEFAULT;
2021 menu.sound[gamemode] = sound;
2024 // now set all '-1' values to levelset specific default values
2025 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2026 if (menu.sound[i] == -1)
2027 menu.sound[i] = menu.sound[GAME_MODE_DEFAULT];
2030 static void set_sound_parameters(int sound, char **parameter_raw)
2032 int parameter[NUM_SND_ARGS];
2035 // get integer values from string parameters
2036 for (i = 0; i < NUM_SND_ARGS; i++)
2038 get_parameter_value(parameter_raw[i],
2039 sound_config_suffix[i].token,
2040 sound_config_suffix[i].type);
2042 // explicit loop mode setting in configuration overrides default value
2043 if (parameter[SND_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2044 sound_info[sound].loop = parameter[SND_ARG_MODE_LOOP];
2046 // sound volume to change the original volume when loading the sound file
2047 sound_info[sound].volume = parameter[SND_ARG_VOLUME];
2049 // sound priority to give certain sounds a higher or lower priority
2050 sound_info[sound].priority = parameter[SND_ARG_PRIORITY];
2053 static void InitSoundInfo(void)
2055 int *sound_effect_properties;
2056 int num_sounds = getSoundListSize();
2059 checked_free(sound_info);
2061 sound_effect_properties = checked_calloc(num_sounds * sizeof(int));
2062 sound_info = checked_calloc(num_sounds * sizeof(struct SoundInfo));
2064 // initialize sound effect for all elements to "no sound"
2065 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2066 for (j = 0; j < NUM_ACTIONS; j++)
2067 element_info[i].sound[j] = SND_UNDEFINED;
2069 for (i = 0; i < num_sounds; i++)
2071 struct FileInfo *sound = getSoundListEntry(i);
2072 int len_effect_text = strlen(sound->token);
2074 sound_effect_properties[i] = ACTION_OTHER;
2075 sound_info[i].loop = FALSE; // default: play sound only once
2077 // determine all loop sounds and identify certain sound classes
2079 for (j = 0; element_action_info[j].suffix; j++)
2081 int len_action_text = strlen(element_action_info[j].suffix);
2083 if (len_action_text < len_effect_text &&
2084 strEqual(&sound->token[len_effect_text - len_action_text],
2085 element_action_info[j].suffix))
2087 sound_effect_properties[i] = element_action_info[j].value;
2088 sound_info[i].loop = element_action_info[j].is_loop_sound;
2094 // associate elements and some selected sound actions
2096 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2098 if (element_info[j].class_name)
2100 int len_class_text = strlen(element_info[j].class_name);
2102 if (len_class_text + 1 < len_effect_text &&
2103 strncmp(sound->token,
2104 element_info[j].class_name, len_class_text) == 0 &&
2105 sound->token[len_class_text] == '.')
2107 int sound_action_value = sound_effect_properties[i];
2109 element_info[j].sound[sound_action_value] = i;
2114 set_sound_parameters(i, sound->parameter);
2117 free(sound_effect_properties);
2120 static void InitGameModeMusicInfo(void)
2122 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
2123 int num_property_mappings = getMusicListPropertyMappingSize();
2124 int default_levelset_music = -1;
2127 // set values to -1 to identify later as "uninitialized" values
2128 for (i = 0; i < MAX_LEVELS; i++)
2129 levelset.music[i] = -1;
2130 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2133 // initialize gamemode/music mapping from static configuration
2134 for (i = 0; gamemode_to_music[i].music > -1; i++)
2136 int gamemode = gamemode_to_music[i].gamemode;
2137 int music = gamemode_to_music[i].music;
2140 gamemode = GAME_MODE_DEFAULT;
2142 menu.music[gamemode] = music;
2145 // initialize gamemode/music mapping from dynamic configuration
2146 for (i = 0; i < num_property_mappings; i++)
2148 int prefix = property_mapping[i].base_index;
2149 int gamemode = property_mapping[i].ext2_index;
2150 int level = property_mapping[i].ext3_index;
2151 int music = property_mapping[i].artwork_index;
2153 if (prefix < 0 || prefix >= NUM_MUSIC_PREFIXES)
2157 gamemode = GAME_MODE_DEFAULT;
2159 // level specific music only allowed for in-game music
2160 if (level != -1 && gamemode == GAME_MODE_DEFAULT)
2161 gamemode = GAME_MODE_PLAYING;
2166 default_levelset_music = music;
2169 if (gamemode == GAME_MODE_PLAYING || gamemode == GAME_MODE_DEFAULT)
2170 levelset.music[level] = music;
2171 if (gamemode != GAME_MODE_PLAYING)
2172 menu.music[gamemode] = music;
2175 // now set all '-1' values to menu specific default values
2176 // (undefined values of "levelset.music[]" might stay at "-1" to
2177 // allow dynamic selection of music files from music directory!)
2178 for (i = 0; i < MAX_LEVELS; i++)
2179 if (levelset.music[i] == -1)
2180 levelset.music[i] = default_levelset_music;
2181 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2182 if (menu.music[i] == -1)
2183 menu.music[i] = menu.music[GAME_MODE_DEFAULT];
2186 static void set_music_parameters(int music, char **parameter_raw)
2188 int parameter[NUM_MUS_ARGS];
2191 // get integer values from string parameters
2192 for (i = 0; i < NUM_MUS_ARGS; i++)
2194 get_parameter_value(parameter_raw[i],
2195 music_config_suffix[i].token,
2196 music_config_suffix[i].type);
2198 // explicit loop mode setting in configuration overrides default value
2199 if (parameter[MUS_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2200 music_info[music].loop = parameter[MUS_ARG_MODE_LOOP];
2203 static void InitMusicInfo(void)
2205 int num_music = getMusicListSize();
2208 checked_free(music_info);
2210 music_info = checked_calloc(num_music * sizeof(struct MusicInfo));
2212 for (i = 0; i < num_music; i++)
2214 struct FileInfo *music = getMusicListEntry(i);
2215 int len_music_text = strlen(music->token);
2217 music_info[i].loop = TRUE; // default: play music in loop mode
2219 // determine all loop music
2221 for (j = 0; music_prefix_info[j].prefix; j++)
2223 int len_prefix_text = strlen(music_prefix_info[j].prefix);
2225 if (len_prefix_text < len_music_text &&
2226 strncmp(music->token,
2227 music_prefix_info[j].prefix, len_prefix_text) == 0)
2229 music_info[i].loop = music_prefix_info[j].is_loop_music;
2235 set_music_parameters(i, music->parameter);
2240 static void InitGameInfoFromArtworkInfo(void)
2242 // special case: store initial value of custom artwork setting
2243 game.use_masked_elements_initial = game.use_masked_elements;
2246 static void ReinitializeGraphics(void)
2248 print_timestamp_init("ReinitializeGraphics");
2250 InitGfxTileSizeInfo(game.tile_size, TILESIZE);
2252 InitGraphicInfo(); // graphic properties mapping
2253 print_timestamp_time("InitGraphicInfo");
2254 InitElementGraphicInfo(); // element game graphic mapping
2255 print_timestamp_time("InitElementGraphicInfo");
2256 InitElementSpecialGraphicInfo(); // element special graphic mapping
2257 print_timestamp_time("InitElementSpecialGraphicInfo");
2259 InitElementSmallImages(); // scale elements to all needed sizes
2260 print_timestamp_time("InitElementSmallImages");
2261 InitScaledImages(); // scale all other images, if needed
2262 print_timestamp_time("InitScaledImages");
2263 InitBitmapPointers(); // set standard size bitmap pointers
2264 print_timestamp_time("InitBitmapPointers");
2265 InitFontGraphicInfo(); // initialize text drawing functions
2266 print_timestamp_time("InitFontGraphicInfo");
2267 InitGlobalAnimGraphicInfo(); // initialize global animation config
2268 print_timestamp_time("InitGlobalAnimGraphicInfo");
2270 InitImageTextures(); // create textures for certain images
2271 print_timestamp_time("InitImageTextures");
2273 InitGraphicInfo_EM(); // graphic mapping for EM engine
2274 print_timestamp_time("InitGraphicInfo_EM");
2276 InitGraphicCompatibilityInfo();
2277 print_timestamp_time("InitGraphicCompatibilityInfo");
2279 SetMainBackgroundImage(IMG_BACKGROUND);
2280 print_timestamp_time("SetMainBackgroundImage");
2281 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
2282 print_timestamp_time("SetDoorBackgroundImage");
2285 print_timestamp_time("InitGadgets");
2287 print_timestamp_time("InitDoors");
2289 InitGameInfoFromArtworkInfo();
2291 print_timestamp_done("ReinitializeGraphics");
2294 static void ReinitializeSounds(void)
2296 InitSoundInfo(); // sound properties mapping
2297 InitElementSoundInfo(); // element game sound mapping
2298 InitGameModeSoundInfo(); // game mode sound mapping
2299 InitGlobalAnimSoundInfo(); // global animation sound settings
2301 InitPlayLevelSound(); // internal game sound settings
2304 static void ReinitializeMusic(void)
2306 InitMusicInfo(); // music properties mapping
2307 InitGameModeMusicInfo(); // game mode music mapping
2308 InitGlobalAnimMusicInfo(); // global animation music settings
2311 static int get_special_property_bit(int element, int property_bit_nr)
2313 struct PropertyBitInfo
2319 static struct PropertyBitInfo pb_can_move_into_acid[] =
2321 // the player may be able fall into acid when gravity is activated
2326 { EL_SP_MURPHY, 0 },
2327 { EL_SOKOBAN_FIELD_PLAYER, 0 },
2329 // all elements that can move may be able to also move into acid
2332 { EL_BUG_RIGHT, 1 },
2335 { EL_SPACESHIP, 2 },
2336 { EL_SPACESHIP_LEFT, 2 },
2337 { EL_SPACESHIP_RIGHT, 2 },
2338 { EL_SPACESHIP_UP, 2 },
2339 { EL_SPACESHIP_DOWN, 2 },
2340 { EL_BD_BUTTERFLY, 3 },
2341 { EL_BD_BUTTERFLY_LEFT, 3 },
2342 { EL_BD_BUTTERFLY_RIGHT, 3 },
2343 { EL_BD_BUTTERFLY_UP, 3 },
2344 { EL_BD_BUTTERFLY_DOWN, 3 },
2345 { EL_BD_FIREFLY, 4 },
2346 { EL_BD_FIREFLY_LEFT, 4 },
2347 { EL_BD_FIREFLY_RIGHT, 4 },
2348 { EL_BD_FIREFLY_UP, 4 },
2349 { EL_BD_FIREFLY_DOWN, 4 },
2351 { EL_YAMYAM_LEFT, 5 },
2352 { EL_YAMYAM_RIGHT, 5 },
2353 { EL_YAMYAM_UP, 5 },
2354 { EL_YAMYAM_DOWN, 5 },
2355 { EL_DARK_YAMYAM, 6 },
2358 { EL_PACMAN_LEFT, 8 },
2359 { EL_PACMAN_RIGHT, 8 },
2360 { EL_PACMAN_UP, 8 },
2361 { EL_PACMAN_DOWN, 8 },
2363 { EL_MOLE_LEFT, 9 },
2364 { EL_MOLE_RIGHT, 9 },
2366 { EL_MOLE_DOWN, 9 },
2370 { EL_SATELLITE, 13 },
2371 { EL_SP_SNIKSNAK, 14 },
2372 { EL_SP_ELECTRON, 15 },
2375 { EL_SPRING_LEFT, 17 },
2376 { EL_SPRING_RIGHT, 17 },
2377 { EL_EMC_ANDROID, 18 },
2382 static struct PropertyBitInfo pb_dont_collide_with[] =
2384 { EL_SP_SNIKSNAK, 0 },
2385 { EL_SP_ELECTRON, 1 },
2393 struct PropertyBitInfo *pb_info;
2396 { EP_CAN_MOVE_INTO_ACID, pb_can_move_into_acid },
2397 { EP_DONT_COLLIDE_WITH, pb_dont_collide_with },
2402 struct PropertyBitInfo *pb_info = NULL;
2405 for (i = 0; pb_definition[i].bit_nr != -1; i++)
2406 if (pb_definition[i].bit_nr == property_bit_nr)
2407 pb_info = pb_definition[i].pb_info;
2409 if (pb_info == NULL)
2412 for (i = 0; pb_info[i].element != -1; i++)
2413 if (pb_info[i].element == element)
2414 return pb_info[i].bit_nr;
2419 void setBitfieldProperty(int *bitfield, int property_bit_nr, int element,
2420 boolean property_value)
2422 int bit_nr = get_special_property_bit(element, property_bit_nr);
2427 *bitfield |= (1 << bit_nr);
2429 *bitfield &= ~(1 << bit_nr);
2433 boolean getBitfieldProperty(int *bitfield, int property_bit_nr, int element)
2435 int bit_nr = get_special_property_bit(element, property_bit_nr);
2438 return ((*bitfield & (1 << bit_nr)) != 0);
2443 static void ResolveGroupElementExt(int group_element, int recursion_depth)
2445 static int group_nr;
2446 static struct ElementGroupInfo *group;
2447 struct ElementGroupInfo *actual_group = element_info[group_element].group;
2450 if (actual_group == NULL) // not yet initialized
2453 if (recursion_depth > NUM_GROUP_ELEMENTS) // recursion too deep
2455 Warn("recursion too deep when resolving group element %d",
2456 group_element - EL_GROUP_START + 1);
2458 // replace element which caused too deep recursion by question mark
2459 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
2464 if (recursion_depth == 0) // initialization
2466 group = actual_group;
2467 group_nr = GROUP_NR(group_element);
2469 group->num_elements_resolved = 0;
2470 group->choice_pos = 0;
2472 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2473 element_info[i].in_group[group_nr] = FALSE;
2476 for (i = 0; i < actual_group->num_elements; i++)
2478 int element = actual_group->element[i];
2480 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
2483 if (IS_GROUP_ELEMENT(element))
2484 ResolveGroupElementExt(element, recursion_depth + 1);
2487 group->element_resolved[group->num_elements_resolved++] = element;
2488 element_info[element].in_group[group_nr] = TRUE;
2493 void ResolveGroupElement(int group_element)
2495 ResolveGroupElementExt(group_element, 0);
2498 void InitElementPropertiesStatic(void)
2500 static boolean clipboard_elements_initialized = FALSE;
2502 static int ep_diggable[] =
2507 EL_SP_BUGGY_BASE_ACTIVATING,
2510 EL_INVISIBLE_SAND_ACTIVE,
2513 // !!! currently not diggable, but handled by 'ep_dont_run_into' !!!
2514 // (if amoeba can grow into anything diggable, maybe keep these out)
2519 EL_SP_BUGGY_BASE_ACTIVE,
2526 static int ep_collectible_only[] =
2548 EL_DYNABOMB_INCREASE_NUMBER,
2549 EL_DYNABOMB_INCREASE_SIZE,
2550 EL_DYNABOMB_INCREASE_POWER,
2568 // !!! handle separately !!!
2569 EL_DC_LANDMINE, // deadly when running into, but can be snapped
2575 static int ep_dont_run_into[] =
2577 // same elements as in 'ep_dont_touch'
2583 // same elements as in 'ep_dont_collide_with'
2595 // !!! maybe this should better be handled by 'ep_diggable' !!!
2600 EL_SP_BUGGY_BASE_ACTIVE,
2607 static int ep_dont_collide_with[] =
2609 // same elements as in 'ep_dont_touch'
2626 static int ep_dont_touch[] =
2636 static int ep_indestructible[] =
2640 EL_ACID_POOL_TOPLEFT,
2641 EL_ACID_POOL_TOPRIGHT,
2642 EL_ACID_POOL_BOTTOMLEFT,
2643 EL_ACID_POOL_BOTTOM,
2644 EL_ACID_POOL_BOTTOMRIGHT,
2645 EL_SP_HARDWARE_GRAY,
2646 EL_SP_HARDWARE_GREEN,
2647 EL_SP_HARDWARE_BLUE,
2649 EL_SP_HARDWARE_YELLOW,
2650 EL_SP_HARDWARE_BASE_1,
2651 EL_SP_HARDWARE_BASE_2,
2652 EL_SP_HARDWARE_BASE_3,
2653 EL_SP_HARDWARE_BASE_4,
2654 EL_SP_HARDWARE_BASE_5,
2655 EL_SP_HARDWARE_BASE_6,
2656 EL_INVISIBLE_STEELWALL,
2657 EL_INVISIBLE_STEELWALL_ACTIVE,
2658 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2659 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
2660 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
2661 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2662 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
2663 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
2664 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2665 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
2666 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
2667 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
2668 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
2669 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
2671 EL_LIGHT_SWITCH_ACTIVE,
2672 EL_SIGN_EXCLAMATION,
2673 EL_SIGN_RADIOACTIVITY,
2680 EL_SIGN_ENTRY_FORBIDDEN,
2681 EL_SIGN_EMERGENCY_EXIT,
2689 EL_STEEL_EXIT_CLOSED,
2691 EL_STEEL_EXIT_OPENING,
2692 EL_STEEL_EXIT_CLOSING,
2693 EL_EM_STEEL_EXIT_CLOSED,
2694 EL_EM_STEEL_EXIT_OPEN,
2695 EL_EM_STEEL_EXIT_OPENING,
2696 EL_EM_STEEL_EXIT_CLOSING,
2697 EL_DC_STEELWALL_1_LEFT,
2698 EL_DC_STEELWALL_1_RIGHT,
2699 EL_DC_STEELWALL_1_TOP,
2700 EL_DC_STEELWALL_1_BOTTOM,
2701 EL_DC_STEELWALL_1_HORIZONTAL,
2702 EL_DC_STEELWALL_1_VERTICAL,
2703 EL_DC_STEELWALL_1_TOPLEFT,
2704 EL_DC_STEELWALL_1_TOPRIGHT,
2705 EL_DC_STEELWALL_1_BOTTOMLEFT,
2706 EL_DC_STEELWALL_1_BOTTOMRIGHT,
2707 EL_DC_STEELWALL_1_TOPLEFT_2,
2708 EL_DC_STEELWALL_1_TOPRIGHT_2,
2709 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
2710 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
2711 EL_DC_STEELWALL_2_LEFT,
2712 EL_DC_STEELWALL_2_RIGHT,
2713 EL_DC_STEELWALL_2_TOP,
2714 EL_DC_STEELWALL_2_BOTTOM,
2715 EL_DC_STEELWALL_2_HORIZONTAL,
2716 EL_DC_STEELWALL_2_VERTICAL,
2717 EL_DC_STEELWALL_2_MIDDLE,
2718 EL_DC_STEELWALL_2_SINGLE,
2719 EL_STEELWALL_SLIPPERY,
2733 EL_GATE_1_GRAY_ACTIVE,
2734 EL_GATE_2_GRAY_ACTIVE,
2735 EL_GATE_3_GRAY_ACTIVE,
2736 EL_GATE_4_GRAY_ACTIVE,
2745 EL_EM_GATE_1_GRAY_ACTIVE,
2746 EL_EM_GATE_2_GRAY_ACTIVE,
2747 EL_EM_GATE_3_GRAY_ACTIVE,
2748 EL_EM_GATE_4_GRAY_ACTIVE,
2757 EL_EMC_GATE_5_GRAY_ACTIVE,
2758 EL_EMC_GATE_6_GRAY_ACTIVE,
2759 EL_EMC_GATE_7_GRAY_ACTIVE,
2760 EL_EMC_GATE_8_GRAY_ACTIVE,
2762 EL_DC_GATE_WHITE_GRAY,
2763 EL_DC_GATE_WHITE_GRAY_ACTIVE,
2764 EL_DC_GATE_FAKE_GRAY,
2766 EL_SWITCHGATE_OPENING,
2767 EL_SWITCHGATE_CLOSED,
2768 EL_SWITCHGATE_CLOSING,
2769 EL_DC_SWITCHGATE_SWITCH_UP,
2770 EL_DC_SWITCHGATE_SWITCH_DOWN,
2772 EL_TIMEGATE_OPENING,
2774 EL_TIMEGATE_CLOSING,
2775 EL_DC_TIMEGATE_SWITCH,
2776 EL_DC_TIMEGATE_SWITCH_ACTIVE,
2780 EL_TUBE_VERTICAL_LEFT,
2781 EL_TUBE_VERTICAL_RIGHT,
2782 EL_TUBE_HORIZONTAL_UP,
2783 EL_TUBE_HORIZONTAL_DOWN,
2788 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
2789 EL_EXPANDABLE_STEELWALL_VERTICAL,
2790 EL_EXPANDABLE_STEELWALL_ANY,
2795 static int ep_slippery[] =
2809 EL_ROBOT_WHEEL_ACTIVE,
2815 EL_ACID_POOL_TOPLEFT,
2816 EL_ACID_POOL_TOPRIGHT,
2826 EL_STEELWALL_SLIPPERY,
2829 EL_EMC_WALL_SLIPPERY_1,
2830 EL_EMC_WALL_SLIPPERY_2,
2831 EL_EMC_WALL_SLIPPERY_3,
2832 EL_EMC_WALL_SLIPPERY_4,
2834 EL_EMC_MAGIC_BALL_ACTIVE,
2839 static int ep_can_change[] =
2844 static int ep_can_move[] =
2846 // same elements as in 'pb_can_move_into_acid'
2869 static int ep_can_fall[] =
2884 EL_QUICKSAND_FAST_FULL,
2886 EL_BD_MAGIC_WALL_FULL,
2887 EL_DC_MAGIC_WALL_FULL,
2901 static int ep_can_smash_player[] =
2927 static int ep_can_smash_enemies[] =
2936 static int ep_can_smash_everything[] =
2945 static int ep_explodes_by_fire[] =
2947 // same elements as in 'ep_explodes_impact'
2952 // same elements as in 'ep_explodes_smashed'
2962 EL_EM_DYNAMITE_ACTIVE,
2963 EL_DYNABOMB_PLAYER_1_ACTIVE,
2964 EL_DYNABOMB_PLAYER_2_ACTIVE,
2965 EL_DYNABOMB_PLAYER_3_ACTIVE,
2966 EL_DYNABOMB_PLAYER_4_ACTIVE,
2967 EL_DYNABOMB_INCREASE_NUMBER,
2968 EL_DYNABOMB_INCREASE_SIZE,
2969 EL_DYNABOMB_INCREASE_POWER,
2970 EL_SP_DISK_RED_ACTIVE,
2984 static int ep_explodes_smashed[] =
2986 // same elements as in 'ep_explodes_impact'
3000 static int ep_explodes_impact[] =
3009 static int ep_walkable_over[] =
3029 EL_SOKOBAN_FIELD_EMPTY,
3036 EL_EM_STEEL_EXIT_OPEN,
3037 EL_EM_STEEL_EXIT_OPENING,
3046 EL_GATE_1_GRAY_ACTIVE,
3047 EL_GATE_2_GRAY_ACTIVE,
3048 EL_GATE_3_GRAY_ACTIVE,
3049 EL_GATE_4_GRAY_ACTIVE,
3057 static int ep_walkable_inside[] =
3062 EL_TUBE_VERTICAL_LEFT,
3063 EL_TUBE_VERTICAL_RIGHT,
3064 EL_TUBE_HORIZONTAL_UP,
3065 EL_TUBE_HORIZONTAL_DOWN,
3074 static int ep_walkable_under[] =
3079 static int ep_passable_over[] =
3089 EL_EM_GATE_1_GRAY_ACTIVE,
3090 EL_EM_GATE_2_GRAY_ACTIVE,
3091 EL_EM_GATE_3_GRAY_ACTIVE,
3092 EL_EM_GATE_4_GRAY_ACTIVE,
3101 EL_EMC_GATE_5_GRAY_ACTIVE,
3102 EL_EMC_GATE_6_GRAY_ACTIVE,
3103 EL_EMC_GATE_7_GRAY_ACTIVE,
3104 EL_EMC_GATE_8_GRAY_ACTIVE,
3106 EL_DC_GATE_WHITE_GRAY,
3107 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3114 static int ep_passable_inside[] =
3120 EL_SP_PORT_HORIZONTAL,
3121 EL_SP_PORT_VERTICAL,
3123 EL_SP_GRAVITY_PORT_LEFT,
3124 EL_SP_GRAVITY_PORT_RIGHT,
3125 EL_SP_GRAVITY_PORT_UP,
3126 EL_SP_GRAVITY_PORT_DOWN,
3127 EL_SP_GRAVITY_ON_PORT_LEFT,
3128 EL_SP_GRAVITY_ON_PORT_RIGHT,
3129 EL_SP_GRAVITY_ON_PORT_UP,
3130 EL_SP_GRAVITY_ON_PORT_DOWN,
3131 EL_SP_GRAVITY_OFF_PORT_LEFT,
3132 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3133 EL_SP_GRAVITY_OFF_PORT_UP,
3134 EL_SP_GRAVITY_OFF_PORT_DOWN,
3139 static int ep_passable_under[] =
3144 static int ep_droppable[] =
3149 static int ep_explodes_1x1_old[] =
3154 static int ep_pushable[] =
3166 EL_SOKOBAN_FIELD_FULL,
3175 static int ep_explodes_cross_old[] =
3180 static int ep_protected[] =
3182 // same elements as in 'ep_walkable_inside'
3186 EL_TUBE_VERTICAL_LEFT,
3187 EL_TUBE_VERTICAL_RIGHT,
3188 EL_TUBE_HORIZONTAL_UP,
3189 EL_TUBE_HORIZONTAL_DOWN,
3195 // same elements as in 'ep_passable_over'
3204 EL_EM_GATE_1_GRAY_ACTIVE,
3205 EL_EM_GATE_2_GRAY_ACTIVE,
3206 EL_EM_GATE_3_GRAY_ACTIVE,
3207 EL_EM_GATE_4_GRAY_ACTIVE,
3216 EL_EMC_GATE_5_GRAY_ACTIVE,
3217 EL_EMC_GATE_6_GRAY_ACTIVE,
3218 EL_EMC_GATE_7_GRAY_ACTIVE,
3219 EL_EMC_GATE_8_GRAY_ACTIVE,
3221 EL_DC_GATE_WHITE_GRAY,
3222 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3226 // same elements as in 'ep_passable_inside'
3231 EL_SP_PORT_HORIZONTAL,
3232 EL_SP_PORT_VERTICAL,
3234 EL_SP_GRAVITY_PORT_LEFT,
3235 EL_SP_GRAVITY_PORT_RIGHT,
3236 EL_SP_GRAVITY_PORT_UP,
3237 EL_SP_GRAVITY_PORT_DOWN,
3238 EL_SP_GRAVITY_ON_PORT_LEFT,
3239 EL_SP_GRAVITY_ON_PORT_RIGHT,
3240 EL_SP_GRAVITY_ON_PORT_UP,
3241 EL_SP_GRAVITY_ON_PORT_DOWN,
3242 EL_SP_GRAVITY_OFF_PORT_LEFT,
3243 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3244 EL_SP_GRAVITY_OFF_PORT_UP,
3245 EL_SP_GRAVITY_OFF_PORT_DOWN,
3250 static int ep_throwable[] =
3255 static int ep_can_explode[] =
3257 // same elements as in 'ep_explodes_impact'
3262 // same elements as in 'ep_explodes_smashed'
3268 // elements that can explode by explosion or by dragonfire
3272 EL_EM_DYNAMITE_ACTIVE,
3273 EL_DYNABOMB_PLAYER_1_ACTIVE,
3274 EL_DYNABOMB_PLAYER_2_ACTIVE,
3275 EL_DYNABOMB_PLAYER_3_ACTIVE,
3276 EL_DYNABOMB_PLAYER_4_ACTIVE,
3277 EL_DYNABOMB_INCREASE_NUMBER,
3278 EL_DYNABOMB_INCREASE_SIZE,
3279 EL_DYNABOMB_INCREASE_POWER,
3280 EL_SP_DISK_RED_ACTIVE,
3288 // elements that can explode only by explosion
3294 static int ep_gravity_reachable[] =
3300 EL_INVISIBLE_SAND_ACTIVE,
3305 EL_SP_PORT_HORIZONTAL,
3306 EL_SP_PORT_VERTICAL,
3308 EL_SP_GRAVITY_PORT_LEFT,
3309 EL_SP_GRAVITY_PORT_RIGHT,
3310 EL_SP_GRAVITY_PORT_UP,
3311 EL_SP_GRAVITY_PORT_DOWN,
3312 EL_SP_GRAVITY_ON_PORT_LEFT,
3313 EL_SP_GRAVITY_ON_PORT_RIGHT,
3314 EL_SP_GRAVITY_ON_PORT_UP,
3315 EL_SP_GRAVITY_ON_PORT_DOWN,
3316 EL_SP_GRAVITY_OFF_PORT_LEFT,
3317 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3318 EL_SP_GRAVITY_OFF_PORT_UP,
3319 EL_SP_GRAVITY_OFF_PORT_DOWN,
3325 static int ep_empty_space[] =
3348 static int ep_player[] =
3355 EL_SOKOBAN_FIELD_PLAYER,
3361 static int ep_can_pass_magic_wall[] =
3375 static int ep_can_pass_dc_magic_wall[] =
3391 static int ep_switchable[] =
3395 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3396 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3397 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3398 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3399 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3400 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3401 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3402 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3403 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3404 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3405 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3406 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3407 EL_SWITCHGATE_SWITCH_UP,
3408 EL_SWITCHGATE_SWITCH_DOWN,
3409 EL_DC_SWITCHGATE_SWITCH_UP,
3410 EL_DC_SWITCHGATE_SWITCH_DOWN,
3412 EL_LIGHT_SWITCH_ACTIVE,
3414 EL_DC_TIMEGATE_SWITCH,
3415 EL_BALLOON_SWITCH_LEFT,
3416 EL_BALLOON_SWITCH_RIGHT,
3417 EL_BALLOON_SWITCH_UP,
3418 EL_BALLOON_SWITCH_DOWN,
3419 EL_BALLOON_SWITCH_ANY,
3420 EL_BALLOON_SWITCH_NONE,
3423 EL_EMC_MAGIC_BALL_SWITCH,
3424 EL_EMC_MAGIC_BALL_SWITCH_ACTIVE,
3429 static int ep_bd_element[] =
3463 static int ep_sp_element[] =
3465 // should always be valid
3468 // standard classic Supaplex elements
3475 EL_SP_HARDWARE_GRAY,
3483 EL_SP_GRAVITY_PORT_RIGHT,
3484 EL_SP_GRAVITY_PORT_DOWN,
3485 EL_SP_GRAVITY_PORT_LEFT,
3486 EL_SP_GRAVITY_PORT_UP,
3491 EL_SP_PORT_VERTICAL,
3492 EL_SP_PORT_HORIZONTAL,
3498 EL_SP_HARDWARE_BASE_1,
3499 EL_SP_HARDWARE_GREEN,
3500 EL_SP_HARDWARE_BLUE,
3502 EL_SP_HARDWARE_YELLOW,
3503 EL_SP_HARDWARE_BASE_2,
3504 EL_SP_HARDWARE_BASE_3,
3505 EL_SP_HARDWARE_BASE_4,
3506 EL_SP_HARDWARE_BASE_5,
3507 EL_SP_HARDWARE_BASE_6,
3511 // additional elements that appeared in newer Supaplex levels
3514 // additional gravity port elements (not switching, but setting gravity)
3515 EL_SP_GRAVITY_ON_PORT_LEFT,
3516 EL_SP_GRAVITY_ON_PORT_RIGHT,
3517 EL_SP_GRAVITY_ON_PORT_UP,
3518 EL_SP_GRAVITY_ON_PORT_DOWN,
3519 EL_SP_GRAVITY_OFF_PORT_LEFT,
3520 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3521 EL_SP_GRAVITY_OFF_PORT_UP,
3522 EL_SP_GRAVITY_OFF_PORT_DOWN,
3524 // more than one Murphy in a level results in an inactive clone
3527 // runtime Supaplex elements
3528 EL_SP_DISK_RED_ACTIVE,
3529 EL_SP_TERMINAL_ACTIVE,
3530 EL_SP_BUGGY_BASE_ACTIVATING,
3531 EL_SP_BUGGY_BASE_ACTIVE,
3538 static int ep_sb_element[] =
3543 EL_SOKOBAN_FIELD_EMPTY,
3544 EL_SOKOBAN_FIELD_FULL,
3545 EL_SOKOBAN_FIELD_PLAYER,
3550 EL_INVISIBLE_STEELWALL,
3555 static int ep_gem[] =
3567 static int ep_food_dark_yamyam[] =
3595 static int ep_food_penguin[] =
3609 static int ep_food_pig[] =
3621 static int ep_historic_wall[] =
3632 EL_GATE_1_GRAY_ACTIVE,
3633 EL_GATE_2_GRAY_ACTIVE,
3634 EL_GATE_3_GRAY_ACTIVE,
3635 EL_GATE_4_GRAY_ACTIVE,
3644 EL_EM_GATE_1_GRAY_ACTIVE,
3645 EL_EM_GATE_2_GRAY_ACTIVE,
3646 EL_EM_GATE_3_GRAY_ACTIVE,
3647 EL_EM_GATE_4_GRAY_ACTIVE,
3654 EL_EXPANDABLE_WALL_HORIZONTAL,
3655 EL_EXPANDABLE_WALL_VERTICAL,
3656 EL_EXPANDABLE_WALL_ANY,
3657 EL_EXPANDABLE_WALL_GROWING,
3658 EL_BD_EXPANDABLE_WALL,
3665 EL_SP_HARDWARE_GRAY,
3666 EL_SP_HARDWARE_GREEN,
3667 EL_SP_HARDWARE_BLUE,
3669 EL_SP_HARDWARE_YELLOW,
3670 EL_SP_HARDWARE_BASE_1,
3671 EL_SP_HARDWARE_BASE_2,
3672 EL_SP_HARDWARE_BASE_3,
3673 EL_SP_HARDWARE_BASE_4,
3674 EL_SP_HARDWARE_BASE_5,
3675 EL_SP_HARDWARE_BASE_6,
3677 EL_SP_TERMINAL_ACTIVE,
3680 EL_INVISIBLE_STEELWALL,
3681 EL_INVISIBLE_STEELWALL_ACTIVE,
3683 EL_INVISIBLE_WALL_ACTIVE,
3684 EL_STEELWALL_SLIPPERY,
3701 static int ep_historic_solid[] =
3705 EL_EXPANDABLE_WALL_HORIZONTAL,
3706 EL_EXPANDABLE_WALL_VERTICAL,
3707 EL_EXPANDABLE_WALL_ANY,
3708 EL_BD_EXPANDABLE_WALL,
3721 EL_QUICKSAND_FILLING,
3722 EL_QUICKSAND_EMPTYING,
3724 EL_MAGIC_WALL_ACTIVE,
3725 EL_MAGIC_WALL_EMPTYING,
3726 EL_MAGIC_WALL_FILLING,
3730 EL_BD_MAGIC_WALL_ACTIVE,
3731 EL_BD_MAGIC_WALL_EMPTYING,
3732 EL_BD_MAGIC_WALL_FULL,
3733 EL_BD_MAGIC_WALL_FILLING,
3734 EL_BD_MAGIC_WALL_DEAD,
3743 EL_SP_TERMINAL_ACTIVE,
3747 EL_INVISIBLE_WALL_ACTIVE,
3748 EL_SWITCHGATE_SWITCH_UP,
3749 EL_SWITCHGATE_SWITCH_DOWN,
3751 EL_TIMEGATE_SWITCH_ACTIVE,
3763 // the following elements are a direct copy of "indestructible" elements,
3764 // except "EL_ACID", which is "indestructible", but not "solid"!
3769 EL_ACID_POOL_TOPLEFT,
3770 EL_ACID_POOL_TOPRIGHT,
3771 EL_ACID_POOL_BOTTOMLEFT,
3772 EL_ACID_POOL_BOTTOM,
3773 EL_ACID_POOL_BOTTOMRIGHT,
3774 EL_SP_HARDWARE_GRAY,
3775 EL_SP_HARDWARE_GREEN,
3776 EL_SP_HARDWARE_BLUE,
3778 EL_SP_HARDWARE_YELLOW,
3779 EL_SP_HARDWARE_BASE_1,
3780 EL_SP_HARDWARE_BASE_2,
3781 EL_SP_HARDWARE_BASE_3,
3782 EL_SP_HARDWARE_BASE_4,
3783 EL_SP_HARDWARE_BASE_5,
3784 EL_SP_HARDWARE_BASE_6,
3785 EL_INVISIBLE_STEELWALL,
3786 EL_INVISIBLE_STEELWALL_ACTIVE,
3787 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3788 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3789 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3790 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3791 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3792 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3793 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3794 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3795 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3796 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3797 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3798 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3800 EL_LIGHT_SWITCH_ACTIVE,
3801 EL_SIGN_EXCLAMATION,
3802 EL_SIGN_RADIOACTIVITY,
3809 EL_SIGN_ENTRY_FORBIDDEN,
3810 EL_SIGN_EMERGENCY_EXIT,
3818 EL_STEEL_EXIT_CLOSED,
3820 EL_STEEL_EXIT_OPENING,
3821 EL_STEEL_EXIT_CLOSING,
3822 EL_EM_STEEL_EXIT_CLOSED,
3823 EL_EM_STEEL_EXIT_OPEN,
3824 EL_EM_STEEL_EXIT_OPENING,
3825 EL_EM_STEEL_EXIT_CLOSING,
3826 EL_DC_STEELWALL_1_LEFT,
3827 EL_DC_STEELWALL_1_RIGHT,
3828 EL_DC_STEELWALL_1_TOP,
3829 EL_DC_STEELWALL_1_BOTTOM,
3830 EL_DC_STEELWALL_1_HORIZONTAL,
3831 EL_DC_STEELWALL_1_VERTICAL,
3832 EL_DC_STEELWALL_1_TOPLEFT,
3833 EL_DC_STEELWALL_1_TOPRIGHT,
3834 EL_DC_STEELWALL_1_BOTTOMLEFT,
3835 EL_DC_STEELWALL_1_BOTTOMRIGHT,
3836 EL_DC_STEELWALL_1_TOPLEFT_2,
3837 EL_DC_STEELWALL_1_TOPRIGHT_2,
3838 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
3839 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
3840 EL_DC_STEELWALL_2_LEFT,
3841 EL_DC_STEELWALL_2_RIGHT,
3842 EL_DC_STEELWALL_2_TOP,
3843 EL_DC_STEELWALL_2_BOTTOM,
3844 EL_DC_STEELWALL_2_HORIZONTAL,
3845 EL_DC_STEELWALL_2_VERTICAL,
3846 EL_DC_STEELWALL_2_MIDDLE,
3847 EL_DC_STEELWALL_2_SINGLE,
3848 EL_STEELWALL_SLIPPERY,
3862 EL_GATE_1_GRAY_ACTIVE,
3863 EL_GATE_2_GRAY_ACTIVE,
3864 EL_GATE_3_GRAY_ACTIVE,
3865 EL_GATE_4_GRAY_ACTIVE,
3874 EL_EM_GATE_1_GRAY_ACTIVE,
3875 EL_EM_GATE_2_GRAY_ACTIVE,
3876 EL_EM_GATE_3_GRAY_ACTIVE,
3877 EL_EM_GATE_4_GRAY_ACTIVE,
3886 EL_EMC_GATE_5_GRAY_ACTIVE,
3887 EL_EMC_GATE_6_GRAY_ACTIVE,
3888 EL_EMC_GATE_7_GRAY_ACTIVE,
3889 EL_EMC_GATE_8_GRAY_ACTIVE,
3891 EL_DC_GATE_WHITE_GRAY,
3892 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3893 EL_DC_GATE_FAKE_GRAY,
3895 EL_SWITCHGATE_OPENING,
3896 EL_SWITCHGATE_CLOSED,
3897 EL_SWITCHGATE_CLOSING,
3898 EL_DC_SWITCHGATE_SWITCH_UP,
3899 EL_DC_SWITCHGATE_SWITCH_DOWN,
3901 EL_TIMEGATE_OPENING,
3903 EL_TIMEGATE_CLOSING,
3904 EL_DC_TIMEGATE_SWITCH,
3905 EL_DC_TIMEGATE_SWITCH_ACTIVE,
3909 EL_TUBE_VERTICAL_LEFT,
3910 EL_TUBE_VERTICAL_RIGHT,
3911 EL_TUBE_HORIZONTAL_UP,
3912 EL_TUBE_HORIZONTAL_DOWN,
3917 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
3918 EL_EXPANDABLE_STEELWALL_VERTICAL,
3919 EL_EXPANDABLE_STEELWALL_ANY,
3924 static int ep_classic_enemy[] =
3941 static int ep_belt[] =
3943 EL_CONVEYOR_BELT_1_LEFT,
3944 EL_CONVEYOR_BELT_1_MIDDLE,
3945 EL_CONVEYOR_BELT_1_RIGHT,
3946 EL_CONVEYOR_BELT_2_LEFT,
3947 EL_CONVEYOR_BELT_2_MIDDLE,
3948 EL_CONVEYOR_BELT_2_RIGHT,
3949 EL_CONVEYOR_BELT_3_LEFT,
3950 EL_CONVEYOR_BELT_3_MIDDLE,
3951 EL_CONVEYOR_BELT_3_RIGHT,
3952 EL_CONVEYOR_BELT_4_LEFT,
3953 EL_CONVEYOR_BELT_4_MIDDLE,
3954 EL_CONVEYOR_BELT_4_RIGHT,
3959 static int ep_belt_active[] =
3961 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3962 EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE,
3963 EL_CONVEYOR_BELT_1_RIGHT_ACTIVE,
3964 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3965 EL_CONVEYOR_BELT_2_MIDDLE_ACTIVE,
3966 EL_CONVEYOR_BELT_2_RIGHT_ACTIVE,
3967 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3968 EL_CONVEYOR_BELT_3_MIDDLE_ACTIVE,
3969 EL_CONVEYOR_BELT_3_RIGHT_ACTIVE,
3970 EL_CONVEYOR_BELT_4_LEFT_ACTIVE,
3971 EL_CONVEYOR_BELT_4_MIDDLE_ACTIVE,
3972 EL_CONVEYOR_BELT_4_RIGHT_ACTIVE,
3977 static int ep_belt_switch[] =
3979 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3980 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3981 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3982 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3983 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3984 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3985 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3986 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3987 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3988 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3989 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3990 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3995 static int ep_tube[] =
4002 EL_TUBE_HORIZONTAL_UP,
4003 EL_TUBE_HORIZONTAL_DOWN,
4005 EL_TUBE_VERTICAL_LEFT,
4006 EL_TUBE_VERTICAL_RIGHT,
4012 static int ep_acid_pool[] =
4014 EL_ACID_POOL_TOPLEFT,
4015 EL_ACID_POOL_TOPRIGHT,
4016 EL_ACID_POOL_BOTTOMLEFT,
4017 EL_ACID_POOL_BOTTOM,
4018 EL_ACID_POOL_BOTTOMRIGHT,
4023 static int ep_keygate[] =
4033 EL_GATE_1_GRAY_ACTIVE,
4034 EL_GATE_2_GRAY_ACTIVE,
4035 EL_GATE_3_GRAY_ACTIVE,
4036 EL_GATE_4_GRAY_ACTIVE,
4045 EL_EM_GATE_1_GRAY_ACTIVE,
4046 EL_EM_GATE_2_GRAY_ACTIVE,
4047 EL_EM_GATE_3_GRAY_ACTIVE,
4048 EL_EM_GATE_4_GRAY_ACTIVE,
4057 EL_EMC_GATE_5_GRAY_ACTIVE,
4058 EL_EMC_GATE_6_GRAY_ACTIVE,
4059 EL_EMC_GATE_7_GRAY_ACTIVE,
4060 EL_EMC_GATE_8_GRAY_ACTIVE,
4062 EL_DC_GATE_WHITE_GRAY,
4063 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4068 static int ep_amoeboid[] =
4080 static int ep_amoebalive[] =
4091 static int ep_has_editor_content[] =
4097 EL_SOKOBAN_FIELD_PLAYER,
4114 static int ep_can_turn_each_move[] =
4116 // !!! do something with this one !!!
4120 static int ep_can_grow[] =
4134 static int ep_active_bomb[] =
4137 EL_EM_DYNAMITE_ACTIVE,
4138 EL_DYNABOMB_PLAYER_1_ACTIVE,
4139 EL_DYNABOMB_PLAYER_2_ACTIVE,
4140 EL_DYNABOMB_PLAYER_3_ACTIVE,
4141 EL_DYNABOMB_PLAYER_4_ACTIVE,
4142 EL_SP_DISK_RED_ACTIVE,
4147 static int ep_inactive[] =
4173 EL_QUICKSAND_FAST_EMPTY,
4196 EL_GATE_1_GRAY_ACTIVE,
4197 EL_GATE_2_GRAY_ACTIVE,
4198 EL_GATE_3_GRAY_ACTIVE,
4199 EL_GATE_4_GRAY_ACTIVE,
4208 EL_EM_GATE_1_GRAY_ACTIVE,
4209 EL_EM_GATE_2_GRAY_ACTIVE,
4210 EL_EM_GATE_3_GRAY_ACTIVE,
4211 EL_EM_GATE_4_GRAY_ACTIVE,
4220 EL_EMC_GATE_5_GRAY_ACTIVE,
4221 EL_EMC_GATE_6_GRAY_ACTIVE,
4222 EL_EMC_GATE_7_GRAY_ACTIVE,
4223 EL_EMC_GATE_8_GRAY_ACTIVE,
4225 EL_DC_GATE_WHITE_GRAY,
4226 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4227 EL_DC_GATE_FAKE_GRAY,
4230 EL_INVISIBLE_STEELWALL,
4238 EL_WALL_EMERALD_YELLOW,
4239 EL_DYNABOMB_INCREASE_NUMBER,
4240 EL_DYNABOMB_INCREASE_SIZE,
4241 EL_DYNABOMB_INCREASE_POWER,
4245 EL_SOKOBAN_FIELD_EMPTY,
4246 EL_SOKOBAN_FIELD_FULL,
4247 EL_WALL_EMERALD_RED,
4248 EL_WALL_EMERALD_PURPLE,
4249 EL_ACID_POOL_TOPLEFT,
4250 EL_ACID_POOL_TOPRIGHT,
4251 EL_ACID_POOL_BOTTOMLEFT,
4252 EL_ACID_POOL_BOTTOM,
4253 EL_ACID_POOL_BOTTOMRIGHT,
4257 EL_BD_MAGIC_WALL_DEAD,
4259 EL_DC_MAGIC_WALL_DEAD,
4260 EL_AMOEBA_TO_DIAMOND,
4268 EL_SP_GRAVITY_PORT_RIGHT,
4269 EL_SP_GRAVITY_PORT_DOWN,
4270 EL_SP_GRAVITY_PORT_LEFT,
4271 EL_SP_GRAVITY_PORT_UP,
4272 EL_SP_PORT_HORIZONTAL,
4273 EL_SP_PORT_VERTICAL,
4284 EL_SP_HARDWARE_GRAY,
4285 EL_SP_HARDWARE_GREEN,
4286 EL_SP_HARDWARE_BLUE,
4288 EL_SP_HARDWARE_YELLOW,
4289 EL_SP_HARDWARE_BASE_1,
4290 EL_SP_HARDWARE_BASE_2,
4291 EL_SP_HARDWARE_BASE_3,
4292 EL_SP_HARDWARE_BASE_4,
4293 EL_SP_HARDWARE_BASE_5,
4294 EL_SP_HARDWARE_BASE_6,
4295 EL_SP_GRAVITY_ON_PORT_LEFT,
4296 EL_SP_GRAVITY_ON_PORT_RIGHT,
4297 EL_SP_GRAVITY_ON_PORT_UP,
4298 EL_SP_GRAVITY_ON_PORT_DOWN,
4299 EL_SP_GRAVITY_OFF_PORT_LEFT,
4300 EL_SP_GRAVITY_OFF_PORT_RIGHT,
4301 EL_SP_GRAVITY_OFF_PORT_UP,
4302 EL_SP_GRAVITY_OFF_PORT_DOWN,
4303 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4304 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4305 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4306 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4307 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4308 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4309 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4310 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4311 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4312 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4313 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4314 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4315 EL_SIGN_EXCLAMATION,
4316 EL_SIGN_RADIOACTIVITY,
4323 EL_SIGN_ENTRY_FORBIDDEN,
4324 EL_SIGN_EMERGENCY_EXIT,
4332 EL_DC_STEELWALL_1_LEFT,
4333 EL_DC_STEELWALL_1_RIGHT,
4334 EL_DC_STEELWALL_1_TOP,
4335 EL_DC_STEELWALL_1_BOTTOM,
4336 EL_DC_STEELWALL_1_HORIZONTAL,
4337 EL_DC_STEELWALL_1_VERTICAL,
4338 EL_DC_STEELWALL_1_TOPLEFT,
4339 EL_DC_STEELWALL_1_TOPRIGHT,
4340 EL_DC_STEELWALL_1_BOTTOMLEFT,
4341 EL_DC_STEELWALL_1_BOTTOMRIGHT,
4342 EL_DC_STEELWALL_1_TOPLEFT_2,
4343 EL_DC_STEELWALL_1_TOPRIGHT_2,
4344 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
4345 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
4346 EL_DC_STEELWALL_2_LEFT,
4347 EL_DC_STEELWALL_2_RIGHT,
4348 EL_DC_STEELWALL_2_TOP,
4349 EL_DC_STEELWALL_2_BOTTOM,
4350 EL_DC_STEELWALL_2_HORIZONTAL,
4351 EL_DC_STEELWALL_2_VERTICAL,
4352 EL_DC_STEELWALL_2_MIDDLE,
4353 EL_DC_STEELWALL_2_SINGLE,
4354 EL_STEELWALL_SLIPPERY,
4359 EL_EMC_WALL_SLIPPERY_1,
4360 EL_EMC_WALL_SLIPPERY_2,
4361 EL_EMC_WALL_SLIPPERY_3,
4362 EL_EMC_WALL_SLIPPERY_4,
4383 static int ep_em_slippery_wall[] =
4388 static int ep_gfx_crumbled[] =
4399 static int ep_editor_cascade_active[] =
4401 EL_INTERNAL_CASCADE_BD_ACTIVE,
4402 EL_INTERNAL_CASCADE_EM_ACTIVE,
4403 EL_INTERNAL_CASCADE_EMC_ACTIVE,
4404 EL_INTERNAL_CASCADE_RND_ACTIVE,
4405 EL_INTERNAL_CASCADE_SB_ACTIVE,
4406 EL_INTERNAL_CASCADE_SP_ACTIVE,
4407 EL_INTERNAL_CASCADE_DC_ACTIVE,
4408 EL_INTERNAL_CASCADE_DX_ACTIVE,
4409 EL_INTERNAL_CASCADE_MM_ACTIVE,
4410 EL_INTERNAL_CASCADE_DF_ACTIVE,
4411 EL_INTERNAL_CASCADE_CHARS_ACTIVE,
4412 EL_INTERNAL_CASCADE_STEEL_CHARS_ACTIVE,
4413 EL_INTERNAL_CASCADE_CE_ACTIVE,
4414 EL_INTERNAL_CASCADE_GE_ACTIVE,
4415 EL_INTERNAL_CASCADE_ES_ACTIVE,
4416 EL_INTERNAL_CASCADE_REF_ACTIVE,
4417 EL_INTERNAL_CASCADE_USER_ACTIVE,
4418 EL_INTERNAL_CASCADE_DYNAMIC_ACTIVE,
4423 static int ep_editor_cascade_inactive[] =
4425 EL_INTERNAL_CASCADE_BD,
4426 EL_INTERNAL_CASCADE_EM,
4427 EL_INTERNAL_CASCADE_EMC,
4428 EL_INTERNAL_CASCADE_RND,
4429 EL_INTERNAL_CASCADE_SB,
4430 EL_INTERNAL_CASCADE_SP,
4431 EL_INTERNAL_CASCADE_DC,
4432 EL_INTERNAL_CASCADE_DX,
4433 EL_INTERNAL_CASCADE_MM,
4434 EL_INTERNAL_CASCADE_DF,
4435 EL_INTERNAL_CASCADE_CHARS,
4436 EL_INTERNAL_CASCADE_STEEL_CHARS,
4437 EL_INTERNAL_CASCADE_CE,
4438 EL_INTERNAL_CASCADE_GE,
4439 EL_INTERNAL_CASCADE_ES,
4440 EL_INTERNAL_CASCADE_REF,
4441 EL_INTERNAL_CASCADE_USER,
4442 EL_INTERNAL_CASCADE_DYNAMIC,
4447 static int ep_obsolete[] =
4451 EL_EM_KEY_1_FILE_OBSOLETE,
4452 EL_EM_KEY_2_FILE_OBSOLETE,
4453 EL_EM_KEY_3_FILE_OBSOLETE,
4454 EL_EM_KEY_4_FILE_OBSOLETE,
4455 EL_ENVELOPE_OBSOLETE,
4464 } element_properties[] =
4466 { ep_diggable, EP_DIGGABLE },
4467 { ep_collectible_only, EP_COLLECTIBLE_ONLY },
4468 { ep_dont_run_into, EP_DONT_RUN_INTO },
4469 { ep_dont_collide_with, EP_DONT_COLLIDE_WITH },
4470 { ep_dont_touch, EP_DONT_TOUCH },
4471 { ep_indestructible, EP_INDESTRUCTIBLE },
4472 { ep_slippery, EP_SLIPPERY },
4473 { ep_can_change, EP_CAN_CHANGE },
4474 { ep_can_move, EP_CAN_MOVE },
4475 { ep_can_fall, EP_CAN_FALL },
4476 { ep_can_smash_player, EP_CAN_SMASH_PLAYER },
4477 { ep_can_smash_enemies, EP_CAN_SMASH_ENEMIES },
4478 { ep_can_smash_everything, EP_CAN_SMASH_EVERYTHING },
4479 { ep_explodes_by_fire, EP_EXPLODES_BY_FIRE },
4480 { ep_explodes_smashed, EP_EXPLODES_SMASHED },
4481 { ep_explodes_impact, EP_EXPLODES_IMPACT },
4482 { ep_walkable_over, EP_WALKABLE_OVER },
4483 { ep_walkable_inside, EP_WALKABLE_INSIDE },
4484 { ep_walkable_under, EP_WALKABLE_UNDER },
4485 { ep_passable_over, EP_PASSABLE_OVER },
4486 { ep_passable_inside, EP_PASSABLE_INSIDE },
4487 { ep_passable_under, EP_PASSABLE_UNDER },
4488 { ep_droppable, EP_DROPPABLE },
4489 { ep_explodes_1x1_old, EP_EXPLODES_1X1_OLD },
4490 { ep_pushable, EP_PUSHABLE },
4491 { ep_explodes_cross_old, EP_EXPLODES_CROSS_OLD },
4492 { ep_protected, EP_PROTECTED },
4493 { ep_throwable, EP_THROWABLE },
4494 { ep_can_explode, EP_CAN_EXPLODE },
4495 { ep_gravity_reachable, EP_GRAVITY_REACHABLE },
4497 { ep_empty_space, EP_EMPTY_SPACE },
4498 { ep_player, EP_PLAYER },
4499 { ep_can_pass_magic_wall, EP_CAN_PASS_MAGIC_WALL },
4500 { ep_can_pass_dc_magic_wall, EP_CAN_PASS_DC_MAGIC_WALL },
4501 { ep_switchable, EP_SWITCHABLE },
4502 { ep_bd_element, EP_BD_ELEMENT },
4503 { ep_sp_element, EP_SP_ELEMENT },
4504 { ep_sb_element, EP_SB_ELEMENT },
4506 { ep_food_dark_yamyam, EP_FOOD_DARK_YAMYAM },
4507 { ep_food_penguin, EP_FOOD_PENGUIN },
4508 { ep_food_pig, EP_FOOD_PIG },
4509 { ep_historic_wall, EP_HISTORIC_WALL },
4510 { ep_historic_solid, EP_HISTORIC_SOLID },
4511 { ep_classic_enemy, EP_CLASSIC_ENEMY },
4512 { ep_belt, EP_BELT },
4513 { ep_belt_active, EP_BELT_ACTIVE },
4514 { ep_belt_switch, EP_BELT_SWITCH },
4515 { ep_tube, EP_TUBE },
4516 { ep_acid_pool, EP_ACID_POOL },
4517 { ep_keygate, EP_KEYGATE },
4518 { ep_amoeboid, EP_AMOEBOID },
4519 { ep_amoebalive, EP_AMOEBALIVE },
4520 { ep_has_editor_content, EP_HAS_EDITOR_CONTENT },
4521 { ep_can_turn_each_move, EP_CAN_TURN_EACH_MOVE },
4522 { ep_can_grow, EP_CAN_GROW },
4523 { ep_active_bomb, EP_ACTIVE_BOMB },
4524 { ep_inactive, EP_INACTIVE },
4526 { ep_em_slippery_wall, EP_EM_SLIPPERY_WALL },
4528 { ep_gfx_crumbled, EP_GFX_CRUMBLED },
4530 { ep_editor_cascade_active, EP_EDITOR_CASCADE_ACTIVE },
4531 { ep_editor_cascade_inactive, EP_EDITOR_CASCADE_INACTIVE },
4533 { ep_obsolete, EP_OBSOLETE },
4540 // always start with reliable default values (element has no properties)
4541 // (but never initialize clipboard elements after the very first time)
4542 // (to be able to use clipboard elements between several levels)
4543 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4544 if (!IS_CLIPBOARD_ELEMENT(i) || !clipboard_elements_initialized)
4545 for (j = 0; j < NUM_ELEMENT_PROPERTIES; j++)
4546 SET_PROPERTY(i, j, FALSE);
4548 // set all base element properties from above array definitions
4549 for (i = 0; element_properties[i].elements != NULL; i++)
4550 for (j = 0; (element_properties[i].elements)[j] != -1; j++)
4551 SET_PROPERTY((element_properties[i].elements)[j],
4552 element_properties[i].property, TRUE);
4554 // copy properties to some elements that are only stored in level file
4555 for (i = 0; i < NUM_ELEMENT_PROPERTIES; i++)
4556 for (j = 0; copy_properties[j][0] != -1; j++)
4557 if (HAS_PROPERTY(copy_properties[j][0], i))
4558 for (k = 1; k <= 4; k++)
4559 SET_PROPERTY(copy_properties[j][k], i, TRUE);
4561 // set static element properties that are not listed in array definitions
4562 for (i = EL_STEEL_CHAR_START; i <= EL_STEEL_CHAR_END; i++)
4563 SET_PROPERTY(i, EP_INDESTRUCTIBLE, TRUE);
4565 clipboard_elements_initialized = TRUE;
4568 void InitElementPropertiesEngine(int engine_version)
4570 static int no_wall_properties[] =
4573 EP_COLLECTIBLE_ONLY,
4575 EP_DONT_COLLIDE_WITH,
4578 EP_CAN_SMASH_PLAYER,
4579 EP_CAN_SMASH_ENEMIES,
4580 EP_CAN_SMASH_EVERYTHING,
4585 EP_FOOD_DARK_YAMYAM,
4601 /* important: after initialization in InitElementPropertiesStatic(), the
4602 elements are not again initialized to a default value; therefore all
4603 changes have to make sure that they leave the element with a defined
4604 property (which means that conditional property changes must be set to
4605 a reliable default value before) */
4607 // resolve group elements
4608 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
4609 ResolveGroupElement(EL_GROUP_START + i);
4611 // set all special, combined or engine dependent element properties
4612 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4614 // do not change (already initialized) clipboard elements here
4615 if (IS_CLIPBOARD_ELEMENT(i))
4618 // ---------- INACTIVE ----------------------------------------------------
4619 SET_PROPERTY(i, EP_INACTIVE, ((i >= EL_CHAR_START &&
4620 i <= EL_CHAR_END) ||
4621 (i >= EL_STEEL_CHAR_START &&
4622 i <= EL_STEEL_CHAR_END)));
4624 // ---------- WALKABLE, PASSABLE, ACCESSIBLE ------------------------------
4625 SET_PROPERTY(i, EP_WALKABLE, (IS_WALKABLE_OVER(i) ||
4626 IS_WALKABLE_INSIDE(i) ||
4627 IS_WALKABLE_UNDER(i)));
4629 SET_PROPERTY(i, EP_PASSABLE, (IS_PASSABLE_OVER(i) ||
4630 IS_PASSABLE_INSIDE(i) ||
4631 IS_PASSABLE_UNDER(i)));
4633 SET_PROPERTY(i, EP_ACCESSIBLE_OVER, (IS_WALKABLE_OVER(i) ||
4634 IS_PASSABLE_OVER(i)));
4636 SET_PROPERTY(i, EP_ACCESSIBLE_INSIDE, (IS_WALKABLE_INSIDE(i) ||
4637 IS_PASSABLE_INSIDE(i)));
4639 SET_PROPERTY(i, EP_ACCESSIBLE_UNDER, (IS_WALKABLE_UNDER(i) ||
4640 IS_PASSABLE_UNDER(i)));
4642 SET_PROPERTY(i, EP_ACCESSIBLE, (IS_WALKABLE(i) ||
4645 // ---------- COLLECTIBLE -------------------------------------------------
4646 SET_PROPERTY(i, EP_COLLECTIBLE, (IS_COLLECTIBLE_ONLY(i) ||
4650 // ---------- SNAPPABLE ---------------------------------------------------
4651 SET_PROPERTY(i, EP_SNAPPABLE, (IS_DIGGABLE(i) ||
4652 IS_COLLECTIBLE(i) ||
4656 // ---------- WALL --------------------------------------------------------
4657 SET_PROPERTY(i, EP_WALL, TRUE); // default: element is wall
4659 for (j = 0; no_wall_properties[j] != -1; j++)
4660 if (HAS_PROPERTY(i, no_wall_properties[j]) ||
4661 i >= EL_FIRST_RUNTIME_UNREAL)
4662 SET_PROPERTY(i, EP_WALL, FALSE);
4664 if (IS_HISTORIC_WALL(i))
4665 SET_PROPERTY(i, EP_WALL, TRUE);
4667 // ---------- SOLID_FOR_PUSHING -------------------------------------------
4668 if (engine_version < VERSION_IDENT(2,2,0,0))
4669 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, IS_HISTORIC_SOLID(i));
4671 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, (!IS_WALKABLE(i) &&
4673 !IS_COLLECTIBLE(i)));
4675 // ---------- DRAGONFIRE_PROOF --------------------------------------------
4676 if (IS_HISTORIC_SOLID(i) || i == EL_EXPLOSION)
4677 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, TRUE);
4679 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, (IS_INDESTRUCTIBLE(i) &&
4682 // ---------- EXPLOSION_PROOF ---------------------------------------------
4684 SET_PROPERTY(i, EP_EXPLOSION_PROOF, TRUE);
4685 else if (engine_version < VERSION_IDENT(2,2,0,0))
4686 SET_PROPERTY(i, EP_EXPLOSION_PROOF, IS_INDESTRUCTIBLE(i));
4688 SET_PROPERTY(i, EP_EXPLOSION_PROOF, (IS_INDESTRUCTIBLE(i) &&
4692 if (IS_CUSTOM_ELEMENT(i))
4694 // these are additional properties which are initially false when set
4696 // ---------- DONT_COLLIDE_WITH / DONT_RUN_INTO -------------------------
4698 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH, TRUE);
4699 if (DONT_COLLIDE_WITH(i))
4700 SET_PROPERTY(i, EP_DONT_RUN_INTO, TRUE);
4702 // ---------- CAN_SMASH_ENEMIES / CAN_SMASH_PLAYER ----------------------
4703 if (CAN_SMASH_EVERYTHING(i))
4704 SET_PROPERTY(i, EP_CAN_SMASH_ENEMIES, TRUE);
4705 if (CAN_SMASH_ENEMIES(i))
4706 SET_PROPERTY(i, EP_CAN_SMASH_PLAYER, TRUE);
4709 // ---------- CAN_SMASH ---------------------------------------------------
4710 SET_PROPERTY(i, EP_CAN_SMASH, (CAN_SMASH_PLAYER(i) ||
4711 CAN_SMASH_ENEMIES(i) ||
4712 CAN_SMASH_EVERYTHING(i)));
4714 // ---------- CAN_EXPLODE_BY_FIRE -----------------------------------------
4715 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_FIRE, (CAN_EXPLODE(i) &&
4716 EXPLODES_BY_FIRE(i)));
4718 // ---------- CAN_EXPLODE_SMASHED -----------------------------------------
4719 SET_PROPERTY(i, EP_CAN_EXPLODE_SMASHED, (CAN_EXPLODE(i) &&
4720 EXPLODES_SMASHED(i)));
4722 // ---------- CAN_EXPLODE_IMPACT ------------------------------------------
4723 SET_PROPERTY(i, EP_CAN_EXPLODE_IMPACT, (CAN_EXPLODE(i) &&
4724 EXPLODES_IMPACT(i)));
4726 // ---------- CAN_EXPLODE_BY_DRAGONFIRE -----------------------------------
4727 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_DRAGONFIRE, CAN_EXPLODE_BY_FIRE(i));
4729 // ---------- CAN_EXPLODE_BY_EXPLOSION ------------------------------------
4730 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_EXPLOSION, (CAN_EXPLODE_BY_FIRE(i) ||
4731 i == EL_BLACK_ORB));
4733 // ---------- COULD_MOVE_INTO_ACID ----------------------------------------
4734 SET_PROPERTY(i, EP_COULD_MOVE_INTO_ACID, (IS_PLAYER_ELEMENT(i) ||
4736 IS_CUSTOM_ELEMENT(i)));
4738 // ---------- MAYBE_DONT_COLLIDE_WITH -------------------------------------
4739 SET_PROPERTY(i, EP_MAYBE_DONT_COLLIDE_WITH, (i == EL_SP_SNIKSNAK ||
4740 i == EL_SP_ELECTRON));
4742 // ---------- CAN_MOVE_INTO_ACID ------------------------------------------
4743 if (COULD_MOVE_INTO_ACID(i) && !IS_CUSTOM_ELEMENT(i))
4744 SET_PROPERTY(i, EP_CAN_MOVE_INTO_ACID,
4745 getMoveIntoAcidProperty(&level, i));
4747 // ---------- DONT_COLLIDE_WITH -------------------------------------------
4748 if (MAYBE_DONT_COLLIDE_WITH(i))
4749 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH,
4750 getDontCollideWithProperty(&level, i));
4752 // ---------- SP_PORT -----------------------------------------------------
4753 SET_PROPERTY(i, EP_SP_PORT, (IS_SP_ELEMENT(i) &&
4754 IS_PASSABLE_INSIDE(i)));
4756 // ---------- CAN_BE_CLONED_BY_ANDROID ------------------------------------
4757 for (j = 0; j < level.num_android_clone_elements; j++)
4758 SET_PROPERTY(i, EP_CAN_BE_CLONED_BY_ANDROID,
4760 IS_EQUAL_OR_IN_GROUP(i, level.android_clone_element[j])));
4762 // ---------- CAN_CHANGE --------------------------------------------------
4763 SET_PROPERTY(i, EP_CAN_CHANGE, FALSE); // default: cannot change
4764 for (j = 0; j < element_info[i].num_change_pages; j++)
4765 if (element_info[i].change_page[j].can_change)
4766 SET_PROPERTY(i, EP_CAN_CHANGE, TRUE);
4768 // ---------- HAS_ACTION --------------------------------------------------
4769 SET_PROPERTY(i, EP_HAS_ACTION, FALSE); // default: has no action
4770 for (j = 0; j < element_info[i].num_change_pages; j++)
4771 if (element_info[i].change_page[j].has_action)
4772 SET_PROPERTY(i, EP_HAS_ACTION, TRUE);
4774 // ---------- CAN_CHANGE_OR_HAS_ACTION ------------------------------------
4775 SET_PROPERTY(i, EP_CAN_CHANGE_OR_HAS_ACTION, (CAN_CHANGE(i) ||
4778 // ---------- GFX_CRUMBLED ------------------------------------------------
4779 SET_PROPERTY(i, EP_GFX_CRUMBLED,
4780 element_info[i].crumbled[ACTION_DEFAULT] !=
4781 element_info[i].graphic[ACTION_DEFAULT]);
4783 // ---------- EDITOR_CASCADE ----------------------------------------------
4784 SET_PROPERTY(i, EP_EDITOR_CASCADE, (IS_EDITOR_CASCADE_ACTIVE(i) ||
4785 IS_EDITOR_CASCADE_INACTIVE(i)));
4788 // dynamically adjust element properties according to game engine version
4790 static int ep_em_slippery_wall[] =
4795 EL_EXPANDABLE_WALL_HORIZONTAL,
4796 EL_EXPANDABLE_WALL_VERTICAL,
4797 EL_EXPANDABLE_WALL_ANY,
4798 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
4799 EL_EXPANDABLE_STEELWALL_VERTICAL,
4800 EL_EXPANDABLE_STEELWALL_ANY,
4801 EL_EXPANDABLE_STEELWALL_GROWING,
4805 static int ep_em_explodes_by_fire[] =
4808 EL_EM_DYNAMITE_ACTIVE,
4813 // special EM style gems behaviour
4814 for (i = 0; ep_em_slippery_wall[i] != -1; i++)
4815 SET_PROPERTY(ep_em_slippery_wall[i], EP_EM_SLIPPERY_WALL,
4816 level.em_slippery_gems);
4818 // "EL_EXPANDABLE_WALL_GROWING" wasn't slippery for EM gems in 2.0.1
4819 SET_PROPERTY(EL_EXPANDABLE_WALL_GROWING, EP_EM_SLIPPERY_WALL,
4820 (level.em_slippery_gems &&
4821 engine_version > VERSION_IDENT(2,0,1,0)));
4823 // special EM style explosion behaviour regarding chain reactions
4824 for (i = 0; ep_em_explodes_by_fire[i] != -1; i++)
4825 SET_PROPERTY(ep_em_explodes_by_fire[i], EP_EXPLODES_BY_FIRE,
4826 level.em_explodes_by_fire);
4829 // this is needed because some graphics depend on element properties
4830 if (game_status == GAME_MODE_PLAYING)
4831 InitElementGraphicInfo();
4834 void InitElementPropertiesGfxElement(void)
4838 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4840 struct ElementInfo *ei = &element_info[i];
4842 ei->gfx_element = (ei->use_gfx_element ? ei->gfx_element_initial : i);
4846 static void InitGlobal(void)
4851 for (i = 0; i < MAX_NUM_ELEMENTS + 1; i++)
4853 // check if element_name_info entry defined for each element in "main.h"
4854 if (i < MAX_NUM_ELEMENTS && element_name_info[i].token_name == NULL)
4855 Fail("undefined 'element_name_info' entry for element %d", i);
4857 element_info[i].token_name = element_name_info[i].token_name;
4858 element_info[i].class_name = element_name_info[i].class_name;
4859 element_info[i].editor_description= element_name_info[i].editor_description;
4862 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS + 1; i++)
4864 // check if global_anim_name_info defined for each entry in "main.h"
4865 if (i < NUM_GLOBAL_ANIM_TOKENS &&
4866 global_anim_name_info[i].token_name == NULL)
4867 Fail("undefined 'global_anim_name_info' entry for anim %d", i);
4869 global_anim_info[i].token_name = global_anim_name_info[i].token_name;
4872 // create hash from image config list
4873 image_config_hash = newSetupFileHash();
4874 for (i = 0; image_config[i].token != NULL; i++)
4875 setHashEntry(image_config_hash,
4876 image_config[i].token,
4877 image_config[i].value);
4879 // create hash from element token list
4880 element_token_hash = newSetupFileHash();
4881 for (i = 0; element_name_info[i].token_name != NULL; i++)
4882 setHashEntry(element_token_hash,
4883 element_name_info[i].token_name,
4886 // create hash from graphic token list
4887 graphic_token_hash = newSetupFileHash();
4888 for (graphic = 0, i = 0; image_config[i].token != NULL; i++)
4889 if (strSuffix(image_config[i].value, ".png") ||
4890 strSuffix(image_config[i].value, ".pcx") ||
4891 strSuffix(image_config[i].value, ".wav") ||
4892 strEqual(image_config[i].value, UNDEFINED_FILENAME))
4893 setHashEntry(graphic_token_hash,
4894 image_config[i].token,
4895 int2str(graphic++, 0));
4897 // create hash from font token list
4898 font_token_hash = newSetupFileHash();
4899 for (i = 0; font_info[i].token_name != NULL; i++)
4900 setHashEntry(font_token_hash,
4901 font_info[i].token_name,
4904 // set default filenames for all cloned graphics in static configuration
4905 for (i = 0; image_config[i].token != NULL; i++)
4907 if (strEqual(image_config[i].value, UNDEFINED_FILENAME))
4909 char *token = image_config[i].token;
4910 char *token_clone_from = getStringCat2(token, ".clone_from");
4911 char *token_cloned = getHashEntry(image_config_hash, token_clone_from);
4913 if (token_cloned != NULL)
4915 char *value_cloned = getHashEntry(image_config_hash, token_cloned);
4917 if (value_cloned != NULL)
4919 // set default filename in static configuration
4920 image_config[i].value = value_cloned;
4922 // set default filename in image config hash
4923 setHashEntry(image_config_hash, token, value_cloned);
4927 free(token_clone_from);
4931 // always start with reliable default values (all elements)
4932 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4933 ActiveElement[i] = i;
4935 // now add all entries that have an active state (active elements)
4936 for (i = 0; element_with_active_state[i].element != -1; i++)
4938 int element = element_with_active_state[i].element;
4939 int element_active = element_with_active_state[i].element_active;
4941 ActiveElement[element] = element_active;
4944 // always start with reliable default values (all buttons)
4945 for (i = 0; i < NUM_IMAGE_FILES; i++)
4946 ActiveButton[i] = i;
4948 // now add all entries that have an active state (active buttons)
4949 for (i = 0; button_with_active_state[i].button != -1; i++)
4951 int button = button_with_active_state[i].button;
4952 int button_active = button_with_active_state[i].button_active;
4954 ActiveButton[button] = button_active;
4957 // always start with reliable default values (all fonts)
4958 for (i = 0; i < NUM_FONTS; i++)
4961 // now add all entries that have an active state (active fonts)
4962 for (i = 0; font_with_active_state[i].font_nr != -1; i++)
4964 int font = font_with_active_state[i].font_nr;
4965 int font_active = font_with_active_state[i].font_nr_active;
4967 ActiveFont[font] = font_active;
4970 global.autoplay_leveldir = NULL;
4971 global.patchtapes_leveldir = NULL;
4972 global.convert_leveldir = NULL;
4973 global.dumplevel_leveldir = NULL;
4974 global.dumptape_leveldir = NULL;
4975 global.create_sketch_images_dir = NULL;
4976 global.create_collect_images_dir = NULL;
4978 global.frames_per_second = 0;
4979 global.show_frames_per_second = FALSE;
4981 global.border_status = GAME_MODE_LOADING;
4982 global.anim_status = global.anim_status_next = GAME_MODE_LOADING;
4984 global.use_envelope_request = FALSE;
4986 global.user_names = NULL;
4989 static void Execute_Command(char *command)
4993 if (strEqual(command, "print graphicsinfo.conf"))
4995 Print("# You can configure additional/alternative image files here.\n");
4996 Print("# (The entries below are default and therefore commented out.)\n");
4998 Print("%s\n", getFormattedSetupEntry("name", "Classic Graphics"));
5000 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5003 for (i = 0; image_config[i].token != NULL; i++)
5004 Print("# %s\n", getFormattedSetupEntry(image_config[i].token,
5005 image_config[i].value));
5009 else if (strEqual(command, "print soundsinfo.conf"))
5011 Print("# You can configure additional/alternative sound files here.\n");
5012 Print("# (The entries below are default and therefore commented out.)\n");
5014 Print("%s\n", getFormattedSetupEntry("name", "Classic Sounds"));
5016 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5019 for (i = 0; sound_config[i].token != NULL; i++)
5020 Print("# %s\n", getFormattedSetupEntry(sound_config[i].token,
5021 sound_config[i].value));
5025 else if (strEqual(command, "print musicinfo.conf"))
5027 Print("# You can configure additional/alternative music files here.\n");
5028 Print("# (The entries below are default and therefore commented out.)\n");
5030 Print("%s\n", getFormattedSetupEntry("name", "Classic Music"));
5032 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5035 for (i = 0; music_config[i].token != NULL; i++)
5036 Print("# %s\n", getFormattedSetupEntry(music_config[i].token,
5037 music_config[i].value));
5041 else if (strEqual(command, "print editorsetup.conf"))
5043 Print("# You can configure your personal editor element list here.\n");
5044 Print("# (The entries below are default and therefore commented out.)\n");
5047 // this is needed to be able to check element list for cascade elements
5048 InitElementPropertiesStatic();
5049 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
5051 PrintEditorElementList();
5055 else if (strEqual(command, "print helpanim.conf"))
5057 Print("# You can configure different element help animations here.\n");
5058 Print("# (The entries below are default and therefore commented out.)\n");
5061 for (i = 0; helpanim_config[i].token != NULL; i++)
5063 Print("# %s\n", getFormattedSetupEntry(helpanim_config[i].token,
5064 helpanim_config[i].value));
5066 if (strEqual(helpanim_config[i].token, "end"))
5072 else if (strEqual(command, "print helptext.conf"))
5074 Print("# You can configure different element help text here.\n");
5075 Print("# (The entries below are default and therefore commented out.)\n");
5078 for (i = 0; helptext_config[i].token != NULL; i++)
5079 Print("# %s\n", getFormattedSetupEntry(helptext_config[i].token,
5080 helptext_config[i].value));
5084 else if (strPrefix(command, "dump level "))
5086 char *filename = &command[11];
5088 if (fileExists(filename))
5090 LoadLevelFromFilename(&level, filename);
5096 char *leveldir = getStringCopy(filename); // read command parameters
5097 char *level_nr = strchr(leveldir, ' ');
5099 if (level_nr == NULL)
5100 Fail("cannot open file '%s'", filename);
5104 global.dumplevel_leveldir = leveldir;
5105 global.dumplevel_level_nr = atoi(level_nr);
5107 program.headless = TRUE;
5109 else if (strPrefix(command, "dump tape "))
5111 char *filename = &command[10];
5113 if (fileExists(filename))
5115 LoadTapeFromFilename(filename);
5121 char *leveldir = getStringCopy(filename); // read command parameters
5122 char *level_nr = strchr(leveldir, ' ');
5124 if (level_nr == NULL)
5125 Fail("cannot open file '%s'", filename);
5129 global.dumptape_leveldir = leveldir;
5130 global.dumptape_level_nr = atoi(level_nr);
5132 program.headless = TRUE;
5134 else if (strPrefix(command, "autoplay ") ||
5135 strPrefix(command, "autoffwd ") ||
5136 strPrefix(command, "autowarp ") ||
5137 strPrefix(command, "autotest ") ||
5138 strPrefix(command, "autosave ") ||
5139 strPrefix(command, "autoupload ") ||
5140 strPrefix(command, "autofix "))
5142 char *arg_ptr = strchr(command, ' ');
5143 char *str_ptr = getStringCopy(arg_ptr); // read command parameters
5145 global.autoplay_mode =
5146 (strPrefix(command, "autoplay") ? AUTOPLAY_MODE_PLAY :
5147 strPrefix(command, "autoffwd") ? AUTOPLAY_MODE_FFWD :
5148 strPrefix(command, "autowarp") ? AUTOPLAY_MODE_WARP :
5149 strPrefix(command, "autotest") ? AUTOPLAY_MODE_TEST :
5150 strPrefix(command, "autosave") ? AUTOPLAY_MODE_SAVE :
5151 strPrefix(command, "autoupload") ? AUTOPLAY_MODE_UPLOAD :
5152 strPrefix(command, "autofix") ? AUTOPLAY_MODE_FIX :
5153 AUTOPLAY_MODE_NONE);
5155 while (*str_ptr != '\0') // continue parsing string
5157 // cut leading whitespace from string, replace it by string terminator
5158 while (*str_ptr == ' ' || *str_ptr == '\t')
5161 if (*str_ptr == '\0') // end of string reached
5164 if (global.autoplay_leveldir == NULL) // read level set string
5166 global.autoplay_leveldir = str_ptr;
5167 global.autoplay_all = TRUE; // default: play all tapes
5169 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5170 global.autoplay_level[i] = FALSE;
5172 else // read level number string
5174 int level_nr = atoi(str_ptr); // get level_nr value
5176 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5177 global.autoplay_level[level_nr] = TRUE;
5179 global.autoplay_all = FALSE;
5182 // advance string pointer to the next whitespace (or end of string)
5183 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5187 if (global.autoplay_mode & AUTOPLAY_WARP_NO_DISPLAY)
5188 program.headless = TRUE;
5190 else if (strPrefix(command, "patch tapes "))
5192 char *str_ptr = getStringCopy(&command[12]); // read command parameters
5194 // skip leading whitespace
5195 while (*str_ptr == ' ' || *str_ptr == '\t')
5198 if (*str_ptr == '\0')
5199 Fail("cannot find MODE in command '%s'", command);
5201 global.patchtapes_mode = str_ptr; // store patch mode
5203 // advance to next whitespace (or end of string)
5204 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5207 while (*str_ptr != '\0') // continue parsing string
5209 // cut leading whitespace from string, replace it by string terminator
5210 while (*str_ptr == ' ' || *str_ptr == '\t')
5213 if (*str_ptr == '\0') // end of string reached
5216 if (global.patchtapes_leveldir == NULL) // read level set string
5218 global.patchtapes_leveldir = str_ptr;
5219 global.patchtapes_all = TRUE; // default: patch all tapes
5221 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5222 global.patchtapes_level[i] = FALSE;
5224 else // read level number string
5226 int level_nr = atoi(str_ptr); // get level_nr value
5228 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5229 global.patchtapes_level[level_nr] = TRUE;
5231 global.patchtapes_all = FALSE;
5234 // advance string pointer to the next whitespace (or end of string)
5235 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5239 if (global.patchtapes_leveldir == NULL)
5241 if (strEqual(global.patchtapes_mode, "help"))
5242 global.patchtapes_leveldir = UNDEFINED_LEVELSET;
5244 Fail("cannot find LEVELDIR in command '%s'", command);
5247 program.headless = TRUE;
5249 else if (strPrefix(command, "convert "))
5251 char *str_copy = getStringCopy(strchr(command, ' ') + 1);
5252 char *str_ptr = strchr(str_copy, ' ');
5254 global.convert_leveldir = str_copy;
5255 global.convert_level_nr = -1;
5257 if (str_ptr != NULL) // level number follows
5259 *str_ptr++ = '\0'; // terminate leveldir string
5260 global.convert_level_nr = atoi(str_ptr); // get level_nr value
5263 program.headless = TRUE;
5265 else if (strPrefix(command, "create sketch images "))
5267 global.create_sketch_images_dir = getStringCopy(&command[21]);
5269 if (access(global.create_sketch_images_dir, W_OK) != 0)
5270 Fail("image target directory '%s' not found or not writable",
5271 global.create_sketch_images_dir);
5273 else if (strPrefix(command, "create collect image "))
5275 global.create_collect_images_dir = getStringCopy(&command[21]);
5277 if (access(global.create_collect_images_dir, W_OK) != 0)
5278 Fail("image target directory '%s' not found or not writable",
5279 global.create_collect_images_dir);
5281 else if (strPrefix(command, "create CE image "))
5283 CreateCustomElementImages(&command[16]);
5289 FailWithHelp("unrecognized command '%s'", command);
5292 // disable networking if any valid command was recognized
5293 options.network = setup.network_mode = FALSE;
5296 static void InitSetup(void)
5298 LoadUserNames(); // global user names
5299 LoadUserSetup(); // global user number
5301 LoadSetup(); // global setup info
5303 // set some options from setup file
5305 if (setup.options.verbose)
5306 options.verbose = TRUE;
5308 if (setup.debug.show_frames_per_second)
5309 global.show_frames_per_second = TRUE;
5312 static void InitGameInfo(void)
5314 game.restart_level = FALSE;
5315 game.restart_game_message = NULL;
5317 game.request_active = FALSE;
5318 game.request_active_or_moving = FALSE;
5320 game.use_masked_elements_initial = FALSE;
5323 static void InitPlayerInfo(void)
5327 // choose default local player
5328 local_player = &stored_player[0];
5330 for (i = 0; i < MAX_PLAYERS; i++)
5332 stored_player[i].connected_locally = FALSE;
5333 stored_player[i].connected_network = FALSE;
5336 local_player->connected_locally = TRUE;
5339 static void InitArtworkInfo(void)
5344 static char *get_string_in_brackets(char *string)
5346 char *string_in_brackets = checked_malloc(strlen(string) + 3);
5348 sprintf(string_in_brackets, "[%s]", string);
5350 return string_in_brackets;
5353 static char *get_level_id_suffix(int id_nr)
5355 char *id_suffix = checked_malloc(1 + 3 + 1);
5357 if (id_nr < 0 || id_nr > 999)
5360 sprintf(id_suffix, ".%03d", id_nr);
5365 static void InitArtworkConfig(void)
5367 static char *image_id_prefix[MAX_NUM_ELEMENTS +
5369 NUM_GLOBAL_ANIM_TOKENS + 1];
5370 static char *sound_id_prefix[2 * MAX_NUM_ELEMENTS +
5371 NUM_GLOBAL_ANIM_TOKENS + 1];
5372 static char *music_id_prefix[NUM_MUSIC_PREFIXES +
5373 NUM_GLOBAL_ANIM_TOKENS + 1];
5374 static char *action_id_suffix[NUM_ACTIONS + 1];
5375 static char *direction_id_suffix[NUM_DIRECTIONS_FULL + 1];
5376 static char *special_id_suffix[NUM_SPECIAL_GFX_ARGS + 1];
5377 static char *level_id_suffix[MAX_LEVELS + 1];
5378 static char *dummy[1] = { NULL };
5379 static char *ignore_generic_tokens[] =
5384 "program_copyright",
5389 static char **ignore_image_tokens;
5390 static char **ignore_sound_tokens;
5391 static char **ignore_music_tokens;
5392 int num_ignore_generic_tokens;
5393 int num_ignore_image_tokens;
5394 int num_ignore_sound_tokens;
5395 int num_ignore_music_tokens;
5398 // dynamically determine list of generic tokens to be ignored
5399 num_ignore_generic_tokens = 0;
5400 for (i = 0; ignore_generic_tokens[i] != NULL; i++)
5401 num_ignore_generic_tokens++;
5403 // dynamically determine list of image tokens to be ignored
5404 num_ignore_image_tokens = num_ignore_generic_tokens;
5405 for (i = 0; image_config_vars[i].token != NULL; i++)
5406 num_ignore_image_tokens++;
5407 ignore_image_tokens =
5408 checked_malloc((num_ignore_image_tokens + 1) * sizeof(char *));
5409 for (i = 0; i < num_ignore_generic_tokens; i++)
5410 ignore_image_tokens[i] = ignore_generic_tokens[i];
5411 for (i = 0; i < num_ignore_image_tokens - num_ignore_generic_tokens; i++)
5412 ignore_image_tokens[num_ignore_generic_tokens + i] =
5413 image_config_vars[i].token;
5414 ignore_image_tokens[num_ignore_image_tokens] = NULL;
5416 // dynamically determine list of sound tokens to be ignored
5417 num_ignore_sound_tokens = num_ignore_generic_tokens;
5418 ignore_sound_tokens =
5419 checked_malloc((num_ignore_sound_tokens + 1) * sizeof(char *));
5420 for (i = 0; i < num_ignore_generic_tokens; i++)
5421 ignore_sound_tokens[i] = ignore_generic_tokens[i];
5422 ignore_sound_tokens[num_ignore_sound_tokens] = NULL;
5424 // dynamically determine list of music tokens to be ignored
5425 num_ignore_music_tokens = num_ignore_generic_tokens;
5426 ignore_music_tokens =
5427 checked_malloc((num_ignore_music_tokens + 1) * sizeof(char *));
5428 for (i = 0; i < num_ignore_generic_tokens; i++)
5429 ignore_music_tokens[i] = ignore_generic_tokens[i];
5430 ignore_music_tokens[num_ignore_music_tokens] = NULL;
5432 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5433 image_id_prefix[i] = element_info[i].token_name;
5434 for (i = 0; i < NUM_FONTS; i++)
5435 image_id_prefix[MAX_NUM_ELEMENTS + i] = font_info[i].token_name;
5436 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5437 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + i] =
5438 global_anim_info[i].token_name;
5439 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5441 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5442 sound_id_prefix[i] = element_info[i].token_name;
5443 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5444 sound_id_prefix[MAX_NUM_ELEMENTS + i] =
5445 get_string_in_brackets(element_info[i].class_name);
5446 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5447 sound_id_prefix[2 * MAX_NUM_ELEMENTS + i] =
5448 global_anim_info[i].token_name;
5449 sound_id_prefix[2 * MAX_NUM_ELEMENTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5451 for (i = 0; i < NUM_MUSIC_PREFIXES; i++)
5452 music_id_prefix[i] = music_prefix_info[i].prefix;
5453 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5454 music_id_prefix[NUM_MUSIC_PREFIXES + i] =
5455 global_anim_info[i].token_name;
5456 music_id_prefix[NUM_MUSIC_PREFIXES + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5458 for (i = 0; i < NUM_ACTIONS; i++)
5459 action_id_suffix[i] = element_action_info[i].suffix;
5460 action_id_suffix[NUM_ACTIONS] = NULL;
5462 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
5463 direction_id_suffix[i] = element_direction_info[i].suffix;
5464 direction_id_suffix[NUM_DIRECTIONS_FULL] = NULL;
5466 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
5467 special_id_suffix[i] = special_suffix_info[i].suffix;
5468 special_id_suffix[NUM_SPECIAL_GFX_ARGS] = NULL;
5470 for (i = 0; i < MAX_LEVELS; i++)
5471 level_id_suffix[i] = get_level_id_suffix(i);
5472 level_id_suffix[MAX_LEVELS] = NULL;
5474 InitImageList(image_config, NUM_IMAGE_FILES, image_config_suffix,
5475 image_id_prefix, action_id_suffix, direction_id_suffix,
5476 special_id_suffix, ignore_image_tokens);
5477 InitSoundList(sound_config, NUM_SOUND_FILES, sound_config_suffix,
5478 sound_id_prefix, action_id_suffix, dummy,
5479 special_id_suffix, ignore_sound_tokens);
5480 InitMusicList(music_config, NUM_MUSIC_FILES, music_config_suffix,
5481 music_id_prefix, action_id_suffix, special_id_suffix,
5482 level_id_suffix, ignore_music_tokens);
5485 static void InitMixer(void)
5492 static void InitVideoOverlay(void)
5494 // if virtual buttons are not loaded from setup file, repeat initializing
5495 // virtual buttons grid with default values now that video is initialized
5496 if (!setup.touch.grid_initialized)
5499 InitTileCursorInfo();
5503 void InitGfxBuffers(void)
5505 static int win_xsize_last = -1;
5506 static int win_ysize_last = -1;
5508 // create additional image buffers for double-buffering and cross-fading
5510 if (WIN_XSIZE != win_xsize_last || WIN_YSIZE != win_ysize_last)
5512 // used to temporarily store the backbuffer -- only re-create if changed
5513 ReCreateBitmap(&bitmap_db_store_1, WIN_XSIZE, WIN_YSIZE);
5514 ReCreateBitmap(&bitmap_db_store_2, WIN_XSIZE, WIN_YSIZE);
5516 win_xsize_last = WIN_XSIZE;
5517 win_ysize_last = WIN_YSIZE;
5520 ReCreateBitmap(&bitmap_db_field, FXSIZE, FYSIZE);
5521 ReCreateBitmap(&bitmap_db_panel, DXSIZE, DYSIZE);
5522 ReCreateBitmap(&bitmap_db_door_1, 3 * DXSIZE, DYSIZE);
5523 ReCreateBitmap(&bitmap_db_door_2, 3 * VXSIZE, VYSIZE);
5525 // initialize screen properties
5526 InitGfxFieldInfo(SX, SY, SXSIZE, SYSIZE,
5527 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
5529 InitGfxDoor1Info(DX, DY, DXSIZE, DYSIZE);
5530 InitGfxDoor2Info(VX, VY, VXSIZE, VYSIZE);
5531 InitGfxDoor3Info(EX, EY, EXSIZE, EYSIZE);
5532 InitGfxWindowInfo(WIN_XSIZE, WIN_YSIZE);
5533 InitGfxScrollbufferInfo(FXSIZE, FYSIZE);
5534 InitGfxClipRegion(FALSE, -1, -1, -1, -1);
5536 // required if door size definitions have changed
5537 InitGraphicCompatibilityInfo_Doors();
5539 InitGfxBuffers_EM();
5540 InitGfxBuffers_SP();
5543 static void InitGfx(void)
5545 struct GraphicInfo *graphic_info_last = graphic_info;
5546 char *filename_font_initial = NULL;
5547 char *filename_anim_initial = NULL;
5548 Bitmap *bitmap_font_initial = NULL;
5551 // determine settings for initial font (for displaying startup messages)
5552 for (i = 0; image_config[i].token != NULL; i++)
5554 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5556 char font_token[128];
5559 sprintf(font_token, "%s_%d", CONFIG_TOKEN_FONT_INITIAL, j + 1);
5560 len_font_token = strlen(font_token);
5562 if (strEqual(image_config[i].token, font_token))
5563 filename_font_initial = image_config[i].value;
5564 else if (strlen(image_config[i].token) > len_font_token &&
5565 strncmp(image_config[i].token, font_token, len_font_token) == 0)
5567 if (strEqual(&image_config[i].token[len_font_token], ".x"))
5568 font_initial[j].src_x = atoi(image_config[i].value);
5569 else if (strEqual(&image_config[i].token[len_font_token], ".y"))
5570 font_initial[j].src_y = atoi(image_config[i].value);
5571 else if (strEqual(&image_config[i].token[len_font_token], ".width"))
5572 font_initial[j].width = atoi(image_config[i].value);
5573 else if (strEqual(&image_config[i].token[len_font_token], ".height"))
5574 font_initial[j].height = atoi(image_config[i].value);
5579 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5581 font_initial[j].num_chars = DEFAULT_NUM_CHARS_PER_FONT;
5582 font_initial[j].num_chars_per_line = DEFAULT_NUM_CHARS_PER_LINE;
5585 if (filename_font_initial == NULL) // should not happen
5586 Fail("cannot get filename for '%s'", CONFIG_TOKEN_FONT_INITIAL);
5589 InitGfxCustomArtworkInfo();
5590 InitGfxOtherSettings();
5592 bitmap_font_initial = LoadCustomImage(filename_font_initial);
5594 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5595 font_initial[j].bitmap = bitmap_font_initial;
5597 InitFontGraphicInfo();
5601 DrawInitTextHead("Loading graphics");
5603 // initialize settings for busy animation with default values
5604 int parameter[NUM_GFX_ARGS];
5605 for (i = 0; i < NUM_GFX_ARGS; i++)
5606 parameter[i] = get_graphic_parameter_value(image_config_suffix[i].value,
5607 image_config_suffix[i].token,
5608 image_config_suffix[i].type);
5610 char *anim_token = CONFIG_TOKEN_GLOBAL_BUSY;
5611 int len_anim_token = strlen(anim_token);
5613 // read settings for busy animation from default custom artwork config
5614 char *gfx_config_filename = getPath3(options.graphics_directory,
5616 GRAPHICSINFO_FILENAME);
5618 if (fileExists(gfx_config_filename))
5620 SetupFileHash *setup_file_hash = loadSetupFileHash(gfx_config_filename);
5622 if (setup_file_hash)
5624 char *filename = getHashEntry(setup_file_hash, anim_token);
5628 filename_anim_initial = getStringCopy(filename);
5630 for (j = 0; image_config_suffix[j].token != NULL; j++)
5632 int type = image_config_suffix[j].type;
5633 char *suffix = image_config_suffix[j].token;
5634 char *token = getStringCat2(anim_token, suffix);
5635 char *value = getHashEntry(setup_file_hash, token);
5637 checked_free(token);
5640 parameter[j] = get_graphic_parameter_value(value, suffix, type);
5644 freeSetupFileHash(setup_file_hash);
5648 if (filename_anim_initial == NULL)
5650 // read settings for busy animation from static default artwork config
5651 for (i = 0; image_config[i].token != NULL; i++)
5653 if (strEqual(image_config[i].token, anim_token))
5654 filename_anim_initial = getStringCopy(image_config[i].value);
5655 else if (strlen(image_config[i].token) > len_anim_token &&
5656 strncmp(image_config[i].token, anim_token, len_anim_token) == 0)
5658 for (j = 0; image_config_suffix[j].token != NULL; j++)
5660 if (strEqual(&image_config[i].token[len_anim_token],
5661 image_config_suffix[j].token))
5663 get_graphic_parameter_value(image_config[i].value,
5664 image_config_suffix[j].token,
5665 image_config_suffix[j].type);
5671 if (filename_anim_initial == NULL) // should not happen
5672 Fail("cannot get filename for '%s'", CONFIG_TOKEN_GLOBAL_BUSY);
5674 anim_initial.bitmaps =
5675 checked_calloc(sizeof(Bitmap *) * NUM_IMG_BITMAP_POINTERS);
5677 anim_initial.bitmaps[IMG_BITMAP_STANDARD] =
5678 LoadCustomImage(filename_anim_initial);
5680 checked_free(filename_anim_initial);
5682 graphic_info = &anim_initial; // graphic == 0 => anim_initial
5684 set_graphic_parameters_ext(0, parameter, anim_initial.bitmaps);
5686 graphic_info = graphic_info_last;
5688 init.busy.width = anim_initial.width;
5689 init.busy.height = anim_initial.height;
5691 InitMenuDesignSettings_Static();
5693 InitGfxDrawBusyAnimFunction(DrawInitAnim);
5694 InitGfxDrawGlobalAnimFunction(DrawGlobalAnimations);
5695 InitGfxDrawGlobalBorderFunction(DrawMaskedBorderToTarget);
5696 InitGfxDrawTileCursorFunction(DrawTileCursor);
5698 gfx.fade_border_source_status = global.border_status;
5699 gfx.fade_border_target_status = global.border_status;
5700 gfx.masked_border_bitmap_ptr = backbuffer;
5702 // use copy of busy animation to prevent change while reloading artwork
5706 static void InitGfxBackground(void)
5708 fieldbuffer = bitmap_db_field;
5709 SetDrawtoField(DRAW_TO_BACKBUFFER);
5711 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
5713 redraw_mask = REDRAW_ALL;
5716 static void InitLevelInfo(void)
5718 LoadLevelInfo(); // global level info
5719 LoadLevelSetup_LastSeries(); // last played series info
5720 LoadLevelSetup_SeriesInfo(); // last played level info
5722 if (global.autoplay_leveldir &&
5723 global.autoplay_mode != AUTOPLAY_MODE_TEST)
5725 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
5726 global.autoplay_leveldir);
5727 if (leveldir_current == NULL)
5728 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
5731 SetLevelSetInfo(leveldir_current->identifier, level_nr);
5734 static void InitLevelArtworkInfo(void)
5736 LoadLevelArtworkInfo();
5739 static void InitImages(void)
5741 print_timestamp_init("InitImages");
5744 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
5745 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5746 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
5747 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5748 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
5749 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5750 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
5751 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5754 setLevelArtworkDir(artwork.gfx_first);
5757 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
5758 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5759 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
5760 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5761 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
5762 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5763 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
5764 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5768 Debug("init:InitImages", "InitImages for '%s' ['%s', '%s'] ['%s', '%s']",
5769 leveldir_current->identifier,
5770 artwork.gfx_current_identifier,
5771 artwork.gfx_current->identifier,
5772 leveldir_current->graphics_set,
5773 leveldir_current->graphics_path);
5776 UPDATE_BUSY_STATE();
5778 ReloadCustomImages();
5779 print_timestamp_time("ReloadCustomImages");
5781 UPDATE_BUSY_STATE();
5783 LoadCustomElementDescriptions();
5784 print_timestamp_time("LoadCustomElementDescriptions");
5786 UPDATE_BUSY_STATE();
5788 LoadMenuDesignSettings();
5789 print_timestamp_time("LoadMenuDesignSettings");
5791 UPDATE_BUSY_STATE();
5793 ReinitializeGraphics();
5794 print_timestamp_time("ReinitializeGraphics");
5796 LoadMenuDesignSettings_AfterGraphics();
5797 print_timestamp_time("LoadMenuDesignSettings_AfterGraphics");
5799 UPDATE_BUSY_STATE();
5801 print_timestamp_done("InitImages");
5804 static void InitSound(char *identifier)
5806 print_timestamp_init("InitSound");
5808 if (identifier == NULL)
5809 identifier = artwork.snd_current->identifier;
5811 // set artwork path to send it to the sound server process
5812 setLevelArtworkDir(artwork.snd_first);
5814 InitReloadCustomSounds(identifier);
5815 print_timestamp_time("InitReloadCustomSounds");
5817 ReinitializeSounds();
5818 print_timestamp_time("ReinitializeSounds");
5820 print_timestamp_done("InitSound");
5823 static void InitMusic(char *identifier)
5825 print_timestamp_init("InitMusic");
5827 if (identifier == NULL)
5828 identifier = artwork.mus_current->identifier;
5830 // set artwork path to send it to the sound server process
5831 setLevelArtworkDir(artwork.mus_first);
5833 InitReloadCustomMusic(identifier);
5834 print_timestamp_time("InitReloadCustomMusic");
5836 ReinitializeMusic();
5837 print_timestamp_time("ReinitializeMusic");
5839 print_timestamp_done("InitMusic");
5842 static void InitArtworkDone(void)
5844 if (program.headless)
5847 InitGlobalAnimations();
5850 static void InitNetworkSettings(void)
5852 boolean network_enabled = (options.network || setup.network_mode);
5853 char *network_server = (options.server_host != NULL ? options.server_host :
5854 setup.network_server_hostname);
5856 if (strEqual(network_server, STR_NETWORK_AUTO_DETECT))
5857 network_server = NULL;
5859 InitNetworkInfo(network_enabled,
5863 options.server_port);
5866 void InitNetworkServer(void)
5868 if (!network.enabled || network.connected)
5871 LimitScreenUpdates(FALSE);
5873 if (game_status == GAME_MODE_LOADING)
5876 if (!ConnectToServer(network.server_host, network.server_port))
5878 network.enabled = FALSE;
5880 setup.network_mode = FALSE;
5884 SendToServer_ProtocolVersion();
5885 SendToServer_PlayerName(setup.player_name);
5886 SendToServer_NrWanted(setup.network_player_nr + 1);
5888 network.connected = TRUE;
5891 // short time to recognize result of network initialization
5892 if (game_status == GAME_MODE_LOADING)
5893 Delay_WithScreenUpdates(1000);
5896 static boolean CheckArtworkConfigForCustomElements(char *filename)
5898 SetupFileHash *setup_file_hash;
5899 boolean redefined_ce_found = FALSE;
5901 // !!! CACHE THIS BY USING HASH 'filename' => 'true/false' !!!
5903 if ((setup_file_hash = loadSetupFileHash(filename)) != NULL)
5905 BEGIN_HASH_ITERATION(setup_file_hash, itr)
5907 char *token = HASH_ITERATION_TOKEN(itr);
5909 if (strPrefix(token, "custom_"))
5911 redefined_ce_found = TRUE;
5916 END_HASH_ITERATION(setup_file_hash, itr)
5918 freeSetupFileHash(setup_file_hash);
5921 return redefined_ce_found;
5924 static boolean CheckArtworkTypeForRedefinedCustomElements(int type)
5926 char *filename_base, *filename_local;
5927 boolean redefined_ce_found = FALSE;
5929 setLevelArtworkDir(ARTWORK_FIRST_NODE(artwork, type));
5932 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
5933 "leveldir_current->identifier == '%s'",
5934 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5935 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
5936 "leveldir_current->graphics_path == '%s'",
5937 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5938 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
5939 "leveldir_current->graphics_set == '%s'",
5940 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5941 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
5942 "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
5943 leveldir_current == NULL ? "[NULL]" :
5944 LEVELDIR_ARTWORK_SET(leveldir_current, type));
5947 // first look for special artwork configured in level series config
5948 filename_base = getCustomArtworkLevelConfigFilename(type);
5951 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
5952 "filename_base == '%s'", filename_base);
5955 if (fileExists(filename_base))
5956 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_base);
5958 filename_local = getCustomArtworkConfigFilename(type);
5961 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
5962 "filename_local == '%s'", filename_local);
5965 if (filename_local != NULL && !strEqual(filename_base, filename_local))
5966 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_local);
5969 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
5970 "redefined_ce_found == %d", redefined_ce_found);
5973 return redefined_ce_found;
5976 static void InitOverrideArtwork(void)
5978 boolean redefined_ce_found = FALSE;
5980 // to check if this level set redefines any CEs, do not use overriding
5981 gfx.override_level_graphics = FALSE;
5982 gfx.override_level_sounds = FALSE;
5983 gfx.override_level_music = FALSE;
5985 // now check if this level set has definitions for custom elements
5986 if (setup.override_level_graphics == AUTO ||
5987 setup.override_level_sounds == AUTO ||
5988 setup.override_level_music == AUTO)
5989 redefined_ce_found =
5990 (CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_GRAPHICS) |
5991 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_SOUNDS) |
5992 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_MUSIC));
5995 Debug("init:InitOverrideArtwork", "redefined_ce_found == %d",
5996 redefined_ce_found);
5999 if (redefined_ce_found)
6001 // this level set has CE definitions: change "AUTO" to "FALSE"
6002 gfx.override_level_graphics = (setup.override_level_graphics == TRUE);
6003 gfx.override_level_sounds = (setup.override_level_sounds == TRUE);
6004 gfx.override_level_music = (setup.override_level_music == TRUE);
6008 // this level set has no CE definitions: change "AUTO" to "TRUE"
6009 gfx.override_level_graphics = (setup.override_level_graphics != FALSE);
6010 gfx.override_level_sounds = (setup.override_level_sounds != FALSE);
6011 gfx.override_level_music = (setup.override_level_music != FALSE);
6015 Debug("init:InitOverrideArtwork", "%d, %d, %d",
6016 gfx.override_level_graphics,
6017 gfx.override_level_sounds,
6018 gfx.override_level_music);
6022 static char *getNewArtworkIdentifier(int type)
6024 static char *last_leveldir_identifier[3] = { NULL, NULL, NULL };
6025 static char *last_artwork_identifier[3] = { NULL, NULL, NULL };
6026 static boolean last_override_level_artwork[3] = { FALSE, FALSE, FALSE };
6027 static boolean last_has_level_artwork_set[3] = { FALSE, FALSE, FALSE };
6028 static boolean initialized[3] = { FALSE, FALSE, FALSE };
6029 TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type);
6030 boolean setup_override_artwork = GFX_OVERRIDE_ARTWORK(type);
6031 char *setup_artwork_set = SETUP_ARTWORK_SET(setup, type);
6032 char *leveldir_identifier = leveldir_current->identifier;
6033 // !!! setLevelArtworkDir() should be moved to an earlier stage !!!
6034 char *leveldir_artwork_set = setLevelArtworkDir(artwork_first_node);
6035 boolean has_level_artwork_set = (leveldir_artwork_set != NULL);
6036 char *artwork_current_identifier;
6037 char *artwork_new_identifier = NULL; // default: nothing has changed
6039 // leveldir_current may be invalid (level group, parent link)
6040 if (!validLevelSeries(leveldir_current))
6043 /* 1st step: determine artwork set to be activated in descending order:
6044 --------------------------------------------------------------------
6045 1. setup artwork (when configured to override everything else)
6046 2. artwork set configured in "levelinfo.conf" of current level set
6047 (artwork in level directory will have priority when loading later)
6048 3. artwork in level directory (stored in artwork sub-directory)
6049 4. setup artwork (currently configured in setup menu) */
6051 if (setup_override_artwork)
6052 artwork_current_identifier = setup_artwork_set;
6053 else if (leveldir_artwork_set != NULL)
6054 artwork_current_identifier = leveldir_artwork_set;
6055 else if (getTreeInfoFromIdentifier(artwork_first_node, leveldir_identifier))
6056 artwork_current_identifier = leveldir_identifier;
6058 artwork_current_identifier = setup_artwork_set;
6060 /* 2nd step: check if it is really needed to reload artwork set
6061 ------------------------------------------------------------ */
6063 // ---------- reload if level set and also artwork set has changed ----------
6064 if (last_leveldir_identifier[type] != leveldir_identifier &&
6065 (last_has_level_artwork_set[type] || has_level_artwork_set))
6066 artwork_new_identifier = artwork_current_identifier;
6068 last_leveldir_identifier[type] = leveldir_identifier;
6069 last_has_level_artwork_set[type] = has_level_artwork_set;
6071 // ---------- reload if "override artwork" setting has changed --------------
6072 if (last_override_level_artwork[type] != setup_override_artwork)
6073 artwork_new_identifier = artwork_current_identifier;
6075 last_override_level_artwork[type] = setup_override_artwork;
6077 // ---------- reload if current artwork identifier has changed --------------
6078 if (!strEqual(last_artwork_identifier[type], artwork_current_identifier))
6079 artwork_new_identifier = artwork_current_identifier;
6081 // (we cannot compare string pointers here, so copy string content itself)
6082 setString(&last_artwork_identifier[type], artwork_current_identifier);
6084 *(ARTWORK_CURRENT_IDENTIFIER_PTR(artwork, type)) = artwork_current_identifier;
6086 // ---------- do not reload directly after starting -------------------------
6087 if (!initialized[type])
6088 artwork_new_identifier = NULL;
6090 initialized[type] = TRUE;
6092 return artwork_new_identifier;
6095 void ReloadCustomArtwork(int force_reload)
6097 int last_game_status = game_status; // save current game status
6098 char *gfx_new_identifier;
6099 char *snd_new_identifier;
6100 char *mus_new_identifier;
6101 boolean force_reload_gfx = (force_reload & (1 << ARTWORK_TYPE_GRAPHICS));
6102 boolean force_reload_snd = (force_reload & (1 << ARTWORK_TYPE_SOUNDS));
6103 boolean force_reload_mus = (force_reload & (1 << ARTWORK_TYPE_MUSIC));
6104 boolean reload_needed;
6106 InitOverrideArtwork();
6108 AdjustGraphicsForEMC();
6109 AdjustSoundsForEMC();
6111 gfx_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6112 snd_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6113 mus_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6115 reload_needed = (gfx_new_identifier != NULL || force_reload_gfx ||
6116 snd_new_identifier != NULL || force_reload_snd ||
6117 mus_new_identifier != NULL || force_reload_mus);
6122 print_timestamp_init("ReloadCustomArtwork");
6124 SetGameStatus(GAME_MODE_LOADING);
6126 FadeOut(REDRAW_ALL);
6128 ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6129 print_timestamp_time("ClearRectangle");
6133 if (gfx_new_identifier != NULL || force_reload_gfx)
6136 Debug("init:ReloadCustomArtwork",
6137 "RELOADING GRAPHICS '%s' -> '%s' ['%s', '%s']",
6138 artwork.gfx_current_identifier,
6140 artwork.gfx_current->identifier,
6141 leveldir_current->graphics_set);
6145 print_timestamp_time("InitImages");
6148 if (snd_new_identifier != NULL || force_reload_snd)
6150 InitSound(snd_new_identifier);
6151 print_timestamp_time("InitSound");
6154 if (mus_new_identifier != NULL || force_reload_mus)
6156 InitMusic(mus_new_identifier);
6157 print_timestamp_time("InitMusic");
6162 SetGameStatus(last_game_status); // restore current game status
6164 init_last = init; // switch to new busy animation
6166 FadeOut(REDRAW_ALL);
6168 RedrawGlobalBorder();
6170 // force redraw of (open or closed) door graphics
6171 SetDoorState(DOOR_OPEN_ALL);
6172 CloseDoor(DOOR_CLOSE_ALL | DOOR_NO_DELAY);
6174 FadeSetEnterScreen();
6175 FadeSkipNextFadeOut();
6177 print_timestamp_done("ReloadCustomArtwork");
6179 LimitScreenUpdates(FALSE);
6182 void KeyboardAutoRepeatOffUnlessAutoplay(void)
6184 if (global.autoplay_leveldir == NULL)
6185 KeyboardAutoRepeatOff();
6188 void DisplayExitMessage(char *format, va_list ap)
6190 // also check for initialized video (headless flag may be temporarily unset)
6191 if (program.headless || !video.initialized)
6194 // check if draw buffer and fonts for exit message are already available
6195 if (drawto == NULL || font_initial[NUM_INITIAL_FONTS - 1].bitmap == NULL)
6198 int font_1 = FC_RED;
6199 int font_2 = FC_YELLOW;
6200 int font_3 = FC_BLUE;
6201 int font_width = getFontWidth(font_2);
6202 int font_height = getFontHeight(font_2);
6205 int sxsize = WIN_XSIZE - 2 * sx;
6206 int sysize = WIN_YSIZE - 2 * sy;
6207 int line_length = sxsize / font_width;
6208 int max_lines = sysize / font_height;
6209 int num_lines_printed;
6213 gfx.sxsize = sxsize;
6214 gfx.sysize = sysize;
6218 ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6220 DrawTextSCentered(sy, font_1, "Fatal error:");
6221 sy += 3 * font_height;;
6224 DrawTextBufferVA(sx, sy, format, ap, font_2,
6225 line_length, line_length, max_lines,
6226 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6227 sy += (num_lines_printed + 3) * font_height;
6229 DrawTextSCentered(sy, font_1, "For details, see the following error file:");
6230 sy += 3 * font_height;
6233 DrawTextBuffer(sx, sy, program.log_filename[LOG_ERR_ID], font_2,
6234 line_length, line_length, max_lines,
6235 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6237 DrawTextSCentered(SYSIZE - 20, font_3, "Press any key or button to exit");
6239 redraw_mask = REDRAW_ALL;
6241 // force drawing exit message even if screen updates are currently limited
6242 LimitScreenUpdates(FALSE);
6246 // deactivate toons on error message screen
6247 setup.toons = FALSE;
6249 WaitForEventToContinue();
6253 // ============================================================================
6255 // ============================================================================
6259 print_timestamp_init("OpenAll");
6261 SetGameStatus(GAME_MODE_LOADING);
6265 InitGlobal(); // initialize some global variables
6267 InitRND(NEW_RANDOMIZE);
6268 InitSimpleRandom(NEW_RANDOMIZE);
6269 InitBetterRandom(NEW_RANDOMIZE);
6271 print_timestamp_time("[init global stuff]");
6275 print_timestamp_time("[init setup/config stuff (1)]");
6277 if (options.execute_command)
6278 Execute_Command(options.execute_command);
6280 InitNetworkSettings();
6284 if (network.serveronly)
6286 #if defined(PLATFORM_UNIX)
6287 NetworkServer(network.server_port, TRUE);
6289 Warn("networking only supported in Unix version");
6292 exit(0); // never reached, server loops forever
6296 print_timestamp_time("[init setup/config stuff (2)]");
6298 print_timestamp_time("[init setup/config stuff (3)]");
6299 InitArtworkInfo(); // needed before loading gfx, sound & music
6300 print_timestamp_time("[init setup/config stuff (4)]");
6301 InitArtworkConfig(); // needed before forking sound child process
6302 print_timestamp_time("[init setup/config stuff (5)]");
6304 print_timestamp_time("[init setup/config stuff (6)]");
6308 print_timestamp_time("[init setup/config stuff]");
6310 InitVideoDefaults();
6312 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
6315 InitEventFilter(FilterMouseMotionEvents);
6317 print_timestamp_time("[init video stuff]");
6319 InitElementPropertiesStatic();
6320 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
6321 InitElementPropertiesGfxElement();
6323 print_timestamp_time("[init element properties stuff]");
6327 print_timestamp_time("InitGfx");
6330 print_timestamp_time("InitLevelInfo");
6332 InitLevelArtworkInfo();
6333 print_timestamp_time("InitLevelArtworkInfo");
6335 InitOverrideArtwork(); // needs to know current level directory
6336 print_timestamp_time("InitOverrideArtwork");
6338 InitImages(); // needs to know current level directory
6339 print_timestamp_time("InitImages");
6341 InitSound(NULL); // needs to know current level directory
6342 print_timestamp_time("InitSound");
6344 InitMusic(NULL); // needs to know current level directory
6345 print_timestamp_time("InitMusic");
6349 InitGfxBackground();
6355 if (global.autoplay_leveldir)
6360 else if (global.patchtapes_leveldir)
6365 else if (global.convert_leveldir)
6370 else if (global.dumplevel_leveldir)
6375 else if (global.dumptape_leveldir)
6380 else if (global.create_sketch_images_dir)
6382 CreateLevelSketchImages();
6385 else if (global.create_collect_images_dir)
6387 CreateCollectElementImages();
6391 InitNetworkServer();
6393 SetGameStatus(GAME_MODE_MAIN);
6395 FadeSetEnterScreen();
6396 if (!(fading.fade_mode & FADE_TYPE_TRANSFORM))
6397 FadeSkipNextFadeOut();
6399 print_timestamp_time("[post-artwork]");
6401 print_timestamp_done("OpenAll");
6403 if (setup.ask_for_remaining_tapes)
6404 setup.ask_for_uploading_tapes = TRUE;
6409 Debug("internal:path", "SDL_GetBasePath() == '%s'",
6411 Debug("internal:path", "SDL_GetPrefPath() == '%s'",
6412 SDL_GetPrefPath("artsoft", "rocksndiamonds"));
6413 #if defined(PLATFORM_ANDROID)
6414 Debug("internal:path", "SDL_AndroidGetInternalStoragePath() == '%s'",
6415 SDL_AndroidGetInternalStoragePath());
6416 Debug("internal:path", "SDL_AndroidGetExternalStoragePath() == '%s'",
6417 SDL_AndroidGetExternalStoragePath());
6418 Debug("internal:path", "SDL_AndroidGetExternalStorageState() == '%s'",
6419 (SDL_AndroidGetExternalStorageState() &
6420 SDL_ANDROID_EXTERNAL_STORAGE_WRITE ? "writable" :
6421 SDL_AndroidGetExternalStorageState() &
6422 SDL_ANDROID_EXTERNAL_STORAGE_READ ? "readable" : "not available"));
6427 static boolean WaitForApiThreads(void)
6429 unsigned int thread_delay = 0;
6430 unsigned int thread_delay_value = 10000;
6432 if (program.api_thread_count == 0)
6435 // deactivate global animations (not accessible in game state "loading")
6436 setup.toons = FALSE;
6438 // set game state to "loading" to be able to show busy animation
6439 SetGameStatus(GAME_MODE_LOADING);
6441 ResetDelayCounter(&thread_delay);
6443 // wait for threads to finish (and fail on timeout)
6444 while (program.api_thread_count > 0)
6446 if (DelayReached(&thread_delay, thread_delay_value))
6448 Error("failed waiting for threads - TIMEOUT");
6453 UPDATE_BUSY_STATE();
6461 void CloseAllAndExit(int exit_value)
6463 WaitForApiThreads();
6468 CloseAudio(); // called after freeing sounds (needed for SDL)
6476 // set a flag to tell the network server thread to quit and wait for it
6477 // using SDL_WaitThread()
6479 // Code used with SDL 1.2:
6480 // if (network.server_thread) // terminate network server
6481 // SDL_KillThread(network.server_thread);
6483 CloseVideoDisplay();
6484 ClosePlatformDependentStuff();
6486 if (exit_value != 0 && !options.execute_command)
6488 // fall back to default level set (current set may have caused an error)
6489 SaveLevelSetup_LastSeries_Deactivate();
6491 // tell user where to find error log file which may contain more details
6492 // (error notification now directly displayed on screen inside R'n'D
6493 // NotifyUserAboutErrorFile(); // currently only works for Windows