1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
12 #include "libgame/libgame.h"
27 #include "conf_e2g.c" // include auto-generated data structure definitions
28 #include "conf_esg.c" // include auto-generated data structure definitions
29 #include "conf_e2s.c" // include auto-generated data structure definitions
30 #include "conf_fnt.c" // include auto-generated data structure definitions
31 #include "conf_g2s.c" // include auto-generated data structure definitions
32 #include "conf_g2m.c" // include auto-generated data structure definitions
33 #include "conf_act.c" // include auto-generated data structure definitions
36 #define CONFIG_TOKEN_FONT_INITIAL "font.initial"
37 #define CONFIG_TOKEN_GLOBAL_BUSY_INITIAL "global.busy_initial"
38 #define CONFIG_TOKEN_GLOBAL_BUSY "global.busy"
39 #define CONFIG_TOKEN_GLOBAL_BUSY_PLAYFIELD "global.busy_playfield"
40 #define CONFIG_TOKEN_BACKGROUND "background"
41 #define CONFIG_TOKEN_BACKGROUND_LOADING_INITIAL "background.LOADING_INITIAL"
42 #define CONFIG_TOKEN_BACKGROUND_LOADING "background.LOADING"
44 #define INITIAL_IMG_GLOBAL_BUSY_INITIAL 0
45 #define INITIAL_IMG_GLOBAL_BUSY 1
46 #define INITIAL_IMG_GLOBAL_BUSY_PLAYFIELD 2
48 #define NUM_INITIAL_IMAGES_BUSY 3
50 #define INITIAL_IMG_BACKGROUND 3
51 #define INITIAL_IMG_BACKGROUND_LOADING_INITIAL 4
52 #define INITIAL_IMG_BACKGROUND_LOADING 5
54 #define NUM_INITIAL_IMAGES 6
57 static struct FontBitmapInfo font_initial[NUM_INITIAL_FONTS];
58 static struct GraphicInfo image_initial[NUM_INITIAL_IMAGES];
60 static int copy_properties[][5] =
64 EL_BUG_LEFT, EL_BUG_RIGHT,
65 EL_BUG_UP, EL_BUG_DOWN
69 EL_SPACESHIP_LEFT, EL_SPACESHIP_RIGHT,
70 EL_SPACESHIP_UP, EL_SPACESHIP_DOWN
74 EL_BD_BUTTERFLY_LEFT, EL_BD_BUTTERFLY_RIGHT,
75 EL_BD_BUTTERFLY_UP, EL_BD_BUTTERFLY_DOWN
79 EL_BD_FIREFLY_LEFT, EL_BD_FIREFLY_RIGHT,
80 EL_BD_FIREFLY_UP, EL_BD_FIREFLY_DOWN
84 EL_PACMAN_LEFT, EL_PACMAN_RIGHT,
85 EL_PACMAN_UP, EL_PACMAN_DOWN
89 EL_YAMYAM_LEFT, EL_YAMYAM_RIGHT,
90 EL_YAMYAM_UP, EL_YAMYAM_DOWN
94 EL_MOLE_LEFT, EL_MOLE_RIGHT,
95 EL_MOLE_UP, EL_MOLE_DOWN
99 EL_SPRING_LEFT, EL_SPRING_RIGHT,
100 EL_SPRING_LEFT, EL_SPRING_RIGHT, // (to match array size)
109 // forward declaration for internal use
110 static int get_graphic_parameter_value(char *, char *, int);
113 static int getLoadingBackgroundImage(int graphic)
115 return getImageFromGraphicOrDefault(graphic, INITIAL_IMG_BACKGROUND);
118 static void SetLoadingWindowBackgroundImage(int graphic)
120 SetBackgroundImage(getLoadingBackgroundImage(graphic), REDRAW_ALL);
123 static void SetLoadingBackgroundImage(void)
125 struct GraphicInfo *graphic_info_last = graphic_info;
126 int background_image = (game_status_last_screen == -1 ?
127 INITIAL_IMG_BACKGROUND_LOADING_INITIAL :
128 INITIAL_IMG_BACKGROUND_LOADING);
130 graphic_info = image_initial;
132 SetDrawDeactivationMask(REDRAW_NONE);
133 SetDrawBackgroundMask(REDRAW_ALL);
135 SetLoadingWindowBackgroundImage(background_image);
137 graphic_info = graphic_info_last;
140 static void DrawInitAnim(boolean only_when_loading)
142 struct GraphicInfo *graphic_info_last = graphic_info;
143 int graphic = (game_status_last_screen == -1 ?
144 INITIAL_IMG_GLOBAL_BUSY_INITIAL :
145 game_status == GAME_MODE_LOADING ?
146 INITIAL_IMG_GLOBAL_BUSY :
147 INITIAL_IMG_GLOBAL_BUSY_PLAYFIELD);
148 struct MenuPosInfo *busy = (game_status_last_screen == -1 ?
149 &init_last.busy_initial :
150 game_status == GAME_MODE_LOADING ?
152 &init_last.busy_playfield);
153 static DelayCounter action_delay = { 0 };
154 int sync_frame = FrameCounter;
157 action_delay.value = GameFrameDelay;
159 // prevent OS (Windows) from complaining about program not responding
162 if (game_status != GAME_MODE_LOADING && only_when_loading)
165 if (image_initial[graphic].bitmap == NULL || window == NULL)
168 if (!DelayReached(&action_delay))
172 busy->x = (game_status == GAME_MODE_LOADING ? WIN_XSIZE / 2 : SXSIZE / 2);
174 busy->y = (game_status == GAME_MODE_LOADING ? WIN_YSIZE / 2 : SYSIZE / 2);
176 x = (game_status == GAME_MODE_LOADING ? 0 : SX) + ALIGNED_TEXT_XPOS(busy);
177 y = (game_status == GAME_MODE_LOADING ? 0 : SY) + ALIGNED_TEXT_YPOS(busy);
179 graphic_info = image_initial;
181 if (sync_frame % image_initial[graphic].anim_delay == 0)
185 int width = graphic_info[graphic].width;
186 int height = graphic_info[graphic].height;
187 int frame = getGraphicAnimationFrame(graphic, sync_frame);
189 ClearRectangleOnBackground(drawto, x, y, width, height);
191 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
192 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height, x, y);
194 BlitBitmap(drawto, window, x, y, width, height, x, y);
197 graphic_info = graphic_info_last;
202 static void DrawProgramInfo(void)
204 int font1_nr = FC_YELLOW;
205 int font2_nr = FC_RED;
206 int font2_height = getFontHeight(font2_nr);
209 int ypos3 = WIN_YSIZE - 20 - font2_height;
211 DrawInitText(getProgramInitString(), ypos1, font1_nr);
212 DrawInitText(setup.internal.program_copyright, ypos2, font2_nr);
213 DrawInitText(setup.internal.program_website, ypos3, font2_nr);
216 static void FreeGadgets(void)
218 FreeLevelEditorGadgets();
225 void InitGadgets(void)
227 static boolean gadgets_initialized = FALSE;
229 if (gadgets_initialized)
232 CreateLevelEditorGadgets();
236 CreateScreenGadgets();
238 InitGadgetsSoundCallback(PlaySoundActivating, PlaySoundSelecting);
240 gadgets_initialized = TRUE;
243 static void InitElementSmallImagesScaledUp(int graphic)
245 struct GraphicInfo *g = &graphic_info[graphic];
247 // create small and game tile sized bitmaps (and scale up, if needed)
248 CreateImageWithSmallImages(graphic, g->scale_up_factor, g->tile_size);
251 static void InitElementSmallImages(void)
253 print_timestamp_init("InitElementSmallImages");
255 static int special_graphics[] =
269 IMG_EDITOR_ELEMENT_BORDER,
270 IMG_EDITOR_ELEMENT_BORDER_INPUT,
271 IMG_EDITOR_CASCADE_LIST,
272 IMG_EDITOR_CASCADE_LIST_ACTIVE,
275 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
276 int num_property_mappings = getImageListPropertyMappingSize();
279 print_timestamp_time("getImageListPropertyMapping/Size");
281 print_timestamp_init("InitElementSmallImagesScaledUp (1)");
282 // initialize normal element images from static configuration
283 for (i = 0; element_to_graphic[i].element > -1; i++)
284 InitElementSmallImagesScaledUp(element_to_graphic[i].graphic);
285 print_timestamp_done("InitElementSmallImagesScaledUp (1)");
287 // initialize special element images from static configuration
288 for (i = 0; element_to_special_graphic[i].element > -1; i++)
289 InitElementSmallImagesScaledUp(element_to_special_graphic[i].graphic);
290 print_timestamp_time("InitElementSmallImagesScaledUp (2)");
292 // initialize element images from dynamic configuration
293 for (i = 0; i < num_property_mappings; i++)
294 if (property_mapping[i].base_index < MAX_NUM_ELEMENTS)
295 InitElementSmallImagesScaledUp(property_mapping[i].artwork_index);
296 print_timestamp_time("InitElementSmallImagesScaledUp (3)");
298 // initialize special non-element images from above list
299 for (i = 0; special_graphics[i] > -1; i++)
300 InitElementSmallImagesScaledUp(special_graphics[i]);
301 print_timestamp_time("InitElementSmallImagesScaledUp (4)");
303 print_timestamp_done("InitElementSmallImages");
306 static void InitScaledImagesScaledUp(int graphic)
308 struct GraphicInfo *g = &graphic_info[graphic];
310 ScaleImage(graphic, g->scale_up_factor);
313 static void InitScaledImages(void)
315 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
316 int num_property_mappings = getImageListPropertyMappingSize();
319 // scale normal images from static configuration, if not already scaled
320 for (i = 0; i < NUM_IMAGE_FILES; i++)
321 InitScaledImagesScaledUp(i);
323 // scale images from dynamic configuration, if not already scaled
324 for (i = 0; i < num_property_mappings; i++)
325 InitScaledImagesScaledUp(property_mapping[i].artwork_index);
328 static void InitBitmapPointers(void)
330 int num_images = getImageListSize();
333 // standard size bitmap may have changed -- update default bitmap pointer
334 for (i = 0; i < num_images; i++)
335 if (graphic_info[i].bitmaps)
336 graphic_info[i].bitmap = graphic_info[i].bitmaps[IMG_BITMAP_STANDARD];
339 void InitImageTextures(void)
341 static int texture_graphics[] =
343 IMG_GFX_REQUEST_BUTTON_TOUCH_YES,
344 IMG_GFX_REQUEST_BUTTON_TOUCH_NO,
345 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM,
346 IMG_GFX_GAME_BUTTON_TOUCH_STOP,
347 IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,
348 IMG_MENU_BUTTON_TOUCH_BACK,
349 IMG_MENU_BUTTON_TOUCH_NEXT,
350 IMG_MENU_BUTTON_TOUCH_BACK2,
351 IMG_MENU_BUTTON_TOUCH_NEXT2,
356 FreeAllImageTextures();
358 for (i = IMG_GLOBAL_BORDER_FIRST; i <= IMG_GLOBAL_BORDER_LAST; i++)
359 CreateImageTextures(i);
361 for (i = 0; i < MAX_NUM_TOONS; i++)
362 CreateImageTextures(IMG_TOON_1 + i);
364 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
366 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
368 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
370 int graphic = global_anim_info[i].graphic[j][k];
372 if (graphic == IMG_UNDEFINED)
375 CreateImageTextures(graphic);
380 for (i = 0; texture_graphics[i] > -1; i++)
381 CreateImageTextures(texture_graphics[i]);
384 static int getFontSpecialSuffix(void)
388 // (special case: do not use special font for GAME_MODE_LOADING)
389 if (game_status >= GAME_MODE_TITLE_INITIAL &&
390 game_status <= GAME_MODE_PSEUDO_PREVIEW)
391 special = game_status;
392 else if (game_status == GAME_MODE_PSEUDO_TYPENAME)
393 special = GFX_SPECIAL_ARG_MAIN;
394 else if (game_status == GAME_MODE_PSEUDO_TYPENAMES)
395 special = GFX_SPECIAL_ARG_NAMES;
400 static int getFontBitmapID(int font_nr)
402 int special = getFontSpecialSuffix();
405 return font_info[font_nr].special_bitmap_id[special];
410 static int getFontFromToken(char *token)
412 char *value = getHashEntry(font_token_hash, token);
417 // if font not found, use reliable default value
418 return FONT_INITIAL_1;
421 static char *getTokenFromFont(int font_nr)
423 static char *token = NULL;
424 int special = getFontSpecialSuffix();
429 token = getStringCat2(font_info[font_nr].token_name,
430 special_suffix_info[special].suffix);
432 token = getStringCopy(font_info[font_nr].token_name);
437 static void InitFontGraphicInfo(void)
439 static struct FontBitmapInfo *font_bitmap_info = NULL;
440 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
441 int num_property_mappings = getImageListPropertyMappingSize();
442 int num_font_bitmaps = NUM_FONTS;
445 if (graphic_info == NULL) // still at startup phase
447 InitFontInfo(font_initial, NUM_INITIAL_FONTS,
448 getFontBitmapID, getFontFromToken, getTokenFromFont);
453 // ---------- initialize font graphic definitions ----------
455 // always start with reliable default values (normal font graphics)
456 for (i = 0; i < NUM_FONTS; i++)
457 font_info[i].graphic = IMG_FONT_INITIAL_1;
459 // initialize normal font/graphic mapping from static configuration
460 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
462 int font_nr = font_to_graphic[i].font_nr;
463 int special = font_to_graphic[i].special;
464 int graphic = font_to_graphic[i].graphic;
469 font_info[font_nr].graphic = graphic;
472 // always start with reliable default values (special font graphics)
473 for (i = 0; i < NUM_FONTS; i++)
475 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
477 font_info[i].special_graphic[j] = font_info[i].graphic;
478 font_info[i].special_bitmap_id[j] = i;
482 // initialize special font/graphic mapping from static configuration
483 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
485 int font_nr = font_to_graphic[i].font_nr;
486 int special = font_to_graphic[i].special;
487 int graphic = font_to_graphic[i].graphic;
488 int base_graphic = font2baseimg(font_nr);
490 if (IS_SPECIAL_GFX_ARG(special))
492 boolean base_redefined =
493 getImageListEntryFromImageID(base_graphic)->redefined;
494 boolean special_redefined =
495 getImageListEntryFromImageID(graphic)->redefined;
496 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
498 /* if the base font ("font.title_1", for example) has been redefined,
499 but not the special font ("font.title_1.LEVELS", for example), do not
500 use an existing (in this case considered obsolete) special font
501 anymore, but use the automatically determined default font */
502 /* special case: cloned special fonts must be explicitly redefined,
503 but are not automatically redefined by redefining base font */
504 if (base_redefined && !special_redefined && !special_cloned)
507 font_info[font_nr].special_graphic[special] = graphic;
508 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
513 // initialize special font/graphic mapping from dynamic configuration
514 for (i = 0; i < num_property_mappings; i++)
516 int font_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
517 int special = property_mapping[i].ext3_index;
518 int graphic = property_mapping[i].artwork_index;
520 if (font_nr < 0 || font_nr >= NUM_FONTS)
523 if (IS_SPECIAL_GFX_ARG(special))
525 font_info[font_nr].special_graphic[special] = graphic;
526 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
531 /* correct special font/graphic mapping for cloned fonts for downwards
532 compatibility of PREVIEW fonts -- this is only needed for implicit
533 redefinition of special font by redefined base font, and only if other
534 fonts are cloned from this special font (like in the "Zelda" level set) */
535 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
537 int font_nr = font_to_graphic[i].font_nr;
538 int special = font_to_graphic[i].special;
539 int graphic = font_to_graphic[i].graphic;
541 if (IS_SPECIAL_GFX_ARG(special))
543 boolean special_redefined =
544 getImageListEntryFromImageID(graphic)->redefined;
545 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
547 if (special_cloned && !special_redefined)
551 for (j = 0; font_to_graphic[j].font_nr > -1; j++)
553 int font_nr2 = font_to_graphic[j].font_nr;
554 int special2 = font_to_graphic[j].special;
555 int graphic2 = font_to_graphic[j].graphic;
557 if (IS_SPECIAL_GFX_ARG(special2) &&
558 graphic2 == graphic_info[graphic].clone_from)
560 font_info[font_nr].special_graphic[special] =
561 font_info[font_nr2].special_graphic[special2];
562 font_info[font_nr].special_bitmap_id[special] =
563 font_info[font_nr2].special_bitmap_id[special2];
570 // reset non-redefined ".active" font graphics if normal font is redefined
571 // (this different treatment is needed because normal and active fonts are
572 // independently defined ("active" is not a property of font definitions!)
573 for (i = 0; i < NUM_FONTS; i++)
575 int font_nr_base = i;
576 int font_nr_active = FONT_ACTIVE(font_nr_base);
578 // check only those fonts with exist as normal and ".active" variant
579 if (font_nr_base != font_nr_active)
581 int base_graphic = font_info[font_nr_base].graphic;
582 int active_graphic = font_info[font_nr_active].graphic;
583 boolean base_redefined =
584 getImageListEntryFromImageID(base_graphic)->redefined;
585 boolean active_redefined =
586 getImageListEntryFromImageID(active_graphic)->redefined;
588 /* if the base font ("font.menu_1", for example) has been redefined,
589 but not the active font ("font.menu_1.active", for example), do not
590 use an existing (in this case considered obsolete) active font
591 anymore, but use the automatically determined default font */
592 if (base_redefined && !active_redefined)
593 font_info[font_nr_active].graphic = base_graphic;
595 // now also check each "special" font (which may be the same as above)
596 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
598 int base_graphic = font_info[font_nr_base].special_graphic[j];
599 int active_graphic = font_info[font_nr_active].special_graphic[j];
600 boolean base_redefined =
601 getImageListEntryFromImageID(base_graphic)->redefined;
602 boolean active_redefined =
603 getImageListEntryFromImageID(active_graphic)->redefined;
605 // same as above, but check special graphic definitions, for example:
606 // redefined "font.menu_1.MAIN" invalidates "font.menu_1.active.MAIN"
607 if (base_redefined && !active_redefined)
609 font_info[font_nr_active].special_graphic[j] =
610 font_info[font_nr_base].special_graphic[j];
611 font_info[font_nr_active].special_bitmap_id[j] =
612 font_info[font_nr_base].special_bitmap_id[j];
618 // ---------- initialize font bitmap array ----------
620 if (font_bitmap_info != NULL)
621 FreeFontInfo(font_bitmap_info);
624 checked_calloc(num_font_bitmaps * sizeof(struct FontBitmapInfo));
626 // ---------- initialize font bitmap definitions ----------
628 for (i = 0; i < NUM_FONTS; i++)
630 if (i < NUM_INITIAL_FONTS)
632 font_bitmap_info[i] = font_initial[i];
636 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
638 int font_bitmap_id = font_info[i].special_bitmap_id[j];
639 int graphic = font_info[i].special_graphic[j];
641 // set 'graphic_info' for font entries, if uninitialized (guessed)
642 if (graphic_info[graphic].anim_frames < MIN_NUM_CHARS_PER_FONT)
644 graphic_info[graphic].anim_frames = DEFAULT_NUM_CHARS_PER_FONT;
645 graphic_info[graphic].anim_frames_per_line = DEFAULT_NUM_CHARS_PER_LINE;
648 // copy font relevant information from graphics information
649 font_bitmap_info[font_bitmap_id].bitmap = graphic_info[graphic].bitmap;
650 font_bitmap_info[font_bitmap_id].src_x = graphic_info[graphic].src_x;
651 font_bitmap_info[font_bitmap_id].src_y = graphic_info[graphic].src_y;
652 font_bitmap_info[font_bitmap_id].width = graphic_info[graphic].width;
653 font_bitmap_info[font_bitmap_id].height = graphic_info[graphic].height;
655 font_bitmap_info[font_bitmap_id].offset_x =
656 graphic_info[graphic].offset_x;
657 font_bitmap_info[font_bitmap_id].offset_y =
658 graphic_info[graphic].offset_y;
660 font_bitmap_info[font_bitmap_id].draw_xoffset =
661 graphic_info[graphic].draw_xoffset;
662 font_bitmap_info[font_bitmap_id].draw_yoffset =
663 graphic_info[graphic].draw_yoffset;
665 font_bitmap_info[font_bitmap_id].num_chars =
666 graphic_info[graphic].anim_frames;
667 font_bitmap_info[font_bitmap_id].num_chars_per_line =
668 graphic_info[graphic].anim_frames_per_line;
672 InitFontInfo(font_bitmap_info, num_font_bitmaps,
673 getFontBitmapID, getFontFromToken, getTokenFromFont);
676 static void InitGlobalAnimGraphicInfo(void)
678 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
679 int num_property_mappings = getImageListPropertyMappingSize();
682 if (graphic_info == NULL) // still at startup phase
685 // always start with reliable default values (no global animations)
686 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
687 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
688 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
689 global_anim_info[i].graphic[j][k] = IMG_UNDEFINED;
691 // initialize global animation definitions from static configuration
692 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
694 int j = GLOBAL_ANIM_ID_PART_BASE;
695 int k = GFX_SPECIAL_ARG_DEFAULT;
697 global_anim_info[i].graphic[j][k] = IMG_GFX_GLOBAL_ANIM_1 + i;
700 // initialize global animation definitions from dynamic configuration
701 for (i = 0; i < num_property_mappings; i++)
703 int anim_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS - NUM_FONTS;
704 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
705 int special = property_mapping[i].ext3_index;
706 int graphic = property_mapping[i].artwork_index;
708 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
711 // set animation part to base part, if not specified
712 if (!IS_GLOBAL_ANIM_PART(part_nr))
713 part_nr = GLOBAL_ANIM_ID_PART_BASE;
715 // set animation screen to default, if not specified
716 if (!IS_SPECIAL_GFX_ARG(special))
717 special = GFX_SPECIAL_ARG_DEFAULT;
719 global_anim_info[anim_nr].graphic[part_nr][special] = graphic;
721 // fix default value for ".draw_masked" (for backward compatibility)
722 struct GraphicInfo *g = &graphic_info[graphic];
723 struct FileInfo *image = getImageListEntryFromImageID(graphic);
724 char **parameter_raw = image->parameter;
725 int p = GFX_ARG_DRAW_MASKED;
726 int draw_masked = get_graphic_parameter_value(parameter_raw[p],
727 image_config_suffix[p].token,
728 image_config_suffix[p].type);
730 // if ".draw_masked" parameter is undefined, use default value "TRUE"
731 if (draw_masked == ARG_UNDEFINED_VALUE)
732 g->draw_masked = TRUE;
736 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
737 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
738 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
739 if (global_anim_info[i].graphic[j][k] != IMG_UNDEFINED &&
740 graphic_info[global_anim_info[i].graphic[j][k]].bitmap != NULL)
741 Debug("init:InitGlobalAnimGraphicInfo",
742 "anim %d, part %d, mode %d => %d",
743 i, j, k, global_anim_info[i].graphic[j][k]);
747 static void InitGlobalAnimSoundInfo(void)
749 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
750 int num_property_mappings = getSoundListPropertyMappingSize();
753 // always start with reliable default values (no global animation sounds)
754 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
755 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
756 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
757 global_anim_info[i].sound[j][k] = SND_UNDEFINED;
759 // initialize global animation sound definitions from dynamic configuration
760 for (i = 0; i < num_property_mappings; i++)
762 int anim_nr = property_mapping[i].base_index - 2 * MAX_NUM_ELEMENTS;
763 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
764 int special = property_mapping[i].ext3_index;
765 int sound = property_mapping[i].artwork_index;
767 // sound uses control definition; map it to position of graphic (artwork)
768 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
770 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
773 // set animation part to base part, if not specified
774 if (!IS_GLOBAL_ANIM_PART(part_nr))
775 part_nr = GLOBAL_ANIM_ID_PART_BASE;
777 // set animation screen to default, if not specified
778 if (!IS_SPECIAL_GFX_ARG(special))
779 special = GFX_SPECIAL_ARG_DEFAULT;
781 global_anim_info[anim_nr].sound[part_nr][special] = sound;
785 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
786 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
787 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
788 if (global_anim_info[i].sound[j][k] != SND_UNDEFINED)
789 Debug("init:InitGlobalAnimSoundInfo",
790 "anim %d, part %d, mode %d => %d",
791 i, j, k, global_anim_info[i].sound[j][k]);
795 static void InitGlobalAnimMusicInfo(void)
797 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
798 int num_property_mappings = getMusicListPropertyMappingSize();
801 // always start with reliable default values (no global animation music)
802 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
803 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
804 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
805 global_anim_info[i].music[j][k] = MUS_UNDEFINED;
807 // initialize global animation music definitions from dynamic configuration
808 for (i = 0; i < num_property_mappings; i++)
810 int anim_nr = property_mapping[i].base_index - NUM_MUSIC_PREFIXES;
811 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
812 int special = property_mapping[i].ext2_index;
813 int music = property_mapping[i].artwork_index;
815 // music uses control definition; map it to position of graphic (artwork)
816 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
818 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
821 // set animation part to base part, if not specified
822 if (!IS_GLOBAL_ANIM_PART(part_nr))
823 part_nr = GLOBAL_ANIM_ID_PART_BASE;
825 // set animation screen to default, if not specified
826 if (!IS_SPECIAL_GFX_ARG(special))
827 special = GFX_SPECIAL_ARG_DEFAULT;
829 global_anim_info[anim_nr].music[part_nr][special] = music;
833 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
834 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
835 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
836 if (global_anim_info[i].music[j][k] != MUS_UNDEFINED)
837 Debug("init:InitGlobalAnimMusicInfo",
838 "anim %d, part %d, mode %d => %d",
839 i, j, k, global_anim_info[i].music[j][k]);
843 static void InitElementGraphicInfo(void)
845 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
846 int num_property_mappings = getImageListPropertyMappingSize();
849 if (graphic_info == NULL) // still at startup phase
852 // set values to -1 to identify later as "uninitialized" values
853 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
855 for (act = 0; act < NUM_ACTIONS; act++)
857 element_info[i].graphic[act] = -1;
858 element_info[i].crumbled[act] = -1;
860 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
862 element_info[i].direction_graphic[act][dir] = -1;
863 element_info[i].direction_crumbled[act][dir] = -1;
870 // initialize normal element/graphic mapping from static configuration
871 for (i = 0; element_to_graphic[i].element > -1; i++)
873 int element = element_to_graphic[i].element;
874 int action = element_to_graphic[i].action;
875 int direction = element_to_graphic[i].direction;
876 boolean crumbled = element_to_graphic[i].crumbled;
877 int graphic = element_to_graphic[i].graphic;
878 int base_graphic = el2baseimg(element);
880 if (graphic_info[graphic].bitmap == NULL)
883 if ((action > -1 || direction > -1 || crumbled == TRUE) &&
886 boolean base_redefined =
887 getImageListEntryFromImageID(base_graphic)->redefined;
888 boolean act_dir_redefined =
889 getImageListEntryFromImageID(graphic)->redefined;
891 /* if the base graphic ("emerald", for example) has been redefined,
892 but not the action graphic ("emerald.falling", for example), do not
893 use an existing (in this case considered obsolete) action graphic
894 anymore, but use the automatically determined default graphic */
895 if (base_redefined && !act_dir_redefined)
900 action = ACTION_DEFAULT;
905 element_info[element].direction_crumbled[action][direction] = graphic;
907 element_info[element].crumbled[action] = graphic;
912 element_info[element].direction_graphic[action][direction] = graphic;
914 element_info[element].graphic[action] = graphic;
918 // initialize normal element/graphic mapping from dynamic configuration
919 for (i = 0; i < num_property_mappings; i++)
921 int element = property_mapping[i].base_index;
922 int action = property_mapping[i].ext1_index;
923 int direction = property_mapping[i].ext2_index;
924 int special = property_mapping[i].ext3_index;
925 int graphic = property_mapping[i].artwork_index;
926 boolean crumbled = FALSE;
928 if (special == GFX_SPECIAL_ARG_CRUMBLED)
934 if (graphic_info[graphic].bitmap == NULL)
937 if (element >= MAX_NUM_ELEMENTS || special != -1)
941 action = ACTION_DEFAULT;
946 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
947 element_info[element].direction_crumbled[action][dir] = -1;
950 element_info[element].direction_crumbled[action][direction] = graphic;
952 element_info[element].crumbled[action] = graphic;
957 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
958 element_info[element].direction_graphic[action][dir] = -1;
961 element_info[element].direction_graphic[action][direction] = graphic;
963 element_info[element].graphic[action] = graphic;
967 // now copy all graphics that are defined to be cloned from other graphics
968 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
970 int graphic = element_info[i].graphic[ACTION_DEFAULT];
971 int crumbled_like, diggable_like;
976 crumbled_like = graphic_info[graphic].crumbled_like;
977 diggable_like = graphic_info[graphic].diggable_like;
979 if (crumbled_like != -1 && element_info[i].crumbled[ACTION_DEFAULT] == -1)
981 for (act = 0; act < NUM_ACTIONS; act++)
982 element_info[i].crumbled[act] =
983 element_info[crumbled_like].crumbled[act];
984 for (act = 0; act < NUM_ACTIONS; act++)
985 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
986 element_info[i].direction_crumbled[act][dir] =
987 element_info[crumbled_like].direction_crumbled[act][dir];
990 if (diggable_like != -1 && element_info[i].graphic[ACTION_DIGGING] == -1)
992 element_info[i].graphic[ACTION_DIGGING] =
993 element_info[diggable_like].graphic[ACTION_DIGGING];
994 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
995 element_info[i].direction_graphic[ACTION_DIGGING][dir] =
996 element_info[diggable_like].direction_graphic[ACTION_DIGGING][dir];
1000 // set hardcoded definitions for some runtime elements without graphic
1001 element_info[EL_AMOEBA_TO_DIAMOND].graphic[ACTION_DEFAULT] = IMG_AMOEBA_DEAD;
1003 // set hardcoded definitions for some internal elements without graphic
1004 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1006 if (IS_EDITOR_CASCADE_INACTIVE(i))
1007 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST;
1008 else if (IS_EDITOR_CASCADE_ACTIVE(i))
1009 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST_ACTIVE;
1012 // now set all undefined/invalid graphics to -1 to set to default after it
1013 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1015 for (act = 0; act < NUM_ACTIONS; act++)
1019 graphic = element_info[i].graphic[act];
1020 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1021 element_info[i].graphic[act] = -1;
1023 graphic = element_info[i].crumbled[act];
1024 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1025 element_info[i].crumbled[act] = -1;
1027 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1029 graphic = element_info[i].direction_graphic[act][dir];
1030 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1031 element_info[i].direction_graphic[act][dir] = -1;
1033 graphic = element_info[i].direction_crumbled[act][dir];
1034 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1035 element_info[i].direction_crumbled[act][dir] = -1;
1040 UPDATE_BUSY_STATE();
1042 // adjust graphics with 2nd tile for movement according to direction
1043 // (do this before correcting '-1' values to minimize calculations)
1044 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1046 for (act = 0; act < NUM_ACTIONS; act++)
1048 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1050 int graphic = element_info[i].direction_graphic[act][dir];
1051 int move_dir = (act == ACTION_FALLING ? MV_BIT_DOWN : dir);
1053 if (act == ACTION_FALLING) // special case
1054 graphic = element_info[i].graphic[act];
1056 if (graphic != -1 &&
1057 graphic_info[graphic].double_movement &&
1058 graphic_info[graphic].swap_double_tiles != 0)
1060 struct GraphicInfo *g = &graphic_info[graphic];
1061 int src_x_front = g->src_x;
1062 int src_y_front = g->src_y;
1063 int src_x_back = g->src_x + g->offset2_x;
1064 int src_y_back = g->src_y + g->offset2_y;
1065 boolean frames_are_ordered_diagonally = (g->offset_x != 0 &&
1067 boolean front_is_left_or_upper = (src_x_front < src_x_back ||
1068 src_y_front < src_y_back);
1069 boolean swap_movement_tiles_always = (g->swap_double_tiles == 1);
1070 boolean swap_movement_tiles_autodetected =
1071 (!frames_are_ordered_diagonally &&
1072 ((move_dir == MV_BIT_LEFT && !front_is_left_or_upper) ||
1073 (move_dir == MV_BIT_UP && !front_is_left_or_upper) ||
1074 (move_dir == MV_BIT_RIGHT && front_is_left_or_upper) ||
1075 (move_dir == MV_BIT_DOWN && front_is_left_or_upper)));
1077 // swap frontside and backside graphic tile coordinates, if needed
1078 if (swap_movement_tiles_always || swap_movement_tiles_autodetected)
1080 // get current (wrong) backside tile coordinates
1081 getGraphicSourceXY(graphic, 0, &src_x_back, &src_y_back, TRUE);
1083 // set frontside tile coordinates to backside tile coordinates
1084 g->src_x = src_x_back;
1085 g->src_y = src_y_back;
1087 // invert tile offset to point to new backside tile coordinates
1091 // do not swap front and backside tiles again after correction
1092 g->swap_double_tiles = 0;
1099 UPDATE_BUSY_STATE();
1101 // now set all '-1' values to element specific default values
1102 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1104 int default_graphic = element_info[i].graphic[ACTION_DEFAULT];
1105 int default_crumbled = element_info[i].crumbled[ACTION_DEFAULT];
1106 int default_direction_graphic[NUM_DIRECTIONS_FULL];
1107 int default_direction_crumbled[NUM_DIRECTIONS_FULL];
1109 if (default_graphic == -1)
1110 default_graphic = IMG_UNKNOWN;
1112 if (default_crumbled == -1)
1113 default_crumbled = default_graphic;
1115 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1117 default_direction_graphic[dir] =
1118 element_info[i].direction_graphic[ACTION_DEFAULT][dir];
1119 default_direction_crumbled[dir] =
1120 element_info[i].direction_crumbled[ACTION_DEFAULT][dir];
1122 if (default_direction_graphic[dir] == -1)
1123 default_direction_graphic[dir] = default_graphic;
1125 if (default_direction_crumbled[dir] == -1)
1126 default_direction_crumbled[dir] = default_direction_graphic[dir];
1129 for (act = 0; act < NUM_ACTIONS; act++)
1131 boolean act_remove = ((IS_DIGGABLE(i) && act == ACTION_DIGGING) ||
1132 (IS_SNAPPABLE(i) && act == ACTION_SNAPPING) ||
1133 (IS_COLLECTIBLE(i) && act == ACTION_COLLECTING));
1134 boolean act_turning = (act == ACTION_TURNING_FROM_LEFT ||
1135 act == ACTION_TURNING_FROM_RIGHT ||
1136 act == ACTION_TURNING_FROM_UP ||
1137 act == ACTION_TURNING_FROM_DOWN);
1139 // generic default action graphic (defined by "[default]" directive)
1140 int default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1141 int default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1142 int default_remove_graphic = IMG_EMPTY;
1144 if (act_remove && default_action_graphic != -1)
1145 default_remove_graphic = default_action_graphic;
1147 // look for special default action graphic (classic game specific)
1148 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].graphic[act] != -1)
1149 default_action_graphic = element_info[EL_BD_DEFAULT].graphic[act];
1150 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].graphic[act] != -1)
1151 default_action_graphic = element_info[EL_SP_DEFAULT].graphic[act];
1152 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].graphic[act] != -1)
1153 default_action_graphic = element_info[EL_SB_DEFAULT].graphic[act];
1154 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].graphic[act] != -1)
1155 default_action_graphic = element_info[EL_MM_DEFAULT].graphic[act];
1157 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].crumbled[act] != -1)
1158 default_action_crumbled = element_info[EL_BD_DEFAULT].crumbled[act];
1159 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].crumbled[act] != -1)
1160 default_action_crumbled = element_info[EL_SP_DEFAULT].crumbled[act];
1161 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].crumbled[act] != -1)
1162 default_action_crumbled = element_info[EL_SB_DEFAULT].crumbled[act];
1163 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].crumbled[act] != -1)
1164 default_action_crumbled = element_info[EL_MM_DEFAULT].crumbled[act];
1166 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
1167 // !!! make this better !!!
1168 if (i == EL_EMPTY_SPACE)
1170 default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1171 default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1174 if (default_action_graphic == -1)
1175 default_action_graphic = default_graphic;
1177 if (default_action_crumbled == -1)
1178 default_action_crumbled = default_action_graphic;
1180 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1182 // use action graphic as the default direction graphic, if undefined
1183 int default_action_direction_graphic = element_info[i].graphic[act];
1184 int default_action_direction_crumbled = element_info[i].crumbled[act];
1186 // no graphic for current action -- use default direction graphic
1187 if (default_action_direction_graphic == -1)
1188 default_action_direction_graphic =
1189 (act_remove ? default_remove_graphic :
1191 element_info[i].direction_graphic[ACTION_TURNING][dir] :
1192 default_action_graphic != default_graphic ?
1193 default_action_graphic :
1194 default_direction_graphic[dir]);
1196 if (element_info[i].direction_graphic[act][dir] == -1)
1197 element_info[i].direction_graphic[act][dir] =
1198 default_action_direction_graphic;
1200 if (default_action_direction_crumbled == -1)
1201 default_action_direction_crumbled =
1202 element_info[i].direction_graphic[act][dir];
1204 if (element_info[i].direction_crumbled[act][dir] == -1)
1205 element_info[i].direction_crumbled[act][dir] =
1206 default_action_direction_crumbled;
1209 // no graphic for this specific action -- use default action graphic
1210 if (element_info[i].graphic[act] == -1)
1211 element_info[i].graphic[act] =
1212 (act_remove ? default_remove_graphic :
1213 act_turning ? element_info[i].graphic[ACTION_TURNING] :
1214 default_action_graphic);
1216 if (element_info[i].crumbled[act] == -1)
1217 element_info[i].crumbled[act] = element_info[i].graphic[act];
1221 UPDATE_BUSY_STATE();
1224 static void InitElementSpecialGraphicInfo(void)
1226 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
1227 int num_property_mappings = getImageListPropertyMappingSize();
1230 // always start with reliable default values
1231 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1232 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1233 element_info[i].special_graphic[j] =
1234 element_info[i].graphic[ACTION_DEFAULT];
1236 // initialize special element/graphic mapping from static configuration
1237 for (i = 0; element_to_special_graphic[i].element > -1; i++)
1239 int element = element_to_special_graphic[i].element;
1240 int special = element_to_special_graphic[i].special;
1241 int graphic = element_to_special_graphic[i].graphic;
1242 int base_graphic = el2baseimg(element);
1243 boolean base_redefined =
1244 getImageListEntryFromImageID(base_graphic)->redefined;
1245 boolean special_redefined =
1246 getImageListEntryFromImageID(graphic)->redefined;
1248 /* if the base graphic ("emerald", for example) has been redefined,
1249 but not the special graphic ("emerald.EDITOR", for example), do not
1250 use an existing (in this case considered obsolete) special graphic
1251 anymore, but use the automatically created (down-scaled) graphic */
1252 if (base_redefined && !special_redefined)
1255 element_info[element].special_graphic[special] = graphic;
1258 // initialize special element/graphic mapping from dynamic configuration
1259 for (i = 0; i < num_property_mappings; i++)
1261 int element = property_mapping[i].base_index;
1262 int action = property_mapping[i].ext1_index;
1263 int direction = property_mapping[i].ext2_index;
1264 int special = property_mapping[i].ext3_index;
1265 int graphic = property_mapping[i].artwork_index;
1267 // for action ".active", replace element with active element, if exists
1268 if (action == ACTION_ACTIVE && element != ELEMENT_ACTIVE(element))
1270 element = ELEMENT_ACTIVE(element);
1274 if (element >= MAX_NUM_ELEMENTS)
1277 // do not change special graphic if action or direction was specified
1278 if (action != -1 || direction != -1)
1281 if (IS_SPECIAL_GFX_ARG(special))
1282 element_info[element].special_graphic[special] = graphic;
1285 // now set all undefined/invalid graphics to default
1286 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1287 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1288 if (graphic_info[element_info[i].special_graphic[j]].bitmap == NULL)
1289 element_info[i].special_graphic[j] =
1290 element_info[i].graphic[ACTION_DEFAULT];
1293 static int get_graphic_parameter_value(char *value_raw, char *suffix, int type)
1295 if (type != TYPE_ELEMENT && type != TYPE_GRAPHIC)
1296 return get_parameter_value(value_raw, suffix, type);
1298 if (strEqual(value_raw, ARG_UNDEFINED))
1299 return ARG_UNDEFINED_VALUE;
1301 if (type == TYPE_ELEMENT)
1303 char *value = getHashEntry(element_token_hash, value_raw);
1308 Warn("error found in config file:");
1309 Warn("- config file: '%s'", getImageConfigFilename());
1310 Warn("error: invalid element token '%s'", value_raw);
1311 Warn("custom graphic rejected for this element/action");
1312 Warn("fallback done to undefined element for this graphic");
1316 return (value != NULL ? atoi(value) : EL_UNDEFINED);
1318 else if (type == TYPE_GRAPHIC)
1320 char *value = getHashEntry(graphic_token_hash, value_raw);
1321 int fallback_graphic = IMG_CHAR_EXCLAM;
1326 Warn("error found in config file:");
1327 Warn("- config file: '%s'", getImageConfigFilename());
1328 Warn("error: invalid graphic token '%s'", value_raw);
1329 Warn("custom graphic rejected for this element/action");
1330 Warn("fallback done to 'char_exclam' for this graphic");
1334 return (value != NULL ? atoi(value) : fallback_graphic);
1340 static int get_scaled_graphic_width(Bitmap *src_bitmap, int graphic)
1342 int original_width = getOriginalImageWidthFromImageID(graphic);
1343 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1345 // only happens when loaded outside artwork system (like "global.busy")
1346 if (graphic_info == image_initial && src_bitmap)
1347 original_width = src_bitmap->width;
1349 return original_width * scale_up_factor;
1352 static int get_scaled_graphic_height(Bitmap *src_bitmap, int graphic)
1354 int original_height = getOriginalImageHeightFromImageID(graphic);
1355 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1357 // only happens when loaded outside artwork system (like "global.busy")
1358 if (graphic_info == image_initial && src_bitmap)
1359 original_height = src_bitmap->height;
1361 return original_height * scale_up_factor;
1364 static void set_graphic_parameters_ext(int graphic, int *parameter,
1365 Bitmap **src_bitmaps)
1367 struct GraphicInfo *g = &graphic_info[graphic];
1368 Bitmap *src_bitmap = (src_bitmaps ? src_bitmaps[IMG_BITMAP_STANDARD] : NULL);
1369 int anim_frames_per_row = 1, anim_frames_per_col = 1;
1370 int anim_frames_per_line = 1;
1372 // always start with reliable default values
1373 g->src_image_width = 0;
1374 g->src_image_height = 0;
1377 g->width = TILEX; // default for element graphics
1378 g->height = TILEY; // default for element graphics
1379 g->offset_x = 0; // one or both of these values ...
1380 g->offset_y = 0; // ... will be corrected later
1381 g->offset2_x = 0; // one or both of these values ...
1382 g->offset2_y = 0; // ... will be corrected later
1383 g->swap_double_tiles = -1; // auto-detect tile swapping
1384 g->crumbled_like = -1; // do not use clone element
1385 g->diggable_like = -1; // do not use clone element
1386 g->border_size = TILEX / 8; // "CRUMBLED" border size
1387 g->scale_up_factor = 1; // default: no scaling up
1388 g->tile_size = TILESIZE; // default: standard tile size
1389 g->clone_from = -1; // do not use clone graphic
1390 g->init_delay_fixed = 0;
1391 g->init_delay_random = 0;
1392 g->init_delay_action = -1;
1393 g->anim_delay_fixed = 0;
1394 g->anim_delay_random = 0;
1395 g->anim_delay_action = -1;
1396 g->post_delay_fixed = 0;
1397 g->post_delay_random = 0;
1398 g->post_delay_action = -1;
1399 g->init_event = ANIM_EVENT_UNDEFINED;
1400 g->anim_event = ANIM_EVENT_UNDEFINED;
1401 g->init_event_action = -1;
1402 g->anim_event_action = -1;
1403 g->draw_masked = FALSE;
1405 g->fade_mode = FADE_MODE_DEFAULT;
1409 g->auto_delay_unit = AUTO_DELAY_UNIT_DEFAULT;
1410 g->align = ALIGN_CENTER; // default for title screens
1411 g->valign = VALIGN_MIDDLE; // default for title screens
1412 g->sort_priority = 0; // default for title screens
1414 g->style = STYLE_DEFAULT;
1416 g->bitmaps = src_bitmaps;
1417 g->bitmap = src_bitmap;
1419 // optional zoom factor for scaling up the image to a larger size
1420 if (parameter[GFX_ARG_SCALE_UP_FACTOR] != ARG_UNDEFINED_VALUE)
1421 g->scale_up_factor = parameter[GFX_ARG_SCALE_UP_FACTOR];
1422 if (g->scale_up_factor < 1)
1423 g->scale_up_factor = 1; // no scaling
1425 // optional tile size for using non-standard image size
1426 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1428 g->tile_size = parameter[GFX_ARG_TILE_SIZE];
1431 // CHECK: should tile sizes less than standard tile size be allowed?
1432 if (g->tile_size < TILESIZE)
1433 g->tile_size = TILESIZE; // standard tile size
1436 // when setting tile size, also set width and height accordingly
1437 g->width = g->tile_size;
1438 g->height = g->tile_size;
1441 if (g->use_image_size)
1443 // set new default bitmap size (with scaling, but without small images)
1444 g->width = get_scaled_graphic_width(src_bitmap, graphic);
1445 g->height = get_scaled_graphic_height(src_bitmap, graphic);
1448 // optional width and height of each animation frame
1449 if (parameter[GFX_ARG_WIDTH] != ARG_UNDEFINED_VALUE)
1450 g->width = parameter[GFX_ARG_WIDTH];
1451 if (parameter[GFX_ARG_HEIGHT] != ARG_UNDEFINED_VALUE)
1452 g->height = parameter[GFX_ARG_HEIGHT];
1454 // optional x and y tile position of animation frame sequence
1455 if (parameter[GFX_ARG_XPOS] != ARG_UNDEFINED_VALUE)
1456 g->src_x = parameter[GFX_ARG_XPOS] * g->width;
1457 if (parameter[GFX_ARG_YPOS] != ARG_UNDEFINED_VALUE)
1458 g->src_y = parameter[GFX_ARG_YPOS] * g->height;
1460 // optional x and y pixel position of animation frame sequence
1461 if (parameter[GFX_ARG_X] != ARG_UNDEFINED_VALUE)
1462 g->src_x = parameter[GFX_ARG_X];
1463 if (parameter[GFX_ARG_Y] != ARG_UNDEFINED_VALUE)
1464 g->src_y = parameter[GFX_ARG_Y];
1471 Warn("invalid value %d for '%s.width' (fallback done to %d)",
1472 g->width, getTokenFromImageID(graphic), TILEX);
1475 g->width = TILEX; // will be checked to be inside bitmap later
1481 Warn("invalid value %d for '%s.height' (fallback done to %d)",
1482 g->height, getTokenFromImageID(graphic), TILEY);
1485 g->height = TILEY; // will be checked to be inside bitmap later
1491 // get final bitmap size (with scaling, but without small images)
1492 int src_image_width = get_scaled_graphic_width(src_bitmap, graphic);
1493 int src_image_height = get_scaled_graphic_height(src_bitmap, graphic);
1495 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1497 anim_frames_per_row = MAX(1, src_image_width / g->tile_size);
1498 anim_frames_per_col = MAX(1, src_image_height / g->tile_size);
1502 anim_frames_per_row = MAX(1, src_image_width / g->width);
1503 anim_frames_per_col = MAX(1, src_image_height / g->height);
1506 g->src_image_width = src_image_width;
1507 g->src_image_height = src_image_height;
1510 // correct x or y offset dependent of vertical or horizontal frame order
1511 if (parameter[GFX_ARG_VERTICAL]) // frames are ordered vertically
1513 g->offset_y = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1514 parameter[GFX_ARG_OFFSET] : g->height);
1515 anim_frames_per_line = anim_frames_per_col;
1517 else // frames are ordered horizontally
1519 g->offset_x = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1520 parameter[GFX_ARG_OFFSET] : g->width);
1521 anim_frames_per_line = anim_frames_per_row;
1524 // optionally, the x and y offset of frames can be specified directly
1525 if (parameter[GFX_ARG_XOFFSET] != ARG_UNDEFINED_VALUE)
1526 g->offset_x = parameter[GFX_ARG_XOFFSET];
1527 if (parameter[GFX_ARG_YOFFSET] != ARG_UNDEFINED_VALUE)
1528 g->offset_y = parameter[GFX_ARG_YOFFSET];
1530 // optionally, moving animations may have separate start and end graphics
1531 g->double_movement = parameter[GFX_ARG_2ND_MOVEMENT_TILE];
1533 if (parameter[GFX_ARG_2ND_VERTICAL] == ARG_UNDEFINED_VALUE)
1534 parameter[GFX_ARG_2ND_VERTICAL] = !parameter[GFX_ARG_VERTICAL];
1536 // correct x or y offset2 dependent of vertical or horizontal frame order
1537 if (parameter[GFX_ARG_2ND_VERTICAL]) // frames are ordered vertically
1538 g->offset2_y = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1539 parameter[GFX_ARG_2ND_OFFSET] : g->height);
1540 else // frames are ordered horizontally
1541 g->offset2_x = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1542 parameter[GFX_ARG_2ND_OFFSET] : g->width);
1544 // optionally, the x and y offset of 2nd graphic can be specified directly
1545 if (parameter[GFX_ARG_2ND_XOFFSET] != ARG_UNDEFINED_VALUE)
1546 g->offset2_x = parameter[GFX_ARG_2ND_XOFFSET];
1547 if (parameter[GFX_ARG_2ND_YOFFSET] != ARG_UNDEFINED_VALUE)
1548 g->offset2_y = parameter[GFX_ARG_2ND_YOFFSET];
1550 // optionally, the second movement tile can be specified as start tile
1551 if (parameter[GFX_ARG_2ND_SWAP_TILES] != ARG_UNDEFINED_VALUE)
1552 g->swap_double_tiles= parameter[GFX_ARG_2ND_SWAP_TILES];
1554 // automatically determine correct number of frames, if not defined
1555 if (parameter[GFX_ARG_FRAMES] != ARG_UNDEFINED_VALUE)
1556 g->anim_frames = parameter[GFX_ARG_FRAMES];
1557 else if (parameter[GFX_ARG_XPOS] == 0 && !parameter[GFX_ARG_VERTICAL])
1558 g->anim_frames = anim_frames_per_row;
1559 else if (parameter[GFX_ARG_YPOS] == 0 && parameter[GFX_ARG_VERTICAL])
1560 g->anim_frames = anim_frames_per_col;
1564 if (g->anim_frames < 1) // frames must be at least 1
1567 g->anim_frames_per_line =
1568 (parameter[GFX_ARG_FRAMES_PER_LINE] != ARG_UNDEFINED_VALUE ?
1569 parameter[GFX_ARG_FRAMES_PER_LINE] : anim_frames_per_line);
1571 g->anim_delay = parameter[GFX_ARG_DELAY];
1572 if (g->anim_delay < 1) // delay must be at least 1
1575 g->anim_mode = parameter[GFX_ARG_ANIM_MODE];
1577 // automatically determine correct start frame, if not defined
1578 if (parameter[GFX_ARG_START_FRAME] == ARG_UNDEFINED_VALUE)
1579 g->anim_start_frame = 0;
1580 else if (g->anim_mode & ANIM_REVERSE)
1581 g->anim_start_frame = g->anim_frames - parameter[GFX_ARG_START_FRAME] - 1;
1583 g->anim_start_frame = parameter[GFX_ARG_START_FRAME];
1585 // animation synchronized with global frame counter, not move position
1586 g->anim_global_sync = parameter[GFX_ARG_GLOBAL_SYNC];
1588 // animation synchronized with global anim frame counter, not move position
1589 g->anim_global_anim_sync = parameter[GFX_ARG_GLOBAL_ANIM_SYNC];
1591 // optional element for cloning crumble graphics
1592 if (parameter[GFX_ARG_CRUMBLED_LIKE] != ARG_UNDEFINED_VALUE)
1593 g->crumbled_like = parameter[GFX_ARG_CRUMBLED_LIKE];
1595 // optional element for cloning digging graphics
1596 if (parameter[GFX_ARG_DIGGABLE_LIKE] != ARG_UNDEFINED_VALUE)
1597 g->diggable_like = parameter[GFX_ARG_DIGGABLE_LIKE];
1599 // optional border size for "crumbling" diggable graphics
1600 if (parameter[GFX_ARG_BORDER_SIZE] != ARG_UNDEFINED_VALUE)
1601 g->border_size = parameter[GFX_ARG_BORDER_SIZE];
1603 // used for global animations and player "boring" and "sleeping" actions
1604 if (parameter[GFX_ARG_INIT_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1605 g->init_delay_fixed = parameter[GFX_ARG_INIT_DELAY_FIXED];
1606 if (parameter[GFX_ARG_INIT_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1607 g->init_delay_random = parameter[GFX_ARG_INIT_DELAY_RANDOM];
1608 if (parameter[GFX_ARG_ANIM_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1609 g->anim_delay_fixed = parameter[GFX_ARG_ANIM_DELAY_FIXED];
1610 if (parameter[GFX_ARG_ANIM_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1611 g->anim_delay_random = parameter[GFX_ARG_ANIM_DELAY_RANDOM];
1612 if (parameter[GFX_ARG_POST_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1613 g->post_delay_fixed = parameter[GFX_ARG_POST_DELAY_FIXED];
1614 if (parameter[GFX_ARG_POST_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1615 g->post_delay_random = parameter[GFX_ARG_POST_DELAY_RANDOM];
1617 // used for global animations
1618 if (parameter[GFX_ARG_INIT_EVENT] != ARG_UNDEFINED_VALUE)
1619 g->init_event = parameter[GFX_ARG_INIT_EVENT];
1620 if (parameter[GFX_ARG_ANIM_EVENT] != ARG_UNDEFINED_VALUE)
1621 g->anim_event = parameter[GFX_ARG_ANIM_EVENT];
1622 if (parameter[GFX_ARG_INIT_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1623 g->init_event_action = parameter[GFX_ARG_INIT_EVENT_ACTION];
1624 if (parameter[GFX_ARG_ANIM_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1625 g->anim_event_action = parameter[GFX_ARG_ANIM_EVENT_ACTION];
1626 if (parameter[GFX_ARG_INIT_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1627 g->init_delay_action = parameter[GFX_ARG_INIT_DELAY_ACTION];
1628 if (parameter[GFX_ARG_ANIM_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1629 g->anim_delay_action = parameter[GFX_ARG_ANIM_DELAY_ACTION];
1630 if (parameter[GFX_ARG_POST_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1631 g->post_delay_action = parameter[GFX_ARG_POST_DELAY_ACTION];
1633 // used for toon animations and global animations
1634 g->step_offset = parameter[GFX_ARG_STEP_OFFSET];
1635 g->step_xoffset = parameter[GFX_ARG_STEP_XOFFSET];
1636 g->step_yoffset = parameter[GFX_ARG_STEP_YOFFSET];
1637 g->step_delay = parameter[GFX_ARG_STEP_DELAY];
1638 g->direction = parameter[GFX_ARG_DIRECTION];
1639 g->position = parameter[GFX_ARG_POSITION];
1640 g->x = parameter[GFX_ARG_X]; // (may be uninitialized,
1641 g->y = parameter[GFX_ARG_Y]; // unlike src_x and src_y)
1643 if (g->step_delay < 1) // delay must be at least 1
1646 // this is only used for drawing font characters
1647 g->draw_xoffset = parameter[GFX_ARG_DRAW_XOFFSET];
1648 g->draw_yoffset = parameter[GFX_ARG_DRAW_YOFFSET];
1650 // use a different default value for global animations and toons
1651 if ((graphic >= IMG_GFX_GLOBAL_ANIM_1 && graphic <= IMG_GFX_GLOBAL_ANIM_8) ||
1652 (graphic >= IMG_TOON_1 && graphic <= IMG_TOON_20))
1653 g->draw_masked = TRUE;
1655 // this is used for drawing envelopes, global animations and toons
1656 if (parameter[GFX_ARG_DRAW_MASKED] != ARG_UNDEFINED_VALUE)
1657 g->draw_masked = parameter[GFX_ARG_DRAW_MASKED];
1659 // used for toon animations and global animations
1660 if (parameter[GFX_ARG_DRAW_ORDER] != ARG_UNDEFINED_VALUE)
1661 g->draw_order = parameter[GFX_ARG_DRAW_ORDER];
1663 // optional graphic for cloning all graphics settings
1664 if (parameter[GFX_ARG_CLONE_FROM] != ARG_UNDEFINED_VALUE)
1665 g->clone_from = parameter[GFX_ARG_CLONE_FROM];
1667 // optional settings for drawing title screens and title messages
1668 if (parameter[GFX_ARG_FADE_MODE] != ARG_UNDEFINED_VALUE)
1669 g->fade_mode = parameter[GFX_ARG_FADE_MODE];
1670 if (parameter[GFX_ARG_FADE_DELAY] != ARG_UNDEFINED_VALUE)
1671 g->fade_delay = parameter[GFX_ARG_FADE_DELAY];
1672 if (parameter[GFX_ARG_POST_DELAY] != ARG_UNDEFINED_VALUE)
1673 g->post_delay = parameter[GFX_ARG_POST_DELAY];
1674 if (parameter[GFX_ARG_AUTO_DELAY] != ARG_UNDEFINED_VALUE)
1675 g->auto_delay = parameter[GFX_ARG_AUTO_DELAY];
1676 if (parameter[GFX_ARG_AUTO_DELAY_UNIT] != ARG_UNDEFINED_VALUE)
1677 g->auto_delay_unit = parameter[GFX_ARG_AUTO_DELAY_UNIT];
1678 if (parameter[GFX_ARG_ALIGN] != ARG_UNDEFINED_VALUE)
1679 g->align = parameter[GFX_ARG_ALIGN];
1680 if (parameter[GFX_ARG_VALIGN] != ARG_UNDEFINED_VALUE)
1681 g->valign = parameter[GFX_ARG_VALIGN];
1682 if (parameter[GFX_ARG_SORT_PRIORITY] != ARG_UNDEFINED_VALUE)
1683 g->sort_priority = parameter[GFX_ARG_SORT_PRIORITY];
1685 if (parameter[GFX_ARG_CLASS] != ARG_UNDEFINED_VALUE)
1686 g->class = parameter[GFX_ARG_CLASS];
1687 if (parameter[GFX_ARG_STYLE] != ARG_UNDEFINED_VALUE)
1688 g->style = parameter[GFX_ARG_STYLE];
1690 // this is only used for drawing menu buttons and text
1691 g->active_xoffset = parameter[GFX_ARG_ACTIVE_XOFFSET];
1692 g->active_yoffset = parameter[GFX_ARG_ACTIVE_YOFFSET];
1693 g->pressed_xoffset = parameter[GFX_ARG_PRESSED_XOFFSET];
1694 g->pressed_yoffset = parameter[GFX_ARG_PRESSED_YOFFSET];
1696 // this is only used for drawing stacked global animations
1697 g->stacked_xfactor = parameter[GFX_ARG_STACKED_XFACTOR];
1698 g->stacked_yfactor = parameter[GFX_ARG_STACKED_YFACTOR];
1699 g->stacked_xoffset = parameter[GFX_ARG_STACKED_XOFFSET];
1700 g->stacked_yoffset = parameter[GFX_ARG_STACKED_YOFFSET];
1703 static void set_graphic_parameters(int graphic)
1705 struct FileInfo *image = getImageListEntryFromImageID(graphic);
1706 char **parameter_raw = image->parameter;
1707 Bitmap **src_bitmaps = getBitmapsFromImageID(graphic);
1708 int parameter[NUM_GFX_ARGS];
1711 // if fallback to default artwork is done, also use the default parameters
1712 if (image->fallback_to_default)
1713 parameter_raw = image->default_parameter;
1715 // get integer values from string parameters
1716 for (i = 0; i < NUM_GFX_ARGS; i++)
1717 parameter[i] = get_graphic_parameter_value(parameter_raw[i],
1718 image_config_suffix[i].token,
1719 image_config_suffix[i].type);
1721 set_graphic_parameters_ext(graphic, parameter, src_bitmaps);
1723 UPDATE_BUSY_STATE();
1726 static void set_cloned_graphic_parameters(int graphic)
1728 int fallback_graphic = IMG_CHAR_EXCLAM;
1729 int max_num_images = getImageListSize();
1730 int clone_graphic = graphic_info[graphic].clone_from;
1731 int num_references_followed = 1;
1733 while (graphic_info[clone_graphic].clone_from != -1 &&
1734 num_references_followed < max_num_images)
1736 clone_graphic = graphic_info[clone_graphic].clone_from;
1738 num_references_followed++;
1741 if (num_references_followed >= max_num_images)
1744 Warn("error found in config file:");
1745 Warn("- config file: '%s'", getImageConfigFilename());
1746 Warn("- config token: '%s'", getTokenFromImageID(graphic));
1747 Warn("error: loop discovered when resolving cloned graphics");
1748 Warn("custom graphic rejected for this element/action");
1750 if (graphic == fallback_graphic)
1751 Fail("no fallback graphic available");
1753 Warn("fallback done to 'char_exclam' for this graphic");
1756 graphic_info[graphic] = graphic_info[fallback_graphic];
1760 graphic_info[graphic] = graphic_info[clone_graphic];
1761 graphic_info[graphic].clone_from = clone_graphic;
1765 static void InitGraphicInfo(void)
1767 int fallback_graphic = IMG_CHAR_EXCLAM;
1768 int num_images = getImageListSize();
1771 // use image size as default values for width and height for these images
1772 static int full_size_graphics[] =
1775 IMG_GLOBAL_BORDER_MAIN,
1776 IMG_GLOBAL_BORDER_SCORES,
1777 IMG_GLOBAL_BORDER_EDITOR,
1778 IMG_GLOBAL_BORDER_PLAYING,
1781 IMG_BACKGROUND_ENVELOPE_1,
1782 IMG_BACKGROUND_ENVELOPE_2,
1783 IMG_BACKGROUND_ENVELOPE_3,
1784 IMG_BACKGROUND_ENVELOPE_4,
1785 IMG_BACKGROUND_REQUEST,
1788 IMG_BACKGROUND_LOADING_INITIAL,
1789 IMG_BACKGROUND_LOADING,
1790 IMG_BACKGROUND_TITLE_INITIAL,
1791 IMG_BACKGROUND_TITLE,
1792 IMG_BACKGROUND_MAIN,
1793 IMG_BACKGROUND_NAMES,
1794 IMG_BACKGROUND_LEVELS,
1795 IMG_BACKGROUND_LEVELNR,
1796 IMG_BACKGROUND_SCORES,
1797 IMG_BACKGROUND_SCOREINFO,
1798 IMG_BACKGROUND_EDITOR,
1799 IMG_BACKGROUND_INFO,
1800 IMG_BACKGROUND_INFO_ELEMENTS,
1801 IMG_BACKGROUND_INFO_MUSIC,
1802 IMG_BACKGROUND_INFO_CREDITS,
1803 IMG_BACKGROUND_INFO_PROGRAM,
1804 IMG_BACKGROUND_INFO_VERSION,
1805 IMG_BACKGROUND_INFO_LEVELSET,
1806 IMG_BACKGROUND_SETUP,
1807 IMG_BACKGROUND_PLAYING,
1808 IMG_BACKGROUND_DOOR,
1809 IMG_BACKGROUND_TAPE,
1810 IMG_BACKGROUND_PANEL,
1811 IMG_BACKGROUND_PALETTE,
1812 IMG_BACKGROUND_TOOLBOX,
1814 IMG_TITLESCREEN_INITIAL_1,
1815 IMG_TITLESCREEN_INITIAL_2,
1816 IMG_TITLESCREEN_INITIAL_3,
1817 IMG_TITLESCREEN_INITIAL_4,
1818 IMG_TITLESCREEN_INITIAL_5,
1825 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_1,
1826 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_2,
1827 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_3,
1828 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_4,
1829 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_5,
1830 IMG_BACKGROUND_TITLEMESSAGE_1,
1831 IMG_BACKGROUND_TITLEMESSAGE_2,
1832 IMG_BACKGROUND_TITLEMESSAGE_3,
1833 IMG_BACKGROUND_TITLEMESSAGE_4,
1834 IMG_BACKGROUND_TITLEMESSAGE_5,
1839 FreeGlobalAnimEventInfo();
1841 checked_free(graphic_info);
1843 graphic_info = checked_calloc(num_images * sizeof(struct GraphicInfo));
1845 // initialize "use_image_size" flag with default value
1846 for (i = 0; i < num_images; i++)
1847 graphic_info[i].use_image_size = FALSE;
1849 // initialize "use_image_size" flag from static configuration above
1850 for (i = 0; full_size_graphics[i] != -1; i++)
1851 graphic_info[full_size_graphics[i]].use_image_size = TRUE;
1853 // first set all graphic paramaters ...
1854 for (i = 0; i < num_images; i++)
1855 set_graphic_parameters(i);
1857 // ... then copy these parameters for cloned graphics
1858 for (i = 0; i < num_images; i++)
1859 if (graphic_info[i].clone_from != -1)
1860 set_cloned_graphic_parameters(i);
1862 for (i = 0; i < num_images; i++)
1864 Bitmap *src_bitmap = graphic_info[i].bitmap;
1868 int src_bitmap_width, src_bitmap_height;
1870 // now check if no animation frames are outside of the loaded image
1872 if (graphic_info[i].bitmap == NULL)
1873 continue; // skip check for optional images that are undefined
1875 // get image size (this can differ from the standard element tile size!)
1876 width = graphic_info[i].width;
1877 height = graphic_info[i].height;
1879 // get final bitmap size (with scaling, but without small images)
1880 src_bitmap_width = graphic_info[i].src_image_width;
1881 src_bitmap_height = graphic_info[i].src_image_height;
1883 // check if first animation frame is inside specified bitmap
1885 // do not use getGraphicSourceXY() here to get position of first frame;
1886 // this avoids calculating wrong start position for out-of-bounds frame
1887 src_x = graphic_info[i].src_x;
1888 src_y = graphic_info[i].src_y;
1890 if (program.headless)
1893 if (src_x < 0 || src_y < 0 ||
1894 src_x + width > src_bitmap_width ||
1895 src_y + height > src_bitmap_height)
1898 Warn("error found in config file:");
1899 Warn("- config file: '%s'", getImageConfigFilename());
1900 Warn("- config token: '%s'", getTokenFromImageID(i));
1901 Warn("- image file: '%s'", src_bitmap->source_filename);
1902 Warn("- frame size: %d, %d", width, height);
1903 Warn("error: first animation frame out of bounds (%d, %d) [%d, %d]",
1904 src_x, src_y, src_bitmap_width, src_bitmap_height);
1905 Warn("custom graphic rejected for this element/action");
1907 if (i == fallback_graphic)
1908 Fail("no fallback graphic available");
1910 Warn("fallback done to 'char_exclam' for this graphic");
1913 graphic_info[i] = graphic_info[fallback_graphic];
1915 // if first frame out of bounds, do not check last frame anymore
1919 // check if last animation frame is inside specified bitmap
1921 last_frame = graphic_info[i].anim_frames - 1;
1922 getGraphicSourceXY(i, last_frame, &src_x, &src_y, FALSE);
1924 if (src_x < 0 || src_y < 0 ||
1925 src_x + width > src_bitmap_width ||
1926 src_y + height > src_bitmap_height)
1929 Warn("error found in config file:");
1930 Warn("- config file: '%s'", getImageConfigFilename());
1931 Warn("- config token: '%s'", getTokenFromImageID(i));
1932 Warn("- image file: '%s'", src_bitmap->source_filename);
1933 Warn("- frame size: %d, %d", width, height);
1934 Warn("error: last animation frame (%d) out of bounds (%d, %d) [%d, %d]",
1935 last_frame, src_x, src_y, src_bitmap_width, src_bitmap_height);
1936 Warn("custom graphic rejected for this element/action");
1938 if (i == fallback_graphic)
1939 Fail("no fallback graphic available");
1941 Warn("fallback done to 'char_exclam' for this graphic");
1944 graphic_info[i] = graphic_info[fallback_graphic];
1949 static void InitGraphicCompatibilityInfo(void)
1951 struct FileInfo *fi_global_door =
1952 getImageListEntryFromImageID(IMG_GLOBAL_DOOR);
1953 int num_images = getImageListSize();
1956 /* the following compatibility handling is needed for the following case:
1957 versions up to 3.3.0.0 used one large bitmap "global.door" for various
1958 graphics mainly used for door and panel graphics, like editor, tape and
1959 in-game buttons with hard-coded bitmap positions and button sizes; as
1960 these graphics now have individual definitions, redefining "global.door"
1961 to change all these graphics at once like before does not work anymore
1962 (because all those individual definitions still have their default values);
1963 to solve this, remap all those individual definitions that are not
1964 redefined to the new bitmap of "global.door" if it was redefined */
1966 // special compatibility handling if image "global.door" was redefined
1967 if (fi_global_door->redefined)
1969 for (i = 0; i < num_images; i++)
1971 struct FileInfo *fi = getImageListEntryFromImageID(i);
1973 // process only those images that still use the default settings
1976 // process all images which default to same image as "global.door"
1977 if (strEqual(fi->default_filename, fi_global_door->default_filename))
1979 // skip all images that are cloned from images that default to same
1980 // image as "global.door", but that are redefined to something else
1981 if (graphic_info[i].clone_from != -1)
1983 int cloned_graphic = graphic_info[i].clone_from;
1985 if (getImageListEntryFromImageID(cloned_graphic)->redefined)
1990 Debug("init:InitGraphicCompatibilityInfo",
1991 "special treatment needed for token '%s'", fi->token);
1994 graphic_info[i].bitmaps = graphic_info[IMG_GLOBAL_DOOR].bitmaps;
1995 graphic_info[i].bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2001 // special compatibility handling for "Snake Bite" graphics set
2002 if (strPrefix(leveldir_current->identifier, "snake_bite"))
2004 Bitmap *bitmap = graphic_info[IMG_BACKGROUND_SCORES].bitmap;
2006 BlitBitmap(bitmap, bitmap, 18, 66, 32, 480, 50, 66);
2007 BlitBitmap(bitmap, bitmap, 466, 66, 32, 480, 434, 66);
2009 ClearRectangle(bitmap, 2, 66, 32, 480);
2010 ClearRectangle(bitmap, 514, 66, 32, 480);
2013 // special compatibility handling for "Jue" graphics sets (2007 and 2019)
2014 boolean supports_score_info = (menu.draw_xoffset[GAME_MODE_SCOREINFO] != 0);
2015 if (strPrefix(artwork.gfx_current_identifier, "jue") && !supports_score_info)
2033 int mode_old = GAME_MODE_SCORES;
2034 int mode_new = GAME_MODE_SCOREINFO;
2037 // adjust title screens on score info page
2038 for (i = 0; font_title[i] != -1; i++)
2040 struct FontInfo *fi = &font_info[font_title[i]];
2042 fi->special_graphic[mode_new] = fi->special_graphic[mode_old];
2043 fi->special_bitmap_id[mode_new] = fi->special_bitmap_id[mode_old];
2046 // adjust vertical text and button positions on scores page
2047 for (i = 0; font_text[i] != -1; i++)
2049 for (j = 0; j < 2; j++)
2051 boolean jue0 = strEqual(artwork.gfx_current_identifier, "jue0");
2052 int font_nr = (j == 0 ? font_text[i] : FONT_ACTIVE(font_text[i]));
2053 int font_bitmap_id = font_info[font_nr].special_bitmap_id[mode_old];
2054 int font_yoffset = (jue0 ? 10 : 5);
2056 gfx.font_bitmap_info[font_bitmap_id].draw_yoffset = font_yoffset;
2060 // adjust page offsets on score info page
2061 menu.draw_xoffset[mode_new] = menu.draw_xoffset[mode_old];
2062 menu.draw_yoffset[mode_new] = menu.draw_yoffset[mode_old];
2065 InitGraphicCompatibilityInfo_Doors();
2068 static void InitElementSoundInfo(void)
2070 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
2071 int num_property_mappings = getSoundListPropertyMappingSize();
2074 // set values to -1 to identify later as "uninitialized" values
2075 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2076 for (act = 0; act < NUM_ACTIONS; act++)
2077 element_info[i].sound[act] = -1;
2079 // initialize element/sound mapping from static configuration
2080 for (i = 0; element_to_sound[i].element > -1; i++)
2082 int element = element_to_sound[i].element;
2083 int action = element_to_sound[i].action;
2084 int sound = element_to_sound[i].sound;
2085 boolean is_class = element_to_sound[i].is_class;
2088 action = ACTION_DEFAULT;
2091 element_info[element].sound[action] = sound;
2093 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2094 if (strEqual(element_info[j].class_name,
2095 element_info[element].class_name))
2096 element_info[j].sound[action] = sound;
2099 // initialize element class/sound mapping from dynamic configuration
2100 for (i = 0; i < num_property_mappings; i++)
2102 int element_class = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
2103 int action = property_mapping[i].ext1_index;
2104 int sound = property_mapping[i].artwork_index;
2106 if (element_class < 0 || element_class >= MAX_NUM_ELEMENTS)
2110 action = ACTION_DEFAULT;
2112 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2113 if (strEqual(element_info[j].class_name,
2114 element_info[element_class].class_name))
2115 element_info[j].sound[action] = sound;
2118 // initialize element/sound mapping from dynamic configuration
2119 for (i = 0; i < num_property_mappings; i++)
2121 int element = property_mapping[i].base_index;
2122 int action = property_mapping[i].ext1_index;
2123 int sound = property_mapping[i].artwork_index;
2125 if (element >= MAX_NUM_ELEMENTS)
2129 action = ACTION_DEFAULT;
2131 element_info[element].sound[action] = sound;
2134 // now set all '-1' values to element specific default values
2135 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2137 for (act = 0; act < NUM_ACTIONS; act++)
2139 // generic default action sound (defined by "[default]" directive)
2140 int default_action_sound = element_info[EL_DEFAULT].sound[act];
2142 // look for special default action sound (classic game specific)
2143 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].sound[act] != -1)
2144 default_action_sound = element_info[EL_BD_DEFAULT].sound[act];
2145 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].sound[act] != -1)
2146 default_action_sound = element_info[EL_SP_DEFAULT].sound[act];
2147 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].sound[act] != -1)
2148 default_action_sound = element_info[EL_SB_DEFAULT].sound[act];
2149 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].sound[act] != -1)
2150 default_action_sound = element_info[EL_MM_DEFAULT].sound[act];
2152 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
2153 // !!! make this better !!!
2154 if (i == EL_EMPTY_SPACE)
2155 default_action_sound = element_info[EL_DEFAULT].sound[act];
2157 // no sound for this specific action -- use default action sound
2158 if (element_info[i].sound[act] == -1)
2159 element_info[i].sound[act] = default_action_sound;
2163 // copy sound settings to some elements that are only stored in level file
2164 // in native R'n'D levels, but are used by game engine in native EM levels
2165 for (i = 0; copy_properties[i][0] != -1; i++)
2166 for (j = 1; j <= 4; j++)
2167 for (act = 0; act < NUM_ACTIONS; act++)
2168 element_info[copy_properties[i][j]].sound[act] =
2169 element_info[copy_properties[i][0]].sound[act];
2172 static void InitGameModeSoundInfo(void)
2176 // set values to -1 to identify later as "uninitialized" values
2177 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2180 // initialize gamemode/sound mapping from static configuration
2181 for (i = 0; gamemode_to_sound[i].sound > -1; i++)
2183 int gamemode = gamemode_to_sound[i].gamemode;
2184 int sound = gamemode_to_sound[i].sound;
2187 gamemode = GAME_MODE_DEFAULT;
2189 menu.sound[gamemode] = sound;
2192 // now set all '-1' values to levelset specific default values
2193 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2194 if (menu.sound[i] == -1)
2195 menu.sound[i] = menu.sound[GAME_MODE_DEFAULT];
2198 static void set_sound_parameters(int sound, char **parameter_raw)
2200 int parameter[NUM_SND_ARGS];
2203 // get integer values from string parameters
2204 for (i = 0; i < NUM_SND_ARGS; i++)
2206 get_parameter_value(parameter_raw[i],
2207 sound_config_suffix[i].token,
2208 sound_config_suffix[i].type);
2210 // explicit loop mode setting in configuration overrides default value
2211 if (parameter[SND_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2212 sound_info[sound].loop = parameter[SND_ARG_MODE_LOOP];
2214 // sound volume to change the original volume when loading the sound file
2215 sound_info[sound].volume = parameter[SND_ARG_VOLUME];
2217 // sound priority to give certain sounds a higher or lower priority
2218 sound_info[sound].priority = parameter[SND_ARG_PRIORITY];
2221 static void InitSoundInfo(void)
2223 int *sound_effect_properties;
2224 int num_sounds = getSoundListSize();
2227 checked_free(sound_info);
2229 sound_effect_properties = checked_calloc(num_sounds * sizeof(int));
2230 sound_info = checked_calloc(num_sounds * sizeof(struct SoundInfo));
2232 // initialize sound effect for all elements to "no sound"
2233 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2234 for (j = 0; j < NUM_ACTIONS; j++)
2235 element_info[i].sound[j] = SND_UNDEFINED;
2237 for (i = 0; i < num_sounds; i++)
2239 struct FileInfo *sound = getSoundListEntry(i);
2240 int len_effect_text = strlen(sound->token);
2242 sound_effect_properties[i] = ACTION_OTHER;
2243 sound_info[i].loop = FALSE; // default: play sound only once
2245 // determine all loop sounds and identify certain sound classes
2247 for (j = 0; element_action_info[j].suffix; j++)
2249 int len_action_text = strlen(element_action_info[j].suffix);
2251 if (len_action_text < len_effect_text &&
2252 strEqual(&sound->token[len_effect_text - len_action_text],
2253 element_action_info[j].suffix))
2255 sound_effect_properties[i] = element_action_info[j].value;
2256 sound_info[i].loop = element_action_info[j].is_loop_sound;
2262 // associate elements and some selected sound actions
2264 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2266 if (element_info[j].class_name)
2268 int len_class_text = strlen(element_info[j].class_name);
2270 if (len_class_text + 1 < len_effect_text &&
2271 strncmp(sound->token,
2272 element_info[j].class_name, len_class_text) == 0 &&
2273 sound->token[len_class_text] == '.')
2275 int sound_action_value = sound_effect_properties[i];
2277 element_info[j].sound[sound_action_value] = i;
2282 set_sound_parameters(i, sound->parameter);
2285 Debug("init:InitSoundInfo", "loop mode: %d ['%s']",
2286 sound_info[i].loop, sound->token);
2290 free(sound_effect_properties);
2293 static void InitGameModeMusicInfo(void)
2295 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
2296 int num_property_mappings = getMusicListPropertyMappingSize();
2297 int default_levelset_music = -1;
2300 // set values to -1 to identify later as "uninitialized" values
2301 for (i = 0; i < MAX_LEVELS; i++)
2302 levelset.music[i] = -1;
2303 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2306 // initialize gamemode/music mapping from static configuration
2307 for (i = 0; gamemode_to_music[i].music > -1; i++)
2309 int gamemode = gamemode_to_music[i].gamemode;
2310 int music = gamemode_to_music[i].music;
2313 gamemode = GAME_MODE_DEFAULT;
2315 menu.music[gamemode] = music;
2318 // initialize gamemode/music mapping from dynamic configuration
2319 for (i = 0; i < num_property_mappings; i++)
2321 int prefix = property_mapping[i].base_index;
2322 int gamemode = property_mapping[i].ext2_index;
2323 int level = property_mapping[i].ext3_index;
2324 int music = property_mapping[i].artwork_index;
2326 if (prefix < 0 || prefix >= NUM_MUSIC_PREFIXES)
2330 gamemode = GAME_MODE_DEFAULT;
2332 // level specific music only allowed for in-game music
2333 if (level != -1 && gamemode == GAME_MODE_DEFAULT)
2334 gamemode = GAME_MODE_PLAYING;
2339 default_levelset_music = music;
2342 if (gamemode == GAME_MODE_PLAYING || gamemode == GAME_MODE_DEFAULT)
2343 levelset.music[level] = music;
2344 if (gamemode != GAME_MODE_PLAYING)
2345 menu.music[gamemode] = music;
2348 // now set all '-1' values to menu specific default values
2349 // (undefined values of "levelset.music[]" might stay at "-1" to
2350 // allow dynamic selection of music files from music directory!)
2351 for (i = 0; i < MAX_LEVELS; i++)
2352 if (levelset.music[i] == -1)
2353 levelset.music[i] = default_levelset_music;
2354 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2355 if (menu.music[i] == -1)
2356 menu.music[i] = menu.music[GAME_MODE_DEFAULT];
2359 static void set_music_parameters(int music, char **parameter_raw)
2361 int parameter[NUM_MUS_ARGS];
2364 // get integer values from string parameters
2365 for (i = 0; i < NUM_MUS_ARGS; i++)
2367 get_parameter_value(parameter_raw[i],
2368 music_config_suffix[i].token,
2369 music_config_suffix[i].type);
2371 // explicit loop mode setting in configuration overrides default value
2372 if (parameter[MUS_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2373 music_info[music].loop = parameter[MUS_ARG_MODE_LOOP];
2376 static void InitMusicInfo(void)
2378 int num_music = getMusicListSize();
2381 checked_free(music_info);
2383 music_info = checked_calloc(num_music * sizeof(struct MusicInfo));
2385 for (i = 0; i < num_music; i++)
2387 struct FileInfo *music = getMusicListEntry(i);
2388 int len_music_text = strlen(music->token);
2390 music_info[i].loop = TRUE; // default: play music in loop mode
2392 // determine all loop music
2394 for (j = 0; music_prefix_info[j].prefix; j++)
2396 int len_prefix_text = strlen(music_prefix_info[j].prefix);
2398 if (len_prefix_text < len_music_text &&
2399 strncmp(music->token,
2400 music_prefix_info[j].prefix, len_prefix_text) == 0)
2402 music_info[i].loop = music_prefix_info[j].is_loop_music;
2408 set_music_parameters(i, music->parameter);
2413 static void InitGameInfoFromArtworkInfo(void)
2415 // special case: store initial value of custom artwork setting
2416 game.use_masked_elements_initial = game.use_masked_elements;
2419 static void ReinitializeGraphics(void)
2421 print_timestamp_init("ReinitializeGraphics");
2423 InitGfxTileSizeInfo(game.tile_size, TILESIZE);
2425 InitGraphicInfo(); // graphic properties mapping
2426 print_timestamp_time("InitGraphicInfo");
2427 InitElementGraphicInfo(); // element game graphic mapping
2428 print_timestamp_time("InitElementGraphicInfo");
2429 InitElementSpecialGraphicInfo(); // element special graphic mapping
2430 print_timestamp_time("InitElementSpecialGraphicInfo");
2432 InitElementSmallImages(); // scale elements to all needed sizes
2433 print_timestamp_time("InitElementSmallImages");
2434 InitScaledImages(); // scale all other images, if needed
2435 print_timestamp_time("InitScaledImages");
2436 InitBitmapPointers(); // set standard size bitmap pointers
2437 print_timestamp_time("InitBitmapPointers");
2438 InitFontGraphicInfo(); // initialize text drawing functions
2439 print_timestamp_time("InitFontGraphicInfo");
2440 InitGlobalAnimGraphicInfo(); // initialize global animation config
2441 print_timestamp_time("InitGlobalAnimGraphicInfo");
2443 InitImageTextures(); // create textures for certain images
2444 print_timestamp_time("InitImageTextures");
2446 InitGraphicInfo_EM(); // graphic mapping for EM engine
2447 print_timestamp_time("InitGraphicInfo_EM");
2449 InitGraphicCompatibilityInfo();
2450 print_timestamp_time("InitGraphicCompatibilityInfo");
2453 print_timestamp_time("InitGadgets");
2455 print_timestamp_time("InitDoors");
2457 InitGameInfoFromArtworkInfo();
2459 print_timestamp_done("ReinitializeGraphics");
2462 static void ReinitializeSounds(void)
2464 InitSoundInfo(); // sound properties mapping
2465 InitElementSoundInfo(); // element game sound mapping
2466 InitGameModeSoundInfo(); // game mode sound mapping
2467 InitGlobalAnimSoundInfo(); // global animation sound settings
2469 InitPlayLevelSound(); // internal game sound settings
2472 static void ReinitializeMusic(void)
2474 InitMusicInfo(); // music properties mapping
2475 InitGameModeMusicInfo(); // game mode music mapping
2476 InitGlobalAnimMusicInfo(); // global animation music settings
2479 static int get_special_property_bit(int element, int property_bit_nr)
2481 struct PropertyBitInfo
2487 static struct PropertyBitInfo pb_can_move_into_acid[] =
2489 // the player may be able fall into acid when gravity is activated
2494 { EL_SP_MURPHY, 0 },
2495 { EL_SOKOBAN_FIELD_PLAYER, 0 },
2497 // all elements that can move may be able to also move into acid
2500 { EL_BUG_RIGHT, 1 },
2503 { EL_SPACESHIP, 2 },
2504 { EL_SPACESHIP_LEFT, 2 },
2505 { EL_SPACESHIP_RIGHT, 2 },
2506 { EL_SPACESHIP_UP, 2 },
2507 { EL_SPACESHIP_DOWN, 2 },
2508 { EL_BD_BUTTERFLY, 3 },
2509 { EL_BD_BUTTERFLY_LEFT, 3 },
2510 { EL_BD_BUTTERFLY_RIGHT, 3 },
2511 { EL_BD_BUTTERFLY_UP, 3 },
2512 { EL_BD_BUTTERFLY_DOWN, 3 },
2513 { EL_BD_FIREFLY, 4 },
2514 { EL_BD_FIREFLY_LEFT, 4 },
2515 { EL_BD_FIREFLY_RIGHT, 4 },
2516 { EL_BD_FIREFLY_UP, 4 },
2517 { EL_BD_FIREFLY_DOWN, 4 },
2519 { EL_YAMYAM_LEFT, 5 },
2520 { EL_YAMYAM_RIGHT, 5 },
2521 { EL_YAMYAM_UP, 5 },
2522 { EL_YAMYAM_DOWN, 5 },
2523 { EL_DARK_YAMYAM, 6 },
2526 { EL_PACMAN_LEFT, 8 },
2527 { EL_PACMAN_RIGHT, 8 },
2528 { EL_PACMAN_UP, 8 },
2529 { EL_PACMAN_DOWN, 8 },
2531 { EL_MOLE_LEFT, 9 },
2532 { EL_MOLE_RIGHT, 9 },
2534 { EL_MOLE_DOWN, 9 },
2538 { EL_SATELLITE, 13 },
2539 { EL_SP_SNIKSNAK, 14 },
2540 { EL_SP_ELECTRON, 15 },
2543 { EL_SPRING_LEFT, 17 },
2544 { EL_SPRING_RIGHT, 17 },
2545 { EL_EMC_ANDROID, 18 },
2550 static struct PropertyBitInfo pb_dont_collide_with[] =
2552 { EL_SP_SNIKSNAK, 0 },
2553 { EL_SP_ELECTRON, 1 },
2561 struct PropertyBitInfo *pb_info;
2564 { EP_CAN_MOVE_INTO_ACID, pb_can_move_into_acid },
2565 { EP_DONT_COLLIDE_WITH, pb_dont_collide_with },
2570 struct PropertyBitInfo *pb_info = NULL;
2573 for (i = 0; pb_definition[i].bit_nr != -1; i++)
2574 if (pb_definition[i].bit_nr == property_bit_nr)
2575 pb_info = pb_definition[i].pb_info;
2577 if (pb_info == NULL)
2580 for (i = 0; pb_info[i].element != -1; i++)
2581 if (pb_info[i].element == element)
2582 return pb_info[i].bit_nr;
2587 void setBitfieldProperty(int *bitfield, int property_bit_nr, int element,
2588 boolean property_value)
2590 int bit_nr = get_special_property_bit(element, property_bit_nr);
2595 *bitfield |= (1 << bit_nr);
2597 *bitfield &= ~(1 << bit_nr);
2601 boolean getBitfieldProperty(int *bitfield, int property_bit_nr, int element)
2603 int bit_nr = get_special_property_bit(element, property_bit_nr);
2606 return ((*bitfield & (1 << bit_nr)) != 0);
2611 static void ResolveGroupElementExt(int group_element, int recursion_depth)
2613 static int group_nr;
2614 static struct ElementGroupInfo *group;
2615 struct ElementGroupInfo *actual_group = element_info[group_element].group;
2618 if (actual_group == NULL) // not yet initialized
2621 if (recursion_depth > NUM_GROUP_ELEMENTS) // recursion too deep
2623 Warn("recursion too deep when resolving group element %d",
2624 group_element - EL_GROUP_START + 1);
2626 // replace element which caused too deep recursion by question mark
2627 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
2632 if (recursion_depth == 0) // initialization
2634 group = actual_group;
2635 group_nr = GROUP_NR(group_element);
2637 group->num_elements_resolved = 0;
2638 group->choice_pos = 0;
2640 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2641 element_info[i].in_group[group_nr] = FALSE;
2644 for (i = 0; i < actual_group->num_elements; i++)
2646 int element = actual_group->element[i];
2648 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
2651 if (IS_GROUP_ELEMENT(element))
2652 ResolveGroupElementExt(element, recursion_depth + 1);
2655 group->element_resolved[group->num_elements_resolved++] = element;
2656 element_info[element].in_group[group_nr] = TRUE;
2661 void ResolveGroupElement(int group_element)
2663 ResolveGroupElementExt(group_element, 0);
2666 void InitElementPropertiesStatic(void)
2668 static boolean clipboard_elements_initialized = FALSE;
2670 static int ep_diggable[] =
2675 EL_SP_BUGGY_BASE_ACTIVATING,
2678 EL_INVISIBLE_SAND_ACTIVE,
2681 // !!! currently not diggable, but handled by 'ep_dont_run_into' !!!
2682 // (if amoeba can grow into anything diggable, maybe keep these out)
2687 EL_SP_BUGGY_BASE_ACTIVE,
2694 static int ep_collectible_only[] =
2716 EL_DYNABOMB_INCREASE_NUMBER,
2717 EL_DYNABOMB_INCREASE_SIZE,
2718 EL_DYNABOMB_INCREASE_POWER,
2736 // !!! handle separately !!!
2737 EL_DC_LANDMINE, // deadly when running into, but can be snapped
2743 static int ep_dont_run_into[] =
2745 // same elements as in 'ep_dont_touch'
2751 // same elements as in 'ep_dont_collide_with'
2763 // !!! maybe this should better be handled by 'ep_diggable' !!!
2768 EL_SP_BUGGY_BASE_ACTIVE,
2775 static int ep_dont_collide_with[] =
2777 // same elements as in 'ep_dont_touch'
2794 static int ep_dont_touch[] =
2804 static int ep_indestructible[] =
2808 EL_ACID_POOL_TOPLEFT,
2809 EL_ACID_POOL_TOPRIGHT,
2810 EL_ACID_POOL_BOTTOMLEFT,
2811 EL_ACID_POOL_BOTTOM,
2812 EL_ACID_POOL_BOTTOMRIGHT,
2813 EL_SP_HARDWARE_GRAY,
2814 EL_SP_HARDWARE_GREEN,
2815 EL_SP_HARDWARE_BLUE,
2817 EL_SP_HARDWARE_YELLOW,
2818 EL_SP_HARDWARE_BASE_1,
2819 EL_SP_HARDWARE_BASE_2,
2820 EL_SP_HARDWARE_BASE_3,
2821 EL_SP_HARDWARE_BASE_4,
2822 EL_SP_HARDWARE_BASE_5,
2823 EL_SP_HARDWARE_BASE_6,
2824 EL_INVISIBLE_STEELWALL,
2825 EL_INVISIBLE_STEELWALL_ACTIVE,
2826 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2827 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
2828 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
2829 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2830 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
2831 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
2832 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2833 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
2834 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
2835 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
2836 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
2837 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
2839 EL_LIGHT_SWITCH_ACTIVE,
2840 EL_SIGN_EXCLAMATION,
2841 EL_SIGN_RADIOACTIVITY,
2848 EL_SIGN_ENTRY_FORBIDDEN,
2849 EL_SIGN_EMERGENCY_EXIT,
2857 EL_STEEL_EXIT_CLOSED,
2859 EL_STEEL_EXIT_OPENING,
2860 EL_STEEL_EXIT_CLOSING,
2861 EL_EM_STEEL_EXIT_CLOSED,
2862 EL_EM_STEEL_EXIT_OPEN,
2863 EL_EM_STEEL_EXIT_OPENING,
2864 EL_EM_STEEL_EXIT_CLOSING,
2865 EL_DC_STEELWALL_1_LEFT,
2866 EL_DC_STEELWALL_1_RIGHT,
2867 EL_DC_STEELWALL_1_TOP,
2868 EL_DC_STEELWALL_1_BOTTOM,
2869 EL_DC_STEELWALL_1_HORIZONTAL,
2870 EL_DC_STEELWALL_1_VERTICAL,
2871 EL_DC_STEELWALL_1_TOPLEFT,
2872 EL_DC_STEELWALL_1_TOPRIGHT,
2873 EL_DC_STEELWALL_1_BOTTOMLEFT,
2874 EL_DC_STEELWALL_1_BOTTOMRIGHT,
2875 EL_DC_STEELWALL_1_TOPLEFT_2,
2876 EL_DC_STEELWALL_1_TOPRIGHT_2,
2877 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
2878 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
2879 EL_DC_STEELWALL_2_LEFT,
2880 EL_DC_STEELWALL_2_RIGHT,
2881 EL_DC_STEELWALL_2_TOP,
2882 EL_DC_STEELWALL_2_BOTTOM,
2883 EL_DC_STEELWALL_2_HORIZONTAL,
2884 EL_DC_STEELWALL_2_VERTICAL,
2885 EL_DC_STEELWALL_2_MIDDLE,
2886 EL_DC_STEELWALL_2_SINGLE,
2887 EL_STEELWALL_SLIPPERY,
2901 EL_GATE_1_GRAY_ACTIVE,
2902 EL_GATE_2_GRAY_ACTIVE,
2903 EL_GATE_3_GRAY_ACTIVE,
2904 EL_GATE_4_GRAY_ACTIVE,
2913 EL_EM_GATE_1_GRAY_ACTIVE,
2914 EL_EM_GATE_2_GRAY_ACTIVE,
2915 EL_EM_GATE_3_GRAY_ACTIVE,
2916 EL_EM_GATE_4_GRAY_ACTIVE,
2925 EL_EMC_GATE_5_GRAY_ACTIVE,
2926 EL_EMC_GATE_6_GRAY_ACTIVE,
2927 EL_EMC_GATE_7_GRAY_ACTIVE,
2928 EL_EMC_GATE_8_GRAY_ACTIVE,
2930 EL_DC_GATE_WHITE_GRAY,
2931 EL_DC_GATE_WHITE_GRAY_ACTIVE,
2932 EL_DC_GATE_FAKE_GRAY,
2934 EL_SWITCHGATE_OPENING,
2935 EL_SWITCHGATE_CLOSED,
2936 EL_SWITCHGATE_CLOSING,
2937 EL_DC_SWITCHGATE_SWITCH_UP,
2938 EL_DC_SWITCHGATE_SWITCH_DOWN,
2940 EL_TIMEGATE_OPENING,
2942 EL_TIMEGATE_CLOSING,
2943 EL_DC_TIMEGATE_SWITCH,
2944 EL_DC_TIMEGATE_SWITCH_ACTIVE,
2948 EL_TUBE_VERTICAL_LEFT,
2949 EL_TUBE_VERTICAL_RIGHT,
2950 EL_TUBE_HORIZONTAL_UP,
2951 EL_TUBE_HORIZONTAL_DOWN,
2956 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
2957 EL_EXPANDABLE_STEELWALL_VERTICAL,
2958 EL_EXPANDABLE_STEELWALL_ANY,
2963 static int ep_slippery[] =
2977 EL_ROBOT_WHEEL_ACTIVE,
2983 EL_ACID_POOL_TOPLEFT,
2984 EL_ACID_POOL_TOPRIGHT,
2994 EL_STEELWALL_SLIPPERY,
2997 EL_EMC_WALL_SLIPPERY_1,
2998 EL_EMC_WALL_SLIPPERY_2,
2999 EL_EMC_WALL_SLIPPERY_3,
3000 EL_EMC_WALL_SLIPPERY_4,
3002 EL_EMC_MAGIC_BALL_ACTIVE,
3007 static int ep_can_change[] =
3012 static int ep_can_move[] =
3014 // same elements as in 'pb_can_move_into_acid'
3037 static int ep_can_fall[] =
3052 EL_QUICKSAND_FAST_FULL,
3054 EL_BD_MAGIC_WALL_FULL,
3055 EL_DC_MAGIC_WALL_FULL,
3069 static int ep_can_smash_player[] =
3095 static int ep_can_smash_enemies[] =
3104 static int ep_can_smash_everything[] =
3113 static int ep_explodes_by_fire[] =
3115 // same elements as in 'ep_explodes_impact'
3120 // same elements as in 'ep_explodes_smashed'
3130 EL_EM_DYNAMITE_ACTIVE,
3131 EL_DYNABOMB_PLAYER_1_ACTIVE,
3132 EL_DYNABOMB_PLAYER_2_ACTIVE,
3133 EL_DYNABOMB_PLAYER_3_ACTIVE,
3134 EL_DYNABOMB_PLAYER_4_ACTIVE,
3135 EL_DYNABOMB_INCREASE_NUMBER,
3136 EL_DYNABOMB_INCREASE_SIZE,
3137 EL_DYNABOMB_INCREASE_POWER,
3138 EL_SP_DISK_RED_ACTIVE,
3152 static int ep_explodes_smashed[] =
3154 // same elements as in 'ep_explodes_impact'
3168 static int ep_explodes_impact[] =
3177 static int ep_walkable_over[] =
3197 EL_SOKOBAN_FIELD_EMPTY,
3204 EL_EM_STEEL_EXIT_OPEN,
3205 EL_EM_STEEL_EXIT_OPENING,
3214 EL_GATE_1_GRAY_ACTIVE,
3215 EL_GATE_2_GRAY_ACTIVE,
3216 EL_GATE_3_GRAY_ACTIVE,
3217 EL_GATE_4_GRAY_ACTIVE,
3225 static int ep_walkable_inside[] =
3230 EL_TUBE_VERTICAL_LEFT,
3231 EL_TUBE_VERTICAL_RIGHT,
3232 EL_TUBE_HORIZONTAL_UP,
3233 EL_TUBE_HORIZONTAL_DOWN,
3242 static int ep_walkable_under[] =
3247 static int ep_passable_over[] =
3257 EL_EM_GATE_1_GRAY_ACTIVE,
3258 EL_EM_GATE_2_GRAY_ACTIVE,
3259 EL_EM_GATE_3_GRAY_ACTIVE,
3260 EL_EM_GATE_4_GRAY_ACTIVE,
3269 EL_EMC_GATE_5_GRAY_ACTIVE,
3270 EL_EMC_GATE_6_GRAY_ACTIVE,
3271 EL_EMC_GATE_7_GRAY_ACTIVE,
3272 EL_EMC_GATE_8_GRAY_ACTIVE,
3274 EL_DC_GATE_WHITE_GRAY,
3275 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3282 static int ep_passable_inside[] =
3288 EL_SP_PORT_HORIZONTAL,
3289 EL_SP_PORT_VERTICAL,
3291 EL_SP_GRAVITY_PORT_LEFT,
3292 EL_SP_GRAVITY_PORT_RIGHT,
3293 EL_SP_GRAVITY_PORT_UP,
3294 EL_SP_GRAVITY_PORT_DOWN,
3295 EL_SP_GRAVITY_ON_PORT_LEFT,
3296 EL_SP_GRAVITY_ON_PORT_RIGHT,
3297 EL_SP_GRAVITY_ON_PORT_UP,
3298 EL_SP_GRAVITY_ON_PORT_DOWN,
3299 EL_SP_GRAVITY_OFF_PORT_LEFT,
3300 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3301 EL_SP_GRAVITY_OFF_PORT_UP,
3302 EL_SP_GRAVITY_OFF_PORT_DOWN,
3307 static int ep_passable_under[] =
3312 static int ep_droppable[] =
3317 static int ep_explodes_1x1_old[] =
3322 static int ep_pushable[] =
3334 EL_SOKOBAN_FIELD_FULL,
3343 static int ep_explodes_cross_old[] =
3348 static int ep_protected[] =
3350 // same elements as in 'ep_walkable_inside'
3354 EL_TUBE_VERTICAL_LEFT,
3355 EL_TUBE_VERTICAL_RIGHT,
3356 EL_TUBE_HORIZONTAL_UP,
3357 EL_TUBE_HORIZONTAL_DOWN,
3363 // same elements as in 'ep_passable_over'
3372 EL_EM_GATE_1_GRAY_ACTIVE,
3373 EL_EM_GATE_2_GRAY_ACTIVE,
3374 EL_EM_GATE_3_GRAY_ACTIVE,
3375 EL_EM_GATE_4_GRAY_ACTIVE,
3384 EL_EMC_GATE_5_GRAY_ACTIVE,
3385 EL_EMC_GATE_6_GRAY_ACTIVE,
3386 EL_EMC_GATE_7_GRAY_ACTIVE,
3387 EL_EMC_GATE_8_GRAY_ACTIVE,
3389 EL_DC_GATE_WHITE_GRAY,
3390 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3394 // same elements as in 'ep_passable_inside'
3399 EL_SP_PORT_HORIZONTAL,
3400 EL_SP_PORT_VERTICAL,
3402 EL_SP_GRAVITY_PORT_LEFT,
3403 EL_SP_GRAVITY_PORT_RIGHT,
3404 EL_SP_GRAVITY_PORT_UP,
3405 EL_SP_GRAVITY_PORT_DOWN,
3406 EL_SP_GRAVITY_ON_PORT_LEFT,
3407 EL_SP_GRAVITY_ON_PORT_RIGHT,
3408 EL_SP_GRAVITY_ON_PORT_UP,
3409 EL_SP_GRAVITY_ON_PORT_DOWN,
3410 EL_SP_GRAVITY_OFF_PORT_LEFT,
3411 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3412 EL_SP_GRAVITY_OFF_PORT_UP,
3413 EL_SP_GRAVITY_OFF_PORT_DOWN,
3418 static int ep_throwable[] =
3423 static int ep_can_explode[] =
3425 // same elements as in 'ep_explodes_impact'
3430 // same elements as in 'ep_explodes_smashed'
3436 // elements that can explode by explosion or by dragonfire
3440 EL_EM_DYNAMITE_ACTIVE,
3441 EL_DYNABOMB_PLAYER_1_ACTIVE,
3442 EL_DYNABOMB_PLAYER_2_ACTIVE,
3443 EL_DYNABOMB_PLAYER_3_ACTIVE,
3444 EL_DYNABOMB_PLAYER_4_ACTIVE,
3445 EL_DYNABOMB_INCREASE_NUMBER,
3446 EL_DYNABOMB_INCREASE_SIZE,
3447 EL_DYNABOMB_INCREASE_POWER,
3448 EL_SP_DISK_RED_ACTIVE,
3456 // elements that can explode only by explosion
3462 static int ep_gravity_reachable[] =
3468 EL_INVISIBLE_SAND_ACTIVE,
3473 EL_SP_PORT_HORIZONTAL,
3474 EL_SP_PORT_VERTICAL,
3476 EL_SP_GRAVITY_PORT_LEFT,
3477 EL_SP_GRAVITY_PORT_RIGHT,
3478 EL_SP_GRAVITY_PORT_UP,
3479 EL_SP_GRAVITY_PORT_DOWN,
3480 EL_SP_GRAVITY_ON_PORT_LEFT,
3481 EL_SP_GRAVITY_ON_PORT_RIGHT,
3482 EL_SP_GRAVITY_ON_PORT_UP,
3483 EL_SP_GRAVITY_ON_PORT_DOWN,
3484 EL_SP_GRAVITY_OFF_PORT_LEFT,
3485 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3486 EL_SP_GRAVITY_OFF_PORT_UP,
3487 EL_SP_GRAVITY_OFF_PORT_DOWN,
3493 static int ep_empty_space[] =
3516 static int ep_player[] =
3523 EL_SOKOBAN_FIELD_PLAYER,
3529 static int ep_can_pass_magic_wall[] =
3543 static int ep_can_pass_dc_magic_wall[] =
3559 static int ep_switchable[] =
3563 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3564 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3565 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3566 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3567 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3568 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3569 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3570 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3571 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3572 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3573 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3574 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3575 EL_SWITCHGATE_SWITCH_UP,
3576 EL_SWITCHGATE_SWITCH_DOWN,
3577 EL_DC_SWITCHGATE_SWITCH_UP,
3578 EL_DC_SWITCHGATE_SWITCH_DOWN,
3580 EL_LIGHT_SWITCH_ACTIVE,
3582 EL_DC_TIMEGATE_SWITCH,
3583 EL_BALLOON_SWITCH_LEFT,
3584 EL_BALLOON_SWITCH_RIGHT,
3585 EL_BALLOON_SWITCH_UP,
3586 EL_BALLOON_SWITCH_DOWN,
3587 EL_BALLOON_SWITCH_ANY,
3588 EL_BALLOON_SWITCH_NONE,
3591 EL_EMC_MAGIC_BALL_SWITCH,
3592 EL_EMC_MAGIC_BALL_SWITCH_ACTIVE,
3597 static int ep_bd_element[] =
3631 static int ep_sp_element[] =
3633 // should always be valid
3636 // standard classic Supaplex elements
3643 EL_SP_HARDWARE_GRAY,
3651 EL_SP_GRAVITY_PORT_RIGHT,
3652 EL_SP_GRAVITY_PORT_DOWN,
3653 EL_SP_GRAVITY_PORT_LEFT,
3654 EL_SP_GRAVITY_PORT_UP,
3659 EL_SP_PORT_VERTICAL,
3660 EL_SP_PORT_HORIZONTAL,
3666 EL_SP_HARDWARE_BASE_1,
3667 EL_SP_HARDWARE_GREEN,
3668 EL_SP_HARDWARE_BLUE,
3670 EL_SP_HARDWARE_YELLOW,
3671 EL_SP_HARDWARE_BASE_2,
3672 EL_SP_HARDWARE_BASE_3,
3673 EL_SP_HARDWARE_BASE_4,
3674 EL_SP_HARDWARE_BASE_5,
3675 EL_SP_HARDWARE_BASE_6,
3679 // additional elements that appeared in newer Supaplex levels
3682 // additional gravity port elements (not switching, but setting gravity)
3683 EL_SP_GRAVITY_ON_PORT_LEFT,
3684 EL_SP_GRAVITY_ON_PORT_RIGHT,
3685 EL_SP_GRAVITY_ON_PORT_UP,
3686 EL_SP_GRAVITY_ON_PORT_DOWN,
3687 EL_SP_GRAVITY_OFF_PORT_LEFT,
3688 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3689 EL_SP_GRAVITY_OFF_PORT_UP,
3690 EL_SP_GRAVITY_OFF_PORT_DOWN,
3692 // more than one Murphy in a level results in an inactive clone
3695 // runtime Supaplex elements
3696 EL_SP_DISK_RED_ACTIVE,
3697 EL_SP_TERMINAL_ACTIVE,
3698 EL_SP_BUGGY_BASE_ACTIVATING,
3699 EL_SP_BUGGY_BASE_ACTIVE,
3706 static int ep_sb_element[] =
3711 EL_SOKOBAN_FIELD_EMPTY,
3712 EL_SOKOBAN_FIELD_FULL,
3713 EL_SOKOBAN_FIELD_PLAYER,
3718 EL_INVISIBLE_STEELWALL,
3723 static int ep_gem[] =
3735 static int ep_food_dark_yamyam[] =
3763 static int ep_food_penguin[] =
3777 static int ep_food_pig[] =
3789 static int ep_historic_wall[] =
3800 EL_GATE_1_GRAY_ACTIVE,
3801 EL_GATE_2_GRAY_ACTIVE,
3802 EL_GATE_3_GRAY_ACTIVE,
3803 EL_GATE_4_GRAY_ACTIVE,
3812 EL_EM_GATE_1_GRAY_ACTIVE,
3813 EL_EM_GATE_2_GRAY_ACTIVE,
3814 EL_EM_GATE_3_GRAY_ACTIVE,
3815 EL_EM_GATE_4_GRAY_ACTIVE,
3822 EL_EXPANDABLE_WALL_HORIZONTAL,
3823 EL_EXPANDABLE_WALL_VERTICAL,
3824 EL_EXPANDABLE_WALL_ANY,
3825 EL_EXPANDABLE_WALL_GROWING,
3826 EL_BD_EXPANDABLE_WALL,
3833 EL_SP_HARDWARE_GRAY,
3834 EL_SP_HARDWARE_GREEN,
3835 EL_SP_HARDWARE_BLUE,
3837 EL_SP_HARDWARE_YELLOW,
3838 EL_SP_HARDWARE_BASE_1,
3839 EL_SP_HARDWARE_BASE_2,
3840 EL_SP_HARDWARE_BASE_3,
3841 EL_SP_HARDWARE_BASE_4,
3842 EL_SP_HARDWARE_BASE_5,
3843 EL_SP_HARDWARE_BASE_6,
3845 EL_SP_TERMINAL_ACTIVE,
3848 EL_INVISIBLE_STEELWALL,
3849 EL_INVISIBLE_STEELWALL_ACTIVE,
3851 EL_INVISIBLE_WALL_ACTIVE,
3852 EL_STEELWALL_SLIPPERY,
3869 static int ep_historic_solid[] =
3873 EL_EXPANDABLE_WALL_HORIZONTAL,
3874 EL_EXPANDABLE_WALL_VERTICAL,
3875 EL_EXPANDABLE_WALL_ANY,
3876 EL_BD_EXPANDABLE_WALL,
3889 EL_QUICKSAND_FILLING,
3890 EL_QUICKSAND_EMPTYING,
3892 EL_MAGIC_WALL_ACTIVE,
3893 EL_MAGIC_WALL_EMPTYING,
3894 EL_MAGIC_WALL_FILLING,
3898 EL_BD_MAGIC_WALL_ACTIVE,
3899 EL_BD_MAGIC_WALL_EMPTYING,
3900 EL_BD_MAGIC_WALL_FULL,
3901 EL_BD_MAGIC_WALL_FILLING,
3902 EL_BD_MAGIC_WALL_DEAD,
3911 EL_SP_TERMINAL_ACTIVE,
3915 EL_INVISIBLE_WALL_ACTIVE,
3916 EL_SWITCHGATE_SWITCH_UP,
3917 EL_SWITCHGATE_SWITCH_DOWN,
3919 EL_TIMEGATE_SWITCH_ACTIVE,
3931 // the following elements are a direct copy of "indestructible" elements,
3932 // except "EL_ACID", which is "indestructible", but not "solid"!
3937 EL_ACID_POOL_TOPLEFT,
3938 EL_ACID_POOL_TOPRIGHT,
3939 EL_ACID_POOL_BOTTOMLEFT,
3940 EL_ACID_POOL_BOTTOM,
3941 EL_ACID_POOL_BOTTOMRIGHT,
3942 EL_SP_HARDWARE_GRAY,
3943 EL_SP_HARDWARE_GREEN,
3944 EL_SP_HARDWARE_BLUE,
3946 EL_SP_HARDWARE_YELLOW,
3947 EL_SP_HARDWARE_BASE_1,
3948 EL_SP_HARDWARE_BASE_2,
3949 EL_SP_HARDWARE_BASE_3,
3950 EL_SP_HARDWARE_BASE_4,
3951 EL_SP_HARDWARE_BASE_5,
3952 EL_SP_HARDWARE_BASE_6,
3953 EL_INVISIBLE_STEELWALL,
3954 EL_INVISIBLE_STEELWALL_ACTIVE,
3955 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3956 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3957 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3958 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3959 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3960 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3961 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3962 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3963 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3964 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3965 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3966 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3968 EL_LIGHT_SWITCH_ACTIVE,
3969 EL_SIGN_EXCLAMATION,
3970 EL_SIGN_RADIOACTIVITY,
3977 EL_SIGN_ENTRY_FORBIDDEN,
3978 EL_SIGN_EMERGENCY_EXIT,
3986 EL_STEEL_EXIT_CLOSED,
3988 EL_STEEL_EXIT_OPENING,
3989 EL_STEEL_EXIT_CLOSING,
3990 EL_EM_STEEL_EXIT_CLOSED,
3991 EL_EM_STEEL_EXIT_OPEN,
3992 EL_EM_STEEL_EXIT_OPENING,
3993 EL_EM_STEEL_EXIT_CLOSING,
3994 EL_DC_STEELWALL_1_LEFT,
3995 EL_DC_STEELWALL_1_RIGHT,
3996 EL_DC_STEELWALL_1_TOP,
3997 EL_DC_STEELWALL_1_BOTTOM,
3998 EL_DC_STEELWALL_1_HORIZONTAL,
3999 EL_DC_STEELWALL_1_VERTICAL,
4000 EL_DC_STEELWALL_1_TOPLEFT,
4001 EL_DC_STEELWALL_1_TOPRIGHT,
4002 EL_DC_STEELWALL_1_BOTTOMLEFT,
4003 EL_DC_STEELWALL_1_BOTTOMRIGHT,
4004 EL_DC_STEELWALL_1_TOPLEFT_2,
4005 EL_DC_STEELWALL_1_TOPRIGHT_2,
4006 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
4007 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
4008 EL_DC_STEELWALL_2_LEFT,
4009 EL_DC_STEELWALL_2_RIGHT,
4010 EL_DC_STEELWALL_2_TOP,
4011 EL_DC_STEELWALL_2_BOTTOM,
4012 EL_DC_STEELWALL_2_HORIZONTAL,
4013 EL_DC_STEELWALL_2_VERTICAL,
4014 EL_DC_STEELWALL_2_MIDDLE,
4015 EL_DC_STEELWALL_2_SINGLE,
4016 EL_STEELWALL_SLIPPERY,
4030 EL_GATE_1_GRAY_ACTIVE,
4031 EL_GATE_2_GRAY_ACTIVE,
4032 EL_GATE_3_GRAY_ACTIVE,
4033 EL_GATE_4_GRAY_ACTIVE,
4042 EL_EM_GATE_1_GRAY_ACTIVE,
4043 EL_EM_GATE_2_GRAY_ACTIVE,
4044 EL_EM_GATE_3_GRAY_ACTIVE,
4045 EL_EM_GATE_4_GRAY_ACTIVE,
4054 EL_EMC_GATE_5_GRAY_ACTIVE,
4055 EL_EMC_GATE_6_GRAY_ACTIVE,
4056 EL_EMC_GATE_7_GRAY_ACTIVE,
4057 EL_EMC_GATE_8_GRAY_ACTIVE,
4059 EL_DC_GATE_WHITE_GRAY,
4060 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4061 EL_DC_GATE_FAKE_GRAY,
4063 EL_SWITCHGATE_OPENING,
4064 EL_SWITCHGATE_CLOSED,
4065 EL_SWITCHGATE_CLOSING,
4066 EL_DC_SWITCHGATE_SWITCH_UP,
4067 EL_DC_SWITCHGATE_SWITCH_DOWN,
4069 EL_TIMEGATE_OPENING,
4071 EL_TIMEGATE_CLOSING,
4072 EL_DC_TIMEGATE_SWITCH,
4073 EL_DC_TIMEGATE_SWITCH_ACTIVE,
4077 EL_TUBE_VERTICAL_LEFT,
4078 EL_TUBE_VERTICAL_RIGHT,
4079 EL_TUBE_HORIZONTAL_UP,
4080 EL_TUBE_HORIZONTAL_DOWN,
4085 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
4086 EL_EXPANDABLE_STEELWALL_VERTICAL,
4087 EL_EXPANDABLE_STEELWALL_ANY,
4092 static int ep_classic_enemy[] =
4109 static int ep_belt[] =
4111 EL_CONVEYOR_BELT_1_LEFT,
4112 EL_CONVEYOR_BELT_1_MIDDLE,
4113 EL_CONVEYOR_BELT_1_RIGHT,
4114 EL_CONVEYOR_BELT_2_LEFT,
4115 EL_CONVEYOR_BELT_2_MIDDLE,
4116 EL_CONVEYOR_BELT_2_RIGHT,
4117 EL_CONVEYOR_BELT_3_LEFT,
4118 EL_CONVEYOR_BELT_3_MIDDLE,
4119 EL_CONVEYOR_BELT_3_RIGHT,
4120 EL_CONVEYOR_BELT_4_LEFT,
4121 EL_CONVEYOR_BELT_4_MIDDLE,
4122 EL_CONVEYOR_BELT_4_RIGHT,
4127 static int ep_belt_active[] =
4129 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4130 EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE,
4131 EL_CONVEYOR_BELT_1_RIGHT_ACTIVE,
4132 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4133 EL_CONVEYOR_BELT_2_MIDDLE_ACTIVE,
4134 EL_CONVEYOR_BELT_2_RIGHT_ACTIVE,
4135 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4136 EL_CONVEYOR_BELT_3_MIDDLE_ACTIVE,
4137 EL_CONVEYOR_BELT_3_RIGHT_ACTIVE,
4138 EL_CONVEYOR_BELT_4_LEFT_ACTIVE,
4139 EL_CONVEYOR_BELT_4_MIDDLE_ACTIVE,
4140 EL_CONVEYOR_BELT_4_RIGHT_ACTIVE,
4145 static int ep_belt_switch[] =
4147 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4148 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4149 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4150 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4151 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4152 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4153 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4154 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4155 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4156 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4157 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4158 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4163 static int ep_tube[] =
4170 EL_TUBE_HORIZONTAL_UP,
4171 EL_TUBE_HORIZONTAL_DOWN,
4173 EL_TUBE_VERTICAL_LEFT,
4174 EL_TUBE_VERTICAL_RIGHT,
4180 static int ep_acid_pool[] =
4182 EL_ACID_POOL_TOPLEFT,
4183 EL_ACID_POOL_TOPRIGHT,
4184 EL_ACID_POOL_BOTTOMLEFT,
4185 EL_ACID_POOL_BOTTOM,
4186 EL_ACID_POOL_BOTTOMRIGHT,
4191 static int ep_keygate[] =
4201 EL_GATE_1_GRAY_ACTIVE,
4202 EL_GATE_2_GRAY_ACTIVE,
4203 EL_GATE_3_GRAY_ACTIVE,
4204 EL_GATE_4_GRAY_ACTIVE,
4213 EL_EM_GATE_1_GRAY_ACTIVE,
4214 EL_EM_GATE_2_GRAY_ACTIVE,
4215 EL_EM_GATE_3_GRAY_ACTIVE,
4216 EL_EM_GATE_4_GRAY_ACTIVE,
4225 EL_EMC_GATE_5_GRAY_ACTIVE,
4226 EL_EMC_GATE_6_GRAY_ACTIVE,
4227 EL_EMC_GATE_7_GRAY_ACTIVE,
4228 EL_EMC_GATE_8_GRAY_ACTIVE,
4230 EL_DC_GATE_WHITE_GRAY,
4231 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4236 static int ep_amoeboid[] =
4248 static int ep_amoebalive[] =
4259 static int ep_has_editor_content[] =
4265 EL_SOKOBAN_FIELD_PLAYER,
4283 static int ep_can_turn_each_move[] =
4285 // !!! do something with this one !!!
4289 static int ep_can_grow[] =
4303 static int ep_active_bomb[] =
4306 EL_EM_DYNAMITE_ACTIVE,
4307 EL_DYNABOMB_PLAYER_1_ACTIVE,
4308 EL_DYNABOMB_PLAYER_2_ACTIVE,
4309 EL_DYNABOMB_PLAYER_3_ACTIVE,
4310 EL_DYNABOMB_PLAYER_4_ACTIVE,
4311 EL_SP_DISK_RED_ACTIVE,
4316 static int ep_inactive[] =
4342 EL_QUICKSAND_FAST_EMPTY,
4365 EL_GATE_1_GRAY_ACTIVE,
4366 EL_GATE_2_GRAY_ACTIVE,
4367 EL_GATE_3_GRAY_ACTIVE,
4368 EL_GATE_4_GRAY_ACTIVE,
4377 EL_EM_GATE_1_GRAY_ACTIVE,
4378 EL_EM_GATE_2_GRAY_ACTIVE,
4379 EL_EM_GATE_3_GRAY_ACTIVE,
4380 EL_EM_GATE_4_GRAY_ACTIVE,
4389 EL_EMC_GATE_5_GRAY_ACTIVE,
4390 EL_EMC_GATE_6_GRAY_ACTIVE,
4391 EL_EMC_GATE_7_GRAY_ACTIVE,
4392 EL_EMC_GATE_8_GRAY_ACTIVE,
4394 EL_DC_GATE_WHITE_GRAY,
4395 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4396 EL_DC_GATE_FAKE_GRAY,
4399 EL_INVISIBLE_STEELWALL,
4407 EL_WALL_EMERALD_YELLOW,
4408 EL_DYNABOMB_INCREASE_NUMBER,
4409 EL_DYNABOMB_INCREASE_SIZE,
4410 EL_DYNABOMB_INCREASE_POWER,
4414 EL_SOKOBAN_FIELD_EMPTY,
4415 EL_SOKOBAN_FIELD_FULL,
4416 EL_WALL_EMERALD_RED,
4417 EL_WALL_EMERALD_PURPLE,
4418 EL_ACID_POOL_TOPLEFT,
4419 EL_ACID_POOL_TOPRIGHT,
4420 EL_ACID_POOL_BOTTOMLEFT,
4421 EL_ACID_POOL_BOTTOM,
4422 EL_ACID_POOL_BOTTOMRIGHT,
4426 EL_BD_MAGIC_WALL_DEAD,
4428 EL_DC_MAGIC_WALL_DEAD,
4429 EL_AMOEBA_TO_DIAMOND,
4437 EL_SP_GRAVITY_PORT_RIGHT,
4438 EL_SP_GRAVITY_PORT_DOWN,
4439 EL_SP_GRAVITY_PORT_LEFT,
4440 EL_SP_GRAVITY_PORT_UP,
4441 EL_SP_PORT_HORIZONTAL,
4442 EL_SP_PORT_VERTICAL,
4453 EL_SP_HARDWARE_GRAY,
4454 EL_SP_HARDWARE_GREEN,
4455 EL_SP_HARDWARE_BLUE,
4457 EL_SP_HARDWARE_YELLOW,
4458 EL_SP_HARDWARE_BASE_1,
4459 EL_SP_HARDWARE_BASE_2,
4460 EL_SP_HARDWARE_BASE_3,
4461 EL_SP_HARDWARE_BASE_4,
4462 EL_SP_HARDWARE_BASE_5,
4463 EL_SP_HARDWARE_BASE_6,
4464 EL_SP_GRAVITY_ON_PORT_LEFT,
4465 EL_SP_GRAVITY_ON_PORT_RIGHT,
4466 EL_SP_GRAVITY_ON_PORT_UP,
4467 EL_SP_GRAVITY_ON_PORT_DOWN,
4468 EL_SP_GRAVITY_OFF_PORT_LEFT,
4469 EL_SP_GRAVITY_OFF_PORT_RIGHT,
4470 EL_SP_GRAVITY_OFF_PORT_UP,
4471 EL_SP_GRAVITY_OFF_PORT_DOWN,
4472 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4473 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4474 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4475 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4476 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4477 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4478 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4479 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4480 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4481 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4482 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4483 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4484 EL_SIGN_EXCLAMATION,
4485 EL_SIGN_RADIOACTIVITY,
4492 EL_SIGN_ENTRY_FORBIDDEN,
4493 EL_SIGN_EMERGENCY_EXIT,
4501 EL_DC_STEELWALL_1_LEFT,
4502 EL_DC_STEELWALL_1_RIGHT,
4503 EL_DC_STEELWALL_1_TOP,
4504 EL_DC_STEELWALL_1_BOTTOM,
4505 EL_DC_STEELWALL_1_HORIZONTAL,
4506 EL_DC_STEELWALL_1_VERTICAL,
4507 EL_DC_STEELWALL_1_TOPLEFT,
4508 EL_DC_STEELWALL_1_TOPRIGHT,
4509 EL_DC_STEELWALL_1_BOTTOMLEFT,
4510 EL_DC_STEELWALL_1_BOTTOMRIGHT,
4511 EL_DC_STEELWALL_1_TOPLEFT_2,
4512 EL_DC_STEELWALL_1_TOPRIGHT_2,
4513 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
4514 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
4515 EL_DC_STEELWALL_2_LEFT,
4516 EL_DC_STEELWALL_2_RIGHT,
4517 EL_DC_STEELWALL_2_TOP,
4518 EL_DC_STEELWALL_2_BOTTOM,
4519 EL_DC_STEELWALL_2_HORIZONTAL,
4520 EL_DC_STEELWALL_2_VERTICAL,
4521 EL_DC_STEELWALL_2_MIDDLE,
4522 EL_DC_STEELWALL_2_SINGLE,
4523 EL_STEELWALL_SLIPPERY,
4528 EL_EMC_WALL_SLIPPERY_1,
4529 EL_EMC_WALL_SLIPPERY_2,
4530 EL_EMC_WALL_SLIPPERY_3,
4531 EL_EMC_WALL_SLIPPERY_4,
4552 static int ep_em_slippery_wall[] =
4557 static int ep_gfx_crumbled[] =
4568 static int ep_editor_cascade_active[] =
4570 EL_INTERNAL_CASCADE_BD_ACTIVE,
4571 EL_INTERNAL_CASCADE_EM_ACTIVE,
4572 EL_INTERNAL_CASCADE_EMC_ACTIVE,
4573 EL_INTERNAL_CASCADE_RND_ACTIVE,
4574 EL_INTERNAL_CASCADE_SB_ACTIVE,
4575 EL_INTERNAL_CASCADE_SP_ACTIVE,
4576 EL_INTERNAL_CASCADE_DC_ACTIVE,
4577 EL_INTERNAL_CASCADE_DX_ACTIVE,
4578 EL_INTERNAL_CASCADE_MM_ACTIVE,
4579 EL_INTERNAL_CASCADE_DF_ACTIVE,
4580 EL_INTERNAL_CASCADE_CHARS_ACTIVE,
4581 EL_INTERNAL_CASCADE_STEEL_CHARS_ACTIVE,
4582 EL_INTERNAL_CASCADE_CE_ACTIVE,
4583 EL_INTERNAL_CASCADE_GE_ACTIVE,
4584 EL_INTERNAL_CASCADE_ES_ACTIVE,
4585 EL_INTERNAL_CASCADE_REF_ACTIVE,
4586 EL_INTERNAL_CASCADE_USER_ACTIVE,
4587 EL_INTERNAL_CASCADE_DYNAMIC_ACTIVE,
4592 static int ep_editor_cascade_inactive[] =
4594 EL_INTERNAL_CASCADE_BD,
4595 EL_INTERNAL_CASCADE_EM,
4596 EL_INTERNAL_CASCADE_EMC,
4597 EL_INTERNAL_CASCADE_RND,
4598 EL_INTERNAL_CASCADE_SB,
4599 EL_INTERNAL_CASCADE_SP,
4600 EL_INTERNAL_CASCADE_DC,
4601 EL_INTERNAL_CASCADE_DX,
4602 EL_INTERNAL_CASCADE_MM,
4603 EL_INTERNAL_CASCADE_DF,
4604 EL_INTERNAL_CASCADE_CHARS,
4605 EL_INTERNAL_CASCADE_STEEL_CHARS,
4606 EL_INTERNAL_CASCADE_CE,
4607 EL_INTERNAL_CASCADE_GE,
4608 EL_INTERNAL_CASCADE_ES,
4609 EL_INTERNAL_CASCADE_REF,
4610 EL_INTERNAL_CASCADE_USER,
4611 EL_INTERNAL_CASCADE_DYNAMIC,
4616 static int ep_obsolete[] =
4620 EL_EM_KEY_1_FILE_OBSOLETE,
4621 EL_EM_KEY_2_FILE_OBSOLETE,
4622 EL_EM_KEY_3_FILE_OBSOLETE,
4623 EL_EM_KEY_4_FILE_OBSOLETE,
4624 EL_ENVELOPE_OBSOLETE,
4633 } element_properties[] =
4635 { ep_diggable, EP_DIGGABLE },
4636 { ep_collectible_only, EP_COLLECTIBLE_ONLY },
4637 { ep_dont_run_into, EP_DONT_RUN_INTO },
4638 { ep_dont_collide_with, EP_DONT_COLLIDE_WITH },
4639 { ep_dont_touch, EP_DONT_TOUCH },
4640 { ep_indestructible, EP_INDESTRUCTIBLE },
4641 { ep_slippery, EP_SLIPPERY },
4642 { ep_can_change, EP_CAN_CHANGE },
4643 { ep_can_move, EP_CAN_MOVE },
4644 { ep_can_fall, EP_CAN_FALL },
4645 { ep_can_smash_player, EP_CAN_SMASH_PLAYER },
4646 { ep_can_smash_enemies, EP_CAN_SMASH_ENEMIES },
4647 { ep_can_smash_everything, EP_CAN_SMASH_EVERYTHING },
4648 { ep_explodes_by_fire, EP_EXPLODES_BY_FIRE },
4649 { ep_explodes_smashed, EP_EXPLODES_SMASHED },
4650 { ep_explodes_impact, EP_EXPLODES_IMPACT },
4651 { ep_walkable_over, EP_WALKABLE_OVER },
4652 { ep_walkable_inside, EP_WALKABLE_INSIDE },
4653 { ep_walkable_under, EP_WALKABLE_UNDER },
4654 { ep_passable_over, EP_PASSABLE_OVER },
4655 { ep_passable_inside, EP_PASSABLE_INSIDE },
4656 { ep_passable_under, EP_PASSABLE_UNDER },
4657 { ep_droppable, EP_DROPPABLE },
4658 { ep_explodes_1x1_old, EP_EXPLODES_1X1_OLD },
4659 { ep_pushable, EP_PUSHABLE },
4660 { ep_explodes_cross_old, EP_EXPLODES_CROSS_OLD },
4661 { ep_protected, EP_PROTECTED },
4662 { ep_throwable, EP_THROWABLE },
4663 { ep_can_explode, EP_CAN_EXPLODE },
4664 { ep_gravity_reachable, EP_GRAVITY_REACHABLE },
4666 { ep_empty_space, EP_EMPTY_SPACE },
4667 { ep_player, EP_PLAYER },
4668 { ep_can_pass_magic_wall, EP_CAN_PASS_MAGIC_WALL },
4669 { ep_can_pass_dc_magic_wall, EP_CAN_PASS_DC_MAGIC_WALL },
4670 { ep_switchable, EP_SWITCHABLE },
4671 { ep_bd_element, EP_BD_ELEMENT },
4672 { ep_sp_element, EP_SP_ELEMENT },
4673 { ep_sb_element, EP_SB_ELEMENT },
4675 { ep_food_dark_yamyam, EP_FOOD_DARK_YAMYAM },
4676 { ep_food_penguin, EP_FOOD_PENGUIN },
4677 { ep_food_pig, EP_FOOD_PIG },
4678 { ep_historic_wall, EP_HISTORIC_WALL },
4679 { ep_historic_solid, EP_HISTORIC_SOLID },
4680 { ep_classic_enemy, EP_CLASSIC_ENEMY },
4681 { ep_belt, EP_BELT },
4682 { ep_belt_active, EP_BELT_ACTIVE },
4683 { ep_belt_switch, EP_BELT_SWITCH },
4684 { ep_tube, EP_TUBE },
4685 { ep_acid_pool, EP_ACID_POOL },
4686 { ep_keygate, EP_KEYGATE },
4687 { ep_amoeboid, EP_AMOEBOID },
4688 { ep_amoebalive, EP_AMOEBALIVE },
4689 { ep_has_editor_content, EP_HAS_EDITOR_CONTENT },
4690 { ep_can_turn_each_move, EP_CAN_TURN_EACH_MOVE },
4691 { ep_can_grow, EP_CAN_GROW },
4692 { ep_active_bomb, EP_ACTIVE_BOMB },
4693 { ep_inactive, EP_INACTIVE },
4695 { ep_em_slippery_wall, EP_EM_SLIPPERY_WALL },
4697 { ep_gfx_crumbled, EP_GFX_CRUMBLED },
4699 { ep_editor_cascade_active, EP_EDITOR_CASCADE_ACTIVE },
4700 { ep_editor_cascade_inactive, EP_EDITOR_CASCADE_INACTIVE },
4702 { ep_obsolete, EP_OBSOLETE },
4709 // always start with reliable default values (element has no properties)
4710 // (but never initialize clipboard elements after the very first time)
4711 // (to be able to use clipboard elements between several levels)
4712 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4713 if (!IS_CLIPBOARD_ELEMENT(i) || !clipboard_elements_initialized)
4714 for (j = 0; j < NUM_ELEMENT_PROPERTIES; j++)
4715 SET_PROPERTY(i, j, FALSE);
4717 // set all base element properties from above array definitions
4718 for (i = 0; element_properties[i].elements != NULL; i++)
4719 for (j = 0; (element_properties[i].elements)[j] != -1; j++)
4720 SET_PROPERTY((element_properties[i].elements)[j],
4721 element_properties[i].property, TRUE);
4723 // copy properties to some elements that are only stored in level file
4724 for (i = 0; i < NUM_ELEMENT_PROPERTIES; i++)
4725 for (j = 0; copy_properties[j][0] != -1; j++)
4726 if (HAS_PROPERTY(copy_properties[j][0], i))
4727 for (k = 1; k <= 4; k++)
4728 SET_PROPERTY(copy_properties[j][k], i, TRUE);
4730 // set static element properties that are not listed in array definitions
4731 for (i = EL_STEEL_CHAR_START; i <= EL_STEEL_CHAR_END; i++)
4732 SET_PROPERTY(i, EP_INDESTRUCTIBLE, TRUE);
4734 clipboard_elements_initialized = TRUE;
4737 void InitElementPropertiesEngine(int engine_version)
4739 static int no_wall_properties[] =
4742 EP_COLLECTIBLE_ONLY,
4744 EP_DONT_COLLIDE_WITH,
4747 EP_CAN_SMASH_PLAYER,
4748 EP_CAN_SMASH_ENEMIES,
4749 EP_CAN_SMASH_EVERYTHING,
4754 EP_FOOD_DARK_YAMYAM,
4770 /* important: after initialization in InitElementPropertiesStatic(), the
4771 elements are not again initialized to a default value; therefore all
4772 changes have to make sure that they leave the element with a defined
4773 property (which means that conditional property changes must be set to
4774 a reliable default value before) */
4776 // resolve group elements
4777 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
4778 ResolveGroupElement(EL_GROUP_START + i);
4780 // set all special, combined or engine dependent element properties
4781 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4783 // do not change (already initialized) clipboard elements here
4784 if (IS_CLIPBOARD_ELEMENT(i))
4787 // ---------- INACTIVE ----------------------------------------------------
4788 SET_PROPERTY(i, EP_INACTIVE, ((i >= EL_CHAR_START &&
4789 i <= EL_CHAR_END) ||
4790 (i >= EL_STEEL_CHAR_START &&
4791 i <= EL_STEEL_CHAR_END)));
4793 // ---------- WALKABLE, PASSABLE, ACCESSIBLE ------------------------------
4794 SET_PROPERTY(i, EP_WALKABLE, (IS_WALKABLE_OVER(i) ||
4795 IS_WALKABLE_INSIDE(i) ||
4796 IS_WALKABLE_UNDER(i)));
4798 SET_PROPERTY(i, EP_PASSABLE, (IS_PASSABLE_OVER(i) ||
4799 IS_PASSABLE_INSIDE(i) ||
4800 IS_PASSABLE_UNDER(i)));
4802 SET_PROPERTY(i, EP_ACCESSIBLE_OVER, (IS_WALKABLE_OVER(i) ||
4803 IS_PASSABLE_OVER(i)));
4805 SET_PROPERTY(i, EP_ACCESSIBLE_INSIDE, (IS_WALKABLE_INSIDE(i) ||
4806 IS_PASSABLE_INSIDE(i)));
4808 SET_PROPERTY(i, EP_ACCESSIBLE_UNDER, (IS_WALKABLE_UNDER(i) ||
4809 IS_PASSABLE_UNDER(i)));
4811 SET_PROPERTY(i, EP_ACCESSIBLE, (IS_WALKABLE(i) ||
4814 // ---------- COLLECTIBLE -------------------------------------------------
4815 SET_PROPERTY(i, EP_COLLECTIBLE, (IS_COLLECTIBLE_ONLY(i) ||
4819 // ---------- SNAPPABLE ---------------------------------------------------
4820 SET_PROPERTY(i, EP_SNAPPABLE, (IS_DIGGABLE(i) ||
4821 IS_COLLECTIBLE(i) ||
4825 // ---------- WALL --------------------------------------------------------
4826 SET_PROPERTY(i, EP_WALL, TRUE); // default: element is wall
4828 for (j = 0; no_wall_properties[j] != -1; j++)
4829 if (HAS_PROPERTY(i, no_wall_properties[j]) ||
4830 i >= EL_FIRST_RUNTIME_UNREAL)
4831 SET_PROPERTY(i, EP_WALL, FALSE);
4833 if (IS_HISTORIC_WALL(i))
4834 SET_PROPERTY(i, EP_WALL, TRUE);
4836 // ---------- SOLID_FOR_PUSHING -------------------------------------------
4837 if (engine_version < VERSION_IDENT(2,2,0,0))
4838 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, IS_HISTORIC_SOLID(i));
4840 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, (!IS_WALKABLE(i) &&
4842 !IS_COLLECTIBLE(i)));
4844 // ---------- DRAGONFIRE_PROOF --------------------------------------------
4845 if (IS_HISTORIC_SOLID(i) || i == EL_EXPLOSION)
4846 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, TRUE);
4848 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, (IS_INDESTRUCTIBLE(i) &&
4851 // ---------- EXPLOSION_PROOF ---------------------------------------------
4853 SET_PROPERTY(i, EP_EXPLOSION_PROOF, TRUE);
4854 else if (engine_version < VERSION_IDENT(2,2,0,0))
4855 SET_PROPERTY(i, EP_EXPLOSION_PROOF, IS_INDESTRUCTIBLE(i));
4857 SET_PROPERTY(i, EP_EXPLOSION_PROOF, (IS_INDESTRUCTIBLE(i) &&
4861 if (IS_CUSTOM_ELEMENT(i))
4863 // these are additional properties which are initially false when set
4865 // ---------- DONT_COLLIDE_WITH / DONT_RUN_INTO -------------------------
4867 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH, TRUE);
4868 if (DONT_COLLIDE_WITH(i))
4869 SET_PROPERTY(i, EP_DONT_RUN_INTO, TRUE);
4871 // ---------- CAN_SMASH_ENEMIES / CAN_SMASH_PLAYER ----------------------
4872 if (CAN_SMASH_EVERYTHING(i))
4873 SET_PROPERTY(i, EP_CAN_SMASH_ENEMIES, TRUE);
4874 if (CAN_SMASH_ENEMIES(i))
4875 SET_PROPERTY(i, EP_CAN_SMASH_PLAYER, TRUE);
4878 // ---------- CAN_SMASH ---------------------------------------------------
4879 SET_PROPERTY(i, EP_CAN_SMASH, (CAN_SMASH_PLAYER(i) ||
4880 CAN_SMASH_ENEMIES(i) ||
4881 CAN_SMASH_EVERYTHING(i)));
4883 // ---------- CAN_EXPLODE_BY_FIRE -----------------------------------------
4884 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_FIRE, (CAN_EXPLODE(i) &&
4885 EXPLODES_BY_FIRE(i)));
4887 // ---------- CAN_EXPLODE_SMASHED -----------------------------------------
4888 SET_PROPERTY(i, EP_CAN_EXPLODE_SMASHED, (CAN_EXPLODE(i) &&
4889 EXPLODES_SMASHED(i)));
4891 // ---------- CAN_EXPLODE_IMPACT ------------------------------------------
4892 SET_PROPERTY(i, EP_CAN_EXPLODE_IMPACT, (CAN_EXPLODE(i) &&
4893 EXPLODES_IMPACT(i)));
4895 // ---------- CAN_EXPLODE_BY_DRAGONFIRE -----------------------------------
4896 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_DRAGONFIRE, CAN_EXPLODE_BY_FIRE(i));
4898 // ---------- CAN_EXPLODE_BY_EXPLOSION ------------------------------------
4899 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_EXPLOSION, (CAN_EXPLODE_BY_FIRE(i) ||
4900 i == EL_BLACK_ORB));
4902 // ---------- COULD_MOVE_INTO_ACID ----------------------------------------
4903 SET_PROPERTY(i, EP_COULD_MOVE_INTO_ACID, (IS_PLAYER_ELEMENT(i) ||
4905 IS_CUSTOM_ELEMENT(i)));
4907 // ---------- MAYBE_DONT_COLLIDE_WITH -------------------------------------
4908 SET_PROPERTY(i, EP_MAYBE_DONT_COLLIDE_WITH, (i == EL_SP_SNIKSNAK ||
4909 i == EL_SP_ELECTRON));
4911 // ---------- CAN_MOVE_INTO_ACID ------------------------------------------
4912 if (COULD_MOVE_INTO_ACID(i) && !IS_CUSTOM_ELEMENT(i))
4913 SET_PROPERTY(i, EP_CAN_MOVE_INTO_ACID,
4914 getMoveIntoAcidProperty(&level, i));
4916 // ---------- DONT_COLLIDE_WITH -------------------------------------------
4917 if (MAYBE_DONT_COLLIDE_WITH(i))
4918 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH,
4919 getDontCollideWithProperty(&level, i));
4921 // ---------- SP_PORT -----------------------------------------------------
4922 SET_PROPERTY(i, EP_SP_PORT, (IS_SP_ELEMENT(i) &&
4923 IS_PASSABLE_INSIDE(i)));
4925 // ---------- CAN_BE_CLONED_BY_ANDROID ------------------------------------
4926 for (j = 0; j < level.num_android_clone_elements; j++)
4927 SET_PROPERTY(i, EP_CAN_BE_CLONED_BY_ANDROID,
4929 IS_EQUAL_OR_IN_GROUP(i, level.android_clone_element[j])));
4931 // ---------- CAN_CHANGE --------------------------------------------------
4932 SET_PROPERTY(i, EP_CAN_CHANGE, FALSE); // default: cannot change
4933 for (j = 0; j < element_info[i].num_change_pages; j++)
4934 if (element_info[i].change_page[j].can_change)
4935 SET_PROPERTY(i, EP_CAN_CHANGE, TRUE);
4937 // ---------- HAS_ACTION --------------------------------------------------
4938 SET_PROPERTY(i, EP_HAS_ACTION, FALSE); // default: has no action
4939 for (j = 0; j < element_info[i].num_change_pages; j++)
4940 if (element_info[i].change_page[j].has_action)
4941 SET_PROPERTY(i, EP_HAS_ACTION, TRUE);
4943 // ---------- CAN_CHANGE_OR_HAS_ACTION ------------------------------------
4944 SET_PROPERTY(i, EP_CAN_CHANGE_OR_HAS_ACTION, (CAN_CHANGE(i) ||
4947 // ---------- GFX_CRUMBLED ------------------------------------------------
4948 SET_PROPERTY(i, EP_GFX_CRUMBLED,
4949 element_info[i].crumbled[ACTION_DEFAULT] !=
4950 element_info[i].graphic[ACTION_DEFAULT]);
4952 // ---------- EDITOR_CASCADE ----------------------------------------------
4953 SET_PROPERTY(i, EP_EDITOR_CASCADE, (IS_EDITOR_CASCADE_ACTIVE(i) ||
4954 IS_EDITOR_CASCADE_INACTIVE(i)));
4957 // dynamically adjust element properties according to game engine version
4959 static int ep_em_slippery_wall[] =
4964 EL_EXPANDABLE_WALL_HORIZONTAL,
4965 EL_EXPANDABLE_WALL_VERTICAL,
4966 EL_EXPANDABLE_WALL_ANY,
4967 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
4968 EL_EXPANDABLE_STEELWALL_VERTICAL,
4969 EL_EXPANDABLE_STEELWALL_ANY,
4970 EL_EXPANDABLE_STEELWALL_GROWING,
4974 static int ep_em_explodes_by_fire[] =
4977 EL_EM_DYNAMITE_ACTIVE,
4982 // special EM style gems behaviour
4983 for (i = 0; ep_em_slippery_wall[i] != -1; i++)
4984 SET_PROPERTY(ep_em_slippery_wall[i], EP_EM_SLIPPERY_WALL,
4985 level.em_slippery_gems);
4987 // "EL_EXPANDABLE_WALL_GROWING" wasn't slippery for EM gems in 2.0.1
4988 SET_PROPERTY(EL_EXPANDABLE_WALL_GROWING, EP_EM_SLIPPERY_WALL,
4989 (level.em_slippery_gems &&
4990 engine_version > VERSION_IDENT(2,0,1,0)));
4992 // special EM style explosion behaviour regarding chain reactions
4993 for (i = 0; ep_em_explodes_by_fire[i] != -1; i++)
4994 SET_PROPERTY(ep_em_explodes_by_fire[i], EP_EXPLODES_BY_FIRE,
4995 level.em_explodes_by_fire);
4998 // this is needed because some graphics depend on element properties
4999 if (game_status == GAME_MODE_PLAYING)
5000 InitElementGraphicInfo();
5003 void InitElementPropertiesGfxElement(void)
5007 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5009 struct ElementInfo *ei = &element_info[i];
5011 ei->gfx_element = (ei->use_gfx_element ? ei->gfx_element_initial : i);
5015 static void InitGlobal(void)
5020 for (i = 0; i < MAX_NUM_ELEMENTS + 1; i++)
5022 // check if element_name_info entry defined for each element in "main.h"
5023 if (i < MAX_NUM_ELEMENTS && element_name_info[i].token_name == NULL)
5024 Fail("undefined 'element_name_info' entry for element %d", i);
5026 element_info[i].token_name = element_name_info[i].token_name;
5027 element_info[i].class_name = element_name_info[i].class_name;
5028 element_info[i].editor_description= element_name_info[i].editor_description;
5031 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS + 1; i++)
5033 // check if global_anim_name_info defined for each entry in "main.h"
5034 if (i < NUM_GLOBAL_ANIM_TOKENS &&
5035 global_anim_name_info[i].token_name == NULL)
5036 Fail("undefined 'global_anim_name_info' entry for anim %d", i);
5038 global_anim_info[i].token_name = global_anim_name_info[i].token_name;
5041 // create hash to store URLs for global animations
5042 anim_url_hash = newSetupFileHash();
5044 // create hash from image config list
5045 image_config_hash = newSetupFileHash();
5046 for (i = 0; image_config[i].token != NULL; i++)
5047 setHashEntry(image_config_hash,
5048 image_config[i].token,
5049 image_config[i].value);
5051 // create hash from element token list
5052 element_token_hash = newSetupFileHash();
5053 for (i = 0; element_name_info[i].token_name != NULL; i++)
5054 setHashEntry(element_token_hash,
5055 element_name_info[i].token_name,
5058 // create hash from graphic token list
5059 graphic_token_hash = newSetupFileHash();
5060 for (graphic = 0, i = 0; image_config[i].token != NULL; i++)
5061 if (strSuffix(image_config[i].value, ".png") ||
5062 strSuffix(image_config[i].value, ".pcx") ||
5063 strSuffix(image_config[i].value, ".wav") ||
5064 strEqual(image_config[i].value, UNDEFINED_FILENAME))
5065 setHashEntry(graphic_token_hash,
5066 image_config[i].token,
5067 int2str(graphic++, 0));
5069 // create hash from font token list
5070 font_token_hash = newSetupFileHash();
5071 for (i = 0; font_info[i].token_name != NULL; i++)
5072 setHashEntry(font_token_hash,
5073 font_info[i].token_name,
5076 // set default filenames for all cloned graphics in static configuration
5077 for (i = 0; image_config[i].token != NULL; i++)
5079 if (strEqual(image_config[i].value, UNDEFINED_FILENAME))
5081 char *token = image_config[i].token;
5082 char *token_clone_from = getStringCat2(token, ".clone_from");
5083 char *token_cloned = getHashEntry(image_config_hash, token_clone_from);
5085 if (token_cloned != NULL)
5087 char *value_cloned = getHashEntry(image_config_hash, token_cloned);
5089 if (value_cloned != NULL)
5091 // set default filename in static configuration
5092 image_config[i].value = value_cloned;
5094 // set default filename in image config hash
5095 setHashEntry(image_config_hash, token, value_cloned);
5099 free(token_clone_from);
5103 // always start with reliable default values (all elements)
5104 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5105 ActiveElement[i] = i;
5107 // now add all entries that have an active state (active elements)
5108 for (i = 0; element_with_active_state[i].element != -1; i++)
5110 int element = element_with_active_state[i].element;
5111 int element_active = element_with_active_state[i].element_active;
5113 ActiveElement[element] = element_active;
5116 // always start with reliable default values (all buttons)
5117 for (i = 0; i < NUM_IMAGE_FILES; i++)
5118 ActiveButton[i] = i;
5120 // now add all entries that have an active state (active buttons)
5121 for (i = 0; button_with_active_state[i].button != -1; i++)
5123 int button = button_with_active_state[i].button;
5124 int button_active = button_with_active_state[i].button_active;
5126 ActiveButton[button] = button_active;
5129 // always start with reliable default values (all fonts)
5130 for (i = 0; i < NUM_FONTS; i++)
5133 // now add all entries that have an active state (active fonts)
5134 for (i = 0; font_with_active_state[i].font_nr != -1; i++)
5136 int font = font_with_active_state[i].font_nr;
5137 int font_active = font_with_active_state[i].font_nr_active;
5139 ActiveFont[font] = font_active;
5142 global.autoplay_leveldir = NULL;
5143 global.patchtapes_leveldir = NULL;
5144 global.convert_leveldir = NULL;
5145 global.dumplevel_leveldir = NULL;
5146 global.dumptape_leveldir = NULL;
5147 global.create_sketch_images_dir = NULL;
5148 global.create_collect_images_dir = NULL;
5150 global.frames_per_second = 0;
5151 global.show_frames_per_second = FALSE;
5153 global.border_status = GAME_MODE_LOADING;
5154 global.anim_status = global.anim_status_next = GAME_MODE_LOADING;
5156 global.use_envelope_request = FALSE;
5158 global.user_names = NULL;
5161 static void Execute_Command(char *command)
5165 if (strEqual(command, "print graphicsinfo.conf"))
5167 Print("# You can configure additional/alternative image files here.\n");
5168 Print("# (The entries below are default and therefore commented out.)\n");
5170 Print("%s\n", getFormattedSetupEntry("name", "Classic Graphics"));
5172 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5175 for (i = 0; image_config[i].token != NULL; i++)
5176 Print("# %s\n", getFormattedSetupEntry(image_config[i].token,
5177 image_config[i].value));
5181 else if (strEqual(command, "print soundsinfo.conf"))
5183 Print("# You can configure additional/alternative sound files here.\n");
5184 Print("# (The entries below are default and therefore commented out.)\n");
5186 Print("%s\n", getFormattedSetupEntry("name", "Classic Sounds"));
5188 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5191 for (i = 0; sound_config[i].token != NULL; i++)
5192 Print("# %s\n", getFormattedSetupEntry(sound_config[i].token,
5193 sound_config[i].value));
5197 else if (strEqual(command, "print musicinfo.conf"))
5199 Print("# You can configure additional/alternative music files here.\n");
5200 Print("# (The entries below are default and therefore commented out.)\n");
5202 Print("%s\n", getFormattedSetupEntry("name", "Classic Music"));
5204 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5207 for (i = 0; music_config[i].token != NULL; i++)
5208 Print("# %s\n", getFormattedSetupEntry(music_config[i].token,
5209 music_config[i].value));
5213 else if (strEqual(command, "print editorsetup.conf"))
5215 Print("# You can configure your personal editor element list here.\n");
5216 Print("# (The entries below are default and therefore commented out.)\n");
5219 // this is needed to be able to check element list for cascade elements
5220 InitElementPropertiesStatic();
5221 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
5223 PrintEditorElementList();
5227 else if (strEqual(command, "print helpanim.conf"))
5229 Print("# You can configure different element help animations here.\n");
5230 Print("# (The entries below are default and therefore commented out.)\n");
5233 for (i = 0; helpanim_config[i].token != NULL; i++)
5235 Print("# %s\n", getFormattedSetupEntry(helpanim_config[i].token,
5236 helpanim_config[i].value));
5238 if (strEqual(helpanim_config[i].token, "end"))
5244 else if (strEqual(command, "print helptext.conf"))
5246 Print("# You can configure different element help text here.\n");
5247 Print("# (The entries below are default and therefore commented out.)\n");
5250 for (i = 0; helptext_config[i].token != NULL; i++)
5251 Print("# %s\n", getFormattedSetupEntry(helptext_config[i].token,
5252 helptext_config[i].value));
5256 else if (strPrefix(command, "dump level "))
5258 char *filename = &command[11];
5260 if (fileExists(filename))
5262 LoadLevelFromFilename(&level, filename);
5268 char *leveldir = getStringCopy(filename); // read command parameters
5269 char *level_nr = strchr(leveldir, ' ');
5271 if (level_nr == NULL)
5272 Fail("cannot open file '%s'", filename);
5276 global.dumplevel_leveldir = leveldir;
5277 global.dumplevel_level_nr = atoi(level_nr);
5279 program.headless = TRUE;
5281 else if (strPrefix(command, "dump tape "))
5283 char *filename = &command[10];
5285 if (fileExists(filename))
5287 LoadTapeFromFilename(filename);
5293 char *leveldir = getStringCopy(filename); // read command parameters
5294 char *level_nr = strchr(leveldir, ' ');
5296 if (level_nr == NULL)
5297 Fail("cannot open file '%s'", filename);
5301 global.dumptape_leveldir = leveldir;
5302 global.dumptape_level_nr = atoi(level_nr);
5304 program.headless = TRUE;
5306 else if (strPrefix(command, "autoplay ") ||
5307 strPrefix(command, "autoffwd ") ||
5308 strPrefix(command, "autowarp ") ||
5309 strPrefix(command, "autotest ") ||
5310 strPrefix(command, "autosave ") ||
5311 strPrefix(command, "autoupload ") ||
5312 strPrefix(command, "autofix "))
5314 char *arg_ptr = strchr(command, ' ');
5315 char *str_ptr = getStringCopy(arg_ptr); // read command parameters
5317 global.autoplay_mode =
5318 (strPrefix(command, "autoplay") ? AUTOPLAY_MODE_PLAY :
5319 strPrefix(command, "autoffwd") ? AUTOPLAY_MODE_FFWD :
5320 strPrefix(command, "autowarp") ? AUTOPLAY_MODE_WARP :
5321 strPrefix(command, "autotest") ? AUTOPLAY_MODE_TEST :
5322 strPrefix(command, "autosave") ? AUTOPLAY_MODE_SAVE :
5323 strPrefix(command, "autoupload") ? AUTOPLAY_MODE_UPLOAD :
5324 strPrefix(command, "autofix") ? AUTOPLAY_MODE_FIX :
5325 AUTOPLAY_MODE_NONE);
5327 while (*str_ptr != '\0') // continue parsing string
5329 // cut leading whitespace from string, replace it by string terminator
5330 while (*str_ptr == ' ' || *str_ptr == '\t')
5333 if (*str_ptr == '\0') // end of string reached
5336 if (global.autoplay_leveldir == NULL) // read level set string
5338 global.autoplay_leveldir = str_ptr;
5339 global.autoplay_all = TRUE; // default: play all tapes
5341 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5342 global.autoplay_level[i] = FALSE;
5344 else // read level number string
5346 int level_nr = atoi(str_ptr); // get level_nr value
5348 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5349 global.autoplay_level[level_nr] = TRUE;
5351 global.autoplay_all = FALSE;
5354 // advance string pointer to the next whitespace (or end of string)
5355 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5359 if (global.autoplay_mode & AUTOPLAY_WARP_NO_DISPLAY)
5360 program.headless = TRUE;
5362 else if (strPrefix(command, "patch tapes "))
5364 char *str_ptr = getStringCopy(&command[12]); // read command parameters
5366 // skip leading whitespace
5367 while (*str_ptr == ' ' || *str_ptr == '\t')
5370 if (*str_ptr == '\0')
5371 Fail("cannot find MODE in command '%s'", command);
5373 global.patchtapes_mode = str_ptr; // store patch mode
5375 // advance to next whitespace (or end of string)
5376 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5379 while (*str_ptr != '\0') // continue parsing string
5381 // cut leading whitespace from string, replace it by string terminator
5382 while (*str_ptr == ' ' || *str_ptr == '\t')
5385 if (*str_ptr == '\0') // end of string reached
5388 if (global.patchtapes_leveldir == NULL) // read level set string
5390 global.patchtapes_leveldir = str_ptr;
5391 global.patchtapes_all = TRUE; // default: patch all tapes
5393 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5394 global.patchtapes_level[i] = FALSE;
5396 else // read level number string
5398 int level_nr = atoi(str_ptr); // get level_nr value
5400 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5401 global.patchtapes_level[level_nr] = TRUE;
5403 global.patchtapes_all = FALSE;
5406 // advance string pointer to the next whitespace (or end of string)
5407 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5411 if (global.patchtapes_leveldir == NULL)
5413 if (strEqual(global.patchtapes_mode, "help"))
5414 global.patchtapes_leveldir = UNDEFINED_LEVELSET;
5416 Fail("cannot find LEVELDIR in command '%s'", command);
5419 program.headless = TRUE;
5421 else if (strPrefix(command, "convert "))
5423 char *str_copy = getStringCopy(strchr(command, ' ') + 1);
5424 char *str_ptr = strchr(str_copy, ' ');
5426 global.convert_leveldir = str_copy;
5427 global.convert_level_nr = -1;
5429 if (str_ptr != NULL) // level number follows
5431 *str_ptr++ = '\0'; // terminate leveldir string
5432 global.convert_level_nr = atoi(str_ptr); // get level_nr value
5435 program.headless = TRUE;
5437 else if (strPrefix(command, "create sketch images "))
5439 global.create_sketch_images_dir = getStringCopy(&command[21]);
5441 if (access(global.create_sketch_images_dir, W_OK) != 0)
5442 Fail("image target directory '%s' not found or not writable",
5443 global.create_sketch_images_dir);
5445 else if (strPrefix(command, "create collect image "))
5447 global.create_collect_images_dir = getStringCopy(&command[21]);
5449 if (access(global.create_collect_images_dir, W_OK) != 0)
5450 Fail("image target directory '%s' not found or not writable",
5451 global.create_collect_images_dir);
5453 else if (strPrefix(command, "create CE image "))
5455 CreateCustomElementImages(&command[16]);
5461 FailWithHelp("unrecognized command '%s'", command);
5464 // disable networking if any valid command was recognized
5465 options.network = setup.network_mode = FALSE;
5468 static void InitSetup(void)
5470 LoadUserNames(); // global user names
5471 LoadUserSetup(); // global user number
5473 LoadSetup(); // global setup info
5475 // set some options from setup file
5477 if (setup.options.verbose)
5478 options.verbose = TRUE;
5480 if (setup.options.debug)
5481 options.debug = TRUE;
5483 if (!strEqual(setup.options.debug_mode, ARG_UNDEFINED_STRING))
5484 options.debug_mode = getStringCopy(setup.options.debug_mode);
5486 if (setup.debug.show_frames_per_second)
5487 global.show_frames_per_second = TRUE;
5490 static void InitGameInfo(void)
5492 game.restart_level = FALSE;
5494 game.request_active = FALSE;
5495 game.request_active_or_moving = FALSE;
5497 game.use_masked_elements_initial = FALSE;
5500 static void InitPlayerInfo(void)
5504 // choose default local player
5505 local_player = &stored_player[0];
5507 for (i = 0; i < MAX_PLAYERS; i++)
5509 stored_player[i].connected_locally = FALSE;
5510 stored_player[i].connected_network = FALSE;
5513 local_player->connected_locally = TRUE;
5516 static void InitArtworkInfo(void)
5521 static char *get_string_in_brackets(char *string)
5523 char *string_in_brackets = checked_malloc(strlen(string) + 3);
5525 sprintf(string_in_brackets, "[%s]", string);
5527 return string_in_brackets;
5530 static char *get_level_id_suffix(int id_nr)
5532 char *id_suffix = checked_malloc(1 + 3 + 1);
5534 if (id_nr < 0 || id_nr > 999)
5537 sprintf(id_suffix, ".%03d", id_nr);
5542 static void InitArtworkConfig(void)
5544 static char *image_id_prefix[MAX_NUM_ELEMENTS +
5546 NUM_GLOBAL_ANIM_TOKENS + 1];
5547 static char *sound_id_prefix[2 * MAX_NUM_ELEMENTS +
5548 NUM_GLOBAL_ANIM_TOKENS + 1];
5549 static char *music_id_prefix[NUM_MUSIC_PREFIXES +
5550 NUM_GLOBAL_ANIM_TOKENS + 1];
5551 static char *action_id_suffix[NUM_ACTIONS + 1];
5552 static char *direction_id_suffix[NUM_DIRECTIONS_FULL + 1];
5553 static char *special_id_suffix[NUM_SPECIAL_GFX_ARGS + 1];
5554 static char *level_id_suffix[MAX_LEVELS + 1];
5555 static char *dummy[1] = { NULL };
5556 static char *ignore_generic_tokens[] =
5561 "program_copyright",
5566 static char **ignore_image_tokens;
5567 static char **ignore_sound_tokens;
5568 static char **ignore_music_tokens;
5569 int num_ignore_generic_tokens;
5570 int num_ignore_image_tokens;
5571 int num_ignore_sound_tokens;
5572 int num_ignore_music_tokens;
5575 // dynamically determine list of generic tokens to be ignored
5576 num_ignore_generic_tokens = 0;
5577 for (i = 0; ignore_generic_tokens[i] != NULL; i++)
5578 num_ignore_generic_tokens++;
5580 // dynamically determine list of image tokens to be ignored
5581 num_ignore_image_tokens = num_ignore_generic_tokens;
5582 for (i = 0; image_config_vars[i].token != NULL; i++)
5583 num_ignore_image_tokens++;
5584 ignore_image_tokens =
5585 checked_malloc((num_ignore_image_tokens + 1) * sizeof(char *));
5586 for (i = 0; i < num_ignore_generic_tokens; i++)
5587 ignore_image_tokens[i] = ignore_generic_tokens[i];
5588 for (i = 0; i < num_ignore_image_tokens - num_ignore_generic_tokens; i++)
5589 ignore_image_tokens[num_ignore_generic_tokens + i] =
5590 image_config_vars[i].token;
5591 ignore_image_tokens[num_ignore_image_tokens] = NULL;
5593 // dynamically determine list of sound tokens to be ignored
5594 num_ignore_sound_tokens = num_ignore_generic_tokens;
5595 ignore_sound_tokens =
5596 checked_malloc((num_ignore_sound_tokens + 1) * sizeof(char *));
5597 for (i = 0; i < num_ignore_generic_tokens; i++)
5598 ignore_sound_tokens[i] = ignore_generic_tokens[i];
5599 ignore_sound_tokens[num_ignore_sound_tokens] = NULL;
5601 // dynamically determine list of music tokens to be ignored
5602 num_ignore_music_tokens = num_ignore_generic_tokens;
5603 ignore_music_tokens =
5604 checked_malloc((num_ignore_music_tokens + 1) * sizeof(char *));
5605 for (i = 0; i < num_ignore_generic_tokens; i++)
5606 ignore_music_tokens[i] = ignore_generic_tokens[i];
5607 ignore_music_tokens[num_ignore_music_tokens] = NULL;
5609 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5610 image_id_prefix[i] = element_info[i].token_name;
5611 for (i = 0; i < NUM_FONTS; i++)
5612 image_id_prefix[MAX_NUM_ELEMENTS + i] = font_info[i].token_name;
5613 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5614 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + i] =
5615 global_anim_info[i].token_name;
5616 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5618 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5619 sound_id_prefix[i] = element_info[i].token_name;
5620 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5621 sound_id_prefix[MAX_NUM_ELEMENTS + i] =
5622 get_string_in_brackets(element_info[i].class_name);
5623 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5624 sound_id_prefix[2 * MAX_NUM_ELEMENTS + i] =
5625 global_anim_info[i].token_name;
5626 sound_id_prefix[2 * MAX_NUM_ELEMENTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5628 for (i = 0; i < NUM_MUSIC_PREFIXES; i++)
5629 music_id_prefix[i] = music_prefix_info[i].prefix;
5630 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5631 music_id_prefix[NUM_MUSIC_PREFIXES + i] =
5632 global_anim_info[i].token_name;
5633 music_id_prefix[NUM_MUSIC_PREFIXES + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5635 for (i = 0; i < NUM_ACTIONS; i++)
5636 action_id_suffix[i] = element_action_info[i].suffix;
5637 action_id_suffix[NUM_ACTIONS] = NULL;
5639 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
5640 direction_id_suffix[i] = element_direction_info[i].suffix;
5641 direction_id_suffix[NUM_DIRECTIONS_FULL] = NULL;
5643 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
5644 special_id_suffix[i] = special_suffix_info[i].suffix;
5645 special_id_suffix[NUM_SPECIAL_GFX_ARGS] = NULL;
5647 for (i = 0; i < MAX_LEVELS; i++)
5648 level_id_suffix[i] = get_level_id_suffix(i);
5649 level_id_suffix[MAX_LEVELS] = NULL;
5651 InitImageList(image_config, NUM_IMAGE_FILES, image_config_suffix,
5652 image_id_prefix, action_id_suffix, direction_id_suffix,
5653 special_id_suffix, ignore_image_tokens);
5654 InitSoundList(sound_config, NUM_SOUND_FILES, sound_config_suffix,
5655 sound_id_prefix, action_id_suffix, dummy,
5656 special_id_suffix, ignore_sound_tokens);
5657 InitMusicList(music_config, NUM_MUSIC_FILES, music_config_suffix,
5658 music_id_prefix, action_id_suffix, special_id_suffix,
5659 level_id_suffix, ignore_music_tokens);
5662 static void InitMixer(void)
5669 static void InitVideoOverlay(void)
5671 // if virtual buttons are not loaded from setup file, repeat initializing
5672 // virtual buttons grid with default values now that video is initialized
5673 if (!setup.touch.grid_initialized)
5676 InitTileCursorInfo();
5680 void InitGfxBuffers(void)
5682 static int win_xsize_last = -1;
5683 static int win_ysize_last = -1;
5685 // create additional image buffers for double-buffering and cross-fading
5687 if (WIN_XSIZE != win_xsize_last || WIN_YSIZE != win_ysize_last)
5689 // used to temporarily store the backbuffer -- only re-create if changed
5690 ReCreateBitmap(&bitmap_db_store_1, WIN_XSIZE, WIN_YSIZE);
5691 ReCreateBitmap(&bitmap_db_store_2, WIN_XSIZE, WIN_YSIZE);
5693 win_xsize_last = WIN_XSIZE;
5694 win_ysize_last = WIN_YSIZE;
5697 ReCreateBitmap(&bitmap_db_field, FXSIZE, FYSIZE);
5698 ReCreateBitmap(&bitmap_db_door_1, 3 * DXSIZE, DYSIZE);
5699 ReCreateBitmap(&bitmap_db_door_2, 3 * VXSIZE, VYSIZE);
5701 // initialize screen properties
5702 InitGfxFieldInfo(SX, SY, SXSIZE, SYSIZE,
5703 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
5705 InitGfxDoor1Info(DX, DY, DXSIZE, DYSIZE);
5706 InitGfxDoor2Info(VX, VY, VXSIZE, VYSIZE);
5707 InitGfxDoor3Info(EX, EY, EXSIZE, EYSIZE);
5708 InitGfxWindowInfo(WIN_XSIZE, WIN_YSIZE);
5709 InitGfxScrollbufferInfo(FXSIZE, FYSIZE);
5710 InitGfxClipRegion(FALSE, -1, -1, -1, -1);
5712 // required if door size definitions have changed
5713 InitGraphicCompatibilityInfo_Doors();
5715 InitGfxBuffers_EM();
5716 InitGfxBuffers_SP();
5717 InitGfxBuffers_MM();
5720 static void InitGfx(void)
5722 struct GraphicInfo *graphic_info_last = graphic_info;
5723 char *filename_font_initial = NULL;
5724 char *filename_image_initial[NUM_INITIAL_IMAGES] = { NULL };
5725 char *image_token[NUM_INITIAL_IMAGES] =
5727 CONFIG_TOKEN_GLOBAL_BUSY_INITIAL,
5728 CONFIG_TOKEN_GLOBAL_BUSY,
5729 CONFIG_TOKEN_GLOBAL_BUSY_PLAYFIELD,
5730 CONFIG_TOKEN_BACKGROUND,
5731 CONFIG_TOKEN_BACKGROUND_LOADING_INITIAL,
5732 CONFIG_TOKEN_BACKGROUND_LOADING
5734 struct MenuPosInfo *init_busy[NUM_INITIAL_IMAGES_BUSY] =
5738 &init.busy_playfield
5740 Bitmap *bitmap_font_initial = NULL;
5741 int parameter[NUM_INITIAL_IMAGES][NUM_GFX_ARGS];
5744 // determine settings for initial font (for displaying startup messages)
5745 for (i = 0; image_config[i].token != NULL; i++)
5747 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5749 char font_token[128];
5752 sprintf(font_token, "%s_%d", CONFIG_TOKEN_FONT_INITIAL, j + 1);
5753 len_font_token = strlen(font_token);
5755 if (strEqual(image_config[i].token, font_token))
5757 filename_font_initial = image_config[i].value;
5759 else if (strlen(image_config[i].token) > len_font_token &&
5760 strncmp(image_config[i].token, font_token, len_font_token) == 0)
5762 if (strEqual(&image_config[i].token[len_font_token], ".x"))
5763 font_initial[j].src_x = atoi(image_config[i].value);
5764 else if (strEqual(&image_config[i].token[len_font_token], ".y"))
5765 font_initial[j].src_y = atoi(image_config[i].value);
5766 else if (strEqual(&image_config[i].token[len_font_token], ".width"))
5767 font_initial[j].width = atoi(image_config[i].value);
5768 else if (strEqual(&image_config[i].token[len_font_token], ".height"))
5769 font_initial[j].height = atoi(image_config[i].value);
5774 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5776 font_initial[j].num_chars = DEFAULT_NUM_CHARS_PER_FONT;
5777 font_initial[j].num_chars_per_line = DEFAULT_NUM_CHARS_PER_LINE;
5780 if (filename_font_initial == NULL) // should not happen
5781 Fail("cannot get filename for '%s'", CONFIG_TOKEN_FONT_INITIAL);
5784 InitGfxCustomArtworkInfo();
5785 InitGfxOtherSettings();
5787 InitGfxTileSizeInfo(TILESIZE, TILESIZE);
5789 bitmap_font_initial = LoadCustomImage(filename_font_initial);
5791 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5792 font_initial[j].bitmap = bitmap_font_initial;
5794 InitFontGraphicInfo();
5796 InitMenuDesignSettings_Static();
5798 // initialize settings for initial images with default values
5799 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5800 for (j = 0; j < NUM_GFX_ARGS; j++)
5802 get_graphic_parameter_value(image_config_suffix[j].value,
5803 image_config_suffix[j].token,
5804 image_config_suffix[j].type);
5806 // read settings for initial images from default custom artwork config
5807 char *gfx_config_filename = getPath3(options.graphics_directory,
5809 GRAPHICSINFO_FILENAME);
5811 if (fileExists(gfx_config_filename))
5813 SetupFileHash *setup_file_hash = loadSetupFileHash(gfx_config_filename);
5815 if (setup_file_hash)
5817 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5819 char *filename = getHashEntry(setup_file_hash, image_token[i]);
5823 filename_image_initial[i] = getStringCopy(filename);
5825 for (j = 0; image_config_suffix[j].token != NULL; j++)
5827 int type = image_config_suffix[j].type;
5828 char *suffix = image_config_suffix[j].token;
5829 char *token = getStringCat2(image_token[i], suffix);
5830 char *value = getHashEntry(setup_file_hash, token);
5832 checked_free(token);
5836 get_graphic_parameter_value(value, suffix, type);
5841 // read values from custom graphics config file
5842 InitMenuDesignSettings_FromHash(setup_file_hash, FALSE);
5844 freeSetupFileHash(setup_file_hash);
5848 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5850 if (filename_image_initial[i] == NULL)
5852 int len_token = strlen(image_token[i]);
5854 // read settings for initial images from static default artwork config
5855 for (j = 0; image_config[j].token != NULL; j++)
5857 if (strEqual(image_config[j].token, image_token[i]))
5859 filename_image_initial[i] = getStringCopy(image_config[j].value);
5861 else if (strlen(image_config[j].token) > len_token &&
5862 strncmp(image_config[j].token, image_token[i], len_token) == 0)
5864 for (k = 0; image_config_suffix[k].token != NULL; k++)
5866 if (strEqual(&image_config[j].token[len_token],
5867 image_config_suffix[k].token))
5869 get_graphic_parameter_value(image_config[j].value,
5870 image_config_suffix[k].token,
5871 image_config_suffix[k].type);
5878 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5880 if (filename_image_initial[i] == NULL) // should not happen
5881 Fail("cannot get filename for '%s'", image_token[i]);
5883 image_initial[i].bitmaps =
5884 checked_calloc(sizeof(Bitmap *) * NUM_IMG_BITMAP_POINTERS);
5886 if (!strEqual(filename_image_initial[i], UNDEFINED_FILENAME))
5887 image_initial[i].bitmaps[IMG_BITMAP_STANDARD] =
5888 LoadCustomImage(filename_image_initial[i]);
5890 checked_free(filename_image_initial[i]);
5893 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5894 image_initial[i].use_image_size = TRUE;
5896 graphic_info = image_initial; // graphic == 0 => image_initial
5898 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5899 set_graphic_parameters_ext(i, parameter[i], image_initial[i].bitmaps);
5901 graphic_info = graphic_info_last;
5903 for (i = 0; i < NUM_INITIAL_IMAGES_BUSY; i++)
5905 // set image size for busy animations
5906 init_busy[i]->width = image_initial[i].width;
5907 init_busy[i]->height = image_initial[i].height;
5910 SetLoadingBackgroundImage();
5912 ClearRectangleOnBackground(window, 0, 0, WIN_XSIZE, WIN_YSIZE);
5916 InitGfxDrawBusyAnimFunction(DrawInitAnim);
5917 InitGfxDrawGlobalAnimFunction(DrawGlobalAnimations);
5918 InitGfxDrawGlobalBorderFunction(DrawMaskedBorderToTarget);
5919 InitGfxDrawTileCursorFunction(DrawTileCursor);
5921 gfx.fade_border_source_status = global.border_status;
5922 gfx.fade_border_target_status = global.border_status;
5923 gfx.masked_border_bitmap_ptr = backbuffer;
5925 // use copy of busy animation to prevent change while reloading artwork
5929 static void InitGfxBackground(void)
5931 fieldbuffer = bitmap_db_field;
5932 SetDrawtoField(DRAW_TO_BACKBUFFER);
5934 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
5936 redraw_mask = REDRAW_ALL;
5939 static void InitLevelInfo(void)
5941 LoadLevelInfo(); // global level info
5942 LoadLevelSetup_LastSeries(); // last played series info
5943 LoadLevelSetup_SeriesInfo(); // last played level info
5945 if (global.autoplay_leveldir &&
5946 global.autoplay_mode != AUTOPLAY_MODE_TEST)
5948 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
5949 global.autoplay_leveldir);
5950 if (leveldir_current == NULL)
5951 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
5954 SetLevelSetInfo(leveldir_current->identifier, level_nr);
5957 static void InitLevelArtworkInfo(void)
5959 LoadLevelArtworkInfo();
5962 static void InitImages(void)
5964 print_timestamp_init("InitImages");
5967 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
5968 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5969 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
5970 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5971 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
5972 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5973 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
5974 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5977 setLevelArtworkDir(artwork.gfx_first);
5980 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
5981 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5982 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
5983 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5984 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
5985 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5986 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
5987 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5991 Debug("init:InitImages", "InitImages for '%s' ['%s', '%s'] ['%s', '%s']",
5992 leveldir_current->identifier,
5993 artwork.gfx_current_identifier,
5994 artwork.gfx_current->identifier,
5995 leveldir_current->graphics_set,
5996 leveldir_current->graphics_path);
5999 UPDATE_BUSY_STATE();
6001 ReloadCustomImages();
6002 print_timestamp_time("ReloadCustomImages");
6004 UPDATE_BUSY_STATE();
6006 LoadCustomElementDescriptions();
6007 print_timestamp_time("LoadCustomElementDescriptions");
6009 UPDATE_BUSY_STATE();
6011 LoadMenuDesignSettings();
6012 print_timestamp_time("LoadMenuDesignSettings");
6014 UPDATE_BUSY_STATE();
6016 ReinitializeGraphics();
6017 print_timestamp_time("ReinitializeGraphics");
6019 LoadMenuDesignSettings_AfterGraphics();
6020 print_timestamp_time("LoadMenuDesignSettings_AfterGraphics");
6022 UPDATE_BUSY_STATE();
6024 print_timestamp_done("InitImages");
6027 static void InitSound(void)
6029 print_timestamp_init("InitSound");
6031 // set artwork path to send it to the sound server process
6032 setLevelArtworkDir(artwork.snd_first);
6034 InitReloadCustomSounds();
6035 print_timestamp_time("InitReloadCustomSounds");
6037 ReinitializeSounds();
6038 print_timestamp_time("ReinitializeSounds");
6040 print_timestamp_done("InitSound");
6043 static void InitMusic(void)
6045 print_timestamp_init("InitMusic");
6047 // set artwork path to send it to the sound server process
6048 setLevelArtworkDir(artwork.mus_first);
6050 InitReloadCustomMusic();
6051 print_timestamp_time("InitReloadCustomMusic");
6053 ReinitializeMusic();
6054 print_timestamp_time("ReinitializeMusic");
6056 print_timestamp_done("InitMusic");
6059 static void InitArtworkDone(void)
6061 if (program.headless)
6064 InitGlobalAnimations();
6067 static void InitNetworkSettings(void)
6069 boolean network_enabled = (options.network || setup.network_mode);
6070 char *network_server = (options.server_host != NULL ? options.server_host :
6071 setup.network_server_hostname);
6073 if (strEqual(network_server, STR_NETWORK_AUTO_DETECT))
6074 network_server = NULL;
6076 InitNetworkInfo(network_enabled,
6080 options.server_port);
6083 void InitNetworkServer(void)
6085 if (!network.enabled || network.connected)
6088 LimitScreenUpdates(FALSE);
6090 if (game_status == GAME_MODE_LOADING)
6093 if (!ConnectToServer(network.server_host, network.server_port))
6095 network.enabled = FALSE;
6097 setup.network_mode = FALSE;
6101 SendToServer_ProtocolVersion();
6102 SendToServer_PlayerName(setup.player_name);
6103 SendToServer_NrWanted(setup.network_player_nr + 1);
6105 network.connected = TRUE;
6108 // short time to recognize result of network initialization
6109 if (game_status == GAME_MODE_LOADING)
6110 Delay_WithScreenUpdates(1000);
6113 static boolean CheckArtworkConfigForCustomElements(char *filename)
6115 SetupFileHash *setup_file_hash;
6116 boolean redefined_ce_found = FALSE;
6118 // !!! CACHE THIS BY USING HASH 'filename' => 'true/false' !!!
6120 if ((setup_file_hash = loadSetupFileHash(filename)) != NULL)
6122 BEGIN_HASH_ITERATION(setup_file_hash, itr)
6124 char *token = HASH_ITERATION_TOKEN(itr);
6126 if (strPrefix(token, "custom_"))
6128 redefined_ce_found = TRUE;
6133 END_HASH_ITERATION(setup_file_hash, itr)
6135 freeSetupFileHash(setup_file_hash);
6138 return redefined_ce_found;
6141 static boolean CheckArtworkTypeForRedefinedCustomElements(int type)
6143 char *filename_base, *filename_local;
6144 boolean redefined_ce_found = FALSE;
6146 setLevelArtworkDir(ARTWORK_FIRST_NODE(artwork, type));
6149 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6150 "leveldir_current->identifier == '%s'",
6151 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
6152 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6153 "leveldir_current->graphics_path == '%s'",
6154 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
6155 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6156 "leveldir_current->graphics_set == '%s'",
6157 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
6158 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6159 "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
6160 leveldir_current == NULL ? "[NULL]" :
6161 LEVELDIR_ARTWORK_SET(leveldir_current, type));
6164 // first look for special artwork configured in level series config
6165 filename_base = getCustomArtworkLevelConfigFilename(type);
6168 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6169 "filename_base == '%s'", filename_base);
6172 if (fileExists(filename_base))
6173 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_base);
6175 filename_local = getCustomArtworkConfigFilename(type);
6178 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6179 "filename_local == '%s'", filename_local);
6182 if (filename_local != NULL && !strEqual(filename_base, filename_local))
6183 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_local);
6186 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6187 "redefined_ce_found == %d", redefined_ce_found);
6190 return redefined_ce_found;
6193 static void InitOverrideArtwork(void)
6195 boolean redefined_ce_found = FALSE;
6197 // to check if this level set redefines any CEs, do not use overriding
6198 gfx.override_level_graphics = FALSE;
6199 gfx.override_level_sounds = FALSE;
6200 gfx.override_level_music = FALSE;
6202 // now check if this level set has definitions for custom elements
6203 if (setup.override_level_graphics == AUTO ||
6204 setup.override_level_sounds == AUTO ||
6205 setup.override_level_music == AUTO)
6206 redefined_ce_found =
6207 (CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_GRAPHICS) |
6208 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_SOUNDS) |
6209 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_MUSIC));
6212 Debug("init:InitOverrideArtwork", "redefined_ce_found == %d",
6213 redefined_ce_found);
6216 if (redefined_ce_found)
6218 // this level set has CE definitions: change "AUTO" to "FALSE"
6219 gfx.override_level_graphics = (setup.override_level_graphics == TRUE);
6220 gfx.override_level_sounds = (setup.override_level_sounds == TRUE);
6221 gfx.override_level_music = (setup.override_level_music == TRUE);
6225 // this level set has no CE definitions: change "AUTO" to "TRUE"
6226 gfx.override_level_graphics = (setup.override_level_graphics != FALSE);
6227 gfx.override_level_sounds = (setup.override_level_sounds != FALSE);
6228 gfx.override_level_music = (setup.override_level_music != FALSE);
6232 Debug("init:InitOverrideArtwork", "%d, %d, %d",
6233 gfx.override_level_graphics,
6234 gfx.override_level_sounds,
6235 gfx.override_level_music);
6239 static char *setNewArtworkIdentifier(int type)
6241 static char *last_leveldir_identifier[3] = { NULL, NULL, NULL };
6242 static char *last_artwork_identifier[3] = { NULL, NULL, NULL };
6243 static boolean last_override_level_artwork[3] = { FALSE, FALSE, FALSE };
6244 static boolean last_has_custom_artwork_set[3] = { FALSE, FALSE, FALSE };
6245 static boolean initialized[3] = { FALSE, FALSE, FALSE };
6246 TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type);
6247 boolean setup_override_artwork = GFX_OVERRIDE_ARTWORK(type);
6248 char *setup_artwork_set = SETUP_ARTWORK_SET(setup, type);
6249 char *leveldir_identifier = leveldir_current->identifier;
6250 // !!! setLevelArtworkDir() should be moved to an earlier stage !!!
6251 char *leveldir_artwork_set = setLevelArtworkDir(artwork_first_node);
6252 boolean has_level_artwork_set = (leveldir_artwork_set != NULL);
6253 TreeInfo *custom_artwork_set =
6254 getTreeInfoFromIdentifier(artwork_first_node, leveldir_identifier);
6255 boolean has_custom_artwork_set = (custom_artwork_set != NULL);
6256 char *artwork_current_identifier;
6257 char *artwork_new_identifier = NULL; // default: nothing has changed
6259 // leveldir_current may be invalid (level group, parent link)
6260 if (!validLevelSeries(leveldir_current))
6263 /* 1st step: determine artwork set to be activated in descending order:
6264 --------------------------------------------------------------------
6265 1. setup artwork (when configured to override everything else)
6266 2. artwork set configured in "levelinfo.conf" of current level set
6267 (artwork in level directory will have priority when loading later)
6268 3. artwork in level directory (stored in artwork sub-directory)
6269 4. setup artwork (currently configured in setup menu) */
6271 if (setup_override_artwork)
6272 artwork_current_identifier = setup_artwork_set;
6273 else if (has_level_artwork_set)
6274 artwork_current_identifier = leveldir_artwork_set;
6275 else if (has_custom_artwork_set)
6276 artwork_current_identifier = leveldir_identifier;
6278 artwork_current_identifier = setup_artwork_set;
6280 /* 2nd step: check if it is really needed to reload artwork set
6281 ------------------------------------------------------------ */
6283 // ---------- reload if level set and also artwork set has changed ----------
6284 if (last_leveldir_identifier[type] != leveldir_identifier &&
6285 (last_has_custom_artwork_set[type] || has_custom_artwork_set))
6286 artwork_new_identifier = artwork_current_identifier;
6288 last_leveldir_identifier[type] = leveldir_identifier;
6289 last_has_custom_artwork_set[type] = has_custom_artwork_set;
6291 // ---------- reload if "override artwork" setting has changed --------------
6292 if (last_override_level_artwork[type] != setup_override_artwork)
6293 artwork_new_identifier = artwork_current_identifier;
6295 last_override_level_artwork[type] = setup_override_artwork;
6297 // ---------- reload if current artwork identifier has changed --------------
6298 if (!strEqual(last_artwork_identifier[type], artwork_current_identifier))
6299 artwork_new_identifier = artwork_current_identifier;
6301 // (we cannot compare string pointers here, so copy string content itself)
6302 setString(&last_artwork_identifier[type], artwork_current_identifier);
6304 // ---------- set new artwork identifier ----------
6305 *(ARTWORK_CURRENT_IDENTIFIER_PTR(artwork, type)) = artwork_current_identifier;
6307 // ---------- do not reload directly after starting -------------------------
6308 if (!initialized[type])
6309 artwork_new_identifier = NULL;
6311 initialized[type] = TRUE;
6313 return artwork_new_identifier;
6316 static void InitArtworkIdentifier(void)
6318 setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6319 setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6320 setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6323 void ReloadCustomArtwork(int force_reload)
6325 int last_game_status = game_status; // save current game status
6326 char *gfx_new_identifier;
6327 char *snd_new_identifier;
6328 char *mus_new_identifier;
6329 boolean force_reload_gfx = (force_reload & (1 << ARTWORK_TYPE_GRAPHICS));
6330 boolean force_reload_snd = (force_reload & (1 << ARTWORK_TYPE_SOUNDS));
6331 boolean force_reload_mus = (force_reload & (1 << ARTWORK_TYPE_MUSIC));
6332 boolean reload_needed;
6334 InitOverrideArtwork();
6336 AdjustGraphicsForEMC();
6337 AdjustSoundsForEMC();
6339 gfx_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6340 snd_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6341 mus_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6343 reload_needed = (gfx_new_identifier != NULL || force_reload_gfx ||
6344 snd_new_identifier != NULL || force_reload_snd ||
6345 mus_new_identifier != NULL || force_reload_mus);
6350 print_timestamp_init("ReloadCustomArtwork");
6352 SetGameStatus(GAME_MODE_LOADING);
6354 FadeOut(REDRAW_ALL);
6356 SetLoadingBackgroundImage();
6358 ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6359 print_timestamp_time("ClearRectangleOnBackground");
6363 UPDATE_BUSY_STATE();
6365 InitMissingFileHash();
6367 if (gfx_new_identifier != NULL || force_reload_gfx)
6370 Debug("init:ReloadCustomArtwork",
6371 "RELOADING GRAPHICS '%s' -> '%s' ['%s', '%s']",
6372 artwork.gfx_current_identifier,
6374 artwork.gfx_current->identifier,
6375 leveldir_current->graphics_set);
6379 print_timestamp_time("InitImages");
6382 if (snd_new_identifier != NULL || force_reload_snd)
6385 print_timestamp_time("InitSound");
6388 if (mus_new_identifier != NULL || force_reload_mus)
6391 print_timestamp_time("InitMusic");
6396 SetGameStatus(last_game_status); // restore current game status
6398 FadeOut(REDRAW_ALL);
6400 RedrawGlobalBorder();
6402 // force redraw of (open or closed) door graphics
6403 SetDoorState(DOOR_OPEN_ALL);
6404 CloseDoor(DOOR_CLOSE_ALL | DOOR_NO_DELAY);
6406 FadeSetEnterScreen();
6407 FadeSkipNextFadeOut();
6409 print_timestamp_done("ReloadCustomArtwork");
6411 LimitScreenUpdates(FALSE);
6414 void KeyboardAutoRepeatOffUnlessAutoplay(void)
6416 if (global.autoplay_leveldir == NULL)
6417 KeyboardAutoRepeatOff();
6420 void DisplayExitMessage(char *format, va_list ap)
6422 // also check for initialized video (headless flag may be temporarily unset)
6423 if (program.headless || !video.initialized)
6426 // check if draw buffer and fonts for exit message are already available
6427 if (drawto == NULL || font_initial[NUM_INITIAL_FONTS - 1].bitmap == NULL)
6430 int font_1 = FC_RED;
6431 int font_2 = FC_YELLOW;
6432 int font_3 = FC_BLUE;
6433 int font_width = getFontWidth(font_2);
6434 int font_height = getFontHeight(font_2);
6437 int sxsize = WIN_XSIZE - 2 * sx;
6438 int sysize = WIN_YSIZE - 2 * sy;
6439 int line_length = sxsize / font_width;
6440 int max_lines = sysize / font_height;
6441 int num_lines_printed;
6445 gfx.sxsize = sxsize;
6446 gfx.sysize = sysize;
6450 ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6452 DrawTextSCentered(sy, font_1, "Fatal error:");
6453 sy += 3 * font_height;;
6456 DrawTextBufferVA(sx, sy, format, ap, font_2,
6457 line_length, line_length, max_lines,
6458 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6459 sy += (num_lines_printed + 3) * font_height;
6461 DrawTextSCentered(sy, font_1, "For details, see the following error file:");
6462 sy += 3 * font_height;
6465 DrawTextBuffer(sx, sy, program.log_filename, font_2,
6466 line_length, line_length, max_lines,
6467 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6469 DrawTextSCentered(SYSIZE - 20, font_3, "Press any key or button to exit");
6471 redraw_mask = REDRAW_ALL;
6473 // force drawing exit message even if screen updates are currently limited
6474 LimitScreenUpdates(FALSE);
6478 // deactivate toons on error message screen
6479 setup.toons = FALSE;
6481 WaitForEventToContinue();
6485 // ============================================================================
6487 // ============================================================================
6491 print_timestamp_init("OpenAll");
6493 SetGameStatus(GAME_MODE_LOADING);
6497 InitGlobal(); // initialize some global variables
6499 InitRND(NEW_RANDOMIZE);
6500 InitSimpleRandom(NEW_RANDOMIZE);
6501 InitBetterRandom(NEW_RANDOMIZE);
6503 InitMissingFileHash();
6505 print_timestamp_time("[init global stuff]");
6509 print_timestamp_time("[init setup/config stuff (1)]");
6511 if (options.execute_command)
6512 Execute_Command(options.execute_command);
6514 InitNetworkSettings();
6518 if (network.serveronly)
6520 #if defined(PLATFORM_UNIX)
6521 NetworkServer(network.server_port, TRUE);
6523 Warn("networking only supported in Unix version");
6526 exit(0); // never reached, server loops forever
6530 print_timestamp_time("[init setup/config stuff (2)]");
6532 print_timestamp_time("[init setup/config stuff (3)]");
6533 InitArtworkInfo(); // needed before loading gfx, sound & music
6534 print_timestamp_time("[init setup/config stuff (4)]");
6535 InitArtworkConfig(); // needed before forking sound child process
6536 print_timestamp_time("[init setup/config stuff (5)]");
6538 print_timestamp_time("[init setup/config stuff (6)]");
6542 print_timestamp_time("[init setup/config stuff]");
6544 InitVideoDefaults();
6546 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
6549 InitEventFilter(FilterMouseMotionEvents);
6551 print_timestamp_time("[init video stuff]");
6553 InitElementPropertiesStatic();
6554 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
6555 InitElementPropertiesGfxElement();
6557 print_timestamp_time("[init element properties stuff]");
6561 print_timestamp_time("InitGfx");
6564 print_timestamp_time("InitLevelInfo");
6566 InitLevelArtworkInfo();
6567 print_timestamp_time("InitLevelArtworkInfo");
6569 InitOverrideArtwork(); // needs to know current level directory
6570 print_timestamp_time("InitOverrideArtwork");
6572 InitArtworkIdentifier(); // needs to know current level directory
6573 print_timestamp_time("InitArtworkIdentifier");
6575 InitImages(); // needs to know current level directory
6576 print_timestamp_time("InitImages");
6578 InitSound(); // needs to know current level directory
6579 print_timestamp_time("InitSound");
6581 InitMusic(); // needs to know current level directory
6582 print_timestamp_time("InitMusic");
6586 InitGfxBackground();
6592 if (global.autoplay_leveldir)
6597 else if (global.patchtapes_leveldir)
6602 else if (global.convert_leveldir)
6607 else if (global.dumplevel_leveldir)
6612 else if (global.dumptape_leveldir)
6617 else if (global.create_sketch_images_dir)
6619 CreateLevelSketchImages();
6622 else if (global.create_collect_images_dir)
6624 CreateCollectElementImages();
6628 InitNetworkServer();
6630 SetGameStatus(GAME_MODE_MAIN);
6632 FadeSetEnterScreen();
6633 if (!(fading.fade_mode & FADE_TYPE_TRANSFORM))
6634 FadeSkipNextFadeOut();
6636 print_timestamp_time("[post-artwork]");
6638 print_timestamp_done("OpenAll");
6640 if (setup.ask_for_remaining_tapes)
6641 setup.ask_for_uploading_tapes = TRUE;
6646 Debug("internal:path", "SDL_GetBasePath() == '%s'",
6648 Debug("internal:path", "SDL_GetPrefPath() == '%s'",
6649 SDL_GetPrefPath("artsoft", "rocksndiamonds"));
6650 #if defined(PLATFORM_ANDROID)
6651 Debug("internal:path", "SDL_AndroidGetInternalStoragePath() == '%s'",
6652 SDL_AndroidGetInternalStoragePath());
6653 Debug("internal:path", "SDL_AndroidGetExternalStoragePath() == '%s'",
6654 SDL_AndroidGetExternalStoragePath());
6655 Debug("internal:path", "SDL_AndroidGetExternalStorageState() == '%s'",
6656 (SDL_AndroidGetExternalStorageState() &
6657 SDL_ANDROID_EXTERNAL_STORAGE_WRITE ? "writable" :
6658 SDL_AndroidGetExternalStorageState() &
6659 SDL_ANDROID_EXTERNAL_STORAGE_READ ? "readable" : "not available"));
6664 static boolean WaitForApiThreads(void)
6666 DelayCounter thread_delay = { 10000 };
6668 if (program.api_thread_count == 0)
6671 // deactivate global animations (not accessible in game state "loading")
6672 setup.toons = FALSE;
6674 // set game state to "loading" to be able to show busy animation
6675 SetGameStatus(GAME_MODE_LOADING);
6677 ResetDelayCounter(&thread_delay);
6679 // wait for threads to finish (and fail on timeout)
6680 while (program.api_thread_count > 0)
6682 if (DelayReached(&thread_delay))
6684 Error("failed waiting for threads - TIMEOUT");
6689 UPDATE_BUSY_STATE();
6697 void CloseAllAndExit(int exit_value)
6699 WaitForApiThreads();
6704 CloseAudio(); // called after freeing sounds (needed for SDL)
6712 // set a flag to tell the network server thread to quit and wait for it
6713 // using SDL_WaitThread()
6715 // Code used with SDL 1.2:
6716 // if (network.server_thread) // terminate network server
6717 // SDL_KillThread(network.server_thread);
6719 CloseVideoDisplay();
6720 ClosePlatformDependentStuff();
6722 if (exit_value != 0 && !options.execute_command)
6724 // fall back to default level set (current set may have caused an error)
6725 SaveLevelSetup_LastSeries_Deactivate();
6727 // tell user where to find error log file which may contain more details
6728 // (error notification now directly displayed on screen inside R'n'D
6729 // NotifyUserAboutErrorFile(); // currently only works for Windows