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_BD_NATIVE_ELEMENT(i) && element_info[EL_BD_NATIVE_DEFAULT].graphic[act] != -1)
1159 default_action_graphic = element_info[EL_BD_NATIVE_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_BD_NATIVE_ELEMENT(i) && element_info[EL_BD_NATIVE_DEFAULT].crumbled[act] != -1)
1170 default_action_crumbled = element_info[EL_BD_NATIVE_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_BD_NATIVE_ELEMENT(i) && element_info[EL_BD_NATIVE_DEFAULT].sound[act] != -1)
2174 default_action_sound = element_info[EL_BD_NATIVE_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,
4325 static int ep_can_turn_each_move[] =
4327 // !!! do something with this one !!!
4331 static int ep_can_grow[] =
4345 static int ep_active_bomb[] =
4348 EL_EM_DYNAMITE_ACTIVE,
4349 EL_DYNABOMB_PLAYER_1_ACTIVE,
4350 EL_DYNABOMB_PLAYER_2_ACTIVE,
4351 EL_DYNABOMB_PLAYER_3_ACTIVE,
4352 EL_DYNABOMB_PLAYER_4_ACTIVE,
4353 EL_SP_DISK_RED_ACTIVE,
4358 static int ep_inactive[] =
4384 EL_QUICKSAND_FAST_EMPTY,
4407 EL_GATE_1_GRAY_ACTIVE,
4408 EL_GATE_2_GRAY_ACTIVE,
4409 EL_GATE_3_GRAY_ACTIVE,
4410 EL_GATE_4_GRAY_ACTIVE,
4419 EL_EM_GATE_1_GRAY_ACTIVE,
4420 EL_EM_GATE_2_GRAY_ACTIVE,
4421 EL_EM_GATE_3_GRAY_ACTIVE,
4422 EL_EM_GATE_4_GRAY_ACTIVE,
4431 EL_EMC_GATE_5_GRAY_ACTIVE,
4432 EL_EMC_GATE_6_GRAY_ACTIVE,
4433 EL_EMC_GATE_7_GRAY_ACTIVE,
4434 EL_EMC_GATE_8_GRAY_ACTIVE,
4436 EL_DC_GATE_WHITE_GRAY,
4437 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4438 EL_DC_GATE_FAKE_GRAY,
4441 EL_INVISIBLE_STEELWALL,
4449 EL_WALL_EMERALD_YELLOW,
4450 EL_DYNABOMB_INCREASE_NUMBER,
4451 EL_DYNABOMB_INCREASE_SIZE,
4452 EL_DYNABOMB_INCREASE_POWER,
4456 EL_SOKOBAN_FIELD_EMPTY,
4457 EL_SOKOBAN_FIELD_FULL,
4458 EL_WALL_EMERALD_RED,
4459 EL_WALL_EMERALD_PURPLE,
4460 EL_ACID_POOL_TOPLEFT,
4461 EL_ACID_POOL_TOPRIGHT,
4462 EL_ACID_POOL_BOTTOMLEFT,
4463 EL_ACID_POOL_BOTTOM,
4464 EL_ACID_POOL_BOTTOMRIGHT,
4468 EL_BD_MAGIC_WALL_DEAD,
4470 EL_DC_MAGIC_WALL_DEAD,
4471 EL_AMOEBA_TO_DIAMOND,
4479 EL_SP_GRAVITY_PORT_RIGHT,
4480 EL_SP_GRAVITY_PORT_DOWN,
4481 EL_SP_GRAVITY_PORT_LEFT,
4482 EL_SP_GRAVITY_PORT_UP,
4483 EL_SP_PORT_HORIZONTAL,
4484 EL_SP_PORT_VERTICAL,
4495 EL_SP_HARDWARE_GRAY,
4496 EL_SP_HARDWARE_GREEN,
4497 EL_SP_HARDWARE_BLUE,
4499 EL_SP_HARDWARE_YELLOW,
4500 EL_SP_HARDWARE_BASE_1,
4501 EL_SP_HARDWARE_BASE_2,
4502 EL_SP_HARDWARE_BASE_3,
4503 EL_SP_HARDWARE_BASE_4,
4504 EL_SP_HARDWARE_BASE_5,
4505 EL_SP_HARDWARE_BASE_6,
4506 EL_SP_GRAVITY_ON_PORT_LEFT,
4507 EL_SP_GRAVITY_ON_PORT_RIGHT,
4508 EL_SP_GRAVITY_ON_PORT_UP,
4509 EL_SP_GRAVITY_ON_PORT_DOWN,
4510 EL_SP_GRAVITY_OFF_PORT_LEFT,
4511 EL_SP_GRAVITY_OFF_PORT_RIGHT,
4512 EL_SP_GRAVITY_OFF_PORT_UP,
4513 EL_SP_GRAVITY_OFF_PORT_DOWN,
4514 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4515 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4516 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4517 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4518 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4519 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4520 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4521 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4522 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4523 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4524 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4525 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4526 EL_SIGN_EXCLAMATION,
4527 EL_SIGN_RADIOACTIVITY,
4534 EL_SIGN_ENTRY_FORBIDDEN,
4535 EL_SIGN_EMERGENCY_EXIT,
4543 EL_DC_STEELWALL_1_LEFT,
4544 EL_DC_STEELWALL_1_RIGHT,
4545 EL_DC_STEELWALL_1_TOP,
4546 EL_DC_STEELWALL_1_BOTTOM,
4547 EL_DC_STEELWALL_1_HORIZONTAL,
4548 EL_DC_STEELWALL_1_VERTICAL,
4549 EL_DC_STEELWALL_1_TOPLEFT,
4550 EL_DC_STEELWALL_1_TOPRIGHT,
4551 EL_DC_STEELWALL_1_BOTTOMLEFT,
4552 EL_DC_STEELWALL_1_BOTTOMRIGHT,
4553 EL_DC_STEELWALL_1_TOPLEFT_2,
4554 EL_DC_STEELWALL_1_TOPRIGHT_2,
4555 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
4556 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
4557 EL_DC_STEELWALL_2_LEFT,
4558 EL_DC_STEELWALL_2_RIGHT,
4559 EL_DC_STEELWALL_2_TOP,
4560 EL_DC_STEELWALL_2_BOTTOM,
4561 EL_DC_STEELWALL_2_HORIZONTAL,
4562 EL_DC_STEELWALL_2_VERTICAL,
4563 EL_DC_STEELWALL_2_MIDDLE,
4564 EL_DC_STEELWALL_2_SINGLE,
4565 EL_STEELWALL_SLIPPERY,
4570 EL_EMC_WALL_SLIPPERY_1,
4571 EL_EMC_WALL_SLIPPERY_2,
4572 EL_EMC_WALL_SLIPPERY_3,
4573 EL_EMC_WALL_SLIPPERY_4,
4594 static int ep_em_slippery_wall[] =
4599 static int ep_gfx_crumbled[] =
4610 static int ep_editor_cascade_active[] =
4612 EL_INTERNAL_CASCADE_BD_ACTIVE,
4613 EL_INTERNAL_CASCADE_BD_NATIVE_ACTIVE,
4614 EL_INTERNAL_CASCADE_BD_EFFECTS_ACTIVE,
4615 EL_INTERNAL_CASCADE_EM_ACTIVE,
4616 EL_INTERNAL_CASCADE_EMC_ACTIVE,
4617 EL_INTERNAL_CASCADE_RND_ACTIVE,
4618 EL_INTERNAL_CASCADE_SB_ACTIVE,
4619 EL_INTERNAL_CASCADE_SP_ACTIVE,
4620 EL_INTERNAL_CASCADE_DC_ACTIVE,
4621 EL_INTERNAL_CASCADE_DX_ACTIVE,
4622 EL_INTERNAL_CASCADE_MM_ACTIVE,
4623 EL_INTERNAL_CASCADE_DF_ACTIVE,
4624 EL_INTERNAL_CASCADE_CHARS_ACTIVE,
4625 EL_INTERNAL_CASCADE_STEEL_CHARS_ACTIVE,
4626 EL_INTERNAL_CASCADE_CE_ACTIVE,
4627 EL_INTERNAL_CASCADE_GE_ACTIVE,
4628 EL_INTERNAL_CASCADE_ES_ACTIVE,
4629 EL_INTERNAL_CASCADE_REF_ACTIVE,
4630 EL_INTERNAL_CASCADE_USER_ACTIVE,
4631 EL_INTERNAL_CASCADE_DYNAMIC_ACTIVE,
4636 static int ep_editor_cascade_inactive[] =
4638 EL_INTERNAL_CASCADE_BD,
4639 EL_INTERNAL_CASCADE_BD_NATIVE,
4640 EL_INTERNAL_CASCADE_BD_EFFECTS,
4641 EL_INTERNAL_CASCADE_EM,
4642 EL_INTERNAL_CASCADE_EMC,
4643 EL_INTERNAL_CASCADE_RND,
4644 EL_INTERNAL_CASCADE_SB,
4645 EL_INTERNAL_CASCADE_SP,
4646 EL_INTERNAL_CASCADE_DC,
4647 EL_INTERNAL_CASCADE_DX,
4648 EL_INTERNAL_CASCADE_MM,
4649 EL_INTERNAL_CASCADE_DF,
4650 EL_INTERNAL_CASCADE_CHARS,
4651 EL_INTERNAL_CASCADE_STEEL_CHARS,
4652 EL_INTERNAL_CASCADE_CE,
4653 EL_INTERNAL_CASCADE_GE,
4654 EL_INTERNAL_CASCADE_ES,
4655 EL_INTERNAL_CASCADE_REF,
4656 EL_INTERNAL_CASCADE_USER,
4657 EL_INTERNAL_CASCADE_DYNAMIC,
4662 static int ep_obsolete[] =
4666 EL_EM_KEY_1_FILE_OBSOLETE,
4667 EL_EM_KEY_2_FILE_OBSOLETE,
4668 EL_EM_KEY_3_FILE_OBSOLETE,
4669 EL_EM_KEY_4_FILE_OBSOLETE,
4670 EL_ENVELOPE_OBSOLETE,
4679 } element_properties[] =
4681 { ep_diggable, EP_DIGGABLE },
4682 { ep_collectible_only, EP_COLLECTIBLE_ONLY },
4683 { ep_dont_run_into, EP_DONT_RUN_INTO },
4684 { ep_dont_collide_with, EP_DONT_COLLIDE_WITH },
4685 { ep_dont_touch, EP_DONT_TOUCH },
4686 { ep_indestructible, EP_INDESTRUCTIBLE },
4687 { ep_slippery, EP_SLIPPERY },
4688 { ep_can_change, EP_CAN_CHANGE },
4689 { ep_can_move, EP_CAN_MOVE },
4690 { ep_can_fall, EP_CAN_FALL },
4691 { ep_can_smash_player, EP_CAN_SMASH_PLAYER },
4692 { ep_can_smash_enemies, EP_CAN_SMASH_ENEMIES },
4693 { ep_can_smash_everything, EP_CAN_SMASH_EVERYTHING },
4694 { ep_explodes_by_fire, EP_EXPLODES_BY_FIRE },
4695 { ep_explodes_smashed, EP_EXPLODES_SMASHED },
4696 { ep_explodes_impact, EP_EXPLODES_IMPACT },
4697 { ep_walkable_over, EP_WALKABLE_OVER },
4698 { ep_walkable_inside, EP_WALKABLE_INSIDE },
4699 { ep_walkable_under, EP_WALKABLE_UNDER },
4700 { ep_passable_over, EP_PASSABLE_OVER },
4701 { ep_passable_inside, EP_PASSABLE_INSIDE },
4702 { ep_passable_under, EP_PASSABLE_UNDER },
4703 { ep_droppable, EP_DROPPABLE },
4704 { ep_explodes_1x1_old, EP_EXPLODES_1X1_OLD },
4705 { ep_pushable, EP_PUSHABLE },
4706 { ep_explodes_cross_old, EP_EXPLODES_CROSS_OLD },
4707 { ep_protected, EP_PROTECTED },
4708 { ep_throwable, EP_THROWABLE },
4709 { ep_can_explode, EP_CAN_EXPLODE },
4710 { ep_gravity_reachable, EP_GRAVITY_REACHABLE },
4712 { ep_empty_space, EP_EMPTY_SPACE },
4713 { ep_player, EP_PLAYER },
4714 { ep_can_pass_magic_wall, EP_CAN_PASS_MAGIC_WALL },
4715 { ep_can_pass_dc_magic_wall, EP_CAN_PASS_DC_MAGIC_WALL },
4716 { ep_switchable, EP_SWITCHABLE },
4717 { ep_bd_element, EP_BD_ELEMENT },
4718 { ep_sp_element, EP_SP_ELEMENT },
4719 { ep_sb_element, EP_SB_ELEMENT },
4721 { ep_food_dark_yamyam, EP_FOOD_DARK_YAMYAM },
4722 { ep_food_penguin, EP_FOOD_PENGUIN },
4723 { ep_food_pig, EP_FOOD_PIG },
4724 { ep_historic_wall, EP_HISTORIC_WALL },
4725 { ep_historic_solid, EP_HISTORIC_SOLID },
4726 { ep_classic_enemy, EP_CLASSIC_ENEMY },
4727 { ep_belt, EP_BELT },
4728 { ep_belt_active, EP_BELT_ACTIVE },
4729 { ep_belt_switch, EP_BELT_SWITCH },
4730 { ep_tube, EP_TUBE },
4731 { ep_acid_pool, EP_ACID_POOL },
4732 { ep_keygate, EP_KEYGATE },
4733 { ep_amoeboid, EP_AMOEBOID },
4734 { ep_amoebalive, EP_AMOEBALIVE },
4735 { ep_has_editor_content, EP_HAS_EDITOR_CONTENT },
4736 { ep_can_turn_each_move, EP_CAN_TURN_EACH_MOVE },
4737 { ep_can_grow, EP_CAN_GROW },
4738 { ep_active_bomb, EP_ACTIVE_BOMB },
4739 { ep_inactive, EP_INACTIVE },
4741 { ep_em_slippery_wall, EP_EM_SLIPPERY_WALL },
4743 { ep_gfx_crumbled, EP_GFX_CRUMBLED },
4745 { ep_editor_cascade_active, EP_EDITOR_CASCADE_ACTIVE },
4746 { ep_editor_cascade_inactive, EP_EDITOR_CASCADE_INACTIVE },
4748 { ep_obsolete, EP_OBSOLETE },
4755 // always start with reliable default values (element has no properties)
4756 // (but never initialize clipboard elements after the very first time)
4757 // (to be able to use clipboard elements between several levels)
4758 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4759 if (!IS_CLIPBOARD_ELEMENT(i) || !clipboard_elements_initialized)
4760 for (j = 0; j < NUM_ELEMENT_PROPERTIES; j++)
4761 SET_PROPERTY(i, j, FALSE);
4763 // set all base element properties from above array definitions
4764 for (i = 0; element_properties[i].elements != NULL; i++)
4765 for (j = 0; (element_properties[i].elements)[j] != -1; j++)
4766 SET_PROPERTY((element_properties[i].elements)[j],
4767 element_properties[i].property, TRUE);
4769 // copy properties to some elements that are only stored in level file
4770 for (i = 0; i < NUM_ELEMENT_PROPERTIES; i++)
4771 for (j = 0; copy_properties[j][0] != -1; j++)
4772 if (HAS_PROPERTY(copy_properties[j][0], i))
4773 for (k = 1; k <= 4; k++)
4774 SET_PROPERTY(copy_properties[j][k], i, TRUE);
4776 // set static element properties that are not listed in array definitions
4777 for (i = EL_STEEL_CHAR_START; i <= EL_STEEL_CHAR_END; i++)
4778 SET_PROPERTY(i, EP_INDESTRUCTIBLE, TRUE);
4780 clipboard_elements_initialized = TRUE;
4783 void InitElementPropertiesEngine(int engine_version)
4785 static int no_wall_properties[] =
4788 EP_COLLECTIBLE_ONLY,
4790 EP_DONT_COLLIDE_WITH,
4793 EP_CAN_SMASH_PLAYER,
4794 EP_CAN_SMASH_ENEMIES,
4795 EP_CAN_SMASH_EVERYTHING,
4800 EP_FOOD_DARK_YAMYAM,
4816 /* important: after initialization in InitElementPropertiesStatic(), the
4817 elements are not again initialized to a default value; therefore all
4818 changes have to make sure that they leave the element with a defined
4819 property (which means that conditional property changes must be set to
4820 a reliable default value before) */
4822 // resolve group elements
4823 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
4824 ResolveGroupElement(EL_GROUP_START + i);
4826 // set all special, combined or engine dependent element properties
4827 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4829 // do not change (already initialized) clipboard elements here
4830 if (IS_CLIPBOARD_ELEMENT(i))
4833 // ---------- INACTIVE ----------------------------------------------------
4834 SET_PROPERTY(i, EP_INACTIVE, ((i >= EL_CHAR_START &&
4835 i <= EL_CHAR_END) ||
4836 (i >= EL_STEEL_CHAR_START &&
4837 i <= EL_STEEL_CHAR_END)));
4839 // ---------- WALKABLE, PASSABLE, ACCESSIBLE ------------------------------
4840 SET_PROPERTY(i, EP_WALKABLE, (IS_WALKABLE_OVER(i) ||
4841 IS_WALKABLE_INSIDE(i) ||
4842 IS_WALKABLE_UNDER(i)));
4844 SET_PROPERTY(i, EP_PASSABLE, (IS_PASSABLE_OVER(i) ||
4845 IS_PASSABLE_INSIDE(i) ||
4846 IS_PASSABLE_UNDER(i)));
4848 SET_PROPERTY(i, EP_ACCESSIBLE_OVER, (IS_WALKABLE_OVER(i) ||
4849 IS_PASSABLE_OVER(i)));
4851 SET_PROPERTY(i, EP_ACCESSIBLE_INSIDE, (IS_WALKABLE_INSIDE(i) ||
4852 IS_PASSABLE_INSIDE(i)));
4854 SET_PROPERTY(i, EP_ACCESSIBLE_UNDER, (IS_WALKABLE_UNDER(i) ||
4855 IS_PASSABLE_UNDER(i)));
4857 SET_PROPERTY(i, EP_ACCESSIBLE, (IS_WALKABLE(i) ||
4860 // ---------- COLLECTIBLE -------------------------------------------------
4861 SET_PROPERTY(i, EP_COLLECTIBLE, (IS_COLLECTIBLE_ONLY(i) ||
4865 // ---------- SNAPPABLE ---------------------------------------------------
4866 SET_PROPERTY(i, EP_SNAPPABLE, (IS_DIGGABLE(i) ||
4867 IS_COLLECTIBLE(i) ||
4871 // ---------- WALL --------------------------------------------------------
4872 SET_PROPERTY(i, EP_WALL, TRUE); // default: element is wall
4874 for (j = 0; no_wall_properties[j] != -1; j++)
4875 if (HAS_PROPERTY(i, no_wall_properties[j]) ||
4876 i >= EL_FIRST_RUNTIME_UNREAL)
4877 SET_PROPERTY(i, EP_WALL, FALSE);
4879 if (IS_HISTORIC_WALL(i))
4880 SET_PROPERTY(i, EP_WALL, TRUE);
4882 // ---------- SOLID_FOR_PUSHING -------------------------------------------
4883 if (engine_version < VERSION_IDENT(2,2,0,0))
4884 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, IS_HISTORIC_SOLID(i));
4886 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, (!IS_WALKABLE(i) &&
4888 !IS_COLLECTIBLE(i)));
4890 // ---------- DRAGONFIRE_PROOF --------------------------------------------
4891 if (IS_HISTORIC_SOLID(i) || i == EL_EXPLOSION)
4892 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, TRUE);
4894 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, (IS_INDESTRUCTIBLE(i) &&
4897 // ---------- EXPLOSION_PROOF ---------------------------------------------
4899 SET_PROPERTY(i, EP_EXPLOSION_PROOF, TRUE);
4900 else if (engine_version < VERSION_IDENT(2,2,0,0))
4901 SET_PROPERTY(i, EP_EXPLOSION_PROOF, IS_INDESTRUCTIBLE(i));
4903 SET_PROPERTY(i, EP_EXPLOSION_PROOF, (IS_INDESTRUCTIBLE(i) &&
4907 if (IS_CUSTOM_ELEMENT(i))
4909 // these are additional properties which are initially false when set
4911 // ---------- DONT_COLLIDE_WITH / DONT_RUN_INTO -------------------------
4913 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH, TRUE);
4914 if (DONT_COLLIDE_WITH(i))
4915 SET_PROPERTY(i, EP_DONT_RUN_INTO, TRUE);
4917 // ---------- CAN_SMASH_ENEMIES / CAN_SMASH_PLAYER ----------------------
4918 if (CAN_SMASH_EVERYTHING(i))
4919 SET_PROPERTY(i, EP_CAN_SMASH_ENEMIES, TRUE);
4920 if (CAN_SMASH_ENEMIES(i))
4921 SET_PROPERTY(i, EP_CAN_SMASH_PLAYER, TRUE);
4924 // ---------- CAN_SMASH ---------------------------------------------------
4925 SET_PROPERTY(i, EP_CAN_SMASH, (CAN_SMASH_PLAYER(i) ||
4926 CAN_SMASH_ENEMIES(i) ||
4927 CAN_SMASH_EVERYTHING(i)));
4929 // ---------- CAN_EXPLODE_BY_FIRE -----------------------------------------
4930 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_FIRE, (CAN_EXPLODE(i) &&
4931 EXPLODES_BY_FIRE(i)));
4933 // ---------- CAN_EXPLODE_SMASHED -----------------------------------------
4934 SET_PROPERTY(i, EP_CAN_EXPLODE_SMASHED, (CAN_EXPLODE(i) &&
4935 EXPLODES_SMASHED(i)));
4937 // ---------- CAN_EXPLODE_IMPACT ------------------------------------------
4938 SET_PROPERTY(i, EP_CAN_EXPLODE_IMPACT, (CAN_EXPLODE(i) &&
4939 EXPLODES_IMPACT(i)));
4941 // ---------- CAN_EXPLODE_BY_DRAGONFIRE -----------------------------------
4942 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_DRAGONFIRE, CAN_EXPLODE_BY_FIRE(i));
4944 // ---------- CAN_EXPLODE_BY_EXPLOSION ------------------------------------
4945 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_EXPLOSION, (CAN_EXPLODE_BY_FIRE(i) ||
4946 i == EL_BLACK_ORB));
4948 // ---------- COULD_MOVE_INTO_ACID ----------------------------------------
4949 SET_PROPERTY(i, EP_COULD_MOVE_INTO_ACID, (IS_PLAYER_ELEMENT(i) ||
4951 IS_CUSTOM_ELEMENT(i)));
4953 // ---------- MAYBE_DONT_COLLIDE_WITH -------------------------------------
4954 SET_PROPERTY(i, EP_MAYBE_DONT_COLLIDE_WITH, (i == EL_SP_SNIKSNAK ||
4955 i == EL_SP_ELECTRON));
4957 // ---------- CAN_MOVE_INTO_ACID ------------------------------------------
4958 if (COULD_MOVE_INTO_ACID(i) && !IS_CUSTOM_ELEMENT(i))
4959 SET_PROPERTY(i, EP_CAN_MOVE_INTO_ACID,
4960 getMoveIntoAcidProperty(&level, i));
4962 // ---------- DONT_COLLIDE_WITH -------------------------------------------
4963 if (MAYBE_DONT_COLLIDE_WITH(i))
4964 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH,
4965 getDontCollideWithProperty(&level, i));
4967 // ---------- SP_PORT -----------------------------------------------------
4968 SET_PROPERTY(i, EP_SP_PORT, (IS_SP_ELEMENT(i) &&
4969 IS_PASSABLE_INSIDE(i)));
4971 // ---------- CAN_BE_CLONED_BY_ANDROID ------------------------------------
4972 for (j = 0; j < level.num_android_clone_elements; j++)
4973 SET_PROPERTY(i, EP_CAN_BE_CLONED_BY_ANDROID,
4975 IS_EQUAL_OR_IN_GROUP(i, level.android_clone_element[j])));
4977 // ---------- CAN_CHANGE --------------------------------------------------
4978 SET_PROPERTY(i, EP_CAN_CHANGE, FALSE); // default: cannot change
4979 for (j = 0; j < element_info[i].num_change_pages; j++)
4980 if (element_info[i].change_page[j].can_change)
4981 SET_PROPERTY(i, EP_CAN_CHANGE, TRUE);
4983 // ---------- HAS_ACTION --------------------------------------------------
4984 SET_PROPERTY(i, EP_HAS_ACTION, FALSE); // default: has no action
4985 for (j = 0; j < element_info[i].num_change_pages; j++)
4986 if (element_info[i].change_page[j].has_action)
4987 SET_PROPERTY(i, EP_HAS_ACTION, TRUE);
4989 // ---------- CAN_CHANGE_OR_HAS_ACTION ------------------------------------
4990 SET_PROPERTY(i, EP_CAN_CHANGE_OR_HAS_ACTION, (CAN_CHANGE(i) ||
4993 // ---------- GFX_CRUMBLED ------------------------------------------------
4994 SET_PROPERTY(i, EP_GFX_CRUMBLED,
4995 element_info[i].crumbled[ACTION_DEFAULT] !=
4996 element_info[i].graphic[ACTION_DEFAULT]);
4998 // ---------- EDITOR_CASCADE ----------------------------------------------
4999 SET_PROPERTY(i, EP_EDITOR_CASCADE, (IS_EDITOR_CASCADE_ACTIVE(i) ||
5000 IS_EDITOR_CASCADE_INACTIVE(i)));
5003 // dynamically adjust element properties according to game engine version
5005 static int ep_em_slippery_wall[] =
5010 EL_EXPANDABLE_WALL_HORIZONTAL,
5011 EL_EXPANDABLE_WALL_VERTICAL,
5012 EL_EXPANDABLE_WALL_ANY,
5013 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
5014 EL_EXPANDABLE_STEELWALL_VERTICAL,
5015 EL_EXPANDABLE_STEELWALL_ANY,
5016 EL_EXPANDABLE_STEELWALL_GROWING,
5020 static int ep_em_explodes_by_fire[] =
5023 EL_EM_DYNAMITE_ACTIVE,
5028 // special EM style gems behaviour
5029 for (i = 0; ep_em_slippery_wall[i] != -1; i++)
5030 SET_PROPERTY(ep_em_slippery_wall[i], EP_EM_SLIPPERY_WALL,
5031 level.em_slippery_gems);
5033 // "EL_EXPANDABLE_WALL_GROWING" wasn't slippery for EM gems in 2.0.1
5034 SET_PROPERTY(EL_EXPANDABLE_WALL_GROWING, EP_EM_SLIPPERY_WALL,
5035 (level.em_slippery_gems &&
5036 engine_version > VERSION_IDENT(2,0,1,0)));
5038 // special EM style explosion behaviour regarding chain reactions
5039 for (i = 0; ep_em_explodes_by_fire[i] != -1; i++)
5040 SET_PROPERTY(ep_em_explodes_by_fire[i], EP_EXPLODES_BY_FIRE,
5041 level.em_explodes_by_fire);
5044 // this is needed because some graphics depend on element properties
5045 if (game_status == GAME_MODE_PLAYING)
5046 InitElementGraphicInfo();
5049 void InitElementPropertiesGfxElement(void)
5053 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5055 struct ElementInfo *ei = &element_info[i];
5057 ei->gfx_element = (ei->use_gfx_element ? ei->gfx_element_initial : i);
5061 static void InitGlobal(void)
5066 for (i = 0; i < MAX_NUM_ELEMENTS + 1; i++)
5068 // check if element_name_info entry defined for each element in "main.h"
5069 if (i < MAX_NUM_ELEMENTS && element_name_info[i].token_name == NULL)
5070 Fail("undefined 'element_name_info' entry for element %d", i);
5072 element_info[i].token_name = element_name_info[i].token_name;
5073 element_info[i].class_name = element_name_info[i].class_name;
5074 element_info[i].editor_description = element_name_info[i].editor_description;
5077 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS + 1; i++)
5079 // check if global_anim_name_info defined for each entry in "main.h"
5080 if (i < NUM_GLOBAL_ANIM_TOKENS &&
5081 global_anim_name_info[i].token_name == NULL)
5082 Fail("undefined 'global_anim_name_info' entry for anim %d", i);
5084 global_anim_info[i].token_name = global_anim_name_info[i].token_name;
5087 // create hash to store URLs for global animations
5088 anim_url_hash = newSetupFileHash();
5090 // create hash from image config list
5091 image_config_hash = newSetupFileHash();
5092 for (i = 0; image_config[i].token != NULL; i++)
5093 setHashEntry(image_config_hash,
5094 image_config[i].token,
5095 image_config[i].value);
5097 // create hash from sound config list
5098 sound_config_hash = newSetupFileHash();
5099 for (i = 0; sound_config[i].token != NULL; i++)
5100 setHashEntry(sound_config_hash,
5101 sound_config[i].token,
5102 sound_config[i].value);
5104 // create hash from element token list
5105 element_token_hash = newSetupFileHash();
5106 for (i = 0; element_name_info[i].token_name != NULL; i++)
5107 setHashEntry(element_token_hash,
5108 element_name_info[i].token_name,
5111 // create hash from graphic token list
5112 graphic_token_hash = newSetupFileHash();
5113 for (graphic = 0, i = 0; image_config[i].token != NULL; i++)
5114 if (strSuffix(image_config[i].value, ".png") ||
5115 strSuffix(image_config[i].value, ".pcx") ||
5116 strSuffix(image_config[i].value, ".wav") ||
5117 strEqual(image_config[i].value, UNDEFINED_FILENAME))
5118 setHashEntry(graphic_token_hash,
5119 image_config[i].token,
5120 int2str(graphic++, 0));
5122 // create hash from font token list
5123 font_token_hash = newSetupFileHash();
5124 for (i = 0; font_info[i].token_name != NULL; i++)
5125 setHashEntry(font_token_hash,
5126 font_info[i].token_name,
5129 // set default filenames for all cloned graphics in static configuration
5130 for (i = 0; image_config[i].token != NULL; i++)
5132 if (strEqual(image_config[i].value, UNDEFINED_FILENAME))
5134 char *token = image_config[i].token;
5135 char *token_clone_from = getStringCat2(token, ".clone_from");
5136 char *token_cloned = getHashEntry(image_config_hash, token_clone_from);
5138 if (token_cloned != NULL)
5140 char *value_cloned = getHashEntry(image_config_hash, token_cloned);
5142 if (value_cloned != NULL)
5144 // set default filename in static configuration
5145 image_config[i].value = value_cloned;
5147 // set default filename in image config hash
5148 setHashEntry(image_config_hash, token, value_cloned);
5152 free(token_clone_from);
5156 // always start with reliable default values (all elements)
5157 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5158 ActiveElement[i] = i;
5160 // now add all entries that have an active state (active elements)
5161 for (i = 0; element_with_active_state[i].element != -1; i++)
5163 int element = element_with_active_state[i].element;
5164 int element_active = element_with_active_state[i].element_active;
5166 ActiveElement[element] = element_active;
5169 // always start with reliable default values (all buttons)
5170 for (i = 0; i < NUM_IMAGE_FILES; i++)
5171 ActiveButton[i] = i;
5173 // now add all entries that have an active state (active buttons)
5174 for (i = 0; button_with_active_state[i].button != -1; i++)
5176 int button = button_with_active_state[i].button;
5177 int button_active = button_with_active_state[i].button_active;
5179 ActiveButton[button] = button_active;
5182 // always start with reliable default values (all fonts)
5183 for (i = 0; i < NUM_FONTS; i++)
5186 // now add all entries that have an active state (active fonts)
5187 for (i = 0; font_with_active_state[i].font_nr != -1; i++)
5189 int font = font_with_active_state[i].font_nr;
5190 int font_active = font_with_active_state[i].font_nr_active;
5192 ActiveFont[font] = font_active;
5195 global.autoplay_leveldir = NULL;
5196 global.patchtapes_leveldir = NULL;
5197 global.convert_leveldir = NULL;
5198 global.dumplevel_leveldir = NULL;
5199 global.dumptape_leveldir = NULL;
5200 global.create_sketch_images_dir = NULL;
5201 global.create_collect_images_dir = NULL;
5203 global.frames_per_second = 0;
5204 global.show_frames_per_second = FALSE;
5206 global.border_status = GAME_MODE_LOADING;
5207 global.anim_status = global.anim_status_next = GAME_MODE_LOADING;
5209 global.use_envelope_request = FALSE;
5211 global.user_names = NULL;
5214 static void Execute_Command(char *command)
5218 if (strEqual(command, "print graphicsinfo.conf"))
5220 Print("# You can configure additional/alternative image files here.\n");
5221 Print("# (The entries below are default and therefore commented out.)\n");
5223 Print("%s\n", getFormattedSetupEntry("name", "Classic Graphics"));
5225 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5228 for (i = 0; image_config[i].token != NULL; i++)
5229 Print("# %s\n", getFormattedSetupEntry(image_config[i].token,
5230 image_config[i].value));
5234 else if (strEqual(command, "print soundsinfo.conf"))
5236 Print("# You can configure additional/alternative sound files here.\n");
5237 Print("# (The entries below are default and therefore commented out.)\n");
5239 Print("%s\n", getFormattedSetupEntry("name", "Classic Sounds"));
5241 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5244 for (i = 0; sound_config[i].token != NULL; i++)
5245 Print("# %s\n", getFormattedSetupEntry(sound_config[i].token,
5246 sound_config[i].value));
5250 else if (strEqual(command, "print musicinfo.conf"))
5252 Print("# You can configure additional/alternative music files here.\n");
5253 Print("# (The entries below are default and therefore commented out.)\n");
5255 Print("%s\n", getFormattedSetupEntry("name", "Classic Music"));
5257 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5260 for (i = 0; music_config[i].token != NULL; i++)
5261 Print("# %s\n", getFormattedSetupEntry(music_config[i].token,
5262 music_config[i].value));
5266 else if (strEqual(command, "print editorsetup.conf"))
5268 Print("# You can configure your personal editor element list here.\n");
5269 Print("# (The entries below are default and therefore commented out.)\n");
5272 // this is needed to be able to check element list for cascade elements
5273 InitElementPropertiesStatic();
5274 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
5276 PrintEditorElementList();
5280 else if (strEqual(command, "print helpanim.conf"))
5282 Print("# You can configure different element help animations here.\n");
5283 Print("# (The entries below are default and therefore commented out.)\n");
5286 for (i = 0; helpanim_config[i].token != NULL; i++)
5288 Print("# %s\n", getFormattedSetupEntry(helpanim_config[i].token,
5289 helpanim_config[i].value));
5291 if (strEqual(helpanim_config[i].token, "end"))
5297 else if (strEqual(command, "print helptext.conf"))
5299 Print("# You can configure different element help text here.\n");
5300 Print("# (The entries below are default and therefore commented out.)\n");
5303 for (i = 0; helptext_config[i].token != NULL; i++)
5304 Print("# %s\n", getFormattedSetupEntry(helptext_config[i].token,
5305 helptext_config[i].value));
5309 else if (strPrefix(command, "dump level "))
5311 char *filename = &command[11];
5313 if (fileExists(filename))
5315 LoadLevelFromFilename(&level, filename);
5321 char *leveldir = getStringCopy(filename); // read command parameters
5322 char *level_nr = strchr(leveldir, ' ');
5324 if (level_nr == NULL)
5325 Fail("cannot open file '%s'", filename);
5329 global.dumplevel_leveldir = leveldir;
5330 global.dumplevel_level_nr = atoi(level_nr);
5332 program.headless = TRUE;
5334 else if (strPrefix(command, "dump tape "))
5336 char *filename = &command[10];
5338 if (fileExists(filename))
5340 LoadTapeFromFilename(filename);
5346 char *leveldir = getStringCopy(filename); // read command parameters
5347 char *level_nr = strchr(leveldir, ' ');
5349 if (level_nr == NULL)
5350 Fail("cannot open file '%s'", filename);
5354 global.dumptape_leveldir = leveldir;
5355 global.dumptape_level_nr = atoi(level_nr);
5357 program.headless = TRUE;
5359 else if (strPrefix(command, "autoplay ") ||
5360 strPrefix(command, "autoffwd ") ||
5361 strPrefix(command, "autowarp ") ||
5362 strPrefix(command, "autotest ") ||
5363 strPrefix(command, "autosave ") ||
5364 strPrefix(command, "autoupload ") ||
5365 strPrefix(command, "autofix "))
5367 char *arg_ptr = strchr(command, ' ');
5368 char *str_ptr = getStringCopy(arg_ptr); // read command parameters
5370 global.autoplay_mode =
5371 (strPrefix(command, "autoplay") ? AUTOPLAY_MODE_PLAY :
5372 strPrefix(command, "autoffwd") ? AUTOPLAY_MODE_FFWD :
5373 strPrefix(command, "autowarp") ? AUTOPLAY_MODE_WARP :
5374 strPrefix(command, "autotest") ? AUTOPLAY_MODE_TEST :
5375 strPrefix(command, "autosave") ? AUTOPLAY_MODE_SAVE :
5376 strPrefix(command, "autoupload") ? AUTOPLAY_MODE_UPLOAD :
5377 strPrefix(command, "autofix") ? AUTOPLAY_MODE_FIX :
5378 AUTOPLAY_MODE_NONE);
5380 while (*str_ptr != '\0') // continue parsing string
5382 // cut leading whitespace from string, replace it by string terminator
5383 while (*str_ptr == ' ' || *str_ptr == '\t')
5386 if (*str_ptr == '\0') // end of string reached
5389 if (global.autoplay_leveldir == NULL) // read level set string
5391 global.autoplay_leveldir = str_ptr;
5392 global.autoplay_all = TRUE; // default: play all tapes
5394 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5395 global.autoplay_level[i] = FALSE;
5397 else // read level number string
5399 int level_nr = atoi(str_ptr); // get level_nr value
5401 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5402 global.autoplay_level[level_nr] = TRUE;
5404 global.autoplay_all = FALSE;
5407 // advance string pointer to the next whitespace (or end of string)
5408 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5412 if (global.autoplay_mode & AUTOPLAY_WARP_NO_DISPLAY)
5413 program.headless = TRUE;
5415 else if (strPrefix(command, "patch tapes "))
5417 char *str_ptr = getStringCopy(&command[12]); // read command parameters
5419 // skip leading whitespace
5420 while (*str_ptr == ' ' || *str_ptr == '\t')
5423 if (*str_ptr == '\0')
5424 Fail("cannot find MODE in command '%s'", command);
5426 global.patchtapes_mode = str_ptr; // store patch mode
5428 // advance to next whitespace (or end of string)
5429 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5432 while (*str_ptr != '\0') // continue parsing string
5434 // cut leading whitespace from string, replace it by string terminator
5435 while (*str_ptr == ' ' || *str_ptr == '\t')
5438 if (*str_ptr == '\0') // end of string reached
5441 if (global.patchtapes_leveldir == NULL) // read level set string
5443 global.patchtapes_leveldir = str_ptr;
5444 global.patchtapes_all = TRUE; // default: patch all tapes
5446 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5447 global.patchtapes_level[i] = FALSE;
5449 else // read level number string
5451 int level_nr = atoi(str_ptr); // get level_nr value
5453 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5454 global.patchtapes_level[level_nr] = TRUE;
5456 global.patchtapes_all = FALSE;
5459 // advance string pointer to the next whitespace (or end of string)
5460 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5464 if (global.patchtapes_leveldir == NULL)
5466 if (strEqual(global.patchtapes_mode, "help"))
5467 global.patchtapes_leveldir = UNDEFINED_LEVELSET;
5469 Fail("cannot find LEVELDIR in command '%s'", command);
5472 program.headless = TRUE;
5474 else if (strPrefix(command, "convert "))
5476 char *str_copy = getStringCopy(strchr(command, ' ') + 1);
5477 char *str_ptr = strchr(str_copy, ' ');
5479 global.convert_leveldir = str_copy;
5480 global.convert_level_nr = -1;
5482 if (str_ptr != NULL) // level number follows
5484 *str_ptr++ = '\0'; // terminate leveldir string
5485 global.convert_level_nr = atoi(str_ptr); // get level_nr value
5488 program.headless = TRUE;
5490 else if (strPrefix(command, "create sketch images "))
5492 global.create_sketch_images_dir = getStringCopy(&command[21]);
5494 if (access(global.create_sketch_images_dir, W_OK) != 0)
5495 Fail("image target directory '%s' not found or not writable",
5496 global.create_sketch_images_dir);
5498 else if (strPrefix(command, "create collect image "))
5500 global.create_collect_images_dir = getStringCopy(&command[21]);
5502 if (access(global.create_collect_images_dir, W_OK) != 0)
5503 Fail("image target directory '%s' not found or not writable",
5504 global.create_collect_images_dir);
5506 else if (strPrefix(command, "create CE image "))
5508 CreateCustomElementImages(&command[16]);
5514 FailWithHelp("unrecognized command '%s'", command);
5517 // disable networking if any valid command was recognized
5518 options.network = setup.network_mode = FALSE;
5521 static void InitSetup(void)
5523 LoadUserNames(); // global user names
5524 LoadUserSetup(); // global user number
5526 LoadSetup(); // global setup info
5528 // set some options from setup file
5530 if (setup.options.verbose)
5531 options.verbose = TRUE;
5533 if (setup.options.debug)
5534 options.debug = TRUE;
5536 if (!strEqual(setup.options.debug_mode, ARG_UNDEFINED_STRING))
5537 options.debug_mode = getStringCopy(setup.options.debug_mode);
5539 if (setup.debug.show_frames_per_second)
5540 global.show_frames_per_second = TRUE;
5543 static void InitGameInfo(void)
5545 game.restart_level = FALSE;
5546 game.request_active = FALSE;
5548 game.use_masked_elements_initial = FALSE;
5551 static void InitPlayerInfo(void)
5555 // choose default local player
5556 local_player = &stored_player[0];
5558 for (i = 0; i < MAX_PLAYERS; i++)
5560 stored_player[i].connected_locally = FALSE;
5561 stored_player[i].connected_network = FALSE;
5564 local_player->connected_locally = TRUE;
5567 static void InitArtworkInfo(void)
5572 static char *get_string_in_brackets(char *string)
5574 char *string_in_brackets = checked_malloc(strlen(string) + 3);
5576 sprintf(string_in_brackets, "[%s]", string);
5578 return string_in_brackets;
5581 static char *get_level_id_suffix(int id_nr)
5583 char *id_suffix = checked_malloc(1 + 3 + 1);
5585 if (id_nr < 0 || id_nr > 999)
5588 sprintf(id_suffix, ".%03d", id_nr);
5593 static void InitArtworkConfig(void)
5595 static char *image_id_prefix[MAX_NUM_ELEMENTS +
5597 NUM_GLOBAL_ANIM_TOKENS + 1];
5598 static char *sound_id_prefix[2 * MAX_NUM_ELEMENTS +
5599 NUM_GLOBAL_ANIM_TOKENS + 1];
5600 static char *music_id_prefix[NUM_MUSIC_PREFIXES +
5601 NUM_GLOBAL_ANIM_TOKENS + 1];
5602 static char *action_id_suffix[NUM_ACTIONS + 1];
5603 static char *direction_id_suffix[NUM_DIRECTIONS_FULL + 1];
5604 static char *special_id_suffix[NUM_SPECIAL_GFX_ARGS + 1];
5605 static char *level_id_suffix[MAX_LEVELS + 1];
5606 static char *dummy[1] = { NULL };
5607 static char *ignore_generic_tokens[] =
5612 "program_copyright",
5617 static char **ignore_image_tokens;
5618 static char **ignore_sound_tokens;
5619 static char **ignore_music_tokens;
5620 int num_ignore_generic_tokens;
5621 int num_ignore_image_tokens;
5622 int num_ignore_sound_tokens;
5623 int num_ignore_music_tokens;
5626 // dynamically determine list of generic tokens to be ignored
5627 num_ignore_generic_tokens = 0;
5628 for (i = 0; ignore_generic_tokens[i] != NULL; i++)
5629 num_ignore_generic_tokens++;
5631 // dynamically determine list of image tokens to be ignored
5632 num_ignore_image_tokens = num_ignore_generic_tokens;
5633 for (i = 0; image_config_vars[i].token != NULL; i++)
5634 num_ignore_image_tokens++;
5635 ignore_image_tokens =
5636 checked_malloc((num_ignore_image_tokens + 1) * sizeof(char *));
5637 for (i = 0; i < num_ignore_generic_tokens; i++)
5638 ignore_image_tokens[i] = ignore_generic_tokens[i];
5639 for (i = 0; i < num_ignore_image_tokens - num_ignore_generic_tokens; i++)
5640 ignore_image_tokens[num_ignore_generic_tokens + i] =
5641 image_config_vars[i].token;
5642 ignore_image_tokens[num_ignore_image_tokens] = NULL;
5644 // dynamically determine list of sound tokens to be ignored
5645 num_ignore_sound_tokens = num_ignore_generic_tokens;
5646 for (i = 0; sound_config_vars[i].token != NULL; i++)
5647 num_ignore_sound_tokens++;
5648 ignore_sound_tokens =
5649 checked_malloc((num_ignore_sound_tokens + 1) * sizeof(char *));
5650 for (i = 0; i < num_ignore_generic_tokens; i++)
5651 ignore_sound_tokens[i] = ignore_generic_tokens[i];
5652 for (i = 0; i < num_ignore_sound_tokens - num_ignore_generic_tokens; i++)
5653 ignore_sound_tokens[num_ignore_generic_tokens + i] =
5654 sound_config_vars[i].token;
5655 ignore_sound_tokens[num_ignore_sound_tokens] = NULL;
5657 // dynamically determine list of music tokens to be ignored
5658 num_ignore_music_tokens = num_ignore_generic_tokens;
5659 ignore_music_tokens =
5660 checked_malloc((num_ignore_music_tokens + 1) * sizeof(char *));
5661 for (i = 0; i < num_ignore_generic_tokens; i++)
5662 ignore_music_tokens[i] = ignore_generic_tokens[i];
5663 ignore_music_tokens[num_ignore_music_tokens] = NULL;
5665 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5666 image_id_prefix[i] = element_info[i].token_name;
5667 for (i = 0; i < NUM_FONTS; i++)
5668 image_id_prefix[MAX_NUM_ELEMENTS + i] = font_info[i].token_name;
5669 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5670 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + i] =
5671 global_anim_info[i].token_name;
5672 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5674 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5675 sound_id_prefix[i] = element_info[i].token_name;
5676 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5677 sound_id_prefix[MAX_NUM_ELEMENTS + i] =
5678 get_string_in_brackets(element_info[i].class_name);
5679 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5680 sound_id_prefix[2 * MAX_NUM_ELEMENTS + i] =
5681 global_anim_info[i].token_name;
5682 sound_id_prefix[2 * MAX_NUM_ELEMENTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5684 for (i = 0; i < NUM_MUSIC_PREFIXES; i++)
5685 music_id_prefix[i] = music_prefix_info[i].prefix;
5686 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5687 music_id_prefix[NUM_MUSIC_PREFIXES + i] =
5688 global_anim_info[i].token_name;
5689 music_id_prefix[NUM_MUSIC_PREFIXES + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5691 for (i = 0; i < NUM_ACTIONS; i++)
5692 action_id_suffix[i] = element_action_info[i].suffix;
5693 action_id_suffix[NUM_ACTIONS] = NULL;
5695 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
5696 direction_id_suffix[i] = element_direction_info[i].suffix;
5697 direction_id_suffix[NUM_DIRECTIONS_FULL] = NULL;
5699 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
5700 special_id_suffix[i] = special_suffix_info[i].suffix;
5701 special_id_suffix[NUM_SPECIAL_GFX_ARGS] = NULL;
5703 for (i = 0; i < MAX_LEVELS; i++)
5704 level_id_suffix[i] = get_level_id_suffix(i);
5705 level_id_suffix[MAX_LEVELS] = NULL;
5707 InitImageList(image_config, NUM_IMAGE_FILES, image_config_suffix,
5708 image_id_prefix, action_id_suffix, direction_id_suffix,
5709 special_id_suffix, ignore_image_tokens);
5710 InitSoundList(sound_config, NUM_SOUND_FILES, sound_config_suffix,
5711 sound_id_prefix, action_id_suffix, dummy,
5712 special_id_suffix, ignore_sound_tokens);
5713 InitMusicList(music_config, NUM_MUSIC_FILES, music_config_suffix,
5714 music_id_prefix, action_id_suffix, special_id_suffix,
5715 level_id_suffix, ignore_music_tokens);
5718 static void InitMixer(void)
5725 static void InitVideoOverlay(void)
5727 // if virtual buttons are not loaded from setup file, repeat initializing
5728 // virtual buttons grid with default values now that video is initialized
5729 if (!setup.touch.grid_initialized)
5732 InitTileCursorInfo();
5736 void InitGfxBuffers(void)
5738 static int win_xsize_last = -1;
5739 static int win_ysize_last = -1;
5741 // create additional image buffers for double-buffering and cross-fading
5743 if (WIN_XSIZE != win_xsize_last || WIN_YSIZE != win_ysize_last)
5745 // used to temporarily store the backbuffer -- only re-create if changed
5746 ReCreateBitmap(&bitmap_db_store_1, WIN_XSIZE, WIN_YSIZE);
5747 ReCreateBitmap(&bitmap_db_store_2, WIN_XSIZE, WIN_YSIZE);
5749 win_xsize_last = WIN_XSIZE;
5750 win_ysize_last = WIN_YSIZE;
5753 ReCreateBitmap(&bitmap_db_field, FXSIZE, FYSIZE);
5754 ReCreateBitmap(&bitmap_db_door_1, 3 * DXSIZE, DYSIZE);
5755 ReCreateBitmap(&bitmap_db_door_2, 3 * VXSIZE, VYSIZE);
5757 // initialize screen properties
5758 InitGfxFieldInfo(SX, SY, SXSIZE, SYSIZE,
5759 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
5761 InitGfxDoor1Info(DX, DY, DXSIZE, DYSIZE);
5762 InitGfxDoor2Info(VX, VY, VXSIZE, VYSIZE);
5763 InitGfxDoor3Info(EX, EY, EXSIZE, EYSIZE);
5764 InitGfxWindowInfo(WIN_XSIZE, WIN_YSIZE);
5765 InitGfxScrollbufferInfo(FXSIZE, FYSIZE);
5766 InitGfxClipRegion(FALSE, -1, -1, -1, -1);
5768 // required if door size definitions have changed
5769 InitGraphicCompatibilityInfo_Doors();
5771 InitGfxBuffers_BD();
5772 InitGfxBuffers_EM();
5773 InitGfxBuffers_SP();
5774 InitGfxBuffers_MM();
5777 static void InitGfx(void)
5779 struct GraphicInfo *graphic_info_last = graphic_info;
5780 char *filename_font_initial = NULL;
5781 char *filename_image_initial[NUM_INITIAL_IMAGES] = { NULL };
5782 char *image_token[NUM_INITIAL_IMAGES] =
5784 CONFIG_TOKEN_GLOBAL_BUSY_INITIAL,
5785 CONFIG_TOKEN_GLOBAL_BUSY,
5786 CONFIG_TOKEN_GLOBAL_BUSY_PLAYFIELD,
5787 CONFIG_TOKEN_BACKGROUND,
5788 CONFIG_TOKEN_BACKGROUND_LOADING_INITIAL,
5789 CONFIG_TOKEN_BACKGROUND_LOADING
5791 struct MenuPosInfo *init_busy[NUM_INITIAL_IMAGES_BUSY] =
5795 &init.busy_playfield
5797 Bitmap *bitmap_font_initial = NULL;
5798 int parameter[NUM_INITIAL_IMAGES][NUM_GFX_ARGS];
5801 // determine settings for initial font (for displaying startup messages)
5802 for (i = 0; image_config[i].token != NULL; i++)
5804 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5806 char font_token[128];
5809 sprintf(font_token, "%s_%d", CONFIG_TOKEN_FONT_INITIAL, j + 1);
5810 len_font_token = strlen(font_token);
5812 if (strEqual(image_config[i].token, font_token))
5814 filename_font_initial = image_config[i].value;
5816 else if (strlen(image_config[i].token) > len_font_token &&
5817 strncmp(image_config[i].token, font_token, len_font_token) == 0)
5819 if (strEqual(&image_config[i].token[len_font_token], ".x"))
5820 font_initial[j].src_x = atoi(image_config[i].value);
5821 else if (strEqual(&image_config[i].token[len_font_token], ".y"))
5822 font_initial[j].src_y = atoi(image_config[i].value);
5823 else if (strEqual(&image_config[i].token[len_font_token], ".width"))
5824 font_initial[j].width = atoi(image_config[i].value);
5825 else if (strEqual(&image_config[i].token[len_font_token], ".height"))
5826 font_initial[j].height = atoi(image_config[i].value);
5831 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5833 font_initial[j].num_chars = DEFAULT_NUM_CHARS_PER_FONT;
5834 font_initial[j].num_chars_per_line = DEFAULT_NUM_CHARS_PER_LINE;
5837 if (filename_font_initial == NULL) // should not happen
5838 Fail("cannot get filename for '%s'", CONFIG_TOKEN_FONT_INITIAL);
5841 InitGfxCustomArtworkInfo();
5842 InitGfxOtherSettings();
5844 InitGfxTileSizeInfo(TILESIZE, TILESIZE);
5846 bitmap_font_initial = LoadCustomImage(filename_font_initial);
5848 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5849 font_initial[j].bitmap = bitmap_font_initial;
5851 InitFontGraphicInfo();
5853 InitMenuDesignSettings_Static();
5855 // initialize settings for initial images with default values
5856 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5857 for (j = 0; j < NUM_GFX_ARGS; j++)
5859 get_graphic_parameter_value(image_config_suffix[j].value,
5860 image_config_suffix[j].token,
5861 image_config_suffix[j].type);
5863 // read settings for initial images from default custom artwork config
5864 char *gfx_config_filename = getPath3(options.graphics_directory,
5866 GRAPHICSINFO_FILENAME);
5868 if (fileExists(gfx_config_filename))
5870 SetupFileHash *setup_file_hash = loadSetupFileHash(gfx_config_filename);
5872 if (setup_file_hash)
5874 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5876 char *filename = getHashEntry(setup_file_hash, image_token[i]);
5880 filename_image_initial[i] = getStringCopy(filename);
5882 for (j = 0; image_config_suffix[j].token != NULL; j++)
5884 int type = image_config_suffix[j].type;
5885 char *suffix = image_config_suffix[j].token;
5886 char *token = getStringCat2(image_token[i], suffix);
5887 char *value = getHashEntry(setup_file_hash, token);
5889 checked_free(token);
5893 get_graphic_parameter_value(value, suffix, type);
5898 // read values from custom graphics config file
5899 InitMenuDesignSettings_FromHash(setup_file_hash, FALSE);
5901 freeSetupFileHash(setup_file_hash);
5905 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5907 if (filename_image_initial[i] == NULL)
5909 int len_token = strlen(image_token[i]);
5911 // read settings for initial images from static default artwork config
5912 for (j = 0; image_config[j].token != NULL; j++)
5914 if (strEqual(image_config[j].token, image_token[i]))
5916 filename_image_initial[i] = getStringCopy(image_config[j].value);
5918 else if (strlen(image_config[j].token) > len_token &&
5919 strncmp(image_config[j].token, image_token[i], len_token) == 0)
5921 for (k = 0; image_config_suffix[k].token != NULL; k++)
5923 if (strEqual(&image_config[j].token[len_token],
5924 image_config_suffix[k].token))
5926 get_graphic_parameter_value(image_config[j].value,
5927 image_config_suffix[k].token,
5928 image_config_suffix[k].type);
5935 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5937 if (filename_image_initial[i] == NULL) // should not happen
5938 Fail("cannot get filename for '%s'", image_token[i]);
5940 image_initial[i].bitmaps =
5941 checked_calloc(sizeof(Bitmap *) * NUM_IMG_BITMAP_POINTERS);
5943 if (!strEqual(filename_image_initial[i], UNDEFINED_FILENAME))
5944 image_initial[i].bitmaps[IMG_BITMAP_STANDARD] =
5945 LoadCustomImage(filename_image_initial[i]);
5947 checked_free(filename_image_initial[i]);
5950 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5951 image_initial[i].use_image_size = TRUE;
5953 graphic_info = image_initial; // graphic == 0 => image_initial
5955 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5956 set_graphic_parameters_ext(i, parameter[i], image_initial[i].bitmaps);
5958 graphic_info = graphic_info_last;
5960 for (i = 0; i < NUM_INITIAL_IMAGES_BUSY; i++)
5962 // set image size for busy animations
5963 init_busy[i]->width = image_initial[i].width;
5964 init_busy[i]->height = image_initial[i].height;
5967 SetLoadingBackgroundImage();
5969 ClearRectangleOnBackground(window, 0, 0, WIN_XSIZE, WIN_YSIZE);
5973 InitGfxDrawBusyAnimFunction(DrawInitAnim);
5974 InitGfxDrawGlobalAnimFunction(DrawGlobalAnimations);
5975 InitGfxDrawGlobalBorderFunction(DrawMaskedBorderToTarget);
5976 InitGfxDrawTileCursorFunction(DrawTileCursor);
5977 InitGfxDrawEnvelopeRequestFunction(DrawEnvelopeRequestToScreen);
5979 gfx.fade_border_source_status = global.border_status;
5980 gfx.fade_border_target_status = global.border_status;
5981 gfx.masked_border_bitmap_ptr = backbuffer;
5983 // use copy of busy animation to prevent change while reloading artwork
5987 static void InitGfxBackground(void)
5989 fieldbuffer = bitmap_db_field;
5990 SetDrawtoField(DRAW_TO_BACKBUFFER);
5992 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
5994 redraw_mask = REDRAW_ALL;
5997 static void InitSnd(void)
5999 InitSoundSettings_Static();
6001 // read settings for initial sounds from default custom artwork config
6002 char *snd_config_filename = getPath3(options.sounds_directory,
6004 SOUNDSINFO_FILENAME);
6006 if (fileExists(snd_config_filename))
6008 SetupFileHash *setup_file_hash = loadSetupFileHash(snd_config_filename);
6010 if (setup_file_hash)
6012 // read values from custom sounds config file
6013 InitSoundSettings_FromHash(setup_file_hash, FALSE);
6015 freeSetupFileHash(setup_file_hash);
6020 static void InitLevelInfo(void)
6022 LoadLevelInfo(); // global level info
6023 LoadLevelSetup_LastSeries(); // last played series info
6024 LoadLevelSetup_SeriesInfo(); // last played level info
6026 if (global.autoplay_leveldir &&
6027 global.autoplay_mode != AUTOPLAY_MODE_TEST)
6029 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
6030 global.autoplay_leveldir);
6031 if (leveldir_current == NULL)
6032 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
6035 SetLevelSetInfo(leveldir_current->identifier, level_nr);
6038 static void InitLevelArtworkInfo(void)
6040 LoadLevelArtworkInfo();
6043 static void InitImages(void)
6045 print_timestamp_init("InitImages");
6048 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
6049 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
6050 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
6051 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
6052 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
6053 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
6054 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
6055 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
6058 setLevelArtworkDir(artwork.gfx_first);
6061 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
6062 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
6063 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
6064 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
6065 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
6066 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
6067 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
6068 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
6072 Debug("init:InitImages", "InitImages for '%s' ['%s', '%s'] ['%s', '%s']",
6073 leveldir_current->identifier,
6074 artwork.gfx_current_identifier,
6075 artwork.gfx_current->identifier,
6076 leveldir_current->graphics_set,
6077 leveldir_current->graphics_path);
6080 UPDATE_BUSY_STATE();
6082 ReloadCustomImages();
6083 print_timestamp_time("ReloadCustomImages");
6085 UPDATE_BUSY_STATE();
6087 LoadCustomElementDescriptions();
6088 print_timestamp_time("LoadCustomElementDescriptions");
6090 UPDATE_BUSY_STATE();
6092 LoadMenuDesignSettings();
6093 print_timestamp_time("LoadMenuDesignSettings");
6095 UPDATE_BUSY_STATE();
6097 ReinitializeGraphics();
6098 print_timestamp_time("ReinitializeGraphics");
6100 LoadMenuDesignSettings_AfterGraphics();
6101 print_timestamp_time("LoadMenuDesignSettings_AfterGraphics");
6103 UPDATE_BUSY_STATE();
6105 print_timestamp_done("InitImages");
6108 static void InitSound(void)
6110 print_timestamp_init("InitSound");
6112 // set artwork path to send it to the sound server process
6113 setLevelArtworkDir(artwork.snd_first);
6115 InitReloadCustomSounds();
6116 print_timestamp_time("InitReloadCustomSounds");
6118 LoadSoundSettings();
6119 print_timestamp_time("LoadSoundSettings");
6121 ReinitializeSounds();
6122 print_timestamp_time("ReinitializeSounds");
6124 print_timestamp_done("InitSound");
6127 static void InitMusic(void)
6129 print_timestamp_init("InitMusic");
6131 // set artwork path to send it to the sound server process
6132 setLevelArtworkDir(artwork.mus_first);
6134 InitReloadCustomMusic();
6135 print_timestamp_time("InitReloadCustomMusic");
6137 ReinitializeMusic();
6138 print_timestamp_time("ReinitializeMusic");
6140 print_timestamp_done("InitMusic");
6143 static void InitArtworkDone(void)
6145 if (program.headless)
6148 InitGlobalAnimations();
6151 static void InitNetworkSettings(void)
6153 boolean network_enabled = (options.network || setup.network_mode);
6154 char *network_server = (options.server_host != NULL ? options.server_host :
6155 setup.network_server_hostname);
6157 if (strEqual(network_server, STR_NETWORK_AUTO_DETECT))
6158 network_server = NULL;
6160 InitNetworkInfo(network_enabled,
6164 options.server_port);
6167 void InitNetworkServer(void)
6169 if (!network.enabled || network.connected)
6172 LimitScreenUpdates(FALSE);
6174 if (game_status == GAME_MODE_LOADING)
6177 if (!ConnectToServer(network.server_host, network.server_port))
6179 network.enabled = FALSE;
6181 setup.network_mode = FALSE;
6185 SendToServer_ProtocolVersion();
6186 SendToServer_PlayerName(setup.player_name);
6187 SendToServer_NrWanted(setup.network_player_nr + 1);
6189 network.connected = TRUE;
6192 // short time to recognize result of network initialization
6193 if (game_status == GAME_MODE_LOADING)
6194 Delay_WithScreenUpdates(1000);
6197 static boolean CheckArtworkConfigForCustomElements(char *filename)
6199 SetupFileHash *setup_file_hash;
6200 boolean redefined_ce_found = FALSE;
6202 // !!! CACHE THIS BY USING HASH 'filename' => 'true/false' !!!
6204 if ((setup_file_hash = loadSetupFileHash(filename)) != NULL)
6206 BEGIN_HASH_ITERATION(setup_file_hash, itr)
6208 char *token = HASH_ITERATION_TOKEN(itr);
6210 if (strPrefix(token, "custom_"))
6212 redefined_ce_found = TRUE;
6217 END_HASH_ITERATION(setup_file_hash, itr)
6219 freeSetupFileHash(setup_file_hash);
6222 return redefined_ce_found;
6225 static boolean CheckArtworkTypeForRedefinedCustomElements(int type)
6227 char *filename_base, *filename_local;
6228 boolean redefined_ce_found = FALSE;
6230 setLevelArtworkDir(ARTWORK_FIRST_NODE(artwork, type));
6233 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6234 "leveldir_current->identifier == '%s'",
6235 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
6236 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6237 "leveldir_current->graphics_path == '%s'",
6238 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
6239 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6240 "leveldir_current->graphics_set == '%s'",
6241 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
6242 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6243 "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
6244 leveldir_current == NULL ? "[NULL]" :
6245 LEVELDIR_ARTWORK_SET(leveldir_current, type));
6248 // first look for special artwork configured in level series config
6249 filename_base = getCustomArtworkLevelConfigFilename(type);
6252 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6253 "filename_base == '%s'", filename_base);
6256 if (fileExists(filename_base))
6257 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_base);
6259 filename_local = getCustomArtworkConfigFilename(type);
6262 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6263 "filename_local == '%s'", filename_local);
6266 if (filename_local != NULL && !strEqual(filename_base, filename_local))
6267 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_local);
6270 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6271 "redefined_ce_found == %d", redefined_ce_found);
6274 return redefined_ce_found;
6277 static void InitOverrideArtwork(void)
6279 boolean redefined_ce_found = FALSE;
6281 // to check if this level set redefines any CEs, do not use overriding
6282 gfx.override_level_graphics = FALSE;
6283 gfx.override_level_sounds = FALSE;
6284 gfx.override_level_music = FALSE;
6286 // now check if this level set has definitions for custom elements
6287 if (setup.override_level_graphics == STATE_AUTO ||
6288 setup.override_level_sounds == STATE_AUTO ||
6289 setup.override_level_music == STATE_AUTO)
6290 redefined_ce_found =
6291 (CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_GRAPHICS) |
6292 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_SOUNDS) |
6293 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_MUSIC));
6296 Debug("init:InitOverrideArtwork", "redefined_ce_found == %d",
6297 redefined_ce_found);
6300 if (redefined_ce_found)
6302 // this level set has CE definitions: change "MODE_AUTO" to "FALSE"
6303 gfx.override_level_graphics = (setup.override_level_graphics == TRUE);
6304 gfx.override_level_sounds = (setup.override_level_sounds == TRUE);
6305 gfx.override_level_music = (setup.override_level_music == TRUE);
6309 // this level set has no CE definitions: change "MODE_AUTO" to "TRUE"
6310 gfx.override_level_graphics = (setup.override_level_graphics != FALSE);
6311 gfx.override_level_sounds = (setup.override_level_sounds != FALSE);
6312 gfx.override_level_music = (setup.override_level_music != FALSE);
6316 Debug("init:InitOverrideArtwork", "%d, %d, %d",
6317 gfx.override_level_graphics,
6318 gfx.override_level_sounds,
6319 gfx.override_level_music);
6323 static char *setNewArtworkIdentifier(int type)
6325 static char *last_leveldir_identifier[3] = { NULL, NULL, NULL };
6326 static char *last_artwork_identifier[3] = { NULL, NULL, NULL };
6327 static boolean last_override_level_artwork[3] = { FALSE, FALSE, FALSE };
6328 static boolean last_has_custom_artwork_set[3] = { FALSE, FALSE, FALSE };
6329 static boolean initialized[3] = { FALSE, FALSE, FALSE };
6330 TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type);
6331 boolean setup_override_artwork = GFX_OVERRIDE_ARTWORK(type);
6332 char *setup_artwork_set = SETUP_ARTWORK_SET(setup, type);
6333 char *leveldir_identifier = leveldir_current->identifier;
6334 // !!! setLevelArtworkDir() should be moved to an earlier stage !!!
6335 char *leveldir_artwork_set = setLevelArtworkDir(artwork_first_node);
6336 boolean has_level_artwork_set = (leveldir_artwork_set != NULL);
6337 TreeInfo *custom_artwork_set =
6338 getTreeInfoFromIdentifier(artwork_first_node, leveldir_identifier);
6339 boolean has_custom_artwork_set = (custom_artwork_set != NULL);
6340 char *artwork_current_identifier;
6341 char *artwork_new_identifier = NULL; // default: nothing has changed
6343 // leveldir_current may be invalid (level group, parent link)
6344 if (!validLevelSeries(leveldir_current))
6347 /* 1st step: determine artwork set to be activated in descending order:
6348 --------------------------------------------------------------------
6349 1. setup artwork (when configured to override everything else)
6350 2. artwork set configured in "levelinfo.conf" of current level set
6351 (artwork in level directory will have priority when loading later)
6352 3. artwork in level directory (stored in artwork sub-directory)
6353 4. setup artwork (currently configured in setup menu) */
6355 if (setup_override_artwork)
6356 artwork_current_identifier = setup_artwork_set;
6357 else if (has_level_artwork_set)
6358 artwork_current_identifier = leveldir_artwork_set;
6359 else if (has_custom_artwork_set)
6360 artwork_current_identifier = leveldir_identifier;
6362 artwork_current_identifier = setup_artwork_set;
6364 /* 2nd step: check if it is really needed to reload artwork set
6365 ------------------------------------------------------------ */
6367 // ---------- reload if level set and also artwork set has changed ----------
6368 if (last_leveldir_identifier[type] != leveldir_identifier &&
6369 (last_has_custom_artwork_set[type] || has_custom_artwork_set))
6370 artwork_new_identifier = artwork_current_identifier;
6372 last_leveldir_identifier[type] = leveldir_identifier;
6373 last_has_custom_artwork_set[type] = has_custom_artwork_set;
6375 // ---------- reload if "override artwork" setting has changed --------------
6376 if (last_override_level_artwork[type] != setup_override_artwork)
6377 artwork_new_identifier = artwork_current_identifier;
6379 last_override_level_artwork[type] = setup_override_artwork;
6381 // ---------- reload if current artwork identifier has changed --------------
6382 if (!strEqual(last_artwork_identifier[type], artwork_current_identifier))
6383 artwork_new_identifier = artwork_current_identifier;
6385 // (we cannot compare string pointers here, so copy string content itself)
6386 setString(&last_artwork_identifier[type], artwork_current_identifier);
6388 // ---------- set new artwork identifier ----------
6389 *(ARTWORK_CURRENT_IDENTIFIER_PTR(artwork, type)) = artwork_current_identifier;
6391 // ---------- do not reload directly after starting -------------------------
6392 if (!initialized[type])
6393 artwork_new_identifier = NULL;
6395 initialized[type] = TRUE;
6397 return artwork_new_identifier;
6400 static void InitArtworkIdentifier(void)
6402 setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6403 setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6404 setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6407 void ReloadCustomArtwork(int force_reload)
6409 int last_game_status = game_status; // save current game status
6410 char *gfx_new_identifier;
6411 char *snd_new_identifier;
6412 char *mus_new_identifier;
6413 boolean force_reload_gfx = (force_reload & (1 << ARTWORK_TYPE_GRAPHICS));
6414 boolean force_reload_snd = (force_reload & (1 << ARTWORK_TYPE_SOUNDS));
6415 boolean force_reload_mus = (force_reload & (1 << ARTWORK_TYPE_MUSIC));
6416 boolean reload_needed;
6418 InitOverrideArtwork();
6420 AdjustGraphicsForEMC();
6421 AdjustSoundsForEMC();
6423 gfx_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6424 snd_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6425 mus_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6427 reload_needed = (gfx_new_identifier != NULL || force_reload_gfx ||
6428 snd_new_identifier != NULL || force_reload_snd ||
6429 mus_new_identifier != NULL || force_reload_mus);
6434 print_timestamp_init("ReloadCustomArtwork");
6436 SetGameStatus(GAME_MODE_LOADING);
6438 FadeOut(REDRAW_ALL);
6440 SetLoadingBackgroundImage();
6442 ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6443 print_timestamp_time("ClearRectangleOnBackground");
6447 UPDATE_BUSY_STATE();
6449 InitMissingFileHash();
6451 if (gfx_new_identifier != NULL || force_reload_gfx)
6454 Debug("init:ReloadCustomArtwork",
6455 "RELOADING GRAPHICS '%s' -> '%s' ['%s', '%s']",
6456 artwork.gfx_current_identifier,
6458 artwork.gfx_current->identifier,
6459 leveldir_current->graphics_set);
6463 print_timestamp_time("InitImages");
6466 if (snd_new_identifier != NULL || force_reload_snd)
6469 print_timestamp_time("InitSound");
6472 if (mus_new_identifier != NULL || force_reload_mus)
6475 print_timestamp_time("InitMusic");
6480 SetGameStatus(last_game_status); // restore current game status
6482 FadeOut(REDRAW_ALL);
6484 RedrawGlobalBorder();
6486 // force redraw of (open or closed) door graphics
6487 SetDoorState(DOOR_OPEN_ALL);
6488 CloseDoor(DOOR_CLOSE_ALL | DOOR_NO_DELAY);
6490 FadeSetEnterScreen();
6491 FadeSkipNextFadeOut();
6493 print_timestamp_done("ReloadCustomArtwork");
6495 LimitScreenUpdates(FALSE);
6498 void KeyboardAutoRepeatOffUnlessAutoplay(void)
6500 if (global.autoplay_leveldir == NULL)
6501 KeyboardAutoRepeatOff();
6504 void DisplayExitMessage(char *format, va_list ap)
6506 // also check for initialized video (headless flag may be temporarily unset)
6507 if (program.headless || !video.initialized)
6510 // check if draw buffer and fonts for exit message are already available
6511 if (drawto == NULL || font_initial[NUM_INITIAL_FONTS - 1].bitmap == NULL)
6514 int font_1 = FC_RED;
6515 int font_2 = FC_YELLOW;
6516 int font_3 = FC_BLUE;
6517 int font_width = getFontWidth(font_2);
6518 int font_height = getFontHeight(font_2);
6521 int sxsize = WIN_XSIZE - 2 * sx;
6522 int sysize = WIN_YSIZE - 2 * sy;
6523 int line_length = sxsize / font_width;
6524 int max_lines = sysize / font_height;
6525 int num_lines_printed;
6529 gfx.sxsize = sxsize;
6530 gfx.sysize = sysize;
6534 ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6536 DrawTextSCentered(sy, font_1, "Fatal error:");
6537 sy += 3 * font_height;;
6540 DrawTextBufferVA(sx, sy, format, ap, font_2,
6541 line_length, line_length, max_lines,
6542 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6543 sy += (num_lines_printed + 3) * font_height;
6545 DrawTextSCentered(sy, font_1, "For details, see the following error file:");
6546 sy += 3 * font_height;
6549 DrawTextBuffer(sx, sy, program.log_filename, font_2,
6550 line_length, line_length, max_lines,
6551 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6553 DrawTextSCentered(SYSIZE - 20, font_3, "Press any key or button to exit");
6555 redraw_mask = REDRAW_ALL;
6557 // force drawing exit message even if screen updates are currently limited
6558 LimitScreenUpdates(FALSE);
6562 // deactivate toons and global animations on error message screen
6563 setup.global_animations = FALSE;
6565 WaitForEventToContinue();
6569 // ============================================================================
6571 // ============================================================================
6575 print_timestamp_init("OpenAll");
6577 SetGameStatus(GAME_MODE_LOADING);
6581 InitGlobal(); // initialize some global variables
6583 InitRND(NEW_RANDOMIZE);
6584 InitSimpleRandom(NEW_RANDOMIZE);
6585 InitBetterRandom(NEW_RANDOMIZE);
6587 InitMissingFileHash();
6589 print_timestamp_time("[init global stuff]");
6593 print_timestamp_time("[init setup/config stuff (1)]");
6595 if (options.execute_command)
6596 Execute_Command(options.execute_command);
6598 InitNetworkSettings();
6602 if (network.serveronly)
6604 #if defined(PLATFORM_UNIX)
6605 NetworkServer(network.server_port, TRUE);
6607 Warn("networking only supported in Unix version");
6610 exit(0); // never reached, server loops forever
6614 print_timestamp_time("[init setup/config stuff (2)]");
6616 print_timestamp_time("[init setup/config stuff (3)]");
6617 InitArtworkInfo(); // needed before loading gfx, sound & music
6618 print_timestamp_time("[init setup/config stuff (4)]");
6619 InitArtworkConfig(); // needed before forking sound child process
6620 print_timestamp_time("[init setup/config stuff (5)]");
6622 print_timestamp_time("[init setup/config stuff (6)]");
6626 print_timestamp_time("[init setup/config stuff]");
6628 InitVideoDefaults();
6630 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
6633 InitEventFilter(FilterMouseMotionEvents);
6635 print_timestamp_time("[init video stuff]");
6637 InitElementPropertiesStatic();
6638 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
6639 InitElementPropertiesGfxElement();
6641 print_timestamp_time("[init element properties stuff]");
6646 print_timestamp_time("InitGfx");
6649 print_timestamp_time("InitLevelInfo");
6651 InitLevelArtworkInfo();
6652 print_timestamp_time("InitLevelArtworkInfo");
6654 InitOverrideArtwork(); // needs to know current level directory
6655 print_timestamp_time("InitOverrideArtwork");
6657 InitArtworkIdentifier(); // needs to know current level directory
6658 print_timestamp_time("InitArtworkIdentifier");
6660 InitImages(); // needs to know current level directory
6661 print_timestamp_time("InitImages");
6663 InitSound(); // needs to know current level directory
6664 print_timestamp_time("InitSound");
6666 InitMusic(); // needs to know current level directory
6667 print_timestamp_time("InitMusic");
6671 InitGfxBackground();
6678 if (global.autoplay_leveldir)
6683 else if (global.patchtapes_leveldir)
6688 else if (global.convert_leveldir)
6693 else if (global.dumplevel_leveldir)
6698 else if (global.dumptape_leveldir)
6703 else if (global.create_sketch_images_dir)
6705 CreateLevelSketchImages();
6708 else if (global.create_collect_images_dir)
6710 CreateCollectElementImages();
6714 InitNetworkServer();
6716 SetGameStatus(GAME_MODE_MAIN);
6718 FadeSetEnterScreen();
6719 if (!(fading.fade_mode & FADE_TYPE_TRANSFORM))
6720 FadeSkipNextFadeOut();
6722 print_timestamp_time("[post-artwork]");
6724 print_timestamp_done("OpenAll");
6726 if (setup.ask_for_remaining_tapes)
6727 setup.ask_for_uploading_tapes = TRUE;
6732 Debug("internal:path", "SDL_GetBasePath() == '%s'",
6734 Debug("internal:path", "SDL_GetPrefPath() == '%s'",
6735 SDL_GetPrefPath("artsoft", "rocksndiamonds"));
6736 #if defined(PLATFORM_ANDROID)
6737 Debug("internal:path", "SDL_AndroidGetInternalStoragePath() == '%s'",
6738 SDL_AndroidGetInternalStoragePath());
6739 Debug("internal:path", "SDL_AndroidGetExternalStoragePath() == '%s'",
6740 SDL_AndroidGetExternalStoragePath());
6741 Debug("internal:path", "SDL_AndroidGetExternalStorageState() == '%s'",
6742 (SDL_AndroidGetExternalStorageState() &
6743 SDL_ANDROID_EXTERNAL_STORAGE_WRITE ? "writable" :
6744 SDL_AndroidGetExternalStorageState() &
6745 SDL_ANDROID_EXTERNAL_STORAGE_READ ? "readable" : "not available"));
6750 static boolean WaitForApiThreads(void)
6752 DelayCounter thread_delay = { 10000 };
6754 if (program.api_thread_count == 0)
6757 // deactivate global animations (not accessible in game state "loading")
6758 setup.global_animations = FALSE;
6760 // set game state to "loading" to be able to show busy animation
6761 SetGameStatus(GAME_MODE_LOADING);
6763 ResetDelayCounter(&thread_delay);
6765 // wait for threads to finish (and fail on timeout)
6766 while (program.api_thread_count > 0)
6768 if (DelayReached(&thread_delay))
6770 Error("failed waiting for threads - TIMEOUT");
6775 UPDATE_BUSY_STATE();
6783 void CloseAllAndExit(int exit_value)
6785 WaitForApiThreads();
6790 CloseAudio(); // called after freeing sounds (needed for SDL)
6799 // set a flag to tell the network server thread to quit and wait for it
6800 // using SDL_WaitThread()
6802 // Code used with SDL 1.2:
6803 // if (network.server_thread) // terminate network server
6804 // SDL_KillThread(network.server_thread);
6806 CloseVideoDisplay();
6807 ClosePlatformDependentStuff();
6809 if (exit_value != 0 && !options.execute_command)
6811 // fall back to default level set (current set may have caused an error)
6812 SaveLevelSetup_LastSeries_Deactivate();
6814 // tell user where to find error log file which may contain more details
6815 // (error notification now directly displayed on screen inside R'n'D
6816 // NotifyUserAboutErrorFile(); // currently only works for Windows