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(int graphic)
1342 int original_width = getOriginalImageWidthFromImageID(graphic);
1343 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1345 return original_width * scale_up_factor;
1348 static int get_scaled_graphic_height(int graphic)
1350 int original_height = getOriginalImageHeightFromImageID(graphic);
1351 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1353 return original_height * scale_up_factor;
1356 static void set_graphic_parameters_ext(int graphic, int *parameter,
1357 Bitmap **src_bitmaps)
1359 struct GraphicInfo *g = &graphic_info[graphic];
1360 Bitmap *src_bitmap = (src_bitmaps ? src_bitmaps[IMG_BITMAP_STANDARD] : NULL);
1361 int anim_frames_per_row = 1, anim_frames_per_col = 1;
1362 int anim_frames_per_line = 1;
1364 // always start with reliable default values
1365 g->src_image_width = 0;
1366 g->src_image_height = 0;
1369 g->width = TILEX; // default for element graphics
1370 g->height = TILEY; // default for element graphics
1371 g->offset_x = 0; // one or both of these values ...
1372 g->offset_y = 0; // ... will be corrected later
1373 g->offset2_x = 0; // one or both of these values ...
1374 g->offset2_y = 0; // ... will be corrected later
1375 g->swap_double_tiles = -1; // auto-detect tile swapping
1376 g->crumbled_like = -1; // do not use clone element
1377 g->diggable_like = -1; // do not use clone element
1378 g->border_size = TILEX / 8; // "CRUMBLED" border size
1379 g->scale_up_factor = 1; // default: no scaling up
1380 g->tile_size = TILESIZE; // default: standard tile size
1381 g->clone_from = -1; // do not use clone graphic
1382 g->init_delay_fixed = 0;
1383 g->init_delay_random = 0;
1384 g->init_delay_action = -1;
1385 g->anim_delay_fixed = 0;
1386 g->anim_delay_random = 0;
1387 g->anim_delay_action = -1;
1388 g->post_delay_fixed = 0;
1389 g->post_delay_random = 0;
1390 g->post_delay_action = -1;
1391 g->init_event = ANIM_EVENT_UNDEFINED;
1392 g->anim_event = ANIM_EVENT_UNDEFINED;
1393 g->init_event_action = -1;
1394 g->anim_event_action = -1;
1395 g->draw_masked = FALSE;
1397 g->fade_mode = FADE_MODE_DEFAULT;
1401 g->auto_delay_unit = AUTO_DELAY_UNIT_DEFAULT;
1402 g->align = ALIGN_CENTER; // default for title screens
1403 g->valign = VALIGN_MIDDLE; // default for title screens
1404 g->sort_priority = 0; // default for title screens
1406 g->style = STYLE_DEFAULT;
1408 g->bitmaps = src_bitmaps;
1409 g->bitmap = src_bitmap;
1411 // optional zoom factor for scaling up the image to a larger size
1412 if (parameter[GFX_ARG_SCALE_UP_FACTOR] != ARG_UNDEFINED_VALUE)
1413 g->scale_up_factor = parameter[GFX_ARG_SCALE_UP_FACTOR];
1414 if (g->scale_up_factor < 1)
1415 g->scale_up_factor = 1; // no scaling
1417 // optional tile size for using non-standard image size
1418 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1420 g->tile_size = parameter[GFX_ARG_TILE_SIZE];
1423 // CHECK: should tile sizes less than standard tile size be allowed?
1424 if (g->tile_size < TILESIZE)
1425 g->tile_size = TILESIZE; // standard tile size
1428 // when setting tile size, also set width and height accordingly
1429 g->width = g->tile_size;
1430 g->height = g->tile_size;
1433 if (g->use_image_size)
1435 // set new default bitmap size (with scaling, but without small images)
1436 g->width = get_scaled_graphic_width(graphic);
1437 g->height = get_scaled_graphic_height(graphic);
1440 // optional width and height of each animation frame
1441 if (parameter[GFX_ARG_WIDTH] != ARG_UNDEFINED_VALUE)
1442 g->width = parameter[GFX_ARG_WIDTH];
1443 if (parameter[GFX_ARG_HEIGHT] != ARG_UNDEFINED_VALUE)
1444 g->height = parameter[GFX_ARG_HEIGHT];
1446 // optional x and y tile position of animation frame sequence
1447 if (parameter[GFX_ARG_XPOS] != ARG_UNDEFINED_VALUE)
1448 g->src_x = parameter[GFX_ARG_XPOS] * g->width;
1449 if (parameter[GFX_ARG_YPOS] != ARG_UNDEFINED_VALUE)
1450 g->src_y = parameter[GFX_ARG_YPOS] * g->height;
1452 // optional x and y pixel position of animation frame sequence
1453 if (parameter[GFX_ARG_X] != ARG_UNDEFINED_VALUE)
1454 g->src_x = parameter[GFX_ARG_X];
1455 if (parameter[GFX_ARG_Y] != ARG_UNDEFINED_VALUE)
1456 g->src_y = parameter[GFX_ARG_Y];
1463 Warn("invalid value %d for '%s.width' (fallback done to %d)",
1464 g->width, getTokenFromImageID(graphic), TILEX);
1467 g->width = TILEX; // will be checked to be inside bitmap later
1473 Warn("invalid value %d for '%s.height' (fallback done to %d)",
1474 g->height, getTokenFromImageID(graphic), TILEY);
1477 g->height = TILEY; // will be checked to be inside bitmap later
1483 // get final bitmap size (with scaling, but without small images)
1484 int src_image_width = get_scaled_graphic_width(graphic);
1485 int src_image_height = get_scaled_graphic_height(graphic);
1487 if (src_image_width == 0 || src_image_height == 0)
1489 // only happens when loaded outside artwork system (like "global.busy")
1490 src_image_width = src_bitmap->width;
1491 src_image_height = src_bitmap->height;
1494 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1496 anim_frames_per_row = MAX(1, src_image_width / g->tile_size);
1497 anim_frames_per_col = MAX(1, src_image_height / g->tile_size);
1501 anim_frames_per_row = MAX(1, src_image_width / g->width);
1502 anim_frames_per_col = MAX(1, src_image_height / g->height);
1505 g->src_image_width = src_image_width;
1506 g->src_image_height = src_image_height;
1509 // correct x or y offset dependent of vertical or horizontal frame order
1510 if (parameter[GFX_ARG_VERTICAL]) // frames are ordered vertically
1512 g->offset_y = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1513 parameter[GFX_ARG_OFFSET] : g->height);
1514 anim_frames_per_line = anim_frames_per_col;
1516 else // frames are ordered horizontally
1518 g->offset_x = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1519 parameter[GFX_ARG_OFFSET] : g->width);
1520 anim_frames_per_line = anim_frames_per_row;
1523 // optionally, the x and y offset of frames can be specified directly
1524 if (parameter[GFX_ARG_XOFFSET] != ARG_UNDEFINED_VALUE)
1525 g->offset_x = parameter[GFX_ARG_XOFFSET];
1526 if (parameter[GFX_ARG_YOFFSET] != ARG_UNDEFINED_VALUE)
1527 g->offset_y = parameter[GFX_ARG_YOFFSET];
1529 // optionally, moving animations may have separate start and end graphics
1530 g->double_movement = parameter[GFX_ARG_2ND_MOVEMENT_TILE];
1532 if (parameter[GFX_ARG_2ND_VERTICAL] == ARG_UNDEFINED_VALUE)
1533 parameter[GFX_ARG_2ND_VERTICAL] = !parameter[GFX_ARG_VERTICAL];
1535 // correct x or y offset2 dependent of vertical or horizontal frame order
1536 if (parameter[GFX_ARG_2ND_VERTICAL]) // frames are ordered vertically
1537 g->offset2_y = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1538 parameter[GFX_ARG_2ND_OFFSET] : g->height);
1539 else // frames are ordered horizontally
1540 g->offset2_x = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1541 parameter[GFX_ARG_2ND_OFFSET] : g->width);
1543 // optionally, the x and y offset of 2nd graphic can be specified directly
1544 if (parameter[GFX_ARG_2ND_XOFFSET] != ARG_UNDEFINED_VALUE)
1545 g->offset2_x = parameter[GFX_ARG_2ND_XOFFSET];
1546 if (parameter[GFX_ARG_2ND_YOFFSET] != ARG_UNDEFINED_VALUE)
1547 g->offset2_y = parameter[GFX_ARG_2ND_YOFFSET];
1549 // optionally, the second movement tile can be specified as start tile
1550 if (parameter[GFX_ARG_2ND_SWAP_TILES] != ARG_UNDEFINED_VALUE)
1551 g->swap_double_tiles= parameter[GFX_ARG_2ND_SWAP_TILES];
1553 // automatically determine correct number of frames, if not defined
1554 if (parameter[GFX_ARG_FRAMES] != ARG_UNDEFINED_VALUE)
1555 g->anim_frames = parameter[GFX_ARG_FRAMES];
1556 else if (parameter[GFX_ARG_XPOS] == 0 && !parameter[GFX_ARG_VERTICAL])
1557 g->anim_frames = anim_frames_per_row;
1558 else if (parameter[GFX_ARG_YPOS] == 0 && parameter[GFX_ARG_VERTICAL])
1559 g->anim_frames = anim_frames_per_col;
1563 if (g->anim_frames < 1) // frames must be at least 1
1566 g->anim_frames_per_line =
1567 (parameter[GFX_ARG_FRAMES_PER_LINE] != ARG_UNDEFINED_VALUE ?
1568 parameter[GFX_ARG_FRAMES_PER_LINE] : anim_frames_per_line);
1570 g->anim_delay = parameter[GFX_ARG_DELAY];
1571 if (g->anim_delay < 1) // delay must be at least 1
1574 g->anim_mode = parameter[GFX_ARG_ANIM_MODE];
1576 // automatically determine correct start frame, if not defined
1577 if (parameter[GFX_ARG_START_FRAME] == ARG_UNDEFINED_VALUE)
1578 g->anim_start_frame = 0;
1579 else if (g->anim_mode & ANIM_REVERSE)
1580 g->anim_start_frame = g->anim_frames - parameter[GFX_ARG_START_FRAME] - 1;
1582 g->anim_start_frame = parameter[GFX_ARG_START_FRAME];
1584 // animation synchronized with global frame counter, not move position
1585 g->anim_global_sync = parameter[GFX_ARG_GLOBAL_SYNC];
1587 // animation synchronized with global anim frame counter, not move position
1588 g->anim_global_anim_sync = parameter[GFX_ARG_GLOBAL_ANIM_SYNC];
1590 // optional element for cloning crumble graphics
1591 if (parameter[GFX_ARG_CRUMBLED_LIKE] != ARG_UNDEFINED_VALUE)
1592 g->crumbled_like = parameter[GFX_ARG_CRUMBLED_LIKE];
1594 // optional element for cloning digging graphics
1595 if (parameter[GFX_ARG_DIGGABLE_LIKE] != ARG_UNDEFINED_VALUE)
1596 g->diggable_like = parameter[GFX_ARG_DIGGABLE_LIKE];
1598 // optional border size for "crumbling" diggable graphics
1599 if (parameter[GFX_ARG_BORDER_SIZE] != ARG_UNDEFINED_VALUE)
1600 g->border_size = parameter[GFX_ARG_BORDER_SIZE];
1602 // used for global animations and player "boring" and "sleeping" actions
1603 if (parameter[GFX_ARG_INIT_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1604 g->init_delay_fixed = parameter[GFX_ARG_INIT_DELAY_FIXED];
1605 if (parameter[GFX_ARG_INIT_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1606 g->init_delay_random = parameter[GFX_ARG_INIT_DELAY_RANDOM];
1607 if (parameter[GFX_ARG_ANIM_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1608 g->anim_delay_fixed = parameter[GFX_ARG_ANIM_DELAY_FIXED];
1609 if (parameter[GFX_ARG_ANIM_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1610 g->anim_delay_random = parameter[GFX_ARG_ANIM_DELAY_RANDOM];
1611 if (parameter[GFX_ARG_POST_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1612 g->post_delay_fixed = parameter[GFX_ARG_POST_DELAY_FIXED];
1613 if (parameter[GFX_ARG_POST_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1614 g->post_delay_random = parameter[GFX_ARG_POST_DELAY_RANDOM];
1616 // used for global animations
1617 if (parameter[GFX_ARG_INIT_EVENT] != ARG_UNDEFINED_VALUE)
1618 g->init_event = parameter[GFX_ARG_INIT_EVENT];
1619 if (parameter[GFX_ARG_ANIM_EVENT] != ARG_UNDEFINED_VALUE)
1620 g->anim_event = parameter[GFX_ARG_ANIM_EVENT];
1621 if (parameter[GFX_ARG_INIT_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1622 g->init_event_action = parameter[GFX_ARG_INIT_EVENT_ACTION];
1623 if (parameter[GFX_ARG_ANIM_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1624 g->anim_event_action = parameter[GFX_ARG_ANIM_EVENT_ACTION];
1625 if (parameter[GFX_ARG_INIT_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1626 g->init_delay_action = parameter[GFX_ARG_INIT_DELAY_ACTION];
1627 if (parameter[GFX_ARG_ANIM_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1628 g->anim_delay_action = parameter[GFX_ARG_ANIM_DELAY_ACTION];
1629 if (parameter[GFX_ARG_POST_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1630 g->post_delay_action = parameter[GFX_ARG_POST_DELAY_ACTION];
1632 // used for toon animations and global animations
1633 g->step_offset = parameter[GFX_ARG_STEP_OFFSET];
1634 g->step_xoffset = parameter[GFX_ARG_STEP_XOFFSET];
1635 g->step_yoffset = parameter[GFX_ARG_STEP_YOFFSET];
1636 g->step_delay = parameter[GFX_ARG_STEP_DELAY];
1637 g->direction = parameter[GFX_ARG_DIRECTION];
1638 g->position = parameter[GFX_ARG_POSITION];
1639 g->x = parameter[GFX_ARG_X]; // (may be uninitialized,
1640 g->y = parameter[GFX_ARG_Y]; // unlike src_x and src_y)
1642 if (g->step_delay < 1) // delay must be at least 1
1645 // this is only used for drawing font characters
1646 g->draw_xoffset = parameter[GFX_ARG_DRAW_XOFFSET];
1647 g->draw_yoffset = parameter[GFX_ARG_DRAW_YOFFSET];
1649 // use a different default value for global animations and toons
1650 if ((graphic >= IMG_GFX_GLOBAL_ANIM_1 && graphic <= IMG_GFX_GLOBAL_ANIM_8) ||
1651 (graphic >= IMG_TOON_1 && graphic <= IMG_TOON_20))
1652 g->draw_masked = TRUE;
1654 // this is used for drawing envelopes, global animations and toons
1655 if (parameter[GFX_ARG_DRAW_MASKED] != ARG_UNDEFINED_VALUE)
1656 g->draw_masked = parameter[GFX_ARG_DRAW_MASKED];
1658 // used for toon animations and global animations
1659 if (parameter[GFX_ARG_DRAW_ORDER] != ARG_UNDEFINED_VALUE)
1660 g->draw_order = parameter[GFX_ARG_DRAW_ORDER];
1662 // optional graphic for cloning all graphics settings
1663 if (parameter[GFX_ARG_CLONE_FROM] != ARG_UNDEFINED_VALUE)
1664 g->clone_from = parameter[GFX_ARG_CLONE_FROM];
1666 // optional settings for drawing title screens and title messages
1667 if (parameter[GFX_ARG_FADE_MODE] != ARG_UNDEFINED_VALUE)
1668 g->fade_mode = parameter[GFX_ARG_FADE_MODE];
1669 if (parameter[GFX_ARG_FADE_DELAY] != ARG_UNDEFINED_VALUE)
1670 g->fade_delay = parameter[GFX_ARG_FADE_DELAY];
1671 if (parameter[GFX_ARG_POST_DELAY] != ARG_UNDEFINED_VALUE)
1672 g->post_delay = parameter[GFX_ARG_POST_DELAY];
1673 if (parameter[GFX_ARG_AUTO_DELAY] != ARG_UNDEFINED_VALUE)
1674 g->auto_delay = parameter[GFX_ARG_AUTO_DELAY];
1675 if (parameter[GFX_ARG_AUTO_DELAY_UNIT] != ARG_UNDEFINED_VALUE)
1676 g->auto_delay_unit = parameter[GFX_ARG_AUTO_DELAY_UNIT];
1677 if (parameter[GFX_ARG_ALIGN] != ARG_UNDEFINED_VALUE)
1678 g->align = parameter[GFX_ARG_ALIGN];
1679 if (parameter[GFX_ARG_VALIGN] != ARG_UNDEFINED_VALUE)
1680 g->valign = parameter[GFX_ARG_VALIGN];
1681 if (parameter[GFX_ARG_SORT_PRIORITY] != ARG_UNDEFINED_VALUE)
1682 g->sort_priority = parameter[GFX_ARG_SORT_PRIORITY];
1684 if (parameter[GFX_ARG_CLASS] != ARG_UNDEFINED_VALUE)
1685 g->class = parameter[GFX_ARG_CLASS];
1686 if (parameter[GFX_ARG_STYLE] != ARG_UNDEFINED_VALUE)
1687 g->style = parameter[GFX_ARG_STYLE];
1689 // this is only used for drawing menu buttons and text
1690 g->active_xoffset = parameter[GFX_ARG_ACTIVE_XOFFSET];
1691 g->active_yoffset = parameter[GFX_ARG_ACTIVE_YOFFSET];
1692 g->pressed_xoffset = parameter[GFX_ARG_PRESSED_XOFFSET];
1693 g->pressed_yoffset = parameter[GFX_ARG_PRESSED_YOFFSET];
1695 // this is only used for drawing stacked global animations
1696 g->stacked_xfactor = parameter[GFX_ARG_STACKED_XFACTOR];
1697 g->stacked_yfactor = parameter[GFX_ARG_STACKED_YFACTOR];
1698 g->stacked_xoffset = parameter[GFX_ARG_STACKED_XOFFSET];
1699 g->stacked_yoffset = parameter[GFX_ARG_STACKED_YOFFSET];
1702 static void set_graphic_parameters(int graphic)
1704 struct FileInfo *image = getImageListEntryFromImageID(graphic);
1705 char **parameter_raw = image->parameter;
1706 Bitmap **src_bitmaps = getBitmapsFromImageID(graphic);
1707 int parameter[NUM_GFX_ARGS];
1710 // if fallback to default artwork is done, also use the default parameters
1711 if (image->fallback_to_default)
1712 parameter_raw = image->default_parameter;
1714 // get integer values from string parameters
1715 for (i = 0; i < NUM_GFX_ARGS; i++)
1716 parameter[i] = get_graphic_parameter_value(parameter_raw[i],
1717 image_config_suffix[i].token,
1718 image_config_suffix[i].type);
1720 set_graphic_parameters_ext(graphic, parameter, src_bitmaps);
1722 UPDATE_BUSY_STATE();
1725 static void set_cloned_graphic_parameters(int graphic)
1727 int fallback_graphic = IMG_CHAR_EXCLAM;
1728 int max_num_images = getImageListSize();
1729 int clone_graphic = graphic_info[graphic].clone_from;
1730 int num_references_followed = 1;
1732 while (graphic_info[clone_graphic].clone_from != -1 &&
1733 num_references_followed < max_num_images)
1735 clone_graphic = graphic_info[clone_graphic].clone_from;
1737 num_references_followed++;
1740 if (num_references_followed >= max_num_images)
1743 Warn("error found in config file:");
1744 Warn("- config file: '%s'", getImageConfigFilename());
1745 Warn("- config token: '%s'", getTokenFromImageID(graphic));
1746 Warn("error: loop discovered when resolving cloned graphics");
1747 Warn("custom graphic rejected for this element/action");
1749 if (graphic == fallback_graphic)
1750 Fail("no fallback graphic available");
1752 Warn("fallback done to 'char_exclam' for this graphic");
1755 graphic_info[graphic] = graphic_info[fallback_graphic];
1759 graphic_info[graphic] = graphic_info[clone_graphic];
1760 graphic_info[graphic].clone_from = clone_graphic;
1764 static void InitGraphicInfo(void)
1766 int fallback_graphic = IMG_CHAR_EXCLAM;
1767 int num_images = getImageListSize();
1770 // use image size as default values for width and height for these images
1771 static int full_size_graphics[] =
1774 IMG_GLOBAL_BORDER_MAIN,
1775 IMG_GLOBAL_BORDER_SCORES,
1776 IMG_GLOBAL_BORDER_EDITOR,
1777 IMG_GLOBAL_BORDER_PLAYING,
1780 IMG_BACKGROUND_ENVELOPE_1,
1781 IMG_BACKGROUND_ENVELOPE_2,
1782 IMG_BACKGROUND_ENVELOPE_3,
1783 IMG_BACKGROUND_ENVELOPE_4,
1784 IMG_BACKGROUND_REQUEST,
1787 IMG_BACKGROUND_LOADING_INITIAL,
1788 IMG_BACKGROUND_LOADING,
1789 IMG_BACKGROUND_TITLE_INITIAL,
1790 IMG_BACKGROUND_TITLE,
1791 IMG_BACKGROUND_MAIN,
1792 IMG_BACKGROUND_NAMES,
1793 IMG_BACKGROUND_LEVELS,
1794 IMG_BACKGROUND_LEVELNR,
1795 IMG_BACKGROUND_SCORES,
1796 IMG_BACKGROUND_SCOREINFO,
1797 IMG_BACKGROUND_EDITOR,
1798 IMG_BACKGROUND_INFO,
1799 IMG_BACKGROUND_INFO_ELEMENTS,
1800 IMG_BACKGROUND_INFO_MUSIC,
1801 IMG_BACKGROUND_INFO_CREDITS,
1802 IMG_BACKGROUND_INFO_PROGRAM,
1803 IMG_BACKGROUND_INFO_VERSION,
1804 IMG_BACKGROUND_INFO_LEVELSET,
1805 IMG_BACKGROUND_SETUP,
1806 IMG_BACKGROUND_PLAYING,
1807 IMG_BACKGROUND_DOOR,
1808 IMG_BACKGROUND_TAPE,
1809 IMG_BACKGROUND_PANEL,
1810 IMG_BACKGROUND_PALETTE,
1811 IMG_BACKGROUND_TOOLBOX,
1813 IMG_TITLESCREEN_INITIAL_1,
1814 IMG_TITLESCREEN_INITIAL_2,
1815 IMG_TITLESCREEN_INITIAL_3,
1816 IMG_TITLESCREEN_INITIAL_4,
1817 IMG_TITLESCREEN_INITIAL_5,
1824 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_1,
1825 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_2,
1826 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_3,
1827 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_4,
1828 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_5,
1829 IMG_BACKGROUND_TITLEMESSAGE_1,
1830 IMG_BACKGROUND_TITLEMESSAGE_2,
1831 IMG_BACKGROUND_TITLEMESSAGE_3,
1832 IMG_BACKGROUND_TITLEMESSAGE_4,
1833 IMG_BACKGROUND_TITLEMESSAGE_5,
1838 FreeGlobalAnimEventInfo();
1840 checked_free(graphic_info);
1842 graphic_info = checked_calloc(num_images * sizeof(struct GraphicInfo));
1844 // initialize "use_image_size" flag with default value
1845 for (i = 0; i < num_images; i++)
1846 graphic_info[i].use_image_size = FALSE;
1848 // initialize "use_image_size" flag from static configuration above
1849 for (i = 0; full_size_graphics[i] != -1; i++)
1850 graphic_info[full_size_graphics[i]].use_image_size = TRUE;
1852 // first set all graphic paramaters ...
1853 for (i = 0; i < num_images; i++)
1854 set_graphic_parameters(i);
1856 // ... then copy these parameters for cloned graphics
1857 for (i = 0; i < num_images; i++)
1858 if (graphic_info[i].clone_from != -1)
1859 set_cloned_graphic_parameters(i);
1861 for (i = 0; i < num_images; i++)
1863 Bitmap *src_bitmap = graphic_info[i].bitmap;
1867 int src_bitmap_width, src_bitmap_height;
1869 // now check if no animation frames are outside of the loaded image
1871 if (graphic_info[i].bitmap == NULL)
1872 continue; // skip check for optional images that are undefined
1874 // get image size (this can differ from the standard element tile size!)
1875 width = graphic_info[i].width;
1876 height = graphic_info[i].height;
1878 // get final bitmap size (with scaling, but without small images)
1879 src_bitmap_width = graphic_info[i].src_image_width;
1880 src_bitmap_height = graphic_info[i].src_image_height;
1882 // check if first animation frame is inside specified bitmap
1884 // do not use getGraphicSourceXY() here to get position of first frame;
1885 // this avoids calculating wrong start position for out-of-bounds frame
1886 src_x = graphic_info[i].src_x;
1887 src_y = graphic_info[i].src_y;
1889 if (program.headless)
1892 if (src_x < 0 || src_y < 0 ||
1893 src_x + width > src_bitmap_width ||
1894 src_y + height > src_bitmap_height)
1897 Warn("error found in config file:");
1898 Warn("- config file: '%s'", getImageConfigFilename());
1899 Warn("- config token: '%s'", getTokenFromImageID(i));
1900 Warn("- image file: '%s'", src_bitmap->source_filename);
1901 Warn("- frame size: %d, %d", width, height);
1902 Warn("error: first animation frame out of bounds (%d, %d) [%d, %d]",
1903 src_x, src_y, src_bitmap_width, src_bitmap_height);
1904 Warn("custom graphic rejected for this element/action");
1906 if (i == fallback_graphic)
1907 Fail("no fallback graphic available");
1909 Warn("fallback done to 'char_exclam' for this graphic");
1912 graphic_info[i] = graphic_info[fallback_graphic];
1914 // if first frame out of bounds, do not check last frame anymore
1918 // check if last animation frame is inside specified bitmap
1920 last_frame = graphic_info[i].anim_frames - 1;
1921 getGraphicSourceXY(i, last_frame, &src_x, &src_y, FALSE);
1923 if (src_x < 0 || src_y < 0 ||
1924 src_x + width > src_bitmap_width ||
1925 src_y + height > src_bitmap_height)
1928 Warn("error found in config file:");
1929 Warn("- config file: '%s'", getImageConfigFilename());
1930 Warn("- config token: '%s'", getTokenFromImageID(i));
1931 Warn("- image file: '%s'", src_bitmap->source_filename);
1932 Warn("- frame size: %d, %d", width, height);
1933 Warn("error: last animation frame (%d) out of bounds (%d, %d) [%d, %d]",
1934 last_frame, src_x, src_y, src_bitmap_width, src_bitmap_height);
1935 Warn("custom graphic rejected for this element/action");
1937 if (i == fallback_graphic)
1938 Fail("no fallback graphic available");
1940 Warn("fallback done to 'char_exclam' for this graphic");
1943 graphic_info[i] = graphic_info[fallback_graphic];
1948 static void InitGraphicCompatibilityInfo(void)
1950 struct FileInfo *fi_global_door =
1951 getImageListEntryFromImageID(IMG_GLOBAL_DOOR);
1952 int num_images = getImageListSize();
1955 /* the following compatibility handling is needed for the following case:
1956 versions up to 3.3.0.0 used one large bitmap "global.door" for various
1957 graphics mainly used for door and panel graphics, like editor, tape and
1958 in-game buttons with hard-coded bitmap positions and button sizes; as
1959 these graphics now have individual definitions, redefining "global.door"
1960 to change all these graphics at once like before does not work anymore
1961 (because all those individual definitions still have their default values);
1962 to solve this, remap all those individual definitions that are not
1963 redefined to the new bitmap of "global.door" if it was redefined */
1965 // special compatibility handling if image "global.door" was redefined
1966 if (fi_global_door->redefined)
1968 for (i = 0; i < num_images; i++)
1970 struct FileInfo *fi = getImageListEntryFromImageID(i);
1972 // process only those images that still use the default settings
1975 // process all images which default to same image as "global.door"
1976 if (strEqual(fi->default_filename, fi_global_door->default_filename))
1978 // skip all images that are cloned from images that default to same
1979 // image as "global.door", but that are redefined to something else
1980 if (graphic_info[i].clone_from != -1)
1982 int cloned_graphic = graphic_info[i].clone_from;
1984 if (getImageListEntryFromImageID(cloned_graphic)->redefined)
1989 Debug("init:InitGraphicCompatibilityInfo",
1990 "special treatment needed for token '%s'", fi->token);
1993 graphic_info[i].bitmaps = graphic_info[IMG_GLOBAL_DOOR].bitmaps;
1994 graphic_info[i].bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2000 // special compatibility handling for "Snake Bite" graphics set
2001 if (strPrefix(leveldir_current->identifier, "snake_bite"))
2003 Bitmap *bitmap = graphic_info[IMG_BACKGROUND_SCORES].bitmap;
2005 BlitBitmap(bitmap, bitmap, 18, 66, 32, 480, 50, 66);
2006 BlitBitmap(bitmap, bitmap, 466, 66, 32, 480, 434, 66);
2008 ClearRectangle(bitmap, 2, 66, 32, 480);
2009 ClearRectangle(bitmap, 514, 66, 32, 480);
2012 // special compatibility handling for "Jue" graphics sets (2007 and 2019)
2013 boolean supports_score_info = (menu.draw_xoffset[GAME_MODE_SCOREINFO] != 0);
2014 if (strPrefix(artwork.gfx_current_identifier, "jue") && !supports_score_info)
2032 int mode_old = GAME_MODE_SCORES;
2033 int mode_new = GAME_MODE_SCOREINFO;
2036 // adjust title screens on score info page
2037 for (i = 0; font_title[i] != -1; i++)
2039 struct FontInfo *fi = &font_info[font_title[i]];
2041 fi->special_graphic[mode_new] = fi->special_graphic[mode_old];
2042 fi->special_bitmap_id[mode_new] = fi->special_bitmap_id[mode_old];
2045 // adjust vertical text and button positions on scores page
2046 for (i = 0; font_text[i] != -1; i++)
2048 for (j = 0; j < 2; j++)
2050 boolean jue0 = strEqual(artwork.gfx_current_identifier, "jue0");
2051 int font_nr = (j == 0 ? font_text[i] : FONT_ACTIVE(font_text[i]));
2052 int font_bitmap_id = font_info[font_nr].special_bitmap_id[mode_old];
2053 int font_yoffset = (jue0 ? 10 : 5);
2055 gfx.font_bitmap_info[font_bitmap_id].draw_yoffset = font_yoffset;
2059 // adjust page offsets on score info page
2060 menu.draw_xoffset[mode_new] = menu.draw_xoffset[mode_old];
2061 menu.draw_yoffset[mode_new] = menu.draw_yoffset[mode_old];
2064 InitGraphicCompatibilityInfo_Doors();
2067 static void InitElementSoundInfo(void)
2069 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
2070 int num_property_mappings = getSoundListPropertyMappingSize();
2073 // set values to -1 to identify later as "uninitialized" values
2074 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2075 for (act = 0; act < NUM_ACTIONS; act++)
2076 element_info[i].sound[act] = -1;
2078 // initialize element/sound mapping from static configuration
2079 for (i = 0; element_to_sound[i].element > -1; i++)
2081 int element = element_to_sound[i].element;
2082 int action = element_to_sound[i].action;
2083 int sound = element_to_sound[i].sound;
2084 boolean is_class = element_to_sound[i].is_class;
2087 action = ACTION_DEFAULT;
2090 element_info[element].sound[action] = sound;
2092 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2093 if (strEqual(element_info[j].class_name,
2094 element_info[element].class_name))
2095 element_info[j].sound[action] = sound;
2098 // initialize element class/sound mapping from dynamic configuration
2099 for (i = 0; i < num_property_mappings; i++)
2101 int element_class = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
2102 int action = property_mapping[i].ext1_index;
2103 int sound = property_mapping[i].artwork_index;
2105 if (element_class < 0 || element_class >= MAX_NUM_ELEMENTS)
2109 action = ACTION_DEFAULT;
2111 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2112 if (strEqual(element_info[j].class_name,
2113 element_info[element_class].class_name))
2114 element_info[j].sound[action] = sound;
2117 // initialize element/sound mapping from dynamic configuration
2118 for (i = 0; i < num_property_mappings; i++)
2120 int element = property_mapping[i].base_index;
2121 int action = property_mapping[i].ext1_index;
2122 int sound = property_mapping[i].artwork_index;
2124 if (element >= MAX_NUM_ELEMENTS)
2128 action = ACTION_DEFAULT;
2130 element_info[element].sound[action] = sound;
2133 // now set all '-1' values to element specific default values
2134 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2136 for (act = 0; act < NUM_ACTIONS; act++)
2138 // generic default action sound (defined by "[default]" directive)
2139 int default_action_sound = element_info[EL_DEFAULT].sound[act];
2141 // look for special default action sound (classic game specific)
2142 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].sound[act] != -1)
2143 default_action_sound = element_info[EL_BD_DEFAULT].sound[act];
2144 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].sound[act] != -1)
2145 default_action_sound = element_info[EL_SP_DEFAULT].sound[act];
2146 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].sound[act] != -1)
2147 default_action_sound = element_info[EL_SB_DEFAULT].sound[act];
2148 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].sound[act] != -1)
2149 default_action_sound = element_info[EL_MM_DEFAULT].sound[act];
2151 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
2152 // !!! make this better !!!
2153 if (i == EL_EMPTY_SPACE)
2154 default_action_sound = element_info[EL_DEFAULT].sound[act];
2156 // no sound for this specific action -- use default action sound
2157 if (element_info[i].sound[act] == -1)
2158 element_info[i].sound[act] = default_action_sound;
2162 // copy sound settings to some elements that are only stored in level file
2163 // in native R'n'D levels, but are used by game engine in native EM levels
2164 for (i = 0; copy_properties[i][0] != -1; i++)
2165 for (j = 1; j <= 4; j++)
2166 for (act = 0; act < NUM_ACTIONS; act++)
2167 element_info[copy_properties[i][j]].sound[act] =
2168 element_info[copy_properties[i][0]].sound[act];
2171 static void InitGameModeSoundInfo(void)
2175 // set values to -1 to identify later as "uninitialized" values
2176 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2179 // initialize gamemode/sound mapping from static configuration
2180 for (i = 0; gamemode_to_sound[i].sound > -1; i++)
2182 int gamemode = gamemode_to_sound[i].gamemode;
2183 int sound = gamemode_to_sound[i].sound;
2186 gamemode = GAME_MODE_DEFAULT;
2188 menu.sound[gamemode] = sound;
2191 // now set all '-1' values to levelset specific default values
2192 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2193 if (menu.sound[i] == -1)
2194 menu.sound[i] = menu.sound[GAME_MODE_DEFAULT];
2197 static void set_sound_parameters(int sound, char **parameter_raw)
2199 int parameter[NUM_SND_ARGS];
2202 // get integer values from string parameters
2203 for (i = 0; i < NUM_SND_ARGS; i++)
2205 get_parameter_value(parameter_raw[i],
2206 sound_config_suffix[i].token,
2207 sound_config_suffix[i].type);
2209 // explicit loop mode setting in configuration overrides default value
2210 if (parameter[SND_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2211 sound_info[sound].loop = parameter[SND_ARG_MODE_LOOP];
2213 // sound volume to change the original volume when loading the sound file
2214 sound_info[sound].volume = parameter[SND_ARG_VOLUME];
2216 // sound priority to give certain sounds a higher or lower priority
2217 sound_info[sound].priority = parameter[SND_ARG_PRIORITY];
2220 static void InitSoundInfo(void)
2222 int *sound_effect_properties;
2223 int num_sounds = getSoundListSize();
2226 checked_free(sound_info);
2228 sound_effect_properties = checked_calloc(num_sounds * sizeof(int));
2229 sound_info = checked_calloc(num_sounds * sizeof(struct SoundInfo));
2231 // initialize sound effect for all elements to "no sound"
2232 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2233 for (j = 0; j < NUM_ACTIONS; j++)
2234 element_info[i].sound[j] = SND_UNDEFINED;
2236 for (i = 0; i < num_sounds; i++)
2238 struct FileInfo *sound = getSoundListEntry(i);
2239 int len_effect_text = strlen(sound->token);
2241 sound_effect_properties[i] = ACTION_OTHER;
2242 sound_info[i].loop = FALSE; // default: play sound only once
2244 // determine all loop sounds and identify certain sound classes
2246 for (j = 0; element_action_info[j].suffix; j++)
2248 int len_action_text = strlen(element_action_info[j].suffix);
2250 if (len_action_text < len_effect_text &&
2251 strEqual(&sound->token[len_effect_text - len_action_text],
2252 element_action_info[j].suffix))
2254 sound_effect_properties[i] = element_action_info[j].value;
2255 sound_info[i].loop = element_action_info[j].is_loop_sound;
2261 // associate elements and some selected sound actions
2263 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2265 if (element_info[j].class_name)
2267 int len_class_text = strlen(element_info[j].class_name);
2269 if (len_class_text + 1 < len_effect_text &&
2270 strncmp(sound->token,
2271 element_info[j].class_name, len_class_text) == 0 &&
2272 sound->token[len_class_text] == '.')
2274 int sound_action_value = sound_effect_properties[i];
2276 element_info[j].sound[sound_action_value] = i;
2281 set_sound_parameters(i, sound->parameter);
2284 Debug("init:InitSoundInfo", "loop mode: %d ['%s']",
2285 sound_info[i].loop, sound->token);
2289 free(sound_effect_properties);
2292 static void InitGameModeMusicInfo(void)
2294 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
2295 int num_property_mappings = getMusicListPropertyMappingSize();
2296 int default_levelset_music = -1;
2299 // set values to -1 to identify later as "uninitialized" values
2300 for (i = 0; i < MAX_LEVELS; i++)
2301 levelset.music[i] = -1;
2302 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2305 // initialize gamemode/music mapping from static configuration
2306 for (i = 0; gamemode_to_music[i].music > -1; i++)
2308 int gamemode = gamemode_to_music[i].gamemode;
2309 int music = gamemode_to_music[i].music;
2312 gamemode = GAME_MODE_DEFAULT;
2314 menu.music[gamemode] = music;
2317 // initialize gamemode/music mapping from dynamic configuration
2318 for (i = 0; i < num_property_mappings; i++)
2320 int prefix = property_mapping[i].base_index;
2321 int gamemode = property_mapping[i].ext2_index;
2322 int level = property_mapping[i].ext3_index;
2323 int music = property_mapping[i].artwork_index;
2325 if (prefix < 0 || prefix >= NUM_MUSIC_PREFIXES)
2329 gamemode = GAME_MODE_DEFAULT;
2331 // level specific music only allowed for in-game music
2332 if (level != -1 && gamemode == GAME_MODE_DEFAULT)
2333 gamemode = GAME_MODE_PLAYING;
2338 default_levelset_music = music;
2341 if (gamemode == GAME_MODE_PLAYING || gamemode == GAME_MODE_DEFAULT)
2342 levelset.music[level] = music;
2343 if (gamemode != GAME_MODE_PLAYING)
2344 menu.music[gamemode] = music;
2347 // now set all '-1' values to menu specific default values
2348 // (undefined values of "levelset.music[]" might stay at "-1" to
2349 // allow dynamic selection of music files from music directory!)
2350 for (i = 0; i < MAX_LEVELS; i++)
2351 if (levelset.music[i] == -1)
2352 levelset.music[i] = default_levelset_music;
2353 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2354 if (menu.music[i] == -1)
2355 menu.music[i] = menu.music[GAME_MODE_DEFAULT];
2358 static void set_music_parameters(int music, char **parameter_raw)
2360 int parameter[NUM_MUS_ARGS];
2363 // get integer values from string parameters
2364 for (i = 0; i < NUM_MUS_ARGS; i++)
2366 get_parameter_value(parameter_raw[i],
2367 music_config_suffix[i].token,
2368 music_config_suffix[i].type);
2370 // explicit loop mode setting in configuration overrides default value
2371 if (parameter[MUS_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2372 music_info[music].loop = parameter[MUS_ARG_MODE_LOOP];
2375 static void InitMusicInfo(void)
2377 int num_music = getMusicListSize();
2380 checked_free(music_info);
2382 music_info = checked_calloc(num_music * sizeof(struct MusicInfo));
2384 for (i = 0; i < num_music; i++)
2386 struct FileInfo *music = getMusicListEntry(i);
2387 int len_music_text = strlen(music->token);
2389 music_info[i].loop = TRUE; // default: play music in loop mode
2391 // determine all loop music
2393 for (j = 0; music_prefix_info[j].prefix; j++)
2395 int len_prefix_text = strlen(music_prefix_info[j].prefix);
2397 if (len_prefix_text < len_music_text &&
2398 strncmp(music->token,
2399 music_prefix_info[j].prefix, len_prefix_text) == 0)
2401 music_info[i].loop = music_prefix_info[j].is_loop_music;
2407 set_music_parameters(i, music->parameter);
2412 static void InitGameInfoFromArtworkInfo(void)
2414 // special case: store initial value of custom artwork setting
2415 game.use_masked_elements_initial = game.use_masked_elements;
2418 static void ReinitializeGraphics(void)
2420 print_timestamp_init("ReinitializeGraphics");
2422 InitGfxTileSizeInfo(game.tile_size, TILESIZE);
2424 InitGraphicInfo(); // graphic properties mapping
2425 print_timestamp_time("InitGraphicInfo");
2426 InitElementGraphicInfo(); // element game graphic mapping
2427 print_timestamp_time("InitElementGraphicInfo");
2428 InitElementSpecialGraphicInfo(); // element special graphic mapping
2429 print_timestamp_time("InitElementSpecialGraphicInfo");
2431 InitElementSmallImages(); // scale elements to all needed sizes
2432 print_timestamp_time("InitElementSmallImages");
2433 InitScaledImages(); // scale all other images, if needed
2434 print_timestamp_time("InitScaledImages");
2435 InitBitmapPointers(); // set standard size bitmap pointers
2436 print_timestamp_time("InitBitmapPointers");
2437 InitFontGraphicInfo(); // initialize text drawing functions
2438 print_timestamp_time("InitFontGraphicInfo");
2439 InitGlobalAnimGraphicInfo(); // initialize global animation config
2440 print_timestamp_time("InitGlobalAnimGraphicInfo");
2442 InitImageTextures(); // create textures for certain images
2443 print_timestamp_time("InitImageTextures");
2445 InitGraphicInfo_EM(); // graphic mapping for EM engine
2446 print_timestamp_time("InitGraphicInfo_EM");
2448 InitGraphicCompatibilityInfo();
2449 print_timestamp_time("InitGraphicCompatibilityInfo");
2452 print_timestamp_time("InitGadgets");
2454 print_timestamp_time("InitDoors");
2456 InitGameInfoFromArtworkInfo();
2458 print_timestamp_done("ReinitializeGraphics");
2461 static void ReinitializeSounds(void)
2463 InitSoundInfo(); // sound properties mapping
2464 InitElementSoundInfo(); // element game sound mapping
2465 InitGameModeSoundInfo(); // game mode sound mapping
2466 InitGlobalAnimSoundInfo(); // global animation sound settings
2468 InitPlayLevelSound(); // internal game sound settings
2471 static void ReinitializeMusic(void)
2473 InitMusicInfo(); // music properties mapping
2474 InitGameModeMusicInfo(); // game mode music mapping
2475 InitGlobalAnimMusicInfo(); // global animation music settings
2478 static int get_special_property_bit(int element, int property_bit_nr)
2480 struct PropertyBitInfo
2486 static struct PropertyBitInfo pb_can_move_into_acid[] =
2488 // the player may be able fall into acid when gravity is activated
2493 { EL_SP_MURPHY, 0 },
2494 { EL_SOKOBAN_FIELD_PLAYER, 0 },
2496 // all elements that can move may be able to also move into acid
2499 { EL_BUG_RIGHT, 1 },
2502 { EL_SPACESHIP, 2 },
2503 { EL_SPACESHIP_LEFT, 2 },
2504 { EL_SPACESHIP_RIGHT, 2 },
2505 { EL_SPACESHIP_UP, 2 },
2506 { EL_SPACESHIP_DOWN, 2 },
2507 { EL_BD_BUTTERFLY, 3 },
2508 { EL_BD_BUTTERFLY_LEFT, 3 },
2509 { EL_BD_BUTTERFLY_RIGHT, 3 },
2510 { EL_BD_BUTTERFLY_UP, 3 },
2511 { EL_BD_BUTTERFLY_DOWN, 3 },
2512 { EL_BD_FIREFLY, 4 },
2513 { EL_BD_FIREFLY_LEFT, 4 },
2514 { EL_BD_FIREFLY_RIGHT, 4 },
2515 { EL_BD_FIREFLY_UP, 4 },
2516 { EL_BD_FIREFLY_DOWN, 4 },
2518 { EL_YAMYAM_LEFT, 5 },
2519 { EL_YAMYAM_RIGHT, 5 },
2520 { EL_YAMYAM_UP, 5 },
2521 { EL_YAMYAM_DOWN, 5 },
2522 { EL_DARK_YAMYAM, 6 },
2525 { EL_PACMAN_LEFT, 8 },
2526 { EL_PACMAN_RIGHT, 8 },
2527 { EL_PACMAN_UP, 8 },
2528 { EL_PACMAN_DOWN, 8 },
2530 { EL_MOLE_LEFT, 9 },
2531 { EL_MOLE_RIGHT, 9 },
2533 { EL_MOLE_DOWN, 9 },
2537 { EL_SATELLITE, 13 },
2538 { EL_SP_SNIKSNAK, 14 },
2539 { EL_SP_ELECTRON, 15 },
2542 { EL_SPRING_LEFT, 17 },
2543 { EL_SPRING_RIGHT, 17 },
2544 { EL_EMC_ANDROID, 18 },
2549 static struct PropertyBitInfo pb_dont_collide_with[] =
2551 { EL_SP_SNIKSNAK, 0 },
2552 { EL_SP_ELECTRON, 1 },
2560 struct PropertyBitInfo *pb_info;
2563 { EP_CAN_MOVE_INTO_ACID, pb_can_move_into_acid },
2564 { EP_DONT_COLLIDE_WITH, pb_dont_collide_with },
2569 struct PropertyBitInfo *pb_info = NULL;
2572 for (i = 0; pb_definition[i].bit_nr != -1; i++)
2573 if (pb_definition[i].bit_nr == property_bit_nr)
2574 pb_info = pb_definition[i].pb_info;
2576 if (pb_info == NULL)
2579 for (i = 0; pb_info[i].element != -1; i++)
2580 if (pb_info[i].element == element)
2581 return pb_info[i].bit_nr;
2586 void setBitfieldProperty(int *bitfield, int property_bit_nr, int element,
2587 boolean property_value)
2589 int bit_nr = get_special_property_bit(element, property_bit_nr);
2594 *bitfield |= (1 << bit_nr);
2596 *bitfield &= ~(1 << bit_nr);
2600 boolean getBitfieldProperty(int *bitfield, int property_bit_nr, int element)
2602 int bit_nr = get_special_property_bit(element, property_bit_nr);
2605 return ((*bitfield & (1 << bit_nr)) != 0);
2610 static void ResolveGroupElementExt(int group_element, int recursion_depth)
2612 static int group_nr;
2613 static struct ElementGroupInfo *group;
2614 struct ElementGroupInfo *actual_group = element_info[group_element].group;
2617 if (actual_group == NULL) // not yet initialized
2620 if (recursion_depth > NUM_GROUP_ELEMENTS) // recursion too deep
2622 Warn("recursion too deep when resolving group element %d",
2623 group_element - EL_GROUP_START + 1);
2625 // replace element which caused too deep recursion by question mark
2626 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
2631 if (recursion_depth == 0) // initialization
2633 group = actual_group;
2634 group_nr = GROUP_NR(group_element);
2636 group->num_elements_resolved = 0;
2637 group->choice_pos = 0;
2639 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2640 element_info[i].in_group[group_nr] = FALSE;
2643 for (i = 0; i < actual_group->num_elements; i++)
2645 int element = actual_group->element[i];
2647 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
2650 if (IS_GROUP_ELEMENT(element))
2651 ResolveGroupElementExt(element, recursion_depth + 1);
2654 group->element_resolved[group->num_elements_resolved++] = element;
2655 element_info[element].in_group[group_nr] = TRUE;
2660 void ResolveGroupElement(int group_element)
2662 ResolveGroupElementExt(group_element, 0);
2665 void InitElementPropertiesStatic(void)
2667 static boolean clipboard_elements_initialized = FALSE;
2669 static int ep_diggable[] =
2674 EL_SP_BUGGY_BASE_ACTIVATING,
2677 EL_INVISIBLE_SAND_ACTIVE,
2680 // !!! currently not diggable, but handled by 'ep_dont_run_into' !!!
2681 // (if amoeba can grow into anything diggable, maybe keep these out)
2686 EL_SP_BUGGY_BASE_ACTIVE,
2693 static int ep_collectible_only[] =
2715 EL_DYNABOMB_INCREASE_NUMBER,
2716 EL_DYNABOMB_INCREASE_SIZE,
2717 EL_DYNABOMB_INCREASE_POWER,
2735 // !!! handle separately !!!
2736 EL_DC_LANDMINE, // deadly when running into, but can be snapped
2742 static int ep_dont_run_into[] =
2744 // same elements as in 'ep_dont_touch'
2750 // same elements as in 'ep_dont_collide_with'
2762 // !!! maybe this should better be handled by 'ep_diggable' !!!
2767 EL_SP_BUGGY_BASE_ACTIVE,
2774 static int ep_dont_collide_with[] =
2776 // same elements as in 'ep_dont_touch'
2793 static int ep_dont_touch[] =
2803 static int ep_indestructible[] =
2807 EL_ACID_POOL_TOPLEFT,
2808 EL_ACID_POOL_TOPRIGHT,
2809 EL_ACID_POOL_BOTTOMLEFT,
2810 EL_ACID_POOL_BOTTOM,
2811 EL_ACID_POOL_BOTTOMRIGHT,
2812 EL_SP_HARDWARE_GRAY,
2813 EL_SP_HARDWARE_GREEN,
2814 EL_SP_HARDWARE_BLUE,
2816 EL_SP_HARDWARE_YELLOW,
2817 EL_SP_HARDWARE_BASE_1,
2818 EL_SP_HARDWARE_BASE_2,
2819 EL_SP_HARDWARE_BASE_3,
2820 EL_SP_HARDWARE_BASE_4,
2821 EL_SP_HARDWARE_BASE_5,
2822 EL_SP_HARDWARE_BASE_6,
2823 EL_INVISIBLE_STEELWALL,
2824 EL_INVISIBLE_STEELWALL_ACTIVE,
2825 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2826 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
2827 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
2828 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2829 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
2830 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
2831 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2832 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
2833 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
2834 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
2835 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
2836 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
2838 EL_LIGHT_SWITCH_ACTIVE,
2839 EL_SIGN_EXCLAMATION,
2840 EL_SIGN_RADIOACTIVITY,
2847 EL_SIGN_ENTRY_FORBIDDEN,
2848 EL_SIGN_EMERGENCY_EXIT,
2856 EL_STEEL_EXIT_CLOSED,
2858 EL_STEEL_EXIT_OPENING,
2859 EL_STEEL_EXIT_CLOSING,
2860 EL_EM_STEEL_EXIT_CLOSED,
2861 EL_EM_STEEL_EXIT_OPEN,
2862 EL_EM_STEEL_EXIT_OPENING,
2863 EL_EM_STEEL_EXIT_CLOSING,
2864 EL_DC_STEELWALL_1_LEFT,
2865 EL_DC_STEELWALL_1_RIGHT,
2866 EL_DC_STEELWALL_1_TOP,
2867 EL_DC_STEELWALL_1_BOTTOM,
2868 EL_DC_STEELWALL_1_HORIZONTAL,
2869 EL_DC_STEELWALL_1_VERTICAL,
2870 EL_DC_STEELWALL_1_TOPLEFT,
2871 EL_DC_STEELWALL_1_TOPRIGHT,
2872 EL_DC_STEELWALL_1_BOTTOMLEFT,
2873 EL_DC_STEELWALL_1_BOTTOMRIGHT,
2874 EL_DC_STEELWALL_1_TOPLEFT_2,
2875 EL_DC_STEELWALL_1_TOPRIGHT_2,
2876 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
2877 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
2878 EL_DC_STEELWALL_2_LEFT,
2879 EL_DC_STEELWALL_2_RIGHT,
2880 EL_DC_STEELWALL_2_TOP,
2881 EL_DC_STEELWALL_2_BOTTOM,
2882 EL_DC_STEELWALL_2_HORIZONTAL,
2883 EL_DC_STEELWALL_2_VERTICAL,
2884 EL_DC_STEELWALL_2_MIDDLE,
2885 EL_DC_STEELWALL_2_SINGLE,
2886 EL_STEELWALL_SLIPPERY,
2900 EL_GATE_1_GRAY_ACTIVE,
2901 EL_GATE_2_GRAY_ACTIVE,
2902 EL_GATE_3_GRAY_ACTIVE,
2903 EL_GATE_4_GRAY_ACTIVE,
2912 EL_EM_GATE_1_GRAY_ACTIVE,
2913 EL_EM_GATE_2_GRAY_ACTIVE,
2914 EL_EM_GATE_3_GRAY_ACTIVE,
2915 EL_EM_GATE_4_GRAY_ACTIVE,
2924 EL_EMC_GATE_5_GRAY_ACTIVE,
2925 EL_EMC_GATE_6_GRAY_ACTIVE,
2926 EL_EMC_GATE_7_GRAY_ACTIVE,
2927 EL_EMC_GATE_8_GRAY_ACTIVE,
2929 EL_DC_GATE_WHITE_GRAY,
2930 EL_DC_GATE_WHITE_GRAY_ACTIVE,
2931 EL_DC_GATE_FAKE_GRAY,
2933 EL_SWITCHGATE_OPENING,
2934 EL_SWITCHGATE_CLOSED,
2935 EL_SWITCHGATE_CLOSING,
2936 EL_DC_SWITCHGATE_SWITCH_UP,
2937 EL_DC_SWITCHGATE_SWITCH_DOWN,
2939 EL_TIMEGATE_OPENING,
2941 EL_TIMEGATE_CLOSING,
2942 EL_DC_TIMEGATE_SWITCH,
2943 EL_DC_TIMEGATE_SWITCH_ACTIVE,
2947 EL_TUBE_VERTICAL_LEFT,
2948 EL_TUBE_VERTICAL_RIGHT,
2949 EL_TUBE_HORIZONTAL_UP,
2950 EL_TUBE_HORIZONTAL_DOWN,
2955 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
2956 EL_EXPANDABLE_STEELWALL_VERTICAL,
2957 EL_EXPANDABLE_STEELWALL_ANY,
2962 static int ep_slippery[] =
2976 EL_ROBOT_WHEEL_ACTIVE,
2982 EL_ACID_POOL_TOPLEFT,
2983 EL_ACID_POOL_TOPRIGHT,
2993 EL_STEELWALL_SLIPPERY,
2996 EL_EMC_WALL_SLIPPERY_1,
2997 EL_EMC_WALL_SLIPPERY_2,
2998 EL_EMC_WALL_SLIPPERY_3,
2999 EL_EMC_WALL_SLIPPERY_4,
3001 EL_EMC_MAGIC_BALL_ACTIVE,
3006 static int ep_can_change[] =
3011 static int ep_can_move[] =
3013 // same elements as in 'pb_can_move_into_acid'
3036 static int ep_can_fall[] =
3051 EL_QUICKSAND_FAST_FULL,
3053 EL_BD_MAGIC_WALL_FULL,
3054 EL_DC_MAGIC_WALL_FULL,
3068 static int ep_can_smash_player[] =
3094 static int ep_can_smash_enemies[] =
3103 static int ep_can_smash_everything[] =
3112 static int ep_explodes_by_fire[] =
3114 // same elements as in 'ep_explodes_impact'
3119 // same elements as in 'ep_explodes_smashed'
3129 EL_EM_DYNAMITE_ACTIVE,
3130 EL_DYNABOMB_PLAYER_1_ACTIVE,
3131 EL_DYNABOMB_PLAYER_2_ACTIVE,
3132 EL_DYNABOMB_PLAYER_3_ACTIVE,
3133 EL_DYNABOMB_PLAYER_4_ACTIVE,
3134 EL_DYNABOMB_INCREASE_NUMBER,
3135 EL_DYNABOMB_INCREASE_SIZE,
3136 EL_DYNABOMB_INCREASE_POWER,
3137 EL_SP_DISK_RED_ACTIVE,
3151 static int ep_explodes_smashed[] =
3153 // same elements as in 'ep_explodes_impact'
3167 static int ep_explodes_impact[] =
3176 static int ep_walkable_over[] =
3196 EL_SOKOBAN_FIELD_EMPTY,
3203 EL_EM_STEEL_EXIT_OPEN,
3204 EL_EM_STEEL_EXIT_OPENING,
3213 EL_GATE_1_GRAY_ACTIVE,
3214 EL_GATE_2_GRAY_ACTIVE,
3215 EL_GATE_3_GRAY_ACTIVE,
3216 EL_GATE_4_GRAY_ACTIVE,
3224 static int ep_walkable_inside[] =
3229 EL_TUBE_VERTICAL_LEFT,
3230 EL_TUBE_VERTICAL_RIGHT,
3231 EL_TUBE_HORIZONTAL_UP,
3232 EL_TUBE_HORIZONTAL_DOWN,
3241 static int ep_walkable_under[] =
3246 static int ep_passable_over[] =
3256 EL_EM_GATE_1_GRAY_ACTIVE,
3257 EL_EM_GATE_2_GRAY_ACTIVE,
3258 EL_EM_GATE_3_GRAY_ACTIVE,
3259 EL_EM_GATE_4_GRAY_ACTIVE,
3268 EL_EMC_GATE_5_GRAY_ACTIVE,
3269 EL_EMC_GATE_6_GRAY_ACTIVE,
3270 EL_EMC_GATE_7_GRAY_ACTIVE,
3271 EL_EMC_GATE_8_GRAY_ACTIVE,
3273 EL_DC_GATE_WHITE_GRAY,
3274 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3281 static int ep_passable_inside[] =
3287 EL_SP_PORT_HORIZONTAL,
3288 EL_SP_PORT_VERTICAL,
3290 EL_SP_GRAVITY_PORT_LEFT,
3291 EL_SP_GRAVITY_PORT_RIGHT,
3292 EL_SP_GRAVITY_PORT_UP,
3293 EL_SP_GRAVITY_PORT_DOWN,
3294 EL_SP_GRAVITY_ON_PORT_LEFT,
3295 EL_SP_GRAVITY_ON_PORT_RIGHT,
3296 EL_SP_GRAVITY_ON_PORT_UP,
3297 EL_SP_GRAVITY_ON_PORT_DOWN,
3298 EL_SP_GRAVITY_OFF_PORT_LEFT,
3299 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3300 EL_SP_GRAVITY_OFF_PORT_UP,
3301 EL_SP_GRAVITY_OFF_PORT_DOWN,
3306 static int ep_passable_under[] =
3311 static int ep_droppable[] =
3316 static int ep_explodes_1x1_old[] =
3321 static int ep_pushable[] =
3333 EL_SOKOBAN_FIELD_FULL,
3342 static int ep_explodes_cross_old[] =
3347 static int ep_protected[] =
3349 // same elements as in 'ep_walkable_inside'
3353 EL_TUBE_VERTICAL_LEFT,
3354 EL_TUBE_VERTICAL_RIGHT,
3355 EL_TUBE_HORIZONTAL_UP,
3356 EL_TUBE_HORIZONTAL_DOWN,
3362 // same elements as in 'ep_passable_over'
3371 EL_EM_GATE_1_GRAY_ACTIVE,
3372 EL_EM_GATE_2_GRAY_ACTIVE,
3373 EL_EM_GATE_3_GRAY_ACTIVE,
3374 EL_EM_GATE_4_GRAY_ACTIVE,
3383 EL_EMC_GATE_5_GRAY_ACTIVE,
3384 EL_EMC_GATE_6_GRAY_ACTIVE,
3385 EL_EMC_GATE_7_GRAY_ACTIVE,
3386 EL_EMC_GATE_8_GRAY_ACTIVE,
3388 EL_DC_GATE_WHITE_GRAY,
3389 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3393 // same elements as in 'ep_passable_inside'
3398 EL_SP_PORT_HORIZONTAL,
3399 EL_SP_PORT_VERTICAL,
3401 EL_SP_GRAVITY_PORT_LEFT,
3402 EL_SP_GRAVITY_PORT_RIGHT,
3403 EL_SP_GRAVITY_PORT_UP,
3404 EL_SP_GRAVITY_PORT_DOWN,
3405 EL_SP_GRAVITY_ON_PORT_LEFT,
3406 EL_SP_GRAVITY_ON_PORT_RIGHT,
3407 EL_SP_GRAVITY_ON_PORT_UP,
3408 EL_SP_GRAVITY_ON_PORT_DOWN,
3409 EL_SP_GRAVITY_OFF_PORT_LEFT,
3410 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3411 EL_SP_GRAVITY_OFF_PORT_UP,
3412 EL_SP_GRAVITY_OFF_PORT_DOWN,
3417 static int ep_throwable[] =
3422 static int ep_can_explode[] =
3424 // same elements as in 'ep_explodes_impact'
3429 // same elements as in 'ep_explodes_smashed'
3435 // elements that can explode by explosion or by dragonfire
3439 EL_EM_DYNAMITE_ACTIVE,
3440 EL_DYNABOMB_PLAYER_1_ACTIVE,
3441 EL_DYNABOMB_PLAYER_2_ACTIVE,
3442 EL_DYNABOMB_PLAYER_3_ACTIVE,
3443 EL_DYNABOMB_PLAYER_4_ACTIVE,
3444 EL_DYNABOMB_INCREASE_NUMBER,
3445 EL_DYNABOMB_INCREASE_SIZE,
3446 EL_DYNABOMB_INCREASE_POWER,
3447 EL_SP_DISK_RED_ACTIVE,
3455 // elements that can explode only by explosion
3461 static int ep_gravity_reachable[] =
3467 EL_INVISIBLE_SAND_ACTIVE,
3472 EL_SP_PORT_HORIZONTAL,
3473 EL_SP_PORT_VERTICAL,
3475 EL_SP_GRAVITY_PORT_LEFT,
3476 EL_SP_GRAVITY_PORT_RIGHT,
3477 EL_SP_GRAVITY_PORT_UP,
3478 EL_SP_GRAVITY_PORT_DOWN,
3479 EL_SP_GRAVITY_ON_PORT_LEFT,
3480 EL_SP_GRAVITY_ON_PORT_RIGHT,
3481 EL_SP_GRAVITY_ON_PORT_UP,
3482 EL_SP_GRAVITY_ON_PORT_DOWN,
3483 EL_SP_GRAVITY_OFF_PORT_LEFT,
3484 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3485 EL_SP_GRAVITY_OFF_PORT_UP,
3486 EL_SP_GRAVITY_OFF_PORT_DOWN,
3492 static int ep_empty_space[] =
3515 static int ep_player[] =
3522 EL_SOKOBAN_FIELD_PLAYER,
3528 static int ep_can_pass_magic_wall[] =
3542 static int ep_can_pass_dc_magic_wall[] =
3558 static int ep_switchable[] =
3562 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3563 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3564 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3565 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3566 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3567 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3568 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3569 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3570 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3571 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3572 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3573 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3574 EL_SWITCHGATE_SWITCH_UP,
3575 EL_SWITCHGATE_SWITCH_DOWN,
3576 EL_DC_SWITCHGATE_SWITCH_UP,
3577 EL_DC_SWITCHGATE_SWITCH_DOWN,
3579 EL_LIGHT_SWITCH_ACTIVE,
3581 EL_DC_TIMEGATE_SWITCH,
3582 EL_BALLOON_SWITCH_LEFT,
3583 EL_BALLOON_SWITCH_RIGHT,
3584 EL_BALLOON_SWITCH_UP,
3585 EL_BALLOON_SWITCH_DOWN,
3586 EL_BALLOON_SWITCH_ANY,
3587 EL_BALLOON_SWITCH_NONE,
3590 EL_EMC_MAGIC_BALL_SWITCH,
3591 EL_EMC_MAGIC_BALL_SWITCH_ACTIVE,
3596 static int ep_bd_element[] =
3630 static int ep_sp_element[] =
3632 // should always be valid
3635 // standard classic Supaplex elements
3642 EL_SP_HARDWARE_GRAY,
3650 EL_SP_GRAVITY_PORT_RIGHT,
3651 EL_SP_GRAVITY_PORT_DOWN,
3652 EL_SP_GRAVITY_PORT_LEFT,
3653 EL_SP_GRAVITY_PORT_UP,
3658 EL_SP_PORT_VERTICAL,
3659 EL_SP_PORT_HORIZONTAL,
3665 EL_SP_HARDWARE_BASE_1,
3666 EL_SP_HARDWARE_GREEN,
3667 EL_SP_HARDWARE_BLUE,
3669 EL_SP_HARDWARE_YELLOW,
3670 EL_SP_HARDWARE_BASE_2,
3671 EL_SP_HARDWARE_BASE_3,
3672 EL_SP_HARDWARE_BASE_4,
3673 EL_SP_HARDWARE_BASE_5,
3674 EL_SP_HARDWARE_BASE_6,
3678 // additional elements that appeared in newer Supaplex levels
3681 // additional gravity port elements (not switching, but setting gravity)
3682 EL_SP_GRAVITY_ON_PORT_LEFT,
3683 EL_SP_GRAVITY_ON_PORT_RIGHT,
3684 EL_SP_GRAVITY_ON_PORT_UP,
3685 EL_SP_GRAVITY_ON_PORT_DOWN,
3686 EL_SP_GRAVITY_OFF_PORT_LEFT,
3687 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3688 EL_SP_GRAVITY_OFF_PORT_UP,
3689 EL_SP_GRAVITY_OFF_PORT_DOWN,
3691 // more than one Murphy in a level results in an inactive clone
3694 // runtime Supaplex elements
3695 EL_SP_DISK_RED_ACTIVE,
3696 EL_SP_TERMINAL_ACTIVE,
3697 EL_SP_BUGGY_BASE_ACTIVATING,
3698 EL_SP_BUGGY_BASE_ACTIVE,
3705 static int ep_sb_element[] =
3710 EL_SOKOBAN_FIELD_EMPTY,
3711 EL_SOKOBAN_FIELD_FULL,
3712 EL_SOKOBAN_FIELD_PLAYER,
3717 EL_INVISIBLE_STEELWALL,
3722 static int ep_gem[] =
3734 static int ep_food_dark_yamyam[] =
3762 static int ep_food_penguin[] =
3776 static int ep_food_pig[] =
3788 static int ep_historic_wall[] =
3799 EL_GATE_1_GRAY_ACTIVE,
3800 EL_GATE_2_GRAY_ACTIVE,
3801 EL_GATE_3_GRAY_ACTIVE,
3802 EL_GATE_4_GRAY_ACTIVE,
3811 EL_EM_GATE_1_GRAY_ACTIVE,
3812 EL_EM_GATE_2_GRAY_ACTIVE,
3813 EL_EM_GATE_3_GRAY_ACTIVE,
3814 EL_EM_GATE_4_GRAY_ACTIVE,
3821 EL_EXPANDABLE_WALL_HORIZONTAL,
3822 EL_EXPANDABLE_WALL_VERTICAL,
3823 EL_EXPANDABLE_WALL_ANY,
3824 EL_EXPANDABLE_WALL_GROWING,
3825 EL_BD_EXPANDABLE_WALL,
3832 EL_SP_HARDWARE_GRAY,
3833 EL_SP_HARDWARE_GREEN,
3834 EL_SP_HARDWARE_BLUE,
3836 EL_SP_HARDWARE_YELLOW,
3837 EL_SP_HARDWARE_BASE_1,
3838 EL_SP_HARDWARE_BASE_2,
3839 EL_SP_HARDWARE_BASE_3,
3840 EL_SP_HARDWARE_BASE_4,
3841 EL_SP_HARDWARE_BASE_5,
3842 EL_SP_HARDWARE_BASE_6,
3844 EL_SP_TERMINAL_ACTIVE,
3847 EL_INVISIBLE_STEELWALL,
3848 EL_INVISIBLE_STEELWALL_ACTIVE,
3850 EL_INVISIBLE_WALL_ACTIVE,
3851 EL_STEELWALL_SLIPPERY,
3868 static int ep_historic_solid[] =
3872 EL_EXPANDABLE_WALL_HORIZONTAL,
3873 EL_EXPANDABLE_WALL_VERTICAL,
3874 EL_EXPANDABLE_WALL_ANY,
3875 EL_BD_EXPANDABLE_WALL,
3888 EL_QUICKSAND_FILLING,
3889 EL_QUICKSAND_EMPTYING,
3891 EL_MAGIC_WALL_ACTIVE,
3892 EL_MAGIC_WALL_EMPTYING,
3893 EL_MAGIC_WALL_FILLING,
3897 EL_BD_MAGIC_WALL_ACTIVE,
3898 EL_BD_MAGIC_WALL_EMPTYING,
3899 EL_BD_MAGIC_WALL_FULL,
3900 EL_BD_MAGIC_WALL_FILLING,
3901 EL_BD_MAGIC_WALL_DEAD,
3910 EL_SP_TERMINAL_ACTIVE,
3914 EL_INVISIBLE_WALL_ACTIVE,
3915 EL_SWITCHGATE_SWITCH_UP,
3916 EL_SWITCHGATE_SWITCH_DOWN,
3918 EL_TIMEGATE_SWITCH_ACTIVE,
3930 // the following elements are a direct copy of "indestructible" elements,
3931 // except "EL_ACID", which is "indestructible", but not "solid"!
3936 EL_ACID_POOL_TOPLEFT,
3937 EL_ACID_POOL_TOPRIGHT,
3938 EL_ACID_POOL_BOTTOMLEFT,
3939 EL_ACID_POOL_BOTTOM,
3940 EL_ACID_POOL_BOTTOMRIGHT,
3941 EL_SP_HARDWARE_GRAY,
3942 EL_SP_HARDWARE_GREEN,
3943 EL_SP_HARDWARE_BLUE,
3945 EL_SP_HARDWARE_YELLOW,
3946 EL_SP_HARDWARE_BASE_1,
3947 EL_SP_HARDWARE_BASE_2,
3948 EL_SP_HARDWARE_BASE_3,
3949 EL_SP_HARDWARE_BASE_4,
3950 EL_SP_HARDWARE_BASE_5,
3951 EL_SP_HARDWARE_BASE_6,
3952 EL_INVISIBLE_STEELWALL,
3953 EL_INVISIBLE_STEELWALL_ACTIVE,
3954 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3955 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3956 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3957 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3958 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3959 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3960 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3961 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3962 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3963 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3964 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3965 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3967 EL_LIGHT_SWITCH_ACTIVE,
3968 EL_SIGN_EXCLAMATION,
3969 EL_SIGN_RADIOACTIVITY,
3976 EL_SIGN_ENTRY_FORBIDDEN,
3977 EL_SIGN_EMERGENCY_EXIT,
3985 EL_STEEL_EXIT_CLOSED,
3987 EL_STEEL_EXIT_OPENING,
3988 EL_STEEL_EXIT_CLOSING,
3989 EL_EM_STEEL_EXIT_CLOSED,
3990 EL_EM_STEEL_EXIT_OPEN,
3991 EL_EM_STEEL_EXIT_OPENING,
3992 EL_EM_STEEL_EXIT_CLOSING,
3993 EL_DC_STEELWALL_1_LEFT,
3994 EL_DC_STEELWALL_1_RIGHT,
3995 EL_DC_STEELWALL_1_TOP,
3996 EL_DC_STEELWALL_1_BOTTOM,
3997 EL_DC_STEELWALL_1_HORIZONTAL,
3998 EL_DC_STEELWALL_1_VERTICAL,
3999 EL_DC_STEELWALL_1_TOPLEFT,
4000 EL_DC_STEELWALL_1_TOPRIGHT,
4001 EL_DC_STEELWALL_1_BOTTOMLEFT,
4002 EL_DC_STEELWALL_1_BOTTOMRIGHT,
4003 EL_DC_STEELWALL_1_TOPLEFT_2,
4004 EL_DC_STEELWALL_1_TOPRIGHT_2,
4005 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
4006 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
4007 EL_DC_STEELWALL_2_LEFT,
4008 EL_DC_STEELWALL_2_RIGHT,
4009 EL_DC_STEELWALL_2_TOP,
4010 EL_DC_STEELWALL_2_BOTTOM,
4011 EL_DC_STEELWALL_2_HORIZONTAL,
4012 EL_DC_STEELWALL_2_VERTICAL,
4013 EL_DC_STEELWALL_2_MIDDLE,
4014 EL_DC_STEELWALL_2_SINGLE,
4015 EL_STEELWALL_SLIPPERY,
4029 EL_GATE_1_GRAY_ACTIVE,
4030 EL_GATE_2_GRAY_ACTIVE,
4031 EL_GATE_3_GRAY_ACTIVE,
4032 EL_GATE_4_GRAY_ACTIVE,
4041 EL_EM_GATE_1_GRAY_ACTIVE,
4042 EL_EM_GATE_2_GRAY_ACTIVE,
4043 EL_EM_GATE_3_GRAY_ACTIVE,
4044 EL_EM_GATE_4_GRAY_ACTIVE,
4053 EL_EMC_GATE_5_GRAY_ACTIVE,
4054 EL_EMC_GATE_6_GRAY_ACTIVE,
4055 EL_EMC_GATE_7_GRAY_ACTIVE,
4056 EL_EMC_GATE_8_GRAY_ACTIVE,
4058 EL_DC_GATE_WHITE_GRAY,
4059 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4060 EL_DC_GATE_FAKE_GRAY,
4062 EL_SWITCHGATE_OPENING,
4063 EL_SWITCHGATE_CLOSED,
4064 EL_SWITCHGATE_CLOSING,
4065 EL_DC_SWITCHGATE_SWITCH_UP,
4066 EL_DC_SWITCHGATE_SWITCH_DOWN,
4068 EL_TIMEGATE_OPENING,
4070 EL_TIMEGATE_CLOSING,
4071 EL_DC_TIMEGATE_SWITCH,
4072 EL_DC_TIMEGATE_SWITCH_ACTIVE,
4076 EL_TUBE_VERTICAL_LEFT,
4077 EL_TUBE_VERTICAL_RIGHT,
4078 EL_TUBE_HORIZONTAL_UP,
4079 EL_TUBE_HORIZONTAL_DOWN,
4084 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
4085 EL_EXPANDABLE_STEELWALL_VERTICAL,
4086 EL_EXPANDABLE_STEELWALL_ANY,
4091 static int ep_classic_enemy[] =
4108 static int ep_belt[] =
4110 EL_CONVEYOR_BELT_1_LEFT,
4111 EL_CONVEYOR_BELT_1_MIDDLE,
4112 EL_CONVEYOR_BELT_1_RIGHT,
4113 EL_CONVEYOR_BELT_2_LEFT,
4114 EL_CONVEYOR_BELT_2_MIDDLE,
4115 EL_CONVEYOR_BELT_2_RIGHT,
4116 EL_CONVEYOR_BELT_3_LEFT,
4117 EL_CONVEYOR_BELT_3_MIDDLE,
4118 EL_CONVEYOR_BELT_3_RIGHT,
4119 EL_CONVEYOR_BELT_4_LEFT,
4120 EL_CONVEYOR_BELT_4_MIDDLE,
4121 EL_CONVEYOR_BELT_4_RIGHT,
4126 static int ep_belt_active[] =
4128 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4129 EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE,
4130 EL_CONVEYOR_BELT_1_RIGHT_ACTIVE,
4131 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4132 EL_CONVEYOR_BELT_2_MIDDLE_ACTIVE,
4133 EL_CONVEYOR_BELT_2_RIGHT_ACTIVE,
4134 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4135 EL_CONVEYOR_BELT_3_MIDDLE_ACTIVE,
4136 EL_CONVEYOR_BELT_3_RIGHT_ACTIVE,
4137 EL_CONVEYOR_BELT_4_LEFT_ACTIVE,
4138 EL_CONVEYOR_BELT_4_MIDDLE_ACTIVE,
4139 EL_CONVEYOR_BELT_4_RIGHT_ACTIVE,
4144 static int ep_belt_switch[] =
4146 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4147 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4148 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4149 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4150 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4151 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4152 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4153 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4154 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4155 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4156 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4157 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4162 static int ep_tube[] =
4169 EL_TUBE_HORIZONTAL_UP,
4170 EL_TUBE_HORIZONTAL_DOWN,
4172 EL_TUBE_VERTICAL_LEFT,
4173 EL_TUBE_VERTICAL_RIGHT,
4179 static int ep_acid_pool[] =
4181 EL_ACID_POOL_TOPLEFT,
4182 EL_ACID_POOL_TOPRIGHT,
4183 EL_ACID_POOL_BOTTOMLEFT,
4184 EL_ACID_POOL_BOTTOM,
4185 EL_ACID_POOL_BOTTOMRIGHT,
4190 static int ep_keygate[] =
4200 EL_GATE_1_GRAY_ACTIVE,
4201 EL_GATE_2_GRAY_ACTIVE,
4202 EL_GATE_3_GRAY_ACTIVE,
4203 EL_GATE_4_GRAY_ACTIVE,
4212 EL_EM_GATE_1_GRAY_ACTIVE,
4213 EL_EM_GATE_2_GRAY_ACTIVE,
4214 EL_EM_GATE_3_GRAY_ACTIVE,
4215 EL_EM_GATE_4_GRAY_ACTIVE,
4224 EL_EMC_GATE_5_GRAY_ACTIVE,
4225 EL_EMC_GATE_6_GRAY_ACTIVE,
4226 EL_EMC_GATE_7_GRAY_ACTIVE,
4227 EL_EMC_GATE_8_GRAY_ACTIVE,
4229 EL_DC_GATE_WHITE_GRAY,
4230 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4235 static int ep_amoeboid[] =
4247 static int ep_amoebalive[] =
4258 static int ep_has_editor_content[] =
4264 EL_SOKOBAN_FIELD_PLAYER,
4282 static int ep_can_turn_each_move[] =
4284 // !!! do something with this one !!!
4288 static int ep_can_grow[] =
4302 static int ep_active_bomb[] =
4305 EL_EM_DYNAMITE_ACTIVE,
4306 EL_DYNABOMB_PLAYER_1_ACTIVE,
4307 EL_DYNABOMB_PLAYER_2_ACTIVE,
4308 EL_DYNABOMB_PLAYER_3_ACTIVE,
4309 EL_DYNABOMB_PLAYER_4_ACTIVE,
4310 EL_SP_DISK_RED_ACTIVE,
4315 static int ep_inactive[] =
4341 EL_QUICKSAND_FAST_EMPTY,
4364 EL_GATE_1_GRAY_ACTIVE,
4365 EL_GATE_2_GRAY_ACTIVE,
4366 EL_GATE_3_GRAY_ACTIVE,
4367 EL_GATE_4_GRAY_ACTIVE,
4376 EL_EM_GATE_1_GRAY_ACTIVE,
4377 EL_EM_GATE_2_GRAY_ACTIVE,
4378 EL_EM_GATE_3_GRAY_ACTIVE,
4379 EL_EM_GATE_4_GRAY_ACTIVE,
4388 EL_EMC_GATE_5_GRAY_ACTIVE,
4389 EL_EMC_GATE_6_GRAY_ACTIVE,
4390 EL_EMC_GATE_7_GRAY_ACTIVE,
4391 EL_EMC_GATE_8_GRAY_ACTIVE,
4393 EL_DC_GATE_WHITE_GRAY,
4394 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4395 EL_DC_GATE_FAKE_GRAY,
4398 EL_INVISIBLE_STEELWALL,
4406 EL_WALL_EMERALD_YELLOW,
4407 EL_DYNABOMB_INCREASE_NUMBER,
4408 EL_DYNABOMB_INCREASE_SIZE,
4409 EL_DYNABOMB_INCREASE_POWER,
4413 EL_SOKOBAN_FIELD_EMPTY,
4414 EL_SOKOBAN_FIELD_FULL,
4415 EL_WALL_EMERALD_RED,
4416 EL_WALL_EMERALD_PURPLE,
4417 EL_ACID_POOL_TOPLEFT,
4418 EL_ACID_POOL_TOPRIGHT,
4419 EL_ACID_POOL_BOTTOMLEFT,
4420 EL_ACID_POOL_BOTTOM,
4421 EL_ACID_POOL_BOTTOMRIGHT,
4425 EL_BD_MAGIC_WALL_DEAD,
4427 EL_DC_MAGIC_WALL_DEAD,
4428 EL_AMOEBA_TO_DIAMOND,
4436 EL_SP_GRAVITY_PORT_RIGHT,
4437 EL_SP_GRAVITY_PORT_DOWN,
4438 EL_SP_GRAVITY_PORT_LEFT,
4439 EL_SP_GRAVITY_PORT_UP,
4440 EL_SP_PORT_HORIZONTAL,
4441 EL_SP_PORT_VERTICAL,
4452 EL_SP_HARDWARE_GRAY,
4453 EL_SP_HARDWARE_GREEN,
4454 EL_SP_HARDWARE_BLUE,
4456 EL_SP_HARDWARE_YELLOW,
4457 EL_SP_HARDWARE_BASE_1,
4458 EL_SP_HARDWARE_BASE_2,
4459 EL_SP_HARDWARE_BASE_3,
4460 EL_SP_HARDWARE_BASE_4,
4461 EL_SP_HARDWARE_BASE_5,
4462 EL_SP_HARDWARE_BASE_6,
4463 EL_SP_GRAVITY_ON_PORT_LEFT,
4464 EL_SP_GRAVITY_ON_PORT_RIGHT,
4465 EL_SP_GRAVITY_ON_PORT_UP,
4466 EL_SP_GRAVITY_ON_PORT_DOWN,
4467 EL_SP_GRAVITY_OFF_PORT_LEFT,
4468 EL_SP_GRAVITY_OFF_PORT_RIGHT,
4469 EL_SP_GRAVITY_OFF_PORT_UP,
4470 EL_SP_GRAVITY_OFF_PORT_DOWN,
4471 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4472 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4473 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4474 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4475 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4476 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4477 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4478 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4479 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4480 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4481 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4482 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4483 EL_SIGN_EXCLAMATION,
4484 EL_SIGN_RADIOACTIVITY,
4491 EL_SIGN_ENTRY_FORBIDDEN,
4492 EL_SIGN_EMERGENCY_EXIT,
4500 EL_DC_STEELWALL_1_LEFT,
4501 EL_DC_STEELWALL_1_RIGHT,
4502 EL_DC_STEELWALL_1_TOP,
4503 EL_DC_STEELWALL_1_BOTTOM,
4504 EL_DC_STEELWALL_1_HORIZONTAL,
4505 EL_DC_STEELWALL_1_VERTICAL,
4506 EL_DC_STEELWALL_1_TOPLEFT,
4507 EL_DC_STEELWALL_1_TOPRIGHT,
4508 EL_DC_STEELWALL_1_BOTTOMLEFT,
4509 EL_DC_STEELWALL_1_BOTTOMRIGHT,
4510 EL_DC_STEELWALL_1_TOPLEFT_2,
4511 EL_DC_STEELWALL_1_TOPRIGHT_2,
4512 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
4513 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
4514 EL_DC_STEELWALL_2_LEFT,
4515 EL_DC_STEELWALL_2_RIGHT,
4516 EL_DC_STEELWALL_2_TOP,
4517 EL_DC_STEELWALL_2_BOTTOM,
4518 EL_DC_STEELWALL_2_HORIZONTAL,
4519 EL_DC_STEELWALL_2_VERTICAL,
4520 EL_DC_STEELWALL_2_MIDDLE,
4521 EL_DC_STEELWALL_2_SINGLE,
4522 EL_STEELWALL_SLIPPERY,
4527 EL_EMC_WALL_SLIPPERY_1,
4528 EL_EMC_WALL_SLIPPERY_2,
4529 EL_EMC_WALL_SLIPPERY_3,
4530 EL_EMC_WALL_SLIPPERY_4,
4551 static int ep_em_slippery_wall[] =
4556 static int ep_gfx_crumbled[] =
4567 static int ep_editor_cascade_active[] =
4569 EL_INTERNAL_CASCADE_BD_ACTIVE,
4570 EL_INTERNAL_CASCADE_EM_ACTIVE,
4571 EL_INTERNAL_CASCADE_EMC_ACTIVE,
4572 EL_INTERNAL_CASCADE_RND_ACTIVE,
4573 EL_INTERNAL_CASCADE_SB_ACTIVE,
4574 EL_INTERNAL_CASCADE_SP_ACTIVE,
4575 EL_INTERNAL_CASCADE_DC_ACTIVE,
4576 EL_INTERNAL_CASCADE_DX_ACTIVE,
4577 EL_INTERNAL_CASCADE_MM_ACTIVE,
4578 EL_INTERNAL_CASCADE_DF_ACTIVE,
4579 EL_INTERNAL_CASCADE_CHARS_ACTIVE,
4580 EL_INTERNAL_CASCADE_STEEL_CHARS_ACTIVE,
4581 EL_INTERNAL_CASCADE_CE_ACTIVE,
4582 EL_INTERNAL_CASCADE_GE_ACTIVE,
4583 EL_INTERNAL_CASCADE_ES_ACTIVE,
4584 EL_INTERNAL_CASCADE_REF_ACTIVE,
4585 EL_INTERNAL_CASCADE_USER_ACTIVE,
4586 EL_INTERNAL_CASCADE_DYNAMIC_ACTIVE,
4591 static int ep_editor_cascade_inactive[] =
4593 EL_INTERNAL_CASCADE_BD,
4594 EL_INTERNAL_CASCADE_EM,
4595 EL_INTERNAL_CASCADE_EMC,
4596 EL_INTERNAL_CASCADE_RND,
4597 EL_INTERNAL_CASCADE_SB,
4598 EL_INTERNAL_CASCADE_SP,
4599 EL_INTERNAL_CASCADE_DC,
4600 EL_INTERNAL_CASCADE_DX,
4601 EL_INTERNAL_CASCADE_MM,
4602 EL_INTERNAL_CASCADE_DF,
4603 EL_INTERNAL_CASCADE_CHARS,
4604 EL_INTERNAL_CASCADE_STEEL_CHARS,
4605 EL_INTERNAL_CASCADE_CE,
4606 EL_INTERNAL_CASCADE_GE,
4607 EL_INTERNAL_CASCADE_ES,
4608 EL_INTERNAL_CASCADE_REF,
4609 EL_INTERNAL_CASCADE_USER,
4610 EL_INTERNAL_CASCADE_DYNAMIC,
4615 static int ep_obsolete[] =
4619 EL_EM_KEY_1_FILE_OBSOLETE,
4620 EL_EM_KEY_2_FILE_OBSOLETE,
4621 EL_EM_KEY_3_FILE_OBSOLETE,
4622 EL_EM_KEY_4_FILE_OBSOLETE,
4623 EL_ENVELOPE_OBSOLETE,
4632 } element_properties[] =
4634 { ep_diggable, EP_DIGGABLE },
4635 { ep_collectible_only, EP_COLLECTIBLE_ONLY },
4636 { ep_dont_run_into, EP_DONT_RUN_INTO },
4637 { ep_dont_collide_with, EP_DONT_COLLIDE_WITH },
4638 { ep_dont_touch, EP_DONT_TOUCH },
4639 { ep_indestructible, EP_INDESTRUCTIBLE },
4640 { ep_slippery, EP_SLIPPERY },
4641 { ep_can_change, EP_CAN_CHANGE },
4642 { ep_can_move, EP_CAN_MOVE },
4643 { ep_can_fall, EP_CAN_FALL },
4644 { ep_can_smash_player, EP_CAN_SMASH_PLAYER },
4645 { ep_can_smash_enemies, EP_CAN_SMASH_ENEMIES },
4646 { ep_can_smash_everything, EP_CAN_SMASH_EVERYTHING },
4647 { ep_explodes_by_fire, EP_EXPLODES_BY_FIRE },
4648 { ep_explodes_smashed, EP_EXPLODES_SMASHED },
4649 { ep_explodes_impact, EP_EXPLODES_IMPACT },
4650 { ep_walkable_over, EP_WALKABLE_OVER },
4651 { ep_walkable_inside, EP_WALKABLE_INSIDE },
4652 { ep_walkable_under, EP_WALKABLE_UNDER },
4653 { ep_passable_over, EP_PASSABLE_OVER },
4654 { ep_passable_inside, EP_PASSABLE_INSIDE },
4655 { ep_passable_under, EP_PASSABLE_UNDER },
4656 { ep_droppable, EP_DROPPABLE },
4657 { ep_explodes_1x1_old, EP_EXPLODES_1X1_OLD },
4658 { ep_pushable, EP_PUSHABLE },
4659 { ep_explodes_cross_old, EP_EXPLODES_CROSS_OLD },
4660 { ep_protected, EP_PROTECTED },
4661 { ep_throwable, EP_THROWABLE },
4662 { ep_can_explode, EP_CAN_EXPLODE },
4663 { ep_gravity_reachable, EP_GRAVITY_REACHABLE },
4665 { ep_empty_space, EP_EMPTY_SPACE },
4666 { ep_player, EP_PLAYER },
4667 { ep_can_pass_magic_wall, EP_CAN_PASS_MAGIC_WALL },
4668 { ep_can_pass_dc_magic_wall, EP_CAN_PASS_DC_MAGIC_WALL },
4669 { ep_switchable, EP_SWITCHABLE },
4670 { ep_bd_element, EP_BD_ELEMENT },
4671 { ep_sp_element, EP_SP_ELEMENT },
4672 { ep_sb_element, EP_SB_ELEMENT },
4674 { ep_food_dark_yamyam, EP_FOOD_DARK_YAMYAM },
4675 { ep_food_penguin, EP_FOOD_PENGUIN },
4676 { ep_food_pig, EP_FOOD_PIG },
4677 { ep_historic_wall, EP_HISTORIC_WALL },
4678 { ep_historic_solid, EP_HISTORIC_SOLID },
4679 { ep_classic_enemy, EP_CLASSIC_ENEMY },
4680 { ep_belt, EP_BELT },
4681 { ep_belt_active, EP_BELT_ACTIVE },
4682 { ep_belt_switch, EP_BELT_SWITCH },
4683 { ep_tube, EP_TUBE },
4684 { ep_acid_pool, EP_ACID_POOL },
4685 { ep_keygate, EP_KEYGATE },
4686 { ep_amoeboid, EP_AMOEBOID },
4687 { ep_amoebalive, EP_AMOEBALIVE },
4688 { ep_has_editor_content, EP_HAS_EDITOR_CONTENT },
4689 { ep_can_turn_each_move, EP_CAN_TURN_EACH_MOVE },
4690 { ep_can_grow, EP_CAN_GROW },
4691 { ep_active_bomb, EP_ACTIVE_BOMB },
4692 { ep_inactive, EP_INACTIVE },
4694 { ep_em_slippery_wall, EP_EM_SLIPPERY_WALL },
4696 { ep_gfx_crumbled, EP_GFX_CRUMBLED },
4698 { ep_editor_cascade_active, EP_EDITOR_CASCADE_ACTIVE },
4699 { ep_editor_cascade_inactive, EP_EDITOR_CASCADE_INACTIVE },
4701 { ep_obsolete, EP_OBSOLETE },
4708 // always start with reliable default values (element has no properties)
4709 // (but never initialize clipboard elements after the very first time)
4710 // (to be able to use clipboard elements between several levels)
4711 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4712 if (!IS_CLIPBOARD_ELEMENT(i) || !clipboard_elements_initialized)
4713 for (j = 0; j < NUM_ELEMENT_PROPERTIES; j++)
4714 SET_PROPERTY(i, j, FALSE);
4716 // set all base element properties from above array definitions
4717 for (i = 0; element_properties[i].elements != NULL; i++)
4718 for (j = 0; (element_properties[i].elements)[j] != -1; j++)
4719 SET_PROPERTY((element_properties[i].elements)[j],
4720 element_properties[i].property, TRUE);
4722 // copy properties to some elements that are only stored in level file
4723 for (i = 0; i < NUM_ELEMENT_PROPERTIES; i++)
4724 for (j = 0; copy_properties[j][0] != -1; j++)
4725 if (HAS_PROPERTY(copy_properties[j][0], i))
4726 for (k = 1; k <= 4; k++)
4727 SET_PROPERTY(copy_properties[j][k], i, TRUE);
4729 // set static element properties that are not listed in array definitions
4730 for (i = EL_STEEL_CHAR_START; i <= EL_STEEL_CHAR_END; i++)
4731 SET_PROPERTY(i, EP_INDESTRUCTIBLE, TRUE);
4733 clipboard_elements_initialized = TRUE;
4736 void InitElementPropertiesEngine(int engine_version)
4738 static int no_wall_properties[] =
4741 EP_COLLECTIBLE_ONLY,
4743 EP_DONT_COLLIDE_WITH,
4746 EP_CAN_SMASH_PLAYER,
4747 EP_CAN_SMASH_ENEMIES,
4748 EP_CAN_SMASH_EVERYTHING,
4753 EP_FOOD_DARK_YAMYAM,
4769 /* important: after initialization in InitElementPropertiesStatic(), the
4770 elements are not again initialized to a default value; therefore all
4771 changes have to make sure that they leave the element with a defined
4772 property (which means that conditional property changes must be set to
4773 a reliable default value before) */
4775 // resolve group elements
4776 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
4777 ResolveGroupElement(EL_GROUP_START + i);
4779 // set all special, combined or engine dependent element properties
4780 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4782 // do not change (already initialized) clipboard elements here
4783 if (IS_CLIPBOARD_ELEMENT(i))
4786 // ---------- INACTIVE ----------------------------------------------------
4787 SET_PROPERTY(i, EP_INACTIVE, ((i >= EL_CHAR_START &&
4788 i <= EL_CHAR_END) ||
4789 (i >= EL_STEEL_CHAR_START &&
4790 i <= EL_STEEL_CHAR_END)));
4792 // ---------- WALKABLE, PASSABLE, ACCESSIBLE ------------------------------
4793 SET_PROPERTY(i, EP_WALKABLE, (IS_WALKABLE_OVER(i) ||
4794 IS_WALKABLE_INSIDE(i) ||
4795 IS_WALKABLE_UNDER(i)));
4797 SET_PROPERTY(i, EP_PASSABLE, (IS_PASSABLE_OVER(i) ||
4798 IS_PASSABLE_INSIDE(i) ||
4799 IS_PASSABLE_UNDER(i)));
4801 SET_PROPERTY(i, EP_ACCESSIBLE_OVER, (IS_WALKABLE_OVER(i) ||
4802 IS_PASSABLE_OVER(i)));
4804 SET_PROPERTY(i, EP_ACCESSIBLE_INSIDE, (IS_WALKABLE_INSIDE(i) ||
4805 IS_PASSABLE_INSIDE(i)));
4807 SET_PROPERTY(i, EP_ACCESSIBLE_UNDER, (IS_WALKABLE_UNDER(i) ||
4808 IS_PASSABLE_UNDER(i)));
4810 SET_PROPERTY(i, EP_ACCESSIBLE, (IS_WALKABLE(i) ||
4813 // ---------- COLLECTIBLE -------------------------------------------------
4814 SET_PROPERTY(i, EP_COLLECTIBLE, (IS_COLLECTIBLE_ONLY(i) ||
4818 // ---------- SNAPPABLE ---------------------------------------------------
4819 SET_PROPERTY(i, EP_SNAPPABLE, (IS_DIGGABLE(i) ||
4820 IS_COLLECTIBLE(i) ||
4824 // ---------- WALL --------------------------------------------------------
4825 SET_PROPERTY(i, EP_WALL, TRUE); // default: element is wall
4827 for (j = 0; no_wall_properties[j] != -1; j++)
4828 if (HAS_PROPERTY(i, no_wall_properties[j]) ||
4829 i >= EL_FIRST_RUNTIME_UNREAL)
4830 SET_PROPERTY(i, EP_WALL, FALSE);
4832 if (IS_HISTORIC_WALL(i))
4833 SET_PROPERTY(i, EP_WALL, TRUE);
4835 // ---------- SOLID_FOR_PUSHING -------------------------------------------
4836 if (engine_version < VERSION_IDENT(2,2,0,0))
4837 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, IS_HISTORIC_SOLID(i));
4839 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, (!IS_WALKABLE(i) &&
4841 !IS_COLLECTIBLE(i)));
4843 // ---------- DRAGONFIRE_PROOF --------------------------------------------
4844 if (IS_HISTORIC_SOLID(i) || i == EL_EXPLOSION)
4845 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, TRUE);
4847 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, (IS_INDESTRUCTIBLE(i) &&
4850 // ---------- EXPLOSION_PROOF ---------------------------------------------
4852 SET_PROPERTY(i, EP_EXPLOSION_PROOF, TRUE);
4853 else if (engine_version < VERSION_IDENT(2,2,0,0))
4854 SET_PROPERTY(i, EP_EXPLOSION_PROOF, IS_INDESTRUCTIBLE(i));
4856 SET_PROPERTY(i, EP_EXPLOSION_PROOF, (IS_INDESTRUCTIBLE(i) &&
4860 if (IS_CUSTOM_ELEMENT(i))
4862 // these are additional properties which are initially false when set
4864 // ---------- DONT_COLLIDE_WITH / DONT_RUN_INTO -------------------------
4866 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH, TRUE);
4867 if (DONT_COLLIDE_WITH(i))
4868 SET_PROPERTY(i, EP_DONT_RUN_INTO, TRUE);
4870 // ---------- CAN_SMASH_ENEMIES / CAN_SMASH_PLAYER ----------------------
4871 if (CAN_SMASH_EVERYTHING(i))
4872 SET_PROPERTY(i, EP_CAN_SMASH_ENEMIES, TRUE);
4873 if (CAN_SMASH_ENEMIES(i))
4874 SET_PROPERTY(i, EP_CAN_SMASH_PLAYER, TRUE);
4877 // ---------- CAN_SMASH ---------------------------------------------------
4878 SET_PROPERTY(i, EP_CAN_SMASH, (CAN_SMASH_PLAYER(i) ||
4879 CAN_SMASH_ENEMIES(i) ||
4880 CAN_SMASH_EVERYTHING(i)));
4882 // ---------- CAN_EXPLODE_BY_FIRE -----------------------------------------
4883 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_FIRE, (CAN_EXPLODE(i) &&
4884 EXPLODES_BY_FIRE(i)));
4886 // ---------- CAN_EXPLODE_SMASHED -----------------------------------------
4887 SET_PROPERTY(i, EP_CAN_EXPLODE_SMASHED, (CAN_EXPLODE(i) &&
4888 EXPLODES_SMASHED(i)));
4890 // ---------- CAN_EXPLODE_IMPACT ------------------------------------------
4891 SET_PROPERTY(i, EP_CAN_EXPLODE_IMPACT, (CAN_EXPLODE(i) &&
4892 EXPLODES_IMPACT(i)));
4894 // ---------- CAN_EXPLODE_BY_DRAGONFIRE -----------------------------------
4895 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_DRAGONFIRE, CAN_EXPLODE_BY_FIRE(i));
4897 // ---------- CAN_EXPLODE_BY_EXPLOSION ------------------------------------
4898 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_EXPLOSION, (CAN_EXPLODE_BY_FIRE(i) ||
4899 i == EL_BLACK_ORB));
4901 // ---------- COULD_MOVE_INTO_ACID ----------------------------------------
4902 SET_PROPERTY(i, EP_COULD_MOVE_INTO_ACID, (IS_PLAYER_ELEMENT(i) ||
4904 IS_CUSTOM_ELEMENT(i)));
4906 // ---------- MAYBE_DONT_COLLIDE_WITH -------------------------------------
4907 SET_PROPERTY(i, EP_MAYBE_DONT_COLLIDE_WITH, (i == EL_SP_SNIKSNAK ||
4908 i == EL_SP_ELECTRON));
4910 // ---------- CAN_MOVE_INTO_ACID ------------------------------------------
4911 if (COULD_MOVE_INTO_ACID(i) && !IS_CUSTOM_ELEMENT(i))
4912 SET_PROPERTY(i, EP_CAN_MOVE_INTO_ACID,
4913 getMoveIntoAcidProperty(&level, i));
4915 // ---------- DONT_COLLIDE_WITH -------------------------------------------
4916 if (MAYBE_DONT_COLLIDE_WITH(i))
4917 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH,
4918 getDontCollideWithProperty(&level, i));
4920 // ---------- SP_PORT -----------------------------------------------------
4921 SET_PROPERTY(i, EP_SP_PORT, (IS_SP_ELEMENT(i) &&
4922 IS_PASSABLE_INSIDE(i)));
4924 // ---------- CAN_BE_CLONED_BY_ANDROID ------------------------------------
4925 for (j = 0; j < level.num_android_clone_elements; j++)
4926 SET_PROPERTY(i, EP_CAN_BE_CLONED_BY_ANDROID,
4928 IS_EQUAL_OR_IN_GROUP(i, level.android_clone_element[j])));
4930 // ---------- CAN_CHANGE --------------------------------------------------
4931 SET_PROPERTY(i, EP_CAN_CHANGE, FALSE); // default: cannot change
4932 for (j = 0; j < element_info[i].num_change_pages; j++)
4933 if (element_info[i].change_page[j].can_change)
4934 SET_PROPERTY(i, EP_CAN_CHANGE, TRUE);
4936 // ---------- HAS_ACTION --------------------------------------------------
4937 SET_PROPERTY(i, EP_HAS_ACTION, FALSE); // default: has no action
4938 for (j = 0; j < element_info[i].num_change_pages; j++)
4939 if (element_info[i].change_page[j].has_action)
4940 SET_PROPERTY(i, EP_HAS_ACTION, TRUE);
4942 // ---------- CAN_CHANGE_OR_HAS_ACTION ------------------------------------
4943 SET_PROPERTY(i, EP_CAN_CHANGE_OR_HAS_ACTION, (CAN_CHANGE(i) ||
4946 // ---------- GFX_CRUMBLED ------------------------------------------------
4947 SET_PROPERTY(i, EP_GFX_CRUMBLED,
4948 element_info[i].crumbled[ACTION_DEFAULT] !=
4949 element_info[i].graphic[ACTION_DEFAULT]);
4951 // ---------- EDITOR_CASCADE ----------------------------------------------
4952 SET_PROPERTY(i, EP_EDITOR_CASCADE, (IS_EDITOR_CASCADE_ACTIVE(i) ||
4953 IS_EDITOR_CASCADE_INACTIVE(i)));
4956 // dynamically adjust element properties according to game engine version
4958 static int ep_em_slippery_wall[] =
4963 EL_EXPANDABLE_WALL_HORIZONTAL,
4964 EL_EXPANDABLE_WALL_VERTICAL,
4965 EL_EXPANDABLE_WALL_ANY,
4966 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
4967 EL_EXPANDABLE_STEELWALL_VERTICAL,
4968 EL_EXPANDABLE_STEELWALL_ANY,
4969 EL_EXPANDABLE_STEELWALL_GROWING,
4973 static int ep_em_explodes_by_fire[] =
4976 EL_EM_DYNAMITE_ACTIVE,
4981 // special EM style gems behaviour
4982 for (i = 0; ep_em_slippery_wall[i] != -1; i++)
4983 SET_PROPERTY(ep_em_slippery_wall[i], EP_EM_SLIPPERY_WALL,
4984 level.em_slippery_gems);
4986 // "EL_EXPANDABLE_WALL_GROWING" wasn't slippery for EM gems in 2.0.1
4987 SET_PROPERTY(EL_EXPANDABLE_WALL_GROWING, EP_EM_SLIPPERY_WALL,
4988 (level.em_slippery_gems &&
4989 engine_version > VERSION_IDENT(2,0,1,0)));
4991 // special EM style explosion behaviour regarding chain reactions
4992 for (i = 0; ep_em_explodes_by_fire[i] != -1; i++)
4993 SET_PROPERTY(ep_em_explodes_by_fire[i], EP_EXPLODES_BY_FIRE,
4994 level.em_explodes_by_fire);
4997 // this is needed because some graphics depend on element properties
4998 if (game_status == GAME_MODE_PLAYING)
4999 InitElementGraphicInfo();
5002 void InitElementPropertiesGfxElement(void)
5006 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5008 struct ElementInfo *ei = &element_info[i];
5010 ei->gfx_element = (ei->use_gfx_element ? ei->gfx_element_initial : i);
5014 static void InitGlobal(void)
5019 for (i = 0; i < MAX_NUM_ELEMENTS + 1; i++)
5021 // check if element_name_info entry defined for each element in "main.h"
5022 if (i < MAX_NUM_ELEMENTS && element_name_info[i].token_name == NULL)
5023 Fail("undefined 'element_name_info' entry for element %d", i);
5025 element_info[i].token_name = element_name_info[i].token_name;
5026 element_info[i].class_name = element_name_info[i].class_name;
5027 element_info[i].editor_description= element_name_info[i].editor_description;
5030 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS + 1; i++)
5032 // check if global_anim_name_info defined for each entry in "main.h"
5033 if (i < NUM_GLOBAL_ANIM_TOKENS &&
5034 global_anim_name_info[i].token_name == NULL)
5035 Fail("undefined 'global_anim_name_info' entry for anim %d", i);
5037 global_anim_info[i].token_name = global_anim_name_info[i].token_name;
5040 // create hash to store URLs for global animations
5041 anim_url_hash = newSetupFileHash();
5043 // create hash from image config list
5044 image_config_hash = newSetupFileHash();
5045 for (i = 0; image_config[i].token != NULL; i++)
5046 setHashEntry(image_config_hash,
5047 image_config[i].token,
5048 image_config[i].value);
5050 // create hash from element token list
5051 element_token_hash = newSetupFileHash();
5052 for (i = 0; element_name_info[i].token_name != NULL; i++)
5053 setHashEntry(element_token_hash,
5054 element_name_info[i].token_name,
5057 // create hash from graphic token list
5058 graphic_token_hash = newSetupFileHash();
5059 for (graphic = 0, i = 0; image_config[i].token != NULL; i++)
5060 if (strSuffix(image_config[i].value, ".png") ||
5061 strSuffix(image_config[i].value, ".pcx") ||
5062 strSuffix(image_config[i].value, ".wav") ||
5063 strEqual(image_config[i].value, UNDEFINED_FILENAME))
5064 setHashEntry(graphic_token_hash,
5065 image_config[i].token,
5066 int2str(graphic++, 0));
5068 // create hash from font token list
5069 font_token_hash = newSetupFileHash();
5070 for (i = 0; font_info[i].token_name != NULL; i++)
5071 setHashEntry(font_token_hash,
5072 font_info[i].token_name,
5075 // set default filenames for all cloned graphics in static configuration
5076 for (i = 0; image_config[i].token != NULL; i++)
5078 if (strEqual(image_config[i].value, UNDEFINED_FILENAME))
5080 char *token = image_config[i].token;
5081 char *token_clone_from = getStringCat2(token, ".clone_from");
5082 char *token_cloned = getHashEntry(image_config_hash, token_clone_from);
5084 if (token_cloned != NULL)
5086 char *value_cloned = getHashEntry(image_config_hash, token_cloned);
5088 if (value_cloned != NULL)
5090 // set default filename in static configuration
5091 image_config[i].value = value_cloned;
5093 // set default filename in image config hash
5094 setHashEntry(image_config_hash, token, value_cloned);
5098 free(token_clone_from);
5102 // always start with reliable default values (all elements)
5103 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5104 ActiveElement[i] = i;
5106 // now add all entries that have an active state (active elements)
5107 for (i = 0; element_with_active_state[i].element != -1; i++)
5109 int element = element_with_active_state[i].element;
5110 int element_active = element_with_active_state[i].element_active;
5112 ActiveElement[element] = element_active;
5115 // always start with reliable default values (all buttons)
5116 for (i = 0; i < NUM_IMAGE_FILES; i++)
5117 ActiveButton[i] = i;
5119 // now add all entries that have an active state (active buttons)
5120 for (i = 0; button_with_active_state[i].button != -1; i++)
5122 int button = button_with_active_state[i].button;
5123 int button_active = button_with_active_state[i].button_active;
5125 ActiveButton[button] = button_active;
5128 // always start with reliable default values (all fonts)
5129 for (i = 0; i < NUM_FONTS; i++)
5132 // now add all entries that have an active state (active fonts)
5133 for (i = 0; font_with_active_state[i].font_nr != -1; i++)
5135 int font = font_with_active_state[i].font_nr;
5136 int font_active = font_with_active_state[i].font_nr_active;
5138 ActiveFont[font] = font_active;
5141 global.autoplay_leveldir = NULL;
5142 global.patchtapes_leveldir = NULL;
5143 global.convert_leveldir = NULL;
5144 global.dumplevel_leveldir = NULL;
5145 global.dumptape_leveldir = NULL;
5146 global.create_sketch_images_dir = NULL;
5147 global.create_collect_images_dir = NULL;
5149 global.frames_per_second = 0;
5150 global.show_frames_per_second = FALSE;
5152 global.border_status = GAME_MODE_LOADING;
5153 global.anim_status = global.anim_status_next = GAME_MODE_LOADING;
5155 global.use_envelope_request = FALSE;
5157 global.user_names = NULL;
5160 static void Execute_Command(char *command)
5164 if (strEqual(command, "print graphicsinfo.conf"))
5166 Print("# You can configure additional/alternative image files here.\n");
5167 Print("# (The entries below are default and therefore commented out.)\n");
5169 Print("%s\n", getFormattedSetupEntry("name", "Classic Graphics"));
5171 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5174 for (i = 0; image_config[i].token != NULL; i++)
5175 Print("# %s\n", getFormattedSetupEntry(image_config[i].token,
5176 image_config[i].value));
5180 else if (strEqual(command, "print soundsinfo.conf"))
5182 Print("# You can configure additional/alternative sound files here.\n");
5183 Print("# (The entries below are default and therefore commented out.)\n");
5185 Print("%s\n", getFormattedSetupEntry("name", "Classic Sounds"));
5187 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5190 for (i = 0; sound_config[i].token != NULL; i++)
5191 Print("# %s\n", getFormattedSetupEntry(sound_config[i].token,
5192 sound_config[i].value));
5196 else if (strEqual(command, "print musicinfo.conf"))
5198 Print("# You can configure additional/alternative music files here.\n");
5199 Print("# (The entries below are default and therefore commented out.)\n");
5201 Print("%s\n", getFormattedSetupEntry("name", "Classic Music"));
5203 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5206 for (i = 0; music_config[i].token != NULL; i++)
5207 Print("# %s\n", getFormattedSetupEntry(music_config[i].token,
5208 music_config[i].value));
5212 else if (strEqual(command, "print editorsetup.conf"))
5214 Print("# You can configure your personal editor element list here.\n");
5215 Print("# (The entries below are default and therefore commented out.)\n");
5218 // this is needed to be able to check element list for cascade elements
5219 InitElementPropertiesStatic();
5220 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
5222 PrintEditorElementList();
5226 else if (strEqual(command, "print helpanim.conf"))
5228 Print("# You can configure different element help animations here.\n");
5229 Print("# (The entries below are default and therefore commented out.)\n");
5232 for (i = 0; helpanim_config[i].token != NULL; i++)
5234 Print("# %s\n", getFormattedSetupEntry(helpanim_config[i].token,
5235 helpanim_config[i].value));
5237 if (strEqual(helpanim_config[i].token, "end"))
5243 else if (strEqual(command, "print helptext.conf"))
5245 Print("# You can configure different element help text here.\n");
5246 Print("# (The entries below are default and therefore commented out.)\n");
5249 for (i = 0; helptext_config[i].token != NULL; i++)
5250 Print("# %s\n", getFormattedSetupEntry(helptext_config[i].token,
5251 helptext_config[i].value));
5255 else if (strPrefix(command, "dump level "))
5257 char *filename = &command[11];
5259 if (fileExists(filename))
5261 LoadLevelFromFilename(&level, filename);
5267 char *leveldir = getStringCopy(filename); // read command parameters
5268 char *level_nr = strchr(leveldir, ' ');
5270 if (level_nr == NULL)
5271 Fail("cannot open file '%s'", filename);
5275 global.dumplevel_leveldir = leveldir;
5276 global.dumplevel_level_nr = atoi(level_nr);
5278 program.headless = TRUE;
5280 else if (strPrefix(command, "dump tape "))
5282 char *filename = &command[10];
5284 if (fileExists(filename))
5286 LoadTapeFromFilename(filename);
5292 char *leveldir = getStringCopy(filename); // read command parameters
5293 char *level_nr = strchr(leveldir, ' ');
5295 if (level_nr == NULL)
5296 Fail("cannot open file '%s'", filename);
5300 global.dumptape_leveldir = leveldir;
5301 global.dumptape_level_nr = atoi(level_nr);
5303 program.headless = TRUE;
5305 else if (strPrefix(command, "autoplay ") ||
5306 strPrefix(command, "autoffwd ") ||
5307 strPrefix(command, "autowarp ") ||
5308 strPrefix(command, "autotest ") ||
5309 strPrefix(command, "autosave ") ||
5310 strPrefix(command, "autoupload ") ||
5311 strPrefix(command, "autofix "))
5313 char *arg_ptr = strchr(command, ' ');
5314 char *str_ptr = getStringCopy(arg_ptr); // read command parameters
5316 global.autoplay_mode =
5317 (strPrefix(command, "autoplay") ? AUTOPLAY_MODE_PLAY :
5318 strPrefix(command, "autoffwd") ? AUTOPLAY_MODE_FFWD :
5319 strPrefix(command, "autowarp") ? AUTOPLAY_MODE_WARP :
5320 strPrefix(command, "autotest") ? AUTOPLAY_MODE_TEST :
5321 strPrefix(command, "autosave") ? AUTOPLAY_MODE_SAVE :
5322 strPrefix(command, "autoupload") ? AUTOPLAY_MODE_UPLOAD :
5323 strPrefix(command, "autofix") ? AUTOPLAY_MODE_FIX :
5324 AUTOPLAY_MODE_NONE);
5326 while (*str_ptr != '\0') // continue parsing string
5328 // cut leading whitespace from string, replace it by string terminator
5329 while (*str_ptr == ' ' || *str_ptr == '\t')
5332 if (*str_ptr == '\0') // end of string reached
5335 if (global.autoplay_leveldir == NULL) // read level set string
5337 global.autoplay_leveldir = str_ptr;
5338 global.autoplay_all = TRUE; // default: play all tapes
5340 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5341 global.autoplay_level[i] = FALSE;
5343 else // read level number string
5345 int level_nr = atoi(str_ptr); // get level_nr value
5347 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5348 global.autoplay_level[level_nr] = TRUE;
5350 global.autoplay_all = FALSE;
5353 // advance string pointer to the next whitespace (or end of string)
5354 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5358 if (global.autoplay_mode & AUTOPLAY_WARP_NO_DISPLAY)
5359 program.headless = TRUE;
5361 else if (strPrefix(command, "patch tapes "))
5363 char *str_ptr = getStringCopy(&command[12]); // read command parameters
5365 // skip leading whitespace
5366 while (*str_ptr == ' ' || *str_ptr == '\t')
5369 if (*str_ptr == '\0')
5370 Fail("cannot find MODE in command '%s'", command);
5372 global.patchtapes_mode = str_ptr; // store patch mode
5374 // advance to next whitespace (or end of string)
5375 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5378 while (*str_ptr != '\0') // continue parsing string
5380 // cut leading whitespace from string, replace it by string terminator
5381 while (*str_ptr == ' ' || *str_ptr == '\t')
5384 if (*str_ptr == '\0') // end of string reached
5387 if (global.patchtapes_leveldir == NULL) // read level set string
5389 global.patchtapes_leveldir = str_ptr;
5390 global.patchtapes_all = TRUE; // default: patch all tapes
5392 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5393 global.patchtapes_level[i] = FALSE;
5395 else // read level number string
5397 int level_nr = atoi(str_ptr); // get level_nr value
5399 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5400 global.patchtapes_level[level_nr] = TRUE;
5402 global.patchtapes_all = FALSE;
5405 // advance string pointer to the next whitespace (or end of string)
5406 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5410 if (global.patchtapes_leveldir == NULL)
5412 if (strEqual(global.patchtapes_mode, "help"))
5413 global.patchtapes_leveldir = UNDEFINED_LEVELSET;
5415 Fail("cannot find LEVELDIR in command '%s'", command);
5418 program.headless = TRUE;
5420 else if (strPrefix(command, "convert "))
5422 char *str_copy = getStringCopy(strchr(command, ' ') + 1);
5423 char *str_ptr = strchr(str_copy, ' ');
5425 global.convert_leveldir = str_copy;
5426 global.convert_level_nr = -1;
5428 if (str_ptr != NULL) // level number follows
5430 *str_ptr++ = '\0'; // terminate leveldir string
5431 global.convert_level_nr = atoi(str_ptr); // get level_nr value
5434 program.headless = TRUE;
5436 else if (strPrefix(command, "create sketch images "))
5438 global.create_sketch_images_dir = getStringCopy(&command[21]);
5440 if (access(global.create_sketch_images_dir, W_OK) != 0)
5441 Fail("image target directory '%s' not found or not writable",
5442 global.create_sketch_images_dir);
5444 else if (strPrefix(command, "create collect image "))
5446 global.create_collect_images_dir = getStringCopy(&command[21]);
5448 if (access(global.create_collect_images_dir, W_OK) != 0)
5449 Fail("image target directory '%s' not found or not writable",
5450 global.create_collect_images_dir);
5452 else if (strPrefix(command, "create CE image "))
5454 CreateCustomElementImages(&command[16]);
5460 FailWithHelp("unrecognized command '%s'", command);
5463 // disable networking if any valid command was recognized
5464 options.network = setup.network_mode = FALSE;
5467 static void InitSetup(void)
5469 LoadUserNames(); // global user names
5470 LoadUserSetup(); // global user number
5472 LoadSetup(); // global setup info
5474 // set some options from setup file
5476 if (setup.options.verbose)
5477 options.verbose = TRUE;
5479 if (setup.options.debug)
5480 options.debug = TRUE;
5482 if (!strEqual(setup.options.debug_mode, ARG_UNDEFINED_STRING))
5483 options.debug_mode = getStringCopy(setup.options.debug_mode);
5485 if (setup.debug.show_frames_per_second)
5486 global.show_frames_per_second = TRUE;
5489 static void InitGameInfo(void)
5491 game.restart_level = FALSE;
5493 game.request_active = FALSE;
5494 game.request_active_or_moving = FALSE;
5496 game.use_masked_elements_initial = FALSE;
5499 static void InitPlayerInfo(void)
5503 // choose default local player
5504 local_player = &stored_player[0];
5506 for (i = 0; i < MAX_PLAYERS; i++)
5508 stored_player[i].connected_locally = FALSE;
5509 stored_player[i].connected_network = FALSE;
5512 local_player->connected_locally = TRUE;
5515 static void InitArtworkInfo(void)
5520 static char *get_string_in_brackets(char *string)
5522 char *string_in_brackets = checked_malloc(strlen(string) + 3);
5524 sprintf(string_in_brackets, "[%s]", string);
5526 return string_in_brackets;
5529 static char *get_level_id_suffix(int id_nr)
5531 char *id_suffix = checked_malloc(1 + 3 + 1);
5533 if (id_nr < 0 || id_nr > 999)
5536 sprintf(id_suffix, ".%03d", id_nr);
5541 static void InitArtworkConfig(void)
5543 static char *image_id_prefix[MAX_NUM_ELEMENTS +
5545 NUM_GLOBAL_ANIM_TOKENS + 1];
5546 static char *sound_id_prefix[2 * MAX_NUM_ELEMENTS +
5547 NUM_GLOBAL_ANIM_TOKENS + 1];
5548 static char *music_id_prefix[NUM_MUSIC_PREFIXES +
5549 NUM_GLOBAL_ANIM_TOKENS + 1];
5550 static char *action_id_suffix[NUM_ACTIONS + 1];
5551 static char *direction_id_suffix[NUM_DIRECTIONS_FULL + 1];
5552 static char *special_id_suffix[NUM_SPECIAL_GFX_ARGS + 1];
5553 static char *level_id_suffix[MAX_LEVELS + 1];
5554 static char *dummy[1] = { NULL };
5555 static char *ignore_generic_tokens[] =
5560 "program_copyright",
5565 static char **ignore_image_tokens;
5566 static char **ignore_sound_tokens;
5567 static char **ignore_music_tokens;
5568 int num_ignore_generic_tokens;
5569 int num_ignore_image_tokens;
5570 int num_ignore_sound_tokens;
5571 int num_ignore_music_tokens;
5574 // dynamically determine list of generic tokens to be ignored
5575 num_ignore_generic_tokens = 0;
5576 for (i = 0; ignore_generic_tokens[i] != NULL; i++)
5577 num_ignore_generic_tokens++;
5579 // dynamically determine list of image tokens to be ignored
5580 num_ignore_image_tokens = num_ignore_generic_tokens;
5581 for (i = 0; image_config_vars[i].token != NULL; i++)
5582 num_ignore_image_tokens++;
5583 ignore_image_tokens =
5584 checked_malloc((num_ignore_image_tokens + 1) * sizeof(char *));
5585 for (i = 0; i < num_ignore_generic_tokens; i++)
5586 ignore_image_tokens[i] = ignore_generic_tokens[i];
5587 for (i = 0; i < num_ignore_image_tokens - num_ignore_generic_tokens; i++)
5588 ignore_image_tokens[num_ignore_generic_tokens + i] =
5589 image_config_vars[i].token;
5590 ignore_image_tokens[num_ignore_image_tokens] = NULL;
5592 // dynamically determine list of sound tokens to be ignored
5593 num_ignore_sound_tokens = num_ignore_generic_tokens;
5594 ignore_sound_tokens =
5595 checked_malloc((num_ignore_sound_tokens + 1) * sizeof(char *));
5596 for (i = 0; i < num_ignore_generic_tokens; i++)
5597 ignore_sound_tokens[i] = ignore_generic_tokens[i];
5598 ignore_sound_tokens[num_ignore_sound_tokens] = NULL;
5600 // dynamically determine list of music tokens to be ignored
5601 num_ignore_music_tokens = num_ignore_generic_tokens;
5602 ignore_music_tokens =
5603 checked_malloc((num_ignore_music_tokens + 1) * sizeof(char *));
5604 for (i = 0; i < num_ignore_generic_tokens; i++)
5605 ignore_music_tokens[i] = ignore_generic_tokens[i];
5606 ignore_music_tokens[num_ignore_music_tokens] = NULL;
5608 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5609 image_id_prefix[i] = element_info[i].token_name;
5610 for (i = 0; i < NUM_FONTS; i++)
5611 image_id_prefix[MAX_NUM_ELEMENTS + i] = font_info[i].token_name;
5612 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5613 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + i] =
5614 global_anim_info[i].token_name;
5615 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5617 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5618 sound_id_prefix[i] = element_info[i].token_name;
5619 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5620 sound_id_prefix[MAX_NUM_ELEMENTS + i] =
5621 get_string_in_brackets(element_info[i].class_name);
5622 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5623 sound_id_prefix[2 * MAX_NUM_ELEMENTS + i] =
5624 global_anim_info[i].token_name;
5625 sound_id_prefix[2 * MAX_NUM_ELEMENTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5627 for (i = 0; i < NUM_MUSIC_PREFIXES; i++)
5628 music_id_prefix[i] = music_prefix_info[i].prefix;
5629 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5630 music_id_prefix[NUM_MUSIC_PREFIXES + i] =
5631 global_anim_info[i].token_name;
5632 music_id_prefix[NUM_MUSIC_PREFIXES + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5634 for (i = 0; i < NUM_ACTIONS; i++)
5635 action_id_suffix[i] = element_action_info[i].suffix;
5636 action_id_suffix[NUM_ACTIONS] = NULL;
5638 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
5639 direction_id_suffix[i] = element_direction_info[i].suffix;
5640 direction_id_suffix[NUM_DIRECTIONS_FULL] = NULL;
5642 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
5643 special_id_suffix[i] = special_suffix_info[i].suffix;
5644 special_id_suffix[NUM_SPECIAL_GFX_ARGS] = NULL;
5646 for (i = 0; i < MAX_LEVELS; i++)
5647 level_id_suffix[i] = get_level_id_suffix(i);
5648 level_id_suffix[MAX_LEVELS] = NULL;
5650 InitImageList(image_config, NUM_IMAGE_FILES, image_config_suffix,
5651 image_id_prefix, action_id_suffix, direction_id_suffix,
5652 special_id_suffix, ignore_image_tokens);
5653 InitSoundList(sound_config, NUM_SOUND_FILES, sound_config_suffix,
5654 sound_id_prefix, action_id_suffix, dummy,
5655 special_id_suffix, ignore_sound_tokens);
5656 InitMusicList(music_config, NUM_MUSIC_FILES, music_config_suffix,
5657 music_id_prefix, action_id_suffix, special_id_suffix,
5658 level_id_suffix, ignore_music_tokens);
5661 static void InitMixer(void)
5668 static void InitVideoOverlay(void)
5670 // if virtual buttons are not loaded from setup file, repeat initializing
5671 // virtual buttons grid with default values now that video is initialized
5672 if (!setup.touch.grid_initialized)
5675 InitTileCursorInfo();
5679 void InitGfxBuffers(void)
5681 static int win_xsize_last = -1;
5682 static int win_ysize_last = -1;
5684 // create additional image buffers for double-buffering and cross-fading
5686 if (WIN_XSIZE != win_xsize_last || WIN_YSIZE != win_ysize_last)
5688 // used to temporarily store the backbuffer -- only re-create if changed
5689 ReCreateBitmap(&bitmap_db_store_1, WIN_XSIZE, WIN_YSIZE);
5690 ReCreateBitmap(&bitmap_db_store_2, WIN_XSIZE, WIN_YSIZE);
5692 win_xsize_last = WIN_XSIZE;
5693 win_ysize_last = WIN_YSIZE;
5696 ReCreateBitmap(&bitmap_db_field, FXSIZE, FYSIZE);
5697 ReCreateBitmap(&bitmap_db_door_1, 3 * DXSIZE, DYSIZE);
5698 ReCreateBitmap(&bitmap_db_door_2, 3 * VXSIZE, VYSIZE);
5700 // initialize screen properties
5701 InitGfxFieldInfo(SX, SY, SXSIZE, SYSIZE,
5702 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
5704 InitGfxDoor1Info(DX, DY, DXSIZE, DYSIZE);
5705 InitGfxDoor2Info(VX, VY, VXSIZE, VYSIZE);
5706 InitGfxDoor3Info(EX, EY, EXSIZE, EYSIZE);
5707 InitGfxWindowInfo(WIN_XSIZE, WIN_YSIZE);
5708 InitGfxScrollbufferInfo(FXSIZE, FYSIZE);
5709 InitGfxClipRegion(FALSE, -1, -1, -1, -1);
5711 // required if door size definitions have changed
5712 InitGraphicCompatibilityInfo_Doors();
5714 InitGfxBuffers_EM();
5715 InitGfxBuffers_SP();
5716 InitGfxBuffers_MM();
5719 static void InitGfx(void)
5721 struct GraphicInfo *graphic_info_last = graphic_info;
5722 char *filename_font_initial = NULL;
5723 char *filename_image_initial[NUM_INITIAL_IMAGES] = { NULL };
5724 char *image_token[NUM_INITIAL_IMAGES] =
5726 CONFIG_TOKEN_GLOBAL_BUSY_INITIAL,
5727 CONFIG_TOKEN_GLOBAL_BUSY,
5728 CONFIG_TOKEN_GLOBAL_BUSY_PLAYFIELD,
5729 CONFIG_TOKEN_BACKGROUND,
5730 CONFIG_TOKEN_BACKGROUND_LOADING_INITIAL,
5731 CONFIG_TOKEN_BACKGROUND_LOADING
5733 struct MenuPosInfo *init_busy[NUM_INITIAL_IMAGES_BUSY] =
5737 &init.busy_playfield
5739 Bitmap *bitmap_font_initial = NULL;
5740 int parameter[NUM_INITIAL_IMAGES][NUM_GFX_ARGS];
5743 // determine settings for initial font (for displaying startup messages)
5744 for (i = 0; image_config[i].token != NULL; i++)
5746 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5748 char font_token[128];
5751 sprintf(font_token, "%s_%d", CONFIG_TOKEN_FONT_INITIAL, j + 1);
5752 len_font_token = strlen(font_token);
5754 if (strEqual(image_config[i].token, font_token))
5756 filename_font_initial = image_config[i].value;
5758 else if (strlen(image_config[i].token) > len_font_token &&
5759 strncmp(image_config[i].token, font_token, len_font_token) == 0)
5761 if (strEqual(&image_config[i].token[len_font_token], ".x"))
5762 font_initial[j].src_x = atoi(image_config[i].value);
5763 else if (strEqual(&image_config[i].token[len_font_token], ".y"))
5764 font_initial[j].src_y = atoi(image_config[i].value);
5765 else if (strEqual(&image_config[i].token[len_font_token], ".width"))
5766 font_initial[j].width = atoi(image_config[i].value);
5767 else if (strEqual(&image_config[i].token[len_font_token], ".height"))
5768 font_initial[j].height = atoi(image_config[i].value);
5773 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5775 font_initial[j].num_chars = DEFAULT_NUM_CHARS_PER_FONT;
5776 font_initial[j].num_chars_per_line = DEFAULT_NUM_CHARS_PER_LINE;
5779 if (filename_font_initial == NULL) // should not happen
5780 Fail("cannot get filename for '%s'", CONFIG_TOKEN_FONT_INITIAL);
5783 InitGfxCustomArtworkInfo();
5784 InitGfxOtherSettings();
5786 InitGfxTileSizeInfo(TILESIZE, TILESIZE);
5788 bitmap_font_initial = LoadCustomImage(filename_font_initial);
5790 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5791 font_initial[j].bitmap = bitmap_font_initial;
5793 InitFontGraphicInfo();
5797 DrawInitTextHead("Loading graphics");
5799 InitMenuDesignSettings_Static();
5801 // initialize settings for initial images with default values
5802 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5803 for (j = 0; j < NUM_GFX_ARGS; j++)
5805 get_graphic_parameter_value(image_config_suffix[j].value,
5806 image_config_suffix[j].token,
5807 image_config_suffix[j].type);
5809 // read settings for initial images from default custom artwork config
5810 char *gfx_config_filename = getPath3(options.graphics_directory,
5812 GRAPHICSINFO_FILENAME);
5814 if (fileExists(gfx_config_filename))
5816 SetupFileHash *setup_file_hash = loadSetupFileHash(gfx_config_filename);
5818 if (setup_file_hash)
5820 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5822 char *filename = getHashEntry(setup_file_hash, image_token[i]);
5826 filename_image_initial[i] = getStringCopy(filename);
5828 for (j = 0; image_config_suffix[j].token != NULL; j++)
5830 int type = image_config_suffix[j].type;
5831 char *suffix = image_config_suffix[j].token;
5832 char *token = getStringCat2(image_token[i], suffix);
5833 char *value = getHashEntry(setup_file_hash, token);
5835 checked_free(token);
5839 get_graphic_parameter_value(value, suffix, type);
5844 // read values from custom graphics config file
5845 InitMenuDesignSettings_FromHash(setup_file_hash, FALSE);
5847 freeSetupFileHash(setup_file_hash);
5851 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5853 if (filename_image_initial[i] == NULL)
5855 int len_token = strlen(image_token[i]);
5857 // read settings for initial images from static default artwork config
5858 for (j = 0; image_config[j].token != NULL; j++)
5860 if (strEqual(image_config[j].token, image_token[i]))
5862 filename_image_initial[i] = getStringCopy(image_config[j].value);
5864 else if (strlen(image_config[j].token) > len_token &&
5865 strncmp(image_config[j].token, image_token[i], len_token) == 0)
5867 for (k = 0; image_config_suffix[k].token != NULL; k++)
5869 if (strEqual(&image_config[j].token[len_token],
5870 image_config_suffix[k].token))
5872 get_graphic_parameter_value(image_config[j].value,
5873 image_config_suffix[k].token,
5874 image_config_suffix[k].type);
5881 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5883 if (filename_image_initial[i] == NULL) // should not happen
5884 Fail("cannot get filename for '%s'", image_token[i]);
5886 image_initial[i].bitmaps =
5887 checked_calloc(sizeof(Bitmap *) * NUM_IMG_BITMAP_POINTERS);
5889 if (!strEqual(filename_image_initial[i], UNDEFINED_FILENAME))
5890 image_initial[i].bitmaps[IMG_BITMAP_STANDARD] =
5891 LoadCustomImage(filename_image_initial[i]);
5893 checked_free(filename_image_initial[i]);
5896 graphic_info = image_initial; // graphic == 0 => image_initial
5898 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5899 set_graphic_parameters_ext(i, parameter[i], image_initial[i].bitmaps);
5901 graphic_info = graphic_info_last;
5903 for (i = 0; i < NUM_INITIAL_IMAGES_BUSY; i++)
5905 // set image size for busy animations
5906 init_busy[i]->width = image_initial[i].width;
5907 init_busy[i]->height = image_initial[i].height;
5910 SetLoadingBackgroundImage();
5912 ClearRectangleOnBackground(window, 0, 0, WIN_XSIZE, WIN_YSIZE);
5914 InitGfxDrawBusyAnimFunction(DrawInitAnim);
5915 InitGfxDrawGlobalAnimFunction(DrawGlobalAnimations);
5916 InitGfxDrawGlobalBorderFunction(DrawMaskedBorderToTarget);
5917 InitGfxDrawTileCursorFunction(DrawTileCursor);
5919 gfx.fade_border_source_status = global.border_status;
5920 gfx.fade_border_target_status = global.border_status;
5921 gfx.masked_border_bitmap_ptr = backbuffer;
5923 // use copy of busy animation to prevent change while reloading artwork
5927 static void InitGfxBackground(void)
5929 fieldbuffer = bitmap_db_field;
5930 SetDrawtoField(DRAW_TO_BACKBUFFER);
5932 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
5934 redraw_mask = REDRAW_ALL;
5937 static void InitLevelInfo(void)
5939 LoadLevelInfo(); // global level info
5940 LoadLevelSetup_LastSeries(); // last played series info
5941 LoadLevelSetup_SeriesInfo(); // last played level info
5943 if (global.autoplay_leveldir &&
5944 global.autoplay_mode != AUTOPLAY_MODE_TEST)
5946 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
5947 global.autoplay_leveldir);
5948 if (leveldir_current == NULL)
5949 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
5952 SetLevelSetInfo(leveldir_current->identifier, level_nr);
5955 static void InitLevelArtworkInfo(void)
5957 LoadLevelArtworkInfo();
5960 static void InitImages(void)
5962 print_timestamp_init("InitImages");
5965 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
5966 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5967 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
5968 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5969 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
5970 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5971 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
5972 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5975 setLevelArtworkDir(artwork.gfx_first);
5978 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
5979 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5980 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
5981 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5982 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
5983 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5984 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
5985 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5989 Debug("init:InitImages", "InitImages for '%s' ['%s', '%s'] ['%s', '%s']",
5990 leveldir_current->identifier,
5991 artwork.gfx_current_identifier,
5992 artwork.gfx_current->identifier,
5993 leveldir_current->graphics_set,
5994 leveldir_current->graphics_path);
5997 UPDATE_BUSY_STATE();
5999 ReloadCustomImages();
6000 print_timestamp_time("ReloadCustomImages");
6002 UPDATE_BUSY_STATE();
6004 LoadCustomElementDescriptions();
6005 print_timestamp_time("LoadCustomElementDescriptions");
6007 UPDATE_BUSY_STATE();
6009 LoadMenuDesignSettings();
6010 print_timestamp_time("LoadMenuDesignSettings");
6012 UPDATE_BUSY_STATE();
6014 ReinitializeGraphics();
6015 print_timestamp_time("ReinitializeGraphics");
6017 LoadMenuDesignSettings_AfterGraphics();
6018 print_timestamp_time("LoadMenuDesignSettings_AfterGraphics");
6020 UPDATE_BUSY_STATE();
6022 print_timestamp_done("InitImages");
6025 static void InitSound(void)
6027 print_timestamp_init("InitSound");
6029 // set artwork path to send it to the sound server process
6030 setLevelArtworkDir(artwork.snd_first);
6032 InitReloadCustomSounds();
6033 print_timestamp_time("InitReloadCustomSounds");
6035 ReinitializeSounds();
6036 print_timestamp_time("ReinitializeSounds");
6038 print_timestamp_done("InitSound");
6041 static void InitMusic(void)
6043 print_timestamp_init("InitMusic");
6045 // set artwork path to send it to the sound server process
6046 setLevelArtworkDir(artwork.mus_first);
6048 InitReloadCustomMusic();
6049 print_timestamp_time("InitReloadCustomMusic");
6051 ReinitializeMusic();
6052 print_timestamp_time("ReinitializeMusic");
6054 print_timestamp_done("InitMusic");
6057 static void InitArtworkDone(void)
6059 if (program.headless)
6062 InitGlobalAnimations();
6065 static void InitNetworkSettings(void)
6067 boolean network_enabled = (options.network || setup.network_mode);
6068 char *network_server = (options.server_host != NULL ? options.server_host :
6069 setup.network_server_hostname);
6071 if (strEqual(network_server, STR_NETWORK_AUTO_DETECT))
6072 network_server = NULL;
6074 InitNetworkInfo(network_enabled,
6078 options.server_port);
6081 void InitNetworkServer(void)
6083 if (!network.enabled || network.connected)
6086 LimitScreenUpdates(FALSE);
6088 if (game_status == GAME_MODE_LOADING)
6091 if (!ConnectToServer(network.server_host, network.server_port))
6093 network.enabled = FALSE;
6095 setup.network_mode = FALSE;
6099 SendToServer_ProtocolVersion();
6100 SendToServer_PlayerName(setup.player_name);
6101 SendToServer_NrWanted(setup.network_player_nr + 1);
6103 network.connected = TRUE;
6106 // short time to recognize result of network initialization
6107 if (game_status == GAME_MODE_LOADING)
6108 Delay_WithScreenUpdates(1000);
6111 static boolean CheckArtworkConfigForCustomElements(char *filename)
6113 SetupFileHash *setup_file_hash;
6114 boolean redefined_ce_found = FALSE;
6116 // !!! CACHE THIS BY USING HASH 'filename' => 'true/false' !!!
6118 if ((setup_file_hash = loadSetupFileHash(filename)) != NULL)
6120 BEGIN_HASH_ITERATION(setup_file_hash, itr)
6122 char *token = HASH_ITERATION_TOKEN(itr);
6124 if (strPrefix(token, "custom_"))
6126 redefined_ce_found = TRUE;
6131 END_HASH_ITERATION(setup_file_hash, itr)
6133 freeSetupFileHash(setup_file_hash);
6136 return redefined_ce_found;
6139 static boolean CheckArtworkTypeForRedefinedCustomElements(int type)
6141 char *filename_base, *filename_local;
6142 boolean redefined_ce_found = FALSE;
6144 setLevelArtworkDir(ARTWORK_FIRST_NODE(artwork, type));
6147 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6148 "leveldir_current->identifier == '%s'",
6149 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
6150 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6151 "leveldir_current->graphics_path == '%s'",
6152 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
6153 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6154 "leveldir_current->graphics_set == '%s'",
6155 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
6156 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6157 "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
6158 leveldir_current == NULL ? "[NULL]" :
6159 LEVELDIR_ARTWORK_SET(leveldir_current, type));
6162 // first look for special artwork configured in level series config
6163 filename_base = getCustomArtworkLevelConfigFilename(type);
6166 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6167 "filename_base == '%s'", filename_base);
6170 if (fileExists(filename_base))
6171 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_base);
6173 filename_local = getCustomArtworkConfigFilename(type);
6176 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6177 "filename_local == '%s'", filename_local);
6180 if (filename_local != NULL && !strEqual(filename_base, filename_local))
6181 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_local);
6184 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6185 "redefined_ce_found == %d", redefined_ce_found);
6188 return redefined_ce_found;
6191 static void InitOverrideArtwork(void)
6193 boolean redefined_ce_found = FALSE;
6195 // to check if this level set redefines any CEs, do not use overriding
6196 gfx.override_level_graphics = FALSE;
6197 gfx.override_level_sounds = FALSE;
6198 gfx.override_level_music = FALSE;
6200 // now check if this level set has definitions for custom elements
6201 if (setup.override_level_graphics == AUTO ||
6202 setup.override_level_sounds == AUTO ||
6203 setup.override_level_music == AUTO)
6204 redefined_ce_found =
6205 (CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_GRAPHICS) |
6206 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_SOUNDS) |
6207 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_MUSIC));
6210 Debug("init:InitOverrideArtwork", "redefined_ce_found == %d",
6211 redefined_ce_found);
6214 if (redefined_ce_found)
6216 // this level set has CE definitions: change "AUTO" to "FALSE"
6217 gfx.override_level_graphics = (setup.override_level_graphics == TRUE);
6218 gfx.override_level_sounds = (setup.override_level_sounds == TRUE);
6219 gfx.override_level_music = (setup.override_level_music == TRUE);
6223 // this level set has no CE definitions: change "AUTO" to "TRUE"
6224 gfx.override_level_graphics = (setup.override_level_graphics != FALSE);
6225 gfx.override_level_sounds = (setup.override_level_sounds != FALSE);
6226 gfx.override_level_music = (setup.override_level_music != FALSE);
6230 Debug("init:InitOverrideArtwork", "%d, %d, %d",
6231 gfx.override_level_graphics,
6232 gfx.override_level_sounds,
6233 gfx.override_level_music);
6237 static char *setNewArtworkIdentifier(int type)
6239 static char *last_leveldir_identifier[3] = { NULL, NULL, NULL };
6240 static char *last_artwork_identifier[3] = { NULL, NULL, NULL };
6241 static boolean last_override_level_artwork[3] = { FALSE, FALSE, FALSE };
6242 static boolean last_has_custom_artwork_set[3] = { FALSE, FALSE, FALSE };
6243 static boolean initialized[3] = { FALSE, FALSE, FALSE };
6244 TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type);
6245 boolean setup_override_artwork = GFX_OVERRIDE_ARTWORK(type);
6246 char *setup_artwork_set = SETUP_ARTWORK_SET(setup, type);
6247 char *leveldir_identifier = leveldir_current->identifier;
6248 // !!! setLevelArtworkDir() should be moved to an earlier stage !!!
6249 char *leveldir_artwork_set = setLevelArtworkDir(artwork_first_node);
6250 boolean has_level_artwork_set = (leveldir_artwork_set != NULL);
6251 TreeInfo *custom_artwork_set =
6252 getTreeInfoFromIdentifier(artwork_first_node, leveldir_identifier);
6253 boolean has_custom_artwork_set = (custom_artwork_set != NULL);
6254 char *artwork_current_identifier;
6255 char *artwork_new_identifier = NULL; // default: nothing has changed
6257 // leveldir_current may be invalid (level group, parent link)
6258 if (!validLevelSeries(leveldir_current))
6261 /* 1st step: determine artwork set to be activated in descending order:
6262 --------------------------------------------------------------------
6263 1. setup artwork (when configured to override everything else)
6264 2. artwork set configured in "levelinfo.conf" of current level set
6265 (artwork in level directory will have priority when loading later)
6266 3. artwork in level directory (stored in artwork sub-directory)
6267 4. setup artwork (currently configured in setup menu) */
6269 if (setup_override_artwork)
6270 artwork_current_identifier = setup_artwork_set;
6271 else if (has_level_artwork_set)
6272 artwork_current_identifier = leveldir_artwork_set;
6273 else if (has_custom_artwork_set)
6274 artwork_current_identifier = leveldir_identifier;
6276 artwork_current_identifier = setup_artwork_set;
6278 /* 2nd step: check if it is really needed to reload artwork set
6279 ------------------------------------------------------------ */
6281 // ---------- reload if level set and also artwork set has changed ----------
6282 if (last_leveldir_identifier[type] != leveldir_identifier &&
6283 (last_has_custom_artwork_set[type] || has_custom_artwork_set))
6284 artwork_new_identifier = artwork_current_identifier;
6286 last_leveldir_identifier[type] = leveldir_identifier;
6287 last_has_custom_artwork_set[type] = has_custom_artwork_set;
6289 // ---------- reload if "override artwork" setting has changed --------------
6290 if (last_override_level_artwork[type] != setup_override_artwork)
6291 artwork_new_identifier = artwork_current_identifier;
6293 last_override_level_artwork[type] = setup_override_artwork;
6295 // ---------- reload if current artwork identifier has changed --------------
6296 if (!strEqual(last_artwork_identifier[type], artwork_current_identifier))
6297 artwork_new_identifier = artwork_current_identifier;
6299 // (we cannot compare string pointers here, so copy string content itself)
6300 setString(&last_artwork_identifier[type], artwork_current_identifier);
6302 // ---------- set new artwork identifier ----------
6303 *(ARTWORK_CURRENT_IDENTIFIER_PTR(artwork, type)) = artwork_current_identifier;
6305 // ---------- do not reload directly after starting -------------------------
6306 if (!initialized[type])
6307 artwork_new_identifier = NULL;
6309 initialized[type] = TRUE;
6311 return artwork_new_identifier;
6314 static void InitArtworkIdentifier(void)
6316 setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6317 setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6318 setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6321 void ReloadCustomArtwork(int force_reload)
6323 int last_game_status = game_status; // save current game status
6324 char *gfx_new_identifier;
6325 char *snd_new_identifier;
6326 char *mus_new_identifier;
6327 boolean force_reload_gfx = (force_reload & (1 << ARTWORK_TYPE_GRAPHICS));
6328 boolean force_reload_snd = (force_reload & (1 << ARTWORK_TYPE_SOUNDS));
6329 boolean force_reload_mus = (force_reload & (1 << ARTWORK_TYPE_MUSIC));
6330 boolean reload_needed;
6332 InitOverrideArtwork();
6334 AdjustGraphicsForEMC();
6335 AdjustSoundsForEMC();
6337 gfx_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6338 snd_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6339 mus_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6341 reload_needed = (gfx_new_identifier != NULL || force_reload_gfx ||
6342 snd_new_identifier != NULL || force_reload_snd ||
6343 mus_new_identifier != NULL || force_reload_mus);
6348 print_timestamp_init("ReloadCustomArtwork");
6350 SetGameStatus(GAME_MODE_LOADING);
6352 FadeOut(REDRAW_ALL);
6354 SetLoadingBackgroundImage();
6356 ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6357 print_timestamp_time("ClearRectangleOnBackground");
6361 UPDATE_BUSY_STATE();
6363 InitMissingFileHash();
6365 if (gfx_new_identifier != NULL || force_reload_gfx)
6368 Debug("init:ReloadCustomArtwork",
6369 "RELOADING GRAPHICS '%s' -> '%s' ['%s', '%s']",
6370 artwork.gfx_current_identifier,
6372 artwork.gfx_current->identifier,
6373 leveldir_current->graphics_set);
6377 print_timestamp_time("InitImages");
6380 if (snd_new_identifier != NULL || force_reload_snd)
6383 print_timestamp_time("InitSound");
6386 if (mus_new_identifier != NULL || force_reload_mus)
6389 print_timestamp_time("InitMusic");
6394 SetGameStatus(last_game_status); // restore current game status
6396 FadeOut(REDRAW_ALL);
6398 RedrawGlobalBorder();
6400 // force redraw of (open or closed) door graphics
6401 SetDoorState(DOOR_OPEN_ALL);
6402 CloseDoor(DOOR_CLOSE_ALL | DOOR_NO_DELAY);
6404 FadeSetEnterScreen();
6405 FadeSkipNextFadeOut();
6407 print_timestamp_done("ReloadCustomArtwork");
6409 LimitScreenUpdates(FALSE);
6412 void KeyboardAutoRepeatOffUnlessAutoplay(void)
6414 if (global.autoplay_leveldir == NULL)
6415 KeyboardAutoRepeatOff();
6418 void DisplayExitMessage(char *format, va_list ap)
6420 // also check for initialized video (headless flag may be temporarily unset)
6421 if (program.headless || !video.initialized)
6424 // check if draw buffer and fonts for exit message are already available
6425 if (drawto == NULL || font_initial[NUM_INITIAL_FONTS - 1].bitmap == NULL)
6428 int font_1 = FC_RED;
6429 int font_2 = FC_YELLOW;
6430 int font_3 = FC_BLUE;
6431 int font_width = getFontWidth(font_2);
6432 int font_height = getFontHeight(font_2);
6435 int sxsize = WIN_XSIZE - 2 * sx;
6436 int sysize = WIN_YSIZE - 2 * sy;
6437 int line_length = sxsize / font_width;
6438 int max_lines = sysize / font_height;
6439 int num_lines_printed;
6443 gfx.sxsize = sxsize;
6444 gfx.sysize = sysize;
6448 ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6450 DrawTextSCentered(sy, font_1, "Fatal error:");
6451 sy += 3 * font_height;;
6454 DrawTextBufferVA(sx, sy, format, ap, font_2,
6455 line_length, line_length, max_lines,
6456 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6457 sy += (num_lines_printed + 3) * font_height;
6459 DrawTextSCentered(sy, font_1, "For details, see the following error file:");
6460 sy += 3 * font_height;
6463 DrawTextBuffer(sx, sy, program.log_filename, font_2,
6464 line_length, line_length, max_lines,
6465 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6467 DrawTextSCentered(SYSIZE - 20, font_3, "Press any key or button to exit");
6469 redraw_mask = REDRAW_ALL;
6471 // force drawing exit message even if screen updates are currently limited
6472 LimitScreenUpdates(FALSE);
6476 // deactivate toons on error message screen
6477 setup.toons = FALSE;
6479 WaitForEventToContinue();
6483 // ============================================================================
6485 // ============================================================================
6489 print_timestamp_init("OpenAll");
6491 SetGameStatus(GAME_MODE_LOADING);
6495 InitGlobal(); // initialize some global variables
6497 InitRND(NEW_RANDOMIZE);
6498 InitSimpleRandom(NEW_RANDOMIZE);
6499 InitBetterRandom(NEW_RANDOMIZE);
6501 InitMissingFileHash();
6503 print_timestamp_time("[init global stuff]");
6507 print_timestamp_time("[init setup/config stuff (1)]");
6509 if (options.execute_command)
6510 Execute_Command(options.execute_command);
6512 InitNetworkSettings();
6516 if (network.serveronly)
6518 #if defined(PLATFORM_UNIX)
6519 NetworkServer(network.server_port, TRUE);
6521 Warn("networking only supported in Unix version");
6524 exit(0); // never reached, server loops forever
6528 print_timestamp_time("[init setup/config stuff (2)]");
6530 print_timestamp_time("[init setup/config stuff (3)]");
6531 InitArtworkInfo(); // needed before loading gfx, sound & music
6532 print_timestamp_time("[init setup/config stuff (4)]");
6533 InitArtworkConfig(); // needed before forking sound child process
6534 print_timestamp_time("[init setup/config stuff (5)]");
6536 print_timestamp_time("[init setup/config stuff (6)]");
6540 print_timestamp_time("[init setup/config stuff]");
6542 InitVideoDefaults();
6544 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
6547 InitEventFilter(FilterMouseMotionEvents);
6549 print_timestamp_time("[init video stuff]");
6551 InitElementPropertiesStatic();
6552 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
6553 InitElementPropertiesGfxElement();
6555 print_timestamp_time("[init element properties stuff]");
6559 print_timestamp_time("InitGfx");
6562 print_timestamp_time("InitLevelInfo");
6564 InitLevelArtworkInfo();
6565 print_timestamp_time("InitLevelArtworkInfo");
6567 InitOverrideArtwork(); // needs to know current level directory
6568 print_timestamp_time("InitOverrideArtwork");
6570 InitArtworkIdentifier(); // needs to know current level directory
6571 print_timestamp_time("InitArtworkIdentifier");
6573 InitImages(); // needs to know current level directory
6574 print_timestamp_time("InitImages");
6576 InitSound(); // needs to know current level directory
6577 print_timestamp_time("InitSound");
6579 InitMusic(); // needs to know current level directory
6580 print_timestamp_time("InitMusic");
6584 InitGfxBackground();
6590 if (global.autoplay_leveldir)
6595 else if (global.patchtapes_leveldir)
6600 else if (global.convert_leveldir)
6605 else if (global.dumplevel_leveldir)
6610 else if (global.dumptape_leveldir)
6615 else if (global.create_sketch_images_dir)
6617 CreateLevelSketchImages();
6620 else if (global.create_collect_images_dir)
6622 CreateCollectElementImages();
6626 InitNetworkServer();
6628 SetGameStatus(GAME_MODE_MAIN);
6630 FadeSetEnterScreen();
6631 if (!(fading.fade_mode & FADE_TYPE_TRANSFORM))
6632 FadeSkipNextFadeOut();
6634 print_timestamp_time("[post-artwork]");
6636 print_timestamp_done("OpenAll");
6638 if (setup.ask_for_remaining_tapes)
6639 setup.ask_for_uploading_tapes = TRUE;
6644 Debug("internal:path", "SDL_GetBasePath() == '%s'",
6646 Debug("internal:path", "SDL_GetPrefPath() == '%s'",
6647 SDL_GetPrefPath("artsoft", "rocksndiamonds"));
6648 #if defined(PLATFORM_ANDROID)
6649 Debug("internal:path", "SDL_AndroidGetInternalStoragePath() == '%s'",
6650 SDL_AndroidGetInternalStoragePath());
6651 Debug("internal:path", "SDL_AndroidGetExternalStoragePath() == '%s'",
6652 SDL_AndroidGetExternalStoragePath());
6653 Debug("internal:path", "SDL_AndroidGetExternalStorageState() == '%s'",
6654 (SDL_AndroidGetExternalStorageState() &
6655 SDL_ANDROID_EXTERNAL_STORAGE_WRITE ? "writable" :
6656 SDL_AndroidGetExternalStorageState() &
6657 SDL_ANDROID_EXTERNAL_STORAGE_READ ? "readable" : "not available"));
6662 static boolean WaitForApiThreads(void)
6664 DelayCounter thread_delay = { 10000 };
6666 if (program.api_thread_count == 0)
6669 // deactivate global animations (not accessible in game state "loading")
6670 setup.toons = FALSE;
6672 // set game state to "loading" to be able to show busy animation
6673 SetGameStatus(GAME_MODE_LOADING);
6675 ResetDelayCounter(&thread_delay);
6677 // wait for threads to finish (and fail on timeout)
6678 while (program.api_thread_count > 0)
6680 if (DelayReached(&thread_delay))
6682 Error("failed waiting for threads - TIMEOUT");
6687 UPDATE_BUSY_STATE();
6695 void CloseAllAndExit(int exit_value)
6697 WaitForApiThreads();
6702 CloseAudio(); // called after freeing sounds (needed for SDL)
6710 // set a flag to tell the network server thread to quit and wait for it
6711 // using SDL_WaitThread()
6713 // Code used with SDL 1.2:
6714 // if (network.server_thread) // terminate network server
6715 // SDL_KillThread(network.server_thread);
6717 CloseVideoDisplay();
6718 ClosePlatformDependentStuff();
6720 if (exit_value != 0 && !options.execute_command)
6722 // fall back to default level set (current set may have caused an error)
6723 SaveLevelSetup_LastSeries_Deactivate();
6725 // tell user where to find error log file which may contain more details
6726 // (error notification now directly displayed on screen inside R'n'D
6727 // NotifyUserAboutErrorFile(); // currently only works for Windows