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_INITIAL "global.busy_initial"
38 #define CONFIG_TOKEN_GLOBAL_BUSY "global.busy"
39 #define CONFIG_TOKEN_GLOBAL_BUSY_PLAYFIELD "global.busy_playfield"
40 #define CONFIG_TOKEN_BACKGROUND "background"
41 #define CONFIG_TOKEN_BACKGROUND_LOADING_INITIAL "background.LOADING_INITIAL"
42 #define CONFIG_TOKEN_BACKGROUND_LOADING "background.LOADING"
44 #define INITIAL_IMG_GLOBAL_BUSY_INITIAL 0
45 #define INITIAL_IMG_GLOBAL_BUSY 1
46 #define INITIAL_IMG_GLOBAL_BUSY_PLAYFIELD 2
48 #define NUM_INITIAL_IMAGES_BUSY 3
50 #define INITIAL_IMG_BACKGROUND 3
51 #define INITIAL_IMG_BACKGROUND_LOADING_INITIAL 4
52 #define INITIAL_IMG_BACKGROUND_LOADING 5
54 #define NUM_INITIAL_IMAGES 6
57 static struct FontBitmapInfo font_initial[NUM_INITIAL_FONTS];
58 static struct GraphicInfo image_initial[NUM_INITIAL_IMAGES];
60 static int copy_properties[][5] =
64 EL_BUG_LEFT, EL_BUG_RIGHT,
65 EL_BUG_UP, EL_BUG_DOWN
69 EL_SPACESHIP_LEFT, EL_SPACESHIP_RIGHT,
70 EL_SPACESHIP_UP, EL_SPACESHIP_DOWN
74 EL_BD_BUTTERFLY_LEFT, EL_BD_BUTTERFLY_RIGHT,
75 EL_BD_BUTTERFLY_UP, EL_BD_BUTTERFLY_DOWN
79 EL_BD_FIREFLY_LEFT, EL_BD_FIREFLY_RIGHT,
80 EL_BD_FIREFLY_UP, EL_BD_FIREFLY_DOWN
84 EL_PACMAN_LEFT, EL_PACMAN_RIGHT,
85 EL_PACMAN_UP, EL_PACMAN_DOWN
89 EL_YAMYAM_LEFT, EL_YAMYAM_RIGHT,
90 EL_YAMYAM_UP, EL_YAMYAM_DOWN
94 EL_MOLE_LEFT, EL_MOLE_RIGHT,
95 EL_MOLE_UP, EL_MOLE_DOWN
99 EL_SPRING_LEFT, EL_SPRING_RIGHT,
100 EL_SPRING_LEFT, EL_SPRING_RIGHT, // (to match array size)
109 // forward declaration for internal use
110 static int get_graphic_parameter_value(char *, char *, int);
113 static int getLoadingBackgroundImage(int graphic)
115 return getImageFromGraphicOrDefault(graphic, INITIAL_IMG_BACKGROUND);
118 static void SetLoadingWindowBackgroundImage(int graphic)
120 SetBackgroundImage(getLoadingBackgroundImage(graphic), REDRAW_ALL);
123 static void SetLoadingBackgroundImage(void)
125 struct GraphicInfo *graphic_info_last = graphic_info;
126 int background_image = (game_status_last_screen == -1 ?
127 INITIAL_IMG_BACKGROUND_LOADING_INITIAL :
128 INITIAL_IMG_BACKGROUND_LOADING);
130 graphic_info = image_initial;
132 SetDrawDeactivationMask(REDRAW_NONE);
133 SetDrawBackgroundMask(REDRAW_ALL);
135 SetLoadingWindowBackgroundImage(background_image);
137 graphic_info = graphic_info_last;
140 static void DrawInitAnim(boolean only_when_loading)
142 struct GraphicInfo *graphic_info_last = graphic_info;
143 int graphic = (game_status_last_screen == -1 ?
144 INITIAL_IMG_GLOBAL_BUSY_INITIAL :
145 game_status == GAME_MODE_LOADING ?
146 INITIAL_IMG_GLOBAL_BUSY :
147 INITIAL_IMG_GLOBAL_BUSY_PLAYFIELD);
148 struct MenuPosInfo *busy = (game_status_last_screen == -1 ?
149 &init_last.busy_initial :
150 game_status == GAME_MODE_LOADING ?
152 &init_last.busy_playfield);
153 static DelayCounter action_delay = { 0 };
154 int sync_frame = FrameCounter;
157 action_delay.value = GameFrameDelay;
159 // prevent OS (Windows) from complaining about program not responding
162 if (game_status != GAME_MODE_LOADING && only_when_loading)
165 if (image_initial[graphic].bitmap == NULL || window == NULL)
168 if (!DelayReached(&action_delay))
172 busy->x = (game_status == GAME_MODE_LOADING ? WIN_XSIZE / 2 : SXSIZE / 2);
174 busy->y = (game_status == GAME_MODE_LOADING ? WIN_YSIZE / 2 : SYSIZE / 2);
176 x = (game_status == GAME_MODE_LOADING ? 0 : SX) + ALIGNED_TEXT_XPOS(busy);
177 y = (game_status == GAME_MODE_LOADING ? 0 : SY) + ALIGNED_TEXT_YPOS(busy);
179 graphic_info = image_initial;
181 if (sync_frame % image_initial[graphic].anim_delay == 0)
185 int width = graphic_info[graphic].width;
186 int height = graphic_info[graphic].height;
187 int frame = getGraphicAnimationFrame(graphic, sync_frame);
189 ClearRectangleOnBackground(drawto, x, y, width, height);
191 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
192 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height, x, y);
194 BlitBitmap(drawto, window, x, y, width, height, x, y);
197 graphic_info = graphic_info_last;
202 static void DrawProgramInfo(void)
204 int font1_nr = FC_YELLOW;
205 int font2_nr = FC_RED;
206 int font2_height = getFontHeight(font2_nr);
209 int ypos3 = WIN_YSIZE - 20 - font2_height;
211 DrawInitText(getProgramInitString(), ypos1, font1_nr);
212 DrawInitText(setup.internal.program_copyright, ypos2, font2_nr);
213 DrawInitText(setup.internal.program_website, ypos3, font2_nr);
216 static void FreeGadgets(void)
218 FreeLevelEditorGadgets();
225 void InitGadgets(void)
227 static boolean gadgets_initialized = FALSE;
229 if (gadgets_initialized)
232 CreateLevelEditorGadgets();
236 CreateScreenGadgets();
238 InitGadgetsSoundCallback(PlaySoundActivating, PlaySoundSelecting);
240 gadgets_initialized = TRUE;
243 static void InitElementSmallImagesScaledUp(int graphic)
245 struct GraphicInfo *g = &graphic_info[graphic];
247 // if graphic was cloned, scale cloned graphic
248 if (graphic_info[graphic].clone_from != -1)
249 graphic = graphic_info[graphic].clone_from;
251 // create small and game tile sized bitmaps (and scale up, if needed)
252 CreateImageWithSmallImages(graphic, g->scale_up_factor, g->tile_size);
255 static void InitElementSmallImages(void)
257 print_timestamp_init("InitElementSmallImages");
259 static int special_graphics[] =
273 IMG_EDITOR_ELEMENT_BORDER,
274 IMG_EDITOR_ELEMENT_BORDER_INPUT,
275 IMG_EDITOR_CASCADE_LIST,
276 IMG_EDITOR_CASCADE_LIST_ACTIVE,
279 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
280 int num_property_mappings = getImageListPropertyMappingSize();
283 print_timestamp_time("getImageListPropertyMapping/Size");
285 print_timestamp_init("InitElementSmallImagesScaledUp (1)");
286 // initialize normal element images from static configuration
287 for (i = 0; element_to_graphic[i].element > -1; i++)
288 InitElementSmallImagesScaledUp(element_to_graphic[i].graphic);
289 print_timestamp_done("InitElementSmallImagesScaledUp (1)");
291 // initialize special element images from static configuration
292 for (i = 0; element_to_special_graphic[i].element > -1; i++)
293 InitElementSmallImagesScaledUp(element_to_special_graphic[i].graphic);
294 print_timestamp_time("InitElementSmallImagesScaledUp (2)");
296 // initialize element images from dynamic configuration
297 for (i = 0; i < num_property_mappings; i++)
298 if (property_mapping[i].base_index < MAX_NUM_ELEMENTS)
299 InitElementSmallImagesScaledUp(property_mapping[i].artwork_index);
300 print_timestamp_time("InitElementSmallImagesScaledUp (3)");
302 // initialize special non-element images from above list
303 for (i = 0; special_graphics[i] > -1; i++)
304 InitElementSmallImagesScaledUp(special_graphics[i]);
305 print_timestamp_time("InitElementSmallImagesScaledUp (4)");
307 print_timestamp_done("InitElementSmallImages");
310 static void InitScaledImagesScaledUp(int graphic)
312 struct GraphicInfo *g = &graphic_info[graphic];
314 // if graphic was cloned, scale cloned graphic
315 if (graphic_info[graphic].clone_from != -1)
316 graphic = graphic_info[graphic].clone_from;
318 ScaleImage(graphic, g->scale_up_factor);
321 static void InitScaledImages(void)
323 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
324 int num_property_mappings = getImageListPropertyMappingSize();
327 // scale normal images from static configuration, if not already scaled
328 for (i = 0; i < NUM_IMAGE_FILES; i++)
329 InitScaledImagesScaledUp(i);
331 // scale images from dynamic configuration, if not already scaled
332 for (i = 0; i < num_property_mappings; i++)
333 InitScaledImagesScaledUp(property_mapping[i].artwork_index);
336 static void InitBitmapPointers(void)
338 int num_images = getImageListSize();
341 // standard size bitmap may have changed -- update default bitmap pointer
342 for (i = 0; i < num_images; i++)
343 if (graphic_info[i].bitmaps)
344 graphic_info[i].bitmap = graphic_info[i].bitmaps[IMG_BITMAP_STANDARD];
347 void InitImageTextures(void)
349 static int texture_graphics[] =
351 IMG_GFX_REQUEST_BUTTON_TOUCH_YES,
352 IMG_GFX_REQUEST_BUTTON_TOUCH_NO,
353 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM,
354 IMG_GFX_GAME_BUTTON_TOUCH_STOP,
355 IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,
356 IMG_MENU_BUTTON_TOUCH_BACK,
357 IMG_MENU_BUTTON_TOUCH_NEXT,
358 IMG_MENU_BUTTON_TOUCH_BACK2,
359 IMG_MENU_BUTTON_TOUCH_NEXT2,
364 FreeAllImageTextures();
366 for (i = IMG_GLOBAL_BORDER_FIRST; i <= IMG_GLOBAL_BORDER_LAST; i++)
367 CreateImageTextures(i);
369 for (i = 0; i < MAX_NUM_TOONS; i++)
370 CreateImageTextures(IMG_TOON_1 + i);
372 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
374 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
376 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
378 int graphic = global_anim_info[i].graphic[j][k];
380 if (graphic == IMG_UNDEFINED)
383 CreateImageTextures(graphic);
388 for (i = 0; texture_graphics[i] > -1; i++)
389 CreateImageTextures(texture_graphics[i]);
392 static int getFontSpecialSuffix(void)
396 // (special case: do not use special font for GAME_MODE_LOADING)
397 if (game_status >= GAME_MODE_TITLE_INITIAL &&
398 game_status <= GAME_MODE_PSEUDO_PREVIEW)
399 special = game_status;
400 else if (game_status == GAME_MODE_PSEUDO_TYPENAME)
401 special = GFX_SPECIAL_ARG_MAIN;
402 else if (game_status == GAME_MODE_PSEUDO_TYPENAMES)
403 special = GFX_SPECIAL_ARG_NAMES;
408 static int getFontBitmapID(int font_nr)
410 int special = getFontSpecialSuffix();
413 return font_info[font_nr].special_bitmap_id[special];
418 static int getFontFromToken(char *token)
420 char *value = getHashEntry(font_token_hash, token);
425 // if font not found, use reliable default value
426 return FONT_INITIAL_1;
429 static char *getTokenFromFont(int font_nr)
431 static char *token = NULL;
432 int special = getFontSpecialSuffix();
437 token = getStringCat2(font_info[font_nr].token_name,
438 special_suffix_info[special].suffix);
440 token = getStringCopy(font_info[font_nr].token_name);
445 static void InitFontGraphicInfo(void)
447 static struct FontBitmapInfo *font_bitmap_info = NULL;
448 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
449 int num_property_mappings = getImageListPropertyMappingSize();
450 int num_font_bitmaps = NUM_FONTS;
453 if (graphic_info == NULL) // still at startup phase
455 InitFontInfo(font_initial, NUM_INITIAL_FONTS,
456 getFontBitmapID, getFontFromToken, getTokenFromFont);
461 // ---------- initialize font graphic definitions ----------
463 // always start with reliable default values (normal font graphics)
464 for (i = 0; i < NUM_FONTS; i++)
465 font_info[i].graphic = IMG_FONT_INITIAL_1;
467 // initialize normal font/graphic mapping from static configuration
468 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
470 int font_nr = font_to_graphic[i].font_nr;
471 int special = font_to_graphic[i].special;
472 int graphic = font_to_graphic[i].graphic;
477 font_info[font_nr].graphic = graphic;
480 // always start with reliable default values (special font graphics)
481 for (i = 0; i < NUM_FONTS; i++)
483 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
485 font_info[i].special_graphic[j] = font_info[i].graphic;
486 font_info[i].special_bitmap_id[j] = i;
490 // initialize special font/graphic mapping from static configuration
491 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
493 int font_nr = font_to_graphic[i].font_nr;
494 int special = font_to_graphic[i].special;
495 int graphic = font_to_graphic[i].graphic;
496 int base_graphic = font2baseimg(font_nr);
498 if (IS_SPECIAL_GFX_ARG(special))
500 boolean base_redefined =
501 getImageListEntryFromImageID(base_graphic)->redefined;
502 boolean special_redefined =
503 getImageListEntryFromImageID(graphic)->redefined;
504 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
506 /* if the base font ("font.title_1", for example) has been redefined,
507 but not the special font ("font.title_1.LEVELS", for example), do not
508 use an existing (in this case considered obsolete) special font
509 anymore, but use the automatically determined default font */
510 /* special case: cloned special fonts must be explicitly redefined,
511 but are not automatically redefined by redefining base font */
512 if (base_redefined && !special_redefined && !special_cloned)
515 font_info[font_nr].special_graphic[special] = graphic;
516 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
521 // initialize special font/graphic mapping from dynamic configuration
522 for (i = 0; i < num_property_mappings; i++)
524 int font_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
525 int special = property_mapping[i].ext3_index;
526 int graphic = property_mapping[i].artwork_index;
528 if (font_nr < 0 || font_nr >= NUM_FONTS)
531 if (IS_SPECIAL_GFX_ARG(special))
533 font_info[font_nr].special_graphic[special] = graphic;
534 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
539 /* correct special font/graphic mapping for cloned fonts for downwards
540 compatibility of PREVIEW fonts -- this is only needed for implicit
541 redefinition of special font by redefined base font, and only if other
542 fonts are cloned from this special font (like in the "Zelda" level set) */
543 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
545 int font_nr = font_to_graphic[i].font_nr;
546 int special = font_to_graphic[i].special;
547 int graphic = font_to_graphic[i].graphic;
549 if (IS_SPECIAL_GFX_ARG(special))
551 boolean special_redefined =
552 getImageListEntryFromImageID(graphic)->redefined;
553 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
555 if (special_cloned && !special_redefined)
559 for (j = 0; font_to_graphic[j].font_nr > -1; j++)
561 int font_nr2 = font_to_graphic[j].font_nr;
562 int special2 = font_to_graphic[j].special;
563 int graphic2 = font_to_graphic[j].graphic;
565 if (IS_SPECIAL_GFX_ARG(special2) &&
566 graphic2 == graphic_info[graphic].clone_from)
568 font_info[font_nr].special_graphic[special] =
569 font_info[font_nr2].special_graphic[special2];
570 font_info[font_nr].special_bitmap_id[special] =
571 font_info[font_nr2].special_bitmap_id[special2];
578 // reset non-redefined ".active" font graphics if normal font is redefined
579 // (this different treatment is needed because normal and active fonts are
580 // independently defined ("active" is not a property of font definitions!)
581 for (i = 0; i < NUM_FONTS; i++)
583 int font_nr_base = i;
584 int font_nr_active = FONT_ACTIVE(font_nr_base);
586 // check only those fonts with exist as normal and ".active" variant
587 if (font_nr_base != font_nr_active)
589 int base_graphic = font_info[font_nr_base].graphic;
590 int active_graphic = font_info[font_nr_active].graphic;
591 boolean base_redefined =
592 getImageListEntryFromImageID(base_graphic)->redefined;
593 boolean active_redefined =
594 getImageListEntryFromImageID(active_graphic)->redefined;
596 /* if the base font ("font.menu_1", for example) has been redefined,
597 but not the active font ("font.menu_1.active", for example), do not
598 use an existing (in this case considered obsolete) active font
599 anymore, but use the automatically determined default font */
600 if (base_redefined && !active_redefined)
601 font_info[font_nr_active].graphic = base_graphic;
603 // now also check each "special" font (which may be the same as above)
604 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
606 int base_graphic = font_info[font_nr_base].special_graphic[j];
607 int active_graphic = font_info[font_nr_active].special_graphic[j];
608 boolean base_redefined =
609 getImageListEntryFromImageID(base_graphic)->redefined;
610 boolean active_redefined =
611 getImageListEntryFromImageID(active_graphic)->redefined;
613 // same as above, but check special graphic definitions, for example:
614 // redefined "font.menu_1.MAIN" invalidates "font.menu_1.active.MAIN"
615 if (base_redefined && !active_redefined)
617 font_info[font_nr_active].special_graphic[j] =
618 font_info[font_nr_base].special_graphic[j];
619 font_info[font_nr_active].special_bitmap_id[j] =
620 font_info[font_nr_base].special_bitmap_id[j];
626 // ---------- initialize font bitmap array ----------
628 if (font_bitmap_info != NULL)
629 FreeFontInfo(font_bitmap_info);
632 checked_calloc(num_font_bitmaps * sizeof(struct FontBitmapInfo));
634 // ---------- initialize font bitmap definitions ----------
636 for (i = 0; i < NUM_FONTS; i++)
638 if (i < NUM_INITIAL_FONTS)
640 font_bitmap_info[i] = font_initial[i];
644 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
646 int font_bitmap_id = font_info[i].special_bitmap_id[j];
647 int graphic = font_info[i].special_graphic[j];
649 // set 'graphic_info' for font entries, if uninitialized (guessed)
650 if (graphic_info[graphic].anim_frames < MIN_NUM_CHARS_PER_FONT)
652 graphic_info[graphic].anim_frames = DEFAULT_NUM_CHARS_PER_FONT;
653 graphic_info[graphic].anim_frames_per_line = DEFAULT_NUM_CHARS_PER_LINE;
656 // copy font relevant information from graphics information
657 font_bitmap_info[font_bitmap_id].bitmap = graphic_info[graphic].bitmap;
658 font_bitmap_info[font_bitmap_id].src_x = graphic_info[graphic].src_x;
659 font_bitmap_info[font_bitmap_id].src_y = graphic_info[graphic].src_y;
660 font_bitmap_info[font_bitmap_id].width = graphic_info[graphic].width;
661 font_bitmap_info[font_bitmap_id].height = graphic_info[graphic].height;
663 font_bitmap_info[font_bitmap_id].offset_x =
664 graphic_info[graphic].offset_x;
665 font_bitmap_info[font_bitmap_id].offset_y =
666 graphic_info[graphic].offset_y;
668 font_bitmap_info[font_bitmap_id].draw_xoffset =
669 graphic_info[graphic].draw_xoffset;
670 font_bitmap_info[font_bitmap_id].draw_yoffset =
671 graphic_info[graphic].draw_yoffset;
673 font_bitmap_info[font_bitmap_id].num_chars =
674 graphic_info[graphic].anim_frames;
675 font_bitmap_info[font_bitmap_id].num_chars_per_line =
676 graphic_info[graphic].anim_frames_per_line;
680 InitFontInfo(font_bitmap_info, num_font_bitmaps,
681 getFontBitmapID, getFontFromToken, getTokenFromFont);
684 static void InitGlobalAnimGraphicInfo(void)
686 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
687 int num_property_mappings = getImageListPropertyMappingSize();
690 if (graphic_info == NULL) // still at startup phase
693 // always start with reliable default values (no global animations)
694 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
695 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
696 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
697 global_anim_info[i].graphic[j][k] = IMG_UNDEFINED;
699 // initialize global animation definitions from static configuration
700 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
702 int j = GLOBAL_ANIM_ID_PART_BASE;
703 int k = GFX_SPECIAL_ARG_DEFAULT;
705 global_anim_info[i].graphic[j][k] = IMG_GFX_GLOBAL_ANIM_1 + i;
708 // initialize global animation definitions from dynamic configuration
709 for (i = 0; i < num_property_mappings; i++)
711 int anim_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS - NUM_FONTS;
712 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
713 int special = property_mapping[i].ext3_index;
714 int graphic = property_mapping[i].artwork_index;
716 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
719 // set animation part to base part, if not specified
720 if (!IS_GLOBAL_ANIM_PART(part_nr))
721 part_nr = GLOBAL_ANIM_ID_PART_BASE;
723 // set animation screen to default, if not specified
724 if (!IS_SPECIAL_GFX_ARG(special))
725 special = GFX_SPECIAL_ARG_DEFAULT;
727 global_anim_info[anim_nr].graphic[part_nr][special] = graphic;
729 // fix default value for ".draw_masked" (for backward compatibility)
730 struct GraphicInfo *g = &graphic_info[graphic];
731 struct FileInfo *image = getImageListEntryFromImageID(graphic);
732 char **parameter_raw = image->parameter;
733 int p = GFX_ARG_DRAW_MASKED;
734 int draw_masked = get_graphic_parameter_value(parameter_raw[p],
735 image_config_suffix[p].token,
736 image_config_suffix[p].type);
738 // if ".draw_masked" parameter is undefined, use default value "TRUE"
739 if (draw_masked == ARG_UNDEFINED_VALUE)
740 g->draw_masked = TRUE;
744 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
745 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
746 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
747 if (global_anim_info[i].graphic[j][k] != IMG_UNDEFINED &&
748 graphic_info[global_anim_info[i].graphic[j][k]].bitmap != NULL)
749 Debug("init:InitGlobalAnimGraphicInfo",
750 "anim %d, part %d, mode %d => %d",
751 i, j, k, global_anim_info[i].graphic[j][k]);
755 static void InitGlobalAnimSoundInfo(void)
757 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
758 int num_property_mappings = getSoundListPropertyMappingSize();
761 // always start with reliable default values (no global animation sounds)
762 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
763 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
764 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
765 global_anim_info[i].sound[j][k] = SND_UNDEFINED;
767 // initialize global animation sound definitions from dynamic configuration
768 for (i = 0; i < num_property_mappings; i++)
770 int anim_nr = property_mapping[i].base_index - 2 * MAX_NUM_ELEMENTS;
771 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
772 int special = property_mapping[i].ext3_index;
773 int sound = property_mapping[i].artwork_index;
775 // sound uses control definition; map it to position of graphic (artwork)
776 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
778 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
781 // set animation part to base part, if not specified
782 if (!IS_GLOBAL_ANIM_PART(part_nr))
783 part_nr = GLOBAL_ANIM_ID_PART_BASE;
785 // set animation screen to default, if not specified
786 if (!IS_SPECIAL_GFX_ARG(special))
787 special = GFX_SPECIAL_ARG_DEFAULT;
789 global_anim_info[anim_nr].sound[part_nr][special] = sound;
793 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
794 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
795 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
796 if (global_anim_info[i].sound[j][k] != SND_UNDEFINED)
797 Debug("init:InitGlobalAnimSoundInfo",
798 "anim %d, part %d, mode %d => %d",
799 i, j, k, global_anim_info[i].sound[j][k]);
803 static void InitGlobalAnimMusicInfo(void)
805 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
806 int num_property_mappings = getMusicListPropertyMappingSize();
809 // always start with reliable default values (no global animation music)
810 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
811 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
812 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
813 global_anim_info[i].music[j][k] = MUS_UNDEFINED;
815 // initialize global animation music definitions from dynamic configuration
816 for (i = 0; i < num_property_mappings; i++)
818 int anim_nr = property_mapping[i].base_index - NUM_MUSIC_PREFIXES;
819 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
820 int special = property_mapping[i].ext2_index;
821 int music = property_mapping[i].artwork_index;
823 // music uses control definition; map it to position of graphic (artwork)
824 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
826 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
829 // set animation part to base part, if not specified
830 if (!IS_GLOBAL_ANIM_PART(part_nr))
831 part_nr = GLOBAL_ANIM_ID_PART_BASE;
833 // set animation screen to default, if not specified
834 if (!IS_SPECIAL_GFX_ARG(special))
835 special = GFX_SPECIAL_ARG_DEFAULT;
837 global_anim_info[anim_nr].music[part_nr][special] = music;
841 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
842 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
843 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
844 if (global_anim_info[i].music[j][k] != MUS_UNDEFINED)
845 Debug("init:InitGlobalAnimMusicInfo",
846 "anim %d, part %d, mode %d => %d",
847 i, j, k, global_anim_info[i].music[j][k]);
851 static void InitElementGraphicInfo(void)
853 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
854 int num_property_mappings = getImageListPropertyMappingSize();
857 if (graphic_info == NULL) // still at startup phase
860 // set values to -1 to identify later as "uninitialized" values
861 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
863 for (act = 0; act < NUM_ACTIONS; act++)
865 element_info[i].graphic[act] = -1;
866 element_info[i].crumbled[act] = -1;
868 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
870 element_info[i].direction_graphic[act][dir] = -1;
871 element_info[i].direction_crumbled[act][dir] = -1;
878 // initialize normal element/graphic mapping from static configuration
879 for (i = 0; element_to_graphic[i].element > -1; i++)
881 int element = element_to_graphic[i].element;
882 int action = element_to_graphic[i].action;
883 int direction = element_to_graphic[i].direction;
884 boolean crumbled = element_to_graphic[i].crumbled;
885 int graphic = element_to_graphic[i].graphic;
886 int base_graphic = el2baseimg(element);
888 if (graphic_info[graphic].bitmap == NULL)
891 if ((action > -1 || direction > -1 || crumbled == TRUE) &&
894 boolean base_redefined =
895 getImageListEntryFromImageID(base_graphic)->redefined;
896 boolean act_dir_redefined =
897 getImageListEntryFromImageID(graphic)->redefined;
899 /* if the base graphic ("emerald", for example) has been redefined,
900 but not the action graphic ("emerald.falling", for example), do not
901 use an existing (in this case considered obsolete) action graphic
902 anymore, but use the automatically determined default graphic */
903 if (base_redefined && !act_dir_redefined)
908 action = ACTION_DEFAULT;
913 element_info[element].direction_crumbled[action][direction] = graphic;
915 element_info[element].crumbled[action] = graphic;
920 element_info[element].direction_graphic[action][direction] = graphic;
922 element_info[element].graphic[action] = graphic;
926 // initialize normal element/graphic mapping from dynamic configuration
927 for (i = 0; i < num_property_mappings; i++)
929 int element = property_mapping[i].base_index;
930 int action = property_mapping[i].ext1_index;
931 int direction = property_mapping[i].ext2_index;
932 int special = property_mapping[i].ext3_index;
933 int graphic = property_mapping[i].artwork_index;
934 boolean crumbled = FALSE;
936 if (special == GFX_SPECIAL_ARG_CRUMBLED)
942 if (graphic_info[graphic].bitmap == NULL)
945 if (element >= MAX_NUM_ELEMENTS || special != -1)
949 action = ACTION_DEFAULT;
954 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
955 element_info[element].direction_crumbled[action][dir] = -1;
958 element_info[element].direction_crumbled[action][direction] = graphic;
960 element_info[element].crumbled[action] = graphic;
965 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
966 element_info[element].direction_graphic[action][dir] = -1;
969 element_info[element].direction_graphic[action][direction] = graphic;
971 element_info[element].graphic[action] = graphic;
975 // now copy all graphics that are defined to be cloned from other graphics
976 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
978 int graphic = element_info[i].graphic[ACTION_DEFAULT];
979 int crumbled_like, diggable_like;
984 crumbled_like = graphic_info[graphic].crumbled_like;
985 diggable_like = graphic_info[graphic].diggable_like;
987 if (crumbled_like != -1 && element_info[i].crumbled[ACTION_DEFAULT] == -1)
989 for (act = 0; act < NUM_ACTIONS; act++)
990 element_info[i].crumbled[act] =
991 element_info[crumbled_like].crumbled[act];
992 for (act = 0; act < NUM_ACTIONS; act++)
993 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
994 element_info[i].direction_crumbled[act][dir] =
995 element_info[crumbled_like].direction_crumbled[act][dir];
998 if (diggable_like != -1 && element_info[i].graphic[ACTION_DIGGING] == -1)
1000 element_info[i].graphic[ACTION_DIGGING] =
1001 element_info[diggable_like].graphic[ACTION_DIGGING];
1002 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1003 element_info[i].direction_graphic[ACTION_DIGGING][dir] =
1004 element_info[diggable_like].direction_graphic[ACTION_DIGGING][dir];
1008 // set hardcoded definitions for some runtime elements without graphic
1009 element_info[EL_AMOEBA_TO_DIAMOND].graphic[ACTION_DEFAULT] = IMG_AMOEBA_DEAD;
1011 // set hardcoded definitions for some internal elements without graphic
1012 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1014 if (IS_EDITOR_CASCADE_INACTIVE(i))
1015 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST;
1016 else if (IS_EDITOR_CASCADE_ACTIVE(i))
1017 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST_ACTIVE;
1020 // now set all undefined/invalid graphics to -1 to set to default after it
1021 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1023 for (act = 0; act < NUM_ACTIONS; act++)
1027 graphic = element_info[i].graphic[act];
1028 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1029 element_info[i].graphic[act] = -1;
1031 graphic = element_info[i].crumbled[act];
1032 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1033 element_info[i].crumbled[act] = -1;
1035 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1037 graphic = element_info[i].direction_graphic[act][dir];
1038 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1039 element_info[i].direction_graphic[act][dir] = -1;
1041 graphic = element_info[i].direction_crumbled[act][dir];
1042 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1043 element_info[i].direction_crumbled[act][dir] = -1;
1048 UPDATE_BUSY_STATE();
1050 // adjust graphics with 2nd tile for movement according to direction
1051 // (do this before correcting '-1' values to minimize calculations)
1052 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1054 for (act = 0; act < NUM_ACTIONS; act++)
1056 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1058 int graphic = element_info[i].direction_graphic[act][dir];
1059 int move_dir = (act == ACTION_FALLING ? MV_BIT_DOWN : dir);
1061 if (act == ACTION_FALLING) // special case
1062 graphic = element_info[i].graphic[act];
1064 if (graphic != -1 &&
1065 graphic_info[graphic].double_movement &&
1066 graphic_info[graphic].swap_double_tiles != 0)
1068 struct GraphicInfo *g = &graphic_info[graphic];
1069 int src_x_front = g->src_x;
1070 int src_y_front = g->src_y;
1071 int src_x_back = g->src_x + g->offset2_x;
1072 int src_y_back = g->src_y + g->offset2_y;
1073 boolean frames_are_ordered_diagonally = (g->offset_x != 0 &&
1075 boolean front_is_left_or_upper = (src_x_front < src_x_back ||
1076 src_y_front < src_y_back);
1077 boolean swap_movement_tiles_always = (g->swap_double_tiles == 1);
1078 boolean swap_movement_tiles_autodetected =
1079 (!frames_are_ordered_diagonally &&
1080 ((move_dir == MV_BIT_LEFT && !front_is_left_or_upper) ||
1081 (move_dir == MV_BIT_UP && !front_is_left_or_upper) ||
1082 (move_dir == MV_BIT_RIGHT && front_is_left_or_upper) ||
1083 (move_dir == MV_BIT_DOWN && front_is_left_or_upper)));
1085 // swap frontside and backside graphic tile coordinates, if needed
1086 if (swap_movement_tiles_always || swap_movement_tiles_autodetected)
1088 // get current (wrong) backside tile coordinates
1089 getGraphicSourceXY(graphic, 0, &src_x_back, &src_y_back, TRUE);
1091 // set frontside tile coordinates to backside tile coordinates
1092 g->src_x = src_x_back;
1093 g->src_y = src_y_back;
1095 // invert tile offset to point to new backside tile coordinates
1099 // do not swap front and backside tiles again after correction
1100 g->swap_double_tiles = 0;
1107 UPDATE_BUSY_STATE();
1109 // now set all '-1' values to element specific default values
1110 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1112 int default_graphic = element_info[i].graphic[ACTION_DEFAULT];
1113 int default_crumbled = element_info[i].crumbled[ACTION_DEFAULT];
1114 int default_direction_graphic[NUM_DIRECTIONS_FULL];
1115 int default_direction_crumbled[NUM_DIRECTIONS_FULL];
1117 if (default_graphic == -1)
1118 default_graphic = IMG_UNKNOWN;
1120 if (default_crumbled == -1)
1121 default_crumbled = default_graphic;
1123 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1125 default_direction_graphic[dir] =
1126 element_info[i].direction_graphic[ACTION_DEFAULT][dir];
1127 default_direction_crumbled[dir] =
1128 element_info[i].direction_crumbled[ACTION_DEFAULT][dir];
1130 if (default_direction_graphic[dir] == -1)
1131 default_direction_graphic[dir] = default_graphic;
1133 if (default_direction_crumbled[dir] == -1)
1134 default_direction_crumbled[dir] = default_direction_graphic[dir];
1137 for (act = 0; act < NUM_ACTIONS; act++)
1139 boolean act_remove = ((IS_DIGGABLE(i) && act == ACTION_DIGGING) ||
1140 (IS_SNAPPABLE(i) && act == ACTION_SNAPPING) ||
1141 (IS_COLLECTIBLE(i) && act == ACTION_COLLECTING));
1142 boolean act_turning = (act == ACTION_TURNING_FROM_LEFT ||
1143 act == ACTION_TURNING_FROM_RIGHT ||
1144 act == ACTION_TURNING_FROM_UP ||
1145 act == ACTION_TURNING_FROM_DOWN);
1147 // generic default action graphic (defined by "[default]" directive)
1148 int default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1149 int default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1150 int default_remove_graphic = IMG_EMPTY;
1152 if (act_remove && default_action_graphic != -1)
1153 default_remove_graphic = default_action_graphic;
1155 // look for special default action graphic (classic game specific)
1156 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].graphic[act] != -1)
1157 default_action_graphic = element_info[EL_BD_DEFAULT].graphic[act];
1158 if (IS_BDX_ELEMENT(i) && element_info[EL_BDX_DEFAULT].graphic[act] != -1)
1159 default_action_graphic = element_info[EL_BDX_DEFAULT].graphic[act];
1160 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].graphic[act] != -1)
1161 default_action_graphic = element_info[EL_SP_DEFAULT].graphic[act];
1162 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].graphic[act] != -1)
1163 default_action_graphic = element_info[EL_SB_DEFAULT].graphic[act];
1164 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].graphic[act] != -1)
1165 default_action_graphic = element_info[EL_MM_DEFAULT].graphic[act];
1167 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].crumbled[act] != -1)
1168 default_action_crumbled = element_info[EL_BD_DEFAULT].crumbled[act];
1169 if (IS_BDX_ELEMENT(i) && element_info[EL_BDX_DEFAULT].crumbled[act] != -1)
1170 default_action_crumbled = element_info[EL_BDX_DEFAULT].crumbled[act];
1171 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].crumbled[act] != -1)
1172 default_action_crumbled = element_info[EL_SP_DEFAULT].crumbled[act];
1173 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].crumbled[act] != -1)
1174 default_action_crumbled = element_info[EL_SB_DEFAULT].crumbled[act];
1175 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].crumbled[act] != -1)
1176 default_action_crumbled = element_info[EL_MM_DEFAULT].crumbled[act];
1178 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
1179 // !!! make this better !!!
1180 if (i == EL_EMPTY_SPACE)
1182 default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1183 default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1186 if (default_action_graphic == -1)
1187 default_action_graphic = default_graphic;
1189 if (default_action_crumbled == -1)
1190 default_action_crumbled = default_action_graphic;
1192 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1194 // use action graphic as the default direction graphic, if undefined
1195 int default_action_direction_graphic = element_info[i].graphic[act];
1196 int default_action_direction_crumbled = element_info[i].crumbled[act];
1198 // no graphic for current action -- use default direction graphic
1199 if (default_action_direction_graphic == -1)
1200 default_action_direction_graphic =
1201 (act_remove ? default_remove_graphic :
1203 element_info[i].direction_graphic[ACTION_TURNING][dir] :
1204 default_action_graphic != default_graphic ?
1205 default_action_graphic :
1206 default_direction_graphic[dir]);
1208 if (element_info[i].direction_graphic[act][dir] == -1)
1209 element_info[i].direction_graphic[act][dir] =
1210 default_action_direction_graphic;
1212 if (default_action_direction_crumbled == -1)
1213 default_action_direction_crumbled =
1214 element_info[i].direction_graphic[act][dir];
1216 if (element_info[i].direction_crumbled[act][dir] == -1)
1217 element_info[i].direction_crumbled[act][dir] =
1218 default_action_direction_crumbled;
1221 // no graphic for this specific action -- use default action graphic
1222 if (element_info[i].graphic[act] == -1)
1223 element_info[i].graphic[act] =
1224 (act_remove ? default_remove_graphic :
1225 act_turning ? element_info[i].graphic[ACTION_TURNING] :
1226 default_action_graphic);
1228 if (element_info[i].crumbled[act] == -1)
1229 element_info[i].crumbled[act] = element_info[i].graphic[act];
1233 UPDATE_BUSY_STATE();
1236 static void InitElementSpecialGraphicInfo(void)
1238 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
1239 int num_property_mappings = getImageListPropertyMappingSize();
1242 // always start with reliable default values
1243 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1244 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1245 element_info[i].special_graphic[j] =
1246 element_info[i].graphic[ACTION_DEFAULT];
1248 // initialize special element/graphic mapping from static configuration
1249 for (i = 0; element_to_special_graphic[i].element > -1; i++)
1251 int element = element_to_special_graphic[i].element;
1252 int special = element_to_special_graphic[i].special;
1253 int graphic = element_to_special_graphic[i].graphic;
1254 int base_graphic = el2baseimg(element);
1255 boolean base_redefined =
1256 getImageListEntryFromImageID(base_graphic)->redefined;
1257 boolean special_redefined =
1258 getImageListEntryFromImageID(graphic)->redefined;
1260 /* if the base graphic ("emerald", for example) has been redefined,
1261 but not the special graphic ("emerald.EDITOR", for example), do not
1262 use an existing (in this case considered obsolete) special graphic
1263 anymore, but use the automatically created (down-scaled) graphic */
1264 if (base_redefined && !special_redefined)
1267 element_info[element].special_graphic[special] = graphic;
1270 // initialize special element/graphic mapping from dynamic configuration
1271 for (i = 0; i < num_property_mappings; i++)
1273 int element = property_mapping[i].base_index;
1274 int action = property_mapping[i].ext1_index;
1275 int direction = property_mapping[i].ext2_index;
1276 int special = property_mapping[i].ext3_index;
1277 int graphic = property_mapping[i].artwork_index;
1279 // for action ".active", replace element with active element, if exists
1280 if (action == ACTION_ACTIVE && element != ELEMENT_ACTIVE(element))
1282 element = ELEMENT_ACTIVE(element);
1286 // for BD effect editor graphics, replace element with effect element, if exists
1287 if (action != -1 && special == GFX_SPECIAL_ARG_EDITOR)
1289 int element_bd = map_element_RND_to_BD_effect(element, action);
1290 int element_ef = map_element_BD_to_RND_cave(element_bd);
1292 if (element_ef != EL_UNKNOWN)
1294 element = element_ef;
1299 if (element >= MAX_NUM_ELEMENTS)
1302 // do not change special graphic if action or direction was specified
1303 if (action != -1 || direction != -1)
1306 if (IS_SPECIAL_GFX_ARG(special))
1307 element_info[element].special_graphic[special] = graphic;
1310 // now set all undefined/invalid graphics to default
1311 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1312 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1313 if (graphic_info[element_info[i].special_graphic[j]].bitmap == NULL)
1314 element_info[i].special_graphic[j] =
1315 element_info[i].graphic[ACTION_DEFAULT];
1318 static int get_graphic_parameter_value(char *value_raw, char *suffix, int type)
1320 if (type != TYPE_ELEMENT && type != TYPE_GRAPHIC)
1321 return get_parameter_value(value_raw, suffix, type);
1323 if (strEqual(value_raw, ARG_UNDEFINED))
1324 return ARG_UNDEFINED_VALUE;
1326 if (type == TYPE_ELEMENT)
1328 char *value = getHashEntry(element_token_hash, value_raw);
1333 Warn("error found in config file:");
1334 Warn("- config file: '%s'", getImageConfigFilename());
1335 Warn("error: invalid element token '%s'", value_raw);
1336 Warn("custom graphic rejected for this element/action");
1337 Warn("fallback done to undefined element for this graphic");
1341 return (value != NULL ? atoi(value) : EL_UNDEFINED);
1343 else if (type == TYPE_GRAPHIC)
1345 char *value = getHashEntry(graphic_token_hash, value_raw);
1346 int fallback_graphic = IMG_CHAR_EXCLAM;
1351 Warn("error found in config file:");
1352 Warn("- config file: '%s'", getImageConfigFilename());
1353 Warn("error: invalid graphic token '%s'", value_raw);
1354 Warn("custom graphic rejected for this element/action");
1355 Warn("fallback done to 'char_exclam' for this graphic");
1359 return (value != NULL ? atoi(value) : fallback_graphic);
1365 static int get_scaled_graphic_width(Bitmap *src_bitmap, int graphic)
1367 int original_width = getOriginalImageWidthFromImageID(graphic);
1368 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1370 // only happens when loaded outside artwork system (like "global.busy")
1371 if (graphic_info == image_initial && src_bitmap)
1372 original_width = src_bitmap->width;
1374 return original_width * scale_up_factor;
1377 static int get_scaled_graphic_height(Bitmap *src_bitmap, int graphic)
1379 int original_height = getOriginalImageHeightFromImageID(graphic);
1380 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1382 // only happens when loaded outside artwork system (like "global.busy")
1383 if (graphic_info == image_initial && src_bitmap)
1384 original_height = src_bitmap->height;
1386 return original_height * scale_up_factor;
1389 static void set_graphic_parameters_ext(int graphic, int *parameter,
1390 Bitmap **src_bitmaps)
1392 struct GraphicInfo *g = &graphic_info[graphic];
1393 Bitmap *src_bitmap = (src_bitmaps ? src_bitmaps[IMG_BITMAP_STANDARD] : NULL);
1394 int anim_frames_per_row = 1, anim_frames_per_col = 1;
1395 int anim_frames_per_line = 1;
1397 // always start with reliable default values
1398 g->src_image_width = 0;
1399 g->src_image_height = 0;
1402 g->width = TILEX; // default for element graphics
1403 g->height = TILEY; // default for element graphics
1404 g->offset_x = 0; // one or both of these values ...
1405 g->offset_y = 0; // ... will be corrected later
1406 g->offset2_x = 0; // one or both of these values ...
1407 g->offset2_y = 0; // ... will be corrected later
1408 g->swap_double_tiles = -1; // auto-detect tile swapping
1409 g->crumbled_like = -1; // do not use clone element
1410 g->diggable_like = -1; // do not use clone element
1411 g->border_size = TILEX / 8; // "CRUMBLED" border size
1412 g->scale_up_factor = 1; // default: no scaling up
1413 g->tile_size = TILESIZE; // default: standard tile size
1414 g->clone_from = -1; // do not use clone graphic
1415 g->init_delay_fixed = 0;
1416 g->init_delay_random = 0;
1417 g->init_delay_action = -1;
1418 g->anim_delay_fixed = 0;
1419 g->anim_delay_random = 0;
1420 g->anim_delay_action = -1;
1421 g->post_delay_fixed = 0;
1422 g->post_delay_random = 0;
1423 g->post_delay_action = -1;
1424 g->init_event = ANIM_EVENT_UNDEFINED;
1425 g->anim_event = ANIM_EVENT_UNDEFINED;
1426 g->init_event_action = -1;
1427 g->anim_event_action = -1;
1428 g->draw_masked = FALSE;
1430 g->fade_mode = FADE_MODE_DEFAULT;
1434 g->auto_delay_unit = AUTO_DELAY_UNIT_DEFAULT;
1435 g->align = ALIGN_CENTER; // default for title screens
1436 g->valign = VALIGN_MIDDLE; // default for title screens
1437 g->sort_priority = 0; // default for title screens
1439 g->style = STYLE_DEFAULT;
1442 g->bitmaps = src_bitmaps;
1443 g->bitmap = src_bitmap;
1445 // optional zoom factor for scaling up the image to a larger size
1446 if (parameter[GFX_ARG_SCALE_UP_FACTOR] != ARG_UNDEFINED_VALUE)
1447 g->scale_up_factor = parameter[GFX_ARG_SCALE_UP_FACTOR];
1448 if (g->scale_up_factor < 1)
1449 g->scale_up_factor = 1; // no scaling
1451 // optional tile size for using non-standard image size
1452 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1454 g->tile_size = parameter[GFX_ARG_TILE_SIZE];
1457 // CHECK: should tile sizes less than standard tile size be allowed?
1458 if (g->tile_size < TILESIZE)
1459 g->tile_size = TILESIZE; // standard tile size
1462 // when setting tile size, also set width and height accordingly
1463 g->width = g->tile_size;
1464 g->height = g->tile_size;
1467 if (g->use_image_size)
1469 // set new default bitmap size (with scaling, but without small images)
1470 g->width = get_scaled_graphic_width(src_bitmap, graphic);
1471 g->height = get_scaled_graphic_height(src_bitmap, graphic);
1474 // optional width and height of each animation frame
1475 if (parameter[GFX_ARG_WIDTH] != ARG_UNDEFINED_VALUE)
1476 g->width = parameter[GFX_ARG_WIDTH];
1477 if (parameter[GFX_ARG_HEIGHT] != ARG_UNDEFINED_VALUE)
1478 g->height = parameter[GFX_ARG_HEIGHT];
1480 // optional x and y tile position of animation frame sequence
1481 if (parameter[GFX_ARG_XPOS] != ARG_UNDEFINED_VALUE)
1482 g->src_x = parameter[GFX_ARG_XPOS] * g->width;
1483 if (parameter[GFX_ARG_YPOS] != ARG_UNDEFINED_VALUE)
1484 g->src_y = parameter[GFX_ARG_YPOS] * g->height;
1486 // optional x and y pixel position of animation frame sequence
1487 if (parameter[GFX_ARG_X] != ARG_UNDEFINED_VALUE)
1488 g->src_x = parameter[GFX_ARG_X];
1489 if (parameter[GFX_ARG_Y] != ARG_UNDEFINED_VALUE)
1490 g->src_y = parameter[GFX_ARG_Y];
1497 Warn("invalid value %d for '%s.width' (fallback done to %d)",
1498 g->width, getTokenFromImageID(graphic), TILEX);
1501 g->width = TILEX; // will be checked to be inside bitmap later
1507 Warn("invalid value %d for '%s.height' (fallback done to %d)",
1508 g->height, getTokenFromImageID(graphic), TILEY);
1511 g->height = TILEY; // will be checked to be inside bitmap later
1517 // get final bitmap size (with scaling, but without small images)
1518 int src_image_width = get_scaled_graphic_width(src_bitmap, graphic);
1519 int src_image_height = get_scaled_graphic_height(src_bitmap, graphic);
1521 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1523 anim_frames_per_row = MAX(1, src_image_width / g->tile_size);
1524 anim_frames_per_col = MAX(1, src_image_height / g->tile_size);
1528 anim_frames_per_row = MAX(1, src_image_width / g->width);
1529 anim_frames_per_col = MAX(1, src_image_height / g->height);
1532 g->src_image_width = src_image_width;
1533 g->src_image_height = src_image_height;
1536 // correct x or y offset dependent of vertical or horizontal frame order
1537 if (parameter[GFX_ARG_VERTICAL]) // frames are ordered vertically
1539 g->offset_y = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1540 parameter[GFX_ARG_OFFSET] : g->height);
1541 anim_frames_per_line = anim_frames_per_col;
1543 else // frames are ordered horizontally
1545 g->offset_x = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1546 parameter[GFX_ARG_OFFSET] : g->width);
1547 anim_frames_per_line = anim_frames_per_row;
1550 // optionally, the x and y offset of frames can be specified directly
1551 if (parameter[GFX_ARG_XOFFSET] != ARG_UNDEFINED_VALUE)
1552 g->offset_x = parameter[GFX_ARG_XOFFSET];
1553 if (parameter[GFX_ARG_YOFFSET] != ARG_UNDEFINED_VALUE)
1554 g->offset_y = parameter[GFX_ARG_YOFFSET];
1556 // optionally, moving animations may have separate start and end graphics
1557 g->double_movement = parameter[GFX_ARG_2ND_MOVEMENT_TILE];
1559 if (parameter[GFX_ARG_2ND_VERTICAL] == ARG_UNDEFINED_VALUE)
1560 parameter[GFX_ARG_2ND_VERTICAL] = !parameter[GFX_ARG_VERTICAL];
1562 // correct x or y offset2 dependent of vertical or horizontal frame order
1563 if (parameter[GFX_ARG_2ND_VERTICAL]) // frames are ordered vertically
1564 g->offset2_y = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1565 parameter[GFX_ARG_2ND_OFFSET] : g->height);
1566 else // frames are ordered horizontally
1567 g->offset2_x = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1568 parameter[GFX_ARG_2ND_OFFSET] : g->width);
1570 // optionally, the x and y offset of 2nd graphic can be specified directly
1571 if (parameter[GFX_ARG_2ND_XOFFSET] != ARG_UNDEFINED_VALUE)
1572 g->offset2_x = parameter[GFX_ARG_2ND_XOFFSET];
1573 if (parameter[GFX_ARG_2ND_YOFFSET] != ARG_UNDEFINED_VALUE)
1574 g->offset2_y = parameter[GFX_ARG_2ND_YOFFSET];
1576 // optionally, the second movement tile can be specified as start tile
1577 if (parameter[GFX_ARG_2ND_SWAP_TILES] != ARG_UNDEFINED_VALUE)
1578 g->swap_double_tiles = parameter[GFX_ARG_2ND_SWAP_TILES];
1580 // automatically determine correct number of frames, if not defined
1581 if (parameter[GFX_ARG_FRAMES] != ARG_UNDEFINED_VALUE)
1582 g->anim_frames = parameter[GFX_ARG_FRAMES];
1583 else if (parameter[GFX_ARG_XPOS] == 0 && !parameter[GFX_ARG_VERTICAL])
1584 g->anim_frames = anim_frames_per_row;
1585 else if (parameter[GFX_ARG_YPOS] == 0 && parameter[GFX_ARG_VERTICAL])
1586 g->anim_frames = anim_frames_per_col;
1590 if (g->anim_frames < 1) // frames must be at least 1
1593 g->anim_frames_per_line =
1594 (parameter[GFX_ARG_FRAMES_PER_LINE] != ARG_UNDEFINED_VALUE ?
1595 parameter[GFX_ARG_FRAMES_PER_LINE] : anim_frames_per_line);
1597 g->anim_delay = parameter[GFX_ARG_DELAY];
1598 if (g->anim_delay < 1) // delay must be at least 1
1601 g->anim_mode = parameter[GFX_ARG_ANIM_MODE];
1603 // automatically determine correct start frame, if not defined
1604 if (parameter[GFX_ARG_START_FRAME] == ARG_UNDEFINED_VALUE)
1605 g->anim_start_frame = 0;
1606 else if (g->anim_mode & ANIM_REVERSE)
1607 g->anim_start_frame = g->anim_frames - parameter[GFX_ARG_START_FRAME] - 1;
1609 g->anim_start_frame = parameter[GFX_ARG_START_FRAME];
1611 // animation synchronized with global frame counter, not move position
1612 g->anim_global_sync = parameter[GFX_ARG_GLOBAL_SYNC];
1614 // animation synchronized with global anim frame counter, not move position
1615 g->anim_global_anim_sync = parameter[GFX_ARG_GLOBAL_ANIM_SYNC];
1617 // optional element for cloning crumble graphics
1618 if (parameter[GFX_ARG_CRUMBLED_LIKE] != ARG_UNDEFINED_VALUE)
1619 g->crumbled_like = parameter[GFX_ARG_CRUMBLED_LIKE];
1621 // optional element for cloning digging graphics
1622 if (parameter[GFX_ARG_DIGGABLE_LIKE] != ARG_UNDEFINED_VALUE)
1623 g->diggable_like = parameter[GFX_ARG_DIGGABLE_LIKE];
1625 // optional border size for "crumbling" diggable graphics
1626 if (parameter[GFX_ARG_BORDER_SIZE] != ARG_UNDEFINED_VALUE)
1627 g->border_size = parameter[GFX_ARG_BORDER_SIZE];
1629 // used for global animations and player "boring" and "sleeping" actions
1630 if (parameter[GFX_ARG_INIT_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1631 g->init_delay_fixed = parameter[GFX_ARG_INIT_DELAY_FIXED];
1632 if (parameter[GFX_ARG_INIT_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1633 g->init_delay_random = parameter[GFX_ARG_INIT_DELAY_RANDOM];
1634 if (parameter[GFX_ARG_ANIM_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1635 g->anim_delay_fixed = parameter[GFX_ARG_ANIM_DELAY_FIXED];
1636 if (parameter[GFX_ARG_ANIM_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1637 g->anim_delay_random = parameter[GFX_ARG_ANIM_DELAY_RANDOM];
1638 if (parameter[GFX_ARG_POST_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1639 g->post_delay_fixed = parameter[GFX_ARG_POST_DELAY_FIXED];
1640 if (parameter[GFX_ARG_POST_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1641 g->post_delay_random = parameter[GFX_ARG_POST_DELAY_RANDOM];
1643 // used for global animations
1644 if (parameter[GFX_ARG_INIT_EVENT] != ARG_UNDEFINED_VALUE)
1645 g->init_event = parameter[GFX_ARG_INIT_EVENT];
1646 if (parameter[GFX_ARG_ANIM_EVENT] != ARG_UNDEFINED_VALUE)
1647 g->anim_event = parameter[GFX_ARG_ANIM_EVENT];
1648 if (parameter[GFX_ARG_INIT_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1649 g->init_event_action = parameter[GFX_ARG_INIT_EVENT_ACTION];
1650 if (parameter[GFX_ARG_ANIM_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1651 g->anim_event_action = parameter[GFX_ARG_ANIM_EVENT_ACTION];
1652 if (parameter[GFX_ARG_INIT_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1653 g->init_delay_action = parameter[GFX_ARG_INIT_DELAY_ACTION];
1654 if (parameter[GFX_ARG_ANIM_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1655 g->anim_delay_action = parameter[GFX_ARG_ANIM_DELAY_ACTION];
1656 if (parameter[GFX_ARG_POST_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1657 g->post_delay_action = parameter[GFX_ARG_POST_DELAY_ACTION];
1659 // used for toon animations and global animations
1660 g->step_offset = parameter[GFX_ARG_STEP_OFFSET];
1661 g->step_xoffset = parameter[GFX_ARG_STEP_XOFFSET];
1662 g->step_yoffset = parameter[GFX_ARG_STEP_YOFFSET];
1663 g->step_delay = parameter[GFX_ARG_STEP_DELAY];
1664 g->direction = parameter[GFX_ARG_DIRECTION];
1665 g->position = parameter[GFX_ARG_POSITION];
1666 g->x = parameter[GFX_ARG_X]; // (may be uninitialized,
1667 g->y = parameter[GFX_ARG_Y]; // unlike src_x and src_y)
1669 if (g->step_delay < 1) // delay must be at least 1
1672 // this is only used for drawing font characters
1673 g->draw_xoffset = parameter[GFX_ARG_DRAW_XOFFSET];
1674 g->draw_yoffset = parameter[GFX_ARG_DRAW_YOFFSET];
1676 // use a different default value for global animations and toons
1677 if ((graphic >= IMG_GFX_GLOBAL_ANIM_1 && graphic <= IMG_GFX_GLOBAL_ANIM_32) ||
1678 (graphic >= IMG_TOON_1 && graphic <= IMG_TOON_20))
1679 g->draw_masked = TRUE;
1681 // this is used for drawing envelopes, global animations and toons
1682 if (parameter[GFX_ARG_DRAW_MASKED] != ARG_UNDEFINED_VALUE)
1683 g->draw_masked = parameter[GFX_ARG_DRAW_MASKED];
1685 // used for toon animations and global animations
1686 if (parameter[GFX_ARG_DRAW_ORDER] != ARG_UNDEFINED_VALUE)
1687 g->draw_order = parameter[GFX_ARG_DRAW_ORDER];
1689 // optional graphic for cloning all graphics settings
1690 if (parameter[GFX_ARG_CLONE_FROM] != ARG_UNDEFINED_VALUE)
1691 g->clone_from = parameter[GFX_ARG_CLONE_FROM];
1693 // optional settings for drawing title screens and title messages
1694 if (parameter[GFX_ARG_FADE_MODE] != ARG_UNDEFINED_VALUE)
1695 g->fade_mode = parameter[GFX_ARG_FADE_MODE];
1696 if (parameter[GFX_ARG_FADE_DELAY] != ARG_UNDEFINED_VALUE)
1697 g->fade_delay = parameter[GFX_ARG_FADE_DELAY];
1698 if (parameter[GFX_ARG_POST_DELAY] != ARG_UNDEFINED_VALUE)
1699 g->post_delay = parameter[GFX_ARG_POST_DELAY];
1700 if (parameter[GFX_ARG_AUTO_DELAY] != ARG_UNDEFINED_VALUE)
1701 g->auto_delay = parameter[GFX_ARG_AUTO_DELAY];
1702 if (parameter[GFX_ARG_AUTO_DELAY_UNIT] != ARG_UNDEFINED_VALUE)
1703 g->auto_delay_unit = parameter[GFX_ARG_AUTO_DELAY_UNIT];
1704 if (parameter[GFX_ARG_ALIGN] != ARG_UNDEFINED_VALUE)
1705 g->align = parameter[GFX_ARG_ALIGN];
1706 if (parameter[GFX_ARG_VALIGN] != ARG_UNDEFINED_VALUE)
1707 g->valign = parameter[GFX_ARG_VALIGN];
1708 if (parameter[GFX_ARG_SORT_PRIORITY] != ARG_UNDEFINED_VALUE)
1709 g->sort_priority = parameter[GFX_ARG_SORT_PRIORITY];
1711 if (parameter[GFX_ARG_CLASS] != ARG_UNDEFINED_VALUE)
1712 g->class = parameter[GFX_ARG_CLASS];
1713 if (parameter[GFX_ARG_STYLE] != ARG_UNDEFINED_VALUE)
1714 g->style = parameter[GFX_ARG_STYLE];
1715 if (parameter[GFX_ARG_ALPHA] != ARG_UNDEFINED_VALUE)
1716 g->alpha = parameter[GFX_ARG_ALPHA];
1718 // this is only used for drawing menu buttons and text
1719 g->active_xoffset = parameter[GFX_ARG_ACTIVE_XOFFSET];
1720 g->active_yoffset = parameter[GFX_ARG_ACTIVE_YOFFSET];
1721 g->pressed_xoffset = parameter[GFX_ARG_PRESSED_XOFFSET];
1722 g->pressed_yoffset = parameter[GFX_ARG_PRESSED_YOFFSET];
1724 // this is only used for drawing stacked global animations
1725 g->stacked_xfactor = parameter[GFX_ARG_STACKED_XFACTOR];
1726 g->stacked_yfactor = parameter[GFX_ARG_STACKED_YFACTOR];
1727 g->stacked_xoffset = parameter[GFX_ARG_STACKED_XOFFSET];
1728 g->stacked_yoffset = parameter[GFX_ARG_STACKED_YOFFSET];
1731 static void set_graphic_parameters(int graphic)
1733 struct FileInfo *image = getImageListEntryFromImageID(graphic);
1734 char **parameter_raw = image->parameter;
1735 Bitmap **src_bitmaps = getBitmapsFromImageID(graphic);
1736 int parameter[NUM_GFX_ARGS];
1739 // if fallback to default artwork is done, also use the default parameters
1740 if (image->fallback_to_default)
1741 parameter_raw = image->default_parameter;
1743 // get integer values from string parameters
1744 for (i = 0; i < NUM_GFX_ARGS; i++)
1745 parameter[i] = get_graphic_parameter_value(parameter_raw[i],
1746 image_config_suffix[i].token,
1747 image_config_suffix[i].type);
1749 set_graphic_parameters_ext(graphic, parameter, src_bitmaps);
1751 UPDATE_BUSY_STATE();
1754 static void set_cloned_graphic_parameters(int graphic)
1756 int fallback_graphic = IMG_CHAR_EXCLAM;
1757 int max_num_images = getImageListSize();
1758 int clone_graphic = graphic_info[graphic].clone_from;
1759 int num_references_followed = 1;
1761 while (graphic_info[clone_graphic].clone_from != -1 &&
1762 num_references_followed < max_num_images)
1764 clone_graphic = graphic_info[clone_graphic].clone_from;
1766 num_references_followed++;
1769 if (num_references_followed >= max_num_images)
1772 Warn("error found in config file:");
1773 Warn("- config file: '%s'", getImageConfigFilename());
1774 Warn("- config token: '%s'", getTokenFromImageID(graphic));
1775 Warn("error: loop discovered when resolving cloned graphics");
1776 Warn("custom graphic rejected for this element/action");
1778 if (graphic == fallback_graphic)
1779 Fail("no fallback graphic available");
1781 Warn("fallback done to 'char_exclam' for this graphic");
1784 graphic_info[graphic] = graphic_info[fallback_graphic];
1788 graphic_info[graphic] = graphic_info[clone_graphic];
1789 graphic_info[graphic].clone_from = clone_graphic;
1793 static void InitGraphicInfo(void)
1795 int fallback_graphic = IMG_CHAR_EXCLAM;
1796 int num_images = getImageListSize();
1799 // use image size as default values for width and height for these images
1800 static int full_size_graphics[] =
1803 IMG_GLOBAL_BORDER_MAIN,
1804 IMG_GLOBAL_BORDER_SCORES,
1805 IMG_GLOBAL_BORDER_EDITOR,
1806 IMG_GLOBAL_BORDER_PLAYING,
1809 IMG_BACKGROUND_ENVELOPE_1,
1810 IMG_BACKGROUND_ENVELOPE_2,
1811 IMG_BACKGROUND_ENVELOPE_3,
1812 IMG_BACKGROUND_ENVELOPE_4,
1813 IMG_BACKGROUND_REQUEST,
1816 IMG_BACKGROUND_LOADING_INITIAL,
1817 IMG_BACKGROUND_LOADING,
1818 IMG_BACKGROUND_TITLE_INITIAL,
1819 IMG_BACKGROUND_TITLE,
1820 IMG_BACKGROUND_MAIN,
1821 IMG_BACKGROUND_NAMES,
1822 IMG_BACKGROUND_LEVELS,
1823 IMG_BACKGROUND_LEVELNR,
1824 IMG_BACKGROUND_SCORES,
1825 IMG_BACKGROUND_SCOREINFO,
1826 IMG_BACKGROUND_EDITOR,
1827 IMG_BACKGROUND_INFO,
1828 IMG_BACKGROUND_INFO_ELEMENTS,
1829 IMG_BACKGROUND_INFO_MUSIC,
1830 IMG_BACKGROUND_INFO_CREDITS,
1831 IMG_BACKGROUND_INFO_PROGRAM,
1832 IMG_BACKGROUND_INFO_VERSION,
1833 IMG_BACKGROUND_INFO_LEVELSET,
1834 IMG_BACKGROUND_SETUP,
1835 IMG_BACKGROUND_PLAYING,
1836 IMG_BACKGROUND_DOOR,
1837 IMG_BACKGROUND_TAPE,
1838 IMG_BACKGROUND_PANEL,
1839 IMG_BACKGROUND_PALETTE,
1840 IMG_BACKGROUND_TOOLBOX,
1842 IMG_TITLESCREEN_INITIAL_1,
1843 IMG_TITLESCREEN_INITIAL_2,
1844 IMG_TITLESCREEN_INITIAL_3,
1845 IMG_TITLESCREEN_INITIAL_4,
1846 IMG_TITLESCREEN_INITIAL_5,
1853 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_1,
1854 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_2,
1855 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_3,
1856 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_4,
1857 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_5,
1858 IMG_BACKGROUND_TITLEMESSAGE_1,
1859 IMG_BACKGROUND_TITLEMESSAGE_2,
1860 IMG_BACKGROUND_TITLEMESSAGE_3,
1861 IMG_BACKGROUND_TITLEMESSAGE_4,
1862 IMG_BACKGROUND_TITLEMESSAGE_5,
1867 FreeGlobalAnimEventInfo();
1869 checked_free(graphic_info);
1871 graphic_info = checked_calloc(num_images * sizeof(struct GraphicInfo));
1873 // initialize "use_image_size" flag with default value
1874 for (i = 0; i < num_images; i++)
1875 graphic_info[i].use_image_size = FALSE;
1877 // initialize "use_image_size" flag from static configuration above
1878 for (i = 0; full_size_graphics[i] != -1; i++)
1879 graphic_info[full_size_graphics[i]].use_image_size = TRUE;
1881 // first set all graphic paramaters ...
1882 for (i = 0; i < num_images; i++)
1883 set_graphic_parameters(i);
1885 // ... then copy these parameters for cloned graphics
1886 for (i = 0; i < num_images; i++)
1887 if (graphic_info[i].clone_from != -1)
1888 set_cloned_graphic_parameters(i);
1890 for (i = 0; i < num_images; i++)
1892 Bitmap *src_bitmap = graphic_info[i].bitmap;
1896 int src_bitmap_width, src_bitmap_height;
1898 // now check if no animation frames are outside of the loaded image
1900 if (graphic_info[i].bitmap == NULL)
1901 continue; // skip check for optional images that are undefined
1903 // get image size (this can differ from the standard element tile size!)
1904 width = graphic_info[i].width;
1905 height = graphic_info[i].height;
1907 // get final bitmap size (with scaling, but without small images)
1908 src_bitmap_width = graphic_info[i].src_image_width;
1909 src_bitmap_height = graphic_info[i].src_image_height;
1911 // check if first animation frame is inside specified bitmap
1913 // do not use getGraphicSourceXY() here to get position of first frame;
1914 // this avoids calculating wrong start position for out-of-bounds frame
1915 src_x = graphic_info[i].src_x;
1916 src_y = graphic_info[i].src_y;
1918 if (program.headless)
1921 if (src_x < 0 || src_y < 0 ||
1922 src_x + width > src_bitmap_width ||
1923 src_y + height > src_bitmap_height)
1926 Warn("error found in config file:");
1927 Warn("- config file: '%s'", getImageConfigFilename());
1928 Warn("- config token: '%s'", getTokenFromImageID(i));
1929 Warn("- image file: '%s'", src_bitmap->source_filename);
1930 Warn("- frame size: %d, %d", width, height);
1931 Warn("error: first animation frame out of bounds (%d, %d) [%d, %d]",
1932 src_x, src_y, src_bitmap_width, src_bitmap_height);
1933 Warn("custom graphic rejected for this element/action");
1935 if (i == fallback_graphic)
1936 Fail("no fallback graphic available");
1938 Warn("fallback done to 'char_exclam' for this graphic");
1941 graphic_info[i] = graphic_info[fallback_graphic];
1943 // if first frame out of bounds, do not check last frame anymore
1947 // check if last animation frame is inside specified bitmap
1949 last_frame = graphic_info[i].anim_frames - 1;
1950 getGraphicSourceXY(i, last_frame, &src_x, &src_y, FALSE);
1952 if (src_x < 0 || src_y < 0 ||
1953 src_x + width > src_bitmap_width ||
1954 src_y + height > src_bitmap_height)
1957 Warn("error found in config file:");
1958 Warn("- config file: '%s'", getImageConfigFilename());
1959 Warn("- config token: '%s'", getTokenFromImageID(i));
1960 Warn("- image file: '%s'", src_bitmap->source_filename);
1961 Warn("- frame size: %d, %d", width, height);
1962 Warn("error: last animation frame (%d) out of bounds (%d, %d) [%d, %d]",
1963 last_frame, src_x, src_y, src_bitmap_width, src_bitmap_height);
1964 Warn("custom graphic rejected for this element/action");
1966 if (i == fallback_graphic)
1967 Fail("no fallback graphic available");
1969 Warn("fallback done to 'char_exclam' for this graphic");
1972 graphic_info[i] = graphic_info[fallback_graphic];
1977 static void InitGraphicCompatibilityInfo(void)
1979 struct FileInfo *fi_global_door =
1980 getImageListEntryFromImageID(IMG_GLOBAL_DOOR);
1981 int num_images = getImageListSize();
1984 /* the following compatibility handling is needed for the following case:
1985 versions up to 3.3.0.0 used one large bitmap "global.door" for various
1986 graphics mainly used for door and panel graphics, like editor, tape and
1987 in-game buttons with hard-coded bitmap positions and button sizes; as
1988 these graphics now have individual definitions, redefining "global.door"
1989 to change all these graphics at once like before does not work anymore
1990 (because all those individual definitions still have their default values);
1991 to solve this, remap all those individual definitions that are not
1992 redefined to the new bitmap of "global.door" if it was redefined */
1994 // special compatibility handling if image "global.door" was redefined
1995 if (fi_global_door->redefined)
1997 for (i = 0; i < num_images; i++)
1999 struct FileInfo *fi = getImageListEntryFromImageID(i);
2001 // process only those images that still use the default settings
2004 // process all images which default to same image as "global.door"
2005 if (strEqual(fi->default_filename, fi_global_door->default_filename))
2007 // skip all images that are cloned from images that default to same
2008 // image as "global.door", but that are redefined to something else
2009 if (graphic_info[i].clone_from != -1)
2011 int cloned_graphic = graphic_info[i].clone_from;
2013 if (getImageListEntryFromImageID(cloned_graphic)->redefined)
2018 Debug("init:InitGraphicCompatibilityInfo",
2019 "special treatment needed for token '%s'", fi->token);
2022 graphic_info[i].bitmaps = graphic_info[IMG_GLOBAL_DOOR].bitmaps;
2023 graphic_info[i].bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2029 // special compatibility handling for "Snake Bite" graphics set
2030 if (strPrefix(leveldir_current->identifier, "snake_bite"))
2032 Bitmap *bitmap = graphic_info[IMG_BACKGROUND_SCORES].bitmap;
2034 BlitBitmap(bitmap, bitmap, 18, 66, 32, 480, 50, 66);
2035 BlitBitmap(bitmap, bitmap, 466, 66, 32, 480, 434, 66);
2037 ClearRectangle(bitmap, 2, 66, 32, 480);
2038 ClearRectangle(bitmap, 514, 66, 32, 480);
2041 // special compatibility handling for "Jue" graphics sets (2007 and 2019)
2042 boolean supports_score_info = (menu.draw_xoffset[GAME_MODE_SCOREINFO] != 0);
2043 if (strPrefix(artwork.gfx_current_identifier, "jue") && !supports_score_info)
2061 int mode_old = GAME_MODE_SCORES;
2062 int mode_new = GAME_MODE_SCOREINFO;
2065 // adjust title screens on score info page
2066 for (i = 0; font_title[i] != -1; i++)
2068 struct FontInfo *fi = &font_info[font_title[i]];
2070 fi->special_graphic[mode_new] = fi->special_graphic[mode_old];
2071 fi->special_bitmap_id[mode_new] = fi->special_bitmap_id[mode_old];
2074 // adjust vertical text and button positions on scores page
2075 for (i = 0; font_text[i] != -1; i++)
2077 for (j = 0; j < 2; j++)
2079 boolean jue0 = strEqual(artwork.gfx_current_identifier, "jue0");
2080 int font_nr = (j == 0 ? font_text[i] : FONT_ACTIVE(font_text[i]));
2081 int font_bitmap_id = font_info[font_nr].special_bitmap_id[mode_old];
2082 int font_yoffset = (jue0 ? 10 : 5);
2084 gfx.font_bitmap_info[font_bitmap_id].draw_yoffset = font_yoffset;
2088 // adjust page offsets on score info page
2089 menu.draw_xoffset[mode_new] = menu.draw_xoffset[mode_old];
2090 menu.draw_yoffset[mode_new] = menu.draw_yoffset[mode_old];
2093 InitGraphicCompatibilityInfo_Doors();
2096 static void InitElementSoundInfo(void)
2098 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
2099 int num_property_mappings = getSoundListPropertyMappingSize();
2102 // set values to -1 to identify later as "uninitialized" values
2103 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2104 for (act = 0; act < NUM_ACTIONS; act++)
2105 element_info[i].sound[act] = -1;
2107 // initialize element/sound mapping from static configuration
2108 for (i = 0; element_to_sound[i].element > -1; i++)
2110 int element = element_to_sound[i].element;
2111 int action = element_to_sound[i].action;
2112 int sound = element_to_sound[i].sound;
2113 boolean is_class = element_to_sound[i].is_class;
2116 action = ACTION_DEFAULT;
2119 element_info[element].sound[action] = sound;
2121 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2122 if (strEqual(element_info[j].class_name,
2123 element_info[element].class_name))
2124 element_info[j].sound[action] = sound;
2127 // initialize element class/sound mapping from dynamic configuration
2128 for (i = 0; i < num_property_mappings; i++)
2130 int element_class = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
2131 int action = property_mapping[i].ext1_index;
2132 int sound = property_mapping[i].artwork_index;
2134 if (element_class < 0 || element_class >= MAX_NUM_ELEMENTS)
2138 action = ACTION_DEFAULT;
2140 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2141 if (strEqual(element_info[j].class_name,
2142 element_info[element_class].class_name))
2143 element_info[j].sound[action] = sound;
2146 // initialize element/sound mapping from dynamic configuration
2147 for (i = 0; i < num_property_mappings; i++)
2149 int element = property_mapping[i].base_index;
2150 int action = property_mapping[i].ext1_index;
2151 int sound = property_mapping[i].artwork_index;
2153 if (element >= MAX_NUM_ELEMENTS)
2157 action = ACTION_DEFAULT;
2159 element_info[element].sound[action] = sound;
2162 // now set all '-1' values to element specific default values
2163 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2165 for (act = 0; act < NUM_ACTIONS; act++)
2167 // generic default action sound (defined by "[default]" directive)
2168 int default_action_sound = element_info[EL_DEFAULT].sound[act];
2170 // look for special default action sound (classic game specific)
2171 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].sound[act] != -1)
2172 default_action_sound = element_info[EL_BD_DEFAULT].sound[act];
2173 if (IS_BDX_ELEMENT(i) && element_info[EL_BDX_DEFAULT].sound[act] != -1)
2174 default_action_sound = element_info[EL_BDX_DEFAULT].sound[act];
2175 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].sound[act] != -1)
2176 default_action_sound = element_info[EL_SP_DEFAULT].sound[act];
2177 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].sound[act] != -1)
2178 default_action_sound = element_info[EL_SB_DEFAULT].sound[act];
2179 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].sound[act] != -1)
2180 default_action_sound = element_info[EL_MM_DEFAULT].sound[act];
2182 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
2183 // !!! make this better !!!
2184 if (i == EL_EMPTY_SPACE)
2185 default_action_sound = element_info[EL_DEFAULT].sound[act];
2187 // no sound for this specific action -- use default action sound
2188 if (element_info[i].sound[act] == -1)
2189 element_info[i].sound[act] = default_action_sound;
2193 // copy sound settings to some elements that are only stored in level file
2194 // in native R'n'D levels, but are used by game engine in native EM levels
2195 for (i = 0; copy_properties[i][0] != -1; i++)
2196 for (j = 1; j <= 4; j++)
2197 for (act = 0; act < NUM_ACTIONS; act++)
2198 element_info[copy_properties[i][j]].sound[act] =
2199 element_info[copy_properties[i][0]].sound[act];
2202 static void InitGameModeSoundInfo(void)
2206 // set values to -1 to identify later as "uninitialized" values
2207 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2210 // initialize gamemode/sound mapping from static configuration
2211 for (i = 0; gamemode_to_sound[i].sound > -1; i++)
2213 int gamemode = gamemode_to_sound[i].gamemode;
2214 int sound = gamemode_to_sound[i].sound;
2217 gamemode = GAME_MODE_DEFAULT;
2219 menu.sound[gamemode] = sound;
2222 // now set all '-1' values to levelset specific default values
2223 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2224 if (menu.sound[i] == -1)
2225 menu.sound[i] = menu.sound[GAME_MODE_DEFAULT];
2228 static void set_sound_parameters(int sound, char **parameter_raw)
2230 int parameter[NUM_SND_ARGS];
2233 // get integer values from string parameters
2234 for (i = 0; i < NUM_SND_ARGS; i++)
2236 get_parameter_value(parameter_raw[i],
2237 sound_config_suffix[i].token,
2238 sound_config_suffix[i].type);
2240 // explicit loop mode setting in configuration overrides default value
2241 if (parameter[SND_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2242 sound_info[sound].loop = parameter[SND_ARG_MODE_LOOP];
2244 // sound volume to change the original volume when loading the sound file
2245 sound_info[sound].volume = parameter[SND_ARG_VOLUME];
2247 // sound priority to give certain sounds a higher or lower priority
2248 sound_info[sound].priority = parameter[SND_ARG_PRIORITY];
2251 static void InitSoundInfo(void)
2253 int *sound_effect_properties;
2254 int num_sounds = getSoundListSize();
2257 checked_free(sound_info);
2259 sound_effect_properties = checked_calloc(num_sounds * sizeof(int));
2260 sound_info = checked_calloc(num_sounds * sizeof(struct SoundInfo));
2262 // initialize sound effect for all elements to "no sound"
2263 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2264 for (j = 0; j < NUM_ACTIONS; j++)
2265 element_info[i].sound[j] = SND_UNDEFINED;
2267 for (i = 0; i < num_sounds; i++)
2269 struct FileInfo *sound = getSoundListEntry(i);
2270 int len_effect_text = strlen(sound->token);
2272 sound_effect_properties[i] = ACTION_OTHER;
2273 sound_info[i].loop = FALSE; // default: play sound only once
2275 // determine all loop sounds and identify certain sound classes
2277 for (j = 0; element_action_info[j].suffix; j++)
2279 int len_action_text = strlen(element_action_info[j].suffix);
2281 if (len_action_text < len_effect_text &&
2282 strEqual(&sound->token[len_effect_text - len_action_text],
2283 element_action_info[j].suffix))
2285 sound_effect_properties[i] = element_action_info[j].value;
2286 sound_info[i].loop = element_action_info[j].is_loop_sound;
2292 // associate elements and some selected sound actions
2294 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2296 if (element_info[j].class_name)
2298 int len_class_text = strlen(element_info[j].class_name);
2300 if (len_class_text + 1 < len_effect_text &&
2301 strncmp(sound->token,
2302 element_info[j].class_name, len_class_text) == 0 &&
2303 sound->token[len_class_text] == '.')
2305 int sound_action_value = sound_effect_properties[i];
2307 element_info[j].sound[sound_action_value] = i;
2312 set_sound_parameters(i, sound->parameter);
2315 Debug("init:InitSoundInfo", "loop mode: %d ['%s']",
2316 sound_info[i].loop, sound->token);
2320 free(sound_effect_properties);
2323 static void InitGameModeMusicInfo(void)
2325 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
2326 int num_property_mappings = getMusicListPropertyMappingSize();
2327 int default_levelset_music = -1;
2330 // set values to -1 to identify later as "uninitialized" values
2331 for (i = 0; i < MAX_LEVELS; i++)
2332 levelset.music[i] = -1;
2333 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2336 // initialize gamemode/music mapping from static configuration
2337 for (i = 0; gamemode_to_music[i].music > -1; i++)
2339 int gamemode = gamemode_to_music[i].gamemode;
2340 int music = gamemode_to_music[i].music;
2343 gamemode = GAME_MODE_DEFAULT;
2345 menu.music[gamemode] = music;
2348 // initialize gamemode/music mapping from dynamic configuration
2349 for (i = 0; i < num_property_mappings; i++)
2351 int prefix = property_mapping[i].base_index;
2352 int gamemode = property_mapping[i].ext2_index;
2353 int level = property_mapping[i].ext3_index;
2354 int music = property_mapping[i].artwork_index;
2356 if (prefix < 0 || prefix >= NUM_MUSIC_PREFIXES)
2360 gamemode = GAME_MODE_DEFAULT;
2362 // level specific music only allowed for in-game music
2363 if (level != -1 && gamemode == GAME_MODE_DEFAULT)
2364 gamemode = GAME_MODE_PLAYING;
2369 default_levelset_music = music;
2372 if (gamemode == GAME_MODE_PLAYING || gamemode == GAME_MODE_DEFAULT)
2373 levelset.music[level] = music;
2374 if (gamemode != GAME_MODE_PLAYING)
2375 menu.music[gamemode] = music;
2378 // now set all '-1' values to menu specific default values
2379 // (undefined values of "levelset.music[]" might stay at "-1" to
2380 // allow dynamic selection of music files from music directory!)
2381 for (i = 0; i < MAX_LEVELS; i++)
2382 if (levelset.music[i] == -1)
2383 levelset.music[i] = default_levelset_music;
2384 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2385 if (menu.music[i] == -1)
2386 menu.music[i] = menu.music[GAME_MODE_DEFAULT];
2389 static void set_music_parameters(int music, char **parameter_raw)
2391 int parameter[NUM_MUS_ARGS];
2394 // get integer values from string parameters
2395 for (i = 0; i < NUM_MUS_ARGS; i++)
2397 get_parameter_value(parameter_raw[i],
2398 music_config_suffix[i].token,
2399 music_config_suffix[i].type);
2401 // explicit loop mode setting in configuration overrides default value
2402 if (parameter[MUS_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2403 music_info[music].loop = parameter[MUS_ARG_MODE_LOOP];
2406 static void InitMusicInfo(void)
2408 int num_music = getMusicListSize();
2411 checked_free(music_info);
2413 music_info = checked_calloc(num_music * sizeof(struct MusicInfo));
2415 for (i = 0; i < num_music; i++)
2417 struct FileInfo *music = getMusicListEntry(i);
2418 int len_music_text = strlen(music->token);
2420 music_info[i].loop = TRUE; // default: play music in loop mode
2422 // determine all loop music
2424 for (j = 0; music_prefix_info[j].prefix; j++)
2426 int len_prefix_text = strlen(music_prefix_info[j].prefix);
2428 if (len_prefix_text < len_music_text &&
2429 strncmp(music->token,
2430 music_prefix_info[j].prefix, len_prefix_text) == 0)
2432 music_info[i].loop = music_prefix_info[j].is_loop_music;
2438 set_music_parameters(i, music->parameter);
2443 static void InitGameInfoFromArtworkInfo(void)
2445 // special case: store initial value of custom artwork setting
2446 game.use_masked_elements_initial = game.use_masked_elements;
2449 static void ReinitializeGraphics(void)
2451 print_timestamp_init("ReinitializeGraphics");
2453 InitGfxTileSizeInfo(game.tile_size, TILESIZE);
2455 InitGraphicInfo(); // graphic properties mapping
2456 print_timestamp_time("InitGraphicInfo");
2457 InitElementGraphicInfo(); // element game graphic mapping
2458 print_timestamp_time("InitElementGraphicInfo");
2459 InitElementSpecialGraphicInfo(); // element special graphic mapping
2460 print_timestamp_time("InitElementSpecialGraphicInfo");
2462 InitElementSmallImages(); // scale elements to all needed sizes
2463 print_timestamp_time("InitElementSmallImages");
2464 InitScaledImages(); // scale all other images, if needed
2465 print_timestamp_time("InitScaledImages");
2466 InitBitmapPointers(); // set standard size bitmap pointers
2467 print_timestamp_time("InitBitmapPointers");
2468 InitFontGraphicInfo(); // initialize text drawing functions
2469 print_timestamp_time("InitFontGraphicInfo");
2470 InitGlobalAnimGraphicInfo(); // initialize global animation config
2471 print_timestamp_time("InitGlobalAnimGraphicInfo");
2473 InitImageTextures(); // create textures for certain images
2474 print_timestamp_time("InitImageTextures");
2476 InitGraphicInfo_BD(); // graphic mapping for BD engine
2477 print_timestamp_time("InitGraphicInfo_BD");
2478 InitGraphicInfo_EM(); // graphic mapping for EM engine
2479 print_timestamp_time("InitGraphicInfo_EM");
2481 InitGraphicCompatibilityInfo();
2482 print_timestamp_time("InitGraphicCompatibilityInfo");
2485 print_timestamp_time("InitGadgets");
2487 print_timestamp_time("InitDoors");
2489 InitGameInfoFromArtworkInfo();
2491 print_timestamp_done("ReinitializeGraphics");
2494 static void ReinitializeSounds(void)
2496 InitSoundInfo(); // sound properties mapping
2497 InitElementSoundInfo(); // element game sound mapping
2498 InitGameModeSoundInfo(); // game mode sound mapping
2499 InitGlobalAnimSoundInfo(); // global animation sound settings
2501 InitPlayLevelSound(); // internal game sound settings
2504 static void ReinitializeMusic(void)
2506 InitMusicInfo(); // music properties mapping
2507 InitGameModeMusicInfo(); // game mode music mapping
2508 InitGlobalAnimMusicInfo(); // global animation music settings
2511 static int get_special_property_bit(int element, int property_bit_nr)
2513 struct PropertyBitInfo
2519 static struct PropertyBitInfo pb_can_move_into_acid[] =
2521 // the player may be able fall into acid when gravity is activated
2526 { EL_SP_MURPHY, 0 },
2527 { EL_SOKOBAN_FIELD_PLAYER, 0 },
2529 // all elements that can move may be able to also move into acid
2532 { EL_BUG_RIGHT, 1 },
2535 { EL_SPACESHIP, 2 },
2536 { EL_SPACESHIP_LEFT, 2 },
2537 { EL_SPACESHIP_RIGHT, 2 },
2538 { EL_SPACESHIP_UP, 2 },
2539 { EL_SPACESHIP_DOWN, 2 },
2540 { EL_BD_BUTTERFLY, 3 },
2541 { EL_BD_BUTTERFLY_LEFT, 3 },
2542 { EL_BD_BUTTERFLY_RIGHT, 3 },
2543 { EL_BD_BUTTERFLY_UP, 3 },
2544 { EL_BD_BUTTERFLY_DOWN, 3 },
2545 { EL_BD_FIREFLY, 4 },
2546 { EL_BD_FIREFLY_LEFT, 4 },
2547 { EL_BD_FIREFLY_RIGHT, 4 },
2548 { EL_BD_FIREFLY_UP, 4 },
2549 { EL_BD_FIREFLY_DOWN, 4 },
2551 { EL_YAMYAM_LEFT, 5 },
2552 { EL_YAMYAM_RIGHT, 5 },
2553 { EL_YAMYAM_UP, 5 },
2554 { EL_YAMYAM_DOWN, 5 },
2555 { EL_DARK_YAMYAM, 6 },
2558 { EL_PACMAN_LEFT, 8 },
2559 { EL_PACMAN_RIGHT, 8 },
2560 { EL_PACMAN_UP, 8 },
2561 { EL_PACMAN_DOWN, 8 },
2563 { EL_MOLE_LEFT, 9 },
2564 { EL_MOLE_RIGHT, 9 },
2566 { EL_MOLE_DOWN, 9 },
2570 { EL_SATELLITE, 13 },
2571 { EL_SP_SNIKSNAK, 14 },
2572 { EL_SP_ELECTRON, 15 },
2575 { EL_SPRING_LEFT, 17 },
2576 { EL_SPRING_RIGHT, 17 },
2577 { EL_EMC_ANDROID, 18 },
2582 static struct PropertyBitInfo pb_dont_collide_with[] =
2584 { EL_SP_SNIKSNAK, 0 },
2585 { EL_SP_ELECTRON, 1 },
2593 struct PropertyBitInfo *pb_info;
2596 { EP_CAN_MOVE_INTO_ACID, pb_can_move_into_acid },
2597 { EP_DONT_COLLIDE_WITH, pb_dont_collide_with },
2602 struct PropertyBitInfo *pb_info = NULL;
2605 for (i = 0; pb_definition[i].bit_nr != -1; i++)
2606 if (pb_definition[i].bit_nr == property_bit_nr)
2607 pb_info = pb_definition[i].pb_info;
2609 if (pb_info == NULL)
2612 for (i = 0; pb_info[i].element != -1; i++)
2613 if (pb_info[i].element == element)
2614 return pb_info[i].bit_nr;
2619 void setBitfieldProperty(int *bitfield, int property_bit_nr, int element,
2620 boolean property_value)
2622 int bit_nr = get_special_property_bit(element, property_bit_nr);
2627 *bitfield |= (1 << bit_nr);
2629 *bitfield &= ~(1 << bit_nr);
2633 boolean getBitfieldProperty(int *bitfield, int property_bit_nr, int element)
2635 int bit_nr = get_special_property_bit(element, property_bit_nr);
2638 return ((*bitfield & (1 << bit_nr)) != 0);
2643 static void ResolveGroupElementExt(int group_element, int recursion_depth)
2645 static int group_nr;
2646 static struct ElementGroupInfo *group;
2647 struct ElementGroupInfo *actual_group = element_info[group_element].group;
2650 if (actual_group == NULL) // not yet initialized
2653 if (recursion_depth > NUM_GROUP_ELEMENTS) // recursion too deep
2655 Warn("recursion too deep when resolving group element %d",
2656 group_element - EL_GROUP_START + 1);
2658 // replace element which caused too deep recursion by question mark
2659 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
2664 if (recursion_depth == 0) // initialization
2666 group = actual_group;
2667 group_nr = GROUP_NR(group_element);
2669 group->num_elements_resolved = 0;
2670 group->choice_pos = 0;
2672 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2673 element_info[i].in_group[group_nr] = FALSE;
2676 for (i = 0; i < actual_group->num_elements; i++)
2678 int element = actual_group->element[i];
2680 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
2683 if (IS_GROUP_ELEMENT(element))
2684 ResolveGroupElementExt(element, recursion_depth + 1);
2687 group->element_resolved[group->num_elements_resolved++] = element;
2688 element_info[element].in_group[group_nr] = TRUE;
2693 void ResolveGroupElement(int group_element)
2695 ResolveGroupElementExt(group_element, 0);
2698 void InitElementPropertiesStatic(void)
2700 static boolean clipboard_elements_initialized = FALSE;
2702 static int ep_diggable[] =
2707 EL_SP_BUGGY_BASE_ACTIVATING,
2710 EL_INVISIBLE_SAND_ACTIVE,
2713 // !!! currently not diggable, but handled by 'ep_dont_run_into' !!!
2714 // (if amoeba can grow into anything diggable, maybe keep these out)
2719 EL_SP_BUGGY_BASE_ACTIVE,
2726 static int ep_collectible_only[] =
2748 EL_DYNABOMB_INCREASE_NUMBER,
2749 EL_DYNABOMB_INCREASE_SIZE,
2750 EL_DYNABOMB_INCREASE_POWER,
2768 // !!! handle separately !!!
2769 EL_DC_LANDMINE, // deadly when running into, but can be snapped
2775 static int ep_dont_run_into[] =
2777 // same elements as in 'ep_dont_touch'
2783 // same elements as in 'ep_dont_collide_with'
2795 // !!! maybe this should better be handled by 'ep_diggable' !!!
2800 EL_SP_BUGGY_BASE_ACTIVE,
2807 static int ep_dont_collide_with[] =
2809 // same elements as in 'ep_dont_touch'
2826 static int ep_dont_touch[] =
2836 static int ep_indestructible[] =
2840 EL_ACID_POOL_TOPLEFT,
2841 EL_ACID_POOL_TOPRIGHT,
2842 EL_ACID_POOL_BOTTOMLEFT,
2843 EL_ACID_POOL_BOTTOM,
2844 EL_ACID_POOL_BOTTOMRIGHT,
2845 EL_SP_HARDWARE_GRAY,
2846 EL_SP_HARDWARE_GREEN,
2847 EL_SP_HARDWARE_BLUE,
2849 EL_SP_HARDWARE_YELLOW,
2850 EL_SP_HARDWARE_BASE_1,
2851 EL_SP_HARDWARE_BASE_2,
2852 EL_SP_HARDWARE_BASE_3,
2853 EL_SP_HARDWARE_BASE_4,
2854 EL_SP_HARDWARE_BASE_5,
2855 EL_SP_HARDWARE_BASE_6,
2856 EL_INVISIBLE_STEELWALL,
2857 EL_INVISIBLE_STEELWALL_ACTIVE,
2858 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2859 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
2860 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
2861 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2862 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
2863 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
2864 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2865 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
2866 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
2867 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
2868 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
2869 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
2871 EL_LIGHT_SWITCH_ACTIVE,
2872 EL_SIGN_EXCLAMATION,
2873 EL_SIGN_RADIOACTIVITY,
2880 EL_SIGN_ENTRY_FORBIDDEN,
2881 EL_SIGN_EMERGENCY_EXIT,
2889 EL_STEEL_EXIT_CLOSED,
2891 EL_STEEL_EXIT_OPENING,
2892 EL_STEEL_EXIT_CLOSING,
2893 EL_EM_STEEL_EXIT_CLOSED,
2894 EL_EM_STEEL_EXIT_OPEN,
2895 EL_EM_STEEL_EXIT_OPENING,
2896 EL_EM_STEEL_EXIT_CLOSING,
2897 EL_DC_STEELWALL_1_LEFT,
2898 EL_DC_STEELWALL_1_RIGHT,
2899 EL_DC_STEELWALL_1_TOP,
2900 EL_DC_STEELWALL_1_BOTTOM,
2901 EL_DC_STEELWALL_1_HORIZONTAL,
2902 EL_DC_STEELWALL_1_VERTICAL,
2903 EL_DC_STEELWALL_1_TOPLEFT,
2904 EL_DC_STEELWALL_1_TOPRIGHT,
2905 EL_DC_STEELWALL_1_BOTTOMLEFT,
2906 EL_DC_STEELWALL_1_BOTTOMRIGHT,
2907 EL_DC_STEELWALL_1_TOPLEFT_2,
2908 EL_DC_STEELWALL_1_TOPRIGHT_2,
2909 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
2910 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
2911 EL_DC_STEELWALL_2_LEFT,
2912 EL_DC_STEELWALL_2_RIGHT,
2913 EL_DC_STEELWALL_2_TOP,
2914 EL_DC_STEELWALL_2_BOTTOM,
2915 EL_DC_STEELWALL_2_HORIZONTAL,
2916 EL_DC_STEELWALL_2_VERTICAL,
2917 EL_DC_STEELWALL_2_MIDDLE,
2918 EL_DC_STEELWALL_2_SINGLE,
2919 EL_STEELWALL_SLIPPERY,
2933 EL_GATE_1_GRAY_ACTIVE,
2934 EL_GATE_2_GRAY_ACTIVE,
2935 EL_GATE_3_GRAY_ACTIVE,
2936 EL_GATE_4_GRAY_ACTIVE,
2945 EL_EM_GATE_1_GRAY_ACTIVE,
2946 EL_EM_GATE_2_GRAY_ACTIVE,
2947 EL_EM_GATE_3_GRAY_ACTIVE,
2948 EL_EM_GATE_4_GRAY_ACTIVE,
2957 EL_EMC_GATE_5_GRAY_ACTIVE,
2958 EL_EMC_GATE_6_GRAY_ACTIVE,
2959 EL_EMC_GATE_7_GRAY_ACTIVE,
2960 EL_EMC_GATE_8_GRAY_ACTIVE,
2962 EL_DC_GATE_WHITE_GRAY,
2963 EL_DC_GATE_WHITE_GRAY_ACTIVE,
2964 EL_DC_GATE_FAKE_GRAY,
2966 EL_SWITCHGATE_OPENING,
2967 EL_SWITCHGATE_CLOSED,
2968 EL_SWITCHGATE_CLOSING,
2969 EL_DC_SWITCHGATE_SWITCH_UP,
2970 EL_DC_SWITCHGATE_SWITCH_DOWN,
2972 EL_TIMEGATE_OPENING,
2974 EL_TIMEGATE_CLOSING,
2975 EL_DC_TIMEGATE_SWITCH,
2976 EL_DC_TIMEGATE_SWITCH_ACTIVE,
2980 EL_TUBE_VERTICAL_LEFT,
2981 EL_TUBE_VERTICAL_RIGHT,
2982 EL_TUBE_HORIZONTAL_UP,
2983 EL_TUBE_HORIZONTAL_DOWN,
2988 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
2989 EL_EXPANDABLE_STEELWALL_VERTICAL,
2990 EL_EXPANDABLE_STEELWALL_ANY,
2995 static int ep_slippery[] =
3009 EL_ROBOT_WHEEL_ACTIVE,
3015 EL_ACID_POOL_TOPLEFT,
3016 EL_ACID_POOL_TOPRIGHT,
3026 EL_STEELWALL_SLIPPERY,
3029 EL_EMC_WALL_SLIPPERY_1,
3030 EL_EMC_WALL_SLIPPERY_2,
3031 EL_EMC_WALL_SLIPPERY_3,
3032 EL_EMC_WALL_SLIPPERY_4,
3034 EL_EMC_MAGIC_BALL_ACTIVE,
3039 static int ep_can_change[] =
3044 static int ep_can_move[] =
3046 // same elements as in 'pb_can_move_into_acid'
3069 static int ep_can_fall[] =
3084 EL_QUICKSAND_FAST_FULL,
3086 EL_BD_MAGIC_WALL_FULL,
3087 EL_DC_MAGIC_WALL_FULL,
3101 static int ep_can_smash_player[] =
3127 static int ep_can_smash_enemies[] =
3136 static int ep_can_smash_everything[] =
3145 static int ep_explodes_by_fire[] =
3147 // same elements as in 'ep_explodes_impact'
3152 // same elements as in 'ep_explodes_smashed'
3162 EL_EM_DYNAMITE_ACTIVE,
3163 EL_DYNABOMB_PLAYER_1_ACTIVE,
3164 EL_DYNABOMB_PLAYER_2_ACTIVE,
3165 EL_DYNABOMB_PLAYER_3_ACTIVE,
3166 EL_DYNABOMB_PLAYER_4_ACTIVE,
3167 EL_DYNABOMB_INCREASE_NUMBER,
3168 EL_DYNABOMB_INCREASE_SIZE,
3169 EL_DYNABOMB_INCREASE_POWER,
3170 EL_SP_DISK_RED_ACTIVE,
3184 static int ep_explodes_smashed[] =
3186 // same elements as in 'ep_explodes_impact'
3200 static int ep_explodes_impact[] =
3209 static int ep_walkable_over[] =
3229 EL_SOKOBAN_FIELD_EMPTY,
3236 EL_EM_STEEL_EXIT_OPEN,
3237 EL_EM_STEEL_EXIT_OPENING,
3246 EL_GATE_1_GRAY_ACTIVE,
3247 EL_GATE_2_GRAY_ACTIVE,
3248 EL_GATE_3_GRAY_ACTIVE,
3249 EL_GATE_4_GRAY_ACTIVE,
3257 static int ep_walkable_inside[] =
3262 EL_TUBE_VERTICAL_LEFT,
3263 EL_TUBE_VERTICAL_RIGHT,
3264 EL_TUBE_HORIZONTAL_UP,
3265 EL_TUBE_HORIZONTAL_DOWN,
3274 static int ep_walkable_under[] =
3279 static int ep_passable_over[] =
3289 EL_EM_GATE_1_GRAY_ACTIVE,
3290 EL_EM_GATE_2_GRAY_ACTIVE,
3291 EL_EM_GATE_3_GRAY_ACTIVE,
3292 EL_EM_GATE_4_GRAY_ACTIVE,
3301 EL_EMC_GATE_5_GRAY_ACTIVE,
3302 EL_EMC_GATE_6_GRAY_ACTIVE,
3303 EL_EMC_GATE_7_GRAY_ACTIVE,
3304 EL_EMC_GATE_8_GRAY_ACTIVE,
3306 EL_DC_GATE_WHITE_GRAY,
3307 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3314 static int ep_passable_inside[] =
3320 EL_SP_PORT_HORIZONTAL,
3321 EL_SP_PORT_VERTICAL,
3323 EL_SP_GRAVITY_PORT_LEFT,
3324 EL_SP_GRAVITY_PORT_RIGHT,
3325 EL_SP_GRAVITY_PORT_UP,
3326 EL_SP_GRAVITY_PORT_DOWN,
3327 EL_SP_GRAVITY_ON_PORT_LEFT,
3328 EL_SP_GRAVITY_ON_PORT_RIGHT,
3329 EL_SP_GRAVITY_ON_PORT_UP,
3330 EL_SP_GRAVITY_ON_PORT_DOWN,
3331 EL_SP_GRAVITY_OFF_PORT_LEFT,
3332 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3333 EL_SP_GRAVITY_OFF_PORT_UP,
3334 EL_SP_GRAVITY_OFF_PORT_DOWN,
3339 static int ep_passable_under[] =
3344 static int ep_droppable[] =
3349 static int ep_explodes_1x1_old[] =
3354 static int ep_pushable[] =
3366 EL_SOKOBAN_FIELD_FULL,
3375 static int ep_explodes_cross_old[] =
3380 static int ep_protected[] =
3382 // same elements as in 'ep_walkable_inside'
3386 EL_TUBE_VERTICAL_LEFT,
3387 EL_TUBE_VERTICAL_RIGHT,
3388 EL_TUBE_HORIZONTAL_UP,
3389 EL_TUBE_HORIZONTAL_DOWN,
3395 // same elements as in 'ep_passable_over'
3404 EL_EM_GATE_1_GRAY_ACTIVE,
3405 EL_EM_GATE_2_GRAY_ACTIVE,
3406 EL_EM_GATE_3_GRAY_ACTIVE,
3407 EL_EM_GATE_4_GRAY_ACTIVE,
3416 EL_EMC_GATE_5_GRAY_ACTIVE,
3417 EL_EMC_GATE_6_GRAY_ACTIVE,
3418 EL_EMC_GATE_7_GRAY_ACTIVE,
3419 EL_EMC_GATE_8_GRAY_ACTIVE,
3421 EL_DC_GATE_WHITE_GRAY,
3422 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3426 // same elements as in 'ep_passable_inside'
3431 EL_SP_PORT_HORIZONTAL,
3432 EL_SP_PORT_VERTICAL,
3434 EL_SP_GRAVITY_PORT_LEFT,
3435 EL_SP_GRAVITY_PORT_RIGHT,
3436 EL_SP_GRAVITY_PORT_UP,
3437 EL_SP_GRAVITY_PORT_DOWN,
3438 EL_SP_GRAVITY_ON_PORT_LEFT,
3439 EL_SP_GRAVITY_ON_PORT_RIGHT,
3440 EL_SP_GRAVITY_ON_PORT_UP,
3441 EL_SP_GRAVITY_ON_PORT_DOWN,
3442 EL_SP_GRAVITY_OFF_PORT_LEFT,
3443 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3444 EL_SP_GRAVITY_OFF_PORT_UP,
3445 EL_SP_GRAVITY_OFF_PORT_DOWN,
3450 static int ep_throwable[] =
3455 static int ep_can_explode[] =
3457 // same elements as in 'ep_explodes_impact'
3462 // same elements as in 'ep_explodes_smashed'
3468 // elements that can explode by explosion or by dragonfire
3472 EL_EM_DYNAMITE_ACTIVE,
3473 EL_DYNABOMB_PLAYER_1_ACTIVE,
3474 EL_DYNABOMB_PLAYER_2_ACTIVE,
3475 EL_DYNABOMB_PLAYER_3_ACTIVE,
3476 EL_DYNABOMB_PLAYER_4_ACTIVE,
3477 EL_DYNABOMB_INCREASE_NUMBER,
3478 EL_DYNABOMB_INCREASE_SIZE,
3479 EL_DYNABOMB_INCREASE_POWER,
3480 EL_SP_DISK_RED_ACTIVE,
3488 // elements that can explode only by explosion
3494 static int ep_gravity_reachable[] =
3500 EL_INVISIBLE_SAND_ACTIVE,
3505 EL_SP_PORT_HORIZONTAL,
3506 EL_SP_PORT_VERTICAL,
3508 EL_SP_GRAVITY_PORT_LEFT,
3509 EL_SP_GRAVITY_PORT_RIGHT,
3510 EL_SP_GRAVITY_PORT_UP,
3511 EL_SP_GRAVITY_PORT_DOWN,
3512 EL_SP_GRAVITY_ON_PORT_LEFT,
3513 EL_SP_GRAVITY_ON_PORT_RIGHT,
3514 EL_SP_GRAVITY_ON_PORT_UP,
3515 EL_SP_GRAVITY_ON_PORT_DOWN,
3516 EL_SP_GRAVITY_OFF_PORT_LEFT,
3517 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3518 EL_SP_GRAVITY_OFF_PORT_UP,
3519 EL_SP_GRAVITY_OFF_PORT_DOWN,
3525 static int ep_empty_space[] =
3548 static int ep_player[] =
3555 EL_SOKOBAN_FIELD_PLAYER,
3561 static int ep_can_pass_magic_wall[] =
3575 static int ep_can_pass_dc_magic_wall[] =
3591 static int ep_switchable[] =
3595 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3596 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3597 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3598 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3599 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3600 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3601 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3602 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3603 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3604 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3605 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3606 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3607 EL_SWITCHGATE_SWITCH_UP,
3608 EL_SWITCHGATE_SWITCH_DOWN,
3609 EL_DC_SWITCHGATE_SWITCH_UP,
3610 EL_DC_SWITCHGATE_SWITCH_DOWN,
3612 EL_LIGHT_SWITCH_ACTIVE,
3614 EL_DC_TIMEGATE_SWITCH,
3615 EL_BALLOON_SWITCH_LEFT,
3616 EL_BALLOON_SWITCH_RIGHT,
3617 EL_BALLOON_SWITCH_UP,
3618 EL_BALLOON_SWITCH_DOWN,
3619 EL_BALLOON_SWITCH_ANY,
3620 EL_BALLOON_SWITCH_NONE,
3623 EL_EMC_MAGIC_BALL_SWITCH,
3624 EL_EMC_MAGIC_BALL_SWITCH_ACTIVE,
3629 static int ep_bd_element[] =
3649 EL_BD_FIREFLY_RIGHT,
3652 EL_BD_BUTTERFLY_DOWN,
3653 EL_BD_BUTTERFLY_LEFT,
3655 EL_BD_BUTTERFLY_RIGHT,
3663 static int ep_sp_element[] =
3665 // should always be valid
3668 // standard classic Supaplex elements
3675 EL_SP_HARDWARE_GRAY,
3683 EL_SP_GRAVITY_PORT_RIGHT,
3684 EL_SP_GRAVITY_PORT_DOWN,
3685 EL_SP_GRAVITY_PORT_LEFT,
3686 EL_SP_GRAVITY_PORT_UP,
3691 EL_SP_PORT_VERTICAL,
3692 EL_SP_PORT_HORIZONTAL,
3698 EL_SP_HARDWARE_BASE_1,
3699 EL_SP_HARDWARE_GREEN,
3700 EL_SP_HARDWARE_BLUE,
3702 EL_SP_HARDWARE_YELLOW,
3703 EL_SP_HARDWARE_BASE_2,
3704 EL_SP_HARDWARE_BASE_3,
3705 EL_SP_HARDWARE_BASE_4,
3706 EL_SP_HARDWARE_BASE_5,
3707 EL_SP_HARDWARE_BASE_6,
3711 // additional elements that appeared in newer Supaplex levels
3714 // additional gravity port elements (not switching, but setting gravity)
3715 EL_SP_GRAVITY_ON_PORT_LEFT,
3716 EL_SP_GRAVITY_ON_PORT_RIGHT,
3717 EL_SP_GRAVITY_ON_PORT_UP,
3718 EL_SP_GRAVITY_ON_PORT_DOWN,
3719 EL_SP_GRAVITY_OFF_PORT_LEFT,
3720 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3721 EL_SP_GRAVITY_OFF_PORT_UP,
3722 EL_SP_GRAVITY_OFF_PORT_DOWN,
3724 // more than one Murphy in a level results in an inactive clone
3727 // runtime Supaplex elements
3728 EL_SP_DISK_RED_ACTIVE,
3729 EL_SP_TERMINAL_ACTIVE,
3730 EL_SP_BUGGY_BASE_ACTIVATING,
3731 EL_SP_BUGGY_BASE_ACTIVE,
3738 static int ep_sb_element[] =
3743 EL_SOKOBAN_FIELD_EMPTY,
3744 EL_SOKOBAN_FIELD_FULL,
3745 EL_SOKOBAN_FIELD_PLAYER,
3750 EL_INVISIBLE_STEELWALL,
3755 static int ep_gem[] =
3767 static int ep_food_dark_yamyam[] =
3795 static int ep_food_penguin[] =
3809 static int ep_food_pig[] =
3821 static int ep_historic_wall[] =
3832 EL_GATE_1_GRAY_ACTIVE,
3833 EL_GATE_2_GRAY_ACTIVE,
3834 EL_GATE_3_GRAY_ACTIVE,
3835 EL_GATE_4_GRAY_ACTIVE,
3844 EL_EM_GATE_1_GRAY_ACTIVE,
3845 EL_EM_GATE_2_GRAY_ACTIVE,
3846 EL_EM_GATE_3_GRAY_ACTIVE,
3847 EL_EM_GATE_4_GRAY_ACTIVE,
3854 EL_EXPANDABLE_WALL_HORIZONTAL,
3855 EL_EXPANDABLE_WALL_VERTICAL,
3856 EL_EXPANDABLE_WALL_ANY,
3857 EL_EXPANDABLE_WALL_GROWING,
3858 EL_BD_EXPANDABLE_WALL,
3865 EL_SP_HARDWARE_GRAY,
3866 EL_SP_HARDWARE_GREEN,
3867 EL_SP_HARDWARE_BLUE,
3869 EL_SP_HARDWARE_YELLOW,
3870 EL_SP_HARDWARE_BASE_1,
3871 EL_SP_HARDWARE_BASE_2,
3872 EL_SP_HARDWARE_BASE_3,
3873 EL_SP_HARDWARE_BASE_4,
3874 EL_SP_HARDWARE_BASE_5,
3875 EL_SP_HARDWARE_BASE_6,
3877 EL_SP_TERMINAL_ACTIVE,
3880 EL_INVISIBLE_STEELWALL,
3881 EL_INVISIBLE_STEELWALL_ACTIVE,
3883 EL_INVISIBLE_WALL_ACTIVE,
3884 EL_STEELWALL_SLIPPERY,
3901 static int ep_historic_solid[] =
3905 EL_EXPANDABLE_WALL_HORIZONTAL,
3906 EL_EXPANDABLE_WALL_VERTICAL,
3907 EL_EXPANDABLE_WALL_ANY,
3908 EL_BD_EXPANDABLE_WALL,
3921 EL_QUICKSAND_FILLING,
3922 EL_QUICKSAND_EMPTYING,
3924 EL_MAGIC_WALL_ACTIVE,
3925 EL_MAGIC_WALL_EMPTYING,
3926 EL_MAGIC_WALL_FILLING,
3930 EL_BD_MAGIC_WALL_ACTIVE,
3931 EL_BD_MAGIC_WALL_EMPTYING,
3932 EL_BD_MAGIC_WALL_FULL,
3933 EL_BD_MAGIC_WALL_FILLING,
3934 EL_BD_MAGIC_WALL_DEAD,
3943 EL_SP_TERMINAL_ACTIVE,
3947 EL_INVISIBLE_WALL_ACTIVE,
3948 EL_SWITCHGATE_SWITCH_UP,
3949 EL_SWITCHGATE_SWITCH_DOWN,
3951 EL_TIMEGATE_SWITCH_ACTIVE,
3963 // the following elements are a direct copy of "indestructible" elements,
3964 // except "EL_ACID", which is "indestructible", but not "solid"!
3969 EL_ACID_POOL_TOPLEFT,
3970 EL_ACID_POOL_TOPRIGHT,
3971 EL_ACID_POOL_BOTTOMLEFT,
3972 EL_ACID_POOL_BOTTOM,
3973 EL_ACID_POOL_BOTTOMRIGHT,
3974 EL_SP_HARDWARE_GRAY,
3975 EL_SP_HARDWARE_GREEN,
3976 EL_SP_HARDWARE_BLUE,
3978 EL_SP_HARDWARE_YELLOW,
3979 EL_SP_HARDWARE_BASE_1,
3980 EL_SP_HARDWARE_BASE_2,
3981 EL_SP_HARDWARE_BASE_3,
3982 EL_SP_HARDWARE_BASE_4,
3983 EL_SP_HARDWARE_BASE_5,
3984 EL_SP_HARDWARE_BASE_6,
3985 EL_INVISIBLE_STEELWALL,
3986 EL_INVISIBLE_STEELWALL_ACTIVE,
3987 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3988 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3989 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3990 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3991 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3992 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3993 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3994 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3995 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3996 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3997 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3998 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4000 EL_LIGHT_SWITCH_ACTIVE,
4001 EL_SIGN_EXCLAMATION,
4002 EL_SIGN_RADIOACTIVITY,
4009 EL_SIGN_ENTRY_FORBIDDEN,
4010 EL_SIGN_EMERGENCY_EXIT,
4018 EL_STEEL_EXIT_CLOSED,
4020 EL_STEEL_EXIT_OPENING,
4021 EL_STEEL_EXIT_CLOSING,
4022 EL_EM_STEEL_EXIT_CLOSED,
4023 EL_EM_STEEL_EXIT_OPEN,
4024 EL_EM_STEEL_EXIT_OPENING,
4025 EL_EM_STEEL_EXIT_CLOSING,
4026 EL_DC_STEELWALL_1_LEFT,
4027 EL_DC_STEELWALL_1_RIGHT,
4028 EL_DC_STEELWALL_1_TOP,
4029 EL_DC_STEELWALL_1_BOTTOM,
4030 EL_DC_STEELWALL_1_HORIZONTAL,
4031 EL_DC_STEELWALL_1_VERTICAL,
4032 EL_DC_STEELWALL_1_TOPLEFT,
4033 EL_DC_STEELWALL_1_TOPRIGHT,
4034 EL_DC_STEELWALL_1_BOTTOMLEFT,
4035 EL_DC_STEELWALL_1_BOTTOMRIGHT,
4036 EL_DC_STEELWALL_1_TOPLEFT_2,
4037 EL_DC_STEELWALL_1_TOPRIGHT_2,
4038 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
4039 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
4040 EL_DC_STEELWALL_2_LEFT,
4041 EL_DC_STEELWALL_2_RIGHT,
4042 EL_DC_STEELWALL_2_TOP,
4043 EL_DC_STEELWALL_2_BOTTOM,
4044 EL_DC_STEELWALL_2_HORIZONTAL,
4045 EL_DC_STEELWALL_2_VERTICAL,
4046 EL_DC_STEELWALL_2_MIDDLE,
4047 EL_DC_STEELWALL_2_SINGLE,
4048 EL_STEELWALL_SLIPPERY,
4062 EL_GATE_1_GRAY_ACTIVE,
4063 EL_GATE_2_GRAY_ACTIVE,
4064 EL_GATE_3_GRAY_ACTIVE,
4065 EL_GATE_4_GRAY_ACTIVE,
4074 EL_EM_GATE_1_GRAY_ACTIVE,
4075 EL_EM_GATE_2_GRAY_ACTIVE,
4076 EL_EM_GATE_3_GRAY_ACTIVE,
4077 EL_EM_GATE_4_GRAY_ACTIVE,
4086 EL_EMC_GATE_5_GRAY_ACTIVE,
4087 EL_EMC_GATE_6_GRAY_ACTIVE,
4088 EL_EMC_GATE_7_GRAY_ACTIVE,
4089 EL_EMC_GATE_8_GRAY_ACTIVE,
4091 EL_DC_GATE_WHITE_GRAY,
4092 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4093 EL_DC_GATE_FAKE_GRAY,
4095 EL_SWITCHGATE_OPENING,
4096 EL_SWITCHGATE_CLOSED,
4097 EL_SWITCHGATE_CLOSING,
4098 EL_DC_SWITCHGATE_SWITCH_UP,
4099 EL_DC_SWITCHGATE_SWITCH_DOWN,
4101 EL_TIMEGATE_OPENING,
4103 EL_TIMEGATE_CLOSING,
4104 EL_DC_TIMEGATE_SWITCH,
4105 EL_DC_TIMEGATE_SWITCH_ACTIVE,
4109 EL_TUBE_VERTICAL_LEFT,
4110 EL_TUBE_VERTICAL_RIGHT,
4111 EL_TUBE_HORIZONTAL_UP,
4112 EL_TUBE_HORIZONTAL_DOWN,
4117 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
4118 EL_EXPANDABLE_STEELWALL_VERTICAL,
4119 EL_EXPANDABLE_STEELWALL_ANY,
4124 static int ep_classic_enemy[] =
4141 static int ep_belt[] =
4143 EL_CONVEYOR_BELT_1_LEFT,
4144 EL_CONVEYOR_BELT_1_MIDDLE,
4145 EL_CONVEYOR_BELT_1_RIGHT,
4146 EL_CONVEYOR_BELT_2_LEFT,
4147 EL_CONVEYOR_BELT_2_MIDDLE,
4148 EL_CONVEYOR_BELT_2_RIGHT,
4149 EL_CONVEYOR_BELT_3_LEFT,
4150 EL_CONVEYOR_BELT_3_MIDDLE,
4151 EL_CONVEYOR_BELT_3_RIGHT,
4152 EL_CONVEYOR_BELT_4_LEFT,
4153 EL_CONVEYOR_BELT_4_MIDDLE,
4154 EL_CONVEYOR_BELT_4_RIGHT,
4159 static int ep_belt_active[] =
4161 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4162 EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE,
4163 EL_CONVEYOR_BELT_1_RIGHT_ACTIVE,
4164 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4165 EL_CONVEYOR_BELT_2_MIDDLE_ACTIVE,
4166 EL_CONVEYOR_BELT_2_RIGHT_ACTIVE,
4167 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4168 EL_CONVEYOR_BELT_3_MIDDLE_ACTIVE,
4169 EL_CONVEYOR_BELT_3_RIGHT_ACTIVE,
4170 EL_CONVEYOR_BELT_4_LEFT_ACTIVE,
4171 EL_CONVEYOR_BELT_4_MIDDLE_ACTIVE,
4172 EL_CONVEYOR_BELT_4_RIGHT_ACTIVE,
4177 static int ep_belt_switch[] =
4179 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4180 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4181 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4182 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4183 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4184 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4185 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4186 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4187 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4188 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4189 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4190 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4195 static int ep_tube[] =
4202 EL_TUBE_HORIZONTAL_UP,
4203 EL_TUBE_HORIZONTAL_DOWN,
4205 EL_TUBE_VERTICAL_LEFT,
4206 EL_TUBE_VERTICAL_RIGHT,
4212 static int ep_acid_pool[] =
4214 EL_ACID_POOL_TOPLEFT,
4215 EL_ACID_POOL_TOPRIGHT,
4216 EL_ACID_POOL_BOTTOMLEFT,
4217 EL_ACID_POOL_BOTTOM,
4218 EL_ACID_POOL_BOTTOMRIGHT,
4223 static int ep_keygate[] =
4233 EL_GATE_1_GRAY_ACTIVE,
4234 EL_GATE_2_GRAY_ACTIVE,
4235 EL_GATE_3_GRAY_ACTIVE,
4236 EL_GATE_4_GRAY_ACTIVE,
4245 EL_EM_GATE_1_GRAY_ACTIVE,
4246 EL_EM_GATE_2_GRAY_ACTIVE,
4247 EL_EM_GATE_3_GRAY_ACTIVE,
4248 EL_EM_GATE_4_GRAY_ACTIVE,
4257 EL_EMC_GATE_5_GRAY_ACTIVE,
4258 EL_EMC_GATE_6_GRAY_ACTIVE,
4259 EL_EMC_GATE_7_GRAY_ACTIVE,
4260 EL_EMC_GATE_8_GRAY_ACTIVE,
4262 EL_DC_GATE_WHITE_GRAY,
4263 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4268 static int ep_amoeboid[] =
4280 static int ep_amoebalive[] =
4291 static int ep_has_editor_content[] =
4297 EL_SOKOBAN_FIELD_PLAYER,
4326 static int ep_can_turn_each_move[] =
4328 // !!! do something with this one !!!
4332 static int ep_can_grow[] =
4346 static int ep_active_bomb[] =
4349 EL_EM_DYNAMITE_ACTIVE,
4350 EL_DYNABOMB_PLAYER_1_ACTIVE,
4351 EL_DYNABOMB_PLAYER_2_ACTIVE,
4352 EL_DYNABOMB_PLAYER_3_ACTIVE,
4353 EL_DYNABOMB_PLAYER_4_ACTIVE,
4354 EL_SP_DISK_RED_ACTIVE,
4359 static int ep_inactive[] =
4385 EL_QUICKSAND_FAST_EMPTY,
4408 EL_GATE_1_GRAY_ACTIVE,
4409 EL_GATE_2_GRAY_ACTIVE,
4410 EL_GATE_3_GRAY_ACTIVE,
4411 EL_GATE_4_GRAY_ACTIVE,
4420 EL_EM_GATE_1_GRAY_ACTIVE,
4421 EL_EM_GATE_2_GRAY_ACTIVE,
4422 EL_EM_GATE_3_GRAY_ACTIVE,
4423 EL_EM_GATE_4_GRAY_ACTIVE,
4432 EL_EMC_GATE_5_GRAY_ACTIVE,
4433 EL_EMC_GATE_6_GRAY_ACTIVE,
4434 EL_EMC_GATE_7_GRAY_ACTIVE,
4435 EL_EMC_GATE_8_GRAY_ACTIVE,
4437 EL_DC_GATE_WHITE_GRAY,
4438 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4439 EL_DC_GATE_FAKE_GRAY,
4442 EL_INVISIBLE_STEELWALL,
4450 EL_WALL_EMERALD_YELLOW,
4451 EL_DYNABOMB_INCREASE_NUMBER,
4452 EL_DYNABOMB_INCREASE_SIZE,
4453 EL_DYNABOMB_INCREASE_POWER,
4457 EL_SOKOBAN_FIELD_EMPTY,
4458 EL_SOKOBAN_FIELD_FULL,
4459 EL_WALL_EMERALD_RED,
4460 EL_WALL_EMERALD_PURPLE,
4461 EL_ACID_POOL_TOPLEFT,
4462 EL_ACID_POOL_TOPRIGHT,
4463 EL_ACID_POOL_BOTTOMLEFT,
4464 EL_ACID_POOL_BOTTOM,
4465 EL_ACID_POOL_BOTTOMRIGHT,
4469 EL_BD_MAGIC_WALL_DEAD,
4471 EL_DC_MAGIC_WALL_DEAD,
4472 EL_AMOEBA_TO_DIAMOND,
4480 EL_SP_GRAVITY_PORT_RIGHT,
4481 EL_SP_GRAVITY_PORT_DOWN,
4482 EL_SP_GRAVITY_PORT_LEFT,
4483 EL_SP_GRAVITY_PORT_UP,
4484 EL_SP_PORT_HORIZONTAL,
4485 EL_SP_PORT_VERTICAL,
4496 EL_SP_HARDWARE_GRAY,
4497 EL_SP_HARDWARE_GREEN,
4498 EL_SP_HARDWARE_BLUE,
4500 EL_SP_HARDWARE_YELLOW,
4501 EL_SP_HARDWARE_BASE_1,
4502 EL_SP_HARDWARE_BASE_2,
4503 EL_SP_HARDWARE_BASE_3,
4504 EL_SP_HARDWARE_BASE_4,
4505 EL_SP_HARDWARE_BASE_5,
4506 EL_SP_HARDWARE_BASE_6,
4507 EL_SP_GRAVITY_ON_PORT_LEFT,
4508 EL_SP_GRAVITY_ON_PORT_RIGHT,
4509 EL_SP_GRAVITY_ON_PORT_UP,
4510 EL_SP_GRAVITY_ON_PORT_DOWN,
4511 EL_SP_GRAVITY_OFF_PORT_LEFT,
4512 EL_SP_GRAVITY_OFF_PORT_RIGHT,
4513 EL_SP_GRAVITY_OFF_PORT_UP,
4514 EL_SP_GRAVITY_OFF_PORT_DOWN,
4515 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4516 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4517 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4518 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4519 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4520 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4521 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4522 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4523 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4524 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4525 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4526 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4527 EL_SIGN_EXCLAMATION,
4528 EL_SIGN_RADIOACTIVITY,
4535 EL_SIGN_ENTRY_FORBIDDEN,
4536 EL_SIGN_EMERGENCY_EXIT,
4544 EL_DC_STEELWALL_1_LEFT,
4545 EL_DC_STEELWALL_1_RIGHT,
4546 EL_DC_STEELWALL_1_TOP,
4547 EL_DC_STEELWALL_1_BOTTOM,
4548 EL_DC_STEELWALL_1_HORIZONTAL,
4549 EL_DC_STEELWALL_1_VERTICAL,
4550 EL_DC_STEELWALL_1_TOPLEFT,
4551 EL_DC_STEELWALL_1_TOPRIGHT,
4552 EL_DC_STEELWALL_1_BOTTOMLEFT,
4553 EL_DC_STEELWALL_1_BOTTOMRIGHT,
4554 EL_DC_STEELWALL_1_TOPLEFT_2,
4555 EL_DC_STEELWALL_1_TOPRIGHT_2,
4556 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
4557 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
4558 EL_DC_STEELWALL_2_LEFT,
4559 EL_DC_STEELWALL_2_RIGHT,
4560 EL_DC_STEELWALL_2_TOP,
4561 EL_DC_STEELWALL_2_BOTTOM,
4562 EL_DC_STEELWALL_2_HORIZONTAL,
4563 EL_DC_STEELWALL_2_VERTICAL,
4564 EL_DC_STEELWALL_2_MIDDLE,
4565 EL_DC_STEELWALL_2_SINGLE,
4566 EL_STEELWALL_SLIPPERY,
4571 EL_EMC_WALL_SLIPPERY_1,
4572 EL_EMC_WALL_SLIPPERY_2,
4573 EL_EMC_WALL_SLIPPERY_3,
4574 EL_EMC_WALL_SLIPPERY_4,
4595 static int ep_em_slippery_wall[] =
4600 static int ep_gfx_crumbled[] =
4611 static int ep_editor_cascade_active[] =
4613 EL_INTERNAL_CASCADE_BD_ACTIVE,
4614 EL_INTERNAL_CASCADE_BDX_ACTIVE,
4615 EL_INTERNAL_CASCADE_BDX_EFFECTS_ACTIVE,
4616 EL_INTERNAL_CASCADE_EM_ACTIVE,
4617 EL_INTERNAL_CASCADE_EMC_ACTIVE,
4618 EL_INTERNAL_CASCADE_RND_ACTIVE,
4619 EL_INTERNAL_CASCADE_SB_ACTIVE,
4620 EL_INTERNAL_CASCADE_SP_ACTIVE,
4621 EL_INTERNAL_CASCADE_DC_ACTIVE,
4622 EL_INTERNAL_CASCADE_DX_ACTIVE,
4623 EL_INTERNAL_CASCADE_MM_ACTIVE,
4624 EL_INTERNAL_CASCADE_DF_ACTIVE,
4625 EL_INTERNAL_CASCADE_CHARS_ACTIVE,
4626 EL_INTERNAL_CASCADE_STEEL_CHARS_ACTIVE,
4627 EL_INTERNAL_CASCADE_CE_ACTIVE,
4628 EL_INTERNAL_CASCADE_GE_ACTIVE,
4629 EL_INTERNAL_CASCADE_ES_ACTIVE,
4630 EL_INTERNAL_CASCADE_REF_ACTIVE,
4631 EL_INTERNAL_CASCADE_USER_ACTIVE,
4632 EL_INTERNAL_CASCADE_DYNAMIC_ACTIVE,
4637 static int ep_editor_cascade_inactive[] =
4639 EL_INTERNAL_CASCADE_BD,
4640 EL_INTERNAL_CASCADE_BDX,
4641 EL_INTERNAL_CASCADE_BDX_EFFECTS,
4642 EL_INTERNAL_CASCADE_EM,
4643 EL_INTERNAL_CASCADE_EMC,
4644 EL_INTERNAL_CASCADE_RND,
4645 EL_INTERNAL_CASCADE_SB,
4646 EL_INTERNAL_CASCADE_SP,
4647 EL_INTERNAL_CASCADE_DC,
4648 EL_INTERNAL_CASCADE_DX,
4649 EL_INTERNAL_CASCADE_MM,
4650 EL_INTERNAL_CASCADE_DF,
4651 EL_INTERNAL_CASCADE_CHARS,
4652 EL_INTERNAL_CASCADE_STEEL_CHARS,
4653 EL_INTERNAL_CASCADE_CE,
4654 EL_INTERNAL_CASCADE_GE,
4655 EL_INTERNAL_CASCADE_ES,
4656 EL_INTERNAL_CASCADE_REF,
4657 EL_INTERNAL_CASCADE_USER,
4658 EL_INTERNAL_CASCADE_DYNAMIC,
4663 static int ep_obsolete[] =
4667 EL_EM_KEY_1_FILE_OBSOLETE,
4668 EL_EM_KEY_2_FILE_OBSOLETE,
4669 EL_EM_KEY_3_FILE_OBSOLETE,
4670 EL_EM_KEY_4_FILE_OBSOLETE,
4671 EL_ENVELOPE_OBSOLETE,
4680 } element_properties[] =
4682 { ep_diggable, EP_DIGGABLE },
4683 { ep_collectible_only, EP_COLLECTIBLE_ONLY },
4684 { ep_dont_run_into, EP_DONT_RUN_INTO },
4685 { ep_dont_collide_with, EP_DONT_COLLIDE_WITH },
4686 { ep_dont_touch, EP_DONT_TOUCH },
4687 { ep_indestructible, EP_INDESTRUCTIBLE },
4688 { ep_slippery, EP_SLIPPERY },
4689 { ep_can_change, EP_CAN_CHANGE },
4690 { ep_can_move, EP_CAN_MOVE },
4691 { ep_can_fall, EP_CAN_FALL },
4692 { ep_can_smash_player, EP_CAN_SMASH_PLAYER },
4693 { ep_can_smash_enemies, EP_CAN_SMASH_ENEMIES },
4694 { ep_can_smash_everything, EP_CAN_SMASH_EVERYTHING },
4695 { ep_explodes_by_fire, EP_EXPLODES_BY_FIRE },
4696 { ep_explodes_smashed, EP_EXPLODES_SMASHED },
4697 { ep_explodes_impact, EP_EXPLODES_IMPACT },
4698 { ep_walkable_over, EP_WALKABLE_OVER },
4699 { ep_walkable_inside, EP_WALKABLE_INSIDE },
4700 { ep_walkable_under, EP_WALKABLE_UNDER },
4701 { ep_passable_over, EP_PASSABLE_OVER },
4702 { ep_passable_inside, EP_PASSABLE_INSIDE },
4703 { ep_passable_under, EP_PASSABLE_UNDER },
4704 { ep_droppable, EP_DROPPABLE },
4705 { ep_explodes_1x1_old, EP_EXPLODES_1X1_OLD },
4706 { ep_pushable, EP_PUSHABLE },
4707 { ep_explodes_cross_old, EP_EXPLODES_CROSS_OLD },
4708 { ep_protected, EP_PROTECTED },
4709 { ep_throwable, EP_THROWABLE },
4710 { ep_can_explode, EP_CAN_EXPLODE },
4711 { ep_gravity_reachable, EP_GRAVITY_REACHABLE },
4713 { ep_empty_space, EP_EMPTY_SPACE },
4714 { ep_player, EP_PLAYER },
4715 { ep_can_pass_magic_wall, EP_CAN_PASS_MAGIC_WALL },
4716 { ep_can_pass_dc_magic_wall, EP_CAN_PASS_DC_MAGIC_WALL },
4717 { ep_switchable, EP_SWITCHABLE },
4718 { ep_bd_element, EP_BD_ELEMENT },
4719 { ep_sp_element, EP_SP_ELEMENT },
4720 { ep_sb_element, EP_SB_ELEMENT },
4722 { ep_food_dark_yamyam, EP_FOOD_DARK_YAMYAM },
4723 { ep_food_penguin, EP_FOOD_PENGUIN },
4724 { ep_food_pig, EP_FOOD_PIG },
4725 { ep_historic_wall, EP_HISTORIC_WALL },
4726 { ep_historic_solid, EP_HISTORIC_SOLID },
4727 { ep_classic_enemy, EP_CLASSIC_ENEMY },
4728 { ep_belt, EP_BELT },
4729 { ep_belt_active, EP_BELT_ACTIVE },
4730 { ep_belt_switch, EP_BELT_SWITCH },
4731 { ep_tube, EP_TUBE },
4732 { ep_acid_pool, EP_ACID_POOL },
4733 { ep_keygate, EP_KEYGATE },
4734 { ep_amoeboid, EP_AMOEBOID },
4735 { ep_amoebalive, EP_AMOEBALIVE },
4736 { ep_has_editor_content, EP_HAS_EDITOR_CONTENT },
4737 { ep_can_turn_each_move, EP_CAN_TURN_EACH_MOVE },
4738 { ep_can_grow, EP_CAN_GROW },
4739 { ep_active_bomb, EP_ACTIVE_BOMB },
4740 { ep_inactive, EP_INACTIVE },
4742 { ep_em_slippery_wall, EP_EM_SLIPPERY_WALL },
4744 { ep_gfx_crumbled, EP_GFX_CRUMBLED },
4746 { ep_editor_cascade_active, EP_EDITOR_CASCADE_ACTIVE },
4747 { ep_editor_cascade_inactive, EP_EDITOR_CASCADE_INACTIVE },
4749 { ep_obsolete, EP_OBSOLETE },
4756 // always start with reliable default values (element has no properties)
4757 // (but never initialize clipboard elements after the very first time)
4758 // (to be able to use clipboard elements between several levels)
4759 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4760 if (!IS_CLIPBOARD_ELEMENT(i) || !clipboard_elements_initialized)
4761 for (j = 0; j < NUM_ELEMENT_PROPERTIES; j++)
4762 SET_PROPERTY(i, j, FALSE);
4764 // set all base element properties from above array definitions
4765 for (i = 0; element_properties[i].elements != NULL; i++)
4766 for (j = 0; (element_properties[i].elements)[j] != -1; j++)
4767 SET_PROPERTY((element_properties[i].elements)[j],
4768 element_properties[i].property, TRUE);
4770 // copy properties to some elements that are only stored in level file
4771 for (i = 0; i < NUM_ELEMENT_PROPERTIES; i++)
4772 for (j = 0; copy_properties[j][0] != -1; j++)
4773 if (HAS_PROPERTY(copy_properties[j][0], i))
4774 for (k = 1; k <= 4; k++)
4775 SET_PROPERTY(copy_properties[j][k], i, TRUE);
4777 // set static element properties that are not listed in array definitions
4778 for (i = EL_STEEL_CHAR_START; i <= EL_STEEL_CHAR_END; i++)
4779 SET_PROPERTY(i, EP_INDESTRUCTIBLE, TRUE);
4781 clipboard_elements_initialized = TRUE;
4784 void InitElementPropertiesEngine(int engine_version)
4786 static int no_wall_properties[] =
4789 EP_COLLECTIBLE_ONLY,
4791 EP_DONT_COLLIDE_WITH,
4794 EP_CAN_SMASH_PLAYER,
4795 EP_CAN_SMASH_ENEMIES,
4796 EP_CAN_SMASH_EVERYTHING,
4801 EP_FOOD_DARK_YAMYAM,
4817 /* important: after initialization in InitElementPropertiesStatic(), the
4818 elements are not again initialized to a default value; therefore all
4819 changes have to make sure that they leave the element with a defined
4820 property (which means that conditional property changes must be set to
4821 a reliable default value before) */
4823 // resolve group elements
4824 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
4825 ResolveGroupElement(EL_GROUP_START + i);
4827 // set all special, combined or engine dependent element properties
4828 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4830 // do not change (already initialized) clipboard elements here
4831 if (IS_CLIPBOARD_ELEMENT(i))
4834 // ---------- INACTIVE ----------------------------------------------------
4835 SET_PROPERTY(i, EP_INACTIVE, ((i >= EL_CHAR_START &&
4836 i <= EL_CHAR_END) ||
4837 (i >= EL_STEEL_CHAR_START &&
4838 i <= EL_STEEL_CHAR_END)));
4840 // ---------- WALKABLE, PASSABLE, ACCESSIBLE ------------------------------
4841 SET_PROPERTY(i, EP_WALKABLE, (IS_WALKABLE_OVER(i) ||
4842 IS_WALKABLE_INSIDE(i) ||
4843 IS_WALKABLE_UNDER(i)));
4845 SET_PROPERTY(i, EP_PASSABLE, (IS_PASSABLE_OVER(i) ||
4846 IS_PASSABLE_INSIDE(i) ||
4847 IS_PASSABLE_UNDER(i)));
4849 SET_PROPERTY(i, EP_ACCESSIBLE_OVER, (IS_WALKABLE_OVER(i) ||
4850 IS_PASSABLE_OVER(i)));
4852 SET_PROPERTY(i, EP_ACCESSIBLE_INSIDE, (IS_WALKABLE_INSIDE(i) ||
4853 IS_PASSABLE_INSIDE(i)));
4855 SET_PROPERTY(i, EP_ACCESSIBLE_UNDER, (IS_WALKABLE_UNDER(i) ||
4856 IS_PASSABLE_UNDER(i)));
4858 SET_PROPERTY(i, EP_ACCESSIBLE, (IS_WALKABLE(i) ||
4861 // ---------- COLLECTIBLE -------------------------------------------------
4862 SET_PROPERTY(i, EP_COLLECTIBLE, (IS_COLLECTIBLE_ONLY(i) ||
4866 // ---------- SNAPPABLE ---------------------------------------------------
4867 SET_PROPERTY(i, EP_SNAPPABLE, (IS_DIGGABLE(i) ||
4868 IS_COLLECTIBLE(i) ||
4872 // ---------- WALL --------------------------------------------------------
4873 SET_PROPERTY(i, EP_WALL, TRUE); // default: element is wall
4875 for (j = 0; no_wall_properties[j] != -1; j++)
4876 if (HAS_PROPERTY(i, no_wall_properties[j]) ||
4877 i >= EL_FIRST_RUNTIME_UNREAL)
4878 SET_PROPERTY(i, EP_WALL, FALSE);
4880 if (IS_HISTORIC_WALL(i))
4881 SET_PROPERTY(i, EP_WALL, TRUE);
4883 // ---------- SOLID_FOR_PUSHING -------------------------------------------
4884 if (engine_version < VERSION_IDENT(2,2,0,0))
4885 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, IS_HISTORIC_SOLID(i));
4887 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, (!IS_WALKABLE(i) &&
4889 !IS_COLLECTIBLE(i)));
4891 // ---------- DRAGONFIRE_PROOF --------------------------------------------
4892 if (IS_HISTORIC_SOLID(i) || i == EL_EXPLOSION)
4893 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, TRUE);
4895 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, (IS_INDESTRUCTIBLE(i) &&
4898 // ---------- EXPLOSION_PROOF ---------------------------------------------
4900 SET_PROPERTY(i, EP_EXPLOSION_PROOF, TRUE);
4901 else if (engine_version < VERSION_IDENT(2,2,0,0))
4902 SET_PROPERTY(i, EP_EXPLOSION_PROOF, IS_INDESTRUCTIBLE(i));
4904 SET_PROPERTY(i, EP_EXPLOSION_PROOF, (IS_INDESTRUCTIBLE(i) &&
4908 if (IS_CUSTOM_ELEMENT(i))
4910 // these are additional properties which are initially false when set
4912 // ---------- DONT_COLLIDE_WITH / DONT_RUN_INTO -------------------------
4914 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH, TRUE);
4915 if (DONT_COLLIDE_WITH(i))
4916 SET_PROPERTY(i, EP_DONT_RUN_INTO, TRUE);
4918 // ---------- CAN_SMASH_ENEMIES / CAN_SMASH_PLAYER ----------------------
4919 if (CAN_SMASH_EVERYTHING(i))
4920 SET_PROPERTY(i, EP_CAN_SMASH_ENEMIES, TRUE);
4921 if (CAN_SMASH_ENEMIES(i))
4922 SET_PROPERTY(i, EP_CAN_SMASH_PLAYER, TRUE);
4925 // ---------- CAN_SMASH ---------------------------------------------------
4926 SET_PROPERTY(i, EP_CAN_SMASH, (CAN_SMASH_PLAYER(i) ||
4927 CAN_SMASH_ENEMIES(i) ||
4928 CAN_SMASH_EVERYTHING(i)));
4930 // ---------- CAN_EXPLODE_BY_FIRE -----------------------------------------
4931 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_FIRE, (CAN_EXPLODE(i) &&
4932 EXPLODES_BY_FIRE(i)));
4934 // ---------- CAN_EXPLODE_SMASHED -----------------------------------------
4935 SET_PROPERTY(i, EP_CAN_EXPLODE_SMASHED, (CAN_EXPLODE(i) &&
4936 EXPLODES_SMASHED(i)));
4938 // ---------- CAN_EXPLODE_IMPACT ------------------------------------------
4939 SET_PROPERTY(i, EP_CAN_EXPLODE_IMPACT, (CAN_EXPLODE(i) &&
4940 EXPLODES_IMPACT(i)));
4942 // ---------- CAN_EXPLODE_BY_DRAGONFIRE -----------------------------------
4943 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_DRAGONFIRE, CAN_EXPLODE_BY_FIRE(i));
4945 // ---------- CAN_EXPLODE_BY_EXPLOSION ------------------------------------
4946 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_EXPLOSION, (CAN_EXPLODE_BY_FIRE(i) ||
4947 i == EL_BLACK_ORB));
4949 // ---------- COULD_MOVE_INTO_ACID ----------------------------------------
4950 SET_PROPERTY(i, EP_COULD_MOVE_INTO_ACID, (IS_PLAYER_ELEMENT(i) ||
4952 IS_CUSTOM_ELEMENT(i)));
4954 // ---------- MAYBE_DONT_COLLIDE_WITH -------------------------------------
4955 SET_PROPERTY(i, EP_MAYBE_DONT_COLLIDE_WITH, (i == EL_SP_SNIKSNAK ||
4956 i == EL_SP_ELECTRON));
4958 // ---------- CAN_MOVE_INTO_ACID ------------------------------------------
4959 if (COULD_MOVE_INTO_ACID(i) && !IS_CUSTOM_ELEMENT(i))
4960 SET_PROPERTY(i, EP_CAN_MOVE_INTO_ACID,
4961 getMoveIntoAcidProperty(&level, i));
4963 // ---------- DONT_COLLIDE_WITH -------------------------------------------
4964 if (MAYBE_DONT_COLLIDE_WITH(i))
4965 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH,
4966 getDontCollideWithProperty(&level, i));
4968 // ---------- SP_PORT -----------------------------------------------------
4969 SET_PROPERTY(i, EP_SP_PORT, (IS_SP_ELEMENT(i) &&
4970 IS_PASSABLE_INSIDE(i)));
4972 // ---------- CAN_BE_CLONED_BY_ANDROID ------------------------------------
4973 for (j = 0; j < level.num_android_clone_elements; j++)
4974 SET_PROPERTY(i, EP_CAN_BE_CLONED_BY_ANDROID,
4976 IS_EQUAL_OR_IN_GROUP(i, level.android_clone_element[j])));
4978 // ---------- CAN_CHANGE --------------------------------------------------
4979 SET_PROPERTY(i, EP_CAN_CHANGE, FALSE); // default: cannot change
4980 for (j = 0; j < element_info[i].num_change_pages; j++)
4981 if (element_info[i].change_page[j].can_change)
4982 SET_PROPERTY(i, EP_CAN_CHANGE, TRUE);
4984 // ---------- HAS_ACTION --------------------------------------------------
4985 SET_PROPERTY(i, EP_HAS_ACTION, FALSE); // default: has no action
4986 for (j = 0; j < element_info[i].num_change_pages; j++)
4987 if (element_info[i].change_page[j].has_action)
4988 SET_PROPERTY(i, EP_HAS_ACTION, TRUE);
4990 // ---------- CAN_CHANGE_OR_HAS_ACTION ------------------------------------
4991 SET_PROPERTY(i, EP_CAN_CHANGE_OR_HAS_ACTION, (CAN_CHANGE(i) ||
4994 // ---------- GFX_CRUMBLED ------------------------------------------------
4995 SET_PROPERTY(i, EP_GFX_CRUMBLED,
4996 element_info[i].crumbled[ACTION_DEFAULT] !=
4997 element_info[i].graphic[ACTION_DEFAULT]);
4999 // ---------- EDITOR_CASCADE ----------------------------------------------
5000 SET_PROPERTY(i, EP_EDITOR_CASCADE, (IS_EDITOR_CASCADE_ACTIVE(i) ||
5001 IS_EDITOR_CASCADE_INACTIVE(i)));
5004 // dynamically adjust element properties according to game engine version
5006 static int ep_em_slippery_wall[] =
5011 EL_EXPANDABLE_WALL_HORIZONTAL,
5012 EL_EXPANDABLE_WALL_VERTICAL,
5013 EL_EXPANDABLE_WALL_ANY,
5014 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
5015 EL_EXPANDABLE_STEELWALL_VERTICAL,
5016 EL_EXPANDABLE_STEELWALL_ANY,
5017 EL_EXPANDABLE_STEELWALL_GROWING,
5021 static int ep_em_explodes_by_fire[] =
5024 EL_EM_DYNAMITE_ACTIVE,
5029 // special EM style gems behaviour
5030 for (i = 0; ep_em_slippery_wall[i] != -1; i++)
5031 SET_PROPERTY(ep_em_slippery_wall[i], EP_EM_SLIPPERY_WALL,
5032 level.em_slippery_gems);
5034 // "EL_EXPANDABLE_WALL_GROWING" wasn't slippery for EM gems in 2.0.1
5035 SET_PROPERTY(EL_EXPANDABLE_WALL_GROWING, EP_EM_SLIPPERY_WALL,
5036 (level.em_slippery_gems &&
5037 engine_version > VERSION_IDENT(2,0,1,0)));
5039 // special EM style explosion behaviour regarding chain reactions
5040 for (i = 0; ep_em_explodes_by_fire[i] != -1; i++)
5041 SET_PROPERTY(ep_em_explodes_by_fire[i], EP_EXPLODES_BY_FIRE,
5042 level.em_explodes_by_fire);
5045 // this is needed because some graphics depend on element properties
5046 if (game_status == GAME_MODE_PLAYING)
5047 InitElementGraphicInfo();
5050 void InitElementPropertiesGfxElement(void)
5054 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5056 struct ElementInfo *ei = &element_info[i];
5058 ei->gfx_element = (ei->use_gfx_element ? ei->gfx_element_initial : i);
5062 static void InitGlobal(void)
5067 for (i = 0; i < MAX_NUM_ELEMENTS + 1; i++)
5069 // check if element_name_info entry defined for each element in "main.h"
5070 if (i < MAX_NUM_ELEMENTS && element_name_info[i].token_name == NULL)
5071 Fail("undefined 'element_name_info' entry for element %d", i);
5073 element_info[i].token_name = element_name_info[i].token_name;
5074 element_info[i].class_name = element_name_info[i].class_name;
5075 element_info[i].editor_description = element_name_info[i].editor_description;
5078 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS + 1; i++)
5080 // check if global_anim_name_info defined for each entry in "main.h"
5081 if (i < NUM_GLOBAL_ANIM_TOKENS &&
5082 global_anim_name_info[i].token_name == NULL)
5083 Fail("undefined 'global_anim_name_info' entry for anim %d", i);
5085 global_anim_info[i].token_name = global_anim_name_info[i].token_name;
5088 // create hash to store URLs for global animations
5089 anim_url_hash = newSetupFileHash();
5091 // create hash from image config list
5092 image_config_hash = newSetupFileHash();
5093 for (i = 0; image_config[i].token != NULL; i++)
5094 setHashEntry(image_config_hash,
5095 image_config[i].token,
5096 image_config[i].value);
5098 // create hash from sound config list
5099 sound_config_hash = newSetupFileHash();
5100 for (i = 0; sound_config[i].token != NULL; i++)
5101 setHashEntry(sound_config_hash,
5102 sound_config[i].token,
5103 sound_config[i].value);
5105 // create hash from element token list
5106 element_token_hash = newSetupFileHash();
5107 for (i = 0; element_name_info[i].token_name != NULL; i++)
5108 setHashEntry(element_token_hash,
5109 element_name_info[i].token_name,
5112 // create hash from graphic token list
5113 graphic_token_hash = newSetupFileHash();
5114 for (graphic = 0, i = 0; image_config[i].token != NULL; i++)
5115 if (strSuffix(image_config[i].value, ".png") ||
5116 strSuffix(image_config[i].value, ".pcx") ||
5117 strSuffix(image_config[i].value, ".wav") ||
5118 strEqual(image_config[i].value, UNDEFINED_FILENAME))
5119 setHashEntry(graphic_token_hash,
5120 image_config[i].token,
5121 int2str(graphic++, 0));
5123 // create hash from font token list
5124 font_token_hash = newSetupFileHash();
5125 for (i = 0; font_info[i].token_name != NULL; i++)
5126 setHashEntry(font_token_hash,
5127 font_info[i].token_name,
5130 // set default filenames for all cloned graphics in static configuration
5131 for (i = 0; image_config[i].token != NULL; i++)
5133 if (strEqual(image_config[i].value, UNDEFINED_FILENAME))
5135 char *token = image_config[i].token;
5136 char *token_clone_from = getStringCat2(token, ".clone_from");
5137 char *token_cloned = getHashEntry(image_config_hash, token_clone_from);
5139 if (token_cloned != NULL)
5141 char *value_cloned = getHashEntry(image_config_hash, token_cloned);
5143 if (value_cloned != NULL)
5145 // set default filename in static configuration
5146 image_config[i].value = value_cloned;
5148 // set default filename in image config hash
5149 setHashEntry(image_config_hash, token, value_cloned);
5153 free(token_clone_from);
5157 // always start with reliable default values (all elements)
5158 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5159 ActiveElement[i] = i;
5161 // now add all entries that have an active state (active elements)
5162 for (i = 0; element_with_active_state[i].element != -1; i++)
5164 int element = element_with_active_state[i].element;
5165 int element_active = element_with_active_state[i].element_active;
5167 ActiveElement[element] = element_active;
5170 // always start with reliable default values (all buttons)
5171 for (i = 0; i < NUM_IMAGE_FILES; i++)
5172 ActiveButton[i] = i;
5174 // now add all entries that have an active state (active buttons)
5175 for (i = 0; button_with_active_state[i].button != -1; i++)
5177 int button = button_with_active_state[i].button;
5178 int button_active = button_with_active_state[i].button_active;
5180 ActiveButton[button] = button_active;
5183 // always start with reliable default values (all fonts)
5184 for (i = 0; i < NUM_FONTS; i++)
5187 // now add all entries that have an active state (active fonts)
5188 for (i = 0; font_with_active_state[i].font_nr != -1; i++)
5190 int font = font_with_active_state[i].font_nr;
5191 int font_active = font_with_active_state[i].font_nr_active;
5193 ActiveFont[font] = font_active;
5196 global.autoplay_leveldir = NULL;
5197 global.patchtapes_leveldir = NULL;
5198 global.convert_leveldir = NULL;
5199 global.dumplevel_leveldir = NULL;
5200 global.dumptape_leveldir = NULL;
5201 global.create_sketch_images_dir = NULL;
5202 global.create_collect_images_dir = NULL;
5204 global.frames_per_second = 0;
5205 global.show_frames_per_second = FALSE;
5207 global.border_status = GAME_MODE_LOADING;
5208 global.anim_status = global.anim_status_next = GAME_MODE_LOADING;
5210 global.use_envelope_request = FALSE;
5212 global.user_names = NULL;
5215 static void Execute_Command(char *command)
5219 if (strEqual(command, "print graphicsinfo.conf"))
5221 Print("# You can configure additional/alternative image files here.\n");
5222 Print("# (The entries below are default and therefore commented out.)\n");
5224 Print("%s\n", getFormattedSetupEntry("name", "Classic Graphics"));
5226 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5229 for (i = 0; image_config[i].token != NULL; i++)
5230 Print("# %s\n", getFormattedSetupEntry(image_config[i].token,
5231 image_config[i].value));
5235 else if (strEqual(command, "print soundsinfo.conf"))
5237 Print("# You can configure additional/alternative sound files here.\n");
5238 Print("# (The entries below are default and therefore commented out.)\n");
5240 Print("%s\n", getFormattedSetupEntry("name", "Classic Sounds"));
5242 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5245 for (i = 0; sound_config[i].token != NULL; i++)
5246 Print("# %s\n", getFormattedSetupEntry(sound_config[i].token,
5247 sound_config[i].value));
5251 else if (strEqual(command, "print musicinfo.conf"))
5253 Print("# You can configure additional/alternative music files here.\n");
5254 Print("# (The entries below are default and therefore commented out.)\n");
5256 Print("%s\n", getFormattedSetupEntry("name", "Classic Music"));
5258 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5261 for (i = 0; music_config[i].token != NULL; i++)
5262 Print("# %s\n", getFormattedSetupEntry(music_config[i].token,
5263 music_config[i].value));
5267 else if (strEqual(command, "print editorsetup.conf"))
5269 Print("# You can configure your personal editor element list here.\n");
5270 Print("# (The entries below are default and therefore commented out.)\n");
5273 // this is needed to be able to check element list for cascade elements
5274 InitElementPropertiesStatic();
5275 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
5277 PrintEditorElementList();
5281 else if (strEqual(command, "print helpanim.conf"))
5283 Print("# You can configure different element help animations here.\n");
5284 Print("# (The entries below are default and therefore commented out.)\n");
5287 for (i = 0; helpanim_config[i].token != NULL; i++)
5289 Print("# %s\n", getFormattedSetupEntry(helpanim_config[i].token,
5290 helpanim_config[i].value));
5292 if (strEqual(helpanim_config[i].token, "end"))
5298 else if (strEqual(command, "print helptext.conf"))
5300 Print("# You can configure different element help text here.\n");
5301 Print("# (The entries below are default and therefore commented out.)\n");
5304 for (i = 0; helptext_config[i].token != NULL; i++)
5305 Print("# %s\n", getFormattedSetupEntry(helptext_config[i].token,
5306 helptext_config[i].value));
5310 else if (strPrefix(command, "dump level "))
5312 char *filename = &command[11];
5314 if (fileExists(filename))
5316 LoadLevelFromFilename(&level, filename);
5322 char *leveldir = getStringCopy(filename); // read command parameters
5323 char *level_nr = strchr(leveldir, ' ');
5325 if (level_nr == NULL)
5326 Fail("cannot open file '%s'", filename);
5330 global.dumplevel_leveldir = leveldir;
5331 global.dumplevel_level_nr = atoi(level_nr);
5333 program.headless = TRUE;
5335 else if (strPrefix(command, "dump tape "))
5337 char *filename = &command[10];
5339 if (fileExists(filename))
5341 LoadTapeFromFilename(filename);
5347 char *leveldir = getStringCopy(filename); // read command parameters
5348 char *level_nr = strchr(leveldir, ' ');
5350 if (level_nr == NULL)
5351 Fail("cannot open file '%s'", filename);
5355 global.dumptape_leveldir = leveldir;
5356 global.dumptape_level_nr = atoi(level_nr);
5358 program.headless = TRUE;
5360 else if (strPrefix(command, "autoplay ") ||
5361 strPrefix(command, "autoffwd ") ||
5362 strPrefix(command, "autowarp ") ||
5363 strPrefix(command, "autotest ") ||
5364 strPrefix(command, "autosave ") ||
5365 strPrefix(command, "autoupload ") ||
5366 strPrefix(command, "autofix "))
5368 char *arg_ptr = strchr(command, ' ');
5369 char *str_ptr = getStringCopy(arg_ptr); // read command parameters
5371 global.autoplay_mode =
5372 (strPrefix(command, "autoplay") ? AUTOPLAY_MODE_PLAY :
5373 strPrefix(command, "autoffwd") ? AUTOPLAY_MODE_FFWD :
5374 strPrefix(command, "autowarp") ? AUTOPLAY_MODE_WARP :
5375 strPrefix(command, "autotest") ? AUTOPLAY_MODE_TEST :
5376 strPrefix(command, "autosave") ? AUTOPLAY_MODE_SAVE :
5377 strPrefix(command, "autoupload") ? AUTOPLAY_MODE_UPLOAD :
5378 strPrefix(command, "autofix") ? AUTOPLAY_MODE_FIX :
5379 AUTOPLAY_MODE_NONE);
5381 while (*str_ptr != '\0') // continue parsing string
5383 // cut leading whitespace from string, replace it by string terminator
5384 while (*str_ptr == ' ' || *str_ptr == '\t')
5387 if (*str_ptr == '\0') // end of string reached
5390 if (global.autoplay_leveldir == NULL) // read level set string
5392 global.autoplay_leveldir = str_ptr;
5393 global.autoplay_all = TRUE; // default: play all tapes
5395 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5396 global.autoplay_level[i] = FALSE;
5398 else // read level number string
5400 int level_nr = atoi(str_ptr); // get level_nr value
5402 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5403 global.autoplay_level[level_nr] = TRUE;
5405 global.autoplay_all = FALSE;
5408 // advance string pointer to the next whitespace (or end of string)
5409 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5413 if (global.autoplay_mode & AUTOPLAY_WARP_NO_DISPLAY)
5414 program.headless = TRUE;
5416 else if (strPrefix(command, "patch tapes "))
5418 char *str_ptr = getStringCopy(&command[12]); // read command parameters
5420 // skip leading whitespace
5421 while (*str_ptr == ' ' || *str_ptr == '\t')
5424 if (*str_ptr == '\0')
5425 Fail("cannot find MODE in command '%s'", command);
5427 global.patchtapes_mode = str_ptr; // store patch mode
5429 // advance to next whitespace (or end of string)
5430 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5433 while (*str_ptr != '\0') // continue parsing string
5435 // cut leading whitespace from string, replace it by string terminator
5436 while (*str_ptr == ' ' || *str_ptr == '\t')
5439 if (*str_ptr == '\0') // end of string reached
5442 if (global.patchtapes_leveldir == NULL) // read level set string
5444 global.patchtapes_leveldir = str_ptr;
5445 global.patchtapes_all = TRUE; // default: patch all tapes
5447 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5448 global.patchtapes_level[i] = FALSE;
5450 else // read level number string
5452 int level_nr = atoi(str_ptr); // get level_nr value
5454 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5455 global.patchtapes_level[level_nr] = TRUE;
5457 global.patchtapes_all = FALSE;
5460 // advance string pointer to the next whitespace (or end of string)
5461 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5465 if (global.patchtapes_leveldir == NULL)
5467 if (strEqual(global.patchtapes_mode, "help"))
5468 global.patchtapes_leveldir = UNDEFINED_LEVELSET;
5470 Fail("cannot find LEVELDIR in command '%s'", command);
5473 program.headless = TRUE;
5475 else if (strPrefix(command, "convert "))
5477 char *str_copy = getStringCopy(strchr(command, ' ') + 1);
5478 char *str_ptr = strchr(str_copy, ' ');
5480 global.convert_leveldir = str_copy;
5481 global.convert_level_nr = -1;
5483 if (str_ptr != NULL) // level number follows
5485 *str_ptr++ = '\0'; // terminate leveldir string
5486 global.convert_level_nr = atoi(str_ptr); // get level_nr value
5489 program.headless = TRUE;
5491 else if (strPrefix(command, "create sketch images "))
5493 global.create_sketch_images_dir = getStringCopy(&command[21]);
5495 if (access(global.create_sketch_images_dir, W_OK) != 0)
5496 Fail("image target directory '%s' not found or not writable",
5497 global.create_sketch_images_dir);
5499 else if (strPrefix(command, "create collect image "))
5501 global.create_collect_images_dir = getStringCopy(&command[21]);
5503 if (access(global.create_collect_images_dir, W_OK) != 0)
5504 Fail("image target directory '%s' not found or not writable",
5505 global.create_collect_images_dir);
5507 else if (strPrefix(command, "create CE image "))
5509 CreateCustomElementImages(&command[16]);
5515 FailWithHelp("unrecognized command '%s'", command);
5518 // disable networking if any valid command was recognized
5519 options.network = setup.network_mode = FALSE;
5522 static void InitSetup(void)
5524 LoadUserNames(); // global user names
5525 LoadUserSetup(); // global user number
5527 LoadSetup(); // global setup info
5529 // set some options from setup file
5531 if (setup.options.verbose)
5532 options.verbose = TRUE;
5534 if (setup.options.debug)
5535 options.debug = TRUE;
5537 if (!strEqual(setup.options.debug_mode, ARG_UNDEFINED_STRING))
5538 options.debug_mode = getStringCopy(setup.options.debug_mode);
5540 if (setup.debug.show_frames_per_second)
5541 global.show_frames_per_second = TRUE;
5544 static void InitGameInfo(void)
5546 game.restart_level = FALSE;
5547 game.request_active = FALSE;
5549 game.use_masked_elements_initial = FALSE;
5552 static void InitPlayerInfo(void)
5556 // choose default local player
5557 local_player = &stored_player[0];
5559 for (i = 0; i < MAX_PLAYERS; i++)
5561 stored_player[i].connected_locally = FALSE;
5562 stored_player[i].connected_network = FALSE;
5565 local_player->connected_locally = TRUE;
5568 static void InitArtworkInfo(void)
5573 static char *get_string_in_brackets(char *string)
5575 char *string_in_brackets = checked_malloc(strlen(string) + 3);
5577 sprintf(string_in_brackets, "[%s]", string);
5579 return string_in_brackets;
5582 static char *get_level_id_suffix(int id_nr)
5584 char *id_suffix = checked_malloc(1 + 3 + 1);
5586 if (id_nr < 0 || id_nr > 999)
5589 sprintf(id_suffix, ".%03d", id_nr);
5594 static void InitArtworkConfig(void)
5596 static char *image_id_prefix[MAX_NUM_ELEMENTS +
5598 NUM_GLOBAL_ANIM_TOKENS + 1];
5599 static char *sound_id_prefix[2 * MAX_NUM_ELEMENTS +
5600 NUM_GLOBAL_ANIM_TOKENS + 1];
5601 static char *music_id_prefix[NUM_MUSIC_PREFIXES +
5602 NUM_GLOBAL_ANIM_TOKENS + 1];
5603 static char *action_id_suffix[NUM_ACTIONS + 1];
5604 static char *direction_id_suffix[NUM_DIRECTIONS_FULL + 1];
5605 static char *special_id_suffix[NUM_SPECIAL_GFX_ARGS + 1];
5606 static char *level_id_suffix[MAX_LEVELS + 1];
5607 static char *dummy[1] = { NULL };
5608 static char *ignore_generic_tokens[] =
5613 "program_copyright",
5618 static char **ignore_image_tokens;
5619 static char **ignore_sound_tokens;
5620 static char **ignore_music_tokens;
5621 int num_ignore_generic_tokens;
5622 int num_ignore_image_tokens;
5623 int num_ignore_sound_tokens;
5624 int num_ignore_music_tokens;
5627 // dynamically determine list of generic tokens to be ignored
5628 num_ignore_generic_tokens = 0;
5629 for (i = 0; ignore_generic_tokens[i] != NULL; i++)
5630 num_ignore_generic_tokens++;
5632 // dynamically determine list of image tokens to be ignored
5633 num_ignore_image_tokens = num_ignore_generic_tokens;
5634 for (i = 0; image_config_vars[i].token != NULL; i++)
5635 num_ignore_image_tokens++;
5636 ignore_image_tokens =
5637 checked_malloc((num_ignore_image_tokens + 1) * sizeof(char *));
5638 for (i = 0; i < num_ignore_generic_tokens; i++)
5639 ignore_image_tokens[i] = ignore_generic_tokens[i];
5640 for (i = 0; i < num_ignore_image_tokens - num_ignore_generic_tokens; i++)
5641 ignore_image_tokens[num_ignore_generic_tokens + i] =
5642 image_config_vars[i].token;
5643 ignore_image_tokens[num_ignore_image_tokens] = NULL;
5645 // dynamically determine list of sound tokens to be ignored
5646 num_ignore_sound_tokens = num_ignore_generic_tokens;
5647 for (i = 0; sound_config_vars[i].token != NULL; i++)
5648 num_ignore_sound_tokens++;
5649 ignore_sound_tokens =
5650 checked_malloc((num_ignore_sound_tokens + 1) * sizeof(char *));
5651 for (i = 0; i < num_ignore_generic_tokens; i++)
5652 ignore_sound_tokens[i] = ignore_generic_tokens[i];
5653 for (i = 0; i < num_ignore_sound_tokens - num_ignore_generic_tokens; i++)
5654 ignore_sound_tokens[num_ignore_generic_tokens + i] =
5655 sound_config_vars[i].token;
5656 ignore_sound_tokens[num_ignore_sound_tokens] = NULL;
5658 // dynamically determine list of music tokens to be ignored
5659 num_ignore_music_tokens = num_ignore_generic_tokens;
5660 ignore_music_tokens =
5661 checked_malloc((num_ignore_music_tokens + 1) * sizeof(char *));
5662 for (i = 0; i < num_ignore_generic_tokens; i++)
5663 ignore_music_tokens[i] = ignore_generic_tokens[i];
5664 ignore_music_tokens[num_ignore_music_tokens] = NULL;
5666 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5667 image_id_prefix[i] = element_info[i].token_name;
5668 for (i = 0; i < NUM_FONTS; i++)
5669 image_id_prefix[MAX_NUM_ELEMENTS + i] = font_info[i].token_name;
5670 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5671 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + i] =
5672 global_anim_info[i].token_name;
5673 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5675 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5676 sound_id_prefix[i] = element_info[i].token_name;
5677 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5678 sound_id_prefix[MAX_NUM_ELEMENTS + i] =
5679 get_string_in_brackets(element_info[i].class_name);
5680 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5681 sound_id_prefix[2 * MAX_NUM_ELEMENTS + i] =
5682 global_anim_info[i].token_name;
5683 sound_id_prefix[2 * MAX_NUM_ELEMENTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5685 for (i = 0; i < NUM_MUSIC_PREFIXES; i++)
5686 music_id_prefix[i] = music_prefix_info[i].prefix;
5687 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5688 music_id_prefix[NUM_MUSIC_PREFIXES + i] =
5689 global_anim_info[i].token_name;
5690 music_id_prefix[NUM_MUSIC_PREFIXES + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5692 for (i = 0; i < NUM_ACTIONS; i++)
5693 action_id_suffix[i] = element_action_info[i].suffix;
5694 action_id_suffix[NUM_ACTIONS] = NULL;
5696 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
5697 direction_id_suffix[i] = element_direction_info[i].suffix;
5698 direction_id_suffix[NUM_DIRECTIONS_FULL] = NULL;
5700 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
5701 special_id_suffix[i] = special_suffix_info[i].suffix;
5702 special_id_suffix[NUM_SPECIAL_GFX_ARGS] = NULL;
5704 for (i = 0; i < MAX_LEVELS; i++)
5705 level_id_suffix[i] = get_level_id_suffix(i);
5706 level_id_suffix[MAX_LEVELS] = NULL;
5708 InitImageList(image_config, NUM_IMAGE_FILES, image_config_suffix,
5709 image_id_prefix, action_id_suffix, direction_id_suffix,
5710 special_id_suffix, ignore_image_tokens);
5711 InitSoundList(sound_config, NUM_SOUND_FILES, sound_config_suffix,
5712 sound_id_prefix, action_id_suffix, dummy,
5713 special_id_suffix, ignore_sound_tokens);
5714 InitMusicList(music_config, NUM_MUSIC_FILES, music_config_suffix,
5715 music_id_prefix, action_id_suffix, special_id_suffix,
5716 level_id_suffix, ignore_music_tokens);
5719 static void InitMixer(void)
5726 static void InitVideoOverlay(void)
5728 // if virtual buttons are not loaded from setup file, repeat initializing
5729 // virtual buttons grid with default values now that video is initialized
5730 if (!setup.touch.grid_initialized)
5733 InitTileCursorInfo();
5737 void InitGfxBuffers(void)
5739 static int win_xsize_last = -1;
5740 static int win_ysize_last = -1;
5742 // create additional image buffers for double-buffering and cross-fading
5744 if (WIN_XSIZE != win_xsize_last || WIN_YSIZE != win_ysize_last)
5746 // used to temporarily store the backbuffer -- only re-create if changed
5747 ReCreateBitmap(&bitmap_db_store_1, WIN_XSIZE, WIN_YSIZE);
5748 ReCreateBitmap(&bitmap_db_store_2, WIN_XSIZE, WIN_YSIZE);
5750 win_xsize_last = WIN_XSIZE;
5751 win_ysize_last = WIN_YSIZE;
5754 ReCreateBitmap(&bitmap_db_field, FXSIZE, FYSIZE);
5755 ReCreateBitmap(&bitmap_db_door_1, 3 * DXSIZE, DYSIZE);
5756 ReCreateBitmap(&bitmap_db_door_2, 3 * VXSIZE, VYSIZE);
5758 // initialize screen properties
5759 InitGfxFieldInfo(SX, SY, SXSIZE, SYSIZE,
5760 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
5762 InitGfxDoor1Info(DX, DY, DXSIZE, DYSIZE);
5763 InitGfxDoor2Info(VX, VY, VXSIZE, VYSIZE);
5764 InitGfxDoor3Info(EX, EY, EXSIZE, EYSIZE);
5765 InitGfxWindowInfo(WIN_XSIZE, WIN_YSIZE);
5766 InitGfxScrollbufferInfo(FXSIZE, FYSIZE);
5767 InitGfxClipRegion(FALSE, -1, -1, -1, -1);
5769 // required if door size definitions have changed
5770 InitGraphicCompatibilityInfo_Doors();
5772 InitGfxBuffers_BD();
5773 InitGfxBuffers_EM();
5774 InitGfxBuffers_SP();
5775 InitGfxBuffers_MM();
5778 static void InitGfx(void)
5780 struct GraphicInfo *graphic_info_last = graphic_info;
5781 char *filename_font_initial = NULL;
5782 char *filename_image_initial[NUM_INITIAL_IMAGES] = { NULL };
5783 char *image_token[NUM_INITIAL_IMAGES] =
5785 CONFIG_TOKEN_GLOBAL_BUSY_INITIAL,
5786 CONFIG_TOKEN_GLOBAL_BUSY,
5787 CONFIG_TOKEN_GLOBAL_BUSY_PLAYFIELD,
5788 CONFIG_TOKEN_BACKGROUND,
5789 CONFIG_TOKEN_BACKGROUND_LOADING_INITIAL,
5790 CONFIG_TOKEN_BACKGROUND_LOADING
5792 struct MenuPosInfo *init_busy[NUM_INITIAL_IMAGES_BUSY] =
5796 &init.busy_playfield
5798 Bitmap *bitmap_font_initial = NULL;
5799 int parameter[NUM_INITIAL_IMAGES][NUM_GFX_ARGS];
5802 // determine settings for initial font (for displaying startup messages)
5803 for (i = 0; image_config[i].token != NULL; i++)
5805 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5807 char font_token[128];
5810 sprintf(font_token, "%s_%d", CONFIG_TOKEN_FONT_INITIAL, j + 1);
5811 len_font_token = strlen(font_token);
5813 if (strEqual(image_config[i].token, font_token))
5815 filename_font_initial = image_config[i].value;
5817 else if (strlen(image_config[i].token) > len_font_token &&
5818 strncmp(image_config[i].token, font_token, len_font_token) == 0)
5820 if (strEqual(&image_config[i].token[len_font_token], ".x"))
5821 font_initial[j].src_x = atoi(image_config[i].value);
5822 else if (strEqual(&image_config[i].token[len_font_token], ".y"))
5823 font_initial[j].src_y = atoi(image_config[i].value);
5824 else if (strEqual(&image_config[i].token[len_font_token], ".width"))
5825 font_initial[j].width = atoi(image_config[i].value);
5826 else if (strEqual(&image_config[i].token[len_font_token], ".height"))
5827 font_initial[j].height = atoi(image_config[i].value);
5832 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5834 font_initial[j].num_chars = DEFAULT_NUM_CHARS_PER_FONT;
5835 font_initial[j].num_chars_per_line = DEFAULT_NUM_CHARS_PER_LINE;
5838 if (filename_font_initial == NULL) // should not happen
5839 Fail("cannot get filename for '%s'", CONFIG_TOKEN_FONT_INITIAL);
5842 InitGfxCustomArtworkInfo();
5843 InitGfxOtherSettings();
5845 InitGfxTileSizeInfo(TILESIZE, TILESIZE);
5847 bitmap_font_initial = LoadCustomImage(filename_font_initial);
5849 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5850 font_initial[j].bitmap = bitmap_font_initial;
5852 InitFontGraphicInfo();
5854 InitMenuDesignSettings_Static();
5856 // initialize settings for initial images with default values
5857 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5858 for (j = 0; j < NUM_GFX_ARGS; j++)
5860 get_graphic_parameter_value(image_config_suffix[j].value,
5861 image_config_suffix[j].token,
5862 image_config_suffix[j].type);
5864 // read settings for initial images from default custom artwork config
5865 char *gfx_config_filename = getPath3(options.graphics_directory,
5867 GRAPHICSINFO_FILENAME);
5869 if (fileExists(gfx_config_filename))
5871 SetupFileHash *setup_file_hash = loadSetupFileHash(gfx_config_filename);
5873 if (setup_file_hash)
5875 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5877 char *filename = getHashEntry(setup_file_hash, image_token[i]);
5881 filename_image_initial[i] = getStringCopy(filename);
5883 for (j = 0; image_config_suffix[j].token != NULL; j++)
5885 int type = image_config_suffix[j].type;
5886 char *suffix = image_config_suffix[j].token;
5887 char *token = getStringCat2(image_token[i], suffix);
5888 char *value = getHashEntry(setup_file_hash, token);
5890 checked_free(token);
5894 get_graphic_parameter_value(value, suffix, type);
5899 // read values from custom graphics config file
5900 InitMenuDesignSettings_FromHash(setup_file_hash, FALSE);
5902 freeSetupFileHash(setup_file_hash);
5906 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5908 if (filename_image_initial[i] == NULL)
5910 int len_token = strlen(image_token[i]);
5912 // read settings for initial images from static default artwork config
5913 for (j = 0; image_config[j].token != NULL; j++)
5915 if (strEqual(image_config[j].token, image_token[i]))
5917 filename_image_initial[i] = getStringCopy(image_config[j].value);
5919 else if (strlen(image_config[j].token) > len_token &&
5920 strncmp(image_config[j].token, image_token[i], len_token) == 0)
5922 for (k = 0; image_config_suffix[k].token != NULL; k++)
5924 if (strEqual(&image_config[j].token[len_token],
5925 image_config_suffix[k].token))
5927 get_graphic_parameter_value(image_config[j].value,
5928 image_config_suffix[k].token,
5929 image_config_suffix[k].type);
5936 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5938 if (filename_image_initial[i] == NULL) // should not happen
5939 Fail("cannot get filename for '%s'", image_token[i]);
5941 image_initial[i].bitmaps =
5942 checked_calloc(sizeof(Bitmap *) * NUM_IMG_BITMAP_POINTERS);
5944 if (!strEqual(filename_image_initial[i], UNDEFINED_FILENAME))
5945 image_initial[i].bitmaps[IMG_BITMAP_STANDARD] =
5946 LoadCustomImage(filename_image_initial[i]);
5948 checked_free(filename_image_initial[i]);
5951 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5952 image_initial[i].use_image_size = TRUE;
5954 graphic_info = image_initial; // graphic == 0 => image_initial
5956 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5957 set_graphic_parameters_ext(i, parameter[i], image_initial[i].bitmaps);
5959 graphic_info = graphic_info_last;
5961 for (i = 0; i < NUM_INITIAL_IMAGES_BUSY; i++)
5963 // set image size for busy animations
5964 init_busy[i]->width = image_initial[i].width;
5965 init_busy[i]->height = image_initial[i].height;
5968 SetLoadingBackgroundImage();
5970 ClearRectangleOnBackground(window, 0, 0, WIN_XSIZE, WIN_YSIZE);
5974 InitGfxDrawBusyAnimFunction(DrawInitAnim);
5975 InitGfxDrawGlobalAnimFunction(DrawGlobalAnimations);
5976 InitGfxDrawGlobalBorderFunction(DrawMaskedBorderToTarget);
5977 InitGfxDrawTileCursorFunction(DrawTileCursor);
5978 InitGfxDrawEnvelopeRequestFunction(DrawEnvelopeRequestToScreen);
5980 gfx.fade_border_source_status = global.border_status;
5981 gfx.fade_border_target_status = global.border_status;
5982 gfx.masked_border_bitmap_ptr = backbuffer;
5984 // use copy of busy animation to prevent change while reloading artwork
5988 static void InitGfxBackground(void)
5990 fieldbuffer = bitmap_db_field;
5991 SetDrawtoField(DRAW_TO_BACKBUFFER);
5993 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
5995 redraw_mask = REDRAW_ALL;
5998 static void InitSnd(void)
6000 InitSoundSettings_Static();
6002 // read settings for initial sounds from default custom artwork config
6003 char *snd_config_filename = getPath3(options.sounds_directory,
6005 SOUNDSINFO_FILENAME);
6007 if (fileExists(snd_config_filename))
6009 SetupFileHash *setup_file_hash = loadSetupFileHash(snd_config_filename);
6011 if (setup_file_hash)
6013 // read values from custom sounds config file
6014 InitSoundSettings_FromHash(setup_file_hash, FALSE);
6016 freeSetupFileHash(setup_file_hash);
6021 static void InitLevelInfo(void)
6023 LoadLevelInfo(); // global level info
6024 LoadLevelSetup_LastSeries(); // last played series info
6025 LoadLevelSetup_SeriesInfo(); // last played level info
6027 if (global.autoplay_leveldir &&
6028 global.autoplay_mode != AUTOPLAY_MODE_TEST)
6030 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
6031 global.autoplay_leveldir);
6032 if (leveldir_current == NULL)
6033 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
6036 SetLevelSetInfo(leveldir_current->identifier, level_nr);
6039 static void InitLevelArtworkInfo(void)
6041 LoadLevelArtworkInfo();
6044 static void InitImages(void)
6046 print_timestamp_init("InitImages");
6049 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
6050 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
6051 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
6052 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
6053 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
6054 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
6055 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
6056 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
6059 setLevelArtworkDir(artwork.gfx_first);
6062 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
6063 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
6064 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
6065 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
6066 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
6067 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
6068 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
6069 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
6073 Debug("init:InitImages", "InitImages for '%s' ['%s', '%s'] ['%s', '%s']",
6074 leveldir_current->identifier,
6075 artwork.gfx_current_identifier,
6076 artwork.gfx_current->identifier,
6077 leveldir_current->graphics_set,
6078 leveldir_current->graphics_path);
6081 UPDATE_BUSY_STATE();
6083 ReloadCustomImages();
6084 print_timestamp_time("ReloadCustomImages");
6086 UPDATE_BUSY_STATE();
6088 LoadCustomElementDescriptions();
6089 print_timestamp_time("LoadCustomElementDescriptions");
6091 UPDATE_BUSY_STATE();
6093 LoadMenuDesignSettings();
6094 print_timestamp_time("LoadMenuDesignSettings");
6096 UPDATE_BUSY_STATE();
6098 ReinitializeGraphics();
6099 print_timestamp_time("ReinitializeGraphics");
6101 LoadMenuDesignSettings_AfterGraphics();
6102 print_timestamp_time("LoadMenuDesignSettings_AfterGraphics");
6104 UPDATE_BUSY_STATE();
6106 print_timestamp_done("InitImages");
6109 static void InitSound(void)
6111 print_timestamp_init("InitSound");
6113 // set artwork path to send it to the sound server process
6114 setLevelArtworkDir(artwork.snd_first);
6116 InitReloadCustomSounds();
6117 print_timestamp_time("InitReloadCustomSounds");
6119 LoadSoundSettings();
6120 print_timestamp_time("LoadSoundSettings");
6122 ReinitializeSounds();
6123 print_timestamp_time("ReinitializeSounds");
6125 print_timestamp_done("InitSound");
6128 static void InitMusic(void)
6130 print_timestamp_init("InitMusic");
6132 // set artwork path to send it to the sound server process
6133 setLevelArtworkDir(artwork.mus_first);
6135 InitReloadCustomMusic();
6136 print_timestamp_time("InitReloadCustomMusic");
6138 ReinitializeMusic();
6139 print_timestamp_time("ReinitializeMusic");
6141 print_timestamp_done("InitMusic");
6144 static void InitArtworkDone(void)
6146 if (program.headless)
6149 InitGlobalAnimations();
6152 static void InitNetworkSettings(void)
6154 boolean network_enabled = (options.network || setup.network_mode);
6155 char *network_server = (options.server_host != NULL ? options.server_host :
6156 setup.network_server_hostname);
6158 if (strEqual(network_server, STR_NETWORK_AUTO_DETECT))
6159 network_server = NULL;
6161 InitNetworkInfo(network_enabled,
6165 options.server_port);
6168 void InitNetworkServer(void)
6170 if (!network.enabled || network.connected)
6173 LimitScreenUpdates(FALSE);
6175 if (game_status == GAME_MODE_LOADING)
6178 if (!ConnectToServer(network.server_host, network.server_port))
6180 network.enabled = FALSE;
6182 setup.network_mode = FALSE;
6186 SendToServer_ProtocolVersion();
6187 SendToServer_PlayerName(setup.player_name);
6188 SendToServer_NrWanted(setup.network_player_nr + 1);
6190 network.connected = TRUE;
6193 // short time to recognize result of network initialization
6194 if (game_status == GAME_MODE_LOADING)
6195 Delay_WithScreenUpdates(1000);
6198 static boolean CheckArtworkConfigForCustomElements(char *filename)
6200 SetupFileHash *setup_file_hash;
6201 boolean redefined_ce_found = FALSE;
6203 // !!! CACHE THIS BY USING HASH 'filename' => 'true/false' !!!
6205 if ((setup_file_hash = loadSetupFileHash(filename)) != NULL)
6207 BEGIN_HASH_ITERATION(setup_file_hash, itr)
6209 char *token = HASH_ITERATION_TOKEN(itr);
6211 if (strPrefix(token, "custom_"))
6213 redefined_ce_found = TRUE;
6218 END_HASH_ITERATION(setup_file_hash, itr)
6220 freeSetupFileHash(setup_file_hash);
6223 return redefined_ce_found;
6226 static boolean CheckArtworkTypeForRedefinedCustomElements(int type)
6228 char *filename_base, *filename_local;
6229 boolean redefined_ce_found = FALSE;
6231 setLevelArtworkDir(ARTWORK_FIRST_NODE(artwork, type));
6234 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6235 "leveldir_current->identifier == '%s'",
6236 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
6237 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6238 "leveldir_current->graphics_path == '%s'",
6239 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
6240 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6241 "leveldir_current->graphics_set == '%s'",
6242 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
6243 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6244 "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
6245 leveldir_current == NULL ? "[NULL]" :
6246 LEVELDIR_ARTWORK_SET(leveldir_current, type));
6249 // first look for special artwork configured in level series config
6250 filename_base = getCustomArtworkLevelConfigFilename(type);
6253 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6254 "filename_base == '%s'", filename_base);
6257 if (fileExists(filename_base))
6258 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_base);
6260 filename_local = getCustomArtworkConfigFilename(type);
6263 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6264 "filename_local == '%s'", filename_local);
6267 if (filename_local != NULL && !strEqual(filename_base, filename_local))
6268 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_local);
6271 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6272 "redefined_ce_found == %d", redefined_ce_found);
6275 return redefined_ce_found;
6278 static void InitOverrideArtwork(void)
6280 boolean redefined_ce_found = FALSE;
6282 // to check if this level set redefines any CEs, do not use overriding
6283 gfx.override_level_graphics = FALSE;
6284 gfx.override_level_sounds = FALSE;
6285 gfx.override_level_music = FALSE;
6287 // now check if this level set has definitions for custom elements
6288 if (setup.override_level_graphics == STATE_AUTO ||
6289 setup.override_level_sounds == STATE_AUTO ||
6290 setup.override_level_music == STATE_AUTO)
6291 redefined_ce_found =
6292 (CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_GRAPHICS) |
6293 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_SOUNDS) |
6294 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_MUSIC));
6297 Debug("init:InitOverrideArtwork", "redefined_ce_found == %d",
6298 redefined_ce_found);
6301 if (redefined_ce_found)
6303 // this level set has CE definitions: change "MODE_AUTO" to "FALSE"
6304 gfx.override_level_graphics = (setup.override_level_graphics == TRUE);
6305 gfx.override_level_sounds = (setup.override_level_sounds == TRUE);
6306 gfx.override_level_music = (setup.override_level_music == TRUE);
6310 // this level set has no CE definitions: change "MODE_AUTO" to "TRUE"
6311 gfx.override_level_graphics = (setup.override_level_graphics != FALSE);
6312 gfx.override_level_sounds = (setup.override_level_sounds != FALSE);
6313 gfx.override_level_music = (setup.override_level_music != FALSE);
6317 Debug("init:InitOverrideArtwork", "%d, %d, %d",
6318 gfx.override_level_graphics,
6319 gfx.override_level_sounds,
6320 gfx.override_level_music);
6324 static char *setNewArtworkIdentifier(int type)
6326 static char *last_leveldir_identifier[3] = { NULL, NULL, NULL };
6327 static char *last_artwork_identifier[3] = { NULL, NULL, NULL };
6328 static boolean last_override_level_artwork[3] = { FALSE, FALSE, FALSE };
6329 static boolean last_has_custom_artwork_set[3] = { FALSE, FALSE, FALSE };
6330 static boolean initialized[3] = { FALSE, FALSE, FALSE };
6331 TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type);
6332 boolean setup_override_artwork = GFX_OVERRIDE_ARTWORK(type);
6333 char *setup_artwork_set = SETUP_ARTWORK_SET(setup, type);
6334 char *leveldir_identifier = leveldir_current->identifier;
6335 // !!! setLevelArtworkDir() should be moved to an earlier stage !!!
6336 char *leveldir_artwork_set = setLevelArtworkDir(artwork_first_node);
6337 boolean has_level_artwork_set = (leveldir_artwork_set != NULL);
6338 TreeInfo *custom_artwork_set =
6339 getTreeInfoFromIdentifier(artwork_first_node, leveldir_identifier);
6340 boolean has_custom_artwork_set = (custom_artwork_set != NULL);
6341 char *artwork_current_identifier;
6342 char *artwork_new_identifier = NULL; // default: nothing has changed
6344 // leveldir_current may be invalid (level group, parent link)
6345 if (!validLevelSeries(leveldir_current))
6348 /* 1st step: determine artwork set to be activated in descending order:
6349 --------------------------------------------------------------------
6350 1. setup artwork (when configured to override everything else)
6351 2. artwork set configured in "levelinfo.conf" of current level set
6352 (artwork in level directory will have priority when loading later)
6353 3. artwork in level directory (stored in artwork sub-directory)
6354 4. setup artwork (currently configured in setup menu) */
6356 if (setup_override_artwork)
6357 artwork_current_identifier = setup_artwork_set;
6358 else if (has_level_artwork_set)
6359 artwork_current_identifier = leveldir_artwork_set;
6360 else if (has_custom_artwork_set)
6361 artwork_current_identifier = leveldir_identifier;
6363 artwork_current_identifier = setup_artwork_set;
6365 /* 2nd step: check if it is really needed to reload artwork set
6366 ------------------------------------------------------------ */
6368 // ---------- reload if level set and also artwork set has changed ----------
6369 if (last_leveldir_identifier[type] != leveldir_identifier &&
6370 (last_has_custom_artwork_set[type] || has_custom_artwork_set))
6371 artwork_new_identifier = artwork_current_identifier;
6373 last_leveldir_identifier[type] = leveldir_identifier;
6374 last_has_custom_artwork_set[type] = has_custom_artwork_set;
6376 // ---------- reload if "override artwork" setting has changed --------------
6377 if (last_override_level_artwork[type] != setup_override_artwork)
6378 artwork_new_identifier = artwork_current_identifier;
6380 last_override_level_artwork[type] = setup_override_artwork;
6382 // ---------- reload if current artwork identifier has changed --------------
6383 if (!strEqual(last_artwork_identifier[type], artwork_current_identifier))
6384 artwork_new_identifier = artwork_current_identifier;
6386 // (we cannot compare string pointers here, so copy string content itself)
6387 setString(&last_artwork_identifier[type], artwork_current_identifier);
6389 // ---------- set new artwork identifier ----------
6390 *(ARTWORK_CURRENT_IDENTIFIER_PTR(artwork, type)) = artwork_current_identifier;
6392 // ---------- do not reload directly after starting -------------------------
6393 if (!initialized[type])
6394 artwork_new_identifier = NULL;
6396 initialized[type] = TRUE;
6398 return artwork_new_identifier;
6401 static void InitArtworkIdentifier(void)
6403 setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6404 setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6405 setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6408 void ReloadCustomArtwork(int force_reload)
6410 int last_game_status = game_status; // save current game status
6411 char *gfx_new_identifier;
6412 char *snd_new_identifier;
6413 char *mus_new_identifier;
6414 boolean force_reload_gfx = (force_reload & (1 << ARTWORK_TYPE_GRAPHICS));
6415 boolean force_reload_snd = (force_reload & (1 << ARTWORK_TYPE_SOUNDS));
6416 boolean force_reload_mus = (force_reload & (1 << ARTWORK_TYPE_MUSIC));
6417 boolean reload_needed;
6419 InitOverrideArtwork();
6421 AdjustGraphicsForEMC();
6422 AdjustSoundsForEMC();
6424 gfx_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6425 snd_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6426 mus_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6428 reload_needed = (gfx_new_identifier != NULL || force_reload_gfx ||
6429 snd_new_identifier != NULL || force_reload_snd ||
6430 mus_new_identifier != NULL || force_reload_mus);
6435 print_timestamp_init("ReloadCustomArtwork");
6437 SetGameStatus(GAME_MODE_LOADING);
6439 FadeOut(REDRAW_ALL);
6441 SetLoadingBackgroundImage();
6443 ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6444 print_timestamp_time("ClearRectangleOnBackground");
6448 UPDATE_BUSY_STATE();
6450 InitMissingFileHash();
6452 if (gfx_new_identifier != NULL || force_reload_gfx)
6455 Debug("init:ReloadCustomArtwork",
6456 "RELOADING GRAPHICS '%s' -> '%s' ['%s', '%s']",
6457 artwork.gfx_current_identifier,
6459 artwork.gfx_current->identifier,
6460 leveldir_current->graphics_set);
6464 print_timestamp_time("InitImages");
6467 if (snd_new_identifier != NULL || force_reload_snd)
6470 print_timestamp_time("InitSound");
6473 if (mus_new_identifier != NULL || force_reload_mus)
6476 print_timestamp_time("InitMusic");
6481 SetGameStatus(last_game_status); // restore current game status
6483 FadeOut(REDRAW_ALL);
6485 RedrawGlobalBorder();
6487 // force redraw of (open or closed) door graphics
6488 SetDoorState(DOOR_OPEN_ALL);
6489 CloseDoor(DOOR_CLOSE_ALL | DOOR_NO_DELAY);
6491 FadeSetEnterScreen();
6492 FadeSkipNextFadeOut();
6494 print_timestamp_done("ReloadCustomArtwork");
6496 LimitScreenUpdates(FALSE);
6499 void KeyboardAutoRepeatOffUnlessAutoplay(void)
6501 if (global.autoplay_leveldir == NULL)
6502 KeyboardAutoRepeatOff();
6505 void DisplayExitMessage(char *format, va_list ap)
6507 // also check for initialized video (headless flag may be temporarily unset)
6508 if (program.headless || !video.initialized)
6511 // check if draw buffer and fonts for exit message are already available
6512 if (drawto == NULL || font_initial[NUM_INITIAL_FONTS - 1].bitmap == NULL)
6515 int font_1 = FC_RED;
6516 int font_2 = FC_YELLOW;
6517 int font_3 = FC_BLUE;
6518 int font_width = getFontWidth(font_2);
6519 int font_height = getFontHeight(font_2);
6522 int sxsize = WIN_XSIZE - 2 * sx;
6523 int sysize = WIN_YSIZE - 2 * sy;
6524 int line_length = sxsize / font_width;
6525 int max_lines = sysize / font_height;
6526 int num_lines_printed;
6530 gfx.sxsize = sxsize;
6531 gfx.sysize = sysize;
6535 ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6537 DrawTextSCentered(sy, font_1, "Fatal error:");
6538 sy += 3 * font_height;;
6541 DrawTextBufferVA(sx, sy, format, ap, font_2,
6542 line_length, line_length, max_lines,
6543 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6544 sy += (num_lines_printed + 3) * font_height;
6546 DrawTextSCentered(sy, font_1, "For details, see the following error file:");
6547 sy += 3 * font_height;
6550 DrawTextBuffer(sx, sy, program.log_filename, font_2,
6551 line_length, line_length, max_lines,
6552 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6554 DrawTextSCentered(SYSIZE - 20, font_3, "Press any key or button to exit");
6556 redraw_mask = REDRAW_ALL;
6558 // force drawing exit message even if screen updates are currently limited
6559 LimitScreenUpdates(FALSE);
6563 // deactivate toons and global animations on error message screen
6564 setup.global_animations = FALSE;
6566 WaitForEventToContinue();
6570 // ============================================================================
6572 // ============================================================================
6576 print_timestamp_init("OpenAll");
6578 SetGameStatus(GAME_MODE_LOADING);
6582 InitGlobal(); // initialize some global variables
6584 InitRND(NEW_RANDOMIZE);
6585 InitSimpleRandom(NEW_RANDOMIZE);
6586 InitBetterRandom(NEW_RANDOMIZE);
6588 InitMissingFileHash();
6590 print_timestamp_time("[init global stuff]");
6594 print_timestamp_time("[init setup/config stuff (1)]");
6596 if (options.execute_command)
6597 Execute_Command(options.execute_command);
6599 InitNetworkSettings();
6603 if (network.serveronly)
6605 #if defined(PLATFORM_UNIX)
6606 NetworkServer(network.server_port, TRUE);
6608 Warn("networking only supported in Unix version");
6611 exit(0); // never reached, server loops forever
6615 print_timestamp_time("[init setup/config stuff (2)]");
6617 print_timestamp_time("[init setup/config stuff (3)]");
6618 InitArtworkInfo(); // needed before loading gfx, sound & music
6619 print_timestamp_time("[init setup/config stuff (4)]");
6620 InitArtworkConfig(); // needed before forking sound child process
6621 print_timestamp_time("[init setup/config stuff (5)]");
6623 print_timestamp_time("[init setup/config stuff (6)]");
6627 print_timestamp_time("[init setup/config stuff]");
6629 InitVideoDefaults();
6631 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
6634 InitEventFilter(FilterMouseMotionEvents);
6636 print_timestamp_time("[init video stuff]");
6638 InitElementPropertiesStatic();
6639 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
6640 InitElementPropertiesGfxElement();
6642 print_timestamp_time("[init element properties stuff]");
6647 print_timestamp_time("InitGfx");
6650 print_timestamp_time("InitLevelInfo");
6652 InitLevelArtworkInfo();
6653 print_timestamp_time("InitLevelArtworkInfo");
6655 InitOverrideArtwork(); // needs to know current level directory
6656 print_timestamp_time("InitOverrideArtwork");
6658 InitArtworkIdentifier(); // needs to know current level directory
6659 print_timestamp_time("InitArtworkIdentifier");
6661 InitImages(); // needs to know current level directory
6662 print_timestamp_time("InitImages");
6664 InitSound(); // needs to know current level directory
6665 print_timestamp_time("InitSound");
6667 InitMusic(); // needs to know current level directory
6668 print_timestamp_time("InitMusic");
6672 InitGfxBackground();
6679 if (global.autoplay_leveldir)
6684 else if (global.patchtapes_leveldir)
6689 else if (global.convert_leveldir)
6694 else if (global.dumplevel_leveldir)
6699 else if (global.dumptape_leveldir)
6704 else if (global.create_sketch_images_dir)
6706 CreateLevelSketchImages();
6709 else if (global.create_collect_images_dir)
6711 CreateCollectElementImages();
6715 InitNetworkServer();
6717 SetGameStatus(GAME_MODE_MAIN);
6719 FadeSetEnterScreen();
6720 if (!(fading.fade_mode & FADE_TYPE_TRANSFORM))
6721 FadeSkipNextFadeOut();
6723 print_timestamp_time("[post-artwork]");
6725 print_timestamp_done("OpenAll");
6727 if (setup.ask_for_remaining_tapes)
6728 setup.ask_for_uploading_tapes = TRUE;
6733 Debug("internal:path", "SDL_GetBasePath() == '%s'",
6735 Debug("internal:path", "SDL_GetPrefPath() == '%s'",
6736 SDL_GetPrefPath("artsoft", "rocksndiamonds"));
6737 #if defined(PLATFORM_ANDROID)
6738 Debug("internal:path", "SDL_AndroidGetInternalStoragePath() == '%s'",
6739 SDL_AndroidGetInternalStoragePath());
6740 Debug("internal:path", "SDL_AndroidGetExternalStoragePath() == '%s'",
6741 SDL_AndroidGetExternalStoragePath());
6742 Debug("internal:path", "SDL_AndroidGetExternalStorageState() == '%s'",
6743 (SDL_AndroidGetExternalStorageState() &
6744 SDL_ANDROID_EXTERNAL_STORAGE_WRITE ? "writable" :
6745 SDL_AndroidGetExternalStorageState() &
6746 SDL_ANDROID_EXTERNAL_STORAGE_READ ? "readable" : "not available"));
6751 static boolean WaitForApiThreads(void)
6753 DelayCounter thread_delay = { 10000 };
6755 if (program.api_thread_count == 0)
6758 // deactivate global animations (not accessible in game state "loading")
6759 setup.global_animations = FALSE;
6761 // set game state to "loading" to be able to show busy animation
6762 SetGameStatus(GAME_MODE_LOADING);
6764 ResetDelayCounter(&thread_delay);
6766 // wait for threads to finish (and fail on timeout)
6767 while (program.api_thread_count > 0)
6769 if (DelayReached(&thread_delay))
6771 Error("failed waiting for threads - TIMEOUT");
6776 UPDATE_BUSY_STATE();
6784 void CloseAllAndExit(int exit_value)
6786 WaitForApiThreads();
6791 CloseAudio(); // called after freeing sounds (needed for SDL)
6800 // set a flag to tell the network server thread to quit and wait for it
6801 // using SDL_WaitThread()
6803 // Code used with SDL 1.2:
6804 // if (network.server_thread) // terminate network server
6805 // SDL_KillThread(network.server_thread);
6807 CloseVideoDisplay();
6808 ClosePlatformDependentStuff();
6810 if (exit_value != 0 && !options.execute_command)
6812 // fall back to default level set (current set may have caused an error)
6813 SaveLevelSetup_LastSeries_Deactivate();
6815 // tell user where to find error log file which may contain more details
6816 // (error notification now directly displayed on screen inside R'n'D
6817 // NotifyUserAboutErrorFile(); // currently only works for Windows