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 // create small and game tile sized bitmaps (and scale up, if needed)
248 CreateImageWithSmallImages(graphic, g->scale_up_factor, g->tile_size);
251 static void InitElementSmallImages(void)
253 print_timestamp_init("InitElementSmallImages");
255 static int special_graphics[] =
269 IMG_EDITOR_ELEMENT_BORDER,
270 IMG_EDITOR_ELEMENT_BORDER_INPUT,
271 IMG_EDITOR_CASCADE_LIST,
272 IMG_EDITOR_CASCADE_LIST_ACTIVE,
275 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
276 int num_property_mappings = getImageListPropertyMappingSize();
279 print_timestamp_time("getImageListPropertyMapping/Size");
281 print_timestamp_init("InitElementSmallImagesScaledUp (1)");
282 // initialize normal element images from static configuration
283 for (i = 0; element_to_graphic[i].element > -1; i++)
284 InitElementSmallImagesScaledUp(element_to_graphic[i].graphic);
285 print_timestamp_done("InitElementSmallImagesScaledUp (1)");
287 // initialize special element images from static configuration
288 for (i = 0; element_to_special_graphic[i].element > -1; i++)
289 InitElementSmallImagesScaledUp(element_to_special_graphic[i].graphic);
290 print_timestamp_time("InitElementSmallImagesScaledUp (2)");
292 // initialize element images from dynamic configuration
293 for (i = 0; i < num_property_mappings; i++)
294 if (property_mapping[i].base_index < MAX_NUM_ELEMENTS)
295 InitElementSmallImagesScaledUp(property_mapping[i].artwork_index);
296 print_timestamp_time("InitElementSmallImagesScaledUp (3)");
298 // initialize special non-element images from above list
299 for (i = 0; special_graphics[i] > -1; i++)
300 InitElementSmallImagesScaledUp(special_graphics[i]);
301 print_timestamp_time("InitElementSmallImagesScaledUp (4)");
303 print_timestamp_done("InitElementSmallImages");
306 static void InitScaledImagesScaledUp(int graphic)
308 struct GraphicInfo *g = &graphic_info[graphic];
310 ScaleImage(graphic, g->scale_up_factor);
313 static void InitScaledImages(void)
315 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
316 int num_property_mappings = getImageListPropertyMappingSize();
319 // scale normal images from static configuration, if not already scaled
320 for (i = 0; i < NUM_IMAGE_FILES; i++)
321 InitScaledImagesScaledUp(i);
323 // scale images from dynamic configuration, if not already scaled
324 for (i = 0; i < num_property_mappings; i++)
325 InitScaledImagesScaledUp(property_mapping[i].artwork_index);
328 static void InitBitmapPointers(void)
330 int num_images = getImageListSize();
333 // standard size bitmap may have changed -- update default bitmap pointer
334 for (i = 0; i < num_images; i++)
335 if (graphic_info[i].bitmaps)
336 graphic_info[i].bitmap = graphic_info[i].bitmaps[IMG_BITMAP_STANDARD];
339 void InitImageTextures(void)
341 static int texture_graphics[] =
343 IMG_GFX_REQUEST_BUTTON_TOUCH_YES,
344 IMG_GFX_REQUEST_BUTTON_TOUCH_NO,
345 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM,
346 IMG_GFX_GAME_BUTTON_TOUCH_STOP,
347 IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,
348 IMG_MENU_BUTTON_TOUCH_BACK,
349 IMG_MENU_BUTTON_TOUCH_NEXT,
350 IMG_MENU_BUTTON_TOUCH_BACK2,
351 IMG_MENU_BUTTON_TOUCH_NEXT2,
356 FreeAllImageTextures();
358 for (i = IMG_GLOBAL_BORDER_FIRST; i <= IMG_GLOBAL_BORDER_LAST; i++)
359 CreateImageTextures(i);
361 for (i = 0; i < MAX_NUM_TOONS; i++)
362 CreateImageTextures(IMG_TOON_1 + i);
364 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
366 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
368 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
370 int graphic = global_anim_info[i].graphic[j][k];
372 if (graphic == IMG_UNDEFINED)
375 CreateImageTextures(graphic);
380 for (i = 0; texture_graphics[i] > -1; i++)
381 CreateImageTextures(texture_graphics[i]);
384 static int getFontSpecialSuffix(void)
388 // (special case: do not use special font for GAME_MODE_LOADING)
389 if (game_status >= GAME_MODE_TITLE_INITIAL &&
390 game_status <= GAME_MODE_PSEUDO_PREVIEW)
391 special = game_status;
392 else if (game_status == GAME_MODE_PSEUDO_TYPENAME)
393 special = GFX_SPECIAL_ARG_MAIN;
394 else if (game_status == GAME_MODE_PSEUDO_TYPENAMES)
395 special = GFX_SPECIAL_ARG_NAMES;
400 static int getFontBitmapID(int font_nr)
402 int special = getFontSpecialSuffix();
405 return font_info[font_nr].special_bitmap_id[special];
410 static int getFontFromToken(char *token)
412 char *value = getHashEntry(font_token_hash, token);
417 // if font not found, use reliable default value
418 return FONT_INITIAL_1;
421 static char *getTokenFromFont(int font_nr)
423 static char *token = NULL;
424 int special = getFontSpecialSuffix();
429 token = getStringCat2(font_info[font_nr].token_name,
430 special_suffix_info[special].suffix);
432 token = getStringCopy(font_info[font_nr].token_name);
437 static void InitFontGraphicInfo(void)
439 static struct FontBitmapInfo *font_bitmap_info = NULL;
440 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
441 int num_property_mappings = getImageListPropertyMappingSize();
442 int num_font_bitmaps = NUM_FONTS;
445 if (graphic_info == NULL) // still at startup phase
447 InitFontInfo(font_initial, NUM_INITIAL_FONTS,
448 getFontBitmapID, getFontFromToken, getTokenFromFont);
453 // ---------- initialize font graphic definitions ----------
455 // always start with reliable default values (normal font graphics)
456 for (i = 0; i < NUM_FONTS; i++)
457 font_info[i].graphic = IMG_FONT_INITIAL_1;
459 // initialize normal font/graphic mapping from static configuration
460 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
462 int font_nr = font_to_graphic[i].font_nr;
463 int special = font_to_graphic[i].special;
464 int graphic = font_to_graphic[i].graphic;
469 font_info[font_nr].graphic = graphic;
472 // always start with reliable default values (special font graphics)
473 for (i = 0; i < NUM_FONTS; i++)
475 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
477 font_info[i].special_graphic[j] = font_info[i].graphic;
478 font_info[i].special_bitmap_id[j] = i;
482 // initialize special font/graphic mapping from static configuration
483 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
485 int font_nr = font_to_graphic[i].font_nr;
486 int special = font_to_graphic[i].special;
487 int graphic = font_to_graphic[i].graphic;
488 int base_graphic = font2baseimg(font_nr);
490 if (IS_SPECIAL_GFX_ARG(special))
492 boolean base_redefined =
493 getImageListEntryFromImageID(base_graphic)->redefined;
494 boolean special_redefined =
495 getImageListEntryFromImageID(graphic)->redefined;
496 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
498 /* if the base font ("font.title_1", for example) has been redefined,
499 but not the special font ("font.title_1.LEVELS", for example), do not
500 use an existing (in this case considered obsolete) special font
501 anymore, but use the automatically determined default font */
502 /* special case: cloned special fonts must be explicitly redefined,
503 but are not automatically redefined by redefining base font */
504 if (base_redefined && !special_redefined && !special_cloned)
507 font_info[font_nr].special_graphic[special] = graphic;
508 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
513 // initialize special font/graphic mapping from dynamic configuration
514 for (i = 0; i < num_property_mappings; i++)
516 int font_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
517 int special = property_mapping[i].ext3_index;
518 int graphic = property_mapping[i].artwork_index;
520 if (font_nr < 0 || font_nr >= NUM_FONTS)
523 if (IS_SPECIAL_GFX_ARG(special))
525 font_info[font_nr].special_graphic[special] = graphic;
526 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
531 /* correct special font/graphic mapping for cloned fonts for downwards
532 compatibility of PREVIEW fonts -- this is only needed for implicit
533 redefinition of special font by redefined base font, and only if other
534 fonts are cloned from this special font (like in the "Zelda" level set) */
535 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
537 int font_nr = font_to_graphic[i].font_nr;
538 int special = font_to_graphic[i].special;
539 int graphic = font_to_graphic[i].graphic;
541 if (IS_SPECIAL_GFX_ARG(special))
543 boolean special_redefined =
544 getImageListEntryFromImageID(graphic)->redefined;
545 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
547 if (special_cloned && !special_redefined)
551 for (j = 0; font_to_graphic[j].font_nr > -1; j++)
553 int font_nr2 = font_to_graphic[j].font_nr;
554 int special2 = font_to_graphic[j].special;
555 int graphic2 = font_to_graphic[j].graphic;
557 if (IS_SPECIAL_GFX_ARG(special2) &&
558 graphic2 == graphic_info[graphic].clone_from)
560 font_info[font_nr].special_graphic[special] =
561 font_info[font_nr2].special_graphic[special2];
562 font_info[font_nr].special_bitmap_id[special] =
563 font_info[font_nr2].special_bitmap_id[special2];
570 // reset non-redefined ".active" font graphics if normal font is redefined
571 // (this different treatment is needed because normal and active fonts are
572 // independently defined ("active" is not a property of font definitions!)
573 for (i = 0; i < NUM_FONTS; i++)
575 int font_nr_base = i;
576 int font_nr_active = FONT_ACTIVE(font_nr_base);
578 // check only those fonts with exist as normal and ".active" variant
579 if (font_nr_base != font_nr_active)
581 int base_graphic = font_info[font_nr_base].graphic;
582 int active_graphic = font_info[font_nr_active].graphic;
583 boolean base_redefined =
584 getImageListEntryFromImageID(base_graphic)->redefined;
585 boolean active_redefined =
586 getImageListEntryFromImageID(active_graphic)->redefined;
588 /* if the base font ("font.menu_1", for example) has been redefined,
589 but not the active font ("font.menu_1.active", for example), do not
590 use an existing (in this case considered obsolete) active font
591 anymore, but use the automatically determined default font */
592 if (base_redefined && !active_redefined)
593 font_info[font_nr_active].graphic = base_graphic;
595 // now also check each "special" font (which may be the same as above)
596 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
598 int base_graphic = font_info[font_nr_base].special_graphic[j];
599 int active_graphic = font_info[font_nr_active].special_graphic[j];
600 boolean base_redefined =
601 getImageListEntryFromImageID(base_graphic)->redefined;
602 boolean active_redefined =
603 getImageListEntryFromImageID(active_graphic)->redefined;
605 // same as above, but check special graphic definitions, for example:
606 // redefined "font.menu_1.MAIN" invalidates "font.menu_1.active.MAIN"
607 if (base_redefined && !active_redefined)
609 font_info[font_nr_active].special_graphic[j] =
610 font_info[font_nr_base].special_graphic[j];
611 font_info[font_nr_active].special_bitmap_id[j] =
612 font_info[font_nr_base].special_bitmap_id[j];
618 // ---------- initialize font bitmap array ----------
620 if (font_bitmap_info != NULL)
621 FreeFontInfo(font_bitmap_info);
624 checked_calloc(num_font_bitmaps * sizeof(struct FontBitmapInfo));
626 // ---------- initialize font bitmap definitions ----------
628 for (i = 0; i < NUM_FONTS; i++)
630 if (i < NUM_INITIAL_FONTS)
632 font_bitmap_info[i] = font_initial[i];
636 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
638 int font_bitmap_id = font_info[i].special_bitmap_id[j];
639 int graphic = font_info[i].special_graphic[j];
641 // set 'graphic_info' for font entries, if uninitialized (guessed)
642 if (graphic_info[graphic].anim_frames < MIN_NUM_CHARS_PER_FONT)
644 graphic_info[graphic].anim_frames = DEFAULT_NUM_CHARS_PER_FONT;
645 graphic_info[graphic].anim_frames_per_line = DEFAULT_NUM_CHARS_PER_LINE;
648 // copy font relevant information from graphics information
649 font_bitmap_info[font_bitmap_id].bitmap = graphic_info[graphic].bitmap;
650 font_bitmap_info[font_bitmap_id].src_x = graphic_info[graphic].src_x;
651 font_bitmap_info[font_bitmap_id].src_y = graphic_info[graphic].src_y;
652 font_bitmap_info[font_bitmap_id].width = graphic_info[graphic].width;
653 font_bitmap_info[font_bitmap_id].height = graphic_info[graphic].height;
655 font_bitmap_info[font_bitmap_id].offset_x =
656 graphic_info[graphic].offset_x;
657 font_bitmap_info[font_bitmap_id].offset_y =
658 graphic_info[graphic].offset_y;
660 font_bitmap_info[font_bitmap_id].draw_xoffset =
661 graphic_info[graphic].draw_xoffset;
662 font_bitmap_info[font_bitmap_id].draw_yoffset =
663 graphic_info[graphic].draw_yoffset;
665 font_bitmap_info[font_bitmap_id].num_chars =
666 graphic_info[graphic].anim_frames;
667 font_bitmap_info[font_bitmap_id].num_chars_per_line =
668 graphic_info[graphic].anim_frames_per_line;
672 InitFontInfo(font_bitmap_info, num_font_bitmaps,
673 getFontBitmapID, getFontFromToken, getTokenFromFont);
676 static void InitGlobalAnimGraphicInfo(void)
678 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
679 int num_property_mappings = getImageListPropertyMappingSize();
682 if (graphic_info == NULL) // still at startup phase
685 // always start with reliable default values (no global animations)
686 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
687 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
688 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
689 global_anim_info[i].graphic[j][k] = IMG_UNDEFINED;
691 // initialize global animation definitions from static configuration
692 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
694 int j = GLOBAL_ANIM_ID_PART_BASE;
695 int k = GFX_SPECIAL_ARG_DEFAULT;
697 global_anim_info[i].graphic[j][k] = IMG_GFX_GLOBAL_ANIM_1 + i;
700 // initialize global animation definitions from dynamic configuration
701 for (i = 0; i < num_property_mappings; i++)
703 int anim_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS - NUM_FONTS;
704 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
705 int special = property_mapping[i].ext3_index;
706 int graphic = property_mapping[i].artwork_index;
708 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
711 // set animation part to base part, if not specified
712 if (!IS_GLOBAL_ANIM_PART(part_nr))
713 part_nr = GLOBAL_ANIM_ID_PART_BASE;
715 // set animation screen to default, if not specified
716 if (!IS_SPECIAL_GFX_ARG(special))
717 special = GFX_SPECIAL_ARG_DEFAULT;
719 global_anim_info[anim_nr].graphic[part_nr][special] = graphic;
721 // fix default value for ".draw_masked" (for backward compatibility)
722 struct GraphicInfo *g = &graphic_info[graphic];
723 struct FileInfo *image = getImageListEntryFromImageID(graphic);
724 char **parameter_raw = image->parameter;
725 int p = GFX_ARG_DRAW_MASKED;
726 int draw_masked = get_graphic_parameter_value(parameter_raw[p],
727 image_config_suffix[p].token,
728 image_config_suffix[p].type);
730 // if ".draw_masked" parameter is undefined, use default value "TRUE"
731 if (draw_masked == ARG_UNDEFINED_VALUE)
732 g->draw_masked = TRUE;
736 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
737 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
738 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
739 if (global_anim_info[i].graphic[j][k] != IMG_UNDEFINED &&
740 graphic_info[global_anim_info[i].graphic[j][k]].bitmap != NULL)
741 Debug("init:InitGlobalAnimGraphicInfo",
742 "anim %d, part %d, mode %d => %d",
743 i, j, k, global_anim_info[i].graphic[j][k]);
747 static void InitGlobalAnimSoundInfo(void)
749 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
750 int num_property_mappings = getSoundListPropertyMappingSize();
753 // always start with reliable default values (no global animation sounds)
754 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
755 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
756 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
757 global_anim_info[i].sound[j][k] = SND_UNDEFINED;
759 // initialize global animation sound definitions from dynamic configuration
760 for (i = 0; i < num_property_mappings; i++)
762 int anim_nr = property_mapping[i].base_index - 2 * MAX_NUM_ELEMENTS;
763 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
764 int special = property_mapping[i].ext3_index;
765 int sound = property_mapping[i].artwork_index;
767 // sound uses control definition; map it to position of graphic (artwork)
768 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
770 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
773 // set animation part to base part, if not specified
774 if (!IS_GLOBAL_ANIM_PART(part_nr))
775 part_nr = GLOBAL_ANIM_ID_PART_BASE;
777 // set animation screen to default, if not specified
778 if (!IS_SPECIAL_GFX_ARG(special))
779 special = GFX_SPECIAL_ARG_DEFAULT;
781 global_anim_info[anim_nr].sound[part_nr][special] = sound;
785 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
786 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
787 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
788 if (global_anim_info[i].sound[j][k] != SND_UNDEFINED)
789 Debug("init:InitGlobalAnimSoundInfo",
790 "anim %d, part %d, mode %d => %d",
791 i, j, k, global_anim_info[i].sound[j][k]);
795 static void InitGlobalAnimMusicInfo(void)
797 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
798 int num_property_mappings = getMusicListPropertyMappingSize();
801 // always start with reliable default values (no global animation music)
802 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
803 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
804 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
805 global_anim_info[i].music[j][k] = MUS_UNDEFINED;
807 // initialize global animation music definitions from dynamic configuration
808 for (i = 0; i < num_property_mappings; i++)
810 int anim_nr = property_mapping[i].base_index - NUM_MUSIC_PREFIXES;
811 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
812 int special = property_mapping[i].ext2_index;
813 int music = property_mapping[i].artwork_index;
815 // music uses control definition; map it to position of graphic (artwork)
816 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
818 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
821 // set animation part to base part, if not specified
822 if (!IS_GLOBAL_ANIM_PART(part_nr))
823 part_nr = GLOBAL_ANIM_ID_PART_BASE;
825 // set animation screen to default, if not specified
826 if (!IS_SPECIAL_GFX_ARG(special))
827 special = GFX_SPECIAL_ARG_DEFAULT;
829 global_anim_info[anim_nr].music[part_nr][special] = music;
833 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
834 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
835 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
836 if (global_anim_info[i].music[j][k] != MUS_UNDEFINED)
837 Debug("init:InitGlobalAnimMusicInfo",
838 "anim %d, part %d, mode %d => %d",
839 i, j, k, global_anim_info[i].music[j][k]);
843 static void InitElementGraphicInfo(void)
845 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
846 int num_property_mappings = getImageListPropertyMappingSize();
849 if (graphic_info == NULL) // still at startup phase
852 // set values to -1 to identify later as "uninitialized" values
853 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
855 for (act = 0; act < NUM_ACTIONS; act++)
857 element_info[i].graphic[act] = -1;
858 element_info[i].crumbled[act] = -1;
860 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
862 element_info[i].direction_graphic[act][dir] = -1;
863 element_info[i].direction_crumbled[act][dir] = -1;
870 // initialize normal element/graphic mapping from static configuration
871 for (i = 0; element_to_graphic[i].element > -1; i++)
873 int element = element_to_graphic[i].element;
874 int action = element_to_graphic[i].action;
875 int direction = element_to_graphic[i].direction;
876 boolean crumbled = element_to_graphic[i].crumbled;
877 int graphic = element_to_graphic[i].graphic;
878 int base_graphic = el2baseimg(element);
880 if (graphic_info[graphic].bitmap == NULL)
883 if ((action > -1 || direction > -1 || crumbled == TRUE) &&
886 boolean base_redefined =
887 getImageListEntryFromImageID(base_graphic)->redefined;
888 boolean act_dir_redefined =
889 getImageListEntryFromImageID(graphic)->redefined;
891 /* if the base graphic ("emerald", for example) has been redefined,
892 but not the action graphic ("emerald.falling", for example), do not
893 use an existing (in this case considered obsolete) action graphic
894 anymore, but use the automatically determined default graphic */
895 if (base_redefined && !act_dir_redefined)
900 action = ACTION_DEFAULT;
905 element_info[element].direction_crumbled[action][direction] = graphic;
907 element_info[element].crumbled[action] = graphic;
912 element_info[element].direction_graphic[action][direction] = graphic;
914 element_info[element].graphic[action] = graphic;
918 // initialize normal element/graphic mapping from dynamic configuration
919 for (i = 0; i < num_property_mappings; i++)
921 int element = property_mapping[i].base_index;
922 int action = property_mapping[i].ext1_index;
923 int direction = property_mapping[i].ext2_index;
924 int special = property_mapping[i].ext3_index;
925 int graphic = property_mapping[i].artwork_index;
926 boolean crumbled = FALSE;
928 if (special == GFX_SPECIAL_ARG_CRUMBLED)
934 if (graphic_info[graphic].bitmap == NULL)
937 if (element >= MAX_NUM_ELEMENTS || special != -1)
941 action = ACTION_DEFAULT;
946 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
947 element_info[element].direction_crumbled[action][dir] = -1;
950 element_info[element].direction_crumbled[action][direction] = graphic;
952 element_info[element].crumbled[action] = graphic;
957 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
958 element_info[element].direction_graphic[action][dir] = -1;
961 element_info[element].direction_graphic[action][direction] = graphic;
963 element_info[element].graphic[action] = graphic;
967 // now copy all graphics that are defined to be cloned from other graphics
968 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
970 int graphic = element_info[i].graphic[ACTION_DEFAULT];
971 int crumbled_like, diggable_like;
976 crumbled_like = graphic_info[graphic].crumbled_like;
977 diggable_like = graphic_info[graphic].diggable_like;
979 if (crumbled_like != -1 && element_info[i].crumbled[ACTION_DEFAULT] == -1)
981 for (act = 0; act < NUM_ACTIONS; act++)
982 element_info[i].crumbled[act] =
983 element_info[crumbled_like].crumbled[act];
984 for (act = 0; act < NUM_ACTIONS; act++)
985 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
986 element_info[i].direction_crumbled[act][dir] =
987 element_info[crumbled_like].direction_crumbled[act][dir];
990 if (diggable_like != -1 && element_info[i].graphic[ACTION_DIGGING] == -1)
992 element_info[i].graphic[ACTION_DIGGING] =
993 element_info[diggable_like].graphic[ACTION_DIGGING];
994 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
995 element_info[i].direction_graphic[ACTION_DIGGING][dir] =
996 element_info[diggable_like].direction_graphic[ACTION_DIGGING][dir];
1000 // set hardcoded definitions for some runtime elements without graphic
1001 element_info[EL_AMOEBA_TO_DIAMOND].graphic[ACTION_DEFAULT] = IMG_AMOEBA_DEAD;
1003 // set hardcoded definitions for some internal elements without graphic
1004 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1006 if (IS_EDITOR_CASCADE_INACTIVE(i))
1007 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST;
1008 else if (IS_EDITOR_CASCADE_ACTIVE(i))
1009 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST_ACTIVE;
1012 // now set all undefined/invalid graphics to -1 to set to default after it
1013 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1015 for (act = 0; act < NUM_ACTIONS; act++)
1019 graphic = element_info[i].graphic[act];
1020 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1021 element_info[i].graphic[act] = -1;
1023 graphic = element_info[i].crumbled[act];
1024 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1025 element_info[i].crumbled[act] = -1;
1027 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1029 graphic = element_info[i].direction_graphic[act][dir];
1030 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1031 element_info[i].direction_graphic[act][dir] = -1;
1033 graphic = element_info[i].direction_crumbled[act][dir];
1034 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1035 element_info[i].direction_crumbled[act][dir] = -1;
1040 UPDATE_BUSY_STATE();
1042 // adjust graphics with 2nd tile for movement according to direction
1043 // (do this before correcting '-1' values to minimize calculations)
1044 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1046 for (act = 0; act < NUM_ACTIONS; act++)
1048 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1050 int graphic = element_info[i].direction_graphic[act][dir];
1051 int move_dir = (act == ACTION_FALLING ? MV_BIT_DOWN : dir);
1053 if (act == ACTION_FALLING) // special case
1054 graphic = element_info[i].graphic[act];
1056 if (graphic != -1 &&
1057 graphic_info[graphic].double_movement &&
1058 graphic_info[graphic].swap_double_tiles != 0)
1060 struct GraphicInfo *g = &graphic_info[graphic];
1061 int src_x_front = g->src_x;
1062 int src_y_front = g->src_y;
1063 int src_x_back = g->src_x + g->offset2_x;
1064 int src_y_back = g->src_y + g->offset2_y;
1065 boolean frames_are_ordered_diagonally = (g->offset_x != 0 &&
1067 boolean front_is_left_or_upper = (src_x_front < src_x_back ||
1068 src_y_front < src_y_back);
1069 boolean swap_movement_tiles_always = (g->swap_double_tiles == 1);
1070 boolean swap_movement_tiles_autodetected =
1071 (!frames_are_ordered_diagonally &&
1072 ((move_dir == MV_BIT_LEFT && !front_is_left_or_upper) ||
1073 (move_dir == MV_BIT_UP && !front_is_left_or_upper) ||
1074 (move_dir == MV_BIT_RIGHT && front_is_left_or_upper) ||
1075 (move_dir == MV_BIT_DOWN && front_is_left_or_upper)));
1077 // swap frontside and backside graphic tile coordinates, if needed
1078 if (swap_movement_tiles_always || swap_movement_tiles_autodetected)
1080 // get current (wrong) backside tile coordinates
1081 getGraphicSourceXY(graphic, 0, &src_x_back, &src_y_back, TRUE);
1083 // set frontside tile coordinates to backside tile coordinates
1084 g->src_x = src_x_back;
1085 g->src_y = src_y_back;
1087 // invert tile offset to point to new backside tile coordinates
1091 // do not swap front and backside tiles again after correction
1092 g->swap_double_tiles = 0;
1099 UPDATE_BUSY_STATE();
1101 // now set all '-1' values to element specific default values
1102 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1104 int default_graphic = element_info[i].graphic[ACTION_DEFAULT];
1105 int default_crumbled = element_info[i].crumbled[ACTION_DEFAULT];
1106 int default_direction_graphic[NUM_DIRECTIONS_FULL];
1107 int default_direction_crumbled[NUM_DIRECTIONS_FULL];
1109 if (default_graphic == -1)
1110 default_graphic = IMG_UNKNOWN;
1112 if (default_crumbled == -1)
1113 default_crumbled = default_graphic;
1115 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1117 default_direction_graphic[dir] =
1118 element_info[i].direction_graphic[ACTION_DEFAULT][dir];
1119 default_direction_crumbled[dir] =
1120 element_info[i].direction_crumbled[ACTION_DEFAULT][dir];
1122 if (default_direction_graphic[dir] == -1)
1123 default_direction_graphic[dir] = default_graphic;
1125 if (default_direction_crumbled[dir] == -1)
1126 default_direction_crumbled[dir] = default_direction_graphic[dir];
1129 for (act = 0; act < NUM_ACTIONS; act++)
1131 boolean act_remove = ((IS_DIGGABLE(i) && act == ACTION_DIGGING) ||
1132 (IS_SNAPPABLE(i) && act == ACTION_SNAPPING) ||
1133 (IS_COLLECTIBLE(i) && act == ACTION_COLLECTING));
1134 boolean act_turning = (act == ACTION_TURNING_FROM_LEFT ||
1135 act == ACTION_TURNING_FROM_RIGHT ||
1136 act == ACTION_TURNING_FROM_UP ||
1137 act == ACTION_TURNING_FROM_DOWN);
1139 // generic default action graphic (defined by "[default]" directive)
1140 int default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1141 int default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1142 int default_remove_graphic = IMG_EMPTY;
1144 if (act_remove && default_action_graphic != -1)
1145 default_remove_graphic = default_action_graphic;
1147 // look for special default action graphic (classic game specific)
1148 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].graphic[act] != -1)
1149 default_action_graphic = element_info[EL_BD_DEFAULT].graphic[act];
1150 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].graphic[act] != -1)
1151 default_action_graphic = element_info[EL_SP_DEFAULT].graphic[act];
1152 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].graphic[act] != -1)
1153 default_action_graphic = element_info[EL_SB_DEFAULT].graphic[act];
1154 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].graphic[act] != -1)
1155 default_action_graphic = element_info[EL_MM_DEFAULT].graphic[act];
1157 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].crumbled[act] != -1)
1158 default_action_crumbled = element_info[EL_BD_DEFAULT].crumbled[act];
1159 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].crumbled[act] != -1)
1160 default_action_crumbled = element_info[EL_SP_DEFAULT].crumbled[act];
1161 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].crumbled[act] != -1)
1162 default_action_crumbled = element_info[EL_SB_DEFAULT].crumbled[act];
1163 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].crumbled[act] != -1)
1164 default_action_crumbled = element_info[EL_MM_DEFAULT].crumbled[act];
1166 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
1167 // !!! make this better !!!
1168 if (i == EL_EMPTY_SPACE)
1170 default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1171 default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1174 if (default_action_graphic == -1)
1175 default_action_graphic = default_graphic;
1177 if (default_action_crumbled == -1)
1178 default_action_crumbled = default_action_graphic;
1180 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1182 // use action graphic as the default direction graphic, if undefined
1183 int default_action_direction_graphic = element_info[i].graphic[act];
1184 int default_action_direction_crumbled = element_info[i].crumbled[act];
1186 // no graphic for current action -- use default direction graphic
1187 if (default_action_direction_graphic == -1)
1188 default_action_direction_graphic =
1189 (act_remove ? default_remove_graphic :
1191 element_info[i].direction_graphic[ACTION_TURNING][dir] :
1192 default_action_graphic != default_graphic ?
1193 default_action_graphic :
1194 default_direction_graphic[dir]);
1196 if (element_info[i].direction_graphic[act][dir] == -1)
1197 element_info[i].direction_graphic[act][dir] =
1198 default_action_direction_graphic;
1200 if (default_action_direction_crumbled == -1)
1201 default_action_direction_crumbled =
1202 element_info[i].direction_graphic[act][dir];
1204 if (element_info[i].direction_crumbled[act][dir] == -1)
1205 element_info[i].direction_crumbled[act][dir] =
1206 default_action_direction_crumbled;
1209 // no graphic for this specific action -- use default action graphic
1210 if (element_info[i].graphic[act] == -1)
1211 element_info[i].graphic[act] =
1212 (act_remove ? default_remove_graphic :
1213 act_turning ? element_info[i].graphic[ACTION_TURNING] :
1214 default_action_graphic);
1216 if (element_info[i].crumbled[act] == -1)
1217 element_info[i].crumbled[act] = element_info[i].graphic[act];
1221 UPDATE_BUSY_STATE();
1224 static void InitElementSpecialGraphicInfo(void)
1226 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
1227 int num_property_mappings = getImageListPropertyMappingSize();
1230 // always start with reliable default values
1231 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1232 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1233 element_info[i].special_graphic[j] =
1234 element_info[i].graphic[ACTION_DEFAULT];
1236 // initialize special element/graphic mapping from static configuration
1237 for (i = 0; element_to_special_graphic[i].element > -1; i++)
1239 int element = element_to_special_graphic[i].element;
1240 int special = element_to_special_graphic[i].special;
1241 int graphic = element_to_special_graphic[i].graphic;
1242 int base_graphic = el2baseimg(element);
1243 boolean base_redefined =
1244 getImageListEntryFromImageID(base_graphic)->redefined;
1245 boolean special_redefined =
1246 getImageListEntryFromImageID(graphic)->redefined;
1248 /* if the base graphic ("emerald", for example) has been redefined,
1249 but not the special graphic ("emerald.EDITOR", for example), do not
1250 use an existing (in this case considered obsolete) special graphic
1251 anymore, but use the automatically created (down-scaled) graphic */
1252 if (base_redefined && !special_redefined)
1255 element_info[element].special_graphic[special] = graphic;
1258 // initialize special element/graphic mapping from dynamic configuration
1259 for (i = 0; i < num_property_mappings; i++)
1261 int element = property_mapping[i].base_index;
1262 int action = property_mapping[i].ext1_index;
1263 int direction = property_mapping[i].ext2_index;
1264 int special = property_mapping[i].ext3_index;
1265 int graphic = property_mapping[i].artwork_index;
1267 // for action ".active", replace element with active element, if exists
1268 if (action == ACTION_ACTIVE && element != ELEMENT_ACTIVE(element))
1270 element = ELEMENT_ACTIVE(element);
1274 if (element >= MAX_NUM_ELEMENTS)
1277 // do not change special graphic if action or direction was specified
1278 if (action != -1 || direction != -1)
1281 if (IS_SPECIAL_GFX_ARG(special))
1282 element_info[element].special_graphic[special] = graphic;
1285 // now set all undefined/invalid graphics to default
1286 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1287 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1288 if (graphic_info[element_info[i].special_graphic[j]].bitmap == NULL)
1289 element_info[i].special_graphic[j] =
1290 element_info[i].graphic[ACTION_DEFAULT];
1293 static int get_graphic_parameter_value(char *value_raw, char *suffix, int type)
1295 if (type != TYPE_ELEMENT && type != TYPE_GRAPHIC)
1296 return get_parameter_value(value_raw, suffix, type);
1298 if (strEqual(value_raw, ARG_UNDEFINED))
1299 return ARG_UNDEFINED_VALUE;
1301 if (type == TYPE_ELEMENT)
1303 char *value = getHashEntry(element_token_hash, value_raw);
1308 Warn("error found in config file:");
1309 Warn("- config file: '%s'", getImageConfigFilename());
1310 Warn("error: invalid element token '%s'", value_raw);
1311 Warn("custom graphic rejected for this element/action");
1312 Warn("fallback done to undefined element for this graphic");
1316 return (value != NULL ? atoi(value) : EL_UNDEFINED);
1318 else if (type == TYPE_GRAPHIC)
1320 char *value = getHashEntry(graphic_token_hash, value_raw);
1321 int fallback_graphic = IMG_CHAR_EXCLAM;
1326 Warn("error found in config file:");
1327 Warn("- config file: '%s'", getImageConfigFilename());
1328 Warn("error: invalid graphic token '%s'", value_raw);
1329 Warn("custom graphic rejected for this element/action");
1330 Warn("fallback done to 'char_exclam' for this graphic");
1334 return (value != NULL ? atoi(value) : fallback_graphic);
1340 static int get_scaled_graphic_width(Bitmap *src_bitmap, int graphic)
1342 int original_width = getOriginalImageWidthFromImageID(graphic);
1343 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1345 // only happens when loaded outside artwork system (like "global.busy")
1346 if (graphic_info == image_initial && src_bitmap)
1347 original_width = src_bitmap->width;
1349 return original_width * scale_up_factor;
1352 static int get_scaled_graphic_height(Bitmap *src_bitmap, int graphic)
1354 int original_height = getOriginalImageHeightFromImageID(graphic);
1355 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1357 // only happens when loaded outside artwork system (like "global.busy")
1358 if (graphic_info == image_initial && src_bitmap)
1359 original_height = src_bitmap->height;
1361 return original_height * scale_up_factor;
1364 static void set_graphic_parameters_ext(int graphic, int *parameter,
1365 Bitmap **src_bitmaps)
1367 struct GraphicInfo *g = &graphic_info[graphic];
1368 Bitmap *src_bitmap = (src_bitmaps ? src_bitmaps[IMG_BITMAP_STANDARD] : NULL);
1369 int anim_frames_per_row = 1, anim_frames_per_col = 1;
1370 int anim_frames_per_line = 1;
1372 // always start with reliable default values
1373 g->src_image_width = 0;
1374 g->src_image_height = 0;
1377 g->width = TILEX; // default for element graphics
1378 g->height = TILEY; // default for element graphics
1379 g->offset_x = 0; // one or both of these values ...
1380 g->offset_y = 0; // ... will be corrected later
1381 g->offset2_x = 0; // one or both of these values ...
1382 g->offset2_y = 0; // ... will be corrected later
1383 g->swap_double_tiles = -1; // auto-detect tile swapping
1384 g->crumbled_like = -1; // do not use clone element
1385 g->diggable_like = -1; // do not use clone element
1386 g->border_size = TILEX / 8; // "CRUMBLED" border size
1387 g->scale_up_factor = 1; // default: no scaling up
1388 g->tile_size = TILESIZE; // default: standard tile size
1389 g->clone_from = -1; // do not use clone graphic
1390 g->init_delay_fixed = 0;
1391 g->init_delay_random = 0;
1392 g->init_delay_action = -1;
1393 g->anim_delay_fixed = 0;
1394 g->anim_delay_random = 0;
1395 g->anim_delay_action = -1;
1396 g->post_delay_fixed = 0;
1397 g->post_delay_random = 0;
1398 g->post_delay_action = -1;
1399 g->init_event = ANIM_EVENT_UNDEFINED;
1400 g->anim_event = ANIM_EVENT_UNDEFINED;
1401 g->init_event_action = -1;
1402 g->anim_event_action = -1;
1403 g->draw_masked = FALSE;
1405 g->fade_mode = FADE_MODE_DEFAULT;
1409 g->auto_delay_unit = AUTO_DELAY_UNIT_DEFAULT;
1410 g->align = ALIGN_CENTER; // default for title screens
1411 g->valign = VALIGN_MIDDLE; // default for title screens
1412 g->sort_priority = 0; // default for title screens
1414 g->style = STYLE_DEFAULT;
1417 g->bitmaps = src_bitmaps;
1418 g->bitmap = src_bitmap;
1420 // optional zoom factor for scaling up the image to a larger size
1421 if (parameter[GFX_ARG_SCALE_UP_FACTOR] != ARG_UNDEFINED_VALUE)
1422 g->scale_up_factor = parameter[GFX_ARG_SCALE_UP_FACTOR];
1423 if (g->scale_up_factor < 1)
1424 g->scale_up_factor = 1; // no scaling
1426 // optional tile size for using non-standard image size
1427 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1429 g->tile_size = parameter[GFX_ARG_TILE_SIZE];
1432 // CHECK: should tile sizes less than standard tile size be allowed?
1433 if (g->tile_size < TILESIZE)
1434 g->tile_size = TILESIZE; // standard tile size
1437 // when setting tile size, also set width and height accordingly
1438 g->width = g->tile_size;
1439 g->height = g->tile_size;
1442 if (g->use_image_size)
1444 // set new default bitmap size (with scaling, but without small images)
1445 g->width = get_scaled_graphic_width(src_bitmap, graphic);
1446 g->height = get_scaled_graphic_height(src_bitmap, graphic);
1449 // optional width and height of each animation frame
1450 if (parameter[GFX_ARG_WIDTH] != ARG_UNDEFINED_VALUE)
1451 g->width = parameter[GFX_ARG_WIDTH];
1452 if (parameter[GFX_ARG_HEIGHT] != ARG_UNDEFINED_VALUE)
1453 g->height = parameter[GFX_ARG_HEIGHT];
1455 // optional x and y tile position of animation frame sequence
1456 if (parameter[GFX_ARG_XPOS] != ARG_UNDEFINED_VALUE)
1457 g->src_x = parameter[GFX_ARG_XPOS] * g->width;
1458 if (parameter[GFX_ARG_YPOS] != ARG_UNDEFINED_VALUE)
1459 g->src_y = parameter[GFX_ARG_YPOS] * g->height;
1461 // optional x and y pixel position of animation frame sequence
1462 if (parameter[GFX_ARG_X] != ARG_UNDEFINED_VALUE)
1463 g->src_x = parameter[GFX_ARG_X];
1464 if (parameter[GFX_ARG_Y] != ARG_UNDEFINED_VALUE)
1465 g->src_y = parameter[GFX_ARG_Y];
1472 Warn("invalid value %d for '%s.width' (fallback done to %d)",
1473 g->width, getTokenFromImageID(graphic), TILEX);
1476 g->width = TILEX; // will be checked to be inside bitmap later
1482 Warn("invalid value %d for '%s.height' (fallback done to %d)",
1483 g->height, getTokenFromImageID(graphic), TILEY);
1486 g->height = TILEY; // will be checked to be inside bitmap later
1492 // get final bitmap size (with scaling, but without small images)
1493 int src_image_width = get_scaled_graphic_width(src_bitmap, graphic);
1494 int src_image_height = get_scaled_graphic_height(src_bitmap, graphic);
1496 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1498 anim_frames_per_row = MAX(1, src_image_width / g->tile_size);
1499 anim_frames_per_col = MAX(1, src_image_height / g->tile_size);
1503 anim_frames_per_row = MAX(1, src_image_width / g->width);
1504 anim_frames_per_col = MAX(1, src_image_height / g->height);
1507 g->src_image_width = src_image_width;
1508 g->src_image_height = src_image_height;
1511 // correct x or y offset dependent of vertical or horizontal frame order
1512 if (parameter[GFX_ARG_VERTICAL]) // frames are ordered vertically
1514 g->offset_y = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1515 parameter[GFX_ARG_OFFSET] : g->height);
1516 anim_frames_per_line = anim_frames_per_col;
1518 else // frames are ordered horizontally
1520 g->offset_x = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1521 parameter[GFX_ARG_OFFSET] : g->width);
1522 anim_frames_per_line = anim_frames_per_row;
1525 // optionally, the x and y offset of frames can be specified directly
1526 if (parameter[GFX_ARG_XOFFSET] != ARG_UNDEFINED_VALUE)
1527 g->offset_x = parameter[GFX_ARG_XOFFSET];
1528 if (parameter[GFX_ARG_YOFFSET] != ARG_UNDEFINED_VALUE)
1529 g->offset_y = parameter[GFX_ARG_YOFFSET];
1531 // optionally, moving animations may have separate start and end graphics
1532 g->double_movement = parameter[GFX_ARG_2ND_MOVEMENT_TILE];
1534 if (parameter[GFX_ARG_2ND_VERTICAL] == ARG_UNDEFINED_VALUE)
1535 parameter[GFX_ARG_2ND_VERTICAL] = !parameter[GFX_ARG_VERTICAL];
1537 // correct x or y offset2 dependent of vertical or horizontal frame order
1538 if (parameter[GFX_ARG_2ND_VERTICAL]) // frames are ordered vertically
1539 g->offset2_y = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1540 parameter[GFX_ARG_2ND_OFFSET] : g->height);
1541 else // frames are ordered horizontally
1542 g->offset2_x = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1543 parameter[GFX_ARG_2ND_OFFSET] : g->width);
1545 // optionally, the x and y offset of 2nd graphic can be specified directly
1546 if (parameter[GFX_ARG_2ND_XOFFSET] != ARG_UNDEFINED_VALUE)
1547 g->offset2_x = parameter[GFX_ARG_2ND_XOFFSET];
1548 if (parameter[GFX_ARG_2ND_YOFFSET] != ARG_UNDEFINED_VALUE)
1549 g->offset2_y = parameter[GFX_ARG_2ND_YOFFSET];
1551 // optionally, the second movement tile can be specified as start tile
1552 if (parameter[GFX_ARG_2ND_SWAP_TILES] != ARG_UNDEFINED_VALUE)
1553 g->swap_double_tiles = parameter[GFX_ARG_2ND_SWAP_TILES];
1555 // automatically determine correct number of frames, if not defined
1556 if (parameter[GFX_ARG_FRAMES] != ARG_UNDEFINED_VALUE)
1557 g->anim_frames = parameter[GFX_ARG_FRAMES];
1558 else if (parameter[GFX_ARG_XPOS] == 0 && !parameter[GFX_ARG_VERTICAL])
1559 g->anim_frames = anim_frames_per_row;
1560 else if (parameter[GFX_ARG_YPOS] == 0 && parameter[GFX_ARG_VERTICAL])
1561 g->anim_frames = anim_frames_per_col;
1565 if (g->anim_frames < 1) // frames must be at least 1
1568 g->anim_frames_per_line =
1569 (parameter[GFX_ARG_FRAMES_PER_LINE] != ARG_UNDEFINED_VALUE ?
1570 parameter[GFX_ARG_FRAMES_PER_LINE] : anim_frames_per_line);
1572 g->anim_delay = parameter[GFX_ARG_DELAY];
1573 if (g->anim_delay < 1) // delay must be at least 1
1576 g->anim_mode = parameter[GFX_ARG_ANIM_MODE];
1578 // automatically determine correct start frame, if not defined
1579 if (parameter[GFX_ARG_START_FRAME] == ARG_UNDEFINED_VALUE)
1580 g->anim_start_frame = 0;
1581 else if (g->anim_mode & ANIM_REVERSE)
1582 g->anim_start_frame = g->anim_frames - parameter[GFX_ARG_START_FRAME] - 1;
1584 g->anim_start_frame = parameter[GFX_ARG_START_FRAME];
1586 // animation synchronized with global frame counter, not move position
1587 g->anim_global_sync = parameter[GFX_ARG_GLOBAL_SYNC];
1589 // animation synchronized with global anim frame counter, not move position
1590 g->anim_global_anim_sync = parameter[GFX_ARG_GLOBAL_ANIM_SYNC];
1592 // optional element for cloning crumble graphics
1593 if (parameter[GFX_ARG_CRUMBLED_LIKE] != ARG_UNDEFINED_VALUE)
1594 g->crumbled_like = parameter[GFX_ARG_CRUMBLED_LIKE];
1596 // optional element for cloning digging graphics
1597 if (parameter[GFX_ARG_DIGGABLE_LIKE] != ARG_UNDEFINED_VALUE)
1598 g->diggable_like = parameter[GFX_ARG_DIGGABLE_LIKE];
1600 // optional border size for "crumbling" diggable graphics
1601 if (parameter[GFX_ARG_BORDER_SIZE] != ARG_UNDEFINED_VALUE)
1602 g->border_size = parameter[GFX_ARG_BORDER_SIZE];
1604 // used for global animations and player "boring" and "sleeping" actions
1605 if (parameter[GFX_ARG_INIT_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1606 g->init_delay_fixed = parameter[GFX_ARG_INIT_DELAY_FIXED];
1607 if (parameter[GFX_ARG_INIT_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1608 g->init_delay_random = parameter[GFX_ARG_INIT_DELAY_RANDOM];
1609 if (parameter[GFX_ARG_ANIM_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1610 g->anim_delay_fixed = parameter[GFX_ARG_ANIM_DELAY_FIXED];
1611 if (parameter[GFX_ARG_ANIM_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1612 g->anim_delay_random = parameter[GFX_ARG_ANIM_DELAY_RANDOM];
1613 if (parameter[GFX_ARG_POST_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1614 g->post_delay_fixed = parameter[GFX_ARG_POST_DELAY_FIXED];
1615 if (parameter[GFX_ARG_POST_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1616 g->post_delay_random = parameter[GFX_ARG_POST_DELAY_RANDOM];
1618 // used for global animations
1619 if (parameter[GFX_ARG_INIT_EVENT] != ARG_UNDEFINED_VALUE)
1620 g->init_event = parameter[GFX_ARG_INIT_EVENT];
1621 if (parameter[GFX_ARG_ANIM_EVENT] != ARG_UNDEFINED_VALUE)
1622 g->anim_event = parameter[GFX_ARG_ANIM_EVENT];
1623 if (parameter[GFX_ARG_INIT_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1624 g->init_event_action = parameter[GFX_ARG_INIT_EVENT_ACTION];
1625 if (parameter[GFX_ARG_ANIM_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1626 g->anim_event_action = parameter[GFX_ARG_ANIM_EVENT_ACTION];
1627 if (parameter[GFX_ARG_INIT_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1628 g->init_delay_action = parameter[GFX_ARG_INIT_DELAY_ACTION];
1629 if (parameter[GFX_ARG_ANIM_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1630 g->anim_delay_action = parameter[GFX_ARG_ANIM_DELAY_ACTION];
1631 if (parameter[GFX_ARG_POST_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1632 g->post_delay_action = parameter[GFX_ARG_POST_DELAY_ACTION];
1634 // used for toon animations and global animations
1635 g->step_offset = parameter[GFX_ARG_STEP_OFFSET];
1636 g->step_xoffset = parameter[GFX_ARG_STEP_XOFFSET];
1637 g->step_yoffset = parameter[GFX_ARG_STEP_YOFFSET];
1638 g->step_delay = parameter[GFX_ARG_STEP_DELAY];
1639 g->direction = parameter[GFX_ARG_DIRECTION];
1640 g->position = parameter[GFX_ARG_POSITION];
1641 g->x = parameter[GFX_ARG_X]; // (may be uninitialized,
1642 g->y = parameter[GFX_ARG_Y]; // unlike src_x and src_y)
1644 if (g->step_delay < 1) // delay must be at least 1
1647 // this is only used for drawing font characters
1648 g->draw_xoffset = parameter[GFX_ARG_DRAW_XOFFSET];
1649 g->draw_yoffset = parameter[GFX_ARG_DRAW_YOFFSET];
1651 // use a different default value for global animations and toons
1652 if ((graphic >= IMG_GFX_GLOBAL_ANIM_1 && graphic <= IMG_GFX_GLOBAL_ANIM_32) ||
1653 (graphic >= IMG_TOON_1 && graphic <= IMG_TOON_20))
1654 g->draw_masked = TRUE;
1656 // this is used for drawing envelopes, global animations and toons
1657 if (parameter[GFX_ARG_DRAW_MASKED] != ARG_UNDEFINED_VALUE)
1658 g->draw_masked = parameter[GFX_ARG_DRAW_MASKED];
1660 // used for toon animations and global animations
1661 if (parameter[GFX_ARG_DRAW_ORDER] != ARG_UNDEFINED_VALUE)
1662 g->draw_order = parameter[GFX_ARG_DRAW_ORDER];
1664 // optional graphic for cloning all graphics settings
1665 if (parameter[GFX_ARG_CLONE_FROM] != ARG_UNDEFINED_VALUE)
1666 g->clone_from = parameter[GFX_ARG_CLONE_FROM];
1668 // optional settings for drawing title screens and title messages
1669 if (parameter[GFX_ARG_FADE_MODE] != ARG_UNDEFINED_VALUE)
1670 g->fade_mode = parameter[GFX_ARG_FADE_MODE];
1671 if (parameter[GFX_ARG_FADE_DELAY] != ARG_UNDEFINED_VALUE)
1672 g->fade_delay = parameter[GFX_ARG_FADE_DELAY];
1673 if (parameter[GFX_ARG_POST_DELAY] != ARG_UNDEFINED_VALUE)
1674 g->post_delay = parameter[GFX_ARG_POST_DELAY];
1675 if (parameter[GFX_ARG_AUTO_DELAY] != ARG_UNDEFINED_VALUE)
1676 g->auto_delay = parameter[GFX_ARG_AUTO_DELAY];
1677 if (parameter[GFX_ARG_AUTO_DELAY_UNIT] != ARG_UNDEFINED_VALUE)
1678 g->auto_delay_unit = parameter[GFX_ARG_AUTO_DELAY_UNIT];
1679 if (parameter[GFX_ARG_ALIGN] != ARG_UNDEFINED_VALUE)
1680 g->align = parameter[GFX_ARG_ALIGN];
1681 if (parameter[GFX_ARG_VALIGN] != ARG_UNDEFINED_VALUE)
1682 g->valign = parameter[GFX_ARG_VALIGN];
1683 if (parameter[GFX_ARG_SORT_PRIORITY] != ARG_UNDEFINED_VALUE)
1684 g->sort_priority = parameter[GFX_ARG_SORT_PRIORITY];
1686 if (parameter[GFX_ARG_CLASS] != ARG_UNDEFINED_VALUE)
1687 g->class = parameter[GFX_ARG_CLASS];
1688 if (parameter[GFX_ARG_STYLE] != ARG_UNDEFINED_VALUE)
1689 g->style = parameter[GFX_ARG_STYLE];
1690 if (parameter[GFX_ARG_ALPHA] != ARG_UNDEFINED_VALUE)
1691 g->alpha = parameter[GFX_ARG_ALPHA];
1693 // this is only used for drawing menu buttons and text
1694 g->active_xoffset = parameter[GFX_ARG_ACTIVE_XOFFSET];
1695 g->active_yoffset = parameter[GFX_ARG_ACTIVE_YOFFSET];
1696 g->pressed_xoffset = parameter[GFX_ARG_PRESSED_XOFFSET];
1697 g->pressed_yoffset = parameter[GFX_ARG_PRESSED_YOFFSET];
1699 // this is only used for drawing stacked global animations
1700 g->stacked_xfactor = parameter[GFX_ARG_STACKED_XFACTOR];
1701 g->stacked_yfactor = parameter[GFX_ARG_STACKED_YFACTOR];
1702 g->stacked_xoffset = parameter[GFX_ARG_STACKED_XOFFSET];
1703 g->stacked_yoffset = parameter[GFX_ARG_STACKED_YOFFSET];
1706 static void set_graphic_parameters(int graphic)
1708 struct FileInfo *image = getImageListEntryFromImageID(graphic);
1709 char **parameter_raw = image->parameter;
1710 Bitmap **src_bitmaps = getBitmapsFromImageID(graphic);
1711 int parameter[NUM_GFX_ARGS];
1714 // if fallback to default artwork is done, also use the default parameters
1715 if (image->fallback_to_default)
1716 parameter_raw = image->default_parameter;
1718 // get integer values from string parameters
1719 for (i = 0; i < NUM_GFX_ARGS; i++)
1720 parameter[i] = get_graphic_parameter_value(parameter_raw[i],
1721 image_config_suffix[i].token,
1722 image_config_suffix[i].type);
1724 set_graphic_parameters_ext(graphic, parameter, src_bitmaps);
1726 UPDATE_BUSY_STATE();
1729 static void set_cloned_graphic_parameters(int graphic)
1731 int fallback_graphic = IMG_CHAR_EXCLAM;
1732 int max_num_images = getImageListSize();
1733 int clone_graphic = graphic_info[graphic].clone_from;
1734 int num_references_followed = 1;
1736 while (graphic_info[clone_graphic].clone_from != -1 &&
1737 num_references_followed < max_num_images)
1739 clone_graphic = graphic_info[clone_graphic].clone_from;
1741 num_references_followed++;
1744 if (num_references_followed >= max_num_images)
1747 Warn("error found in config file:");
1748 Warn("- config file: '%s'", getImageConfigFilename());
1749 Warn("- config token: '%s'", getTokenFromImageID(graphic));
1750 Warn("error: loop discovered when resolving cloned graphics");
1751 Warn("custom graphic rejected for this element/action");
1753 if (graphic == fallback_graphic)
1754 Fail("no fallback graphic available");
1756 Warn("fallback done to 'char_exclam' for this graphic");
1759 graphic_info[graphic] = graphic_info[fallback_graphic];
1763 graphic_info[graphic] = graphic_info[clone_graphic];
1764 graphic_info[graphic].clone_from = clone_graphic;
1768 static void InitGraphicInfo(void)
1770 int fallback_graphic = IMG_CHAR_EXCLAM;
1771 int num_images = getImageListSize();
1774 // use image size as default values for width and height for these images
1775 static int full_size_graphics[] =
1778 IMG_GLOBAL_BORDER_MAIN,
1779 IMG_GLOBAL_BORDER_SCORES,
1780 IMG_GLOBAL_BORDER_EDITOR,
1781 IMG_GLOBAL_BORDER_PLAYING,
1784 IMG_BACKGROUND_ENVELOPE_1,
1785 IMG_BACKGROUND_ENVELOPE_2,
1786 IMG_BACKGROUND_ENVELOPE_3,
1787 IMG_BACKGROUND_ENVELOPE_4,
1788 IMG_BACKGROUND_REQUEST,
1791 IMG_BACKGROUND_LOADING_INITIAL,
1792 IMG_BACKGROUND_LOADING,
1793 IMG_BACKGROUND_TITLE_INITIAL,
1794 IMG_BACKGROUND_TITLE,
1795 IMG_BACKGROUND_MAIN,
1796 IMG_BACKGROUND_NAMES,
1797 IMG_BACKGROUND_LEVELS,
1798 IMG_BACKGROUND_LEVELNR,
1799 IMG_BACKGROUND_SCORES,
1800 IMG_BACKGROUND_SCOREINFO,
1801 IMG_BACKGROUND_EDITOR,
1802 IMG_BACKGROUND_INFO,
1803 IMG_BACKGROUND_INFO_ELEMENTS,
1804 IMG_BACKGROUND_INFO_MUSIC,
1805 IMG_BACKGROUND_INFO_CREDITS,
1806 IMG_BACKGROUND_INFO_PROGRAM,
1807 IMG_BACKGROUND_INFO_VERSION,
1808 IMG_BACKGROUND_INFO_LEVELSET,
1809 IMG_BACKGROUND_SETUP,
1810 IMG_BACKGROUND_PLAYING,
1811 IMG_BACKGROUND_DOOR,
1812 IMG_BACKGROUND_TAPE,
1813 IMG_BACKGROUND_PANEL,
1814 IMG_BACKGROUND_PALETTE,
1815 IMG_BACKGROUND_TOOLBOX,
1817 IMG_TITLESCREEN_INITIAL_1,
1818 IMG_TITLESCREEN_INITIAL_2,
1819 IMG_TITLESCREEN_INITIAL_3,
1820 IMG_TITLESCREEN_INITIAL_4,
1821 IMG_TITLESCREEN_INITIAL_5,
1828 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_1,
1829 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_2,
1830 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_3,
1831 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_4,
1832 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_5,
1833 IMG_BACKGROUND_TITLEMESSAGE_1,
1834 IMG_BACKGROUND_TITLEMESSAGE_2,
1835 IMG_BACKGROUND_TITLEMESSAGE_3,
1836 IMG_BACKGROUND_TITLEMESSAGE_4,
1837 IMG_BACKGROUND_TITLEMESSAGE_5,
1842 FreeGlobalAnimEventInfo();
1844 checked_free(graphic_info);
1846 graphic_info = checked_calloc(num_images * sizeof(struct GraphicInfo));
1848 // initialize "use_image_size" flag with default value
1849 for (i = 0; i < num_images; i++)
1850 graphic_info[i].use_image_size = FALSE;
1852 // initialize "use_image_size" flag from static configuration above
1853 for (i = 0; full_size_graphics[i] != -1; i++)
1854 graphic_info[full_size_graphics[i]].use_image_size = TRUE;
1856 // first set all graphic paramaters ...
1857 for (i = 0; i < num_images; i++)
1858 set_graphic_parameters(i);
1860 // ... then copy these parameters for cloned graphics
1861 for (i = 0; i < num_images; i++)
1862 if (graphic_info[i].clone_from != -1)
1863 set_cloned_graphic_parameters(i);
1865 for (i = 0; i < num_images; i++)
1867 Bitmap *src_bitmap = graphic_info[i].bitmap;
1871 int src_bitmap_width, src_bitmap_height;
1873 // now check if no animation frames are outside of the loaded image
1875 if (graphic_info[i].bitmap == NULL)
1876 continue; // skip check for optional images that are undefined
1878 // get image size (this can differ from the standard element tile size!)
1879 width = graphic_info[i].width;
1880 height = graphic_info[i].height;
1882 // get final bitmap size (with scaling, but without small images)
1883 src_bitmap_width = graphic_info[i].src_image_width;
1884 src_bitmap_height = graphic_info[i].src_image_height;
1886 // check if first animation frame is inside specified bitmap
1888 // do not use getGraphicSourceXY() here to get position of first frame;
1889 // this avoids calculating wrong start position for out-of-bounds frame
1890 src_x = graphic_info[i].src_x;
1891 src_y = graphic_info[i].src_y;
1893 if (program.headless)
1896 if (src_x < 0 || src_y < 0 ||
1897 src_x + width > src_bitmap_width ||
1898 src_y + height > src_bitmap_height)
1901 Warn("error found in config file:");
1902 Warn("- config file: '%s'", getImageConfigFilename());
1903 Warn("- config token: '%s'", getTokenFromImageID(i));
1904 Warn("- image file: '%s'", src_bitmap->source_filename);
1905 Warn("- frame size: %d, %d", width, height);
1906 Warn("error: first animation frame out of bounds (%d, %d) [%d, %d]",
1907 src_x, src_y, src_bitmap_width, src_bitmap_height);
1908 Warn("custom graphic rejected for this element/action");
1910 if (i == fallback_graphic)
1911 Fail("no fallback graphic available");
1913 Warn("fallback done to 'char_exclam' for this graphic");
1916 graphic_info[i] = graphic_info[fallback_graphic];
1918 // if first frame out of bounds, do not check last frame anymore
1922 // check if last animation frame is inside specified bitmap
1924 last_frame = graphic_info[i].anim_frames - 1;
1925 getGraphicSourceXY(i, last_frame, &src_x, &src_y, FALSE);
1927 if (src_x < 0 || src_y < 0 ||
1928 src_x + width > src_bitmap_width ||
1929 src_y + height > src_bitmap_height)
1932 Warn("error found in config file:");
1933 Warn("- config file: '%s'", getImageConfigFilename());
1934 Warn("- config token: '%s'", getTokenFromImageID(i));
1935 Warn("- image file: '%s'", src_bitmap->source_filename);
1936 Warn("- frame size: %d, %d", width, height);
1937 Warn("error: last animation frame (%d) out of bounds (%d, %d) [%d, %d]",
1938 last_frame, src_x, src_y, src_bitmap_width, src_bitmap_height);
1939 Warn("custom graphic rejected for this element/action");
1941 if (i == fallback_graphic)
1942 Fail("no fallback graphic available");
1944 Warn("fallback done to 'char_exclam' for this graphic");
1947 graphic_info[i] = graphic_info[fallback_graphic];
1952 static void InitGraphicCompatibilityInfo(void)
1954 struct FileInfo *fi_global_door =
1955 getImageListEntryFromImageID(IMG_GLOBAL_DOOR);
1956 int num_images = getImageListSize();
1959 /* the following compatibility handling is needed for the following case:
1960 versions up to 3.3.0.0 used one large bitmap "global.door" for various
1961 graphics mainly used for door and panel graphics, like editor, tape and
1962 in-game buttons with hard-coded bitmap positions and button sizes; as
1963 these graphics now have individual definitions, redefining "global.door"
1964 to change all these graphics at once like before does not work anymore
1965 (because all those individual definitions still have their default values);
1966 to solve this, remap all those individual definitions that are not
1967 redefined to the new bitmap of "global.door" if it was redefined */
1969 // special compatibility handling if image "global.door" was redefined
1970 if (fi_global_door->redefined)
1972 for (i = 0; i < num_images; i++)
1974 struct FileInfo *fi = getImageListEntryFromImageID(i);
1976 // process only those images that still use the default settings
1979 // process all images which default to same image as "global.door"
1980 if (strEqual(fi->default_filename, fi_global_door->default_filename))
1982 // skip all images that are cloned from images that default to same
1983 // image as "global.door", but that are redefined to something else
1984 if (graphic_info[i].clone_from != -1)
1986 int cloned_graphic = graphic_info[i].clone_from;
1988 if (getImageListEntryFromImageID(cloned_graphic)->redefined)
1993 Debug("init:InitGraphicCompatibilityInfo",
1994 "special treatment needed for token '%s'", fi->token);
1997 graphic_info[i].bitmaps = graphic_info[IMG_GLOBAL_DOOR].bitmaps;
1998 graphic_info[i].bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2004 // special compatibility handling for "Snake Bite" graphics set
2005 if (strPrefix(leveldir_current->identifier, "snake_bite"))
2007 Bitmap *bitmap = graphic_info[IMG_BACKGROUND_SCORES].bitmap;
2009 BlitBitmap(bitmap, bitmap, 18, 66, 32, 480, 50, 66);
2010 BlitBitmap(bitmap, bitmap, 466, 66, 32, 480, 434, 66);
2012 ClearRectangle(bitmap, 2, 66, 32, 480);
2013 ClearRectangle(bitmap, 514, 66, 32, 480);
2016 // special compatibility handling for "Jue" graphics sets (2007 and 2019)
2017 boolean supports_score_info = (menu.draw_xoffset[GAME_MODE_SCOREINFO] != 0);
2018 if (strPrefix(artwork.gfx_current_identifier, "jue") && !supports_score_info)
2036 int mode_old = GAME_MODE_SCORES;
2037 int mode_new = GAME_MODE_SCOREINFO;
2040 // adjust title screens on score info page
2041 for (i = 0; font_title[i] != -1; i++)
2043 struct FontInfo *fi = &font_info[font_title[i]];
2045 fi->special_graphic[mode_new] = fi->special_graphic[mode_old];
2046 fi->special_bitmap_id[mode_new] = fi->special_bitmap_id[mode_old];
2049 // adjust vertical text and button positions on scores page
2050 for (i = 0; font_text[i] != -1; i++)
2052 for (j = 0; j < 2; j++)
2054 boolean jue0 = strEqual(artwork.gfx_current_identifier, "jue0");
2055 int font_nr = (j == 0 ? font_text[i] : FONT_ACTIVE(font_text[i]));
2056 int font_bitmap_id = font_info[font_nr].special_bitmap_id[mode_old];
2057 int font_yoffset = (jue0 ? 10 : 5);
2059 gfx.font_bitmap_info[font_bitmap_id].draw_yoffset = font_yoffset;
2063 // adjust page offsets on score info page
2064 menu.draw_xoffset[mode_new] = menu.draw_xoffset[mode_old];
2065 menu.draw_yoffset[mode_new] = menu.draw_yoffset[mode_old];
2068 InitGraphicCompatibilityInfo_Doors();
2071 static void InitElementSoundInfo(void)
2073 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
2074 int num_property_mappings = getSoundListPropertyMappingSize();
2077 // set values to -1 to identify later as "uninitialized" values
2078 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2079 for (act = 0; act < NUM_ACTIONS; act++)
2080 element_info[i].sound[act] = -1;
2082 // initialize element/sound mapping from static configuration
2083 for (i = 0; element_to_sound[i].element > -1; i++)
2085 int element = element_to_sound[i].element;
2086 int action = element_to_sound[i].action;
2087 int sound = element_to_sound[i].sound;
2088 boolean is_class = element_to_sound[i].is_class;
2091 action = ACTION_DEFAULT;
2094 element_info[element].sound[action] = sound;
2096 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2097 if (strEqual(element_info[j].class_name,
2098 element_info[element].class_name))
2099 element_info[j].sound[action] = sound;
2102 // initialize element class/sound mapping from dynamic configuration
2103 for (i = 0; i < num_property_mappings; i++)
2105 int element_class = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
2106 int action = property_mapping[i].ext1_index;
2107 int sound = property_mapping[i].artwork_index;
2109 if (element_class < 0 || element_class >= MAX_NUM_ELEMENTS)
2113 action = ACTION_DEFAULT;
2115 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2116 if (strEqual(element_info[j].class_name,
2117 element_info[element_class].class_name))
2118 element_info[j].sound[action] = sound;
2121 // initialize element/sound mapping from dynamic configuration
2122 for (i = 0; i < num_property_mappings; i++)
2124 int element = property_mapping[i].base_index;
2125 int action = property_mapping[i].ext1_index;
2126 int sound = property_mapping[i].artwork_index;
2128 if (element >= MAX_NUM_ELEMENTS)
2132 action = ACTION_DEFAULT;
2134 element_info[element].sound[action] = sound;
2137 // now set all '-1' values to element specific default values
2138 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2140 for (act = 0; act < NUM_ACTIONS; act++)
2142 // generic default action sound (defined by "[default]" directive)
2143 int default_action_sound = element_info[EL_DEFAULT].sound[act];
2145 // look for special default action sound (classic game specific)
2146 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].sound[act] != -1)
2147 default_action_sound = element_info[EL_BD_DEFAULT].sound[act];
2148 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].sound[act] != -1)
2149 default_action_sound = element_info[EL_SP_DEFAULT].sound[act];
2150 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].sound[act] != -1)
2151 default_action_sound = element_info[EL_SB_DEFAULT].sound[act];
2152 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].sound[act] != -1)
2153 default_action_sound = element_info[EL_MM_DEFAULT].sound[act];
2155 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
2156 // !!! make this better !!!
2157 if (i == EL_EMPTY_SPACE)
2158 default_action_sound = element_info[EL_DEFAULT].sound[act];
2160 // no sound for this specific action -- use default action sound
2161 if (element_info[i].sound[act] == -1)
2162 element_info[i].sound[act] = default_action_sound;
2166 // copy sound settings to some elements that are only stored in level file
2167 // in native R'n'D levels, but are used by game engine in native EM levels
2168 for (i = 0; copy_properties[i][0] != -1; i++)
2169 for (j = 1; j <= 4; j++)
2170 for (act = 0; act < NUM_ACTIONS; act++)
2171 element_info[copy_properties[i][j]].sound[act] =
2172 element_info[copy_properties[i][0]].sound[act];
2175 static void InitGameModeSoundInfo(void)
2179 // set values to -1 to identify later as "uninitialized" values
2180 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2183 // initialize gamemode/sound mapping from static configuration
2184 for (i = 0; gamemode_to_sound[i].sound > -1; i++)
2186 int gamemode = gamemode_to_sound[i].gamemode;
2187 int sound = gamemode_to_sound[i].sound;
2190 gamemode = GAME_MODE_DEFAULT;
2192 menu.sound[gamemode] = sound;
2195 // now set all '-1' values to levelset specific default values
2196 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2197 if (menu.sound[i] == -1)
2198 menu.sound[i] = menu.sound[GAME_MODE_DEFAULT];
2201 static void set_sound_parameters(int sound, char **parameter_raw)
2203 int parameter[NUM_SND_ARGS];
2206 // get integer values from string parameters
2207 for (i = 0; i < NUM_SND_ARGS; i++)
2209 get_parameter_value(parameter_raw[i],
2210 sound_config_suffix[i].token,
2211 sound_config_suffix[i].type);
2213 // explicit loop mode setting in configuration overrides default value
2214 if (parameter[SND_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2215 sound_info[sound].loop = parameter[SND_ARG_MODE_LOOP];
2217 // sound volume to change the original volume when loading the sound file
2218 sound_info[sound].volume = parameter[SND_ARG_VOLUME];
2220 // sound priority to give certain sounds a higher or lower priority
2221 sound_info[sound].priority = parameter[SND_ARG_PRIORITY];
2224 static void InitSoundInfo(void)
2226 int *sound_effect_properties;
2227 int num_sounds = getSoundListSize();
2230 checked_free(sound_info);
2232 sound_effect_properties = checked_calloc(num_sounds * sizeof(int));
2233 sound_info = checked_calloc(num_sounds * sizeof(struct SoundInfo));
2235 // initialize sound effect for all elements to "no sound"
2236 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2237 for (j = 0; j < NUM_ACTIONS; j++)
2238 element_info[i].sound[j] = SND_UNDEFINED;
2240 for (i = 0; i < num_sounds; i++)
2242 struct FileInfo *sound = getSoundListEntry(i);
2243 int len_effect_text = strlen(sound->token);
2245 sound_effect_properties[i] = ACTION_OTHER;
2246 sound_info[i].loop = FALSE; // default: play sound only once
2248 // determine all loop sounds and identify certain sound classes
2250 for (j = 0; element_action_info[j].suffix; j++)
2252 int len_action_text = strlen(element_action_info[j].suffix);
2254 if (len_action_text < len_effect_text &&
2255 strEqual(&sound->token[len_effect_text - len_action_text],
2256 element_action_info[j].suffix))
2258 sound_effect_properties[i] = element_action_info[j].value;
2259 sound_info[i].loop = element_action_info[j].is_loop_sound;
2265 // associate elements and some selected sound actions
2267 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2269 if (element_info[j].class_name)
2271 int len_class_text = strlen(element_info[j].class_name);
2273 if (len_class_text + 1 < len_effect_text &&
2274 strncmp(sound->token,
2275 element_info[j].class_name, len_class_text) == 0 &&
2276 sound->token[len_class_text] == '.')
2278 int sound_action_value = sound_effect_properties[i];
2280 element_info[j].sound[sound_action_value] = i;
2285 set_sound_parameters(i, sound->parameter);
2288 Debug("init:InitSoundInfo", "loop mode: %d ['%s']",
2289 sound_info[i].loop, sound->token);
2293 free(sound_effect_properties);
2296 static void InitGameModeMusicInfo(void)
2298 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
2299 int num_property_mappings = getMusicListPropertyMappingSize();
2300 int default_levelset_music = -1;
2303 // set values to -1 to identify later as "uninitialized" values
2304 for (i = 0; i < MAX_LEVELS; i++)
2305 levelset.music[i] = -1;
2306 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2309 // initialize gamemode/music mapping from static configuration
2310 for (i = 0; gamemode_to_music[i].music > -1; i++)
2312 int gamemode = gamemode_to_music[i].gamemode;
2313 int music = gamemode_to_music[i].music;
2316 gamemode = GAME_MODE_DEFAULT;
2318 menu.music[gamemode] = music;
2321 // initialize gamemode/music mapping from dynamic configuration
2322 for (i = 0; i < num_property_mappings; i++)
2324 int prefix = property_mapping[i].base_index;
2325 int gamemode = property_mapping[i].ext2_index;
2326 int level = property_mapping[i].ext3_index;
2327 int music = property_mapping[i].artwork_index;
2329 if (prefix < 0 || prefix >= NUM_MUSIC_PREFIXES)
2333 gamemode = GAME_MODE_DEFAULT;
2335 // level specific music only allowed for in-game music
2336 if (level != -1 && gamemode == GAME_MODE_DEFAULT)
2337 gamemode = GAME_MODE_PLAYING;
2342 default_levelset_music = music;
2345 if (gamemode == GAME_MODE_PLAYING || gamemode == GAME_MODE_DEFAULT)
2346 levelset.music[level] = music;
2347 if (gamemode != GAME_MODE_PLAYING)
2348 menu.music[gamemode] = music;
2351 // now set all '-1' values to menu specific default values
2352 // (undefined values of "levelset.music[]" might stay at "-1" to
2353 // allow dynamic selection of music files from music directory!)
2354 for (i = 0; i < MAX_LEVELS; i++)
2355 if (levelset.music[i] == -1)
2356 levelset.music[i] = default_levelset_music;
2357 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2358 if (menu.music[i] == -1)
2359 menu.music[i] = menu.music[GAME_MODE_DEFAULT];
2362 static void set_music_parameters(int music, char **parameter_raw)
2364 int parameter[NUM_MUS_ARGS];
2367 // get integer values from string parameters
2368 for (i = 0; i < NUM_MUS_ARGS; i++)
2370 get_parameter_value(parameter_raw[i],
2371 music_config_suffix[i].token,
2372 music_config_suffix[i].type);
2374 // explicit loop mode setting in configuration overrides default value
2375 if (parameter[MUS_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2376 music_info[music].loop = parameter[MUS_ARG_MODE_LOOP];
2379 static void InitMusicInfo(void)
2381 int num_music = getMusicListSize();
2384 checked_free(music_info);
2386 music_info = checked_calloc(num_music * sizeof(struct MusicInfo));
2388 for (i = 0; i < num_music; i++)
2390 struct FileInfo *music = getMusicListEntry(i);
2391 int len_music_text = strlen(music->token);
2393 music_info[i].loop = TRUE; // default: play music in loop mode
2395 // determine all loop music
2397 for (j = 0; music_prefix_info[j].prefix; j++)
2399 int len_prefix_text = strlen(music_prefix_info[j].prefix);
2401 if (len_prefix_text < len_music_text &&
2402 strncmp(music->token,
2403 music_prefix_info[j].prefix, len_prefix_text) == 0)
2405 music_info[i].loop = music_prefix_info[j].is_loop_music;
2411 set_music_parameters(i, music->parameter);
2416 static void InitGameInfoFromArtworkInfo(void)
2418 // special case: store initial value of custom artwork setting
2419 game.use_masked_elements_initial = game.use_masked_elements;
2422 static void ReinitializeGraphics(void)
2424 print_timestamp_init("ReinitializeGraphics");
2426 InitGfxTileSizeInfo(game.tile_size, TILESIZE);
2428 InitGraphicInfo(); // graphic properties mapping
2429 print_timestamp_time("InitGraphicInfo");
2430 InitElementGraphicInfo(); // element game graphic mapping
2431 print_timestamp_time("InitElementGraphicInfo");
2432 InitElementSpecialGraphicInfo(); // element special graphic mapping
2433 print_timestamp_time("InitElementSpecialGraphicInfo");
2435 InitElementSmallImages(); // scale elements to all needed sizes
2436 print_timestamp_time("InitElementSmallImages");
2437 InitScaledImages(); // scale all other images, if needed
2438 print_timestamp_time("InitScaledImages");
2439 InitBitmapPointers(); // set standard size bitmap pointers
2440 print_timestamp_time("InitBitmapPointers");
2441 InitFontGraphicInfo(); // initialize text drawing functions
2442 print_timestamp_time("InitFontGraphicInfo");
2443 InitGlobalAnimGraphicInfo(); // initialize global animation config
2444 print_timestamp_time("InitGlobalAnimGraphicInfo");
2446 InitImageTextures(); // create textures for certain images
2447 print_timestamp_time("InitImageTextures");
2449 InitGraphicInfo_BD(); // graphic mapping for BD engine
2450 print_timestamp_time("InitGraphicInfo_BD");
2451 InitGraphicInfo_EM(); // graphic mapping for EM engine
2452 print_timestamp_time("InitGraphicInfo_EM");
2454 InitGraphicCompatibilityInfo();
2455 print_timestamp_time("InitGraphicCompatibilityInfo");
2458 print_timestamp_time("InitGadgets");
2460 print_timestamp_time("InitDoors");
2462 InitGameInfoFromArtworkInfo();
2464 print_timestamp_done("ReinitializeGraphics");
2467 static void ReinitializeSounds(void)
2469 InitSoundInfo(); // sound properties mapping
2470 InitElementSoundInfo(); // element game sound mapping
2471 InitGameModeSoundInfo(); // game mode sound mapping
2472 InitGlobalAnimSoundInfo(); // global animation sound settings
2474 InitPlayLevelSound(); // internal game sound settings
2477 static void ReinitializeMusic(void)
2479 InitMusicInfo(); // music properties mapping
2480 InitGameModeMusicInfo(); // game mode music mapping
2481 InitGlobalAnimMusicInfo(); // global animation music settings
2484 static int get_special_property_bit(int element, int property_bit_nr)
2486 struct PropertyBitInfo
2492 static struct PropertyBitInfo pb_can_move_into_acid[] =
2494 // the player may be able fall into acid when gravity is activated
2499 { EL_SP_MURPHY, 0 },
2500 { EL_SOKOBAN_FIELD_PLAYER, 0 },
2502 // all elements that can move may be able to also move into acid
2505 { EL_BUG_RIGHT, 1 },
2508 { EL_SPACESHIP, 2 },
2509 { EL_SPACESHIP_LEFT, 2 },
2510 { EL_SPACESHIP_RIGHT, 2 },
2511 { EL_SPACESHIP_UP, 2 },
2512 { EL_SPACESHIP_DOWN, 2 },
2513 { EL_BD_BUTTERFLY, 3 },
2514 { EL_BD_BUTTERFLY_LEFT, 3 },
2515 { EL_BD_BUTTERFLY_RIGHT, 3 },
2516 { EL_BD_BUTTERFLY_UP, 3 },
2517 { EL_BD_BUTTERFLY_DOWN, 3 },
2518 { EL_BD_FIREFLY, 4 },
2519 { EL_BD_FIREFLY_LEFT, 4 },
2520 { EL_BD_FIREFLY_RIGHT, 4 },
2521 { EL_BD_FIREFLY_UP, 4 },
2522 { EL_BD_FIREFLY_DOWN, 4 },
2524 { EL_YAMYAM_LEFT, 5 },
2525 { EL_YAMYAM_RIGHT, 5 },
2526 { EL_YAMYAM_UP, 5 },
2527 { EL_YAMYAM_DOWN, 5 },
2528 { EL_DARK_YAMYAM, 6 },
2531 { EL_PACMAN_LEFT, 8 },
2532 { EL_PACMAN_RIGHT, 8 },
2533 { EL_PACMAN_UP, 8 },
2534 { EL_PACMAN_DOWN, 8 },
2536 { EL_MOLE_LEFT, 9 },
2537 { EL_MOLE_RIGHT, 9 },
2539 { EL_MOLE_DOWN, 9 },
2543 { EL_SATELLITE, 13 },
2544 { EL_SP_SNIKSNAK, 14 },
2545 { EL_SP_ELECTRON, 15 },
2548 { EL_SPRING_LEFT, 17 },
2549 { EL_SPRING_RIGHT, 17 },
2550 { EL_EMC_ANDROID, 18 },
2555 static struct PropertyBitInfo pb_dont_collide_with[] =
2557 { EL_SP_SNIKSNAK, 0 },
2558 { EL_SP_ELECTRON, 1 },
2566 struct PropertyBitInfo *pb_info;
2569 { EP_CAN_MOVE_INTO_ACID, pb_can_move_into_acid },
2570 { EP_DONT_COLLIDE_WITH, pb_dont_collide_with },
2575 struct PropertyBitInfo *pb_info = NULL;
2578 for (i = 0; pb_definition[i].bit_nr != -1; i++)
2579 if (pb_definition[i].bit_nr == property_bit_nr)
2580 pb_info = pb_definition[i].pb_info;
2582 if (pb_info == NULL)
2585 for (i = 0; pb_info[i].element != -1; i++)
2586 if (pb_info[i].element == element)
2587 return pb_info[i].bit_nr;
2592 void setBitfieldProperty(int *bitfield, int property_bit_nr, int element,
2593 boolean property_value)
2595 int bit_nr = get_special_property_bit(element, property_bit_nr);
2600 *bitfield |= (1 << bit_nr);
2602 *bitfield &= ~(1 << bit_nr);
2606 boolean getBitfieldProperty(int *bitfield, int property_bit_nr, int element)
2608 int bit_nr = get_special_property_bit(element, property_bit_nr);
2611 return ((*bitfield & (1 << bit_nr)) != 0);
2616 static void ResolveGroupElementExt(int group_element, int recursion_depth)
2618 static int group_nr;
2619 static struct ElementGroupInfo *group;
2620 struct ElementGroupInfo *actual_group = element_info[group_element].group;
2623 if (actual_group == NULL) // not yet initialized
2626 if (recursion_depth > NUM_GROUP_ELEMENTS) // recursion too deep
2628 Warn("recursion too deep when resolving group element %d",
2629 group_element - EL_GROUP_START + 1);
2631 // replace element which caused too deep recursion by question mark
2632 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
2637 if (recursion_depth == 0) // initialization
2639 group = actual_group;
2640 group_nr = GROUP_NR(group_element);
2642 group->num_elements_resolved = 0;
2643 group->choice_pos = 0;
2645 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2646 element_info[i].in_group[group_nr] = FALSE;
2649 for (i = 0; i < actual_group->num_elements; i++)
2651 int element = actual_group->element[i];
2653 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
2656 if (IS_GROUP_ELEMENT(element))
2657 ResolveGroupElementExt(element, recursion_depth + 1);
2660 group->element_resolved[group->num_elements_resolved++] = element;
2661 element_info[element].in_group[group_nr] = TRUE;
2666 void ResolveGroupElement(int group_element)
2668 ResolveGroupElementExt(group_element, 0);
2671 void InitElementPropertiesStatic(void)
2673 static boolean clipboard_elements_initialized = FALSE;
2675 static int ep_diggable[] =
2680 EL_SP_BUGGY_BASE_ACTIVATING,
2683 EL_INVISIBLE_SAND_ACTIVE,
2686 // !!! currently not diggable, but handled by 'ep_dont_run_into' !!!
2687 // (if amoeba can grow into anything diggable, maybe keep these out)
2692 EL_SP_BUGGY_BASE_ACTIVE,
2699 static int ep_collectible_only[] =
2721 EL_DYNABOMB_INCREASE_NUMBER,
2722 EL_DYNABOMB_INCREASE_SIZE,
2723 EL_DYNABOMB_INCREASE_POWER,
2741 // !!! handle separately !!!
2742 EL_DC_LANDMINE, // deadly when running into, but can be snapped
2748 static int ep_dont_run_into[] =
2750 // same elements as in 'ep_dont_touch'
2756 // same elements as in 'ep_dont_collide_with'
2768 // !!! maybe this should better be handled by 'ep_diggable' !!!
2773 EL_SP_BUGGY_BASE_ACTIVE,
2780 static int ep_dont_collide_with[] =
2782 // same elements as in 'ep_dont_touch'
2799 static int ep_dont_touch[] =
2809 static int ep_indestructible[] =
2813 EL_ACID_POOL_TOPLEFT,
2814 EL_ACID_POOL_TOPRIGHT,
2815 EL_ACID_POOL_BOTTOMLEFT,
2816 EL_ACID_POOL_BOTTOM,
2817 EL_ACID_POOL_BOTTOMRIGHT,
2818 EL_SP_HARDWARE_GRAY,
2819 EL_SP_HARDWARE_GREEN,
2820 EL_SP_HARDWARE_BLUE,
2822 EL_SP_HARDWARE_YELLOW,
2823 EL_SP_HARDWARE_BASE_1,
2824 EL_SP_HARDWARE_BASE_2,
2825 EL_SP_HARDWARE_BASE_3,
2826 EL_SP_HARDWARE_BASE_4,
2827 EL_SP_HARDWARE_BASE_5,
2828 EL_SP_HARDWARE_BASE_6,
2829 EL_INVISIBLE_STEELWALL,
2830 EL_INVISIBLE_STEELWALL_ACTIVE,
2831 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2832 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
2833 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
2834 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2835 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
2836 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
2837 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2838 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
2839 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
2840 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
2841 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
2842 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
2844 EL_LIGHT_SWITCH_ACTIVE,
2845 EL_SIGN_EXCLAMATION,
2846 EL_SIGN_RADIOACTIVITY,
2853 EL_SIGN_ENTRY_FORBIDDEN,
2854 EL_SIGN_EMERGENCY_EXIT,
2862 EL_STEEL_EXIT_CLOSED,
2864 EL_STEEL_EXIT_OPENING,
2865 EL_STEEL_EXIT_CLOSING,
2866 EL_EM_STEEL_EXIT_CLOSED,
2867 EL_EM_STEEL_EXIT_OPEN,
2868 EL_EM_STEEL_EXIT_OPENING,
2869 EL_EM_STEEL_EXIT_CLOSING,
2870 EL_DC_STEELWALL_1_LEFT,
2871 EL_DC_STEELWALL_1_RIGHT,
2872 EL_DC_STEELWALL_1_TOP,
2873 EL_DC_STEELWALL_1_BOTTOM,
2874 EL_DC_STEELWALL_1_HORIZONTAL,
2875 EL_DC_STEELWALL_1_VERTICAL,
2876 EL_DC_STEELWALL_1_TOPLEFT,
2877 EL_DC_STEELWALL_1_TOPRIGHT,
2878 EL_DC_STEELWALL_1_BOTTOMLEFT,
2879 EL_DC_STEELWALL_1_BOTTOMRIGHT,
2880 EL_DC_STEELWALL_1_TOPLEFT_2,
2881 EL_DC_STEELWALL_1_TOPRIGHT_2,
2882 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
2883 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
2884 EL_DC_STEELWALL_2_LEFT,
2885 EL_DC_STEELWALL_2_RIGHT,
2886 EL_DC_STEELWALL_2_TOP,
2887 EL_DC_STEELWALL_2_BOTTOM,
2888 EL_DC_STEELWALL_2_HORIZONTAL,
2889 EL_DC_STEELWALL_2_VERTICAL,
2890 EL_DC_STEELWALL_2_MIDDLE,
2891 EL_DC_STEELWALL_2_SINGLE,
2892 EL_STEELWALL_SLIPPERY,
2906 EL_GATE_1_GRAY_ACTIVE,
2907 EL_GATE_2_GRAY_ACTIVE,
2908 EL_GATE_3_GRAY_ACTIVE,
2909 EL_GATE_4_GRAY_ACTIVE,
2918 EL_EM_GATE_1_GRAY_ACTIVE,
2919 EL_EM_GATE_2_GRAY_ACTIVE,
2920 EL_EM_GATE_3_GRAY_ACTIVE,
2921 EL_EM_GATE_4_GRAY_ACTIVE,
2930 EL_EMC_GATE_5_GRAY_ACTIVE,
2931 EL_EMC_GATE_6_GRAY_ACTIVE,
2932 EL_EMC_GATE_7_GRAY_ACTIVE,
2933 EL_EMC_GATE_8_GRAY_ACTIVE,
2935 EL_DC_GATE_WHITE_GRAY,
2936 EL_DC_GATE_WHITE_GRAY_ACTIVE,
2937 EL_DC_GATE_FAKE_GRAY,
2939 EL_SWITCHGATE_OPENING,
2940 EL_SWITCHGATE_CLOSED,
2941 EL_SWITCHGATE_CLOSING,
2942 EL_DC_SWITCHGATE_SWITCH_UP,
2943 EL_DC_SWITCHGATE_SWITCH_DOWN,
2945 EL_TIMEGATE_OPENING,
2947 EL_TIMEGATE_CLOSING,
2948 EL_DC_TIMEGATE_SWITCH,
2949 EL_DC_TIMEGATE_SWITCH_ACTIVE,
2953 EL_TUBE_VERTICAL_LEFT,
2954 EL_TUBE_VERTICAL_RIGHT,
2955 EL_TUBE_HORIZONTAL_UP,
2956 EL_TUBE_HORIZONTAL_DOWN,
2961 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
2962 EL_EXPANDABLE_STEELWALL_VERTICAL,
2963 EL_EXPANDABLE_STEELWALL_ANY,
2968 static int ep_slippery[] =
2982 EL_ROBOT_WHEEL_ACTIVE,
2988 EL_ACID_POOL_TOPLEFT,
2989 EL_ACID_POOL_TOPRIGHT,
2999 EL_STEELWALL_SLIPPERY,
3002 EL_EMC_WALL_SLIPPERY_1,
3003 EL_EMC_WALL_SLIPPERY_2,
3004 EL_EMC_WALL_SLIPPERY_3,
3005 EL_EMC_WALL_SLIPPERY_4,
3007 EL_EMC_MAGIC_BALL_ACTIVE,
3012 static int ep_can_change[] =
3017 static int ep_can_move[] =
3019 // same elements as in 'pb_can_move_into_acid'
3042 static int ep_can_fall[] =
3057 EL_QUICKSAND_FAST_FULL,
3059 EL_BD_MAGIC_WALL_FULL,
3060 EL_DC_MAGIC_WALL_FULL,
3074 static int ep_can_smash_player[] =
3100 static int ep_can_smash_enemies[] =
3109 static int ep_can_smash_everything[] =
3118 static int ep_explodes_by_fire[] =
3120 // same elements as in 'ep_explodes_impact'
3125 // same elements as in 'ep_explodes_smashed'
3135 EL_EM_DYNAMITE_ACTIVE,
3136 EL_DYNABOMB_PLAYER_1_ACTIVE,
3137 EL_DYNABOMB_PLAYER_2_ACTIVE,
3138 EL_DYNABOMB_PLAYER_3_ACTIVE,
3139 EL_DYNABOMB_PLAYER_4_ACTIVE,
3140 EL_DYNABOMB_INCREASE_NUMBER,
3141 EL_DYNABOMB_INCREASE_SIZE,
3142 EL_DYNABOMB_INCREASE_POWER,
3143 EL_SP_DISK_RED_ACTIVE,
3157 static int ep_explodes_smashed[] =
3159 // same elements as in 'ep_explodes_impact'
3173 static int ep_explodes_impact[] =
3182 static int ep_walkable_over[] =
3202 EL_SOKOBAN_FIELD_EMPTY,
3209 EL_EM_STEEL_EXIT_OPEN,
3210 EL_EM_STEEL_EXIT_OPENING,
3219 EL_GATE_1_GRAY_ACTIVE,
3220 EL_GATE_2_GRAY_ACTIVE,
3221 EL_GATE_3_GRAY_ACTIVE,
3222 EL_GATE_4_GRAY_ACTIVE,
3230 static int ep_walkable_inside[] =
3235 EL_TUBE_VERTICAL_LEFT,
3236 EL_TUBE_VERTICAL_RIGHT,
3237 EL_TUBE_HORIZONTAL_UP,
3238 EL_TUBE_HORIZONTAL_DOWN,
3247 static int ep_walkable_under[] =
3252 static int ep_passable_over[] =
3262 EL_EM_GATE_1_GRAY_ACTIVE,
3263 EL_EM_GATE_2_GRAY_ACTIVE,
3264 EL_EM_GATE_3_GRAY_ACTIVE,
3265 EL_EM_GATE_4_GRAY_ACTIVE,
3274 EL_EMC_GATE_5_GRAY_ACTIVE,
3275 EL_EMC_GATE_6_GRAY_ACTIVE,
3276 EL_EMC_GATE_7_GRAY_ACTIVE,
3277 EL_EMC_GATE_8_GRAY_ACTIVE,
3279 EL_DC_GATE_WHITE_GRAY,
3280 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3287 static int ep_passable_inside[] =
3293 EL_SP_PORT_HORIZONTAL,
3294 EL_SP_PORT_VERTICAL,
3296 EL_SP_GRAVITY_PORT_LEFT,
3297 EL_SP_GRAVITY_PORT_RIGHT,
3298 EL_SP_GRAVITY_PORT_UP,
3299 EL_SP_GRAVITY_PORT_DOWN,
3300 EL_SP_GRAVITY_ON_PORT_LEFT,
3301 EL_SP_GRAVITY_ON_PORT_RIGHT,
3302 EL_SP_GRAVITY_ON_PORT_UP,
3303 EL_SP_GRAVITY_ON_PORT_DOWN,
3304 EL_SP_GRAVITY_OFF_PORT_LEFT,
3305 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3306 EL_SP_GRAVITY_OFF_PORT_UP,
3307 EL_SP_GRAVITY_OFF_PORT_DOWN,
3312 static int ep_passable_under[] =
3317 static int ep_droppable[] =
3322 static int ep_explodes_1x1_old[] =
3327 static int ep_pushable[] =
3339 EL_SOKOBAN_FIELD_FULL,
3348 static int ep_explodes_cross_old[] =
3353 static int ep_protected[] =
3355 // same elements as in 'ep_walkable_inside'
3359 EL_TUBE_VERTICAL_LEFT,
3360 EL_TUBE_VERTICAL_RIGHT,
3361 EL_TUBE_HORIZONTAL_UP,
3362 EL_TUBE_HORIZONTAL_DOWN,
3368 // same elements as in 'ep_passable_over'
3377 EL_EM_GATE_1_GRAY_ACTIVE,
3378 EL_EM_GATE_2_GRAY_ACTIVE,
3379 EL_EM_GATE_3_GRAY_ACTIVE,
3380 EL_EM_GATE_4_GRAY_ACTIVE,
3389 EL_EMC_GATE_5_GRAY_ACTIVE,
3390 EL_EMC_GATE_6_GRAY_ACTIVE,
3391 EL_EMC_GATE_7_GRAY_ACTIVE,
3392 EL_EMC_GATE_8_GRAY_ACTIVE,
3394 EL_DC_GATE_WHITE_GRAY,
3395 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3399 // same elements as in 'ep_passable_inside'
3404 EL_SP_PORT_HORIZONTAL,
3405 EL_SP_PORT_VERTICAL,
3407 EL_SP_GRAVITY_PORT_LEFT,
3408 EL_SP_GRAVITY_PORT_RIGHT,
3409 EL_SP_GRAVITY_PORT_UP,
3410 EL_SP_GRAVITY_PORT_DOWN,
3411 EL_SP_GRAVITY_ON_PORT_LEFT,
3412 EL_SP_GRAVITY_ON_PORT_RIGHT,
3413 EL_SP_GRAVITY_ON_PORT_UP,
3414 EL_SP_GRAVITY_ON_PORT_DOWN,
3415 EL_SP_GRAVITY_OFF_PORT_LEFT,
3416 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3417 EL_SP_GRAVITY_OFF_PORT_UP,
3418 EL_SP_GRAVITY_OFF_PORT_DOWN,
3423 static int ep_throwable[] =
3428 static int ep_can_explode[] =
3430 // same elements as in 'ep_explodes_impact'
3435 // same elements as in 'ep_explodes_smashed'
3441 // elements that can explode by explosion or by dragonfire
3445 EL_EM_DYNAMITE_ACTIVE,
3446 EL_DYNABOMB_PLAYER_1_ACTIVE,
3447 EL_DYNABOMB_PLAYER_2_ACTIVE,
3448 EL_DYNABOMB_PLAYER_3_ACTIVE,
3449 EL_DYNABOMB_PLAYER_4_ACTIVE,
3450 EL_DYNABOMB_INCREASE_NUMBER,
3451 EL_DYNABOMB_INCREASE_SIZE,
3452 EL_DYNABOMB_INCREASE_POWER,
3453 EL_SP_DISK_RED_ACTIVE,
3461 // elements that can explode only by explosion
3467 static int ep_gravity_reachable[] =
3473 EL_INVISIBLE_SAND_ACTIVE,
3478 EL_SP_PORT_HORIZONTAL,
3479 EL_SP_PORT_VERTICAL,
3481 EL_SP_GRAVITY_PORT_LEFT,
3482 EL_SP_GRAVITY_PORT_RIGHT,
3483 EL_SP_GRAVITY_PORT_UP,
3484 EL_SP_GRAVITY_PORT_DOWN,
3485 EL_SP_GRAVITY_ON_PORT_LEFT,
3486 EL_SP_GRAVITY_ON_PORT_RIGHT,
3487 EL_SP_GRAVITY_ON_PORT_UP,
3488 EL_SP_GRAVITY_ON_PORT_DOWN,
3489 EL_SP_GRAVITY_OFF_PORT_LEFT,
3490 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3491 EL_SP_GRAVITY_OFF_PORT_UP,
3492 EL_SP_GRAVITY_OFF_PORT_DOWN,
3498 static int ep_empty_space[] =
3521 static int ep_player[] =
3528 EL_SOKOBAN_FIELD_PLAYER,
3534 static int ep_can_pass_magic_wall[] =
3548 static int ep_can_pass_dc_magic_wall[] =
3564 static int ep_switchable[] =
3568 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3569 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3570 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3571 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3572 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3573 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3574 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3575 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3576 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3577 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3578 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3579 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3580 EL_SWITCHGATE_SWITCH_UP,
3581 EL_SWITCHGATE_SWITCH_DOWN,
3582 EL_DC_SWITCHGATE_SWITCH_UP,
3583 EL_DC_SWITCHGATE_SWITCH_DOWN,
3585 EL_LIGHT_SWITCH_ACTIVE,
3587 EL_DC_TIMEGATE_SWITCH,
3588 EL_BALLOON_SWITCH_LEFT,
3589 EL_BALLOON_SWITCH_RIGHT,
3590 EL_BALLOON_SWITCH_UP,
3591 EL_BALLOON_SWITCH_DOWN,
3592 EL_BALLOON_SWITCH_ANY,
3593 EL_BALLOON_SWITCH_NONE,
3596 EL_EMC_MAGIC_BALL_SWITCH,
3597 EL_EMC_MAGIC_BALL_SWITCH_ACTIVE,
3602 static int ep_bd_element[] =
3622 EL_BD_FIREFLY_RIGHT,
3625 EL_BD_BUTTERFLY_DOWN,
3626 EL_BD_BUTTERFLY_LEFT,
3628 EL_BD_BUTTERFLY_RIGHT,
3636 static int ep_sp_element[] =
3638 // should always be valid
3641 // standard classic Supaplex elements
3648 EL_SP_HARDWARE_GRAY,
3656 EL_SP_GRAVITY_PORT_RIGHT,
3657 EL_SP_GRAVITY_PORT_DOWN,
3658 EL_SP_GRAVITY_PORT_LEFT,
3659 EL_SP_GRAVITY_PORT_UP,
3664 EL_SP_PORT_VERTICAL,
3665 EL_SP_PORT_HORIZONTAL,
3671 EL_SP_HARDWARE_BASE_1,
3672 EL_SP_HARDWARE_GREEN,
3673 EL_SP_HARDWARE_BLUE,
3675 EL_SP_HARDWARE_YELLOW,
3676 EL_SP_HARDWARE_BASE_2,
3677 EL_SP_HARDWARE_BASE_3,
3678 EL_SP_HARDWARE_BASE_4,
3679 EL_SP_HARDWARE_BASE_5,
3680 EL_SP_HARDWARE_BASE_6,
3684 // additional elements that appeared in newer Supaplex levels
3687 // additional gravity port elements (not switching, but setting gravity)
3688 EL_SP_GRAVITY_ON_PORT_LEFT,
3689 EL_SP_GRAVITY_ON_PORT_RIGHT,
3690 EL_SP_GRAVITY_ON_PORT_UP,
3691 EL_SP_GRAVITY_ON_PORT_DOWN,
3692 EL_SP_GRAVITY_OFF_PORT_LEFT,
3693 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3694 EL_SP_GRAVITY_OFF_PORT_UP,
3695 EL_SP_GRAVITY_OFF_PORT_DOWN,
3697 // more than one Murphy in a level results in an inactive clone
3700 // runtime Supaplex elements
3701 EL_SP_DISK_RED_ACTIVE,
3702 EL_SP_TERMINAL_ACTIVE,
3703 EL_SP_BUGGY_BASE_ACTIVATING,
3704 EL_SP_BUGGY_BASE_ACTIVE,
3711 static int ep_sb_element[] =
3716 EL_SOKOBAN_FIELD_EMPTY,
3717 EL_SOKOBAN_FIELD_FULL,
3718 EL_SOKOBAN_FIELD_PLAYER,
3723 EL_INVISIBLE_STEELWALL,
3728 static int ep_gem[] =
3740 static int ep_food_dark_yamyam[] =
3768 static int ep_food_penguin[] =
3782 static int ep_food_pig[] =
3794 static int ep_historic_wall[] =
3805 EL_GATE_1_GRAY_ACTIVE,
3806 EL_GATE_2_GRAY_ACTIVE,
3807 EL_GATE_3_GRAY_ACTIVE,
3808 EL_GATE_4_GRAY_ACTIVE,
3817 EL_EM_GATE_1_GRAY_ACTIVE,
3818 EL_EM_GATE_2_GRAY_ACTIVE,
3819 EL_EM_GATE_3_GRAY_ACTIVE,
3820 EL_EM_GATE_4_GRAY_ACTIVE,
3827 EL_EXPANDABLE_WALL_HORIZONTAL,
3828 EL_EXPANDABLE_WALL_VERTICAL,
3829 EL_EXPANDABLE_WALL_ANY,
3830 EL_EXPANDABLE_WALL_GROWING,
3831 EL_BD_EXPANDABLE_WALL,
3838 EL_SP_HARDWARE_GRAY,
3839 EL_SP_HARDWARE_GREEN,
3840 EL_SP_HARDWARE_BLUE,
3842 EL_SP_HARDWARE_YELLOW,
3843 EL_SP_HARDWARE_BASE_1,
3844 EL_SP_HARDWARE_BASE_2,
3845 EL_SP_HARDWARE_BASE_3,
3846 EL_SP_HARDWARE_BASE_4,
3847 EL_SP_HARDWARE_BASE_5,
3848 EL_SP_HARDWARE_BASE_6,
3850 EL_SP_TERMINAL_ACTIVE,
3853 EL_INVISIBLE_STEELWALL,
3854 EL_INVISIBLE_STEELWALL_ACTIVE,
3856 EL_INVISIBLE_WALL_ACTIVE,
3857 EL_STEELWALL_SLIPPERY,
3874 static int ep_historic_solid[] =
3878 EL_EXPANDABLE_WALL_HORIZONTAL,
3879 EL_EXPANDABLE_WALL_VERTICAL,
3880 EL_EXPANDABLE_WALL_ANY,
3881 EL_BD_EXPANDABLE_WALL,
3894 EL_QUICKSAND_FILLING,
3895 EL_QUICKSAND_EMPTYING,
3897 EL_MAGIC_WALL_ACTIVE,
3898 EL_MAGIC_WALL_EMPTYING,
3899 EL_MAGIC_WALL_FILLING,
3903 EL_BD_MAGIC_WALL_ACTIVE,
3904 EL_BD_MAGIC_WALL_EMPTYING,
3905 EL_BD_MAGIC_WALL_FULL,
3906 EL_BD_MAGIC_WALL_FILLING,
3907 EL_BD_MAGIC_WALL_DEAD,
3916 EL_SP_TERMINAL_ACTIVE,
3920 EL_INVISIBLE_WALL_ACTIVE,
3921 EL_SWITCHGATE_SWITCH_UP,
3922 EL_SWITCHGATE_SWITCH_DOWN,
3924 EL_TIMEGATE_SWITCH_ACTIVE,
3936 // the following elements are a direct copy of "indestructible" elements,
3937 // except "EL_ACID", which is "indestructible", but not "solid"!
3942 EL_ACID_POOL_TOPLEFT,
3943 EL_ACID_POOL_TOPRIGHT,
3944 EL_ACID_POOL_BOTTOMLEFT,
3945 EL_ACID_POOL_BOTTOM,
3946 EL_ACID_POOL_BOTTOMRIGHT,
3947 EL_SP_HARDWARE_GRAY,
3948 EL_SP_HARDWARE_GREEN,
3949 EL_SP_HARDWARE_BLUE,
3951 EL_SP_HARDWARE_YELLOW,
3952 EL_SP_HARDWARE_BASE_1,
3953 EL_SP_HARDWARE_BASE_2,
3954 EL_SP_HARDWARE_BASE_3,
3955 EL_SP_HARDWARE_BASE_4,
3956 EL_SP_HARDWARE_BASE_5,
3957 EL_SP_HARDWARE_BASE_6,
3958 EL_INVISIBLE_STEELWALL,
3959 EL_INVISIBLE_STEELWALL_ACTIVE,
3960 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3961 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3962 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3963 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3964 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3965 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3966 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3967 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3968 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3969 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3970 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3971 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3973 EL_LIGHT_SWITCH_ACTIVE,
3974 EL_SIGN_EXCLAMATION,
3975 EL_SIGN_RADIOACTIVITY,
3982 EL_SIGN_ENTRY_FORBIDDEN,
3983 EL_SIGN_EMERGENCY_EXIT,
3991 EL_STEEL_EXIT_CLOSED,
3993 EL_STEEL_EXIT_OPENING,
3994 EL_STEEL_EXIT_CLOSING,
3995 EL_EM_STEEL_EXIT_CLOSED,
3996 EL_EM_STEEL_EXIT_OPEN,
3997 EL_EM_STEEL_EXIT_OPENING,
3998 EL_EM_STEEL_EXIT_CLOSING,
3999 EL_DC_STEELWALL_1_LEFT,
4000 EL_DC_STEELWALL_1_RIGHT,
4001 EL_DC_STEELWALL_1_TOP,
4002 EL_DC_STEELWALL_1_BOTTOM,
4003 EL_DC_STEELWALL_1_HORIZONTAL,
4004 EL_DC_STEELWALL_1_VERTICAL,
4005 EL_DC_STEELWALL_1_TOPLEFT,
4006 EL_DC_STEELWALL_1_TOPRIGHT,
4007 EL_DC_STEELWALL_1_BOTTOMLEFT,
4008 EL_DC_STEELWALL_1_BOTTOMRIGHT,
4009 EL_DC_STEELWALL_1_TOPLEFT_2,
4010 EL_DC_STEELWALL_1_TOPRIGHT_2,
4011 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
4012 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
4013 EL_DC_STEELWALL_2_LEFT,
4014 EL_DC_STEELWALL_2_RIGHT,
4015 EL_DC_STEELWALL_2_TOP,
4016 EL_DC_STEELWALL_2_BOTTOM,
4017 EL_DC_STEELWALL_2_HORIZONTAL,
4018 EL_DC_STEELWALL_2_VERTICAL,
4019 EL_DC_STEELWALL_2_MIDDLE,
4020 EL_DC_STEELWALL_2_SINGLE,
4021 EL_STEELWALL_SLIPPERY,
4035 EL_GATE_1_GRAY_ACTIVE,
4036 EL_GATE_2_GRAY_ACTIVE,
4037 EL_GATE_3_GRAY_ACTIVE,
4038 EL_GATE_4_GRAY_ACTIVE,
4047 EL_EM_GATE_1_GRAY_ACTIVE,
4048 EL_EM_GATE_2_GRAY_ACTIVE,
4049 EL_EM_GATE_3_GRAY_ACTIVE,
4050 EL_EM_GATE_4_GRAY_ACTIVE,
4059 EL_EMC_GATE_5_GRAY_ACTIVE,
4060 EL_EMC_GATE_6_GRAY_ACTIVE,
4061 EL_EMC_GATE_7_GRAY_ACTIVE,
4062 EL_EMC_GATE_8_GRAY_ACTIVE,
4064 EL_DC_GATE_WHITE_GRAY,
4065 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4066 EL_DC_GATE_FAKE_GRAY,
4068 EL_SWITCHGATE_OPENING,
4069 EL_SWITCHGATE_CLOSED,
4070 EL_SWITCHGATE_CLOSING,
4071 EL_DC_SWITCHGATE_SWITCH_UP,
4072 EL_DC_SWITCHGATE_SWITCH_DOWN,
4074 EL_TIMEGATE_OPENING,
4076 EL_TIMEGATE_CLOSING,
4077 EL_DC_TIMEGATE_SWITCH,
4078 EL_DC_TIMEGATE_SWITCH_ACTIVE,
4082 EL_TUBE_VERTICAL_LEFT,
4083 EL_TUBE_VERTICAL_RIGHT,
4084 EL_TUBE_HORIZONTAL_UP,
4085 EL_TUBE_HORIZONTAL_DOWN,
4090 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
4091 EL_EXPANDABLE_STEELWALL_VERTICAL,
4092 EL_EXPANDABLE_STEELWALL_ANY,
4097 static int ep_classic_enemy[] =
4114 static int ep_belt[] =
4116 EL_CONVEYOR_BELT_1_LEFT,
4117 EL_CONVEYOR_BELT_1_MIDDLE,
4118 EL_CONVEYOR_BELT_1_RIGHT,
4119 EL_CONVEYOR_BELT_2_LEFT,
4120 EL_CONVEYOR_BELT_2_MIDDLE,
4121 EL_CONVEYOR_BELT_2_RIGHT,
4122 EL_CONVEYOR_BELT_3_LEFT,
4123 EL_CONVEYOR_BELT_3_MIDDLE,
4124 EL_CONVEYOR_BELT_3_RIGHT,
4125 EL_CONVEYOR_BELT_4_LEFT,
4126 EL_CONVEYOR_BELT_4_MIDDLE,
4127 EL_CONVEYOR_BELT_4_RIGHT,
4132 static int ep_belt_active[] =
4134 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4135 EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE,
4136 EL_CONVEYOR_BELT_1_RIGHT_ACTIVE,
4137 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4138 EL_CONVEYOR_BELT_2_MIDDLE_ACTIVE,
4139 EL_CONVEYOR_BELT_2_RIGHT_ACTIVE,
4140 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4141 EL_CONVEYOR_BELT_3_MIDDLE_ACTIVE,
4142 EL_CONVEYOR_BELT_3_RIGHT_ACTIVE,
4143 EL_CONVEYOR_BELT_4_LEFT_ACTIVE,
4144 EL_CONVEYOR_BELT_4_MIDDLE_ACTIVE,
4145 EL_CONVEYOR_BELT_4_RIGHT_ACTIVE,
4150 static int ep_belt_switch[] =
4152 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4153 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4154 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4155 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4156 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4157 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4158 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4159 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4160 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4161 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4162 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4163 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4168 static int ep_tube[] =
4175 EL_TUBE_HORIZONTAL_UP,
4176 EL_TUBE_HORIZONTAL_DOWN,
4178 EL_TUBE_VERTICAL_LEFT,
4179 EL_TUBE_VERTICAL_RIGHT,
4185 static int ep_acid_pool[] =
4187 EL_ACID_POOL_TOPLEFT,
4188 EL_ACID_POOL_TOPRIGHT,
4189 EL_ACID_POOL_BOTTOMLEFT,
4190 EL_ACID_POOL_BOTTOM,
4191 EL_ACID_POOL_BOTTOMRIGHT,
4196 static int ep_keygate[] =
4206 EL_GATE_1_GRAY_ACTIVE,
4207 EL_GATE_2_GRAY_ACTIVE,
4208 EL_GATE_3_GRAY_ACTIVE,
4209 EL_GATE_4_GRAY_ACTIVE,
4218 EL_EM_GATE_1_GRAY_ACTIVE,
4219 EL_EM_GATE_2_GRAY_ACTIVE,
4220 EL_EM_GATE_3_GRAY_ACTIVE,
4221 EL_EM_GATE_4_GRAY_ACTIVE,
4230 EL_EMC_GATE_5_GRAY_ACTIVE,
4231 EL_EMC_GATE_6_GRAY_ACTIVE,
4232 EL_EMC_GATE_7_GRAY_ACTIVE,
4233 EL_EMC_GATE_8_GRAY_ACTIVE,
4235 EL_DC_GATE_WHITE_GRAY,
4236 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4241 static int ep_amoeboid[] =
4253 static int ep_amoebalive[] =
4264 static int ep_has_editor_content[] =
4270 EL_SOKOBAN_FIELD_PLAYER,
4296 static int ep_can_turn_each_move[] =
4298 // !!! do something with this one !!!
4302 static int ep_can_grow[] =
4316 static int ep_active_bomb[] =
4319 EL_EM_DYNAMITE_ACTIVE,
4320 EL_DYNABOMB_PLAYER_1_ACTIVE,
4321 EL_DYNABOMB_PLAYER_2_ACTIVE,
4322 EL_DYNABOMB_PLAYER_3_ACTIVE,
4323 EL_DYNABOMB_PLAYER_4_ACTIVE,
4324 EL_SP_DISK_RED_ACTIVE,
4329 static int ep_inactive[] =
4355 EL_QUICKSAND_FAST_EMPTY,
4378 EL_GATE_1_GRAY_ACTIVE,
4379 EL_GATE_2_GRAY_ACTIVE,
4380 EL_GATE_3_GRAY_ACTIVE,
4381 EL_GATE_4_GRAY_ACTIVE,
4390 EL_EM_GATE_1_GRAY_ACTIVE,
4391 EL_EM_GATE_2_GRAY_ACTIVE,
4392 EL_EM_GATE_3_GRAY_ACTIVE,
4393 EL_EM_GATE_4_GRAY_ACTIVE,
4402 EL_EMC_GATE_5_GRAY_ACTIVE,
4403 EL_EMC_GATE_6_GRAY_ACTIVE,
4404 EL_EMC_GATE_7_GRAY_ACTIVE,
4405 EL_EMC_GATE_8_GRAY_ACTIVE,
4407 EL_DC_GATE_WHITE_GRAY,
4408 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4409 EL_DC_GATE_FAKE_GRAY,
4412 EL_INVISIBLE_STEELWALL,
4420 EL_WALL_EMERALD_YELLOW,
4421 EL_DYNABOMB_INCREASE_NUMBER,
4422 EL_DYNABOMB_INCREASE_SIZE,
4423 EL_DYNABOMB_INCREASE_POWER,
4427 EL_SOKOBAN_FIELD_EMPTY,
4428 EL_SOKOBAN_FIELD_FULL,
4429 EL_WALL_EMERALD_RED,
4430 EL_WALL_EMERALD_PURPLE,
4431 EL_ACID_POOL_TOPLEFT,
4432 EL_ACID_POOL_TOPRIGHT,
4433 EL_ACID_POOL_BOTTOMLEFT,
4434 EL_ACID_POOL_BOTTOM,
4435 EL_ACID_POOL_BOTTOMRIGHT,
4439 EL_BD_MAGIC_WALL_DEAD,
4441 EL_DC_MAGIC_WALL_DEAD,
4442 EL_AMOEBA_TO_DIAMOND,
4450 EL_SP_GRAVITY_PORT_RIGHT,
4451 EL_SP_GRAVITY_PORT_DOWN,
4452 EL_SP_GRAVITY_PORT_LEFT,
4453 EL_SP_GRAVITY_PORT_UP,
4454 EL_SP_PORT_HORIZONTAL,
4455 EL_SP_PORT_VERTICAL,
4466 EL_SP_HARDWARE_GRAY,
4467 EL_SP_HARDWARE_GREEN,
4468 EL_SP_HARDWARE_BLUE,
4470 EL_SP_HARDWARE_YELLOW,
4471 EL_SP_HARDWARE_BASE_1,
4472 EL_SP_HARDWARE_BASE_2,
4473 EL_SP_HARDWARE_BASE_3,
4474 EL_SP_HARDWARE_BASE_4,
4475 EL_SP_HARDWARE_BASE_5,
4476 EL_SP_HARDWARE_BASE_6,
4477 EL_SP_GRAVITY_ON_PORT_LEFT,
4478 EL_SP_GRAVITY_ON_PORT_RIGHT,
4479 EL_SP_GRAVITY_ON_PORT_UP,
4480 EL_SP_GRAVITY_ON_PORT_DOWN,
4481 EL_SP_GRAVITY_OFF_PORT_LEFT,
4482 EL_SP_GRAVITY_OFF_PORT_RIGHT,
4483 EL_SP_GRAVITY_OFF_PORT_UP,
4484 EL_SP_GRAVITY_OFF_PORT_DOWN,
4485 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4486 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4487 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4488 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4489 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4490 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4491 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4492 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4493 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4494 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4495 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4496 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4497 EL_SIGN_EXCLAMATION,
4498 EL_SIGN_RADIOACTIVITY,
4505 EL_SIGN_ENTRY_FORBIDDEN,
4506 EL_SIGN_EMERGENCY_EXIT,
4514 EL_DC_STEELWALL_1_LEFT,
4515 EL_DC_STEELWALL_1_RIGHT,
4516 EL_DC_STEELWALL_1_TOP,
4517 EL_DC_STEELWALL_1_BOTTOM,
4518 EL_DC_STEELWALL_1_HORIZONTAL,
4519 EL_DC_STEELWALL_1_VERTICAL,
4520 EL_DC_STEELWALL_1_TOPLEFT,
4521 EL_DC_STEELWALL_1_TOPRIGHT,
4522 EL_DC_STEELWALL_1_BOTTOMLEFT,
4523 EL_DC_STEELWALL_1_BOTTOMRIGHT,
4524 EL_DC_STEELWALL_1_TOPLEFT_2,
4525 EL_DC_STEELWALL_1_TOPRIGHT_2,
4526 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
4527 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
4528 EL_DC_STEELWALL_2_LEFT,
4529 EL_DC_STEELWALL_2_RIGHT,
4530 EL_DC_STEELWALL_2_TOP,
4531 EL_DC_STEELWALL_2_BOTTOM,
4532 EL_DC_STEELWALL_2_HORIZONTAL,
4533 EL_DC_STEELWALL_2_VERTICAL,
4534 EL_DC_STEELWALL_2_MIDDLE,
4535 EL_DC_STEELWALL_2_SINGLE,
4536 EL_STEELWALL_SLIPPERY,
4541 EL_EMC_WALL_SLIPPERY_1,
4542 EL_EMC_WALL_SLIPPERY_2,
4543 EL_EMC_WALL_SLIPPERY_3,
4544 EL_EMC_WALL_SLIPPERY_4,
4565 static int ep_em_slippery_wall[] =
4570 static int ep_gfx_crumbled[] =
4581 static int ep_editor_cascade_active[] =
4583 EL_INTERNAL_CASCADE_BD_ACTIVE,
4584 EL_INTERNAL_CASCADE_BD_NATIVE_ACTIVE,
4585 EL_INTERNAL_CASCADE_EM_ACTIVE,
4586 EL_INTERNAL_CASCADE_EMC_ACTIVE,
4587 EL_INTERNAL_CASCADE_RND_ACTIVE,
4588 EL_INTERNAL_CASCADE_SB_ACTIVE,
4589 EL_INTERNAL_CASCADE_SP_ACTIVE,
4590 EL_INTERNAL_CASCADE_DC_ACTIVE,
4591 EL_INTERNAL_CASCADE_DX_ACTIVE,
4592 EL_INTERNAL_CASCADE_MM_ACTIVE,
4593 EL_INTERNAL_CASCADE_DF_ACTIVE,
4594 EL_INTERNAL_CASCADE_CHARS_ACTIVE,
4595 EL_INTERNAL_CASCADE_STEEL_CHARS_ACTIVE,
4596 EL_INTERNAL_CASCADE_CE_ACTIVE,
4597 EL_INTERNAL_CASCADE_GE_ACTIVE,
4598 EL_INTERNAL_CASCADE_ES_ACTIVE,
4599 EL_INTERNAL_CASCADE_REF_ACTIVE,
4600 EL_INTERNAL_CASCADE_USER_ACTIVE,
4601 EL_INTERNAL_CASCADE_DYNAMIC_ACTIVE,
4606 static int ep_editor_cascade_inactive[] =
4608 EL_INTERNAL_CASCADE_BD,
4609 EL_INTERNAL_CASCADE_BD_NATIVE,
4610 EL_INTERNAL_CASCADE_EM,
4611 EL_INTERNAL_CASCADE_EMC,
4612 EL_INTERNAL_CASCADE_RND,
4613 EL_INTERNAL_CASCADE_SB,
4614 EL_INTERNAL_CASCADE_SP,
4615 EL_INTERNAL_CASCADE_DC,
4616 EL_INTERNAL_CASCADE_DX,
4617 EL_INTERNAL_CASCADE_MM,
4618 EL_INTERNAL_CASCADE_DF,
4619 EL_INTERNAL_CASCADE_CHARS,
4620 EL_INTERNAL_CASCADE_STEEL_CHARS,
4621 EL_INTERNAL_CASCADE_CE,
4622 EL_INTERNAL_CASCADE_GE,
4623 EL_INTERNAL_CASCADE_ES,
4624 EL_INTERNAL_CASCADE_REF,
4625 EL_INTERNAL_CASCADE_USER,
4626 EL_INTERNAL_CASCADE_DYNAMIC,
4631 static int ep_obsolete[] =
4635 EL_EM_KEY_1_FILE_OBSOLETE,
4636 EL_EM_KEY_2_FILE_OBSOLETE,
4637 EL_EM_KEY_3_FILE_OBSOLETE,
4638 EL_EM_KEY_4_FILE_OBSOLETE,
4639 EL_ENVELOPE_OBSOLETE,
4648 } element_properties[] =
4650 { ep_diggable, EP_DIGGABLE },
4651 { ep_collectible_only, EP_COLLECTIBLE_ONLY },
4652 { ep_dont_run_into, EP_DONT_RUN_INTO },
4653 { ep_dont_collide_with, EP_DONT_COLLIDE_WITH },
4654 { ep_dont_touch, EP_DONT_TOUCH },
4655 { ep_indestructible, EP_INDESTRUCTIBLE },
4656 { ep_slippery, EP_SLIPPERY },
4657 { ep_can_change, EP_CAN_CHANGE },
4658 { ep_can_move, EP_CAN_MOVE },
4659 { ep_can_fall, EP_CAN_FALL },
4660 { ep_can_smash_player, EP_CAN_SMASH_PLAYER },
4661 { ep_can_smash_enemies, EP_CAN_SMASH_ENEMIES },
4662 { ep_can_smash_everything, EP_CAN_SMASH_EVERYTHING },
4663 { ep_explodes_by_fire, EP_EXPLODES_BY_FIRE },
4664 { ep_explodes_smashed, EP_EXPLODES_SMASHED },
4665 { ep_explodes_impact, EP_EXPLODES_IMPACT },
4666 { ep_walkable_over, EP_WALKABLE_OVER },
4667 { ep_walkable_inside, EP_WALKABLE_INSIDE },
4668 { ep_walkable_under, EP_WALKABLE_UNDER },
4669 { ep_passable_over, EP_PASSABLE_OVER },
4670 { ep_passable_inside, EP_PASSABLE_INSIDE },
4671 { ep_passable_under, EP_PASSABLE_UNDER },
4672 { ep_droppable, EP_DROPPABLE },
4673 { ep_explodes_1x1_old, EP_EXPLODES_1X1_OLD },
4674 { ep_pushable, EP_PUSHABLE },
4675 { ep_explodes_cross_old, EP_EXPLODES_CROSS_OLD },
4676 { ep_protected, EP_PROTECTED },
4677 { ep_throwable, EP_THROWABLE },
4678 { ep_can_explode, EP_CAN_EXPLODE },
4679 { ep_gravity_reachable, EP_GRAVITY_REACHABLE },
4681 { ep_empty_space, EP_EMPTY_SPACE },
4682 { ep_player, EP_PLAYER },
4683 { ep_can_pass_magic_wall, EP_CAN_PASS_MAGIC_WALL },
4684 { ep_can_pass_dc_magic_wall, EP_CAN_PASS_DC_MAGIC_WALL },
4685 { ep_switchable, EP_SWITCHABLE },
4686 { ep_bd_element, EP_BD_ELEMENT },
4687 { ep_sp_element, EP_SP_ELEMENT },
4688 { ep_sb_element, EP_SB_ELEMENT },
4690 { ep_food_dark_yamyam, EP_FOOD_DARK_YAMYAM },
4691 { ep_food_penguin, EP_FOOD_PENGUIN },
4692 { ep_food_pig, EP_FOOD_PIG },
4693 { ep_historic_wall, EP_HISTORIC_WALL },
4694 { ep_historic_solid, EP_HISTORIC_SOLID },
4695 { ep_classic_enemy, EP_CLASSIC_ENEMY },
4696 { ep_belt, EP_BELT },
4697 { ep_belt_active, EP_BELT_ACTIVE },
4698 { ep_belt_switch, EP_BELT_SWITCH },
4699 { ep_tube, EP_TUBE },
4700 { ep_acid_pool, EP_ACID_POOL },
4701 { ep_keygate, EP_KEYGATE },
4702 { ep_amoeboid, EP_AMOEBOID },
4703 { ep_amoebalive, EP_AMOEBALIVE },
4704 { ep_has_editor_content, EP_HAS_EDITOR_CONTENT },
4705 { ep_can_turn_each_move, EP_CAN_TURN_EACH_MOVE },
4706 { ep_can_grow, EP_CAN_GROW },
4707 { ep_active_bomb, EP_ACTIVE_BOMB },
4708 { ep_inactive, EP_INACTIVE },
4710 { ep_em_slippery_wall, EP_EM_SLIPPERY_WALL },
4712 { ep_gfx_crumbled, EP_GFX_CRUMBLED },
4714 { ep_editor_cascade_active, EP_EDITOR_CASCADE_ACTIVE },
4715 { ep_editor_cascade_inactive, EP_EDITOR_CASCADE_INACTIVE },
4717 { ep_obsolete, EP_OBSOLETE },
4724 // always start with reliable default values (element has no properties)
4725 // (but never initialize clipboard elements after the very first time)
4726 // (to be able to use clipboard elements between several levels)
4727 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4728 if (!IS_CLIPBOARD_ELEMENT(i) || !clipboard_elements_initialized)
4729 for (j = 0; j < NUM_ELEMENT_PROPERTIES; j++)
4730 SET_PROPERTY(i, j, FALSE);
4732 // set all base element properties from above array definitions
4733 for (i = 0; element_properties[i].elements != NULL; i++)
4734 for (j = 0; (element_properties[i].elements)[j] != -1; j++)
4735 SET_PROPERTY((element_properties[i].elements)[j],
4736 element_properties[i].property, TRUE);
4738 // copy properties to some elements that are only stored in level file
4739 for (i = 0; i < NUM_ELEMENT_PROPERTIES; i++)
4740 for (j = 0; copy_properties[j][0] != -1; j++)
4741 if (HAS_PROPERTY(copy_properties[j][0], i))
4742 for (k = 1; k <= 4; k++)
4743 SET_PROPERTY(copy_properties[j][k], i, TRUE);
4745 // set static element properties that are not listed in array definitions
4746 for (i = EL_STEEL_CHAR_START; i <= EL_STEEL_CHAR_END; i++)
4747 SET_PROPERTY(i, EP_INDESTRUCTIBLE, TRUE);
4749 clipboard_elements_initialized = TRUE;
4752 void InitElementPropertiesEngine(int engine_version)
4754 static int no_wall_properties[] =
4757 EP_COLLECTIBLE_ONLY,
4759 EP_DONT_COLLIDE_WITH,
4762 EP_CAN_SMASH_PLAYER,
4763 EP_CAN_SMASH_ENEMIES,
4764 EP_CAN_SMASH_EVERYTHING,
4769 EP_FOOD_DARK_YAMYAM,
4785 /* important: after initialization in InitElementPropertiesStatic(), the
4786 elements are not again initialized to a default value; therefore all
4787 changes have to make sure that they leave the element with a defined
4788 property (which means that conditional property changes must be set to
4789 a reliable default value before) */
4791 // resolve group elements
4792 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
4793 ResolveGroupElement(EL_GROUP_START + i);
4795 // set all special, combined or engine dependent element properties
4796 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4798 // do not change (already initialized) clipboard elements here
4799 if (IS_CLIPBOARD_ELEMENT(i))
4802 // ---------- INACTIVE ----------------------------------------------------
4803 SET_PROPERTY(i, EP_INACTIVE, ((i >= EL_CHAR_START &&
4804 i <= EL_CHAR_END) ||
4805 (i >= EL_STEEL_CHAR_START &&
4806 i <= EL_STEEL_CHAR_END)));
4808 // ---------- WALKABLE, PASSABLE, ACCESSIBLE ------------------------------
4809 SET_PROPERTY(i, EP_WALKABLE, (IS_WALKABLE_OVER(i) ||
4810 IS_WALKABLE_INSIDE(i) ||
4811 IS_WALKABLE_UNDER(i)));
4813 SET_PROPERTY(i, EP_PASSABLE, (IS_PASSABLE_OVER(i) ||
4814 IS_PASSABLE_INSIDE(i) ||
4815 IS_PASSABLE_UNDER(i)));
4817 SET_PROPERTY(i, EP_ACCESSIBLE_OVER, (IS_WALKABLE_OVER(i) ||
4818 IS_PASSABLE_OVER(i)));
4820 SET_PROPERTY(i, EP_ACCESSIBLE_INSIDE, (IS_WALKABLE_INSIDE(i) ||
4821 IS_PASSABLE_INSIDE(i)));
4823 SET_PROPERTY(i, EP_ACCESSIBLE_UNDER, (IS_WALKABLE_UNDER(i) ||
4824 IS_PASSABLE_UNDER(i)));
4826 SET_PROPERTY(i, EP_ACCESSIBLE, (IS_WALKABLE(i) ||
4829 // ---------- COLLECTIBLE -------------------------------------------------
4830 SET_PROPERTY(i, EP_COLLECTIBLE, (IS_COLLECTIBLE_ONLY(i) ||
4834 // ---------- SNAPPABLE ---------------------------------------------------
4835 SET_PROPERTY(i, EP_SNAPPABLE, (IS_DIGGABLE(i) ||
4836 IS_COLLECTIBLE(i) ||
4840 // ---------- WALL --------------------------------------------------------
4841 SET_PROPERTY(i, EP_WALL, TRUE); // default: element is wall
4843 for (j = 0; no_wall_properties[j] != -1; j++)
4844 if (HAS_PROPERTY(i, no_wall_properties[j]) ||
4845 i >= EL_FIRST_RUNTIME_UNREAL)
4846 SET_PROPERTY(i, EP_WALL, FALSE);
4848 if (IS_HISTORIC_WALL(i))
4849 SET_PROPERTY(i, EP_WALL, TRUE);
4851 // ---------- SOLID_FOR_PUSHING -------------------------------------------
4852 if (engine_version < VERSION_IDENT(2,2,0,0))
4853 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, IS_HISTORIC_SOLID(i));
4855 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, (!IS_WALKABLE(i) &&
4857 !IS_COLLECTIBLE(i)));
4859 // ---------- DRAGONFIRE_PROOF --------------------------------------------
4860 if (IS_HISTORIC_SOLID(i) || i == EL_EXPLOSION)
4861 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, TRUE);
4863 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, (IS_INDESTRUCTIBLE(i) &&
4866 // ---------- EXPLOSION_PROOF ---------------------------------------------
4868 SET_PROPERTY(i, EP_EXPLOSION_PROOF, TRUE);
4869 else if (engine_version < VERSION_IDENT(2,2,0,0))
4870 SET_PROPERTY(i, EP_EXPLOSION_PROOF, IS_INDESTRUCTIBLE(i));
4872 SET_PROPERTY(i, EP_EXPLOSION_PROOF, (IS_INDESTRUCTIBLE(i) &&
4876 if (IS_CUSTOM_ELEMENT(i))
4878 // these are additional properties which are initially false when set
4880 // ---------- DONT_COLLIDE_WITH / DONT_RUN_INTO -------------------------
4882 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH, TRUE);
4883 if (DONT_COLLIDE_WITH(i))
4884 SET_PROPERTY(i, EP_DONT_RUN_INTO, TRUE);
4886 // ---------- CAN_SMASH_ENEMIES / CAN_SMASH_PLAYER ----------------------
4887 if (CAN_SMASH_EVERYTHING(i))
4888 SET_PROPERTY(i, EP_CAN_SMASH_ENEMIES, TRUE);
4889 if (CAN_SMASH_ENEMIES(i))
4890 SET_PROPERTY(i, EP_CAN_SMASH_PLAYER, TRUE);
4893 // ---------- CAN_SMASH ---------------------------------------------------
4894 SET_PROPERTY(i, EP_CAN_SMASH, (CAN_SMASH_PLAYER(i) ||
4895 CAN_SMASH_ENEMIES(i) ||
4896 CAN_SMASH_EVERYTHING(i)));
4898 // ---------- CAN_EXPLODE_BY_FIRE -----------------------------------------
4899 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_FIRE, (CAN_EXPLODE(i) &&
4900 EXPLODES_BY_FIRE(i)));
4902 // ---------- CAN_EXPLODE_SMASHED -----------------------------------------
4903 SET_PROPERTY(i, EP_CAN_EXPLODE_SMASHED, (CAN_EXPLODE(i) &&
4904 EXPLODES_SMASHED(i)));
4906 // ---------- CAN_EXPLODE_IMPACT ------------------------------------------
4907 SET_PROPERTY(i, EP_CAN_EXPLODE_IMPACT, (CAN_EXPLODE(i) &&
4908 EXPLODES_IMPACT(i)));
4910 // ---------- CAN_EXPLODE_BY_DRAGONFIRE -----------------------------------
4911 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_DRAGONFIRE, CAN_EXPLODE_BY_FIRE(i));
4913 // ---------- CAN_EXPLODE_BY_EXPLOSION ------------------------------------
4914 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_EXPLOSION, (CAN_EXPLODE_BY_FIRE(i) ||
4915 i == EL_BLACK_ORB));
4917 // ---------- COULD_MOVE_INTO_ACID ----------------------------------------
4918 SET_PROPERTY(i, EP_COULD_MOVE_INTO_ACID, (IS_PLAYER_ELEMENT(i) ||
4920 IS_CUSTOM_ELEMENT(i)));
4922 // ---------- MAYBE_DONT_COLLIDE_WITH -------------------------------------
4923 SET_PROPERTY(i, EP_MAYBE_DONT_COLLIDE_WITH, (i == EL_SP_SNIKSNAK ||
4924 i == EL_SP_ELECTRON));
4926 // ---------- CAN_MOVE_INTO_ACID ------------------------------------------
4927 if (COULD_MOVE_INTO_ACID(i) && !IS_CUSTOM_ELEMENT(i))
4928 SET_PROPERTY(i, EP_CAN_MOVE_INTO_ACID,
4929 getMoveIntoAcidProperty(&level, i));
4931 // ---------- DONT_COLLIDE_WITH -------------------------------------------
4932 if (MAYBE_DONT_COLLIDE_WITH(i))
4933 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH,
4934 getDontCollideWithProperty(&level, i));
4936 // ---------- SP_PORT -----------------------------------------------------
4937 SET_PROPERTY(i, EP_SP_PORT, (IS_SP_ELEMENT(i) &&
4938 IS_PASSABLE_INSIDE(i)));
4940 // ---------- CAN_BE_CLONED_BY_ANDROID ------------------------------------
4941 for (j = 0; j < level.num_android_clone_elements; j++)
4942 SET_PROPERTY(i, EP_CAN_BE_CLONED_BY_ANDROID,
4944 IS_EQUAL_OR_IN_GROUP(i, level.android_clone_element[j])));
4946 // ---------- CAN_CHANGE --------------------------------------------------
4947 SET_PROPERTY(i, EP_CAN_CHANGE, FALSE); // default: cannot change
4948 for (j = 0; j < element_info[i].num_change_pages; j++)
4949 if (element_info[i].change_page[j].can_change)
4950 SET_PROPERTY(i, EP_CAN_CHANGE, TRUE);
4952 // ---------- HAS_ACTION --------------------------------------------------
4953 SET_PROPERTY(i, EP_HAS_ACTION, FALSE); // default: has no action
4954 for (j = 0; j < element_info[i].num_change_pages; j++)
4955 if (element_info[i].change_page[j].has_action)
4956 SET_PROPERTY(i, EP_HAS_ACTION, TRUE);
4958 // ---------- CAN_CHANGE_OR_HAS_ACTION ------------------------------------
4959 SET_PROPERTY(i, EP_CAN_CHANGE_OR_HAS_ACTION, (CAN_CHANGE(i) ||
4962 // ---------- GFX_CRUMBLED ------------------------------------------------
4963 SET_PROPERTY(i, EP_GFX_CRUMBLED,
4964 element_info[i].crumbled[ACTION_DEFAULT] !=
4965 element_info[i].graphic[ACTION_DEFAULT]);
4967 // ---------- EDITOR_CASCADE ----------------------------------------------
4968 SET_PROPERTY(i, EP_EDITOR_CASCADE, (IS_EDITOR_CASCADE_ACTIVE(i) ||
4969 IS_EDITOR_CASCADE_INACTIVE(i)));
4972 // dynamically adjust element properties according to game engine version
4974 static int ep_em_slippery_wall[] =
4979 EL_EXPANDABLE_WALL_HORIZONTAL,
4980 EL_EXPANDABLE_WALL_VERTICAL,
4981 EL_EXPANDABLE_WALL_ANY,
4982 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
4983 EL_EXPANDABLE_STEELWALL_VERTICAL,
4984 EL_EXPANDABLE_STEELWALL_ANY,
4985 EL_EXPANDABLE_STEELWALL_GROWING,
4989 static int ep_em_explodes_by_fire[] =
4992 EL_EM_DYNAMITE_ACTIVE,
4997 // special EM style gems behaviour
4998 for (i = 0; ep_em_slippery_wall[i] != -1; i++)
4999 SET_PROPERTY(ep_em_slippery_wall[i], EP_EM_SLIPPERY_WALL,
5000 level.em_slippery_gems);
5002 // "EL_EXPANDABLE_WALL_GROWING" wasn't slippery for EM gems in 2.0.1
5003 SET_PROPERTY(EL_EXPANDABLE_WALL_GROWING, EP_EM_SLIPPERY_WALL,
5004 (level.em_slippery_gems &&
5005 engine_version > VERSION_IDENT(2,0,1,0)));
5007 // special EM style explosion behaviour regarding chain reactions
5008 for (i = 0; ep_em_explodes_by_fire[i] != -1; i++)
5009 SET_PROPERTY(ep_em_explodes_by_fire[i], EP_EXPLODES_BY_FIRE,
5010 level.em_explodes_by_fire);
5013 // this is needed because some graphics depend on element properties
5014 if (game_status == GAME_MODE_PLAYING)
5015 InitElementGraphicInfo();
5018 void InitElementPropertiesGfxElement(void)
5022 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5024 struct ElementInfo *ei = &element_info[i];
5026 ei->gfx_element = (ei->use_gfx_element ? ei->gfx_element_initial : i);
5030 static void InitGlobal(void)
5035 for (i = 0; i < MAX_NUM_ELEMENTS + 1; i++)
5037 // check if element_name_info entry defined for each element in "main.h"
5038 if (i < MAX_NUM_ELEMENTS && element_name_info[i].token_name == NULL)
5039 Fail("undefined 'element_name_info' entry for element %d", i);
5041 element_info[i].token_name = element_name_info[i].token_name;
5042 element_info[i].class_name = element_name_info[i].class_name;
5043 element_info[i].editor_description = element_name_info[i].editor_description;
5046 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS + 1; i++)
5048 // check if global_anim_name_info defined for each entry in "main.h"
5049 if (i < NUM_GLOBAL_ANIM_TOKENS &&
5050 global_anim_name_info[i].token_name == NULL)
5051 Fail("undefined 'global_anim_name_info' entry for anim %d", i);
5053 global_anim_info[i].token_name = global_anim_name_info[i].token_name;
5056 // create hash to store URLs for global animations
5057 anim_url_hash = newSetupFileHash();
5059 // create hash from image config list
5060 image_config_hash = newSetupFileHash();
5061 for (i = 0; image_config[i].token != NULL; i++)
5062 setHashEntry(image_config_hash,
5063 image_config[i].token,
5064 image_config[i].value);
5066 // create hash from sound config list
5067 sound_config_hash = newSetupFileHash();
5068 for (i = 0; sound_config[i].token != NULL; i++)
5069 setHashEntry(sound_config_hash,
5070 sound_config[i].token,
5071 sound_config[i].value);
5073 // create hash from element token list
5074 element_token_hash = newSetupFileHash();
5075 for (i = 0; element_name_info[i].token_name != NULL; i++)
5076 setHashEntry(element_token_hash,
5077 element_name_info[i].token_name,
5080 // create hash from graphic token list
5081 graphic_token_hash = newSetupFileHash();
5082 for (graphic = 0, i = 0; image_config[i].token != NULL; i++)
5083 if (strSuffix(image_config[i].value, ".png") ||
5084 strSuffix(image_config[i].value, ".pcx") ||
5085 strSuffix(image_config[i].value, ".wav") ||
5086 strEqual(image_config[i].value, UNDEFINED_FILENAME))
5087 setHashEntry(graphic_token_hash,
5088 image_config[i].token,
5089 int2str(graphic++, 0));
5091 // create hash from font token list
5092 font_token_hash = newSetupFileHash();
5093 for (i = 0; font_info[i].token_name != NULL; i++)
5094 setHashEntry(font_token_hash,
5095 font_info[i].token_name,
5098 // set default filenames for all cloned graphics in static configuration
5099 for (i = 0; image_config[i].token != NULL; i++)
5101 if (strEqual(image_config[i].value, UNDEFINED_FILENAME))
5103 char *token = image_config[i].token;
5104 char *token_clone_from = getStringCat2(token, ".clone_from");
5105 char *token_cloned = getHashEntry(image_config_hash, token_clone_from);
5107 if (token_cloned != NULL)
5109 char *value_cloned = getHashEntry(image_config_hash, token_cloned);
5111 if (value_cloned != NULL)
5113 // set default filename in static configuration
5114 image_config[i].value = value_cloned;
5116 // set default filename in image config hash
5117 setHashEntry(image_config_hash, token, value_cloned);
5121 free(token_clone_from);
5125 // always start with reliable default values (all elements)
5126 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5127 ActiveElement[i] = i;
5129 // now add all entries that have an active state (active elements)
5130 for (i = 0; element_with_active_state[i].element != -1; i++)
5132 int element = element_with_active_state[i].element;
5133 int element_active = element_with_active_state[i].element_active;
5135 ActiveElement[element] = element_active;
5138 // always start with reliable default values (all buttons)
5139 for (i = 0; i < NUM_IMAGE_FILES; i++)
5140 ActiveButton[i] = i;
5142 // now add all entries that have an active state (active buttons)
5143 for (i = 0; button_with_active_state[i].button != -1; i++)
5145 int button = button_with_active_state[i].button;
5146 int button_active = button_with_active_state[i].button_active;
5148 ActiveButton[button] = button_active;
5151 // always start with reliable default values (all fonts)
5152 for (i = 0; i < NUM_FONTS; i++)
5155 // now add all entries that have an active state (active fonts)
5156 for (i = 0; font_with_active_state[i].font_nr != -1; i++)
5158 int font = font_with_active_state[i].font_nr;
5159 int font_active = font_with_active_state[i].font_nr_active;
5161 ActiveFont[font] = font_active;
5164 global.autoplay_leveldir = NULL;
5165 global.patchtapes_leveldir = NULL;
5166 global.convert_leveldir = NULL;
5167 global.dumplevel_leveldir = NULL;
5168 global.dumptape_leveldir = NULL;
5169 global.create_sketch_images_dir = NULL;
5170 global.create_collect_images_dir = NULL;
5172 global.frames_per_second = 0;
5173 global.show_frames_per_second = FALSE;
5175 global.border_status = GAME_MODE_LOADING;
5176 global.anim_status = global.anim_status_next = GAME_MODE_LOADING;
5178 global.use_envelope_request = FALSE;
5180 global.user_names = NULL;
5183 static void Execute_Command(char *command)
5187 if (strEqual(command, "print graphicsinfo.conf"))
5189 Print("# You can configure additional/alternative image files here.\n");
5190 Print("# (The entries below are default and therefore commented out.)\n");
5192 Print("%s\n", getFormattedSetupEntry("name", "Classic Graphics"));
5194 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5197 for (i = 0; image_config[i].token != NULL; i++)
5198 Print("# %s\n", getFormattedSetupEntry(image_config[i].token,
5199 image_config[i].value));
5203 else if (strEqual(command, "print soundsinfo.conf"))
5205 Print("# You can configure additional/alternative sound files here.\n");
5206 Print("# (The entries below are default and therefore commented out.)\n");
5208 Print("%s\n", getFormattedSetupEntry("name", "Classic Sounds"));
5210 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5213 for (i = 0; sound_config[i].token != NULL; i++)
5214 Print("# %s\n", getFormattedSetupEntry(sound_config[i].token,
5215 sound_config[i].value));
5219 else if (strEqual(command, "print musicinfo.conf"))
5221 Print("# You can configure additional/alternative music files here.\n");
5222 Print("# (The entries below are default and therefore commented out.)\n");
5224 Print("%s\n", getFormattedSetupEntry("name", "Classic Music"));
5226 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5229 for (i = 0; music_config[i].token != NULL; i++)
5230 Print("# %s\n", getFormattedSetupEntry(music_config[i].token,
5231 music_config[i].value));
5235 else if (strEqual(command, "print editorsetup.conf"))
5237 Print("# You can configure your personal editor element list here.\n");
5238 Print("# (The entries below are default and therefore commented out.)\n");
5241 // this is needed to be able to check element list for cascade elements
5242 InitElementPropertiesStatic();
5243 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
5245 PrintEditorElementList();
5249 else if (strEqual(command, "print helpanim.conf"))
5251 Print("# You can configure different element help animations here.\n");
5252 Print("# (The entries below are default and therefore commented out.)\n");
5255 for (i = 0; helpanim_config[i].token != NULL; i++)
5257 Print("# %s\n", getFormattedSetupEntry(helpanim_config[i].token,
5258 helpanim_config[i].value));
5260 if (strEqual(helpanim_config[i].token, "end"))
5266 else if (strEqual(command, "print helptext.conf"))
5268 Print("# You can configure different element help text here.\n");
5269 Print("# (The entries below are default and therefore commented out.)\n");
5272 for (i = 0; helptext_config[i].token != NULL; i++)
5273 Print("# %s\n", getFormattedSetupEntry(helptext_config[i].token,
5274 helptext_config[i].value));
5278 else if (strPrefix(command, "dump level "))
5280 char *filename = &command[11];
5282 if (fileExists(filename))
5284 LoadLevelFromFilename(&level, filename);
5290 char *leveldir = getStringCopy(filename); // read command parameters
5291 char *level_nr = strchr(leveldir, ' ');
5293 if (level_nr == NULL)
5294 Fail("cannot open file '%s'", filename);
5298 global.dumplevel_leveldir = leveldir;
5299 global.dumplevel_level_nr = atoi(level_nr);
5301 program.headless = TRUE;
5303 else if (strPrefix(command, "dump tape "))
5305 char *filename = &command[10];
5307 if (fileExists(filename))
5309 LoadTapeFromFilename(filename);
5315 char *leveldir = getStringCopy(filename); // read command parameters
5316 char *level_nr = strchr(leveldir, ' ');
5318 if (level_nr == NULL)
5319 Fail("cannot open file '%s'", filename);
5323 global.dumptape_leveldir = leveldir;
5324 global.dumptape_level_nr = atoi(level_nr);
5326 program.headless = TRUE;
5328 else if (strPrefix(command, "autoplay ") ||
5329 strPrefix(command, "autoffwd ") ||
5330 strPrefix(command, "autowarp ") ||
5331 strPrefix(command, "autotest ") ||
5332 strPrefix(command, "autosave ") ||
5333 strPrefix(command, "autoupload ") ||
5334 strPrefix(command, "autofix "))
5336 char *arg_ptr = strchr(command, ' ');
5337 char *str_ptr = getStringCopy(arg_ptr); // read command parameters
5339 global.autoplay_mode =
5340 (strPrefix(command, "autoplay") ? AUTOPLAY_MODE_PLAY :
5341 strPrefix(command, "autoffwd") ? AUTOPLAY_MODE_FFWD :
5342 strPrefix(command, "autowarp") ? AUTOPLAY_MODE_WARP :
5343 strPrefix(command, "autotest") ? AUTOPLAY_MODE_TEST :
5344 strPrefix(command, "autosave") ? AUTOPLAY_MODE_SAVE :
5345 strPrefix(command, "autoupload") ? AUTOPLAY_MODE_UPLOAD :
5346 strPrefix(command, "autofix") ? AUTOPLAY_MODE_FIX :
5347 AUTOPLAY_MODE_NONE);
5349 while (*str_ptr != '\0') // continue parsing string
5351 // cut leading whitespace from string, replace it by string terminator
5352 while (*str_ptr == ' ' || *str_ptr == '\t')
5355 if (*str_ptr == '\0') // end of string reached
5358 if (global.autoplay_leveldir == NULL) // read level set string
5360 global.autoplay_leveldir = str_ptr;
5361 global.autoplay_all = TRUE; // default: play all tapes
5363 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5364 global.autoplay_level[i] = FALSE;
5366 else // read level number string
5368 int level_nr = atoi(str_ptr); // get level_nr value
5370 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5371 global.autoplay_level[level_nr] = TRUE;
5373 global.autoplay_all = FALSE;
5376 // advance string pointer to the next whitespace (or end of string)
5377 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5381 if (global.autoplay_mode & AUTOPLAY_WARP_NO_DISPLAY)
5382 program.headless = TRUE;
5384 else if (strPrefix(command, "patch tapes "))
5386 char *str_ptr = getStringCopy(&command[12]); // read command parameters
5388 // skip leading whitespace
5389 while (*str_ptr == ' ' || *str_ptr == '\t')
5392 if (*str_ptr == '\0')
5393 Fail("cannot find MODE in command '%s'", command);
5395 global.patchtapes_mode = str_ptr; // store patch mode
5397 // advance to next whitespace (or end of string)
5398 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5401 while (*str_ptr != '\0') // continue parsing string
5403 // cut leading whitespace from string, replace it by string terminator
5404 while (*str_ptr == ' ' || *str_ptr == '\t')
5407 if (*str_ptr == '\0') // end of string reached
5410 if (global.patchtapes_leveldir == NULL) // read level set string
5412 global.patchtapes_leveldir = str_ptr;
5413 global.patchtapes_all = TRUE; // default: patch all tapes
5415 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5416 global.patchtapes_level[i] = FALSE;
5418 else // read level number string
5420 int level_nr = atoi(str_ptr); // get level_nr value
5422 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5423 global.patchtapes_level[level_nr] = TRUE;
5425 global.patchtapes_all = FALSE;
5428 // advance string pointer to the next whitespace (or end of string)
5429 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5433 if (global.patchtapes_leveldir == NULL)
5435 if (strEqual(global.patchtapes_mode, "help"))
5436 global.patchtapes_leveldir = UNDEFINED_LEVELSET;
5438 Fail("cannot find LEVELDIR in command '%s'", command);
5441 program.headless = TRUE;
5443 else if (strPrefix(command, "convert "))
5445 char *str_copy = getStringCopy(strchr(command, ' ') + 1);
5446 char *str_ptr = strchr(str_copy, ' ');
5448 global.convert_leveldir = str_copy;
5449 global.convert_level_nr = -1;
5451 if (str_ptr != NULL) // level number follows
5453 *str_ptr++ = '\0'; // terminate leveldir string
5454 global.convert_level_nr = atoi(str_ptr); // get level_nr value
5457 program.headless = TRUE;
5459 else if (strPrefix(command, "create sketch images "))
5461 global.create_sketch_images_dir = getStringCopy(&command[21]);
5463 if (access(global.create_sketch_images_dir, W_OK) != 0)
5464 Fail("image target directory '%s' not found or not writable",
5465 global.create_sketch_images_dir);
5467 else if (strPrefix(command, "create collect image "))
5469 global.create_collect_images_dir = getStringCopy(&command[21]);
5471 if (access(global.create_collect_images_dir, W_OK) != 0)
5472 Fail("image target directory '%s' not found or not writable",
5473 global.create_collect_images_dir);
5475 else if (strPrefix(command, "create CE image "))
5477 CreateCustomElementImages(&command[16]);
5483 FailWithHelp("unrecognized command '%s'", command);
5486 // disable networking if any valid command was recognized
5487 options.network = setup.network_mode = FALSE;
5490 static void InitSetup(void)
5492 LoadUserNames(); // global user names
5493 LoadUserSetup(); // global user number
5495 LoadSetup(); // global setup info
5497 // set some options from setup file
5499 if (setup.options.verbose)
5500 options.verbose = TRUE;
5502 if (setup.options.debug)
5503 options.debug = TRUE;
5505 if (!strEqual(setup.options.debug_mode, ARG_UNDEFINED_STRING))
5506 options.debug_mode = getStringCopy(setup.options.debug_mode);
5508 if (setup.debug.show_frames_per_second)
5509 global.show_frames_per_second = TRUE;
5512 static void InitGameInfo(void)
5514 game.restart_level = FALSE;
5515 game.request_active = FALSE;
5517 game.use_masked_elements_initial = FALSE;
5520 static void InitPlayerInfo(void)
5524 // choose default local player
5525 local_player = &stored_player[0];
5527 for (i = 0; i < MAX_PLAYERS; i++)
5529 stored_player[i].connected_locally = FALSE;
5530 stored_player[i].connected_network = FALSE;
5533 local_player->connected_locally = TRUE;
5536 static void InitArtworkInfo(void)
5541 static char *get_string_in_brackets(char *string)
5543 char *string_in_brackets = checked_malloc(strlen(string) + 3);
5545 sprintf(string_in_brackets, "[%s]", string);
5547 return string_in_brackets;
5550 static char *get_level_id_suffix(int id_nr)
5552 char *id_suffix = checked_malloc(1 + 3 + 1);
5554 if (id_nr < 0 || id_nr > 999)
5557 sprintf(id_suffix, ".%03d", id_nr);
5562 static void InitArtworkConfig(void)
5564 static char *image_id_prefix[MAX_NUM_ELEMENTS +
5566 NUM_GLOBAL_ANIM_TOKENS + 1];
5567 static char *sound_id_prefix[2 * MAX_NUM_ELEMENTS +
5568 NUM_GLOBAL_ANIM_TOKENS + 1];
5569 static char *music_id_prefix[NUM_MUSIC_PREFIXES +
5570 NUM_GLOBAL_ANIM_TOKENS + 1];
5571 static char *action_id_suffix[NUM_ACTIONS + 1];
5572 static char *direction_id_suffix[NUM_DIRECTIONS_FULL + 1];
5573 static char *special_id_suffix[NUM_SPECIAL_GFX_ARGS + 1];
5574 static char *level_id_suffix[MAX_LEVELS + 1];
5575 static char *dummy[1] = { NULL };
5576 static char *ignore_generic_tokens[] =
5581 "program_copyright",
5586 static char **ignore_image_tokens;
5587 static char **ignore_sound_tokens;
5588 static char **ignore_music_tokens;
5589 int num_ignore_generic_tokens;
5590 int num_ignore_image_tokens;
5591 int num_ignore_sound_tokens;
5592 int num_ignore_music_tokens;
5595 // dynamically determine list of generic tokens to be ignored
5596 num_ignore_generic_tokens = 0;
5597 for (i = 0; ignore_generic_tokens[i] != NULL; i++)
5598 num_ignore_generic_tokens++;
5600 // dynamically determine list of image tokens to be ignored
5601 num_ignore_image_tokens = num_ignore_generic_tokens;
5602 for (i = 0; image_config_vars[i].token != NULL; i++)
5603 num_ignore_image_tokens++;
5604 ignore_image_tokens =
5605 checked_malloc((num_ignore_image_tokens + 1) * sizeof(char *));
5606 for (i = 0; i < num_ignore_generic_tokens; i++)
5607 ignore_image_tokens[i] = ignore_generic_tokens[i];
5608 for (i = 0; i < num_ignore_image_tokens - num_ignore_generic_tokens; i++)
5609 ignore_image_tokens[num_ignore_generic_tokens + i] =
5610 image_config_vars[i].token;
5611 ignore_image_tokens[num_ignore_image_tokens] = NULL;
5613 // dynamically determine list of sound tokens to be ignored
5614 num_ignore_sound_tokens = num_ignore_generic_tokens;
5615 for (i = 0; sound_config_vars[i].token != NULL; i++)
5616 num_ignore_sound_tokens++;
5617 ignore_sound_tokens =
5618 checked_malloc((num_ignore_sound_tokens + 1) * sizeof(char *));
5619 for (i = 0; i < num_ignore_generic_tokens; i++)
5620 ignore_sound_tokens[i] = ignore_generic_tokens[i];
5621 for (i = 0; i < num_ignore_sound_tokens - num_ignore_generic_tokens; i++)
5622 ignore_sound_tokens[num_ignore_generic_tokens + i] =
5623 sound_config_vars[i].token;
5624 ignore_sound_tokens[num_ignore_sound_tokens] = NULL;
5626 // dynamically determine list of music tokens to be ignored
5627 num_ignore_music_tokens = num_ignore_generic_tokens;
5628 ignore_music_tokens =
5629 checked_malloc((num_ignore_music_tokens + 1) * sizeof(char *));
5630 for (i = 0; i < num_ignore_generic_tokens; i++)
5631 ignore_music_tokens[i] = ignore_generic_tokens[i];
5632 ignore_music_tokens[num_ignore_music_tokens] = NULL;
5634 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5635 image_id_prefix[i] = element_info[i].token_name;
5636 for (i = 0; i < NUM_FONTS; i++)
5637 image_id_prefix[MAX_NUM_ELEMENTS + i] = font_info[i].token_name;
5638 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5639 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + i] =
5640 global_anim_info[i].token_name;
5641 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5643 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5644 sound_id_prefix[i] = element_info[i].token_name;
5645 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5646 sound_id_prefix[MAX_NUM_ELEMENTS + i] =
5647 get_string_in_brackets(element_info[i].class_name);
5648 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5649 sound_id_prefix[2 * MAX_NUM_ELEMENTS + i] =
5650 global_anim_info[i].token_name;
5651 sound_id_prefix[2 * MAX_NUM_ELEMENTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5653 for (i = 0; i < NUM_MUSIC_PREFIXES; i++)
5654 music_id_prefix[i] = music_prefix_info[i].prefix;
5655 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5656 music_id_prefix[NUM_MUSIC_PREFIXES + i] =
5657 global_anim_info[i].token_name;
5658 music_id_prefix[NUM_MUSIC_PREFIXES + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5660 for (i = 0; i < NUM_ACTIONS; i++)
5661 action_id_suffix[i] = element_action_info[i].suffix;
5662 action_id_suffix[NUM_ACTIONS] = NULL;
5664 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
5665 direction_id_suffix[i] = element_direction_info[i].suffix;
5666 direction_id_suffix[NUM_DIRECTIONS_FULL] = NULL;
5668 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
5669 special_id_suffix[i] = special_suffix_info[i].suffix;
5670 special_id_suffix[NUM_SPECIAL_GFX_ARGS] = NULL;
5672 for (i = 0; i < MAX_LEVELS; i++)
5673 level_id_suffix[i] = get_level_id_suffix(i);
5674 level_id_suffix[MAX_LEVELS] = NULL;
5676 InitImageList(image_config, NUM_IMAGE_FILES, image_config_suffix,
5677 image_id_prefix, action_id_suffix, direction_id_suffix,
5678 special_id_suffix, ignore_image_tokens);
5679 InitSoundList(sound_config, NUM_SOUND_FILES, sound_config_suffix,
5680 sound_id_prefix, action_id_suffix, dummy,
5681 special_id_suffix, ignore_sound_tokens);
5682 InitMusicList(music_config, NUM_MUSIC_FILES, music_config_suffix,
5683 music_id_prefix, action_id_suffix, special_id_suffix,
5684 level_id_suffix, ignore_music_tokens);
5687 static void InitMixer(void)
5694 static void InitVideoOverlay(void)
5696 // if virtual buttons are not loaded from setup file, repeat initializing
5697 // virtual buttons grid with default values now that video is initialized
5698 if (!setup.touch.grid_initialized)
5701 InitTileCursorInfo();
5705 void InitGfxBuffers(void)
5707 static int win_xsize_last = -1;
5708 static int win_ysize_last = -1;
5710 // create additional image buffers for double-buffering and cross-fading
5712 if (WIN_XSIZE != win_xsize_last || WIN_YSIZE != win_ysize_last)
5714 // used to temporarily store the backbuffer -- only re-create if changed
5715 ReCreateBitmap(&bitmap_db_store_1, WIN_XSIZE, WIN_YSIZE);
5716 ReCreateBitmap(&bitmap_db_store_2, WIN_XSIZE, WIN_YSIZE);
5718 win_xsize_last = WIN_XSIZE;
5719 win_ysize_last = WIN_YSIZE;
5722 ReCreateBitmap(&bitmap_db_field, FXSIZE, FYSIZE);
5723 ReCreateBitmap(&bitmap_db_door_1, 3 * DXSIZE, DYSIZE);
5724 ReCreateBitmap(&bitmap_db_door_2, 3 * VXSIZE, VYSIZE);
5726 // initialize screen properties
5727 InitGfxFieldInfo(SX, SY, SXSIZE, SYSIZE,
5728 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
5730 InitGfxDoor1Info(DX, DY, DXSIZE, DYSIZE);
5731 InitGfxDoor2Info(VX, VY, VXSIZE, VYSIZE);
5732 InitGfxDoor3Info(EX, EY, EXSIZE, EYSIZE);
5733 InitGfxWindowInfo(WIN_XSIZE, WIN_YSIZE);
5734 InitGfxScrollbufferInfo(FXSIZE, FYSIZE);
5735 InitGfxClipRegion(FALSE, -1, -1, -1, -1);
5737 // required if door size definitions have changed
5738 InitGraphicCompatibilityInfo_Doors();
5740 InitGfxBuffers_BD();
5741 InitGfxBuffers_EM();
5742 InitGfxBuffers_SP();
5743 InitGfxBuffers_MM();
5746 static void InitGfx(void)
5748 struct GraphicInfo *graphic_info_last = graphic_info;
5749 char *filename_font_initial = NULL;
5750 char *filename_image_initial[NUM_INITIAL_IMAGES] = { NULL };
5751 char *image_token[NUM_INITIAL_IMAGES] =
5753 CONFIG_TOKEN_GLOBAL_BUSY_INITIAL,
5754 CONFIG_TOKEN_GLOBAL_BUSY,
5755 CONFIG_TOKEN_GLOBAL_BUSY_PLAYFIELD,
5756 CONFIG_TOKEN_BACKGROUND,
5757 CONFIG_TOKEN_BACKGROUND_LOADING_INITIAL,
5758 CONFIG_TOKEN_BACKGROUND_LOADING
5760 struct MenuPosInfo *init_busy[NUM_INITIAL_IMAGES_BUSY] =
5764 &init.busy_playfield
5766 Bitmap *bitmap_font_initial = NULL;
5767 int parameter[NUM_INITIAL_IMAGES][NUM_GFX_ARGS];
5770 // determine settings for initial font (for displaying startup messages)
5771 for (i = 0; image_config[i].token != NULL; i++)
5773 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5775 char font_token[128];
5778 sprintf(font_token, "%s_%d", CONFIG_TOKEN_FONT_INITIAL, j + 1);
5779 len_font_token = strlen(font_token);
5781 if (strEqual(image_config[i].token, font_token))
5783 filename_font_initial = image_config[i].value;
5785 else if (strlen(image_config[i].token) > len_font_token &&
5786 strncmp(image_config[i].token, font_token, len_font_token) == 0)
5788 if (strEqual(&image_config[i].token[len_font_token], ".x"))
5789 font_initial[j].src_x = atoi(image_config[i].value);
5790 else if (strEqual(&image_config[i].token[len_font_token], ".y"))
5791 font_initial[j].src_y = atoi(image_config[i].value);
5792 else if (strEqual(&image_config[i].token[len_font_token], ".width"))
5793 font_initial[j].width = atoi(image_config[i].value);
5794 else if (strEqual(&image_config[i].token[len_font_token], ".height"))
5795 font_initial[j].height = atoi(image_config[i].value);
5800 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5802 font_initial[j].num_chars = DEFAULT_NUM_CHARS_PER_FONT;
5803 font_initial[j].num_chars_per_line = DEFAULT_NUM_CHARS_PER_LINE;
5806 if (filename_font_initial == NULL) // should not happen
5807 Fail("cannot get filename for '%s'", CONFIG_TOKEN_FONT_INITIAL);
5810 InitGfxCustomArtworkInfo();
5811 InitGfxOtherSettings();
5813 InitGfxTileSizeInfo(TILESIZE, TILESIZE);
5815 bitmap_font_initial = LoadCustomImage(filename_font_initial);
5817 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5818 font_initial[j].bitmap = bitmap_font_initial;
5820 InitFontGraphicInfo();
5822 InitMenuDesignSettings_Static();
5824 // initialize settings for initial images with default values
5825 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5826 for (j = 0; j < NUM_GFX_ARGS; j++)
5828 get_graphic_parameter_value(image_config_suffix[j].value,
5829 image_config_suffix[j].token,
5830 image_config_suffix[j].type);
5832 // read settings for initial images from default custom artwork config
5833 char *gfx_config_filename = getPath3(options.graphics_directory,
5835 GRAPHICSINFO_FILENAME);
5837 if (fileExists(gfx_config_filename))
5839 SetupFileHash *setup_file_hash = loadSetupFileHash(gfx_config_filename);
5841 if (setup_file_hash)
5843 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5845 char *filename = getHashEntry(setup_file_hash, image_token[i]);
5849 filename_image_initial[i] = getStringCopy(filename);
5851 for (j = 0; image_config_suffix[j].token != NULL; j++)
5853 int type = image_config_suffix[j].type;
5854 char *suffix = image_config_suffix[j].token;
5855 char *token = getStringCat2(image_token[i], suffix);
5856 char *value = getHashEntry(setup_file_hash, token);
5858 checked_free(token);
5862 get_graphic_parameter_value(value, suffix, type);
5867 // read values from custom graphics config file
5868 InitMenuDesignSettings_FromHash(setup_file_hash, FALSE);
5870 freeSetupFileHash(setup_file_hash);
5874 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5876 if (filename_image_initial[i] == NULL)
5878 int len_token = strlen(image_token[i]);
5880 // read settings for initial images from static default artwork config
5881 for (j = 0; image_config[j].token != NULL; j++)
5883 if (strEqual(image_config[j].token, image_token[i]))
5885 filename_image_initial[i] = getStringCopy(image_config[j].value);
5887 else if (strlen(image_config[j].token) > len_token &&
5888 strncmp(image_config[j].token, image_token[i], len_token) == 0)
5890 for (k = 0; image_config_suffix[k].token != NULL; k++)
5892 if (strEqual(&image_config[j].token[len_token],
5893 image_config_suffix[k].token))
5895 get_graphic_parameter_value(image_config[j].value,
5896 image_config_suffix[k].token,
5897 image_config_suffix[k].type);
5904 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5906 if (filename_image_initial[i] == NULL) // should not happen
5907 Fail("cannot get filename for '%s'", image_token[i]);
5909 image_initial[i].bitmaps =
5910 checked_calloc(sizeof(Bitmap *) * NUM_IMG_BITMAP_POINTERS);
5912 if (!strEqual(filename_image_initial[i], UNDEFINED_FILENAME))
5913 image_initial[i].bitmaps[IMG_BITMAP_STANDARD] =
5914 LoadCustomImage(filename_image_initial[i]);
5916 checked_free(filename_image_initial[i]);
5919 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5920 image_initial[i].use_image_size = TRUE;
5922 graphic_info = image_initial; // graphic == 0 => image_initial
5924 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5925 set_graphic_parameters_ext(i, parameter[i], image_initial[i].bitmaps);
5927 graphic_info = graphic_info_last;
5929 for (i = 0; i < NUM_INITIAL_IMAGES_BUSY; i++)
5931 // set image size for busy animations
5932 init_busy[i]->width = image_initial[i].width;
5933 init_busy[i]->height = image_initial[i].height;
5936 SetLoadingBackgroundImage();
5938 ClearRectangleOnBackground(window, 0, 0, WIN_XSIZE, WIN_YSIZE);
5942 InitGfxDrawBusyAnimFunction(DrawInitAnim);
5943 InitGfxDrawGlobalAnimFunction(DrawGlobalAnimations);
5944 InitGfxDrawGlobalBorderFunction(DrawMaskedBorderToTarget);
5945 InitGfxDrawTileCursorFunction(DrawTileCursor);
5946 InitGfxDrawEnvelopeRequestFunction(DrawEnvelopeRequestToScreen);
5948 gfx.fade_border_source_status = global.border_status;
5949 gfx.fade_border_target_status = global.border_status;
5950 gfx.masked_border_bitmap_ptr = backbuffer;
5952 // use copy of busy animation to prevent change while reloading artwork
5956 static void InitGfxBackground(void)
5958 fieldbuffer = bitmap_db_field;
5959 SetDrawtoField(DRAW_TO_BACKBUFFER);
5961 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
5963 redraw_mask = REDRAW_ALL;
5966 static void InitSnd(void)
5968 InitSoundSettings_Static();
5970 // read settings for initial sounds from default custom artwork config
5971 char *snd_config_filename = getPath3(options.sounds_directory,
5973 SOUNDSINFO_FILENAME);
5975 if (fileExists(snd_config_filename))
5977 SetupFileHash *setup_file_hash = loadSetupFileHash(snd_config_filename);
5979 if (setup_file_hash)
5981 // read values from custom sounds config file
5982 InitSoundSettings_FromHash(setup_file_hash, FALSE);
5984 freeSetupFileHash(setup_file_hash);
5989 static void InitLevelInfo(void)
5991 LoadLevelInfo(); // global level info
5992 LoadLevelSetup_LastSeries(); // last played series info
5993 LoadLevelSetup_SeriesInfo(); // last played level info
5995 if (global.autoplay_leveldir &&
5996 global.autoplay_mode != AUTOPLAY_MODE_TEST)
5998 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
5999 global.autoplay_leveldir);
6000 if (leveldir_current == NULL)
6001 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
6004 SetLevelSetInfo(leveldir_current->identifier, level_nr);
6007 static void InitLevelArtworkInfo(void)
6009 LoadLevelArtworkInfo();
6012 static void InitImages(void)
6014 print_timestamp_init("InitImages");
6017 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
6018 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
6019 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
6020 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
6021 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
6022 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
6023 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
6024 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
6027 setLevelArtworkDir(artwork.gfx_first);
6030 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
6031 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
6032 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
6033 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
6034 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
6035 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
6036 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
6037 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
6041 Debug("init:InitImages", "InitImages for '%s' ['%s', '%s'] ['%s', '%s']",
6042 leveldir_current->identifier,
6043 artwork.gfx_current_identifier,
6044 artwork.gfx_current->identifier,
6045 leveldir_current->graphics_set,
6046 leveldir_current->graphics_path);
6049 UPDATE_BUSY_STATE();
6051 ReloadCustomImages();
6052 print_timestamp_time("ReloadCustomImages");
6054 UPDATE_BUSY_STATE();
6056 LoadCustomElementDescriptions();
6057 print_timestamp_time("LoadCustomElementDescriptions");
6059 UPDATE_BUSY_STATE();
6061 LoadMenuDesignSettings();
6062 print_timestamp_time("LoadMenuDesignSettings");
6064 UPDATE_BUSY_STATE();
6066 ReinitializeGraphics();
6067 print_timestamp_time("ReinitializeGraphics");
6069 LoadMenuDesignSettings_AfterGraphics();
6070 print_timestamp_time("LoadMenuDesignSettings_AfterGraphics");
6072 UPDATE_BUSY_STATE();
6074 print_timestamp_done("InitImages");
6077 static void InitSound(void)
6079 print_timestamp_init("InitSound");
6081 // set artwork path to send it to the sound server process
6082 setLevelArtworkDir(artwork.snd_first);
6084 InitReloadCustomSounds();
6085 print_timestamp_time("InitReloadCustomSounds");
6087 LoadSoundSettings();
6088 print_timestamp_time("LoadSoundSettings");
6090 ReinitializeSounds();
6091 print_timestamp_time("ReinitializeSounds");
6093 print_timestamp_done("InitSound");
6096 static void InitMusic(void)
6098 print_timestamp_init("InitMusic");
6100 // set artwork path to send it to the sound server process
6101 setLevelArtworkDir(artwork.mus_first);
6103 InitReloadCustomMusic();
6104 print_timestamp_time("InitReloadCustomMusic");
6106 ReinitializeMusic();
6107 print_timestamp_time("ReinitializeMusic");
6109 print_timestamp_done("InitMusic");
6112 static void InitArtworkDone(void)
6114 if (program.headless)
6117 InitGlobalAnimations();
6120 static void InitNetworkSettings(void)
6122 boolean network_enabled = (options.network || setup.network_mode);
6123 char *network_server = (options.server_host != NULL ? options.server_host :
6124 setup.network_server_hostname);
6126 if (strEqual(network_server, STR_NETWORK_AUTO_DETECT))
6127 network_server = NULL;
6129 InitNetworkInfo(network_enabled,
6133 options.server_port);
6136 void InitNetworkServer(void)
6138 if (!network.enabled || network.connected)
6141 LimitScreenUpdates(FALSE);
6143 if (game_status == GAME_MODE_LOADING)
6146 if (!ConnectToServer(network.server_host, network.server_port))
6148 network.enabled = FALSE;
6150 setup.network_mode = FALSE;
6154 SendToServer_ProtocolVersion();
6155 SendToServer_PlayerName(setup.player_name);
6156 SendToServer_NrWanted(setup.network_player_nr + 1);
6158 network.connected = TRUE;
6161 // short time to recognize result of network initialization
6162 if (game_status == GAME_MODE_LOADING)
6163 Delay_WithScreenUpdates(1000);
6166 static boolean CheckArtworkConfigForCustomElements(char *filename)
6168 SetupFileHash *setup_file_hash;
6169 boolean redefined_ce_found = FALSE;
6171 // !!! CACHE THIS BY USING HASH 'filename' => 'true/false' !!!
6173 if ((setup_file_hash = loadSetupFileHash(filename)) != NULL)
6175 BEGIN_HASH_ITERATION(setup_file_hash, itr)
6177 char *token = HASH_ITERATION_TOKEN(itr);
6179 if (strPrefix(token, "custom_"))
6181 redefined_ce_found = TRUE;
6186 END_HASH_ITERATION(setup_file_hash, itr)
6188 freeSetupFileHash(setup_file_hash);
6191 return redefined_ce_found;
6194 static boolean CheckArtworkTypeForRedefinedCustomElements(int type)
6196 char *filename_base, *filename_local;
6197 boolean redefined_ce_found = FALSE;
6199 setLevelArtworkDir(ARTWORK_FIRST_NODE(artwork, type));
6202 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6203 "leveldir_current->identifier == '%s'",
6204 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
6205 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6206 "leveldir_current->graphics_path == '%s'",
6207 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
6208 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6209 "leveldir_current->graphics_set == '%s'",
6210 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
6211 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6212 "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
6213 leveldir_current == NULL ? "[NULL]" :
6214 LEVELDIR_ARTWORK_SET(leveldir_current, type));
6217 // first look for special artwork configured in level series config
6218 filename_base = getCustomArtworkLevelConfigFilename(type);
6221 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6222 "filename_base == '%s'", filename_base);
6225 if (fileExists(filename_base))
6226 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_base);
6228 filename_local = getCustomArtworkConfigFilename(type);
6231 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6232 "filename_local == '%s'", filename_local);
6235 if (filename_local != NULL && !strEqual(filename_base, filename_local))
6236 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_local);
6239 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6240 "redefined_ce_found == %d", redefined_ce_found);
6243 return redefined_ce_found;
6246 static void InitOverrideArtwork(void)
6248 boolean redefined_ce_found = FALSE;
6250 // to check if this level set redefines any CEs, do not use overriding
6251 gfx.override_level_graphics = FALSE;
6252 gfx.override_level_sounds = FALSE;
6253 gfx.override_level_music = FALSE;
6255 // now check if this level set has definitions for custom elements
6256 if (setup.override_level_graphics == AUTO ||
6257 setup.override_level_sounds == AUTO ||
6258 setup.override_level_music == AUTO)
6259 redefined_ce_found =
6260 (CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_GRAPHICS) |
6261 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_SOUNDS) |
6262 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_MUSIC));
6265 Debug("init:InitOverrideArtwork", "redefined_ce_found == %d",
6266 redefined_ce_found);
6269 if (redefined_ce_found)
6271 // this level set has CE definitions: change "AUTO" to "FALSE"
6272 gfx.override_level_graphics = (setup.override_level_graphics == TRUE);
6273 gfx.override_level_sounds = (setup.override_level_sounds == TRUE);
6274 gfx.override_level_music = (setup.override_level_music == TRUE);
6278 // this level set has no CE definitions: change "AUTO" to "TRUE"
6279 gfx.override_level_graphics = (setup.override_level_graphics != FALSE);
6280 gfx.override_level_sounds = (setup.override_level_sounds != FALSE);
6281 gfx.override_level_music = (setup.override_level_music != FALSE);
6285 Debug("init:InitOverrideArtwork", "%d, %d, %d",
6286 gfx.override_level_graphics,
6287 gfx.override_level_sounds,
6288 gfx.override_level_music);
6292 static char *setNewArtworkIdentifier(int type)
6294 static char *last_leveldir_identifier[3] = { NULL, NULL, NULL };
6295 static char *last_artwork_identifier[3] = { NULL, NULL, NULL };
6296 static boolean last_override_level_artwork[3] = { FALSE, FALSE, FALSE };
6297 static boolean last_has_custom_artwork_set[3] = { FALSE, FALSE, FALSE };
6298 static boolean initialized[3] = { FALSE, FALSE, FALSE };
6299 TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type);
6300 boolean setup_override_artwork = GFX_OVERRIDE_ARTWORK(type);
6301 char *setup_artwork_set = SETUP_ARTWORK_SET(setup, type);
6302 char *leveldir_identifier = leveldir_current->identifier;
6303 // !!! setLevelArtworkDir() should be moved to an earlier stage !!!
6304 char *leveldir_artwork_set = setLevelArtworkDir(artwork_first_node);
6305 boolean has_level_artwork_set = (leveldir_artwork_set != NULL);
6306 TreeInfo *custom_artwork_set =
6307 getTreeInfoFromIdentifier(artwork_first_node, leveldir_identifier);
6308 boolean has_custom_artwork_set = (custom_artwork_set != NULL);
6309 char *artwork_current_identifier;
6310 char *artwork_new_identifier = NULL; // default: nothing has changed
6312 // leveldir_current may be invalid (level group, parent link)
6313 if (!validLevelSeries(leveldir_current))
6316 /* 1st step: determine artwork set to be activated in descending order:
6317 --------------------------------------------------------------------
6318 1. setup artwork (when configured to override everything else)
6319 2. artwork set configured in "levelinfo.conf" of current level set
6320 (artwork in level directory will have priority when loading later)
6321 3. artwork in level directory (stored in artwork sub-directory)
6322 4. setup artwork (currently configured in setup menu) */
6324 if (setup_override_artwork)
6325 artwork_current_identifier = setup_artwork_set;
6326 else if (has_level_artwork_set)
6327 artwork_current_identifier = leveldir_artwork_set;
6328 else if (has_custom_artwork_set)
6329 artwork_current_identifier = leveldir_identifier;
6331 artwork_current_identifier = setup_artwork_set;
6333 /* 2nd step: check if it is really needed to reload artwork set
6334 ------------------------------------------------------------ */
6336 // ---------- reload if level set and also artwork set has changed ----------
6337 if (last_leveldir_identifier[type] != leveldir_identifier &&
6338 (last_has_custom_artwork_set[type] || has_custom_artwork_set))
6339 artwork_new_identifier = artwork_current_identifier;
6341 last_leveldir_identifier[type] = leveldir_identifier;
6342 last_has_custom_artwork_set[type] = has_custom_artwork_set;
6344 // ---------- reload if "override artwork" setting has changed --------------
6345 if (last_override_level_artwork[type] != setup_override_artwork)
6346 artwork_new_identifier = artwork_current_identifier;
6348 last_override_level_artwork[type] = setup_override_artwork;
6350 // ---------- reload if current artwork identifier has changed --------------
6351 if (!strEqual(last_artwork_identifier[type], artwork_current_identifier))
6352 artwork_new_identifier = artwork_current_identifier;
6354 // (we cannot compare string pointers here, so copy string content itself)
6355 setString(&last_artwork_identifier[type], artwork_current_identifier);
6357 // ---------- set new artwork identifier ----------
6358 *(ARTWORK_CURRENT_IDENTIFIER_PTR(artwork, type)) = artwork_current_identifier;
6360 // ---------- do not reload directly after starting -------------------------
6361 if (!initialized[type])
6362 artwork_new_identifier = NULL;
6364 initialized[type] = TRUE;
6366 return artwork_new_identifier;
6369 static void InitArtworkIdentifier(void)
6371 setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6372 setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6373 setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6376 void ReloadCustomArtwork(int force_reload)
6378 int last_game_status = game_status; // save current game status
6379 char *gfx_new_identifier;
6380 char *snd_new_identifier;
6381 char *mus_new_identifier;
6382 boolean force_reload_gfx = (force_reload & (1 << ARTWORK_TYPE_GRAPHICS));
6383 boolean force_reload_snd = (force_reload & (1 << ARTWORK_TYPE_SOUNDS));
6384 boolean force_reload_mus = (force_reload & (1 << ARTWORK_TYPE_MUSIC));
6385 boolean reload_needed;
6387 InitOverrideArtwork();
6389 AdjustGraphicsForEMC();
6390 AdjustSoundsForEMC();
6392 gfx_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6393 snd_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6394 mus_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6396 reload_needed = (gfx_new_identifier != NULL || force_reload_gfx ||
6397 snd_new_identifier != NULL || force_reload_snd ||
6398 mus_new_identifier != NULL || force_reload_mus);
6403 print_timestamp_init("ReloadCustomArtwork");
6405 SetGameStatus(GAME_MODE_LOADING);
6407 FadeOut(REDRAW_ALL);
6409 SetLoadingBackgroundImage();
6411 ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6412 print_timestamp_time("ClearRectangleOnBackground");
6416 UPDATE_BUSY_STATE();
6418 InitMissingFileHash();
6420 if (gfx_new_identifier != NULL || force_reload_gfx)
6423 Debug("init:ReloadCustomArtwork",
6424 "RELOADING GRAPHICS '%s' -> '%s' ['%s', '%s']",
6425 artwork.gfx_current_identifier,
6427 artwork.gfx_current->identifier,
6428 leveldir_current->graphics_set);
6432 print_timestamp_time("InitImages");
6435 if (snd_new_identifier != NULL || force_reload_snd)
6438 print_timestamp_time("InitSound");
6441 if (mus_new_identifier != NULL || force_reload_mus)
6444 print_timestamp_time("InitMusic");
6449 SetGameStatus(last_game_status); // restore current game status
6451 FadeOut(REDRAW_ALL);
6453 RedrawGlobalBorder();
6455 // force redraw of (open or closed) door graphics
6456 SetDoorState(DOOR_OPEN_ALL);
6457 CloseDoor(DOOR_CLOSE_ALL | DOOR_NO_DELAY);
6459 FadeSetEnterScreen();
6460 FadeSkipNextFadeOut();
6462 print_timestamp_done("ReloadCustomArtwork");
6464 LimitScreenUpdates(FALSE);
6467 void KeyboardAutoRepeatOffUnlessAutoplay(void)
6469 if (global.autoplay_leveldir == NULL)
6470 KeyboardAutoRepeatOff();
6473 void DisplayExitMessage(char *format, va_list ap)
6475 // also check for initialized video (headless flag may be temporarily unset)
6476 if (program.headless || !video.initialized)
6479 // check if draw buffer and fonts for exit message are already available
6480 if (drawto == NULL || font_initial[NUM_INITIAL_FONTS - 1].bitmap == NULL)
6483 int font_1 = FC_RED;
6484 int font_2 = FC_YELLOW;
6485 int font_3 = FC_BLUE;
6486 int font_width = getFontWidth(font_2);
6487 int font_height = getFontHeight(font_2);
6490 int sxsize = WIN_XSIZE - 2 * sx;
6491 int sysize = WIN_YSIZE - 2 * sy;
6492 int line_length = sxsize / font_width;
6493 int max_lines = sysize / font_height;
6494 int num_lines_printed;
6498 gfx.sxsize = sxsize;
6499 gfx.sysize = sysize;
6503 ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6505 DrawTextSCentered(sy, font_1, "Fatal error:");
6506 sy += 3 * font_height;;
6509 DrawTextBufferVA(sx, sy, format, ap, font_2,
6510 line_length, line_length, max_lines,
6511 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6512 sy += (num_lines_printed + 3) * font_height;
6514 DrawTextSCentered(sy, font_1, "For details, see the following error file:");
6515 sy += 3 * font_height;
6518 DrawTextBuffer(sx, sy, program.log_filename, font_2,
6519 line_length, line_length, max_lines,
6520 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6522 DrawTextSCentered(SYSIZE - 20, font_3, "Press any key or button to exit");
6524 redraw_mask = REDRAW_ALL;
6526 // force drawing exit message even if screen updates are currently limited
6527 LimitScreenUpdates(FALSE);
6531 // deactivate toons and global animations on error message screen
6532 setup.global_animations = FALSE;
6534 WaitForEventToContinue();
6538 // ============================================================================
6540 // ============================================================================
6544 print_timestamp_init("OpenAll");
6546 SetGameStatus(GAME_MODE_LOADING);
6550 InitGlobal(); // initialize some global variables
6552 InitRND(NEW_RANDOMIZE);
6553 InitSimpleRandom(NEW_RANDOMIZE);
6554 InitBetterRandom(NEW_RANDOMIZE);
6556 InitMissingFileHash();
6558 print_timestamp_time("[init global stuff]");
6562 print_timestamp_time("[init setup/config stuff (1)]");
6564 if (options.execute_command)
6565 Execute_Command(options.execute_command);
6567 InitNetworkSettings();
6571 if (network.serveronly)
6573 #if defined(PLATFORM_UNIX)
6574 NetworkServer(network.server_port, TRUE);
6576 Warn("networking only supported in Unix version");
6579 exit(0); // never reached, server loops forever
6583 print_timestamp_time("[init setup/config stuff (2)]");
6585 print_timestamp_time("[init setup/config stuff (3)]");
6586 InitArtworkInfo(); // needed before loading gfx, sound & music
6587 print_timestamp_time("[init setup/config stuff (4)]");
6588 InitArtworkConfig(); // needed before forking sound child process
6589 print_timestamp_time("[init setup/config stuff (5)]");
6591 print_timestamp_time("[init setup/config stuff (6)]");
6595 print_timestamp_time("[init setup/config stuff]");
6597 InitVideoDefaults();
6599 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
6602 InitEventFilter(FilterMouseMotionEvents);
6604 print_timestamp_time("[init video stuff]");
6606 InitElementPropertiesStatic();
6607 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
6608 InitElementPropertiesGfxElement();
6610 print_timestamp_time("[init element properties stuff]");
6615 print_timestamp_time("InitGfx");
6618 print_timestamp_time("InitLevelInfo");
6620 InitLevelArtworkInfo();
6621 print_timestamp_time("InitLevelArtworkInfo");
6623 InitOverrideArtwork(); // needs to know current level directory
6624 print_timestamp_time("InitOverrideArtwork");
6626 InitArtworkIdentifier(); // needs to know current level directory
6627 print_timestamp_time("InitArtworkIdentifier");
6629 InitImages(); // needs to know current level directory
6630 print_timestamp_time("InitImages");
6632 InitSound(); // needs to know current level directory
6633 print_timestamp_time("InitSound");
6635 InitMusic(); // needs to know current level directory
6636 print_timestamp_time("InitMusic");
6640 InitGfxBackground();
6647 if (global.autoplay_leveldir)
6652 else if (global.patchtapes_leveldir)
6657 else if (global.convert_leveldir)
6662 else if (global.dumplevel_leveldir)
6667 else if (global.dumptape_leveldir)
6672 else if (global.create_sketch_images_dir)
6674 CreateLevelSketchImages();
6677 else if (global.create_collect_images_dir)
6679 CreateCollectElementImages();
6683 InitNetworkServer();
6685 SetGameStatus(GAME_MODE_MAIN);
6687 FadeSetEnterScreen();
6688 if (!(fading.fade_mode & FADE_TYPE_TRANSFORM))
6689 FadeSkipNextFadeOut();
6691 print_timestamp_time("[post-artwork]");
6693 print_timestamp_done("OpenAll");
6695 if (setup.ask_for_remaining_tapes)
6696 setup.ask_for_uploading_tapes = TRUE;
6701 Debug("internal:path", "SDL_GetBasePath() == '%s'",
6703 Debug("internal:path", "SDL_GetPrefPath() == '%s'",
6704 SDL_GetPrefPath("artsoft", "rocksndiamonds"));
6705 #if defined(PLATFORM_ANDROID)
6706 Debug("internal:path", "SDL_AndroidGetInternalStoragePath() == '%s'",
6707 SDL_AndroidGetInternalStoragePath());
6708 Debug("internal:path", "SDL_AndroidGetExternalStoragePath() == '%s'",
6709 SDL_AndroidGetExternalStoragePath());
6710 Debug("internal:path", "SDL_AndroidGetExternalStorageState() == '%s'",
6711 (SDL_AndroidGetExternalStorageState() &
6712 SDL_ANDROID_EXTERNAL_STORAGE_WRITE ? "writable" :
6713 SDL_AndroidGetExternalStorageState() &
6714 SDL_ANDROID_EXTERNAL_STORAGE_READ ? "readable" : "not available"));
6719 static boolean WaitForApiThreads(void)
6721 DelayCounter thread_delay = { 10000 };
6723 if (program.api_thread_count == 0)
6726 // deactivate global animations (not accessible in game state "loading")
6727 setup.global_animations = FALSE;
6729 // set game state to "loading" to be able to show busy animation
6730 SetGameStatus(GAME_MODE_LOADING);
6732 ResetDelayCounter(&thread_delay);
6734 // wait for threads to finish (and fail on timeout)
6735 while (program.api_thread_count > 0)
6737 if (DelayReached(&thread_delay))
6739 Error("failed waiting for threads - TIMEOUT");
6744 UPDATE_BUSY_STATE();
6752 void CloseAllAndExit(int exit_value)
6754 WaitForApiThreads();
6759 CloseAudio(); // called after freeing sounds (needed for SDL)
6768 // set a flag to tell the network server thread to quit and wait for it
6769 // using SDL_WaitThread()
6771 // Code used with SDL 1.2:
6772 // if (network.server_thread) // terminate network server
6773 // SDL_KillThread(network.server_thread);
6775 CloseVideoDisplay();
6776 ClosePlatformDependentStuff();
6778 if (exit_value != 0 && !options.execute_command)
6780 // fall back to default level set (current set may have caused an error)
6781 SaveLevelSetup_LastSeries_Deactivate();
6783 // tell user where to find error log file which may contain more details
6784 // (error notification now directly displayed on screen inside R'n'D
6785 // NotifyUserAboutErrorFile(); // currently only works for Windows