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 p1 = GFX_ARG_DRAW_MASKED;
726 int p2 = GFX_ARG_DRAW_ORDER;
727 int draw_masked = get_graphic_parameter_value(parameter_raw[p1],
728 image_config_suffix[p1].token,
729 image_config_suffix[p1].type);
730 int draw_order = get_graphic_parameter_value(parameter_raw[p2],
731 image_config_suffix[p2].token,
732 image_config_suffix[p2].type);
734 // if ".draw_masked" parameter is undefined, use default value "TRUE"
735 if (draw_masked == ARG_UNDEFINED_VALUE)
736 g->draw_masked = TRUE;
738 // if ".draw_order" parameter is undefined, set back to "undefined"
739 // (used to be able to inherit draw order from main animation later)
740 if (draw_order == ARG_UNDEFINED_VALUE)
741 g->draw_order = ARG_UNDEFINED_VALUE;
745 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
746 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
747 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
748 if (global_anim_info[i].graphic[j][k] != IMG_UNDEFINED &&
749 graphic_info[global_anim_info[i].graphic[j][k]].bitmap != NULL)
750 Debug("init:InitGlobalAnimGraphicInfo",
751 "anim %d, part %d, mode %d => %d",
752 i, j, k, global_anim_info[i].graphic[j][k]);
756 static void InitGlobalAnimSoundInfo(void)
758 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
759 int num_property_mappings = getSoundListPropertyMappingSize();
762 // always start with reliable default values (no global animation sounds)
763 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
764 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
765 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
766 global_anim_info[i].sound[j][k] = SND_UNDEFINED;
768 // initialize global animation sound definitions from dynamic configuration
769 for (i = 0; i < num_property_mappings; i++)
771 int anim_nr = property_mapping[i].base_index - 2 * MAX_NUM_ELEMENTS;
772 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
773 int special = property_mapping[i].ext3_index;
774 int sound = property_mapping[i].artwork_index;
776 // sound uses control definition; map it to position of graphic (artwork)
777 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
779 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
782 // set animation part to base part, if not specified
783 if (!IS_GLOBAL_ANIM_PART(part_nr))
784 part_nr = GLOBAL_ANIM_ID_PART_BASE;
786 // set animation screen to default, if not specified
787 if (!IS_SPECIAL_GFX_ARG(special))
788 special = GFX_SPECIAL_ARG_DEFAULT;
790 global_anim_info[anim_nr].sound[part_nr][special] = sound;
794 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
795 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
796 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
797 if (global_anim_info[i].sound[j][k] != SND_UNDEFINED)
798 Debug("init:InitGlobalAnimSoundInfo",
799 "anim %d, part %d, mode %d => %d",
800 i, j, k, global_anim_info[i].sound[j][k]);
804 static void InitGlobalAnimMusicInfo(void)
806 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
807 int num_property_mappings = getMusicListPropertyMappingSize();
810 // always start with reliable default values (no global animation music)
811 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
812 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
813 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
814 global_anim_info[i].music[j][k] = MUS_UNDEFINED;
816 // initialize global animation music definitions from dynamic configuration
817 for (i = 0; i < num_property_mappings; i++)
819 int anim_nr = property_mapping[i].base_index - NUM_MUSIC_PREFIXES;
820 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
821 int special = property_mapping[i].ext2_index;
822 int music = property_mapping[i].artwork_index;
824 // music uses control definition; map it to position of graphic (artwork)
825 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
827 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
830 // set animation part to base part, if not specified
831 if (!IS_GLOBAL_ANIM_PART(part_nr))
832 part_nr = GLOBAL_ANIM_ID_PART_BASE;
834 // set animation screen to default, if not specified
835 if (!IS_SPECIAL_GFX_ARG(special))
836 special = GFX_SPECIAL_ARG_DEFAULT;
838 global_anim_info[anim_nr].music[part_nr][special] = music;
842 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
843 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
844 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
845 if (global_anim_info[i].music[j][k] != MUS_UNDEFINED)
846 Debug("init:InitGlobalAnimMusicInfo",
847 "anim %d, part %d, mode %d => %d",
848 i, j, k, global_anim_info[i].music[j][k]);
852 static void InitElementGraphicInfo(void)
854 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
855 int num_property_mappings = getImageListPropertyMappingSize();
858 if (graphic_info == NULL) // still at startup phase
861 // set values to -1 to identify later as "uninitialized" values
862 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
864 for (act = 0; act < NUM_ACTIONS; act++)
866 element_info[i].graphic[act] = -1;
867 element_info[i].crumbled[act] = -1;
869 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
871 element_info[i].direction_graphic[act][dir] = -1;
872 element_info[i].direction_crumbled[act][dir] = -1;
879 // initialize normal element/graphic mapping from static configuration
880 for (i = 0; element_to_graphic[i].element > -1; i++)
882 int element = element_to_graphic[i].element;
883 int action = element_to_graphic[i].action;
884 int direction = element_to_graphic[i].direction;
885 boolean crumbled = element_to_graphic[i].crumbled;
886 int graphic = element_to_graphic[i].graphic;
887 int base_graphic = el2baseimg(element);
889 if (graphic_info[graphic].bitmap == NULL)
892 if ((action > -1 || direction > -1 || crumbled == TRUE) &&
895 boolean base_redefined =
896 getImageListEntryFromImageID(base_graphic)->redefined;
897 boolean act_dir_redefined =
898 getImageListEntryFromImageID(graphic)->redefined;
900 /* if the base graphic ("emerald", for example) has been redefined,
901 but not the action graphic ("emerald.falling", for example), do not
902 use an existing (in this case considered obsolete) action graphic
903 anymore, but use the automatically determined default graphic */
904 if (base_redefined && !act_dir_redefined)
909 action = ACTION_DEFAULT;
914 element_info[element].direction_crumbled[action][direction] = graphic;
916 element_info[element].crumbled[action] = graphic;
921 element_info[element].direction_graphic[action][direction] = graphic;
923 element_info[element].graphic[action] = graphic;
927 // initialize normal element/graphic mapping from dynamic configuration
928 for (i = 0; i < num_property_mappings; i++)
930 int element = property_mapping[i].base_index;
931 int action = property_mapping[i].ext1_index;
932 int direction = property_mapping[i].ext2_index;
933 int special = property_mapping[i].ext3_index;
934 int graphic = property_mapping[i].artwork_index;
935 boolean crumbled = FALSE;
937 if (special == GFX_SPECIAL_ARG_CRUMBLED)
943 if (graphic_info[graphic].bitmap == NULL)
946 if (element >= MAX_NUM_ELEMENTS || special != -1)
950 action = ACTION_DEFAULT;
955 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
956 element_info[element].direction_crumbled[action][dir] = -1;
959 element_info[element].direction_crumbled[action][direction] = graphic;
961 element_info[element].crumbled[action] = graphic;
966 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
967 element_info[element].direction_graphic[action][dir] = -1;
970 element_info[element].direction_graphic[action][direction] = graphic;
972 element_info[element].graphic[action] = graphic;
976 // now copy all graphics that are defined to be cloned from other graphics
977 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
979 int graphic = element_info[i].graphic[ACTION_DEFAULT];
980 int crumbled_like, diggable_like;
985 crumbled_like = graphic_info[graphic].crumbled_like;
986 diggable_like = graphic_info[graphic].diggable_like;
988 if (crumbled_like != -1 && element_info[i].crumbled[ACTION_DEFAULT] == -1)
990 for (act = 0; act < NUM_ACTIONS; act++)
991 element_info[i].crumbled[act] =
992 element_info[crumbled_like].crumbled[act];
993 for (act = 0; act < NUM_ACTIONS; act++)
994 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
995 element_info[i].direction_crumbled[act][dir] =
996 element_info[crumbled_like].direction_crumbled[act][dir];
999 if (diggable_like != -1 && element_info[i].graphic[ACTION_DIGGING] == -1)
1001 element_info[i].graphic[ACTION_DIGGING] =
1002 element_info[diggable_like].graphic[ACTION_DIGGING];
1003 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1004 element_info[i].direction_graphic[ACTION_DIGGING][dir] =
1005 element_info[diggable_like].direction_graphic[ACTION_DIGGING][dir];
1009 // set hardcoded definitions for some runtime elements without graphic
1010 element_info[EL_AMOEBA_TO_DIAMOND].graphic[ACTION_DEFAULT] = IMG_AMOEBA_DEAD;
1012 // set hardcoded definitions for some internal elements without graphic
1013 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1015 if (IS_EDITOR_CASCADE_INACTIVE(i))
1016 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST;
1017 else if (IS_EDITOR_CASCADE_ACTIVE(i))
1018 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST_ACTIVE;
1021 // now set all undefined/invalid graphics to -1 to set to default after it
1022 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1024 for (act = 0; act < NUM_ACTIONS; act++)
1028 graphic = element_info[i].graphic[act];
1029 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1030 element_info[i].graphic[act] = -1;
1032 graphic = element_info[i].crumbled[act];
1033 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1034 element_info[i].crumbled[act] = -1;
1036 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1038 graphic = element_info[i].direction_graphic[act][dir];
1039 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1040 element_info[i].direction_graphic[act][dir] = -1;
1042 graphic = element_info[i].direction_crumbled[act][dir];
1043 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1044 element_info[i].direction_crumbled[act][dir] = -1;
1049 UPDATE_BUSY_STATE();
1051 // adjust graphics with 2nd tile for movement according to direction
1052 // (do this before correcting '-1' values to minimize calculations)
1053 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1055 for (act = 0; act < NUM_ACTIONS; act++)
1057 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1059 int graphic = element_info[i].direction_graphic[act][dir];
1060 int move_dir = (act == ACTION_FALLING ? MV_BIT_DOWN : dir);
1062 if (act == ACTION_FALLING) // special case
1063 graphic = element_info[i].graphic[act];
1065 if (graphic != -1 &&
1066 graphic_info[graphic].double_movement &&
1067 graphic_info[graphic].swap_double_tiles != 0)
1069 struct GraphicInfo *g = &graphic_info[graphic];
1070 int src_x_front = g->src_x;
1071 int src_y_front = g->src_y;
1072 int src_x_back = g->src_x + g->offset2_x;
1073 int src_y_back = g->src_y + g->offset2_y;
1074 boolean frames_are_ordered_diagonally = (g->offset_x != 0 &&
1076 boolean front_is_left_or_upper = (src_x_front < src_x_back ||
1077 src_y_front < src_y_back);
1078 boolean swap_movement_tiles_always = (g->swap_double_tiles == 1);
1079 boolean swap_movement_tiles_autodetected =
1080 (!frames_are_ordered_diagonally &&
1081 ((move_dir == MV_BIT_LEFT && !front_is_left_or_upper) ||
1082 (move_dir == MV_BIT_UP && !front_is_left_or_upper) ||
1083 (move_dir == MV_BIT_RIGHT && front_is_left_or_upper) ||
1084 (move_dir == MV_BIT_DOWN && front_is_left_or_upper)));
1086 // swap frontside and backside graphic tile coordinates, if needed
1087 if (swap_movement_tiles_always || swap_movement_tiles_autodetected)
1089 // get current (wrong) backside tile coordinates
1090 getGraphicSourceXY(graphic, 0, &src_x_back, &src_y_back, TRUE);
1092 // set frontside tile coordinates to backside tile coordinates
1093 g->src_x = src_x_back;
1094 g->src_y = src_y_back;
1096 // invert tile offset to point to new backside tile coordinates
1100 // do not swap front and backside tiles again after correction
1101 g->swap_double_tiles = 0;
1108 UPDATE_BUSY_STATE();
1110 // now set all '-1' values to element specific default values
1111 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1113 int default_graphic = element_info[i].graphic[ACTION_DEFAULT];
1114 int default_crumbled = element_info[i].crumbled[ACTION_DEFAULT];
1115 int default_direction_graphic[NUM_DIRECTIONS_FULL];
1116 int default_direction_crumbled[NUM_DIRECTIONS_FULL];
1118 if (default_graphic == -1)
1119 default_graphic = IMG_UNKNOWN;
1121 if (default_crumbled == -1)
1122 default_crumbled = default_graphic;
1124 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1126 default_direction_graphic[dir] =
1127 element_info[i].direction_graphic[ACTION_DEFAULT][dir];
1128 default_direction_crumbled[dir] =
1129 element_info[i].direction_crumbled[ACTION_DEFAULT][dir];
1131 if (default_direction_graphic[dir] == -1)
1132 default_direction_graphic[dir] = default_graphic;
1134 if (default_direction_crumbled[dir] == -1)
1135 default_direction_crumbled[dir] = default_direction_graphic[dir];
1138 for (act = 0; act < NUM_ACTIONS; act++)
1140 boolean act_remove = ((IS_DIGGABLE(i) && act == ACTION_DIGGING) ||
1141 (IS_SNAPPABLE(i) && act == ACTION_SNAPPING) ||
1142 (IS_COLLECTIBLE(i) && act == ACTION_COLLECTING));
1143 boolean act_turning = (act == ACTION_TURNING_FROM_LEFT ||
1144 act == ACTION_TURNING_FROM_RIGHT ||
1145 act == ACTION_TURNING_FROM_UP ||
1146 act == ACTION_TURNING_FROM_DOWN);
1148 // generic default action graphic (defined by "[default]" directive)
1149 int default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1150 int default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1151 int default_remove_graphic = IMG_EMPTY;
1153 if (act_remove && default_action_graphic != -1)
1154 default_remove_graphic = default_action_graphic;
1156 // look for special default action graphic (classic game specific)
1157 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].graphic[act] != -1)
1158 default_action_graphic = element_info[EL_BD_DEFAULT].graphic[act];
1159 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].graphic[act] != -1)
1160 default_action_graphic = element_info[EL_SP_DEFAULT].graphic[act];
1161 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].graphic[act] != -1)
1162 default_action_graphic = element_info[EL_SB_DEFAULT].graphic[act];
1163 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].graphic[act] != -1)
1164 default_action_graphic = element_info[EL_MM_DEFAULT].graphic[act];
1166 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].crumbled[act] != -1)
1167 default_action_crumbled = element_info[EL_BD_DEFAULT].crumbled[act];
1168 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].crumbled[act] != -1)
1169 default_action_crumbled = element_info[EL_SP_DEFAULT].crumbled[act];
1170 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].crumbled[act] != -1)
1171 default_action_crumbled = element_info[EL_SB_DEFAULT].crumbled[act];
1172 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].crumbled[act] != -1)
1173 default_action_crumbled = element_info[EL_MM_DEFAULT].crumbled[act];
1175 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
1176 // !!! make this better !!!
1177 if (i == EL_EMPTY_SPACE)
1179 default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1180 default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1183 if (default_action_graphic == -1)
1184 default_action_graphic = default_graphic;
1186 if (default_action_crumbled == -1)
1187 default_action_crumbled = default_action_graphic;
1189 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1191 // use action graphic as the default direction graphic, if undefined
1192 int default_action_direction_graphic = element_info[i].graphic[act];
1193 int default_action_direction_crumbled = element_info[i].crumbled[act];
1195 // no graphic for current action -- use default direction graphic
1196 if (default_action_direction_graphic == -1)
1197 default_action_direction_graphic =
1198 (act_remove ? default_remove_graphic :
1200 element_info[i].direction_graphic[ACTION_TURNING][dir] :
1201 default_action_graphic != default_graphic ?
1202 default_action_graphic :
1203 default_direction_graphic[dir]);
1205 if (element_info[i].direction_graphic[act][dir] == -1)
1206 element_info[i].direction_graphic[act][dir] =
1207 default_action_direction_graphic;
1209 if (default_action_direction_crumbled == -1)
1210 default_action_direction_crumbled =
1211 element_info[i].direction_graphic[act][dir];
1213 if (element_info[i].direction_crumbled[act][dir] == -1)
1214 element_info[i].direction_crumbled[act][dir] =
1215 default_action_direction_crumbled;
1218 // no graphic for this specific action -- use default action graphic
1219 if (element_info[i].graphic[act] == -1)
1220 element_info[i].graphic[act] =
1221 (act_remove ? default_remove_graphic :
1222 act_turning ? element_info[i].graphic[ACTION_TURNING] :
1223 default_action_graphic);
1225 if (element_info[i].crumbled[act] == -1)
1226 element_info[i].crumbled[act] = element_info[i].graphic[act];
1230 UPDATE_BUSY_STATE();
1233 static void InitElementSpecialGraphicInfo(void)
1235 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
1236 int num_property_mappings = getImageListPropertyMappingSize();
1239 // always start with reliable default values
1240 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1241 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1242 element_info[i].special_graphic[j] =
1243 element_info[i].graphic[ACTION_DEFAULT];
1245 // initialize special element/graphic mapping from static configuration
1246 for (i = 0; element_to_special_graphic[i].element > -1; i++)
1248 int element = element_to_special_graphic[i].element;
1249 int special = element_to_special_graphic[i].special;
1250 int graphic = element_to_special_graphic[i].graphic;
1251 int base_graphic = el2baseimg(element);
1252 boolean base_redefined =
1253 getImageListEntryFromImageID(base_graphic)->redefined;
1254 boolean special_redefined =
1255 getImageListEntryFromImageID(graphic)->redefined;
1257 /* if the base graphic ("emerald", for example) has been redefined,
1258 but not the special graphic ("emerald.EDITOR", for example), do not
1259 use an existing (in this case considered obsolete) special graphic
1260 anymore, but use the automatically created (down-scaled) graphic */
1261 if (base_redefined && !special_redefined)
1264 element_info[element].special_graphic[special] = graphic;
1267 // initialize special element/graphic mapping from dynamic configuration
1268 for (i = 0; i < num_property_mappings; i++)
1270 int element = property_mapping[i].base_index;
1271 int action = property_mapping[i].ext1_index;
1272 int direction = property_mapping[i].ext2_index;
1273 int special = property_mapping[i].ext3_index;
1274 int graphic = property_mapping[i].artwork_index;
1276 // for action ".active", replace element with active element, if exists
1277 if (action == ACTION_ACTIVE && element != ELEMENT_ACTIVE(element))
1279 element = ELEMENT_ACTIVE(element);
1283 if (element >= MAX_NUM_ELEMENTS)
1286 // do not change special graphic if action or direction was specified
1287 if (action != -1 || direction != -1)
1290 if (IS_SPECIAL_GFX_ARG(special))
1291 element_info[element].special_graphic[special] = graphic;
1294 // now set all undefined/invalid graphics to default
1295 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1296 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1297 if (graphic_info[element_info[i].special_graphic[j]].bitmap == NULL)
1298 element_info[i].special_graphic[j] =
1299 element_info[i].graphic[ACTION_DEFAULT];
1302 static int get_graphic_parameter_value(char *value_raw, char *suffix, int type)
1304 if (type != TYPE_ELEMENT && type != TYPE_GRAPHIC)
1305 return get_parameter_value(value_raw, suffix, type);
1307 if (strEqual(value_raw, ARG_UNDEFINED))
1308 return ARG_UNDEFINED_VALUE;
1310 if (type == TYPE_ELEMENT)
1312 char *value = getHashEntry(element_token_hash, value_raw);
1317 Warn("error found in config file:");
1318 Warn("- config file: '%s'", getImageConfigFilename());
1319 Warn("error: invalid element token '%s'", value_raw);
1320 Warn("custom graphic rejected for this element/action");
1321 Warn("fallback done to undefined element for this graphic");
1325 return (value != NULL ? atoi(value) : EL_UNDEFINED);
1327 else if (type == TYPE_GRAPHIC)
1329 char *value = getHashEntry(graphic_token_hash, value_raw);
1330 int fallback_graphic = IMG_CHAR_EXCLAM;
1335 Warn("error found in config file:");
1336 Warn("- config file: '%s'", getImageConfigFilename());
1337 Warn("error: invalid graphic token '%s'", value_raw);
1338 Warn("custom graphic rejected for this element/action");
1339 Warn("fallback done to 'char_exclam' for this graphic");
1343 return (value != NULL ? atoi(value) : fallback_graphic);
1349 static int get_scaled_graphic_width(Bitmap *src_bitmap, int graphic)
1351 int original_width = getOriginalImageWidthFromImageID(graphic);
1352 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1354 // only happens when loaded outside artwork system (like "global.busy")
1355 if (graphic_info == image_initial && src_bitmap)
1356 original_width = src_bitmap->width;
1358 return original_width * scale_up_factor;
1361 static int get_scaled_graphic_height(Bitmap *src_bitmap, int graphic)
1363 int original_height = getOriginalImageHeightFromImageID(graphic);
1364 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1366 // only happens when loaded outside artwork system (like "global.busy")
1367 if (graphic_info == image_initial && src_bitmap)
1368 original_height = src_bitmap->height;
1370 return original_height * scale_up_factor;
1373 static void set_graphic_parameters_ext(int graphic, int *parameter,
1374 Bitmap **src_bitmaps)
1376 struct GraphicInfo *g = &graphic_info[graphic];
1377 Bitmap *src_bitmap = (src_bitmaps ? src_bitmaps[IMG_BITMAP_STANDARD] : NULL);
1378 int anim_frames_per_row = 1, anim_frames_per_col = 1;
1379 int anim_frames_per_line = 1;
1381 // always start with reliable default values
1382 g->src_image_width = 0;
1383 g->src_image_height = 0;
1386 g->width = TILEX; // default for element graphics
1387 g->height = TILEY; // default for element graphics
1388 g->offset_x = 0; // one or both of these values ...
1389 g->offset_y = 0; // ... will be corrected later
1390 g->offset2_x = 0; // one or both of these values ...
1391 g->offset2_y = 0; // ... will be corrected later
1392 g->swap_double_tiles = -1; // auto-detect tile swapping
1393 g->crumbled_like = -1; // do not use clone element
1394 g->diggable_like = -1; // do not use clone element
1395 g->border_size = TILEX / 8; // "CRUMBLED" border size
1396 g->scale_up_factor = 1; // default: no scaling up
1397 g->tile_size = TILESIZE; // default: standard tile size
1398 g->clone_from = -1; // do not use clone graphic
1399 g->init_delay_fixed = 0;
1400 g->init_delay_random = 0;
1401 g->init_delay_action = -1;
1402 g->anim_delay_fixed = 0;
1403 g->anim_delay_random = 0;
1404 g->anim_delay_action = -1;
1405 g->post_delay_fixed = 0;
1406 g->post_delay_random = 0;
1407 g->post_delay_action = -1;
1408 g->init_event = ANIM_EVENT_UNDEFINED;
1409 g->anim_event = ANIM_EVENT_UNDEFINED;
1410 g->init_event_action = -1;
1411 g->anim_event_action = -1;
1412 g->draw_masked = FALSE;
1414 g->fade_mode = FADE_MODE_DEFAULT;
1418 g->auto_delay_unit = AUTO_DELAY_UNIT_DEFAULT;
1419 g->align = ALIGN_CENTER; // default for title screens
1420 g->valign = VALIGN_MIDDLE; // default for title screens
1421 g->sort_priority = 0; // default for title screens
1423 g->style = STYLE_DEFAULT;
1426 g->bitmaps = src_bitmaps;
1427 g->bitmap = src_bitmap;
1429 // optional zoom factor for scaling up the image to a larger size
1430 if (parameter[GFX_ARG_SCALE_UP_FACTOR] != ARG_UNDEFINED_VALUE)
1431 g->scale_up_factor = parameter[GFX_ARG_SCALE_UP_FACTOR];
1432 if (g->scale_up_factor < 1)
1433 g->scale_up_factor = 1; // no scaling
1435 // optional tile size for using non-standard image size
1436 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1438 g->tile_size = parameter[GFX_ARG_TILE_SIZE];
1441 // CHECK: should tile sizes less than standard tile size be allowed?
1442 if (g->tile_size < TILESIZE)
1443 g->tile_size = TILESIZE; // standard tile size
1446 // when setting tile size, also set width and height accordingly
1447 g->width = g->tile_size;
1448 g->height = g->tile_size;
1451 if (g->use_image_size)
1453 // set new default bitmap size (with scaling, but without small images)
1454 g->width = get_scaled_graphic_width(src_bitmap, graphic);
1455 g->height = get_scaled_graphic_height(src_bitmap, graphic);
1458 // optional width and height of each animation frame
1459 if (parameter[GFX_ARG_WIDTH] != ARG_UNDEFINED_VALUE)
1460 g->width = parameter[GFX_ARG_WIDTH];
1461 if (parameter[GFX_ARG_HEIGHT] != ARG_UNDEFINED_VALUE)
1462 g->height = parameter[GFX_ARG_HEIGHT];
1464 // optional x and y tile position of animation frame sequence
1465 if (parameter[GFX_ARG_XPOS] != ARG_UNDEFINED_VALUE)
1466 g->src_x = parameter[GFX_ARG_XPOS] * g->width;
1467 if (parameter[GFX_ARG_YPOS] != ARG_UNDEFINED_VALUE)
1468 g->src_y = parameter[GFX_ARG_YPOS] * g->height;
1470 // optional x and y pixel position of animation frame sequence
1471 if (parameter[GFX_ARG_X] != ARG_UNDEFINED_VALUE)
1472 g->src_x = parameter[GFX_ARG_X];
1473 if (parameter[GFX_ARG_Y] != ARG_UNDEFINED_VALUE)
1474 g->src_y = parameter[GFX_ARG_Y];
1481 Warn("invalid value %d for '%s.width' (fallback done to %d)",
1482 g->width, getTokenFromImageID(graphic), TILEX);
1485 g->width = TILEX; // will be checked to be inside bitmap later
1491 Warn("invalid value %d for '%s.height' (fallback done to %d)",
1492 g->height, getTokenFromImageID(graphic), TILEY);
1495 g->height = TILEY; // will be checked to be inside bitmap later
1501 // get final bitmap size (with scaling, but without small images)
1502 int src_image_width = get_scaled_graphic_width(src_bitmap, graphic);
1503 int src_image_height = get_scaled_graphic_height(src_bitmap, graphic);
1505 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1507 anim_frames_per_row = MAX(1, src_image_width / g->tile_size);
1508 anim_frames_per_col = MAX(1, src_image_height / g->tile_size);
1512 anim_frames_per_row = MAX(1, src_image_width / g->width);
1513 anim_frames_per_col = MAX(1, src_image_height / g->height);
1516 g->src_image_width = src_image_width;
1517 g->src_image_height = src_image_height;
1520 // correct x or y offset dependent of vertical or horizontal frame order
1521 if (parameter[GFX_ARG_VERTICAL]) // frames are ordered vertically
1523 g->offset_y = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1524 parameter[GFX_ARG_OFFSET] : g->height);
1525 anim_frames_per_line = anim_frames_per_col;
1527 else // frames are ordered horizontally
1529 g->offset_x = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1530 parameter[GFX_ARG_OFFSET] : g->width);
1531 anim_frames_per_line = anim_frames_per_row;
1534 // optionally, the x and y offset of frames can be specified directly
1535 if (parameter[GFX_ARG_XOFFSET] != ARG_UNDEFINED_VALUE)
1536 g->offset_x = parameter[GFX_ARG_XOFFSET];
1537 if (parameter[GFX_ARG_YOFFSET] != ARG_UNDEFINED_VALUE)
1538 g->offset_y = parameter[GFX_ARG_YOFFSET];
1540 // optionally, moving animations may have separate start and end graphics
1541 g->double_movement = parameter[GFX_ARG_2ND_MOVEMENT_TILE];
1543 if (parameter[GFX_ARG_2ND_VERTICAL] == ARG_UNDEFINED_VALUE)
1544 parameter[GFX_ARG_2ND_VERTICAL] = !parameter[GFX_ARG_VERTICAL];
1546 // correct x or y offset2 dependent of vertical or horizontal frame order
1547 if (parameter[GFX_ARG_2ND_VERTICAL]) // frames are ordered vertically
1548 g->offset2_y = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1549 parameter[GFX_ARG_2ND_OFFSET] : g->height);
1550 else // frames are ordered horizontally
1551 g->offset2_x = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1552 parameter[GFX_ARG_2ND_OFFSET] : g->width);
1554 // optionally, the x and y offset of 2nd graphic can be specified directly
1555 if (parameter[GFX_ARG_2ND_XOFFSET] != ARG_UNDEFINED_VALUE)
1556 g->offset2_x = parameter[GFX_ARG_2ND_XOFFSET];
1557 if (parameter[GFX_ARG_2ND_YOFFSET] != ARG_UNDEFINED_VALUE)
1558 g->offset2_y = parameter[GFX_ARG_2ND_YOFFSET];
1560 // optionally, the second movement tile can be specified as start tile
1561 if (parameter[GFX_ARG_2ND_SWAP_TILES] != ARG_UNDEFINED_VALUE)
1562 g->swap_double_tiles= parameter[GFX_ARG_2ND_SWAP_TILES];
1564 // automatically determine correct number of frames, if not defined
1565 if (parameter[GFX_ARG_FRAMES] != ARG_UNDEFINED_VALUE)
1566 g->anim_frames = parameter[GFX_ARG_FRAMES];
1567 else if (parameter[GFX_ARG_XPOS] == 0 && !parameter[GFX_ARG_VERTICAL])
1568 g->anim_frames = anim_frames_per_row;
1569 else if (parameter[GFX_ARG_YPOS] == 0 && parameter[GFX_ARG_VERTICAL])
1570 g->anim_frames = anim_frames_per_col;
1574 if (g->anim_frames < 1) // frames must be at least 1
1577 g->anim_frames_per_line =
1578 (parameter[GFX_ARG_FRAMES_PER_LINE] != ARG_UNDEFINED_VALUE ?
1579 parameter[GFX_ARG_FRAMES_PER_LINE] : anim_frames_per_line);
1581 g->anim_delay = parameter[GFX_ARG_DELAY];
1582 if (g->anim_delay < 1) // delay must be at least 1
1585 g->anim_mode = parameter[GFX_ARG_ANIM_MODE];
1587 // automatically determine correct start frame, if not defined
1588 if (parameter[GFX_ARG_START_FRAME] == ARG_UNDEFINED_VALUE)
1589 g->anim_start_frame = 0;
1590 else if (g->anim_mode & ANIM_REVERSE)
1591 g->anim_start_frame = g->anim_frames - parameter[GFX_ARG_START_FRAME] - 1;
1593 g->anim_start_frame = parameter[GFX_ARG_START_FRAME];
1595 // animation synchronized with global frame counter, not move position
1596 g->anim_global_sync = parameter[GFX_ARG_GLOBAL_SYNC];
1598 // animation synchronized with global anim frame counter, not move position
1599 g->anim_global_anim_sync = parameter[GFX_ARG_GLOBAL_ANIM_SYNC];
1601 // optional element for cloning crumble graphics
1602 if (parameter[GFX_ARG_CRUMBLED_LIKE] != ARG_UNDEFINED_VALUE)
1603 g->crumbled_like = parameter[GFX_ARG_CRUMBLED_LIKE];
1605 // optional element for cloning digging graphics
1606 if (parameter[GFX_ARG_DIGGABLE_LIKE] != ARG_UNDEFINED_VALUE)
1607 g->diggable_like = parameter[GFX_ARG_DIGGABLE_LIKE];
1609 // optional border size for "crumbling" diggable graphics
1610 if (parameter[GFX_ARG_BORDER_SIZE] != ARG_UNDEFINED_VALUE)
1611 g->border_size = parameter[GFX_ARG_BORDER_SIZE];
1613 // used for global animations and player "boring" and "sleeping" actions
1614 if (parameter[GFX_ARG_INIT_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1615 g->init_delay_fixed = parameter[GFX_ARG_INIT_DELAY_FIXED];
1616 if (parameter[GFX_ARG_INIT_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1617 g->init_delay_random = parameter[GFX_ARG_INIT_DELAY_RANDOM];
1618 if (parameter[GFX_ARG_ANIM_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1619 g->anim_delay_fixed = parameter[GFX_ARG_ANIM_DELAY_FIXED];
1620 if (parameter[GFX_ARG_ANIM_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1621 g->anim_delay_random = parameter[GFX_ARG_ANIM_DELAY_RANDOM];
1622 if (parameter[GFX_ARG_POST_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1623 g->post_delay_fixed = parameter[GFX_ARG_POST_DELAY_FIXED];
1624 if (parameter[GFX_ARG_POST_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1625 g->post_delay_random = parameter[GFX_ARG_POST_DELAY_RANDOM];
1627 // used for global animations
1628 if (parameter[GFX_ARG_INIT_EVENT] != ARG_UNDEFINED_VALUE)
1629 g->init_event = parameter[GFX_ARG_INIT_EVENT];
1630 if (parameter[GFX_ARG_ANIM_EVENT] != ARG_UNDEFINED_VALUE)
1631 g->anim_event = parameter[GFX_ARG_ANIM_EVENT];
1632 if (parameter[GFX_ARG_INIT_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1633 g->init_event_action = parameter[GFX_ARG_INIT_EVENT_ACTION];
1634 if (parameter[GFX_ARG_ANIM_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1635 g->anim_event_action = parameter[GFX_ARG_ANIM_EVENT_ACTION];
1636 if (parameter[GFX_ARG_INIT_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1637 g->init_delay_action = parameter[GFX_ARG_INIT_DELAY_ACTION];
1638 if (parameter[GFX_ARG_ANIM_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1639 g->anim_delay_action = parameter[GFX_ARG_ANIM_DELAY_ACTION];
1640 if (parameter[GFX_ARG_POST_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1641 g->post_delay_action = parameter[GFX_ARG_POST_DELAY_ACTION];
1643 // used for toon animations and global animations
1644 g->step_offset = parameter[GFX_ARG_STEP_OFFSET];
1645 g->step_xoffset = parameter[GFX_ARG_STEP_XOFFSET];
1646 g->step_yoffset = parameter[GFX_ARG_STEP_YOFFSET];
1647 g->step_delay = parameter[GFX_ARG_STEP_DELAY];
1648 g->direction = parameter[GFX_ARG_DIRECTION];
1649 g->position = parameter[GFX_ARG_POSITION];
1650 g->x = parameter[GFX_ARG_X]; // (may be uninitialized,
1651 g->y = parameter[GFX_ARG_Y]; // unlike src_x and src_y)
1653 if (g->step_delay < 1) // delay must be at least 1
1656 // this is only used for drawing font characters
1657 g->draw_xoffset = parameter[GFX_ARG_DRAW_XOFFSET];
1658 g->draw_yoffset = parameter[GFX_ARG_DRAW_YOFFSET];
1660 // use a different default value for global animations and toons
1661 if ((graphic >= IMG_GFX_GLOBAL_ANIM_1 && graphic <= IMG_GFX_GLOBAL_ANIM_32) ||
1662 (graphic >= IMG_TOON_1 && graphic <= IMG_TOON_20))
1663 g->draw_masked = TRUE;
1665 // this is used for drawing envelopes, global animations and toons
1666 if (parameter[GFX_ARG_DRAW_MASKED] != ARG_UNDEFINED_VALUE)
1667 g->draw_masked = parameter[GFX_ARG_DRAW_MASKED];
1669 // use a different default value for global animations (corrected later)
1670 if (graphic >= IMG_GLOBAL_ANIM_1 && graphic <= IMG_GLOBAL_ANIM_32)
1671 g->draw_order = ARG_UNDEFINED_VALUE;
1673 // used for toon animations and global animations
1674 if (parameter[GFX_ARG_DRAW_ORDER] != ARG_UNDEFINED_VALUE)
1675 g->draw_order = parameter[GFX_ARG_DRAW_ORDER];
1677 // optional graphic for cloning all graphics settings
1678 if (parameter[GFX_ARG_CLONE_FROM] != ARG_UNDEFINED_VALUE)
1679 g->clone_from = parameter[GFX_ARG_CLONE_FROM];
1681 // optional settings for drawing title screens and title messages
1682 if (parameter[GFX_ARG_FADE_MODE] != ARG_UNDEFINED_VALUE)
1683 g->fade_mode = parameter[GFX_ARG_FADE_MODE];
1684 if (parameter[GFX_ARG_FADE_DELAY] != ARG_UNDEFINED_VALUE)
1685 g->fade_delay = parameter[GFX_ARG_FADE_DELAY];
1686 if (parameter[GFX_ARG_POST_DELAY] != ARG_UNDEFINED_VALUE)
1687 g->post_delay = parameter[GFX_ARG_POST_DELAY];
1688 if (parameter[GFX_ARG_AUTO_DELAY] != ARG_UNDEFINED_VALUE)
1689 g->auto_delay = parameter[GFX_ARG_AUTO_DELAY];
1690 if (parameter[GFX_ARG_AUTO_DELAY_UNIT] != ARG_UNDEFINED_VALUE)
1691 g->auto_delay_unit = parameter[GFX_ARG_AUTO_DELAY_UNIT];
1692 if (parameter[GFX_ARG_ALIGN] != ARG_UNDEFINED_VALUE)
1693 g->align = parameter[GFX_ARG_ALIGN];
1694 if (parameter[GFX_ARG_VALIGN] != ARG_UNDEFINED_VALUE)
1695 g->valign = parameter[GFX_ARG_VALIGN];
1696 if (parameter[GFX_ARG_SORT_PRIORITY] != ARG_UNDEFINED_VALUE)
1697 g->sort_priority = parameter[GFX_ARG_SORT_PRIORITY];
1699 if (parameter[GFX_ARG_CLASS] != ARG_UNDEFINED_VALUE)
1700 g->class = parameter[GFX_ARG_CLASS];
1701 if (parameter[GFX_ARG_STYLE] != ARG_UNDEFINED_VALUE)
1702 g->style = parameter[GFX_ARG_STYLE];
1703 if (parameter[GFX_ARG_ALPHA] != ARG_UNDEFINED_VALUE)
1704 g->alpha = parameter[GFX_ARG_ALPHA];
1706 // this is only used for drawing menu buttons and text
1707 g->active_xoffset = parameter[GFX_ARG_ACTIVE_XOFFSET];
1708 g->active_yoffset = parameter[GFX_ARG_ACTIVE_YOFFSET];
1709 g->pressed_xoffset = parameter[GFX_ARG_PRESSED_XOFFSET];
1710 g->pressed_yoffset = parameter[GFX_ARG_PRESSED_YOFFSET];
1712 // this is only used for drawing stacked global animations
1713 g->stacked_xfactor = parameter[GFX_ARG_STACKED_XFACTOR];
1714 g->stacked_yfactor = parameter[GFX_ARG_STACKED_YFACTOR];
1715 g->stacked_xoffset = parameter[GFX_ARG_STACKED_XOFFSET];
1716 g->stacked_yoffset = parameter[GFX_ARG_STACKED_YOFFSET];
1719 static void set_graphic_parameters(int graphic)
1721 struct FileInfo *image = getImageListEntryFromImageID(graphic);
1722 char **parameter_raw = image->parameter;
1723 Bitmap **src_bitmaps = getBitmapsFromImageID(graphic);
1724 int parameter[NUM_GFX_ARGS];
1727 // if fallback to default artwork is done, also use the default parameters
1728 if (image->fallback_to_default)
1729 parameter_raw = image->default_parameter;
1731 // get integer values from string parameters
1732 for (i = 0; i < NUM_GFX_ARGS; i++)
1733 parameter[i] = get_graphic_parameter_value(parameter_raw[i],
1734 image_config_suffix[i].token,
1735 image_config_suffix[i].type);
1737 set_graphic_parameters_ext(graphic, parameter, src_bitmaps);
1739 UPDATE_BUSY_STATE();
1742 static void set_cloned_graphic_parameters(int graphic)
1744 int fallback_graphic = IMG_CHAR_EXCLAM;
1745 int max_num_images = getImageListSize();
1746 int clone_graphic = graphic_info[graphic].clone_from;
1747 int num_references_followed = 1;
1749 while (graphic_info[clone_graphic].clone_from != -1 &&
1750 num_references_followed < max_num_images)
1752 clone_graphic = graphic_info[clone_graphic].clone_from;
1754 num_references_followed++;
1757 if (num_references_followed >= max_num_images)
1760 Warn("error found in config file:");
1761 Warn("- config file: '%s'", getImageConfigFilename());
1762 Warn("- config token: '%s'", getTokenFromImageID(graphic));
1763 Warn("error: loop discovered when resolving cloned graphics");
1764 Warn("custom graphic rejected for this element/action");
1766 if (graphic == fallback_graphic)
1767 Fail("no fallback graphic available");
1769 Warn("fallback done to 'char_exclam' for this graphic");
1772 graphic_info[graphic] = graphic_info[fallback_graphic];
1776 graphic_info[graphic] = graphic_info[clone_graphic];
1777 graphic_info[graphic].clone_from = clone_graphic;
1781 static void InitGraphicInfo(void)
1783 int fallback_graphic = IMG_CHAR_EXCLAM;
1784 int num_images = getImageListSize();
1787 // use image size as default values for width and height for these images
1788 static int full_size_graphics[] =
1791 IMG_GLOBAL_BORDER_MAIN,
1792 IMG_GLOBAL_BORDER_SCORES,
1793 IMG_GLOBAL_BORDER_EDITOR,
1794 IMG_GLOBAL_BORDER_PLAYING,
1797 IMG_BACKGROUND_ENVELOPE_1,
1798 IMG_BACKGROUND_ENVELOPE_2,
1799 IMG_BACKGROUND_ENVELOPE_3,
1800 IMG_BACKGROUND_ENVELOPE_4,
1801 IMG_BACKGROUND_REQUEST,
1804 IMG_BACKGROUND_LOADING_INITIAL,
1805 IMG_BACKGROUND_LOADING,
1806 IMG_BACKGROUND_TITLE_INITIAL,
1807 IMG_BACKGROUND_TITLE,
1808 IMG_BACKGROUND_MAIN,
1809 IMG_BACKGROUND_NAMES,
1810 IMG_BACKGROUND_LEVELS,
1811 IMG_BACKGROUND_LEVELNR,
1812 IMG_BACKGROUND_SCORES,
1813 IMG_BACKGROUND_SCOREINFO,
1814 IMG_BACKGROUND_EDITOR,
1815 IMG_BACKGROUND_INFO,
1816 IMG_BACKGROUND_INFO_ELEMENTS,
1817 IMG_BACKGROUND_INFO_MUSIC,
1818 IMG_BACKGROUND_INFO_CREDITS,
1819 IMG_BACKGROUND_INFO_PROGRAM,
1820 IMG_BACKGROUND_INFO_VERSION,
1821 IMG_BACKGROUND_INFO_LEVELSET,
1822 IMG_BACKGROUND_SETUP,
1823 IMG_BACKGROUND_PLAYING,
1824 IMG_BACKGROUND_DOOR,
1825 IMG_BACKGROUND_TAPE,
1826 IMG_BACKGROUND_PANEL,
1827 IMG_BACKGROUND_PALETTE,
1828 IMG_BACKGROUND_TOOLBOX,
1830 IMG_TITLESCREEN_INITIAL_1,
1831 IMG_TITLESCREEN_INITIAL_2,
1832 IMG_TITLESCREEN_INITIAL_3,
1833 IMG_TITLESCREEN_INITIAL_4,
1834 IMG_TITLESCREEN_INITIAL_5,
1841 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_1,
1842 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_2,
1843 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_3,
1844 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_4,
1845 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_5,
1846 IMG_BACKGROUND_TITLEMESSAGE_1,
1847 IMG_BACKGROUND_TITLEMESSAGE_2,
1848 IMG_BACKGROUND_TITLEMESSAGE_3,
1849 IMG_BACKGROUND_TITLEMESSAGE_4,
1850 IMG_BACKGROUND_TITLEMESSAGE_5,
1855 FreeGlobalAnimEventInfo();
1857 checked_free(graphic_info);
1859 graphic_info = checked_calloc(num_images * sizeof(struct GraphicInfo));
1861 // initialize "use_image_size" flag with default value
1862 for (i = 0; i < num_images; i++)
1863 graphic_info[i].use_image_size = FALSE;
1865 // initialize "use_image_size" flag from static configuration above
1866 for (i = 0; full_size_graphics[i] != -1; i++)
1867 graphic_info[full_size_graphics[i]].use_image_size = TRUE;
1869 // first set all graphic paramaters ...
1870 for (i = 0; i < num_images; i++)
1871 set_graphic_parameters(i);
1873 // ... then copy these parameters for cloned graphics
1874 for (i = 0; i < num_images; i++)
1875 if (graphic_info[i].clone_from != -1)
1876 set_cloned_graphic_parameters(i);
1878 for (i = 0; i < num_images; i++)
1880 Bitmap *src_bitmap = graphic_info[i].bitmap;
1884 int src_bitmap_width, src_bitmap_height;
1886 // now check if no animation frames are outside of the loaded image
1888 if (graphic_info[i].bitmap == NULL)
1889 continue; // skip check for optional images that are undefined
1891 // get image size (this can differ from the standard element tile size!)
1892 width = graphic_info[i].width;
1893 height = graphic_info[i].height;
1895 // get final bitmap size (with scaling, but without small images)
1896 src_bitmap_width = graphic_info[i].src_image_width;
1897 src_bitmap_height = graphic_info[i].src_image_height;
1899 // check if first animation frame is inside specified bitmap
1901 // do not use getGraphicSourceXY() here to get position of first frame;
1902 // this avoids calculating wrong start position for out-of-bounds frame
1903 src_x = graphic_info[i].src_x;
1904 src_y = graphic_info[i].src_y;
1906 if (program.headless)
1909 if (src_x < 0 || src_y < 0 ||
1910 src_x + width > src_bitmap_width ||
1911 src_y + height > src_bitmap_height)
1914 Warn("error found in config file:");
1915 Warn("- config file: '%s'", getImageConfigFilename());
1916 Warn("- config token: '%s'", getTokenFromImageID(i));
1917 Warn("- image file: '%s'", src_bitmap->source_filename);
1918 Warn("- frame size: %d, %d", width, height);
1919 Warn("error: first animation frame out of bounds (%d, %d) [%d, %d]",
1920 src_x, src_y, src_bitmap_width, src_bitmap_height);
1921 Warn("custom graphic rejected for this element/action");
1923 if (i == fallback_graphic)
1924 Fail("no fallback graphic available");
1926 Warn("fallback done to 'char_exclam' for this graphic");
1929 graphic_info[i] = graphic_info[fallback_graphic];
1931 // if first frame out of bounds, do not check last frame anymore
1935 // check if last animation frame is inside specified bitmap
1937 last_frame = graphic_info[i].anim_frames - 1;
1938 getGraphicSourceXY(i, last_frame, &src_x, &src_y, FALSE);
1940 if (src_x < 0 || src_y < 0 ||
1941 src_x + width > src_bitmap_width ||
1942 src_y + height > src_bitmap_height)
1945 Warn("error found in config file:");
1946 Warn("- config file: '%s'", getImageConfigFilename());
1947 Warn("- config token: '%s'", getTokenFromImageID(i));
1948 Warn("- image file: '%s'", src_bitmap->source_filename);
1949 Warn("- frame size: %d, %d", width, height);
1950 Warn("error: last animation frame (%d) out of bounds (%d, %d) [%d, %d]",
1951 last_frame, src_x, src_y, src_bitmap_width, src_bitmap_height);
1952 Warn("custom graphic rejected for this element/action");
1954 if (i == fallback_graphic)
1955 Fail("no fallback graphic available");
1957 Warn("fallback done to 'char_exclam' for this graphic");
1960 graphic_info[i] = graphic_info[fallback_graphic];
1965 static void InitGraphicCompatibilityInfo(void)
1967 struct FileInfo *fi_global_door =
1968 getImageListEntryFromImageID(IMG_GLOBAL_DOOR);
1969 int num_images = getImageListSize();
1972 /* the following compatibility handling is needed for the following case:
1973 versions up to 3.3.0.0 used one large bitmap "global.door" for various
1974 graphics mainly used for door and panel graphics, like editor, tape and
1975 in-game buttons with hard-coded bitmap positions and button sizes; as
1976 these graphics now have individual definitions, redefining "global.door"
1977 to change all these graphics at once like before does not work anymore
1978 (because all those individual definitions still have their default values);
1979 to solve this, remap all those individual definitions that are not
1980 redefined to the new bitmap of "global.door" if it was redefined */
1982 // special compatibility handling if image "global.door" was redefined
1983 if (fi_global_door->redefined)
1985 for (i = 0; i < num_images; i++)
1987 struct FileInfo *fi = getImageListEntryFromImageID(i);
1989 // process only those images that still use the default settings
1992 // process all images which default to same image as "global.door"
1993 if (strEqual(fi->default_filename, fi_global_door->default_filename))
1995 // skip all images that are cloned from images that default to same
1996 // image as "global.door", but that are redefined to something else
1997 if (graphic_info[i].clone_from != -1)
1999 int cloned_graphic = graphic_info[i].clone_from;
2001 if (getImageListEntryFromImageID(cloned_graphic)->redefined)
2006 Debug("init:InitGraphicCompatibilityInfo",
2007 "special treatment needed for token '%s'", fi->token);
2010 graphic_info[i].bitmaps = graphic_info[IMG_GLOBAL_DOOR].bitmaps;
2011 graphic_info[i].bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2017 // special compatibility handling for "Snake Bite" graphics set
2018 if (strPrefix(leveldir_current->identifier, "snake_bite"))
2020 Bitmap *bitmap = graphic_info[IMG_BACKGROUND_SCORES].bitmap;
2022 BlitBitmap(bitmap, bitmap, 18, 66, 32, 480, 50, 66);
2023 BlitBitmap(bitmap, bitmap, 466, 66, 32, 480, 434, 66);
2025 ClearRectangle(bitmap, 2, 66, 32, 480);
2026 ClearRectangle(bitmap, 514, 66, 32, 480);
2029 // special compatibility handling for "Jue" graphics sets (2007 and 2019)
2030 boolean supports_score_info = (menu.draw_xoffset[GAME_MODE_SCOREINFO] != 0);
2031 if (strPrefix(artwork.gfx_current_identifier, "jue") && !supports_score_info)
2049 int mode_old = GAME_MODE_SCORES;
2050 int mode_new = GAME_MODE_SCOREINFO;
2053 // adjust title screens on score info page
2054 for (i = 0; font_title[i] != -1; i++)
2056 struct FontInfo *fi = &font_info[font_title[i]];
2058 fi->special_graphic[mode_new] = fi->special_graphic[mode_old];
2059 fi->special_bitmap_id[mode_new] = fi->special_bitmap_id[mode_old];
2062 // adjust vertical text and button positions on scores page
2063 for (i = 0; font_text[i] != -1; i++)
2065 for (j = 0; j < 2; j++)
2067 boolean jue0 = strEqual(artwork.gfx_current_identifier, "jue0");
2068 int font_nr = (j == 0 ? font_text[i] : FONT_ACTIVE(font_text[i]));
2069 int font_bitmap_id = font_info[font_nr].special_bitmap_id[mode_old];
2070 int font_yoffset = (jue0 ? 10 : 5);
2072 gfx.font_bitmap_info[font_bitmap_id].draw_yoffset = font_yoffset;
2076 // adjust page offsets on score info page
2077 menu.draw_xoffset[mode_new] = menu.draw_xoffset[mode_old];
2078 menu.draw_yoffset[mode_new] = menu.draw_yoffset[mode_old];
2081 InitGraphicCompatibilityInfo_Doors();
2084 static void InitElementSoundInfo(void)
2086 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
2087 int num_property_mappings = getSoundListPropertyMappingSize();
2090 // set values to -1 to identify later as "uninitialized" values
2091 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2092 for (act = 0; act < NUM_ACTIONS; act++)
2093 element_info[i].sound[act] = -1;
2095 // initialize element/sound mapping from static configuration
2096 for (i = 0; element_to_sound[i].element > -1; i++)
2098 int element = element_to_sound[i].element;
2099 int action = element_to_sound[i].action;
2100 int sound = element_to_sound[i].sound;
2101 boolean is_class = element_to_sound[i].is_class;
2104 action = ACTION_DEFAULT;
2107 element_info[element].sound[action] = sound;
2109 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2110 if (strEqual(element_info[j].class_name,
2111 element_info[element].class_name))
2112 element_info[j].sound[action] = sound;
2115 // initialize element class/sound mapping from dynamic configuration
2116 for (i = 0; i < num_property_mappings; i++)
2118 int element_class = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
2119 int action = property_mapping[i].ext1_index;
2120 int sound = property_mapping[i].artwork_index;
2122 if (element_class < 0 || element_class >= MAX_NUM_ELEMENTS)
2126 action = ACTION_DEFAULT;
2128 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2129 if (strEqual(element_info[j].class_name,
2130 element_info[element_class].class_name))
2131 element_info[j].sound[action] = sound;
2134 // initialize element/sound mapping from dynamic configuration
2135 for (i = 0; i < num_property_mappings; i++)
2137 int element = property_mapping[i].base_index;
2138 int action = property_mapping[i].ext1_index;
2139 int sound = property_mapping[i].artwork_index;
2141 if (element >= MAX_NUM_ELEMENTS)
2145 action = ACTION_DEFAULT;
2147 element_info[element].sound[action] = sound;
2150 // now set all '-1' values to element specific default values
2151 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2153 for (act = 0; act < NUM_ACTIONS; act++)
2155 // generic default action sound (defined by "[default]" directive)
2156 int default_action_sound = element_info[EL_DEFAULT].sound[act];
2158 // look for special default action sound (classic game specific)
2159 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].sound[act] != -1)
2160 default_action_sound = element_info[EL_BD_DEFAULT].sound[act];
2161 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].sound[act] != -1)
2162 default_action_sound = element_info[EL_SP_DEFAULT].sound[act];
2163 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].sound[act] != -1)
2164 default_action_sound = element_info[EL_SB_DEFAULT].sound[act];
2165 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].sound[act] != -1)
2166 default_action_sound = element_info[EL_MM_DEFAULT].sound[act];
2168 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
2169 // !!! make this better !!!
2170 if (i == EL_EMPTY_SPACE)
2171 default_action_sound = element_info[EL_DEFAULT].sound[act];
2173 // no sound for this specific action -- use default action sound
2174 if (element_info[i].sound[act] == -1)
2175 element_info[i].sound[act] = default_action_sound;
2179 // copy sound settings to some elements that are only stored in level file
2180 // in native R'n'D levels, but are used by game engine in native EM levels
2181 for (i = 0; copy_properties[i][0] != -1; i++)
2182 for (j = 1; j <= 4; j++)
2183 for (act = 0; act < NUM_ACTIONS; act++)
2184 element_info[copy_properties[i][j]].sound[act] =
2185 element_info[copy_properties[i][0]].sound[act];
2188 static void InitGameModeSoundInfo(void)
2192 // set values to -1 to identify later as "uninitialized" values
2193 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2196 // initialize gamemode/sound mapping from static configuration
2197 for (i = 0; gamemode_to_sound[i].sound > -1; i++)
2199 int gamemode = gamemode_to_sound[i].gamemode;
2200 int sound = gamemode_to_sound[i].sound;
2203 gamemode = GAME_MODE_DEFAULT;
2205 menu.sound[gamemode] = sound;
2208 // now set all '-1' values to levelset specific default values
2209 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2210 if (menu.sound[i] == -1)
2211 menu.sound[i] = menu.sound[GAME_MODE_DEFAULT];
2214 static void set_sound_parameters(int sound, char **parameter_raw)
2216 int parameter[NUM_SND_ARGS];
2219 // get integer values from string parameters
2220 for (i = 0; i < NUM_SND_ARGS; i++)
2222 get_parameter_value(parameter_raw[i],
2223 sound_config_suffix[i].token,
2224 sound_config_suffix[i].type);
2226 // explicit loop mode setting in configuration overrides default value
2227 if (parameter[SND_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2228 sound_info[sound].loop = parameter[SND_ARG_MODE_LOOP];
2230 // sound volume to change the original volume when loading the sound file
2231 sound_info[sound].volume = parameter[SND_ARG_VOLUME];
2233 // sound priority to give certain sounds a higher or lower priority
2234 sound_info[sound].priority = parameter[SND_ARG_PRIORITY];
2237 static void InitSoundInfo(void)
2239 int *sound_effect_properties;
2240 int num_sounds = getSoundListSize();
2243 checked_free(sound_info);
2245 sound_effect_properties = checked_calloc(num_sounds * sizeof(int));
2246 sound_info = checked_calloc(num_sounds * sizeof(struct SoundInfo));
2248 // initialize sound effect for all elements to "no sound"
2249 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2250 for (j = 0; j < NUM_ACTIONS; j++)
2251 element_info[i].sound[j] = SND_UNDEFINED;
2253 for (i = 0; i < num_sounds; i++)
2255 struct FileInfo *sound = getSoundListEntry(i);
2256 int len_effect_text = strlen(sound->token);
2258 sound_effect_properties[i] = ACTION_OTHER;
2259 sound_info[i].loop = FALSE; // default: play sound only once
2261 // determine all loop sounds and identify certain sound classes
2263 for (j = 0; element_action_info[j].suffix; j++)
2265 int len_action_text = strlen(element_action_info[j].suffix);
2267 if (len_action_text < len_effect_text &&
2268 strEqual(&sound->token[len_effect_text - len_action_text],
2269 element_action_info[j].suffix))
2271 sound_effect_properties[i] = element_action_info[j].value;
2272 sound_info[i].loop = element_action_info[j].is_loop_sound;
2278 // associate elements and some selected sound actions
2280 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2282 if (element_info[j].class_name)
2284 int len_class_text = strlen(element_info[j].class_name);
2286 if (len_class_text + 1 < len_effect_text &&
2287 strncmp(sound->token,
2288 element_info[j].class_name, len_class_text) == 0 &&
2289 sound->token[len_class_text] == '.')
2291 int sound_action_value = sound_effect_properties[i];
2293 element_info[j].sound[sound_action_value] = i;
2298 set_sound_parameters(i, sound->parameter);
2301 Debug("init:InitSoundInfo", "loop mode: %d ['%s']",
2302 sound_info[i].loop, sound->token);
2306 free(sound_effect_properties);
2309 static void InitGameModeMusicInfo(void)
2311 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
2312 int num_property_mappings = getMusicListPropertyMappingSize();
2313 int default_levelset_music = -1;
2316 // set values to -1 to identify later as "uninitialized" values
2317 for (i = 0; i < MAX_LEVELS; i++)
2318 levelset.music[i] = -1;
2319 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2322 // initialize gamemode/music mapping from static configuration
2323 for (i = 0; gamemode_to_music[i].music > -1; i++)
2325 int gamemode = gamemode_to_music[i].gamemode;
2326 int music = gamemode_to_music[i].music;
2329 gamemode = GAME_MODE_DEFAULT;
2331 menu.music[gamemode] = music;
2334 // initialize gamemode/music mapping from dynamic configuration
2335 for (i = 0; i < num_property_mappings; i++)
2337 int prefix = property_mapping[i].base_index;
2338 int gamemode = property_mapping[i].ext2_index;
2339 int level = property_mapping[i].ext3_index;
2340 int music = property_mapping[i].artwork_index;
2342 if (prefix < 0 || prefix >= NUM_MUSIC_PREFIXES)
2346 gamemode = GAME_MODE_DEFAULT;
2348 // level specific music only allowed for in-game music
2349 if (level != -1 && gamemode == GAME_MODE_DEFAULT)
2350 gamemode = GAME_MODE_PLAYING;
2355 default_levelset_music = music;
2358 if (gamemode == GAME_MODE_PLAYING || gamemode == GAME_MODE_DEFAULT)
2359 levelset.music[level] = music;
2360 if (gamemode != GAME_MODE_PLAYING)
2361 menu.music[gamemode] = music;
2364 // now set all '-1' values to menu specific default values
2365 // (undefined values of "levelset.music[]" might stay at "-1" to
2366 // allow dynamic selection of music files from music directory!)
2367 for (i = 0; i < MAX_LEVELS; i++)
2368 if (levelset.music[i] == -1)
2369 levelset.music[i] = default_levelset_music;
2370 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2371 if (menu.music[i] == -1)
2372 menu.music[i] = menu.music[GAME_MODE_DEFAULT];
2375 static void set_music_parameters(int music, char **parameter_raw)
2377 int parameter[NUM_MUS_ARGS];
2380 // get integer values from string parameters
2381 for (i = 0; i < NUM_MUS_ARGS; i++)
2383 get_parameter_value(parameter_raw[i],
2384 music_config_suffix[i].token,
2385 music_config_suffix[i].type);
2387 // explicit loop mode setting in configuration overrides default value
2388 if (parameter[MUS_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2389 music_info[music].loop = parameter[MUS_ARG_MODE_LOOP];
2392 static void InitMusicInfo(void)
2394 int num_music = getMusicListSize();
2397 checked_free(music_info);
2399 music_info = checked_calloc(num_music * sizeof(struct MusicInfo));
2401 for (i = 0; i < num_music; i++)
2403 struct FileInfo *music = getMusicListEntry(i);
2404 int len_music_text = strlen(music->token);
2406 music_info[i].loop = TRUE; // default: play music in loop mode
2408 // determine all loop music
2410 for (j = 0; music_prefix_info[j].prefix; j++)
2412 int len_prefix_text = strlen(music_prefix_info[j].prefix);
2414 if (len_prefix_text < len_music_text &&
2415 strncmp(music->token,
2416 music_prefix_info[j].prefix, len_prefix_text) == 0)
2418 music_info[i].loop = music_prefix_info[j].is_loop_music;
2424 set_music_parameters(i, music->parameter);
2429 static void InitGameInfoFromArtworkInfo(void)
2431 // special case: store initial value of custom artwork setting
2432 game.use_masked_elements_initial = game.use_masked_elements;
2435 static void ReinitializeGraphics(void)
2437 print_timestamp_init("ReinitializeGraphics");
2439 InitGfxTileSizeInfo(game.tile_size, TILESIZE);
2441 InitGraphicInfo(); // graphic properties mapping
2442 print_timestamp_time("InitGraphicInfo");
2443 InitElementGraphicInfo(); // element game graphic mapping
2444 print_timestamp_time("InitElementGraphicInfo");
2445 InitElementSpecialGraphicInfo(); // element special graphic mapping
2446 print_timestamp_time("InitElementSpecialGraphicInfo");
2448 InitElementSmallImages(); // scale elements to all needed sizes
2449 print_timestamp_time("InitElementSmallImages");
2450 InitScaledImages(); // scale all other images, if needed
2451 print_timestamp_time("InitScaledImages");
2452 InitBitmapPointers(); // set standard size bitmap pointers
2453 print_timestamp_time("InitBitmapPointers");
2454 InitFontGraphicInfo(); // initialize text drawing functions
2455 print_timestamp_time("InitFontGraphicInfo");
2456 InitGlobalAnimGraphicInfo(); // initialize global animation config
2457 print_timestamp_time("InitGlobalAnimGraphicInfo");
2459 InitImageTextures(); // create textures for certain images
2460 print_timestamp_time("InitImageTextures");
2462 InitGraphicInfo_EM(); // graphic mapping for EM engine
2463 print_timestamp_time("InitGraphicInfo_EM");
2465 InitGraphicCompatibilityInfo();
2466 print_timestamp_time("InitGraphicCompatibilityInfo");
2469 print_timestamp_time("InitGadgets");
2471 print_timestamp_time("InitDoors");
2473 InitGameInfoFromArtworkInfo();
2475 print_timestamp_done("ReinitializeGraphics");
2478 static void ReinitializeSounds(void)
2480 InitSoundInfo(); // sound properties mapping
2481 InitElementSoundInfo(); // element game sound mapping
2482 InitGameModeSoundInfo(); // game mode sound mapping
2483 InitGlobalAnimSoundInfo(); // global animation sound settings
2485 InitPlayLevelSound(); // internal game sound settings
2488 static void ReinitializeMusic(void)
2490 InitMusicInfo(); // music properties mapping
2491 InitGameModeMusicInfo(); // game mode music mapping
2492 InitGlobalAnimMusicInfo(); // global animation music settings
2495 static int get_special_property_bit(int element, int property_bit_nr)
2497 struct PropertyBitInfo
2503 static struct PropertyBitInfo pb_can_move_into_acid[] =
2505 // the player may be able fall into acid when gravity is activated
2510 { EL_SP_MURPHY, 0 },
2511 { EL_SOKOBAN_FIELD_PLAYER, 0 },
2513 // all elements that can move may be able to also move into acid
2516 { EL_BUG_RIGHT, 1 },
2519 { EL_SPACESHIP, 2 },
2520 { EL_SPACESHIP_LEFT, 2 },
2521 { EL_SPACESHIP_RIGHT, 2 },
2522 { EL_SPACESHIP_UP, 2 },
2523 { EL_SPACESHIP_DOWN, 2 },
2524 { EL_BD_BUTTERFLY, 3 },
2525 { EL_BD_BUTTERFLY_LEFT, 3 },
2526 { EL_BD_BUTTERFLY_RIGHT, 3 },
2527 { EL_BD_BUTTERFLY_UP, 3 },
2528 { EL_BD_BUTTERFLY_DOWN, 3 },
2529 { EL_BD_FIREFLY, 4 },
2530 { EL_BD_FIREFLY_LEFT, 4 },
2531 { EL_BD_FIREFLY_RIGHT, 4 },
2532 { EL_BD_FIREFLY_UP, 4 },
2533 { EL_BD_FIREFLY_DOWN, 4 },
2535 { EL_YAMYAM_LEFT, 5 },
2536 { EL_YAMYAM_RIGHT, 5 },
2537 { EL_YAMYAM_UP, 5 },
2538 { EL_YAMYAM_DOWN, 5 },
2539 { EL_DARK_YAMYAM, 6 },
2542 { EL_PACMAN_LEFT, 8 },
2543 { EL_PACMAN_RIGHT, 8 },
2544 { EL_PACMAN_UP, 8 },
2545 { EL_PACMAN_DOWN, 8 },
2547 { EL_MOLE_LEFT, 9 },
2548 { EL_MOLE_RIGHT, 9 },
2550 { EL_MOLE_DOWN, 9 },
2554 { EL_SATELLITE, 13 },
2555 { EL_SP_SNIKSNAK, 14 },
2556 { EL_SP_ELECTRON, 15 },
2559 { EL_SPRING_LEFT, 17 },
2560 { EL_SPRING_RIGHT, 17 },
2561 { EL_EMC_ANDROID, 18 },
2566 static struct PropertyBitInfo pb_dont_collide_with[] =
2568 { EL_SP_SNIKSNAK, 0 },
2569 { EL_SP_ELECTRON, 1 },
2577 struct PropertyBitInfo *pb_info;
2580 { EP_CAN_MOVE_INTO_ACID, pb_can_move_into_acid },
2581 { EP_DONT_COLLIDE_WITH, pb_dont_collide_with },
2586 struct PropertyBitInfo *pb_info = NULL;
2589 for (i = 0; pb_definition[i].bit_nr != -1; i++)
2590 if (pb_definition[i].bit_nr == property_bit_nr)
2591 pb_info = pb_definition[i].pb_info;
2593 if (pb_info == NULL)
2596 for (i = 0; pb_info[i].element != -1; i++)
2597 if (pb_info[i].element == element)
2598 return pb_info[i].bit_nr;
2603 void setBitfieldProperty(int *bitfield, int property_bit_nr, int element,
2604 boolean property_value)
2606 int bit_nr = get_special_property_bit(element, property_bit_nr);
2611 *bitfield |= (1 << bit_nr);
2613 *bitfield &= ~(1 << bit_nr);
2617 boolean getBitfieldProperty(int *bitfield, int property_bit_nr, int element)
2619 int bit_nr = get_special_property_bit(element, property_bit_nr);
2622 return ((*bitfield & (1 << bit_nr)) != 0);
2627 static void ResolveGroupElementExt(int group_element, int recursion_depth)
2629 static int group_nr;
2630 static struct ElementGroupInfo *group;
2631 struct ElementGroupInfo *actual_group = element_info[group_element].group;
2634 if (actual_group == NULL) // not yet initialized
2637 if (recursion_depth > NUM_GROUP_ELEMENTS) // recursion too deep
2639 Warn("recursion too deep when resolving group element %d",
2640 group_element - EL_GROUP_START + 1);
2642 // replace element which caused too deep recursion by question mark
2643 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
2648 if (recursion_depth == 0) // initialization
2650 group = actual_group;
2651 group_nr = GROUP_NR(group_element);
2653 group->num_elements_resolved = 0;
2654 group->choice_pos = 0;
2656 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2657 element_info[i].in_group[group_nr] = FALSE;
2660 for (i = 0; i < actual_group->num_elements; i++)
2662 int element = actual_group->element[i];
2664 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
2667 if (IS_GROUP_ELEMENT(element))
2668 ResolveGroupElementExt(element, recursion_depth + 1);
2671 group->element_resolved[group->num_elements_resolved++] = element;
2672 element_info[element].in_group[group_nr] = TRUE;
2677 void ResolveGroupElement(int group_element)
2679 ResolveGroupElementExt(group_element, 0);
2682 void InitElementPropertiesStatic(void)
2684 static boolean clipboard_elements_initialized = FALSE;
2686 static int ep_diggable[] =
2691 EL_SP_BUGGY_BASE_ACTIVATING,
2694 EL_INVISIBLE_SAND_ACTIVE,
2697 // !!! currently not diggable, but handled by 'ep_dont_run_into' !!!
2698 // (if amoeba can grow into anything diggable, maybe keep these out)
2703 EL_SP_BUGGY_BASE_ACTIVE,
2710 static int ep_collectible_only[] =
2732 EL_DYNABOMB_INCREASE_NUMBER,
2733 EL_DYNABOMB_INCREASE_SIZE,
2734 EL_DYNABOMB_INCREASE_POWER,
2752 // !!! handle separately !!!
2753 EL_DC_LANDMINE, // deadly when running into, but can be snapped
2759 static int ep_dont_run_into[] =
2761 // same elements as in 'ep_dont_touch'
2767 // same elements as in 'ep_dont_collide_with'
2779 // !!! maybe this should better be handled by 'ep_diggable' !!!
2784 EL_SP_BUGGY_BASE_ACTIVE,
2791 static int ep_dont_collide_with[] =
2793 // same elements as in 'ep_dont_touch'
2810 static int ep_dont_touch[] =
2820 static int ep_indestructible[] =
2824 EL_ACID_POOL_TOPLEFT,
2825 EL_ACID_POOL_TOPRIGHT,
2826 EL_ACID_POOL_BOTTOMLEFT,
2827 EL_ACID_POOL_BOTTOM,
2828 EL_ACID_POOL_BOTTOMRIGHT,
2829 EL_SP_HARDWARE_GRAY,
2830 EL_SP_HARDWARE_GREEN,
2831 EL_SP_HARDWARE_BLUE,
2833 EL_SP_HARDWARE_YELLOW,
2834 EL_SP_HARDWARE_BASE_1,
2835 EL_SP_HARDWARE_BASE_2,
2836 EL_SP_HARDWARE_BASE_3,
2837 EL_SP_HARDWARE_BASE_4,
2838 EL_SP_HARDWARE_BASE_5,
2839 EL_SP_HARDWARE_BASE_6,
2840 EL_INVISIBLE_STEELWALL,
2841 EL_INVISIBLE_STEELWALL_ACTIVE,
2842 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2843 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
2844 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
2845 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2846 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
2847 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
2848 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2849 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
2850 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
2851 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
2852 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
2853 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
2855 EL_LIGHT_SWITCH_ACTIVE,
2856 EL_SIGN_EXCLAMATION,
2857 EL_SIGN_RADIOACTIVITY,
2864 EL_SIGN_ENTRY_FORBIDDEN,
2865 EL_SIGN_EMERGENCY_EXIT,
2873 EL_STEEL_EXIT_CLOSED,
2875 EL_STEEL_EXIT_OPENING,
2876 EL_STEEL_EXIT_CLOSING,
2877 EL_EM_STEEL_EXIT_CLOSED,
2878 EL_EM_STEEL_EXIT_OPEN,
2879 EL_EM_STEEL_EXIT_OPENING,
2880 EL_EM_STEEL_EXIT_CLOSING,
2881 EL_DC_STEELWALL_1_LEFT,
2882 EL_DC_STEELWALL_1_RIGHT,
2883 EL_DC_STEELWALL_1_TOP,
2884 EL_DC_STEELWALL_1_BOTTOM,
2885 EL_DC_STEELWALL_1_HORIZONTAL,
2886 EL_DC_STEELWALL_1_VERTICAL,
2887 EL_DC_STEELWALL_1_TOPLEFT,
2888 EL_DC_STEELWALL_1_TOPRIGHT,
2889 EL_DC_STEELWALL_1_BOTTOMLEFT,
2890 EL_DC_STEELWALL_1_BOTTOMRIGHT,
2891 EL_DC_STEELWALL_1_TOPLEFT_2,
2892 EL_DC_STEELWALL_1_TOPRIGHT_2,
2893 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
2894 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
2895 EL_DC_STEELWALL_2_LEFT,
2896 EL_DC_STEELWALL_2_RIGHT,
2897 EL_DC_STEELWALL_2_TOP,
2898 EL_DC_STEELWALL_2_BOTTOM,
2899 EL_DC_STEELWALL_2_HORIZONTAL,
2900 EL_DC_STEELWALL_2_VERTICAL,
2901 EL_DC_STEELWALL_2_MIDDLE,
2902 EL_DC_STEELWALL_2_SINGLE,
2903 EL_STEELWALL_SLIPPERY,
2917 EL_GATE_1_GRAY_ACTIVE,
2918 EL_GATE_2_GRAY_ACTIVE,
2919 EL_GATE_3_GRAY_ACTIVE,
2920 EL_GATE_4_GRAY_ACTIVE,
2929 EL_EM_GATE_1_GRAY_ACTIVE,
2930 EL_EM_GATE_2_GRAY_ACTIVE,
2931 EL_EM_GATE_3_GRAY_ACTIVE,
2932 EL_EM_GATE_4_GRAY_ACTIVE,
2941 EL_EMC_GATE_5_GRAY_ACTIVE,
2942 EL_EMC_GATE_6_GRAY_ACTIVE,
2943 EL_EMC_GATE_7_GRAY_ACTIVE,
2944 EL_EMC_GATE_8_GRAY_ACTIVE,
2946 EL_DC_GATE_WHITE_GRAY,
2947 EL_DC_GATE_WHITE_GRAY_ACTIVE,
2948 EL_DC_GATE_FAKE_GRAY,
2950 EL_SWITCHGATE_OPENING,
2951 EL_SWITCHGATE_CLOSED,
2952 EL_SWITCHGATE_CLOSING,
2953 EL_DC_SWITCHGATE_SWITCH_UP,
2954 EL_DC_SWITCHGATE_SWITCH_DOWN,
2956 EL_TIMEGATE_OPENING,
2958 EL_TIMEGATE_CLOSING,
2959 EL_DC_TIMEGATE_SWITCH,
2960 EL_DC_TIMEGATE_SWITCH_ACTIVE,
2964 EL_TUBE_VERTICAL_LEFT,
2965 EL_TUBE_VERTICAL_RIGHT,
2966 EL_TUBE_HORIZONTAL_UP,
2967 EL_TUBE_HORIZONTAL_DOWN,
2972 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
2973 EL_EXPANDABLE_STEELWALL_VERTICAL,
2974 EL_EXPANDABLE_STEELWALL_ANY,
2979 static int ep_slippery[] =
2993 EL_ROBOT_WHEEL_ACTIVE,
2999 EL_ACID_POOL_TOPLEFT,
3000 EL_ACID_POOL_TOPRIGHT,
3010 EL_STEELWALL_SLIPPERY,
3013 EL_EMC_WALL_SLIPPERY_1,
3014 EL_EMC_WALL_SLIPPERY_2,
3015 EL_EMC_WALL_SLIPPERY_3,
3016 EL_EMC_WALL_SLIPPERY_4,
3018 EL_EMC_MAGIC_BALL_ACTIVE,
3023 static int ep_can_change[] =
3028 static int ep_can_move[] =
3030 // same elements as in 'pb_can_move_into_acid'
3053 static int ep_can_fall[] =
3068 EL_QUICKSAND_FAST_FULL,
3070 EL_BD_MAGIC_WALL_FULL,
3071 EL_DC_MAGIC_WALL_FULL,
3085 static int ep_can_smash_player[] =
3111 static int ep_can_smash_enemies[] =
3120 static int ep_can_smash_everything[] =
3129 static int ep_explodes_by_fire[] =
3131 // same elements as in 'ep_explodes_impact'
3136 // same elements as in 'ep_explodes_smashed'
3146 EL_EM_DYNAMITE_ACTIVE,
3147 EL_DYNABOMB_PLAYER_1_ACTIVE,
3148 EL_DYNABOMB_PLAYER_2_ACTIVE,
3149 EL_DYNABOMB_PLAYER_3_ACTIVE,
3150 EL_DYNABOMB_PLAYER_4_ACTIVE,
3151 EL_DYNABOMB_INCREASE_NUMBER,
3152 EL_DYNABOMB_INCREASE_SIZE,
3153 EL_DYNABOMB_INCREASE_POWER,
3154 EL_SP_DISK_RED_ACTIVE,
3168 static int ep_explodes_smashed[] =
3170 // same elements as in 'ep_explodes_impact'
3184 static int ep_explodes_impact[] =
3193 static int ep_walkable_over[] =
3213 EL_SOKOBAN_FIELD_EMPTY,
3220 EL_EM_STEEL_EXIT_OPEN,
3221 EL_EM_STEEL_EXIT_OPENING,
3230 EL_GATE_1_GRAY_ACTIVE,
3231 EL_GATE_2_GRAY_ACTIVE,
3232 EL_GATE_3_GRAY_ACTIVE,
3233 EL_GATE_4_GRAY_ACTIVE,
3241 static int ep_walkable_inside[] =
3246 EL_TUBE_VERTICAL_LEFT,
3247 EL_TUBE_VERTICAL_RIGHT,
3248 EL_TUBE_HORIZONTAL_UP,
3249 EL_TUBE_HORIZONTAL_DOWN,
3258 static int ep_walkable_under[] =
3263 static int ep_passable_over[] =
3273 EL_EM_GATE_1_GRAY_ACTIVE,
3274 EL_EM_GATE_2_GRAY_ACTIVE,
3275 EL_EM_GATE_3_GRAY_ACTIVE,
3276 EL_EM_GATE_4_GRAY_ACTIVE,
3285 EL_EMC_GATE_5_GRAY_ACTIVE,
3286 EL_EMC_GATE_6_GRAY_ACTIVE,
3287 EL_EMC_GATE_7_GRAY_ACTIVE,
3288 EL_EMC_GATE_8_GRAY_ACTIVE,
3290 EL_DC_GATE_WHITE_GRAY,
3291 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3298 static int ep_passable_inside[] =
3304 EL_SP_PORT_HORIZONTAL,
3305 EL_SP_PORT_VERTICAL,
3307 EL_SP_GRAVITY_PORT_LEFT,
3308 EL_SP_GRAVITY_PORT_RIGHT,
3309 EL_SP_GRAVITY_PORT_UP,
3310 EL_SP_GRAVITY_PORT_DOWN,
3311 EL_SP_GRAVITY_ON_PORT_LEFT,
3312 EL_SP_GRAVITY_ON_PORT_RIGHT,
3313 EL_SP_GRAVITY_ON_PORT_UP,
3314 EL_SP_GRAVITY_ON_PORT_DOWN,
3315 EL_SP_GRAVITY_OFF_PORT_LEFT,
3316 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3317 EL_SP_GRAVITY_OFF_PORT_UP,
3318 EL_SP_GRAVITY_OFF_PORT_DOWN,
3323 static int ep_passable_under[] =
3328 static int ep_droppable[] =
3333 static int ep_explodes_1x1_old[] =
3338 static int ep_pushable[] =
3350 EL_SOKOBAN_FIELD_FULL,
3359 static int ep_explodes_cross_old[] =
3364 static int ep_protected[] =
3366 // same elements as in 'ep_walkable_inside'
3370 EL_TUBE_VERTICAL_LEFT,
3371 EL_TUBE_VERTICAL_RIGHT,
3372 EL_TUBE_HORIZONTAL_UP,
3373 EL_TUBE_HORIZONTAL_DOWN,
3379 // same elements as in 'ep_passable_over'
3388 EL_EM_GATE_1_GRAY_ACTIVE,
3389 EL_EM_GATE_2_GRAY_ACTIVE,
3390 EL_EM_GATE_3_GRAY_ACTIVE,
3391 EL_EM_GATE_4_GRAY_ACTIVE,
3400 EL_EMC_GATE_5_GRAY_ACTIVE,
3401 EL_EMC_GATE_6_GRAY_ACTIVE,
3402 EL_EMC_GATE_7_GRAY_ACTIVE,
3403 EL_EMC_GATE_8_GRAY_ACTIVE,
3405 EL_DC_GATE_WHITE_GRAY,
3406 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3410 // same elements as in 'ep_passable_inside'
3415 EL_SP_PORT_HORIZONTAL,
3416 EL_SP_PORT_VERTICAL,
3418 EL_SP_GRAVITY_PORT_LEFT,
3419 EL_SP_GRAVITY_PORT_RIGHT,
3420 EL_SP_GRAVITY_PORT_UP,
3421 EL_SP_GRAVITY_PORT_DOWN,
3422 EL_SP_GRAVITY_ON_PORT_LEFT,
3423 EL_SP_GRAVITY_ON_PORT_RIGHT,
3424 EL_SP_GRAVITY_ON_PORT_UP,
3425 EL_SP_GRAVITY_ON_PORT_DOWN,
3426 EL_SP_GRAVITY_OFF_PORT_LEFT,
3427 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3428 EL_SP_GRAVITY_OFF_PORT_UP,
3429 EL_SP_GRAVITY_OFF_PORT_DOWN,
3434 static int ep_throwable[] =
3439 static int ep_can_explode[] =
3441 // same elements as in 'ep_explodes_impact'
3446 // same elements as in 'ep_explodes_smashed'
3452 // elements that can explode by explosion or by dragonfire
3456 EL_EM_DYNAMITE_ACTIVE,
3457 EL_DYNABOMB_PLAYER_1_ACTIVE,
3458 EL_DYNABOMB_PLAYER_2_ACTIVE,
3459 EL_DYNABOMB_PLAYER_3_ACTIVE,
3460 EL_DYNABOMB_PLAYER_4_ACTIVE,
3461 EL_DYNABOMB_INCREASE_NUMBER,
3462 EL_DYNABOMB_INCREASE_SIZE,
3463 EL_DYNABOMB_INCREASE_POWER,
3464 EL_SP_DISK_RED_ACTIVE,
3472 // elements that can explode only by explosion
3478 static int ep_gravity_reachable[] =
3484 EL_INVISIBLE_SAND_ACTIVE,
3489 EL_SP_PORT_HORIZONTAL,
3490 EL_SP_PORT_VERTICAL,
3492 EL_SP_GRAVITY_PORT_LEFT,
3493 EL_SP_GRAVITY_PORT_RIGHT,
3494 EL_SP_GRAVITY_PORT_UP,
3495 EL_SP_GRAVITY_PORT_DOWN,
3496 EL_SP_GRAVITY_ON_PORT_LEFT,
3497 EL_SP_GRAVITY_ON_PORT_RIGHT,
3498 EL_SP_GRAVITY_ON_PORT_UP,
3499 EL_SP_GRAVITY_ON_PORT_DOWN,
3500 EL_SP_GRAVITY_OFF_PORT_LEFT,
3501 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3502 EL_SP_GRAVITY_OFF_PORT_UP,
3503 EL_SP_GRAVITY_OFF_PORT_DOWN,
3509 static int ep_empty_space[] =
3532 static int ep_player[] =
3539 EL_SOKOBAN_FIELD_PLAYER,
3545 static int ep_can_pass_magic_wall[] =
3559 static int ep_can_pass_dc_magic_wall[] =
3575 static int ep_switchable[] =
3579 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3580 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3581 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3582 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3583 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3584 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3585 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3586 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3587 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3588 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3589 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3590 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3591 EL_SWITCHGATE_SWITCH_UP,
3592 EL_SWITCHGATE_SWITCH_DOWN,
3593 EL_DC_SWITCHGATE_SWITCH_UP,
3594 EL_DC_SWITCHGATE_SWITCH_DOWN,
3596 EL_LIGHT_SWITCH_ACTIVE,
3598 EL_DC_TIMEGATE_SWITCH,
3599 EL_BALLOON_SWITCH_LEFT,
3600 EL_BALLOON_SWITCH_RIGHT,
3601 EL_BALLOON_SWITCH_UP,
3602 EL_BALLOON_SWITCH_DOWN,
3603 EL_BALLOON_SWITCH_ANY,
3604 EL_BALLOON_SWITCH_NONE,
3607 EL_EMC_MAGIC_BALL_SWITCH,
3608 EL_EMC_MAGIC_BALL_SWITCH_ACTIVE,
3613 static int ep_bd_element[] =
3647 static int ep_sp_element[] =
3649 // should always be valid
3652 // standard classic Supaplex elements
3659 EL_SP_HARDWARE_GRAY,
3667 EL_SP_GRAVITY_PORT_RIGHT,
3668 EL_SP_GRAVITY_PORT_DOWN,
3669 EL_SP_GRAVITY_PORT_LEFT,
3670 EL_SP_GRAVITY_PORT_UP,
3675 EL_SP_PORT_VERTICAL,
3676 EL_SP_PORT_HORIZONTAL,
3682 EL_SP_HARDWARE_BASE_1,
3683 EL_SP_HARDWARE_GREEN,
3684 EL_SP_HARDWARE_BLUE,
3686 EL_SP_HARDWARE_YELLOW,
3687 EL_SP_HARDWARE_BASE_2,
3688 EL_SP_HARDWARE_BASE_3,
3689 EL_SP_HARDWARE_BASE_4,
3690 EL_SP_HARDWARE_BASE_5,
3691 EL_SP_HARDWARE_BASE_6,
3695 // additional elements that appeared in newer Supaplex levels
3698 // additional gravity port elements (not switching, but setting gravity)
3699 EL_SP_GRAVITY_ON_PORT_LEFT,
3700 EL_SP_GRAVITY_ON_PORT_RIGHT,
3701 EL_SP_GRAVITY_ON_PORT_UP,
3702 EL_SP_GRAVITY_ON_PORT_DOWN,
3703 EL_SP_GRAVITY_OFF_PORT_LEFT,
3704 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3705 EL_SP_GRAVITY_OFF_PORT_UP,
3706 EL_SP_GRAVITY_OFF_PORT_DOWN,
3708 // more than one Murphy in a level results in an inactive clone
3711 // runtime Supaplex elements
3712 EL_SP_DISK_RED_ACTIVE,
3713 EL_SP_TERMINAL_ACTIVE,
3714 EL_SP_BUGGY_BASE_ACTIVATING,
3715 EL_SP_BUGGY_BASE_ACTIVE,
3722 static int ep_sb_element[] =
3727 EL_SOKOBAN_FIELD_EMPTY,
3728 EL_SOKOBAN_FIELD_FULL,
3729 EL_SOKOBAN_FIELD_PLAYER,
3734 EL_INVISIBLE_STEELWALL,
3739 static int ep_gem[] =
3751 static int ep_food_dark_yamyam[] =
3779 static int ep_food_penguin[] =
3793 static int ep_food_pig[] =
3805 static int ep_historic_wall[] =
3816 EL_GATE_1_GRAY_ACTIVE,
3817 EL_GATE_2_GRAY_ACTIVE,
3818 EL_GATE_3_GRAY_ACTIVE,
3819 EL_GATE_4_GRAY_ACTIVE,
3828 EL_EM_GATE_1_GRAY_ACTIVE,
3829 EL_EM_GATE_2_GRAY_ACTIVE,
3830 EL_EM_GATE_3_GRAY_ACTIVE,
3831 EL_EM_GATE_4_GRAY_ACTIVE,
3838 EL_EXPANDABLE_WALL_HORIZONTAL,
3839 EL_EXPANDABLE_WALL_VERTICAL,
3840 EL_EXPANDABLE_WALL_ANY,
3841 EL_EXPANDABLE_WALL_GROWING,
3842 EL_BD_EXPANDABLE_WALL,
3849 EL_SP_HARDWARE_GRAY,
3850 EL_SP_HARDWARE_GREEN,
3851 EL_SP_HARDWARE_BLUE,
3853 EL_SP_HARDWARE_YELLOW,
3854 EL_SP_HARDWARE_BASE_1,
3855 EL_SP_HARDWARE_BASE_2,
3856 EL_SP_HARDWARE_BASE_3,
3857 EL_SP_HARDWARE_BASE_4,
3858 EL_SP_HARDWARE_BASE_5,
3859 EL_SP_HARDWARE_BASE_6,
3861 EL_SP_TERMINAL_ACTIVE,
3864 EL_INVISIBLE_STEELWALL,
3865 EL_INVISIBLE_STEELWALL_ACTIVE,
3867 EL_INVISIBLE_WALL_ACTIVE,
3868 EL_STEELWALL_SLIPPERY,
3885 static int ep_historic_solid[] =
3889 EL_EXPANDABLE_WALL_HORIZONTAL,
3890 EL_EXPANDABLE_WALL_VERTICAL,
3891 EL_EXPANDABLE_WALL_ANY,
3892 EL_BD_EXPANDABLE_WALL,
3905 EL_QUICKSAND_FILLING,
3906 EL_QUICKSAND_EMPTYING,
3908 EL_MAGIC_WALL_ACTIVE,
3909 EL_MAGIC_WALL_EMPTYING,
3910 EL_MAGIC_WALL_FILLING,
3914 EL_BD_MAGIC_WALL_ACTIVE,
3915 EL_BD_MAGIC_WALL_EMPTYING,
3916 EL_BD_MAGIC_WALL_FULL,
3917 EL_BD_MAGIC_WALL_FILLING,
3918 EL_BD_MAGIC_WALL_DEAD,
3927 EL_SP_TERMINAL_ACTIVE,
3931 EL_INVISIBLE_WALL_ACTIVE,
3932 EL_SWITCHGATE_SWITCH_UP,
3933 EL_SWITCHGATE_SWITCH_DOWN,
3935 EL_TIMEGATE_SWITCH_ACTIVE,
3947 // the following elements are a direct copy of "indestructible" elements,
3948 // except "EL_ACID", which is "indestructible", but not "solid"!
3953 EL_ACID_POOL_TOPLEFT,
3954 EL_ACID_POOL_TOPRIGHT,
3955 EL_ACID_POOL_BOTTOMLEFT,
3956 EL_ACID_POOL_BOTTOM,
3957 EL_ACID_POOL_BOTTOMRIGHT,
3958 EL_SP_HARDWARE_GRAY,
3959 EL_SP_HARDWARE_GREEN,
3960 EL_SP_HARDWARE_BLUE,
3962 EL_SP_HARDWARE_YELLOW,
3963 EL_SP_HARDWARE_BASE_1,
3964 EL_SP_HARDWARE_BASE_2,
3965 EL_SP_HARDWARE_BASE_3,
3966 EL_SP_HARDWARE_BASE_4,
3967 EL_SP_HARDWARE_BASE_5,
3968 EL_SP_HARDWARE_BASE_6,
3969 EL_INVISIBLE_STEELWALL,
3970 EL_INVISIBLE_STEELWALL_ACTIVE,
3971 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3972 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3973 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3974 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3975 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3976 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3977 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3978 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3979 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3980 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3981 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3982 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3984 EL_LIGHT_SWITCH_ACTIVE,
3985 EL_SIGN_EXCLAMATION,
3986 EL_SIGN_RADIOACTIVITY,
3993 EL_SIGN_ENTRY_FORBIDDEN,
3994 EL_SIGN_EMERGENCY_EXIT,
4002 EL_STEEL_EXIT_CLOSED,
4004 EL_STEEL_EXIT_OPENING,
4005 EL_STEEL_EXIT_CLOSING,
4006 EL_EM_STEEL_EXIT_CLOSED,
4007 EL_EM_STEEL_EXIT_OPEN,
4008 EL_EM_STEEL_EXIT_OPENING,
4009 EL_EM_STEEL_EXIT_CLOSING,
4010 EL_DC_STEELWALL_1_LEFT,
4011 EL_DC_STEELWALL_1_RIGHT,
4012 EL_DC_STEELWALL_1_TOP,
4013 EL_DC_STEELWALL_1_BOTTOM,
4014 EL_DC_STEELWALL_1_HORIZONTAL,
4015 EL_DC_STEELWALL_1_VERTICAL,
4016 EL_DC_STEELWALL_1_TOPLEFT,
4017 EL_DC_STEELWALL_1_TOPRIGHT,
4018 EL_DC_STEELWALL_1_BOTTOMLEFT,
4019 EL_DC_STEELWALL_1_BOTTOMRIGHT,
4020 EL_DC_STEELWALL_1_TOPLEFT_2,
4021 EL_DC_STEELWALL_1_TOPRIGHT_2,
4022 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
4023 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
4024 EL_DC_STEELWALL_2_LEFT,
4025 EL_DC_STEELWALL_2_RIGHT,
4026 EL_DC_STEELWALL_2_TOP,
4027 EL_DC_STEELWALL_2_BOTTOM,
4028 EL_DC_STEELWALL_2_HORIZONTAL,
4029 EL_DC_STEELWALL_2_VERTICAL,
4030 EL_DC_STEELWALL_2_MIDDLE,
4031 EL_DC_STEELWALL_2_SINGLE,
4032 EL_STEELWALL_SLIPPERY,
4046 EL_GATE_1_GRAY_ACTIVE,
4047 EL_GATE_2_GRAY_ACTIVE,
4048 EL_GATE_3_GRAY_ACTIVE,
4049 EL_GATE_4_GRAY_ACTIVE,
4058 EL_EM_GATE_1_GRAY_ACTIVE,
4059 EL_EM_GATE_2_GRAY_ACTIVE,
4060 EL_EM_GATE_3_GRAY_ACTIVE,
4061 EL_EM_GATE_4_GRAY_ACTIVE,
4070 EL_EMC_GATE_5_GRAY_ACTIVE,
4071 EL_EMC_GATE_6_GRAY_ACTIVE,
4072 EL_EMC_GATE_7_GRAY_ACTIVE,
4073 EL_EMC_GATE_8_GRAY_ACTIVE,
4075 EL_DC_GATE_WHITE_GRAY,
4076 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4077 EL_DC_GATE_FAKE_GRAY,
4079 EL_SWITCHGATE_OPENING,
4080 EL_SWITCHGATE_CLOSED,
4081 EL_SWITCHGATE_CLOSING,
4082 EL_DC_SWITCHGATE_SWITCH_UP,
4083 EL_DC_SWITCHGATE_SWITCH_DOWN,
4085 EL_TIMEGATE_OPENING,
4087 EL_TIMEGATE_CLOSING,
4088 EL_DC_TIMEGATE_SWITCH,
4089 EL_DC_TIMEGATE_SWITCH_ACTIVE,
4093 EL_TUBE_VERTICAL_LEFT,
4094 EL_TUBE_VERTICAL_RIGHT,
4095 EL_TUBE_HORIZONTAL_UP,
4096 EL_TUBE_HORIZONTAL_DOWN,
4101 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
4102 EL_EXPANDABLE_STEELWALL_VERTICAL,
4103 EL_EXPANDABLE_STEELWALL_ANY,
4108 static int ep_classic_enemy[] =
4125 static int ep_belt[] =
4127 EL_CONVEYOR_BELT_1_LEFT,
4128 EL_CONVEYOR_BELT_1_MIDDLE,
4129 EL_CONVEYOR_BELT_1_RIGHT,
4130 EL_CONVEYOR_BELT_2_LEFT,
4131 EL_CONVEYOR_BELT_2_MIDDLE,
4132 EL_CONVEYOR_BELT_2_RIGHT,
4133 EL_CONVEYOR_BELT_3_LEFT,
4134 EL_CONVEYOR_BELT_3_MIDDLE,
4135 EL_CONVEYOR_BELT_3_RIGHT,
4136 EL_CONVEYOR_BELT_4_LEFT,
4137 EL_CONVEYOR_BELT_4_MIDDLE,
4138 EL_CONVEYOR_BELT_4_RIGHT,
4143 static int ep_belt_active[] =
4145 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4146 EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE,
4147 EL_CONVEYOR_BELT_1_RIGHT_ACTIVE,
4148 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4149 EL_CONVEYOR_BELT_2_MIDDLE_ACTIVE,
4150 EL_CONVEYOR_BELT_2_RIGHT_ACTIVE,
4151 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4152 EL_CONVEYOR_BELT_3_MIDDLE_ACTIVE,
4153 EL_CONVEYOR_BELT_3_RIGHT_ACTIVE,
4154 EL_CONVEYOR_BELT_4_LEFT_ACTIVE,
4155 EL_CONVEYOR_BELT_4_MIDDLE_ACTIVE,
4156 EL_CONVEYOR_BELT_4_RIGHT_ACTIVE,
4161 static int ep_belt_switch[] =
4163 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4164 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4165 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4166 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4167 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4168 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4169 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4170 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4171 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4172 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4173 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4174 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4179 static int ep_tube[] =
4186 EL_TUBE_HORIZONTAL_UP,
4187 EL_TUBE_HORIZONTAL_DOWN,
4189 EL_TUBE_VERTICAL_LEFT,
4190 EL_TUBE_VERTICAL_RIGHT,
4196 static int ep_acid_pool[] =
4198 EL_ACID_POOL_TOPLEFT,
4199 EL_ACID_POOL_TOPRIGHT,
4200 EL_ACID_POOL_BOTTOMLEFT,
4201 EL_ACID_POOL_BOTTOM,
4202 EL_ACID_POOL_BOTTOMRIGHT,
4207 static int ep_keygate[] =
4217 EL_GATE_1_GRAY_ACTIVE,
4218 EL_GATE_2_GRAY_ACTIVE,
4219 EL_GATE_3_GRAY_ACTIVE,
4220 EL_GATE_4_GRAY_ACTIVE,
4229 EL_EM_GATE_1_GRAY_ACTIVE,
4230 EL_EM_GATE_2_GRAY_ACTIVE,
4231 EL_EM_GATE_3_GRAY_ACTIVE,
4232 EL_EM_GATE_4_GRAY_ACTIVE,
4241 EL_EMC_GATE_5_GRAY_ACTIVE,
4242 EL_EMC_GATE_6_GRAY_ACTIVE,
4243 EL_EMC_GATE_7_GRAY_ACTIVE,
4244 EL_EMC_GATE_8_GRAY_ACTIVE,
4246 EL_DC_GATE_WHITE_GRAY,
4247 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4252 static int ep_amoeboid[] =
4264 static int ep_amoebalive[] =
4275 static int ep_has_editor_content[] =
4281 EL_SOKOBAN_FIELD_PLAYER,
4299 static int ep_can_turn_each_move[] =
4301 // !!! do something with this one !!!
4305 static int ep_can_grow[] =
4319 static int ep_active_bomb[] =
4322 EL_EM_DYNAMITE_ACTIVE,
4323 EL_DYNABOMB_PLAYER_1_ACTIVE,
4324 EL_DYNABOMB_PLAYER_2_ACTIVE,
4325 EL_DYNABOMB_PLAYER_3_ACTIVE,
4326 EL_DYNABOMB_PLAYER_4_ACTIVE,
4327 EL_SP_DISK_RED_ACTIVE,
4332 static int ep_inactive[] =
4358 EL_QUICKSAND_FAST_EMPTY,
4381 EL_GATE_1_GRAY_ACTIVE,
4382 EL_GATE_2_GRAY_ACTIVE,
4383 EL_GATE_3_GRAY_ACTIVE,
4384 EL_GATE_4_GRAY_ACTIVE,
4393 EL_EM_GATE_1_GRAY_ACTIVE,
4394 EL_EM_GATE_2_GRAY_ACTIVE,
4395 EL_EM_GATE_3_GRAY_ACTIVE,
4396 EL_EM_GATE_4_GRAY_ACTIVE,
4405 EL_EMC_GATE_5_GRAY_ACTIVE,
4406 EL_EMC_GATE_6_GRAY_ACTIVE,
4407 EL_EMC_GATE_7_GRAY_ACTIVE,
4408 EL_EMC_GATE_8_GRAY_ACTIVE,
4410 EL_DC_GATE_WHITE_GRAY,
4411 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4412 EL_DC_GATE_FAKE_GRAY,
4415 EL_INVISIBLE_STEELWALL,
4423 EL_WALL_EMERALD_YELLOW,
4424 EL_DYNABOMB_INCREASE_NUMBER,
4425 EL_DYNABOMB_INCREASE_SIZE,
4426 EL_DYNABOMB_INCREASE_POWER,
4430 EL_SOKOBAN_FIELD_EMPTY,
4431 EL_SOKOBAN_FIELD_FULL,
4432 EL_WALL_EMERALD_RED,
4433 EL_WALL_EMERALD_PURPLE,
4434 EL_ACID_POOL_TOPLEFT,
4435 EL_ACID_POOL_TOPRIGHT,
4436 EL_ACID_POOL_BOTTOMLEFT,
4437 EL_ACID_POOL_BOTTOM,
4438 EL_ACID_POOL_BOTTOMRIGHT,
4442 EL_BD_MAGIC_WALL_DEAD,
4444 EL_DC_MAGIC_WALL_DEAD,
4445 EL_AMOEBA_TO_DIAMOND,
4453 EL_SP_GRAVITY_PORT_RIGHT,
4454 EL_SP_GRAVITY_PORT_DOWN,
4455 EL_SP_GRAVITY_PORT_LEFT,
4456 EL_SP_GRAVITY_PORT_UP,
4457 EL_SP_PORT_HORIZONTAL,
4458 EL_SP_PORT_VERTICAL,
4469 EL_SP_HARDWARE_GRAY,
4470 EL_SP_HARDWARE_GREEN,
4471 EL_SP_HARDWARE_BLUE,
4473 EL_SP_HARDWARE_YELLOW,
4474 EL_SP_HARDWARE_BASE_1,
4475 EL_SP_HARDWARE_BASE_2,
4476 EL_SP_HARDWARE_BASE_3,
4477 EL_SP_HARDWARE_BASE_4,
4478 EL_SP_HARDWARE_BASE_5,
4479 EL_SP_HARDWARE_BASE_6,
4480 EL_SP_GRAVITY_ON_PORT_LEFT,
4481 EL_SP_GRAVITY_ON_PORT_RIGHT,
4482 EL_SP_GRAVITY_ON_PORT_UP,
4483 EL_SP_GRAVITY_ON_PORT_DOWN,
4484 EL_SP_GRAVITY_OFF_PORT_LEFT,
4485 EL_SP_GRAVITY_OFF_PORT_RIGHT,
4486 EL_SP_GRAVITY_OFF_PORT_UP,
4487 EL_SP_GRAVITY_OFF_PORT_DOWN,
4488 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4489 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4490 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4491 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4492 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4493 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4494 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4495 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4496 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4497 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4498 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4499 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4500 EL_SIGN_EXCLAMATION,
4501 EL_SIGN_RADIOACTIVITY,
4508 EL_SIGN_ENTRY_FORBIDDEN,
4509 EL_SIGN_EMERGENCY_EXIT,
4517 EL_DC_STEELWALL_1_LEFT,
4518 EL_DC_STEELWALL_1_RIGHT,
4519 EL_DC_STEELWALL_1_TOP,
4520 EL_DC_STEELWALL_1_BOTTOM,
4521 EL_DC_STEELWALL_1_HORIZONTAL,
4522 EL_DC_STEELWALL_1_VERTICAL,
4523 EL_DC_STEELWALL_1_TOPLEFT,
4524 EL_DC_STEELWALL_1_TOPRIGHT,
4525 EL_DC_STEELWALL_1_BOTTOMLEFT,
4526 EL_DC_STEELWALL_1_BOTTOMRIGHT,
4527 EL_DC_STEELWALL_1_TOPLEFT_2,
4528 EL_DC_STEELWALL_1_TOPRIGHT_2,
4529 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
4530 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
4531 EL_DC_STEELWALL_2_LEFT,
4532 EL_DC_STEELWALL_2_RIGHT,
4533 EL_DC_STEELWALL_2_TOP,
4534 EL_DC_STEELWALL_2_BOTTOM,
4535 EL_DC_STEELWALL_2_HORIZONTAL,
4536 EL_DC_STEELWALL_2_VERTICAL,
4537 EL_DC_STEELWALL_2_MIDDLE,
4538 EL_DC_STEELWALL_2_SINGLE,
4539 EL_STEELWALL_SLIPPERY,
4544 EL_EMC_WALL_SLIPPERY_1,
4545 EL_EMC_WALL_SLIPPERY_2,
4546 EL_EMC_WALL_SLIPPERY_3,
4547 EL_EMC_WALL_SLIPPERY_4,
4568 static int ep_em_slippery_wall[] =
4573 static int ep_gfx_crumbled[] =
4584 static int ep_editor_cascade_active[] =
4586 EL_INTERNAL_CASCADE_BD_ACTIVE,
4587 EL_INTERNAL_CASCADE_EM_ACTIVE,
4588 EL_INTERNAL_CASCADE_EMC_ACTIVE,
4589 EL_INTERNAL_CASCADE_RND_ACTIVE,
4590 EL_INTERNAL_CASCADE_SB_ACTIVE,
4591 EL_INTERNAL_CASCADE_SP_ACTIVE,
4592 EL_INTERNAL_CASCADE_DC_ACTIVE,
4593 EL_INTERNAL_CASCADE_DX_ACTIVE,
4594 EL_INTERNAL_CASCADE_MM_ACTIVE,
4595 EL_INTERNAL_CASCADE_DF_ACTIVE,
4596 EL_INTERNAL_CASCADE_CHARS_ACTIVE,
4597 EL_INTERNAL_CASCADE_STEEL_CHARS_ACTIVE,
4598 EL_INTERNAL_CASCADE_CE_ACTIVE,
4599 EL_INTERNAL_CASCADE_GE_ACTIVE,
4600 EL_INTERNAL_CASCADE_ES_ACTIVE,
4601 EL_INTERNAL_CASCADE_REF_ACTIVE,
4602 EL_INTERNAL_CASCADE_USER_ACTIVE,
4603 EL_INTERNAL_CASCADE_DYNAMIC_ACTIVE,
4608 static int ep_editor_cascade_inactive[] =
4610 EL_INTERNAL_CASCADE_BD,
4611 EL_INTERNAL_CASCADE_EM,
4612 EL_INTERNAL_CASCADE_EMC,
4613 EL_INTERNAL_CASCADE_RND,
4614 EL_INTERNAL_CASCADE_SB,
4615 EL_INTERNAL_CASCADE_SP,
4616 EL_INTERNAL_CASCADE_DC,
4617 EL_INTERNAL_CASCADE_DX,
4618 EL_INTERNAL_CASCADE_MM,
4619 EL_INTERNAL_CASCADE_DF,
4620 EL_INTERNAL_CASCADE_CHARS,
4621 EL_INTERNAL_CASCADE_STEEL_CHARS,
4622 EL_INTERNAL_CASCADE_CE,
4623 EL_INTERNAL_CASCADE_GE,
4624 EL_INTERNAL_CASCADE_ES,
4625 EL_INTERNAL_CASCADE_REF,
4626 EL_INTERNAL_CASCADE_USER,
4627 EL_INTERNAL_CASCADE_DYNAMIC,
4632 static int ep_obsolete[] =
4636 EL_EM_KEY_1_FILE_OBSOLETE,
4637 EL_EM_KEY_2_FILE_OBSOLETE,
4638 EL_EM_KEY_3_FILE_OBSOLETE,
4639 EL_EM_KEY_4_FILE_OBSOLETE,
4640 EL_ENVELOPE_OBSOLETE,
4649 } element_properties[] =
4651 { ep_diggable, EP_DIGGABLE },
4652 { ep_collectible_only, EP_COLLECTIBLE_ONLY },
4653 { ep_dont_run_into, EP_DONT_RUN_INTO },
4654 { ep_dont_collide_with, EP_DONT_COLLIDE_WITH },
4655 { ep_dont_touch, EP_DONT_TOUCH },
4656 { ep_indestructible, EP_INDESTRUCTIBLE },
4657 { ep_slippery, EP_SLIPPERY },
4658 { ep_can_change, EP_CAN_CHANGE },
4659 { ep_can_move, EP_CAN_MOVE },
4660 { ep_can_fall, EP_CAN_FALL },
4661 { ep_can_smash_player, EP_CAN_SMASH_PLAYER },
4662 { ep_can_smash_enemies, EP_CAN_SMASH_ENEMIES },
4663 { ep_can_smash_everything, EP_CAN_SMASH_EVERYTHING },
4664 { ep_explodes_by_fire, EP_EXPLODES_BY_FIRE },
4665 { ep_explodes_smashed, EP_EXPLODES_SMASHED },
4666 { ep_explodes_impact, EP_EXPLODES_IMPACT },
4667 { ep_walkable_over, EP_WALKABLE_OVER },
4668 { ep_walkable_inside, EP_WALKABLE_INSIDE },
4669 { ep_walkable_under, EP_WALKABLE_UNDER },
4670 { ep_passable_over, EP_PASSABLE_OVER },
4671 { ep_passable_inside, EP_PASSABLE_INSIDE },
4672 { ep_passable_under, EP_PASSABLE_UNDER },
4673 { ep_droppable, EP_DROPPABLE },
4674 { ep_explodes_1x1_old, EP_EXPLODES_1X1_OLD },
4675 { ep_pushable, EP_PUSHABLE },
4676 { ep_explodes_cross_old, EP_EXPLODES_CROSS_OLD },
4677 { ep_protected, EP_PROTECTED },
4678 { ep_throwable, EP_THROWABLE },
4679 { ep_can_explode, EP_CAN_EXPLODE },
4680 { ep_gravity_reachable, EP_GRAVITY_REACHABLE },
4682 { ep_empty_space, EP_EMPTY_SPACE },
4683 { ep_player, EP_PLAYER },
4684 { ep_can_pass_magic_wall, EP_CAN_PASS_MAGIC_WALL },
4685 { ep_can_pass_dc_magic_wall, EP_CAN_PASS_DC_MAGIC_WALL },
4686 { ep_switchable, EP_SWITCHABLE },
4687 { ep_bd_element, EP_BD_ELEMENT },
4688 { ep_sp_element, EP_SP_ELEMENT },
4689 { ep_sb_element, EP_SB_ELEMENT },
4691 { ep_food_dark_yamyam, EP_FOOD_DARK_YAMYAM },
4692 { ep_food_penguin, EP_FOOD_PENGUIN },
4693 { ep_food_pig, EP_FOOD_PIG },
4694 { ep_historic_wall, EP_HISTORIC_WALL },
4695 { ep_historic_solid, EP_HISTORIC_SOLID },
4696 { ep_classic_enemy, EP_CLASSIC_ENEMY },
4697 { ep_belt, EP_BELT },
4698 { ep_belt_active, EP_BELT_ACTIVE },
4699 { ep_belt_switch, EP_BELT_SWITCH },
4700 { ep_tube, EP_TUBE },
4701 { ep_acid_pool, EP_ACID_POOL },
4702 { ep_keygate, EP_KEYGATE },
4703 { ep_amoeboid, EP_AMOEBOID },
4704 { ep_amoebalive, EP_AMOEBALIVE },
4705 { ep_has_editor_content, EP_HAS_EDITOR_CONTENT },
4706 { ep_can_turn_each_move, EP_CAN_TURN_EACH_MOVE },
4707 { ep_can_grow, EP_CAN_GROW },
4708 { ep_active_bomb, EP_ACTIVE_BOMB },
4709 { ep_inactive, EP_INACTIVE },
4711 { ep_em_slippery_wall, EP_EM_SLIPPERY_WALL },
4713 { ep_gfx_crumbled, EP_GFX_CRUMBLED },
4715 { ep_editor_cascade_active, EP_EDITOR_CASCADE_ACTIVE },
4716 { ep_editor_cascade_inactive, EP_EDITOR_CASCADE_INACTIVE },
4718 { ep_obsolete, EP_OBSOLETE },
4725 // always start with reliable default values (element has no properties)
4726 // (but never initialize clipboard elements after the very first time)
4727 // (to be able to use clipboard elements between several levels)
4728 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4729 if (!IS_CLIPBOARD_ELEMENT(i) || !clipboard_elements_initialized)
4730 for (j = 0; j < NUM_ELEMENT_PROPERTIES; j++)
4731 SET_PROPERTY(i, j, FALSE);
4733 // set all base element properties from above array definitions
4734 for (i = 0; element_properties[i].elements != NULL; i++)
4735 for (j = 0; (element_properties[i].elements)[j] != -1; j++)
4736 SET_PROPERTY((element_properties[i].elements)[j],
4737 element_properties[i].property, TRUE);
4739 // copy properties to some elements that are only stored in level file
4740 for (i = 0; i < NUM_ELEMENT_PROPERTIES; i++)
4741 for (j = 0; copy_properties[j][0] != -1; j++)
4742 if (HAS_PROPERTY(copy_properties[j][0], i))
4743 for (k = 1; k <= 4; k++)
4744 SET_PROPERTY(copy_properties[j][k], i, TRUE);
4746 // set static element properties that are not listed in array definitions
4747 for (i = EL_STEEL_CHAR_START; i <= EL_STEEL_CHAR_END; i++)
4748 SET_PROPERTY(i, EP_INDESTRUCTIBLE, TRUE);
4750 clipboard_elements_initialized = TRUE;
4753 void InitElementPropertiesEngine(int engine_version)
4755 static int no_wall_properties[] =
4758 EP_COLLECTIBLE_ONLY,
4760 EP_DONT_COLLIDE_WITH,
4763 EP_CAN_SMASH_PLAYER,
4764 EP_CAN_SMASH_ENEMIES,
4765 EP_CAN_SMASH_EVERYTHING,
4770 EP_FOOD_DARK_YAMYAM,
4786 /* important: after initialization in InitElementPropertiesStatic(), the
4787 elements are not again initialized to a default value; therefore all
4788 changes have to make sure that they leave the element with a defined
4789 property (which means that conditional property changes must be set to
4790 a reliable default value before) */
4792 // resolve group elements
4793 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
4794 ResolveGroupElement(EL_GROUP_START + i);
4796 // set all special, combined or engine dependent element properties
4797 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4799 // do not change (already initialized) clipboard elements here
4800 if (IS_CLIPBOARD_ELEMENT(i))
4803 // ---------- INACTIVE ----------------------------------------------------
4804 SET_PROPERTY(i, EP_INACTIVE, ((i >= EL_CHAR_START &&
4805 i <= EL_CHAR_END) ||
4806 (i >= EL_STEEL_CHAR_START &&
4807 i <= EL_STEEL_CHAR_END)));
4809 // ---------- WALKABLE, PASSABLE, ACCESSIBLE ------------------------------
4810 SET_PROPERTY(i, EP_WALKABLE, (IS_WALKABLE_OVER(i) ||
4811 IS_WALKABLE_INSIDE(i) ||
4812 IS_WALKABLE_UNDER(i)));
4814 SET_PROPERTY(i, EP_PASSABLE, (IS_PASSABLE_OVER(i) ||
4815 IS_PASSABLE_INSIDE(i) ||
4816 IS_PASSABLE_UNDER(i)));
4818 SET_PROPERTY(i, EP_ACCESSIBLE_OVER, (IS_WALKABLE_OVER(i) ||
4819 IS_PASSABLE_OVER(i)));
4821 SET_PROPERTY(i, EP_ACCESSIBLE_INSIDE, (IS_WALKABLE_INSIDE(i) ||
4822 IS_PASSABLE_INSIDE(i)));
4824 SET_PROPERTY(i, EP_ACCESSIBLE_UNDER, (IS_WALKABLE_UNDER(i) ||
4825 IS_PASSABLE_UNDER(i)));
4827 SET_PROPERTY(i, EP_ACCESSIBLE, (IS_WALKABLE(i) ||
4830 // ---------- COLLECTIBLE -------------------------------------------------
4831 SET_PROPERTY(i, EP_COLLECTIBLE, (IS_COLLECTIBLE_ONLY(i) ||
4835 // ---------- SNAPPABLE ---------------------------------------------------
4836 SET_PROPERTY(i, EP_SNAPPABLE, (IS_DIGGABLE(i) ||
4837 IS_COLLECTIBLE(i) ||
4841 // ---------- WALL --------------------------------------------------------
4842 SET_PROPERTY(i, EP_WALL, TRUE); // default: element is wall
4844 for (j = 0; no_wall_properties[j] != -1; j++)
4845 if (HAS_PROPERTY(i, no_wall_properties[j]) ||
4846 i >= EL_FIRST_RUNTIME_UNREAL)
4847 SET_PROPERTY(i, EP_WALL, FALSE);
4849 if (IS_HISTORIC_WALL(i))
4850 SET_PROPERTY(i, EP_WALL, TRUE);
4852 // ---------- SOLID_FOR_PUSHING -------------------------------------------
4853 if (engine_version < VERSION_IDENT(2,2,0,0))
4854 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, IS_HISTORIC_SOLID(i));
4856 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, (!IS_WALKABLE(i) &&
4858 !IS_COLLECTIBLE(i)));
4860 // ---------- DRAGONFIRE_PROOF --------------------------------------------
4861 if (IS_HISTORIC_SOLID(i) || i == EL_EXPLOSION)
4862 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, TRUE);
4864 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, (IS_INDESTRUCTIBLE(i) &&
4867 // ---------- EXPLOSION_PROOF ---------------------------------------------
4869 SET_PROPERTY(i, EP_EXPLOSION_PROOF, TRUE);
4870 else if (engine_version < VERSION_IDENT(2,2,0,0))
4871 SET_PROPERTY(i, EP_EXPLOSION_PROOF, IS_INDESTRUCTIBLE(i));
4873 SET_PROPERTY(i, EP_EXPLOSION_PROOF, (IS_INDESTRUCTIBLE(i) &&
4877 if (IS_CUSTOM_ELEMENT(i))
4879 // these are additional properties which are initially false when set
4881 // ---------- DONT_COLLIDE_WITH / DONT_RUN_INTO -------------------------
4883 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH, TRUE);
4884 if (DONT_COLLIDE_WITH(i))
4885 SET_PROPERTY(i, EP_DONT_RUN_INTO, TRUE);
4887 // ---------- CAN_SMASH_ENEMIES / CAN_SMASH_PLAYER ----------------------
4888 if (CAN_SMASH_EVERYTHING(i))
4889 SET_PROPERTY(i, EP_CAN_SMASH_ENEMIES, TRUE);
4890 if (CAN_SMASH_ENEMIES(i))
4891 SET_PROPERTY(i, EP_CAN_SMASH_PLAYER, TRUE);
4894 // ---------- CAN_SMASH ---------------------------------------------------
4895 SET_PROPERTY(i, EP_CAN_SMASH, (CAN_SMASH_PLAYER(i) ||
4896 CAN_SMASH_ENEMIES(i) ||
4897 CAN_SMASH_EVERYTHING(i)));
4899 // ---------- CAN_EXPLODE_BY_FIRE -----------------------------------------
4900 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_FIRE, (CAN_EXPLODE(i) &&
4901 EXPLODES_BY_FIRE(i)));
4903 // ---------- CAN_EXPLODE_SMASHED -----------------------------------------
4904 SET_PROPERTY(i, EP_CAN_EXPLODE_SMASHED, (CAN_EXPLODE(i) &&
4905 EXPLODES_SMASHED(i)));
4907 // ---------- CAN_EXPLODE_IMPACT ------------------------------------------
4908 SET_PROPERTY(i, EP_CAN_EXPLODE_IMPACT, (CAN_EXPLODE(i) &&
4909 EXPLODES_IMPACT(i)));
4911 // ---------- CAN_EXPLODE_BY_DRAGONFIRE -----------------------------------
4912 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_DRAGONFIRE, CAN_EXPLODE_BY_FIRE(i));
4914 // ---------- CAN_EXPLODE_BY_EXPLOSION ------------------------------------
4915 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_EXPLOSION, (CAN_EXPLODE_BY_FIRE(i) ||
4916 i == EL_BLACK_ORB));
4918 // ---------- COULD_MOVE_INTO_ACID ----------------------------------------
4919 SET_PROPERTY(i, EP_COULD_MOVE_INTO_ACID, (IS_PLAYER_ELEMENT(i) ||
4921 IS_CUSTOM_ELEMENT(i)));
4923 // ---------- MAYBE_DONT_COLLIDE_WITH -------------------------------------
4924 SET_PROPERTY(i, EP_MAYBE_DONT_COLLIDE_WITH, (i == EL_SP_SNIKSNAK ||
4925 i == EL_SP_ELECTRON));
4927 // ---------- CAN_MOVE_INTO_ACID ------------------------------------------
4928 if (COULD_MOVE_INTO_ACID(i) && !IS_CUSTOM_ELEMENT(i))
4929 SET_PROPERTY(i, EP_CAN_MOVE_INTO_ACID,
4930 getMoveIntoAcidProperty(&level, i));
4932 // ---------- DONT_COLLIDE_WITH -------------------------------------------
4933 if (MAYBE_DONT_COLLIDE_WITH(i))
4934 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH,
4935 getDontCollideWithProperty(&level, i));
4937 // ---------- SP_PORT -----------------------------------------------------
4938 SET_PROPERTY(i, EP_SP_PORT, (IS_SP_ELEMENT(i) &&
4939 IS_PASSABLE_INSIDE(i)));
4941 // ---------- CAN_BE_CLONED_BY_ANDROID ------------------------------------
4942 for (j = 0; j < level.num_android_clone_elements; j++)
4943 SET_PROPERTY(i, EP_CAN_BE_CLONED_BY_ANDROID,
4945 IS_EQUAL_OR_IN_GROUP(i, level.android_clone_element[j])));
4947 // ---------- CAN_CHANGE --------------------------------------------------
4948 SET_PROPERTY(i, EP_CAN_CHANGE, FALSE); // default: cannot change
4949 for (j = 0; j < element_info[i].num_change_pages; j++)
4950 if (element_info[i].change_page[j].can_change)
4951 SET_PROPERTY(i, EP_CAN_CHANGE, TRUE);
4953 // ---------- HAS_ACTION --------------------------------------------------
4954 SET_PROPERTY(i, EP_HAS_ACTION, FALSE); // default: has no action
4955 for (j = 0; j < element_info[i].num_change_pages; j++)
4956 if (element_info[i].change_page[j].has_action)
4957 SET_PROPERTY(i, EP_HAS_ACTION, TRUE);
4959 // ---------- CAN_CHANGE_OR_HAS_ACTION ------------------------------------
4960 SET_PROPERTY(i, EP_CAN_CHANGE_OR_HAS_ACTION, (CAN_CHANGE(i) ||
4963 // ---------- GFX_CRUMBLED ------------------------------------------------
4964 SET_PROPERTY(i, EP_GFX_CRUMBLED,
4965 element_info[i].crumbled[ACTION_DEFAULT] !=
4966 element_info[i].graphic[ACTION_DEFAULT]);
4968 // ---------- EDITOR_CASCADE ----------------------------------------------
4969 SET_PROPERTY(i, EP_EDITOR_CASCADE, (IS_EDITOR_CASCADE_ACTIVE(i) ||
4970 IS_EDITOR_CASCADE_INACTIVE(i)));
4973 // dynamically adjust element properties according to game engine version
4975 static int ep_em_slippery_wall[] =
4980 EL_EXPANDABLE_WALL_HORIZONTAL,
4981 EL_EXPANDABLE_WALL_VERTICAL,
4982 EL_EXPANDABLE_WALL_ANY,
4983 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
4984 EL_EXPANDABLE_STEELWALL_VERTICAL,
4985 EL_EXPANDABLE_STEELWALL_ANY,
4986 EL_EXPANDABLE_STEELWALL_GROWING,
4990 static int ep_em_explodes_by_fire[] =
4993 EL_EM_DYNAMITE_ACTIVE,
4998 // special EM style gems behaviour
4999 for (i = 0; ep_em_slippery_wall[i] != -1; i++)
5000 SET_PROPERTY(ep_em_slippery_wall[i], EP_EM_SLIPPERY_WALL,
5001 level.em_slippery_gems);
5003 // "EL_EXPANDABLE_WALL_GROWING" wasn't slippery for EM gems in 2.0.1
5004 SET_PROPERTY(EL_EXPANDABLE_WALL_GROWING, EP_EM_SLIPPERY_WALL,
5005 (level.em_slippery_gems &&
5006 engine_version > VERSION_IDENT(2,0,1,0)));
5008 // special EM style explosion behaviour regarding chain reactions
5009 for (i = 0; ep_em_explodes_by_fire[i] != -1; i++)
5010 SET_PROPERTY(ep_em_explodes_by_fire[i], EP_EXPLODES_BY_FIRE,
5011 level.em_explodes_by_fire);
5014 // this is needed because some graphics depend on element properties
5015 if (game_status == GAME_MODE_PLAYING)
5016 InitElementGraphicInfo();
5019 void InitElementPropertiesGfxElement(void)
5023 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5025 struct ElementInfo *ei = &element_info[i];
5027 ei->gfx_element = (ei->use_gfx_element ? ei->gfx_element_initial : i);
5031 static void InitGlobal(void)
5036 for (i = 0; i < MAX_NUM_ELEMENTS + 1; i++)
5038 // check if element_name_info entry defined for each element in "main.h"
5039 if (i < MAX_NUM_ELEMENTS && element_name_info[i].token_name == NULL)
5040 Fail("undefined 'element_name_info' entry for element %d", i);
5042 element_info[i].token_name = element_name_info[i].token_name;
5043 element_info[i].class_name = element_name_info[i].class_name;
5044 element_info[i].editor_description= element_name_info[i].editor_description;
5047 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS + 1; i++)
5049 // check if global_anim_name_info defined for each entry in "main.h"
5050 if (i < NUM_GLOBAL_ANIM_TOKENS &&
5051 global_anim_name_info[i].token_name == NULL)
5052 Fail("undefined 'global_anim_name_info' entry for anim %d", i);
5054 global_anim_info[i].token_name = global_anim_name_info[i].token_name;
5057 // create hash to store URLs for global animations
5058 anim_url_hash = newSetupFileHash();
5060 // create hash from image config list
5061 image_config_hash = newSetupFileHash();
5062 for (i = 0; image_config[i].token != NULL; i++)
5063 setHashEntry(image_config_hash,
5064 image_config[i].token,
5065 image_config[i].value);
5067 // create hash from element token list
5068 element_token_hash = newSetupFileHash();
5069 for (i = 0; element_name_info[i].token_name != NULL; i++)
5070 setHashEntry(element_token_hash,
5071 element_name_info[i].token_name,
5074 // create hash from graphic token list
5075 graphic_token_hash = newSetupFileHash();
5076 for (graphic = 0, i = 0; image_config[i].token != NULL; i++)
5077 if (strSuffix(image_config[i].value, ".png") ||
5078 strSuffix(image_config[i].value, ".pcx") ||
5079 strSuffix(image_config[i].value, ".wav") ||
5080 strEqual(image_config[i].value, UNDEFINED_FILENAME))
5081 setHashEntry(graphic_token_hash,
5082 image_config[i].token,
5083 int2str(graphic++, 0));
5085 // create hash from font token list
5086 font_token_hash = newSetupFileHash();
5087 for (i = 0; font_info[i].token_name != NULL; i++)
5088 setHashEntry(font_token_hash,
5089 font_info[i].token_name,
5092 // set default filenames for all cloned graphics in static configuration
5093 for (i = 0; image_config[i].token != NULL; i++)
5095 if (strEqual(image_config[i].value, UNDEFINED_FILENAME))
5097 char *token = image_config[i].token;
5098 char *token_clone_from = getStringCat2(token, ".clone_from");
5099 char *token_cloned = getHashEntry(image_config_hash, token_clone_from);
5101 if (token_cloned != NULL)
5103 char *value_cloned = getHashEntry(image_config_hash, token_cloned);
5105 if (value_cloned != NULL)
5107 // set default filename in static configuration
5108 image_config[i].value = value_cloned;
5110 // set default filename in image config hash
5111 setHashEntry(image_config_hash, token, value_cloned);
5115 free(token_clone_from);
5119 // always start with reliable default values (all elements)
5120 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5121 ActiveElement[i] = i;
5123 // now add all entries that have an active state (active elements)
5124 for (i = 0; element_with_active_state[i].element != -1; i++)
5126 int element = element_with_active_state[i].element;
5127 int element_active = element_with_active_state[i].element_active;
5129 ActiveElement[element] = element_active;
5132 // always start with reliable default values (all buttons)
5133 for (i = 0; i < NUM_IMAGE_FILES; i++)
5134 ActiveButton[i] = i;
5136 // now add all entries that have an active state (active buttons)
5137 for (i = 0; button_with_active_state[i].button != -1; i++)
5139 int button = button_with_active_state[i].button;
5140 int button_active = button_with_active_state[i].button_active;
5142 ActiveButton[button] = button_active;
5145 // always start with reliable default values (all fonts)
5146 for (i = 0; i < NUM_FONTS; i++)
5149 // now add all entries that have an active state (active fonts)
5150 for (i = 0; font_with_active_state[i].font_nr != -1; i++)
5152 int font = font_with_active_state[i].font_nr;
5153 int font_active = font_with_active_state[i].font_nr_active;
5155 ActiveFont[font] = font_active;
5158 global.autoplay_leveldir = NULL;
5159 global.patchtapes_leveldir = NULL;
5160 global.convert_leveldir = NULL;
5161 global.dumplevel_leveldir = NULL;
5162 global.dumptape_leveldir = NULL;
5163 global.create_sketch_images_dir = NULL;
5164 global.create_collect_images_dir = NULL;
5166 global.frames_per_second = 0;
5167 global.show_frames_per_second = FALSE;
5169 global.border_status = GAME_MODE_LOADING;
5170 global.anim_status = global.anim_status_next = GAME_MODE_LOADING;
5172 global.use_envelope_request = FALSE;
5174 global.user_names = NULL;
5177 static void Execute_Command(char *command)
5181 if (strEqual(command, "print graphicsinfo.conf"))
5183 Print("# You can configure additional/alternative image files here.\n");
5184 Print("# (The entries below are default and therefore commented out.)\n");
5186 Print("%s\n", getFormattedSetupEntry("name", "Classic Graphics"));
5188 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5191 for (i = 0; image_config[i].token != NULL; i++)
5192 Print("# %s\n", getFormattedSetupEntry(image_config[i].token,
5193 image_config[i].value));
5197 else if (strEqual(command, "print soundsinfo.conf"))
5199 Print("# You can configure additional/alternative sound files here.\n");
5200 Print("# (The entries below are default and therefore commented out.)\n");
5202 Print("%s\n", getFormattedSetupEntry("name", "Classic Sounds"));
5204 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5207 for (i = 0; sound_config[i].token != NULL; i++)
5208 Print("# %s\n", getFormattedSetupEntry(sound_config[i].token,
5209 sound_config[i].value));
5213 else if (strEqual(command, "print musicinfo.conf"))
5215 Print("# You can configure additional/alternative music files here.\n");
5216 Print("# (The entries below are default and therefore commented out.)\n");
5218 Print("%s\n", getFormattedSetupEntry("name", "Classic Music"));
5220 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5223 for (i = 0; music_config[i].token != NULL; i++)
5224 Print("# %s\n", getFormattedSetupEntry(music_config[i].token,
5225 music_config[i].value));
5229 else if (strEqual(command, "print editorsetup.conf"))
5231 Print("# You can configure your personal editor element list here.\n");
5232 Print("# (The entries below are default and therefore commented out.)\n");
5235 // this is needed to be able to check element list for cascade elements
5236 InitElementPropertiesStatic();
5237 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
5239 PrintEditorElementList();
5243 else if (strEqual(command, "print helpanim.conf"))
5245 Print("# You can configure different element help animations here.\n");
5246 Print("# (The entries below are default and therefore commented out.)\n");
5249 for (i = 0; helpanim_config[i].token != NULL; i++)
5251 Print("# %s\n", getFormattedSetupEntry(helpanim_config[i].token,
5252 helpanim_config[i].value));
5254 if (strEqual(helpanim_config[i].token, "end"))
5260 else if (strEqual(command, "print helptext.conf"))
5262 Print("# You can configure different element help text here.\n");
5263 Print("# (The entries below are default and therefore commented out.)\n");
5266 for (i = 0; helptext_config[i].token != NULL; i++)
5267 Print("# %s\n", getFormattedSetupEntry(helptext_config[i].token,
5268 helptext_config[i].value));
5272 else if (strPrefix(command, "dump level "))
5274 char *filename = &command[11];
5276 if (fileExists(filename))
5278 LoadLevelFromFilename(&level, filename);
5284 char *leveldir = getStringCopy(filename); // read command parameters
5285 char *level_nr = strchr(leveldir, ' ');
5287 if (level_nr == NULL)
5288 Fail("cannot open file '%s'", filename);
5292 global.dumplevel_leveldir = leveldir;
5293 global.dumplevel_level_nr = atoi(level_nr);
5295 program.headless = TRUE;
5297 else if (strPrefix(command, "dump tape "))
5299 char *filename = &command[10];
5301 if (fileExists(filename))
5303 LoadTapeFromFilename(filename);
5309 char *leveldir = getStringCopy(filename); // read command parameters
5310 char *level_nr = strchr(leveldir, ' ');
5312 if (level_nr == NULL)
5313 Fail("cannot open file '%s'", filename);
5317 global.dumptape_leveldir = leveldir;
5318 global.dumptape_level_nr = atoi(level_nr);
5320 program.headless = TRUE;
5322 else if (strPrefix(command, "autoplay ") ||
5323 strPrefix(command, "autoffwd ") ||
5324 strPrefix(command, "autowarp ") ||
5325 strPrefix(command, "autotest ") ||
5326 strPrefix(command, "autosave ") ||
5327 strPrefix(command, "autoupload ") ||
5328 strPrefix(command, "autofix "))
5330 char *arg_ptr = strchr(command, ' ');
5331 char *str_ptr = getStringCopy(arg_ptr); // read command parameters
5333 global.autoplay_mode =
5334 (strPrefix(command, "autoplay") ? AUTOPLAY_MODE_PLAY :
5335 strPrefix(command, "autoffwd") ? AUTOPLAY_MODE_FFWD :
5336 strPrefix(command, "autowarp") ? AUTOPLAY_MODE_WARP :
5337 strPrefix(command, "autotest") ? AUTOPLAY_MODE_TEST :
5338 strPrefix(command, "autosave") ? AUTOPLAY_MODE_SAVE :
5339 strPrefix(command, "autoupload") ? AUTOPLAY_MODE_UPLOAD :
5340 strPrefix(command, "autofix") ? AUTOPLAY_MODE_FIX :
5341 AUTOPLAY_MODE_NONE);
5343 while (*str_ptr != '\0') // continue parsing string
5345 // cut leading whitespace from string, replace it by string terminator
5346 while (*str_ptr == ' ' || *str_ptr == '\t')
5349 if (*str_ptr == '\0') // end of string reached
5352 if (global.autoplay_leveldir == NULL) // read level set string
5354 global.autoplay_leveldir = str_ptr;
5355 global.autoplay_all = TRUE; // default: play all tapes
5357 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5358 global.autoplay_level[i] = FALSE;
5360 else // read level number string
5362 int level_nr = atoi(str_ptr); // get level_nr value
5364 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5365 global.autoplay_level[level_nr] = TRUE;
5367 global.autoplay_all = FALSE;
5370 // advance string pointer to the next whitespace (or end of string)
5371 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5375 if (global.autoplay_mode & AUTOPLAY_WARP_NO_DISPLAY)
5376 program.headless = TRUE;
5378 else if (strPrefix(command, "patch tapes "))
5380 char *str_ptr = getStringCopy(&command[12]); // read command parameters
5382 // skip leading whitespace
5383 while (*str_ptr == ' ' || *str_ptr == '\t')
5386 if (*str_ptr == '\0')
5387 Fail("cannot find MODE in command '%s'", command);
5389 global.patchtapes_mode = str_ptr; // store patch mode
5391 // advance to next whitespace (or end of string)
5392 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5395 while (*str_ptr != '\0') // continue parsing string
5397 // cut leading whitespace from string, replace it by string terminator
5398 while (*str_ptr == ' ' || *str_ptr == '\t')
5401 if (*str_ptr == '\0') // end of string reached
5404 if (global.patchtapes_leveldir == NULL) // read level set string
5406 global.patchtapes_leveldir = str_ptr;
5407 global.patchtapes_all = TRUE; // default: patch all tapes
5409 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5410 global.patchtapes_level[i] = FALSE;
5412 else // read level number string
5414 int level_nr = atoi(str_ptr); // get level_nr value
5416 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5417 global.patchtapes_level[level_nr] = TRUE;
5419 global.patchtapes_all = FALSE;
5422 // advance string pointer to the next whitespace (or end of string)
5423 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5427 if (global.patchtapes_leveldir == NULL)
5429 if (strEqual(global.patchtapes_mode, "help"))
5430 global.patchtapes_leveldir = UNDEFINED_LEVELSET;
5432 Fail("cannot find LEVELDIR in command '%s'", command);
5435 program.headless = TRUE;
5437 else if (strPrefix(command, "convert "))
5439 char *str_copy = getStringCopy(strchr(command, ' ') + 1);
5440 char *str_ptr = strchr(str_copy, ' ');
5442 global.convert_leveldir = str_copy;
5443 global.convert_level_nr = -1;
5445 if (str_ptr != NULL) // level number follows
5447 *str_ptr++ = '\0'; // terminate leveldir string
5448 global.convert_level_nr = atoi(str_ptr); // get level_nr value
5451 program.headless = TRUE;
5453 else if (strPrefix(command, "create sketch images "))
5455 global.create_sketch_images_dir = getStringCopy(&command[21]);
5457 if (access(global.create_sketch_images_dir, W_OK) != 0)
5458 Fail("image target directory '%s' not found or not writable",
5459 global.create_sketch_images_dir);
5461 else if (strPrefix(command, "create collect image "))
5463 global.create_collect_images_dir = getStringCopy(&command[21]);
5465 if (access(global.create_collect_images_dir, W_OK) != 0)
5466 Fail("image target directory '%s' not found or not writable",
5467 global.create_collect_images_dir);
5469 else if (strPrefix(command, "create CE image "))
5471 CreateCustomElementImages(&command[16]);
5477 FailWithHelp("unrecognized command '%s'", command);
5480 // disable networking if any valid command was recognized
5481 options.network = setup.network_mode = FALSE;
5484 static void InitSetup(void)
5486 LoadUserNames(); // global user names
5487 LoadUserSetup(); // global user number
5489 LoadSetup(); // global setup info
5491 // set some options from setup file
5493 if (setup.options.verbose)
5494 options.verbose = TRUE;
5496 if (setup.options.debug)
5497 options.debug = TRUE;
5499 if (!strEqual(setup.options.debug_mode, ARG_UNDEFINED_STRING))
5500 options.debug_mode = getStringCopy(setup.options.debug_mode);
5502 if (setup.debug.show_frames_per_second)
5503 global.show_frames_per_second = TRUE;
5506 static void InitGameInfo(void)
5508 game.restart_level = FALSE;
5509 game.request_active = FALSE;
5511 game.use_masked_elements_initial = FALSE;
5514 static void InitPlayerInfo(void)
5518 // choose default local player
5519 local_player = &stored_player[0];
5521 for (i = 0; i < MAX_PLAYERS; i++)
5523 stored_player[i].connected_locally = FALSE;
5524 stored_player[i].connected_network = FALSE;
5527 local_player->connected_locally = TRUE;
5530 static void InitArtworkInfo(void)
5535 static char *get_string_in_brackets(char *string)
5537 char *string_in_brackets = checked_malloc(strlen(string) + 3);
5539 sprintf(string_in_brackets, "[%s]", string);
5541 return string_in_brackets;
5544 static char *get_level_id_suffix(int id_nr)
5546 char *id_suffix = checked_malloc(1 + 3 + 1);
5548 if (id_nr < 0 || id_nr > 999)
5551 sprintf(id_suffix, ".%03d", id_nr);
5556 static void InitArtworkConfig(void)
5558 static char *image_id_prefix[MAX_NUM_ELEMENTS +
5560 NUM_GLOBAL_ANIM_TOKENS + 1];
5561 static char *sound_id_prefix[2 * MAX_NUM_ELEMENTS +
5562 NUM_GLOBAL_ANIM_TOKENS + 1];
5563 static char *music_id_prefix[NUM_MUSIC_PREFIXES +
5564 NUM_GLOBAL_ANIM_TOKENS + 1];
5565 static char *action_id_suffix[NUM_ACTIONS + 1];
5566 static char *direction_id_suffix[NUM_DIRECTIONS_FULL + 1];
5567 static char *special_id_suffix[NUM_SPECIAL_GFX_ARGS + 1];
5568 static char *level_id_suffix[MAX_LEVELS + 1];
5569 static char *dummy[1] = { NULL };
5570 static char *ignore_generic_tokens[] =
5575 "program_copyright",
5580 static char **ignore_image_tokens;
5581 static char **ignore_sound_tokens;
5582 static char **ignore_music_tokens;
5583 int num_ignore_generic_tokens;
5584 int num_ignore_image_tokens;
5585 int num_ignore_sound_tokens;
5586 int num_ignore_music_tokens;
5589 // dynamically determine list of generic tokens to be ignored
5590 num_ignore_generic_tokens = 0;
5591 for (i = 0; ignore_generic_tokens[i] != NULL; i++)
5592 num_ignore_generic_tokens++;
5594 // dynamically determine list of image tokens to be ignored
5595 num_ignore_image_tokens = num_ignore_generic_tokens;
5596 for (i = 0; image_config_vars[i].token != NULL; i++)
5597 num_ignore_image_tokens++;
5598 ignore_image_tokens =
5599 checked_malloc((num_ignore_image_tokens + 1) * sizeof(char *));
5600 for (i = 0; i < num_ignore_generic_tokens; i++)
5601 ignore_image_tokens[i] = ignore_generic_tokens[i];
5602 for (i = 0; i < num_ignore_image_tokens - num_ignore_generic_tokens; i++)
5603 ignore_image_tokens[num_ignore_generic_tokens + i] =
5604 image_config_vars[i].token;
5605 ignore_image_tokens[num_ignore_image_tokens] = NULL;
5607 // dynamically determine list of sound tokens to be ignored
5608 num_ignore_sound_tokens = num_ignore_generic_tokens;
5609 ignore_sound_tokens =
5610 checked_malloc((num_ignore_sound_tokens + 1) * sizeof(char *));
5611 for (i = 0; i < num_ignore_generic_tokens; i++)
5612 ignore_sound_tokens[i] = ignore_generic_tokens[i];
5613 ignore_sound_tokens[num_ignore_sound_tokens] = NULL;
5615 // dynamically determine list of music tokens to be ignored
5616 num_ignore_music_tokens = num_ignore_generic_tokens;
5617 ignore_music_tokens =
5618 checked_malloc((num_ignore_music_tokens + 1) * sizeof(char *));
5619 for (i = 0; i < num_ignore_generic_tokens; i++)
5620 ignore_music_tokens[i] = ignore_generic_tokens[i];
5621 ignore_music_tokens[num_ignore_music_tokens] = NULL;
5623 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5624 image_id_prefix[i] = element_info[i].token_name;
5625 for (i = 0; i < NUM_FONTS; i++)
5626 image_id_prefix[MAX_NUM_ELEMENTS + i] = font_info[i].token_name;
5627 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5628 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + i] =
5629 global_anim_info[i].token_name;
5630 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5632 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5633 sound_id_prefix[i] = element_info[i].token_name;
5634 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5635 sound_id_prefix[MAX_NUM_ELEMENTS + i] =
5636 get_string_in_brackets(element_info[i].class_name);
5637 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5638 sound_id_prefix[2 * MAX_NUM_ELEMENTS + i] =
5639 global_anim_info[i].token_name;
5640 sound_id_prefix[2 * MAX_NUM_ELEMENTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5642 for (i = 0; i < NUM_MUSIC_PREFIXES; i++)
5643 music_id_prefix[i] = music_prefix_info[i].prefix;
5644 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5645 music_id_prefix[NUM_MUSIC_PREFIXES + i] =
5646 global_anim_info[i].token_name;
5647 music_id_prefix[NUM_MUSIC_PREFIXES + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5649 for (i = 0; i < NUM_ACTIONS; i++)
5650 action_id_suffix[i] = element_action_info[i].suffix;
5651 action_id_suffix[NUM_ACTIONS] = NULL;
5653 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
5654 direction_id_suffix[i] = element_direction_info[i].suffix;
5655 direction_id_suffix[NUM_DIRECTIONS_FULL] = NULL;
5657 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
5658 special_id_suffix[i] = special_suffix_info[i].suffix;
5659 special_id_suffix[NUM_SPECIAL_GFX_ARGS] = NULL;
5661 for (i = 0; i < MAX_LEVELS; i++)
5662 level_id_suffix[i] = get_level_id_suffix(i);
5663 level_id_suffix[MAX_LEVELS] = NULL;
5665 InitImageList(image_config, NUM_IMAGE_FILES, image_config_suffix,
5666 image_id_prefix, action_id_suffix, direction_id_suffix,
5667 special_id_suffix, ignore_image_tokens);
5668 InitSoundList(sound_config, NUM_SOUND_FILES, sound_config_suffix,
5669 sound_id_prefix, action_id_suffix, dummy,
5670 special_id_suffix, ignore_sound_tokens);
5671 InitMusicList(music_config, NUM_MUSIC_FILES, music_config_suffix,
5672 music_id_prefix, action_id_suffix, special_id_suffix,
5673 level_id_suffix, ignore_music_tokens);
5676 static void InitMixer(void)
5683 static void InitVideoOverlay(void)
5685 // if virtual buttons are not loaded from setup file, repeat initializing
5686 // virtual buttons grid with default values now that video is initialized
5687 if (!setup.touch.grid_initialized)
5690 InitTileCursorInfo();
5694 void InitGfxBuffers(void)
5696 static int win_xsize_last = -1;
5697 static int win_ysize_last = -1;
5699 // create additional image buffers for double-buffering and cross-fading
5701 if (WIN_XSIZE != win_xsize_last || WIN_YSIZE != win_ysize_last)
5703 // used to temporarily store the backbuffer -- only re-create if changed
5704 ReCreateBitmap(&bitmap_db_store_1, WIN_XSIZE, WIN_YSIZE);
5705 ReCreateBitmap(&bitmap_db_store_2, WIN_XSIZE, WIN_YSIZE);
5707 win_xsize_last = WIN_XSIZE;
5708 win_ysize_last = WIN_YSIZE;
5711 ReCreateBitmap(&bitmap_db_field, FXSIZE, FYSIZE);
5712 ReCreateBitmap(&bitmap_db_door_1, 3 * DXSIZE, DYSIZE);
5713 ReCreateBitmap(&bitmap_db_door_2, 3 * VXSIZE, VYSIZE);
5715 // initialize screen properties
5716 InitGfxFieldInfo(SX, SY, SXSIZE, SYSIZE,
5717 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
5719 InitGfxDoor1Info(DX, DY, DXSIZE, DYSIZE);
5720 InitGfxDoor2Info(VX, VY, VXSIZE, VYSIZE);
5721 InitGfxDoor3Info(EX, EY, EXSIZE, EYSIZE);
5722 InitGfxWindowInfo(WIN_XSIZE, WIN_YSIZE);
5723 InitGfxScrollbufferInfo(FXSIZE, FYSIZE);
5724 InitGfxClipRegion(FALSE, -1, -1, -1, -1);
5726 // required if door size definitions have changed
5727 InitGraphicCompatibilityInfo_Doors();
5729 InitGfxBuffers_EM();
5730 InitGfxBuffers_SP();
5731 InitGfxBuffers_MM();
5734 static void InitGfx(void)
5736 struct GraphicInfo *graphic_info_last = graphic_info;
5737 char *filename_font_initial = NULL;
5738 char *filename_image_initial[NUM_INITIAL_IMAGES] = { NULL };
5739 char *image_token[NUM_INITIAL_IMAGES] =
5741 CONFIG_TOKEN_GLOBAL_BUSY_INITIAL,
5742 CONFIG_TOKEN_GLOBAL_BUSY,
5743 CONFIG_TOKEN_GLOBAL_BUSY_PLAYFIELD,
5744 CONFIG_TOKEN_BACKGROUND,
5745 CONFIG_TOKEN_BACKGROUND_LOADING_INITIAL,
5746 CONFIG_TOKEN_BACKGROUND_LOADING
5748 struct MenuPosInfo *init_busy[NUM_INITIAL_IMAGES_BUSY] =
5752 &init.busy_playfield
5754 Bitmap *bitmap_font_initial = NULL;
5755 int parameter[NUM_INITIAL_IMAGES][NUM_GFX_ARGS];
5758 // determine settings for initial font (for displaying startup messages)
5759 for (i = 0; image_config[i].token != NULL; i++)
5761 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5763 char font_token[128];
5766 sprintf(font_token, "%s_%d", CONFIG_TOKEN_FONT_INITIAL, j + 1);
5767 len_font_token = strlen(font_token);
5769 if (strEqual(image_config[i].token, font_token))
5771 filename_font_initial = image_config[i].value;
5773 else if (strlen(image_config[i].token) > len_font_token &&
5774 strncmp(image_config[i].token, font_token, len_font_token) == 0)
5776 if (strEqual(&image_config[i].token[len_font_token], ".x"))
5777 font_initial[j].src_x = atoi(image_config[i].value);
5778 else if (strEqual(&image_config[i].token[len_font_token], ".y"))
5779 font_initial[j].src_y = atoi(image_config[i].value);
5780 else if (strEqual(&image_config[i].token[len_font_token], ".width"))
5781 font_initial[j].width = atoi(image_config[i].value);
5782 else if (strEqual(&image_config[i].token[len_font_token], ".height"))
5783 font_initial[j].height = atoi(image_config[i].value);
5788 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5790 font_initial[j].num_chars = DEFAULT_NUM_CHARS_PER_FONT;
5791 font_initial[j].num_chars_per_line = DEFAULT_NUM_CHARS_PER_LINE;
5794 if (filename_font_initial == NULL) // should not happen
5795 Fail("cannot get filename for '%s'", CONFIG_TOKEN_FONT_INITIAL);
5798 InitGfxCustomArtworkInfo();
5799 InitGfxOtherSettings();
5801 InitGfxTileSizeInfo(TILESIZE, TILESIZE);
5803 bitmap_font_initial = LoadCustomImage(filename_font_initial);
5805 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5806 font_initial[j].bitmap = bitmap_font_initial;
5808 InitFontGraphicInfo();
5810 InitMenuDesignSettings_Static();
5812 // initialize settings for initial images with default values
5813 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5814 for (j = 0; j < NUM_GFX_ARGS; j++)
5816 get_graphic_parameter_value(image_config_suffix[j].value,
5817 image_config_suffix[j].token,
5818 image_config_suffix[j].type);
5820 // read settings for initial images from default custom artwork config
5821 char *gfx_config_filename = getPath3(options.graphics_directory,
5823 GRAPHICSINFO_FILENAME);
5825 if (fileExists(gfx_config_filename))
5827 SetupFileHash *setup_file_hash = loadSetupFileHash(gfx_config_filename);
5829 if (setup_file_hash)
5831 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5833 char *filename = getHashEntry(setup_file_hash, image_token[i]);
5837 filename_image_initial[i] = getStringCopy(filename);
5839 for (j = 0; image_config_suffix[j].token != NULL; j++)
5841 int type = image_config_suffix[j].type;
5842 char *suffix = image_config_suffix[j].token;
5843 char *token = getStringCat2(image_token[i], suffix);
5844 char *value = getHashEntry(setup_file_hash, token);
5846 checked_free(token);
5850 get_graphic_parameter_value(value, suffix, type);
5855 // read values from custom graphics config file
5856 InitMenuDesignSettings_FromHash(setup_file_hash, FALSE);
5858 freeSetupFileHash(setup_file_hash);
5862 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5864 if (filename_image_initial[i] == NULL)
5866 int len_token = strlen(image_token[i]);
5868 // read settings for initial images from static default artwork config
5869 for (j = 0; image_config[j].token != NULL; j++)
5871 if (strEqual(image_config[j].token, image_token[i]))
5873 filename_image_initial[i] = getStringCopy(image_config[j].value);
5875 else if (strlen(image_config[j].token) > len_token &&
5876 strncmp(image_config[j].token, image_token[i], len_token) == 0)
5878 for (k = 0; image_config_suffix[k].token != NULL; k++)
5880 if (strEqual(&image_config[j].token[len_token],
5881 image_config_suffix[k].token))
5883 get_graphic_parameter_value(image_config[j].value,
5884 image_config_suffix[k].token,
5885 image_config_suffix[k].type);
5892 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5894 if (filename_image_initial[i] == NULL) // should not happen
5895 Fail("cannot get filename for '%s'", image_token[i]);
5897 image_initial[i].bitmaps =
5898 checked_calloc(sizeof(Bitmap *) * NUM_IMG_BITMAP_POINTERS);
5900 if (!strEqual(filename_image_initial[i], UNDEFINED_FILENAME))
5901 image_initial[i].bitmaps[IMG_BITMAP_STANDARD] =
5902 LoadCustomImage(filename_image_initial[i]);
5904 checked_free(filename_image_initial[i]);
5907 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5908 image_initial[i].use_image_size = TRUE;
5910 graphic_info = image_initial; // graphic == 0 => image_initial
5912 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5913 set_graphic_parameters_ext(i, parameter[i], image_initial[i].bitmaps);
5915 graphic_info = graphic_info_last;
5917 for (i = 0; i < NUM_INITIAL_IMAGES_BUSY; i++)
5919 // set image size for busy animations
5920 init_busy[i]->width = image_initial[i].width;
5921 init_busy[i]->height = image_initial[i].height;
5924 SetLoadingBackgroundImage();
5926 ClearRectangleOnBackground(window, 0, 0, WIN_XSIZE, WIN_YSIZE);
5930 InitGfxDrawBusyAnimFunction(DrawInitAnim);
5931 InitGfxDrawGlobalAnimFunction(DrawGlobalAnimations);
5932 InitGfxDrawGlobalBorderFunction(DrawMaskedBorderToTarget);
5933 InitGfxDrawTileCursorFunction(DrawTileCursor);
5934 InitGfxDrawEnvelopeRequestFunction(DrawEnvelopeRequestToScreen);
5936 gfx.fade_border_source_status = global.border_status;
5937 gfx.fade_border_target_status = global.border_status;
5938 gfx.masked_border_bitmap_ptr = backbuffer;
5940 // use copy of busy animation to prevent change while reloading artwork
5944 static void InitGfxBackground(void)
5946 fieldbuffer = bitmap_db_field;
5947 SetDrawtoField(DRAW_TO_BACKBUFFER);
5949 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
5951 redraw_mask = REDRAW_ALL;
5954 static void InitLevelInfo(void)
5956 LoadLevelInfo(); // global level info
5957 LoadLevelSetup_LastSeries(); // last played series info
5958 LoadLevelSetup_SeriesInfo(); // last played level info
5960 if (global.autoplay_leveldir &&
5961 global.autoplay_mode != AUTOPLAY_MODE_TEST)
5963 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
5964 global.autoplay_leveldir);
5965 if (leveldir_current == NULL)
5966 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
5969 SetLevelSetInfo(leveldir_current->identifier, level_nr);
5972 static void InitLevelArtworkInfo(void)
5974 LoadLevelArtworkInfo();
5977 static void InitImages(void)
5979 print_timestamp_init("InitImages");
5982 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
5983 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5984 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
5985 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5986 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
5987 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5988 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
5989 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5992 setLevelArtworkDir(artwork.gfx_first);
5995 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
5996 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5997 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
5998 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5999 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
6000 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
6001 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
6002 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
6006 Debug("init:InitImages", "InitImages for '%s' ['%s', '%s'] ['%s', '%s']",
6007 leveldir_current->identifier,
6008 artwork.gfx_current_identifier,
6009 artwork.gfx_current->identifier,
6010 leveldir_current->graphics_set,
6011 leveldir_current->graphics_path);
6014 UPDATE_BUSY_STATE();
6016 ReloadCustomImages();
6017 print_timestamp_time("ReloadCustomImages");
6019 UPDATE_BUSY_STATE();
6021 LoadCustomElementDescriptions();
6022 print_timestamp_time("LoadCustomElementDescriptions");
6024 UPDATE_BUSY_STATE();
6026 LoadMenuDesignSettings();
6027 print_timestamp_time("LoadMenuDesignSettings");
6029 UPDATE_BUSY_STATE();
6031 ReinitializeGraphics();
6032 print_timestamp_time("ReinitializeGraphics");
6034 LoadMenuDesignSettings_AfterGraphics();
6035 print_timestamp_time("LoadMenuDesignSettings_AfterGraphics");
6037 UPDATE_BUSY_STATE();
6039 print_timestamp_done("InitImages");
6042 static void InitSound(void)
6044 print_timestamp_init("InitSound");
6046 // set artwork path to send it to the sound server process
6047 setLevelArtworkDir(artwork.snd_first);
6049 InitReloadCustomSounds();
6050 print_timestamp_time("InitReloadCustomSounds");
6052 ReinitializeSounds();
6053 print_timestamp_time("ReinitializeSounds");
6055 print_timestamp_done("InitSound");
6058 static void InitMusic(void)
6060 print_timestamp_init("InitMusic");
6062 // set artwork path to send it to the sound server process
6063 setLevelArtworkDir(artwork.mus_first);
6065 InitReloadCustomMusic();
6066 print_timestamp_time("InitReloadCustomMusic");
6068 ReinitializeMusic();
6069 print_timestamp_time("ReinitializeMusic");
6071 print_timestamp_done("InitMusic");
6074 static void InitArtworkDone(void)
6076 if (program.headless)
6079 InitGlobalAnimations();
6082 static void InitNetworkSettings(void)
6084 boolean network_enabled = (options.network || setup.network_mode);
6085 char *network_server = (options.server_host != NULL ? options.server_host :
6086 setup.network_server_hostname);
6088 if (strEqual(network_server, STR_NETWORK_AUTO_DETECT))
6089 network_server = NULL;
6091 InitNetworkInfo(network_enabled,
6095 options.server_port);
6098 void InitNetworkServer(void)
6100 if (!network.enabled || network.connected)
6103 LimitScreenUpdates(FALSE);
6105 if (game_status == GAME_MODE_LOADING)
6108 if (!ConnectToServer(network.server_host, network.server_port))
6110 network.enabled = FALSE;
6112 setup.network_mode = FALSE;
6116 SendToServer_ProtocolVersion();
6117 SendToServer_PlayerName(setup.player_name);
6118 SendToServer_NrWanted(setup.network_player_nr + 1);
6120 network.connected = TRUE;
6123 // short time to recognize result of network initialization
6124 if (game_status == GAME_MODE_LOADING)
6125 Delay_WithScreenUpdates(1000);
6128 static boolean CheckArtworkConfigForCustomElements(char *filename)
6130 SetupFileHash *setup_file_hash;
6131 boolean redefined_ce_found = FALSE;
6133 // !!! CACHE THIS BY USING HASH 'filename' => 'true/false' !!!
6135 if ((setup_file_hash = loadSetupFileHash(filename)) != NULL)
6137 BEGIN_HASH_ITERATION(setup_file_hash, itr)
6139 char *token = HASH_ITERATION_TOKEN(itr);
6141 if (strPrefix(token, "custom_"))
6143 redefined_ce_found = TRUE;
6148 END_HASH_ITERATION(setup_file_hash, itr)
6150 freeSetupFileHash(setup_file_hash);
6153 return redefined_ce_found;
6156 static boolean CheckArtworkTypeForRedefinedCustomElements(int type)
6158 char *filename_base, *filename_local;
6159 boolean redefined_ce_found = FALSE;
6161 setLevelArtworkDir(ARTWORK_FIRST_NODE(artwork, type));
6164 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6165 "leveldir_current->identifier == '%s'",
6166 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
6167 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6168 "leveldir_current->graphics_path == '%s'",
6169 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
6170 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6171 "leveldir_current->graphics_set == '%s'",
6172 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
6173 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6174 "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
6175 leveldir_current == NULL ? "[NULL]" :
6176 LEVELDIR_ARTWORK_SET(leveldir_current, type));
6179 // first look for special artwork configured in level series config
6180 filename_base = getCustomArtworkLevelConfigFilename(type);
6183 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6184 "filename_base == '%s'", filename_base);
6187 if (fileExists(filename_base))
6188 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_base);
6190 filename_local = getCustomArtworkConfigFilename(type);
6193 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6194 "filename_local == '%s'", filename_local);
6197 if (filename_local != NULL && !strEqual(filename_base, filename_local))
6198 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_local);
6201 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6202 "redefined_ce_found == %d", redefined_ce_found);
6205 return redefined_ce_found;
6208 static void InitOverrideArtwork(void)
6210 boolean redefined_ce_found = FALSE;
6212 // to check if this level set redefines any CEs, do not use overriding
6213 gfx.override_level_graphics = FALSE;
6214 gfx.override_level_sounds = FALSE;
6215 gfx.override_level_music = FALSE;
6217 // now check if this level set has definitions for custom elements
6218 if (setup.override_level_graphics == AUTO ||
6219 setup.override_level_sounds == AUTO ||
6220 setup.override_level_music == AUTO)
6221 redefined_ce_found =
6222 (CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_GRAPHICS) |
6223 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_SOUNDS) |
6224 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_MUSIC));
6227 Debug("init:InitOverrideArtwork", "redefined_ce_found == %d",
6228 redefined_ce_found);
6231 if (redefined_ce_found)
6233 // this level set has CE definitions: change "AUTO" to "FALSE"
6234 gfx.override_level_graphics = (setup.override_level_graphics == TRUE);
6235 gfx.override_level_sounds = (setup.override_level_sounds == TRUE);
6236 gfx.override_level_music = (setup.override_level_music == TRUE);
6240 // this level set has no CE definitions: change "AUTO" to "TRUE"
6241 gfx.override_level_graphics = (setup.override_level_graphics != FALSE);
6242 gfx.override_level_sounds = (setup.override_level_sounds != FALSE);
6243 gfx.override_level_music = (setup.override_level_music != FALSE);
6247 Debug("init:InitOverrideArtwork", "%d, %d, %d",
6248 gfx.override_level_graphics,
6249 gfx.override_level_sounds,
6250 gfx.override_level_music);
6254 static char *setNewArtworkIdentifier(int type)
6256 static char *last_leveldir_identifier[3] = { NULL, NULL, NULL };
6257 static char *last_artwork_identifier[3] = { NULL, NULL, NULL };
6258 static boolean last_override_level_artwork[3] = { FALSE, FALSE, FALSE };
6259 static boolean last_has_custom_artwork_set[3] = { FALSE, FALSE, FALSE };
6260 static boolean initialized[3] = { FALSE, FALSE, FALSE };
6261 TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type);
6262 boolean setup_override_artwork = GFX_OVERRIDE_ARTWORK(type);
6263 char *setup_artwork_set = SETUP_ARTWORK_SET(setup, type);
6264 char *leveldir_identifier = leveldir_current->identifier;
6265 // !!! setLevelArtworkDir() should be moved to an earlier stage !!!
6266 char *leveldir_artwork_set = setLevelArtworkDir(artwork_first_node);
6267 boolean has_level_artwork_set = (leveldir_artwork_set != NULL);
6268 TreeInfo *custom_artwork_set =
6269 getTreeInfoFromIdentifier(artwork_first_node, leveldir_identifier);
6270 boolean has_custom_artwork_set = (custom_artwork_set != NULL);
6271 char *artwork_current_identifier;
6272 char *artwork_new_identifier = NULL; // default: nothing has changed
6274 // leveldir_current may be invalid (level group, parent link)
6275 if (!validLevelSeries(leveldir_current))
6278 /* 1st step: determine artwork set to be activated in descending order:
6279 --------------------------------------------------------------------
6280 1. setup artwork (when configured to override everything else)
6281 2. artwork set configured in "levelinfo.conf" of current level set
6282 (artwork in level directory will have priority when loading later)
6283 3. artwork in level directory (stored in artwork sub-directory)
6284 4. setup artwork (currently configured in setup menu) */
6286 if (setup_override_artwork)
6287 artwork_current_identifier = setup_artwork_set;
6288 else if (has_level_artwork_set)
6289 artwork_current_identifier = leveldir_artwork_set;
6290 else if (has_custom_artwork_set)
6291 artwork_current_identifier = leveldir_identifier;
6293 artwork_current_identifier = setup_artwork_set;
6295 /* 2nd step: check if it is really needed to reload artwork set
6296 ------------------------------------------------------------ */
6298 // ---------- reload if level set and also artwork set has changed ----------
6299 if (last_leveldir_identifier[type] != leveldir_identifier &&
6300 (last_has_custom_artwork_set[type] || has_custom_artwork_set))
6301 artwork_new_identifier = artwork_current_identifier;
6303 last_leveldir_identifier[type] = leveldir_identifier;
6304 last_has_custom_artwork_set[type] = has_custom_artwork_set;
6306 // ---------- reload if "override artwork" setting has changed --------------
6307 if (last_override_level_artwork[type] != setup_override_artwork)
6308 artwork_new_identifier = artwork_current_identifier;
6310 last_override_level_artwork[type] = setup_override_artwork;
6312 // ---------- reload if current artwork identifier has changed --------------
6313 if (!strEqual(last_artwork_identifier[type], artwork_current_identifier))
6314 artwork_new_identifier = artwork_current_identifier;
6316 // (we cannot compare string pointers here, so copy string content itself)
6317 setString(&last_artwork_identifier[type], artwork_current_identifier);
6319 // ---------- set new artwork identifier ----------
6320 *(ARTWORK_CURRENT_IDENTIFIER_PTR(artwork, type)) = artwork_current_identifier;
6322 // ---------- do not reload directly after starting -------------------------
6323 if (!initialized[type])
6324 artwork_new_identifier = NULL;
6326 initialized[type] = TRUE;
6328 return artwork_new_identifier;
6331 static void InitArtworkIdentifier(void)
6333 setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6334 setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6335 setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6338 void ReloadCustomArtwork(int force_reload)
6340 int last_game_status = game_status; // save current game status
6341 char *gfx_new_identifier;
6342 char *snd_new_identifier;
6343 char *mus_new_identifier;
6344 boolean force_reload_gfx = (force_reload & (1 << ARTWORK_TYPE_GRAPHICS));
6345 boolean force_reload_snd = (force_reload & (1 << ARTWORK_TYPE_SOUNDS));
6346 boolean force_reload_mus = (force_reload & (1 << ARTWORK_TYPE_MUSIC));
6347 boolean reload_needed;
6349 InitOverrideArtwork();
6351 AdjustGraphicsForEMC();
6352 AdjustSoundsForEMC();
6354 gfx_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6355 snd_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6356 mus_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6358 reload_needed = (gfx_new_identifier != NULL || force_reload_gfx ||
6359 snd_new_identifier != NULL || force_reload_snd ||
6360 mus_new_identifier != NULL || force_reload_mus);
6365 print_timestamp_init("ReloadCustomArtwork");
6367 SetGameStatus(GAME_MODE_LOADING);
6369 FadeOut(REDRAW_ALL);
6371 SetLoadingBackgroundImage();
6373 ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6374 print_timestamp_time("ClearRectangleOnBackground");
6378 UPDATE_BUSY_STATE();
6380 InitMissingFileHash();
6382 if (gfx_new_identifier != NULL || force_reload_gfx)
6385 Debug("init:ReloadCustomArtwork",
6386 "RELOADING GRAPHICS '%s' -> '%s' ['%s', '%s']",
6387 artwork.gfx_current_identifier,
6389 artwork.gfx_current->identifier,
6390 leveldir_current->graphics_set);
6394 print_timestamp_time("InitImages");
6397 if (snd_new_identifier != NULL || force_reload_snd)
6400 print_timestamp_time("InitSound");
6403 if (mus_new_identifier != NULL || force_reload_mus)
6406 print_timestamp_time("InitMusic");
6411 SetGameStatus(last_game_status); // restore current game status
6413 FadeOut(REDRAW_ALL);
6415 RedrawGlobalBorder();
6417 // force redraw of (open or closed) door graphics
6418 SetDoorState(DOOR_OPEN_ALL);
6419 CloseDoor(DOOR_CLOSE_ALL | DOOR_NO_DELAY);
6421 FadeSetEnterScreen();
6422 FadeSkipNextFadeOut();
6424 print_timestamp_done("ReloadCustomArtwork");
6426 LimitScreenUpdates(FALSE);
6429 void KeyboardAutoRepeatOffUnlessAutoplay(void)
6431 if (global.autoplay_leveldir == NULL)
6432 KeyboardAutoRepeatOff();
6435 void DisplayExitMessage(char *format, va_list ap)
6437 // also check for initialized video (headless flag may be temporarily unset)
6438 if (program.headless || !video.initialized)
6441 // check if draw buffer and fonts for exit message are already available
6442 if (drawto == NULL || font_initial[NUM_INITIAL_FONTS - 1].bitmap == NULL)
6445 int font_1 = FC_RED;
6446 int font_2 = FC_YELLOW;
6447 int font_3 = FC_BLUE;
6448 int font_width = getFontWidth(font_2);
6449 int font_height = getFontHeight(font_2);
6452 int sxsize = WIN_XSIZE - 2 * sx;
6453 int sysize = WIN_YSIZE - 2 * sy;
6454 int line_length = sxsize / font_width;
6455 int max_lines = sysize / font_height;
6456 int num_lines_printed;
6460 gfx.sxsize = sxsize;
6461 gfx.sysize = sysize;
6465 ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6467 DrawTextSCentered(sy, font_1, "Fatal error:");
6468 sy += 3 * font_height;;
6471 DrawTextBufferVA(sx, sy, format, ap, font_2,
6472 line_length, line_length, max_lines,
6473 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6474 sy += (num_lines_printed + 3) * font_height;
6476 DrawTextSCentered(sy, font_1, "For details, see the following error file:");
6477 sy += 3 * font_height;
6480 DrawTextBuffer(sx, sy, program.log_filename, font_2,
6481 line_length, line_length, max_lines,
6482 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6484 DrawTextSCentered(SYSIZE - 20, font_3, "Press any key or button to exit");
6486 redraw_mask = REDRAW_ALL;
6488 // force drawing exit message even if screen updates are currently limited
6489 LimitScreenUpdates(FALSE);
6493 // deactivate toons on error message screen
6494 setup.toons = FALSE;
6496 WaitForEventToContinue();
6500 // ============================================================================
6502 // ============================================================================
6506 print_timestamp_init("OpenAll");
6508 SetGameStatus(GAME_MODE_LOADING);
6512 InitGlobal(); // initialize some global variables
6514 InitRND(NEW_RANDOMIZE);
6515 InitSimpleRandom(NEW_RANDOMIZE);
6516 InitBetterRandom(NEW_RANDOMIZE);
6518 InitMissingFileHash();
6520 print_timestamp_time("[init global stuff]");
6524 print_timestamp_time("[init setup/config stuff (1)]");
6526 if (options.execute_command)
6527 Execute_Command(options.execute_command);
6529 InitNetworkSettings();
6533 if (network.serveronly)
6535 #if defined(PLATFORM_UNIX)
6536 NetworkServer(network.server_port, TRUE);
6538 Warn("networking only supported in Unix version");
6541 exit(0); // never reached, server loops forever
6545 print_timestamp_time("[init setup/config stuff (2)]");
6547 print_timestamp_time("[init setup/config stuff (3)]");
6548 InitArtworkInfo(); // needed before loading gfx, sound & music
6549 print_timestamp_time("[init setup/config stuff (4)]");
6550 InitArtworkConfig(); // needed before forking sound child process
6551 print_timestamp_time("[init setup/config stuff (5)]");
6553 print_timestamp_time("[init setup/config stuff (6)]");
6557 print_timestamp_time("[init setup/config stuff]");
6559 InitVideoDefaults();
6561 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
6564 InitEventFilter(FilterMouseMotionEvents);
6566 print_timestamp_time("[init video stuff]");
6568 InitElementPropertiesStatic();
6569 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
6570 InitElementPropertiesGfxElement();
6572 print_timestamp_time("[init element properties stuff]");
6576 print_timestamp_time("InitGfx");
6579 print_timestamp_time("InitLevelInfo");
6581 InitLevelArtworkInfo();
6582 print_timestamp_time("InitLevelArtworkInfo");
6584 InitOverrideArtwork(); // needs to know current level directory
6585 print_timestamp_time("InitOverrideArtwork");
6587 InitArtworkIdentifier(); // needs to know current level directory
6588 print_timestamp_time("InitArtworkIdentifier");
6590 InitImages(); // needs to know current level directory
6591 print_timestamp_time("InitImages");
6593 InitSound(); // needs to know current level directory
6594 print_timestamp_time("InitSound");
6596 InitMusic(); // needs to know current level directory
6597 print_timestamp_time("InitMusic");
6601 InitGfxBackground();
6607 if (global.autoplay_leveldir)
6612 else if (global.patchtapes_leveldir)
6617 else if (global.convert_leveldir)
6622 else if (global.dumplevel_leveldir)
6627 else if (global.dumptape_leveldir)
6632 else if (global.create_sketch_images_dir)
6634 CreateLevelSketchImages();
6637 else if (global.create_collect_images_dir)
6639 CreateCollectElementImages();
6643 InitNetworkServer();
6645 SetGameStatus(GAME_MODE_MAIN);
6647 FadeSetEnterScreen();
6648 if (!(fading.fade_mode & FADE_TYPE_TRANSFORM))
6649 FadeSkipNextFadeOut();
6651 print_timestamp_time("[post-artwork]");
6653 print_timestamp_done("OpenAll");
6655 if (setup.ask_for_remaining_tapes)
6656 setup.ask_for_uploading_tapes = TRUE;
6661 Debug("internal:path", "SDL_GetBasePath() == '%s'",
6663 Debug("internal:path", "SDL_GetPrefPath() == '%s'",
6664 SDL_GetPrefPath("artsoft", "rocksndiamonds"));
6665 #if defined(PLATFORM_ANDROID)
6666 Debug("internal:path", "SDL_AndroidGetInternalStoragePath() == '%s'",
6667 SDL_AndroidGetInternalStoragePath());
6668 Debug("internal:path", "SDL_AndroidGetExternalStoragePath() == '%s'",
6669 SDL_AndroidGetExternalStoragePath());
6670 Debug("internal:path", "SDL_AndroidGetExternalStorageState() == '%s'",
6671 (SDL_AndroidGetExternalStorageState() &
6672 SDL_ANDROID_EXTERNAL_STORAGE_WRITE ? "writable" :
6673 SDL_AndroidGetExternalStorageState() &
6674 SDL_ANDROID_EXTERNAL_STORAGE_READ ? "readable" : "not available"));
6679 static boolean WaitForApiThreads(void)
6681 DelayCounter thread_delay = { 10000 };
6683 if (program.api_thread_count == 0)
6686 // deactivate global animations (not accessible in game state "loading")
6687 setup.toons = FALSE;
6689 // set game state to "loading" to be able to show busy animation
6690 SetGameStatus(GAME_MODE_LOADING);
6692 ResetDelayCounter(&thread_delay);
6694 // wait for threads to finish (and fail on timeout)
6695 while (program.api_thread_count > 0)
6697 if (DelayReached(&thread_delay))
6699 Error("failed waiting for threads - TIMEOUT");
6704 UPDATE_BUSY_STATE();
6712 void CloseAllAndExit(int exit_value)
6714 WaitForApiThreads();
6719 CloseAudio(); // called after freeing sounds (needed for SDL)
6727 // set a flag to tell the network server thread to quit and wait for it
6728 // using SDL_WaitThread()
6730 // Code used with SDL 1.2:
6731 // if (network.server_thread) // terminate network server
6732 // SDL_KillThread(network.server_thread);
6734 CloseVideoDisplay();
6735 ClosePlatformDependentStuff();
6737 if (exit_value != 0 && !options.execute_command)
6739 // fall back to default level set (current set may have caused an error)
6740 SaveLevelSetup_LastSeries_Deactivate();
6742 // tell user where to find error log file which may contain more details
6743 // (error notification now directly displayed on screen inside R'n'D
6744 // NotifyUserAboutErrorFile(); // currently only works for Windows