1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
12 #include "libgame/libgame.h"
27 #include "conf_e2g.c" // include auto-generated data structure definitions
28 #include "conf_esg.c" // include auto-generated data structure definitions
29 #include "conf_e2s.c" // include auto-generated data structure definitions
30 #include "conf_fnt.c" // include auto-generated data structure definitions
31 #include "conf_g2s.c" // include auto-generated data structure definitions
32 #include "conf_g2m.c" // include auto-generated data structure definitions
33 #include "conf_act.c" // include auto-generated data structure definitions
36 #define CONFIG_TOKEN_FONT_INITIAL "font.initial"
37 #define CONFIG_TOKEN_GLOBAL_BUSY_INITIAL "global.busy_initial"
38 #define CONFIG_TOKEN_GLOBAL_BUSY "global.busy"
39 #define CONFIG_TOKEN_GLOBAL_BUSY_PLAYFIELD "global.busy_playfield"
40 #define CONFIG_TOKEN_BACKGROUND "background"
41 #define CONFIG_TOKEN_BACKGROUND_LOADING_INITIAL "background.LOADING_INITIAL"
42 #define CONFIG_TOKEN_BACKGROUND_LOADING "background.LOADING"
44 #define INITIAL_IMG_GLOBAL_BUSY_INITIAL 0
45 #define INITIAL_IMG_GLOBAL_BUSY 1
46 #define INITIAL_IMG_GLOBAL_BUSY_PLAYFIELD 2
48 #define NUM_INITIAL_IMAGES_BUSY 3
50 #define INITIAL_IMG_BACKGROUND 3
51 #define INITIAL_IMG_BACKGROUND_LOADING_INITIAL 4
52 #define INITIAL_IMG_BACKGROUND_LOADING 5
54 #define NUM_INITIAL_IMAGES 6
57 static struct FontBitmapInfo font_initial[NUM_INITIAL_FONTS];
58 static struct GraphicInfo image_initial[NUM_INITIAL_IMAGES];
60 static int copy_properties[][5] =
64 EL_BUG_LEFT, EL_BUG_RIGHT,
65 EL_BUG_UP, EL_BUG_DOWN
69 EL_SPACESHIP_LEFT, EL_SPACESHIP_RIGHT,
70 EL_SPACESHIP_UP, EL_SPACESHIP_DOWN
74 EL_BD_BUTTERFLY_LEFT, EL_BD_BUTTERFLY_RIGHT,
75 EL_BD_BUTTERFLY_UP, EL_BD_BUTTERFLY_DOWN
79 EL_BD_FIREFLY_LEFT, EL_BD_FIREFLY_RIGHT,
80 EL_BD_FIREFLY_UP, EL_BD_FIREFLY_DOWN
84 EL_PACMAN_LEFT, EL_PACMAN_RIGHT,
85 EL_PACMAN_UP, EL_PACMAN_DOWN
89 EL_YAMYAM_LEFT, EL_YAMYAM_RIGHT,
90 EL_YAMYAM_UP, EL_YAMYAM_DOWN
94 EL_MOLE_LEFT, EL_MOLE_RIGHT,
95 EL_MOLE_UP, EL_MOLE_DOWN
99 EL_SPRING_LEFT, EL_SPRING_RIGHT,
100 EL_SPRING_LEFT, EL_SPRING_RIGHT, // (to match array size)
109 // forward declaration for internal use
110 static int get_graphic_parameter_value(char *, char *, int);
113 static int getLoadingBackgroundImage(int graphic)
115 return getImageFromGraphicOrDefault(graphic, INITIAL_IMG_BACKGROUND);
118 static void SetLoadingWindowBackgroundImage(int graphic)
120 SetBackgroundImage(getLoadingBackgroundImage(graphic), REDRAW_ALL);
123 static void SetLoadingBackgroundImage(void)
125 struct GraphicInfo *graphic_info_last = graphic_info;
126 int background_image = (game_status_last_screen == -1 ?
127 INITIAL_IMG_BACKGROUND_LOADING_INITIAL :
128 INITIAL_IMG_BACKGROUND_LOADING);
130 graphic_info = image_initial;
132 SetDrawDeactivationMask(REDRAW_NONE);
133 SetDrawBackgroundMask(REDRAW_ALL);
135 SetLoadingWindowBackgroundImage(background_image);
137 graphic_info = graphic_info_last;
140 static void DrawInitAnim(boolean only_when_loading)
142 struct GraphicInfo *graphic_info_last = graphic_info;
143 int graphic = (game_status_last_screen == -1 ?
144 INITIAL_IMG_GLOBAL_BUSY_INITIAL :
145 game_status == GAME_MODE_LOADING ?
146 INITIAL_IMG_GLOBAL_BUSY :
147 INITIAL_IMG_GLOBAL_BUSY_PLAYFIELD);
148 struct MenuPosInfo *busy = (game_status_last_screen == -1 ?
149 &init_last.busy_initial :
150 game_status == GAME_MODE_LOADING ?
152 &init_last.busy_playfield);
153 static DelayCounter action_delay = { 0 };
154 int sync_frame = FrameCounter;
157 action_delay.value = GameFrameDelay;
159 // prevent OS (Windows) from complaining about program not responding
162 if (game_status != GAME_MODE_LOADING && only_when_loading)
165 if (image_initial[graphic].bitmap == NULL || window == NULL)
168 if (!DelayReached(&action_delay))
172 busy->x = (game_status == GAME_MODE_LOADING ? WIN_XSIZE / 2 : SXSIZE / 2);
174 busy->y = (game_status == GAME_MODE_LOADING ? WIN_YSIZE / 2 : SYSIZE / 2);
176 x = (game_status == GAME_MODE_LOADING ? 0 : SX) + ALIGNED_TEXT_XPOS(busy);
177 y = (game_status == GAME_MODE_LOADING ? 0 : SY) + ALIGNED_TEXT_YPOS(busy);
179 graphic_info = image_initial;
181 if (sync_frame % image_initial[graphic].anim_delay == 0)
185 int width = graphic_info[graphic].width;
186 int height = graphic_info[graphic].height;
187 int frame = getGraphicAnimationFrame(graphic, sync_frame);
189 ClearRectangleOnBackground(drawto, x, y, width, height);
191 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
192 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height, x, y);
194 BlitBitmap(drawto, window, x, y, width, height, x, y);
197 graphic_info = graphic_info_last;
202 static void DrawProgramInfo(void)
204 int font1_nr = FC_YELLOW;
205 int font2_nr = FC_RED;
206 int font2_height = getFontHeight(font2_nr);
209 int ypos3 = WIN_YSIZE - 20 - font2_height;
211 DrawInitText(getProgramInitString(), ypos1, font1_nr);
212 DrawInitText(setup.internal.program_copyright, ypos2, font2_nr);
213 DrawInitText(setup.internal.program_website, ypos3, font2_nr);
216 static void FreeGadgets(void)
218 FreeLevelEditorGadgets();
225 void InitGadgets(void)
227 static boolean gadgets_initialized = FALSE;
229 if (gadgets_initialized)
232 CreateLevelEditorGadgets();
236 CreateScreenGadgets();
238 InitGadgetsSoundCallback(PlaySoundActivating, PlaySoundSelecting);
240 gadgets_initialized = TRUE;
243 static void InitElementSmallImagesScaledUp(int graphic)
245 struct GraphicInfo *g = &graphic_info[graphic];
247 // create small and game tile sized bitmaps (and scale up, if needed)
248 CreateImageWithSmallImages(graphic, g->scale_up_factor, g->tile_size);
251 static void InitElementSmallImages(void)
253 print_timestamp_init("InitElementSmallImages");
255 static int special_graphics[] =
269 IMG_EDITOR_ELEMENT_BORDER,
270 IMG_EDITOR_ELEMENT_BORDER_INPUT,
271 IMG_EDITOR_CASCADE_LIST,
272 IMG_EDITOR_CASCADE_LIST_ACTIVE,
275 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
276 int num_property_mappings = getImageListPropertyMappingSize();
279 print_timestamp_time("getImageListPropertyMapping/Size");
281 print_timestamp_init("InitElementSmallImagesScaledUp (1)");
282 // initialize normal element images from static configuration
283 for (i = 0; element_to_graphic[i].element > -1; i++)
284 InitElementSmallImagesScaledUp(element_to_graphic[i].graphic);
285 print_timestamp_done("InitElementSmallImagesScaledUp (1)");
287 // initialize special element images from static configuration
288 for (i = 0; element_to_special_graphic[i].element > -1; i++)
289 InitElementSmallImagesScaledUp(element_to_special_graphic[i].graphic);
290 print_timestamp_time("InitElementSmallImagesScaledUp (2)");
292 // initialize element images from dynamic configuration
293 for (i = 0; i < num_property_mappings; i++)
294 if (property_mapping[i].base_index < MAX_NUM_ELEMENTS)
295 InitElementSmallImagesScaledUp(property_mapping[i].artwork_index);
296 print_timestamp_time("InitElementSmallImagesScaledUp (3)");
298 // initialize special non-element images from above list
299 for (i = 0; special_graphics[i] > -1; i++)
300 InitElementSmallImagesScaledUp(special_graphics[i]);
301 print_timestamp_time("InitElementSmallImagesScaledUp (4)");
303 print_timestamp_done("InitElementSmallImages");
306 static void InitScaledImagesScaledUp(int graphic)
308 struct GraphicInfo *g = &graphic_info[graphic];
310 ScaleImage(graphic, g->scale_up_factor);
313 static void InitScaledImages(void)
315 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
316 int num_property_mappings = getImageListPropertyMappingSize();
319 // scale normal images from static configuration, if not already scaled
320 for (i = 0; i < NUM_IMAGE_FILES; i++)
321 InitScaledImagesScaledUp(i);
323 // scale images from dynamic configuration, if not already scaled
324 for (i = 0; i < num_property_mappings; i++)
325 InitScaledImagesScaledUp(property_mapping[i].artwork_index);
328 static void InitBitmapPointers(void)
330 int num_images = getImageListSize();
333 // standard size bitmap may have changed -- update default bitmap pointer
334 for (i = 0; i < num_images; i++)
335 if (graphic_info[i].bitmaps)
336 graphic_info[i].bitmap = graphic_info[i].bitmaps[IMG_BITMAP_STANDARD];
339 void InitImageTextures(void)
341 static int texture_graphics[] =
343 IMG_GFX_REQUEST_BUTTON_TOUCH_YES,
344 IMG_GFX_REQUEST_BUTTON_TOUCH_NO,
345 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM,
346 IMG_GFX_GAME_BUTTON_TOUCH_STOP,
347 IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,
348 IMG_MENU_BUTTON_TOUCH_BACK,
349 IMG_MENU_BUTTON_TOUCH_NEXT,
350 IMG_MENU_BUTTON_TOUCH_BACK2,
351 IMG_MENU_BUTTON_TOUCH_NEXT2,
356 FreeAllImageTextures();
358 for (i = IMG_GLOBAL_BORDER_FIRST; i <= IMG_GLOBAL_BORDER_LAST; i++)
359 CreateImageTextures(i);
361 for (i = 0; i < MAX_NUM_TOONS; i++)
362 CreateImageTextures(IMG_TOON_1 + i);
364 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
366 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
368 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
370 int graphic = global_anim_info[i].graphic[j][k];
372 if (graphic == IMG_UNDEFINED)
375 CreateImageTextures(graphic);
380 for (i = 0; texture_graphics[i] > -1; i++)
381 CreateImageTextures(texture_graphics[i]);
384 static int getFontSpecialSuffix(void)
388 // (special case: do not use special font for GAME_MODE_LOADING)
389 if (game_status >= GAME_MODE_TITLE_INITIAL &&
390 game_status <= GAME_MODE_PSEUDO_PREVIEW)
391 special = game_status;
392 else if (game_status == GAME_MODE_PSEUDO_TYPENAME)
393 special = GFX_SPECIAL_ARG_MAIN;
394 else if (game_status == GAME_MODE_PSEUDO_TYPENAMES)
395 special = GFX_SPECIAL_ARG_NAMES;
400 static int getFontBitmapID(int font_nr)
402 int special = getFontSpecialSuffix();
405 return font_info[font_nr].special_bitmap_id[special];
410 static int getFontFromToken(char *token)
412 char *value = getHashEntry(font_token_hash, token);
417 // if font not found, use reliable default value
418 return FONT_INITIAL_1;
421 static char *getTokenFromFont(int font_nr)
423 static char *token = NULL;
424 int special = getFontSpecialSuffix();
429 token = getStringCat2(font_info[font_nr].token_name,
430 special_suffix_info[special].suffix);
432 token = getStringCopy(font_info[font_nr].token_name);
437 static void InitFontGraphicInfo(void)
439 static struct FontBitmapInfo *font_bitmap_info = NULL;
440 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
441 int num_property_mappings = getImageListPropertyMappingSize();
442 int num_font_bitmaps = NUM_FONTS;
445 if (graphic_info == NULL) // still at startup phase
447 InitFontInfo(font_initial, NUM_INITIAL_FONTS,
448 getFontBitmapID, getFontFromToken, getTokenFromFont);
453 // ---------- initialize font graphic definitions ----------
455 // always start with reliable default values (normal font graphics)
456 for (i = 0; i < NUM_FONTS; i++)
457 font_info[i].graphic = IMG_FONT_INITIAL_1;
459 // initialize normal font/graphic mapping from static configuration
460 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
462 int font_nr = font_to_graphic[i].font_nr;
463 int special = font_to_graphic[i].special;
464 int graphic = font_to_graphic[i].graphic;
469 font_info[font_nr].graphic = graphic;
472 // always start with reliable default values (special font graphics)
473 for (i = 0; i < NUM_FONTS; i++)
475 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
477 font_info[i].special_graphic[j] = font_info[i].graphic;
478 font_info[i].special_bitmap_id[j] = i;
482 // initialize special font/graphic mapping from static configuration
483 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
485 int font_nr = font_to_graphic[i].font_nr;
486 int special = font_to_graphic[i].special;
487 int graphic = font_to_graphic[i].graphic;
488 int base_graphic = font2baseimg(font_nr);
490 if (IS_SPECIAL_GFX_ARG(special))
492 boolean base_redefined =
493 getImageListEntryFromImageID(base_graphic)->redefined;
494 boolean special_redefined =
495 getImageListEntryFromImageID(graphic)->redefined;
496 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
498 /* if the base font ("font.title_1", for example) has been redefined,
499 but not the special font ("font.title_1.LEVELS", for example), do not
500 use an existing (in this case considered obsolete) special font
501 anymore, but use the automatically determined default font */
502 /* special case: cloned special fonts must be explicitly redefined,
503 but are not automatically redefined by redefining base font */
504 if (base_redefined && !special_redefined && !special_cloned)
507 font_info[font_nr].special_graphic[special] = graphic;
508 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
513 // initialize special font/graphic mapping from dynamic configuration
514 for (i = 0; i < num_property_mappings; i++)
516 int font_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
517 int special = property_mapping[i].ext3_index;
518 int graphic = property_mapping[i].artwork_index;
520 if (font_nr < 0 || font_nr >= NUM_FONTS)
523 if (IS_SPECIAL_GFX_ARG(special))
525 font_info[font_nr].special_graphic[special] = graphic;
526 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
531 /* correct special font/graphic mapping for cloned fonts for downwards
532 compatibility of PREVIEW fonts -- this is only needed for implicit
533 redefinition of special font by redefined base font, and only if other
534 fonts are cloned from this special font (like in the "Zelda" level set) */
535 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
537 int font_nr = font_to_graphic[i].font_nr;
538 int special = font_to_graphic[i].special;
539 int graphic = font_to_graphic[i].graphic;
541 if (IS_SPECIAL_GFX_ARG(special))
543 boolean special_redefined =
544 getImageListEntryFromImageID(graphic)->redefined;
545 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
547 if (special_cloned && !special_redefined)
551 for (j = 0; font_to_graphic[j].font_nr > -1; j++)
553 int font_nr2 = font_to_graphic[j].font_nr;
554 int special2 = font_to_graphic[j].special;
555 int graphic2 = font_to_graphic[j].graphic;
557 if (IS_SPECIAL_GFX_ARG(special2) &&
558 graphic2 == graphic_info[graphic].clone_from)
560 font_info[font_nr].special_graphic[special] =
561 font_info[font_nr2].special_graphic[special2];
562 font_info[font_nr].special_bitmap_id[special] =
563 font_info[font_nr2].special_bitmap_id[special2];
570 // reset non-redefined ".active" font graphics if normal font is redefined
571 // (this different treatment is needed because normal and active fonts are
572 // independently defined ("active" is not a property of font definitions!)
573 for (i = 0; i < NUM_FONTS; i++)
575 int font_nr_base = i;
576 int font_nr_active = FONT_ACTIVE(font_nr_base);
578 // check only those fonts with exist as normal and ".active" variant
579 if (font_nr_base != font_nr_active)
581 int base_graphic = font_info[font_nr_base].graphic;
582 int active_graphic = font_info[font_nr_active].graphic;
583 boolean base_redefined =
584 getImageListEntryFromImageID(base_graphic)->redefined;
585 boolean active_redefined =
586 getImageListEntryFromImageID(active_graphic)->redefined;
588 /* if the base font ("font.menu_1", for example) has been redefined,
589 but not the active font ("font.menu_1.active", for example), do not
590 use an existing (in this case considered obsolete) active font
591 anymore, but use the automatically determined default font */
592 if (base_redefined && !active_redefined)
593 font_info[font_nr_active].graphic = base_graphic;
595 // now also check each "special" font (which may be the same as above)
596 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
598 int base_graphic = font_info[font_nr_base].special_graphic[j];
599 int active_graphic = font_info[font_nr_active].special_graphic[j];
600 boolean base_redefined =
601 getImageListEntryFromImageID(base_graphic)->redefined;
602 boolean active_redefined =
603 getImageListEntryFromImageID(active_graphic)->redefined;
605 // same as above, but check special graphic definitions, for example:
606 // redefined "font.menu_1.MAIN" invalidates "font.menu_1.active.MAIN"
607 if (base_redefined && !active_redefined)
609 font_info[font_nr_active].special_graphic[j] =
610 font_info[font_nr_base].special_graphic[j];
611 font_info[font_nr_active].special_bitmap_id[j] =
612 font_info[font_nr_base].special_bitmap_id[j];
618 // ---------- initialize font bitmap array ----------
620 if (font_bitmap_info != NULL)
621 FreeFontInfo(font_bitmap_info);
624 checked_calloc(num_font_bitmaps * sizeof(struct FontBitmapInfo));
626 // ---------- initialize font bitmap definitions ----------
628 for (i = 0; i < NUM_FONTS; i++)
630 if (i < NUM_INITIAL_FONTS)
632 font_bitmap_info[i] = font_initial[i];
636 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
638 int font_bitmap_id = font_info[i].special_bitmap_id[j];
639 int graphic = font_info[i].special_graphic[j];
641 // set 'graphic_info' for font entries, if uninitialized (guessed)
642 if (graphic_info[graphic].anim_frames < MIN_NUM_CHARS_PER_FONT)
644 graphic_info[graphic].anim_frames = DEFAULT_NUM_CHARS_PER_FONT;
645 graphic_info[graphic].anim_frames_per_line = DEFAULT_NUM_CHARS_PER_LINE;
648 // copy font relevant information from graphics information
649 font_bitmap_info[font_bitmap_id].bitmap = graphic_info[graphic].bitmap;
650 font_bitmap_info[font_bitmap_id].src_x = graphic_info[graphic].src_x;
651 font_bitmap_info[font_bitmap_id].src_y = graphic_info[graphic].src_y;
652 font_bitmap_info[font_bitmap_id].width = graphic_info[graphic].width;
653 font_bitmap_info[font_bitmap_id].height = graphic_info[graphic].height;
655 font_bitmap_info[font_bitmap_id].offset_x =
656 graphic_info[graphic].offset_x;
657 font_bitmap_info[font_bitmap_id].offset_y =
658 graphic_info[graphic].offset_y;
660 font_bitmap_info[font_bitmap_id].draw_xoffset =
661 graphic_info[graphic].draw_xoffset;
662 font_bitmap_info[font_bitmap_id].draw_yoffset =
663 graphic_info[graphic].draw_yoffset;
665 font_bitmap_info[font_bitmap_id].num_chars =
666 graphic_info[graphic].anim_frames;
667 font_bitmap_info[font_bitmap_id].num_chars_per_line =
668 graphic_info[graphic].anim_frames_per_line;
672 InitFontInfo(font_bitmap_info, num_font_bitmaps,
673 getFontBitmapID, getFontFromToken, getTokenFromFont);
676 static void InitGlobalAnimGraphicInfo(void)
678 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
679 int num_property_mappings = getImageListPropertyMappingSize();
682 if (graphic_info == NULL) // still at startup phase
685 // always start with reliable default values (no global animations)
686 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
687 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
688 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
689 global_anim_info[i].graphic[j][k] = IMG_UNDEFINED;
691 // initialize global animation definitions from static configuration
692 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
694 int j = GLOBAL_ANIM_ID_PART_BASE;
695 int k = GFX_SPECIAL_ARG_DEFAULT;
697 global_anim_info[i].graphic[j][k] = IMG_GFX_GLOBAL_ANIM_1 + i;
700 // initialize global animation definitions from dynamic configuration
701 for (i = 0; i < num_property_mappings; i++)
703 int anim_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS - NUM_FONTS;
704 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
705 int special = property_mapping[i].ext3_index;
706 int graphic = property_mapping[i].artwork_index;
708 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
711 // set animation part to base part, if not specified
712 if (!IS_GLOBAL_ANIM_PART(part_nr))
713 part_nr = GLOBAL_ANIM_ID_PART_BASE;
715 // set animation screen to default, if not specified
716 if (!IS_SPECIAL_GFX_ARG(special))
717 special = GFX_SPECIAL_ARG_DEFAULT;
719 global_anim_info[anim_nr].graphic[part_nr][special] = graphic;
721 // fix default value for ".draw_masked" (for backward compatibility)
722 struct GraphicInfo *g = &graphic_info[graphic];
723 struct FileInfo *image = getImageListEntryFromImageID(graphic);
724 char **parameter_raw = image->parameter;
725 int p = GFX_ARG_DRAW_MASKED;
726 int draw_masked = get_graphic_parameter_value(parameter_raw[p],
727 image_config_suffix[p].token,
728 image_config_suffix[p].type);
730 // if ".draw_masked" parameter is undefined, use default value "TRUE"
731 if (draw_masked == ARG_UNDEFINED_VALUE)
732 g->draw_masked = TRUE;
736 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
737 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
738 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
739 if (global_anim_info[i].graphic[j][k] != IMG_UNDEFINED &&
740 graphic_info[global_anim_info[i].graphic[j][k]].bitmap != NULL)
741 Debug("init:InitGlobalAnimGraphicInfo",
742 "anim %d, part %d, mode %d => %d",
743 i, j, k, global_anim_info[i].graphic[j][k]);
747 static void InitGlobalAnimSoundInfo(void)
749 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
750 int num_property_mappings = getSoundListPropertyMappingSize();
753 // always start with reliable default values (no global animation sounds)
754 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
755 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
756 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
757 global_anim_info[i].sound[j][k] = SND_UNDEFINED;
759 // initialize global animation sound definitions from dynamic configuration
760 for (i = 0; i < num_property_mappings; i++)
762 int anim_nr = property_mapping[i].base_index - 2 * MAX_NUM_ELEMENTS;
763 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
764 int special = property_mapping[i].ext3_index;
765 int sound = property_mapping[i].artwork_index;
767 // sound uses control definition; map it to position of graphic (artwork)
768 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
770 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
773 // set animation part to base part, if not specified
774 if (!IS_GLOBAL_ANIM_PART(part_nr))
775 part_nr = GLOBAL_ANIM_ID_PART_BASE;
777 // set animation screen to default, if not specified
778 if (!IS_SPECIAL_GFX_ARG(special))
779 special = GFX_SPECIAL_ARG_DEFAULT;
781 global_anim_info[anim_nr].sound[part_nr][special] = sound;
785 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
786 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
787 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
788 if (global_anim_info[i].sound[j][k] != SND_UNDEFINED)
789 Debug("init:InitGlobalAnimSoundInfo",
790 "anim %d, part %d, mode %d => %d",
791 i, j, k, global_anim_info[i].sound[j][k]);
795 static void InitGlobalAnimMusicInfo(void)
797 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
798 int num_property_mappings = getMusicListPropertyMappingSize();
801 // always start with reliable default values (no global animation music)
802 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
803 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
804 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
805 global_anim_info[i].music[j][k] = MUS_UNDEFINED;
807 // initialize global animation music definitions from dynamic configuration
808 for (i = 0; i < num_property_mappings; i++)
810 int anim_nr = property_mapping[i].base_index - NUM_MUSIC_PREFIXES;
811 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
812 int special = property_mapping[i].ext2_index;
813 int music = property_mapping[i].artwork_index;
815 // music uses control definition; map it to position of graphic (artwork)
816 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
818 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
821 // set animation part to base part, if not specified
822 if (!IS_GLOBAL_ANIM_PART(part_nr))
823 part_nr = GLOBAL_ANIM_ID_PART_BASE;
825 // set animation screen to default, if not specified
826 if (!IS_SPECIAL_GFX_ARG(special))
827 special = GFX_SPECIAL_ARG_DEFAULT;
829 global_anim_info[anim_nr].music[part_nr][special] = music;
833 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
834 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
835 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
836 if (global_anim_info[i].music[j][k] != MUS_UNDEFINED)
837 Debug("init:InitGlobalAnimMusicInfo",
838 "anim %d, part %d, mode %d => %d",
839 i, j, k, global_anim_info[i].music[j][k]);
843 static void InitElementGraphicInfo(void)
845 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
846 int num_property_mappings = getImageListPropertyMappingSize();
849 if (graphic_info == NULL) // still at startup phase
852 // set values to -1 to identify later as "uninitialized" values
853 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
855 for (act = 0; act < NUM_ACTIONS; act++)
857 element_info[i].graphic[act] = -1;
858 element_info[i].crumbled[act] = -1;
860 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
862 element_info[i].direction_graphic[act][dir] = -1;
863 element_info[i].direction_crumbled[act][dir] = -1;
870 // initialize normal element/graphic mapping from static configuration
871 for (i = 0; element_to_graphic[i].element > -1; i++)
873 int element = element_to_graphic[i].element;
874 int action = element_to_graphic[i].action;
875 int direction = element_to_graphic[i].direction;
876 boolean crumbled = element_to_graphic[i].crumbled;
877 int graphic = element_to_graphic[i].graphic;
878 int base_graphic = el2baseimg(element);
880 if (graphic_info[graphic].bitmap == NULL)
883 if ((action > -1 || direction > -1 || crumbled == TRUE) &&
886 boolean base_redefined =
887 getImageListEntryFromImageID(base_graphic)->redefined;
888 boolean act_dir_redefined =
889 getImageListEntryFromImageID(graphic)->redefined;
891 /* if the base graphic ("emerald", for example) has been redefined,
892 but not the action graphic ("emerald.falling", for example), do not
893 use an existing (in this case considered obsolete) action graphic
894 anymore, but use the automatically determined default graphic */
895 if (base_redefined && !act_dir_redefined)
900 action = ACTION_DEFAULT;
905 element_info[element].direction_crumbled[action][direction] = graphic;
907 element_info[element].crumbled[action] = graphic;
912 element_info[element].direction_graphic[action][direction] = graphic;
914 element_info[element].graphic[action] = graphic;
918 // initialize normal element/graphic mapping from dynamic configuration
919 for (i = 0; i < num_property_mappings; i++)
921 int element = property_mapping[i].base_index;
922 int action = property_mapping[i].ext1_index;
923 int direction = property_mapping[i].ext2_index;
924 int special = property_mapping[i].ext3_index;
925 int graphic = property_mapping[i].artwork_index;
926 boolean crumbled = FALSE;
928 if (special == GFX_SPECIAL_ARG_CRUMBLED)
934 if (graphic_info[graphic].bitmap == NULL)
937 if (element >= MAX_NUM_ELEMENTS || special != -1)
941 action = ACTION_DEFAULT;
946 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
947 element_info[element].direction_crumbled[action][dir] = -1;
950 element_info[element].direction_crumbled[action][direction] = graphic;
952 element_info[element].crumbled[action] = graphic;
957 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
958 element_info[element].direction_graphic[action][dir] = -1;
961 element_info[element].direction_graphic[action][direction] = graphic;
963 element_info[element].graphic[action] = graphic;
967 // now copy all graphics that are defined to be cloned from other graphics
968 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
970 int graphic = element_info[i].graphic[ACTION_DEFAULT];
971 int crumbled_like, diggable_like;
976 crumbled_like = graphic_info[graphic].crumbled_like;
977 diggable_like = graphic_info[graphic].diggable_like;
979 if (crumbled_like != -1 && element_info[i].crumbled[ACTION_DEFAULT] == -1)
981 for (act = 0; act < NUM_ACTIONS; act++)
982 element_info[i].crumbled[act] =
983 element_info[crumbled_like].crumbled[act];
984 for (act = 0; act < NUM_ACTIONS; act++)
985 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
986 element_info[i].direction_crumbled[act][dir] =
987 element_info[crumbled_like].direction_crumbled[act][dir];
990 if (diggable_like != -1 && element_info[i].graphic[ACTION_DIGGING] == -1)
992 element_info[i].graphic[ACTION_DIGGING] =
993 element_info[diggable_like].graphic[ACTION_DIGGING];
994 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
995 element_info[i].direction_graphic[ACTION_DIGGING][dir] =
996 element_info[diggable_like].direction_graphic[ACTION_DIGGING][dir];
1000 // set hardcoded definitions for some runtime elements without graphic
1001 element_info[EL_AMOEBA_TO_DIAMOND].graphic[ACTION_DEFAULT] = IMG_AMOEBA_DEAD;
1003 // set hardcoded definitions for some internal elements without graphic
1004 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1006 if (IS_EDITOR_CASCADE_INACTIVE(i))
1007 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST;
1008 else if (IS_EDITOR_CASCADE_ACTIVE(i))
1009 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST_ACTIVE;
1012 // now set all undefined/invalid graphics to -1 to set to default after it
1013 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1015 for (act = 0; act < NUM_ACTIONS; act++)
1019 graphic = element_info[i].graphic[act];
1020 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1021 element_info[i].graphic[act] = -1;
1023 graphic = element_info[i].crumbled[act];
1024 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1025 element_info[i].crumbled[act] = -1;
1027 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1029 graphic = element_info[i].direction_graphic[act][dir];
1030 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1031 element_info[i].direction_graphic[act][dir] = -1;
1033 graphic = element_info[i].direction_crumbled[act][dir];
1034 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1035 element_info[i].direction_crumbled[act][dir] = -1;
1040 UPDATE_BUSY_STATE();
1042 // adjust graphics with 2nd tile for movement according to direction
1043 // (do this before correcting '-1' values to minimize calculations)
1044 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1046 for (act = 0; act < NUM_ACTIONS; act++)
1048 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1050 int graphic = element_info[i].direction_graphic[act][dir];
1051 int move_dir = (act == ACTION_FALLING ? MV_BIT_DOWN : dir);
1053 if (act == ACTION_FALLING) // special case
1054 graphic = element_info[i].graphic[act];
1056 if (graphic != -1 &&
1057 graphic_info[graphic].double_movement &&
1058 graphic_info[graphic].swap_double_tiles != 0)
1060 struct GraphicInfo *g = &graphic_info[graphic];
1061 int src_x_front = g->src_x;
1062 int src_y_front = g->src_y;
1063 int src_x_back = g->src_x + g->offset2_x;
1064 int src_y_back = g->src_y + g->offset2_y;
1065 boolean frames_are_ordered_diagonally = (g->offset_x != 0 &&
1067 boolean front_is_left_or_upper = (src_x_front < src_x_back ||
1068 src_y_front < src_y_back);
1069 boolean swap_movement_tiles_always = (g->swap_double_tiles == 1);
1070 boolean swap_movement_tiles_autodetected =
1071 (!frames_are_ordered_diagonally &&
1072 ((move_dir == MV_BIT_LEFT && !front_is_left_or_upper) ||
1073 (move_dir == MV_BIT_UP && !front_is_left_or_upper) ||
1074 (move_dir == MV_BIT_RIGHT && front_is_left_or_upper) ||
1075 (move_dir == MV_BIT_DOWN && front_is_left_or_upper)));
1077 // swap frontside and backside graphic tile coordinates, if needed
1078 if (swap_movement_tiles_always || swap_movement_tiles_autodetected)
1080 // get current (wrong) backside tile coordinates
1081 getGraphicSourceXY(graphic, 0, &src_x_back, &src_y_back, TRUE);
1083 // set frontside tile coordinates to backside tile coordinates
1084 g->src_x = src_x_back;
1085 g->src_y = src_y_back;
1087 // invert tile offset to point to new backside tile coordinates
1091 // do not swap front and backside tiles again after correction
1092 g->swap_double_tiles = 0;
1099 UPDATE_BUSY_STATE();
1101 // now set all '-1' values to element specific default values
1102 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1104 int default_graphic = element_info[i].graphic[ACTION_DEFAULT];
1105 int default_crumbled = element_info[i].crumbled[ACTION_DEFAULT];
1106 int default_direction_graphic[NUM_DIRECTIONS_FULL];
1107 int default_direction_crumbled[NUM_DIRECTIONS_FULL];
1109 if (default_graphic == -1)
1110 default_graphic = IMG_UNKNOWN;
1112 if (default_crumbled == -1)
1113 default_crumbled = default_graphic;
1115 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1117 default_direction_graphic[dir] =
1118 element_info[i].direction_graphic[ACTION_DEFAULT][dir];
1119 default_direction_crumbled[dir] =
1120 element_info[i].direction_crumbled[ACTION_DEFAULT][dir];
1122 if (default_direction_graphic[dir] == -1)
1123 default_direction_graphic[dir] = default_graphic;
1125 if (default_direction_crumbled[dir] == -1)
1126 default_direction_crumbled[dir] = default_direction_graphic[dir];
1129 for (act = 0; act < NUM_ACTIONS; act++)
1131 boolean act_remove = ((IS_DIGGABLE(i) && act == ACTION_DIGGING) ||
1132 (IS_SNAPPABLE(i) && act == ACTION_SNAPPING) ||
1133 (IS_COLLECTIBLE(i) && act == ACTION_COLLECTING));
1134 boolean act_turning = (act == ACTION_TURNING_FROM_LEFT ||
1135 act == ACTION_TURNING_FROM_RIGHT ||
1136 act == ACTION_TURNING_FROM_UP ||
1137 act == ACTION_TURNING_FROM_DOWN);
1139 // generic default action graphic (defined by "[default]" directive)
1140 int default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1141 int default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1142 int default_remove_graphic = IMG_EMPTY;
1144 if (act_remove && default_action_graphic != -1)
1145 default_remove_graphic = default_action_graphic;
1147 // look for special default action graphic (classic game specific)
1148 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].graphic[act] != -1)
1149 default_action_graphic = element_info[EL_BD_DEFAULT].graphic[act];
1150 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].graphic[act] != -1)
1151 default_action_graphic = element_info[EL_SP_DEFAULT].graphic[act];
1152 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].graphic[act] != -1)
1153 default_action_graphic = element_info[EL_SB_DEFAULT].graphic[act];
1154 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].graphic[act] != -1)
1155 default_action_graphic = element_info[EL_MM_DEFAULT].graphic[act];
1157 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].crumbled[act] != -1)
1158 default_action_crumbled = element_info[EL_BD_DEFAULT].crumbled[act];
1159 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].crumbled[act] != -1)
1160 default_action_crumbled = element_info[EL_SP_DEFAULT].crumbled[act];
1161 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].crumbled[act] != -1)
1162 default_action_crumbled = element_info[EL_SB_DEFAULT].crumbled[act];
1163 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].crumbled[act] != -1)
1164 default_action_crumbled = element_info[EL_MM_DEFAULT].crumbled[act];
1166 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
1167 // !!! make this better !!!
1168 if (i == EL_EMPTY_SPACE)
1170 default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1171 default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1174 if (default_action_graphic == -1)
1175 default_action_graphic = default_graphic;
1177 if (default_action_crumbled == -1)
1178 default_action_crumbled = default_action_graphic;
1180 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1182 // use action graphic as the default direction graphic, if undefined
1183 int default_action_direction_graphic = element_info[i].graphic[act];
1184 int default_action_direction_crumbled = element_info[i].crumbled[act];
1186 // no graphic for current action -- use default direction graphic
1187 if (default_action_direction_graphic == -1)
1188 default_action_direction_graphic =
1189 (act_remove ? default_remove_graphic :
1191 element_info[i].direction_graphic[ACTION_TURNING][dir] :
1192 default_action_graphic != default_graphic ?
1193 default_action_graphic :
1194 default_direction_graphic[dir]);
1196 if (element_info[i].direction_graphic[act][dir] == -1)
1197 element_info[i].direction_graphic[act][dir] =
1198 default_action_direction_graphic;
1200 if (default_action_direction_crumbled == -1)
1201 default_action_direction_crumbled =
1202 element_info[i].direction_graphic[act][dir];
1204 if (element_info[i].direction_crumbled[act][dir] == -1)
1205 element_info[i].direction_crumbled[act][dir] =
1206 default_action_direction_crumbled;
1209 // no graphic for this specific action -- use default action graphic
1210 if (element_info[i].graphic[act] == -1)
1211 element_info[i].graphic[act] =
1212 (act_remove ? default_remove_graphic :
1213 act_turning ? element_info[i].graphic[ACTION_TURNING] :
1214 default_action_graphic);
1216 if (element_info[i].crumbled[act] == -1)
1217 element_info[i].crumbled[act] = element_info[i].graphic[act];
1221 UPDATE_BUSY_STATE();
1224 static void InitElementSpecialGraphicInfo(void)
1226 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
1227 int num_property_mappings = getImageListPropertyMappingSize();
1230 // always start with reliable default values
1231 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1232 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1233 element_info[i].special_graphic[j] =
1234 element_info[i].graphic[ACTION_DEFAULT];
1236 // initialize special element/graphic mapping from static configuration
1237 for (i = 0; element_to_special_graphic[i].element > -1; i++)
1239 int element = element_to_special_graphic[i].element;
1240 int special = element_to_special_graphic[i].special;
1241 int graphic = element_to_special_graphic[i].graphic;
1242 int base_graphic = el2baseimg(element);
1243 boolean base_redefined =
1244 getImageListEntryFromImageID(base_graphic)->redefined;
1245 boolean special_redefined =
1246 getImageListEntryFromImageID(graphic)->redefined;
1248 /* if the base graphic ("emerald", for example) has been redefined,
1249 but not the special graphic ("emerald.EDITOR", for example), do not
1250 use an existing (in this case considered obsolete) special graphic
1251 anymore, but use the automatically created (down-scaled) graphic */
1252 if (base_redefined && !special_redefined)
1255 element_info[element].special_graphic[special] = graphic;
1258 // initialize special element/graphic mapping from dynamic configuration
1259 for (i = 0; i < num_property_mappings; i++)
1261 int element = property_mapping[i].base_index;
1262 int action = property_mapping[i].ext1_index;
1263 int direction = property_mapping[i].ext2_index;
1264 int special = property_mapping[i].ext3_index;
1265 int graphic = property_mapping[i].artwork_index;
1267 // for action ".active", replace element with active element, if exists
1268 if (action == ACTION_ACTIVE && element != ELEMENT_ACTIVE(element))
1270 element = ELEMENT_ACTIVE(element);
1274 if (element >= MAX_NUM_ELEMENTS)
1277 // do not change special graphic if action or direction was specified
1278 if (action != -1 || direction != -1)
1281 if (IS_SPECIAL_GFX_ARG(special))
1282 element_info[element].special_graphic[special] = graphic;
1285 // now set all undefined/invalid graphics to default
1286 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1287 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1288 if (graphic_info[element_info[i].special_graphic[j]].bitmap == NULL)
1289 element_info[i].special_graphic[j] =
1290 element_info[i].graphic[ACTION_DEFAULT];
1293 static int get_graphic_parameter_value(char *value_raw, char *suffix, int type)
1295 if (type != TYPE_ELEMENT && type != TYPE_GRAPHIC)
1296 return get_parameter_value(value_raw, suffix, type);
1298 if (strEqual(value_raw, ARG_UNDEFINED))
1299 return ARG_UNDEFINED_VALUE;
1301 if (type == TYPE_ELEMENT)
1303 char *value = getHashEntry(element_token_hash, value_raw);
1308 Warn("error found in config file:");
1309 Warn("- config file: '%s'", getImageConfigFilename());
1310 Warn("error: invalid element token '%s'", value_raw);
1311 Warn("custom graphic rejected for this element/action");
1312 Warn("fallback done to undefined element for this graphic");
1316 return (value != NULL ? atoi(value) : EL_UNDEFINED);
1318 else if (type == TYPE_GRAPHIC)
1320 char *value = getHashEntry(graphic_token_hash, value_raw);
1321 int fallback_graphic = IMG_CHAR_EXCLAM;
1326 Warn("error found in config file:");
1327 Warn("- config file: '%s'", getImageConfigFilename());
1328 Warn("error: invalid graphic token '%s'", value_raw);
1329 Warn("custom graphic rejected for this element/action");
1330 Warn("fallback done to 'char_exclam' for this graphic");
1334 return (value != NULL ? atoi(value) : fallback_graphic);
1340 static int get_scaled_graphic_width(int graphic)
1342 int original_width = getOriginalImageWidthFromImageID(graphic);
1343 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1345 return original_width * scale_up_factor;
1348 static int get_scaled_graphic_height(int graphic)
1350 int original_height = getOriginalImageHeightFromImageID(graphic);
1351 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1353 return original_height * scale_up_factor;
1356 static void set_graphic_parameters_ext(int graphic, int *parameter,
1357 Bitmap **src_bitmaps)
1359 struct GraphicInfo *g = &graphic_info[graphic];
1360 Bitmap *src_bitmap = (src_bitmaps ? src_bitmaps[IMG_BITMAP_STANDARD] : NULL);
1361 int anim_frames_per_row = 1, anim_frames_per_col = 1;
1362 int anim_frames_per_line = 1;
1364 // always start with reliable default values
1365 g->src_image_width = 0;
1366 g->src_image_height = 0;
1369 g->width = TILEX; // default for element graphics
1370 g->height = TILEY; // default for element graphics
1371 g->offset_x = 0; // one or both of these values ...
1372 g->offset_y = 0; // ... will be corrected later
1373 g->offset2_x = 0; // one or both of these values ...
1374 g->offset2_y = 0; // ... will be corrected later
1375 g->swap_double_tiles = -1; // auto-detect tile swapping
1376 g->crumbled_like = -1; // do not use clone element
1377 g->diggable_like = -1; // do not use clone element
1378 g->border_size = TILEX / 8; // "CRUMBLED" border size
1379 g->scale_up_factor = 1; // default: no scaling up
1380 g->tile_size = TILESIZE; // default: standard tile size
1381 g->clone_from = -1; // do not use clone graphic
1382 g->init_delay_fixed = 0;
1383 g->init_delay_random = 0;
1384 g->init_delay_action = -1;
1385 g->anim_delay_fixed = 0;
1386 g->anim_delay_random = 0;
1387 g->anim_delay_action = -1;
1388 g->post_delay_fixed = 0;
1389 g->post_delay_random = 0;
1390 g->post_delay_action = -1;
1391 g->init_event = ANIM_EVENT_UNDEFINED;
1392 g->anim_event = ANIM_EVENT_UNDEFINED;
1393 g->init_event_action = -1;
1394 g->anim_event_action = -1;
1395 g->draw_masked = FALSE;
1397 g->fade_mode = FADE_MODE_DEFAULT;
1401 g->auto_delay_unit = AUTO_DELAY_UNIT_DEFAULT;
1402 g->align = ALIGN_CENTER; // default for title screens
1403 g->valign = VALIGN_MIDDLE; // default for title screens
1404 g->sort_priority = 0; // default for title screens
1406 g->style = STYLE_DEFAULT;
1408 g->bitmaps = src_bitmaps;
1409 g->bitmap = src_bitmap;
1411 // optional zoom factor for scaling up the image to a larger size
1412 if (parameter[GFX_ARG_SCALE_UP_FACTOR] != ARG_UNDEFINED_VALUE)
1413 g->scale_up_factor = parameter[GFX_ARG_SCALE_UP_FACTOR];
1414 if (g->scale_up_factor < 1)
1415 g->scale_up_factor = 1; // no scaling
1417 // optional tile size for using non-standard image size
1418 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1420 g->tile_size = parameter[GFX_ARG_TILE_SIZE];
1423 // CHECK: should tile sizes less than standard tile size be allowed?
1424 if (g->tile_size < TILESIZE)
1425 g->tile_size = TILESIZE; // standard tile size
1428 // when setting tile size, also set width and height accordingly
1429 g->width = g->tile_size;
1430 g->height = g->tile_size;
1433 if (g->use_image_size)
1435 // set new default bitmap size (with scaling, but without small images)
1436 g->width = get_scaled_graphic_width(graphic);
1437 g->height = get_scaled_graphic_height(graphic);
1440 // optional width and height of each animation frame
1441 if (parameter[GFX_ARG_WIDTH] != ARG_UNDEFINED_VALUE)
1442 g->width = parameter[GFX_ARG_WIDTH];
1443 if (parameter[GFX_ARG_HEIGHT] != ARG_UNDEFINED_VALUE)
1444 g->height = parameter[GFX_ARG_HEIGHT];
1446 // optional x and y tile position of animation frame sequence
1447 if (parameter[GFX_ARG_XPOS] != ARG_UNDEFINED_VALUE)
1448 g->src_x = parameter[GFX_ARG_XPOS] * g->width;
1449 if (parameter[GFX_ARG_YPOS] != ARG_UNDEFINED_VALUE)
1450 g->src_y = parameter[GFX_ARG_YPOS] * g->height;
1452 // optional x and y pixel position of animation frame sequence
1453 if (parameter[GFX_ARG_X] != ARG_UNDEFINED_VALUE)
1454 g->src_x = parameter[GFX_ARG_X];
1455 if (parameter[GFX_ARG_Y] != ARG_UNDEFINED_VALUE)
1456 g->src_y = parameter[GFX_ARG_Y];
1463 Warn("invalid value %d for '%s.width' (fallback done to %d)",
1464 g->width, getTokenFromImageID(graphic), TILEX);
1467 g->width = TILEX; // will be checked to be inside bitmap later
1473 Warn("invalid value %d for '%s.height' (fallback done to %d)",
1474 g->height, getTokenFromImageID(graphic), TILEY);
1477 g->height = TILEY; // will be checked to be inside bitmap later
1483 // get final bitmap size (with scaling, but without small images)
1484 int src_image_width = get_scaled_graphic_width(graphic);
1485 int src_image_height = get_scaled_graphic_height(graphic);
1487 if (src_image_width == 0 || src_image_height == 0)
1489 // only happens when loaded outside artwork system (like "global.busy")
1490 src_image_width = src_bitmap->width;
1491 src_image_height = src_bitmap->height;
1494 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1496 anim_frames_per_row = MAX(1, src_image_width / g->tile_size);
1497 anim_frames_per_col = MAX(1, src_image_height / g->tile_size);
1501 anim_frames_per_row = MAX(1, src_image_width / g->width);
1502 anim_frames_per_col = MAX(1, src_image_height / g->height);
1505 g->src_image_width = src_image_width;
1506 g->src_image_height = src_image_height;
1509 // correct x or y offset dependent of vertical or horizontal frame order
1510 if (parameter[GFX_ARG_VERTICAL]) // frames are ordered vertically
1512 g->offset_y = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1513 parameter[GFX_ARG_OFFSET] : g->height);
1514 anim_frames_per_line = anim_frames_per_col;
1516 else // frames are ordered horizontally
1518 g->offset_x = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1519 parameter[GFX_ARG_OFFSET] : g->width);
1520 anim_frames_per_line = anim_frames_per_row;
1523 // optionally, the x and y offset of frames can be specified directly
1524 if (parameter[GFX_ARG_XOFFSET] != ARG_UNDEFINED_VALUE)
1525 g->offset_x = parameter[GFX_ARG_XOFFSET];
1526 if (parameter[GFX_ARG_YOFFSET] != ARG_UNDEFINED_VALUE)
1527 g->offset_y = parameter[GFX_ARG_YOFFSET];
1529 // optionally, moving animations may have separate start and end graphics
1530 g->double_movement = parameter[GFX_ARG_2ND_MOVEMENT_TILE];
1532 if (parameter[GFX_ARG_2ND_VERTICAL] == ARG_UNDEFINED_VALUE)
1533 parameter[GFX_ARG_2ND_VERTICAL] = !parameter[GFX_ARG_VERTICAL];
1535 // correct x or y offset2 dependent of vertical or horizontal frame order
1536 if (parameter[GFX_ARG_2ND_VERTICAL]) // frames are ordered vertically
1537 g->offset2_y = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1538 parameter[GFX_ARG_2ND_OFFSET] : g->height);
1539 else // frames are ordered horizontally
1540 g->offset2_x = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1541 parameter[GFX_ARG_2ND_OFFSET] : g->width);
1543 // optionally, the x and y offset of 2nd graphic can be specified directly
1544 if (parameter[GFX_ARG_2ND_XOFFSET] != ARG_UNDEFINED_VALUE)
1545 g->offset2_x = parameter[GFX_ARG_2ND_XOFFSET];
1546 if (parameter[GFX_ARG_2ND_YOFFSET] != ARG_UNDEFINED_VALUE)
1547 g->offset2_y = parameter[GFX_ARG_2ND_YOFFSET];
1549 // optionally, the second movement tile can be specified as start tile
1550 if (parameter[GFX_ARG_2ND_SWAP_TILES] != ARG_UNDEFINED_VALUE)
1551 g->swap_double_tiles= parameter[GFX_ARG_2ND_SWAP_TILES];
1553 // automatically determine correct number of frames, if not defined
1554 if (parameter[GFX_ARG_FRAMES] != ARG_UNDEFINED_VALUE)
1555 g->anim_frames = parameter[GFX_ARG_FRAMES];
1556 else if (parameter[GFX_ARG_XPOS] == 0 && !parameter[GFX_ARG_VERTICAL])
1557 g->anim_frames = anim_frames_per_row;
1558 else if (parameter[GFX_ARG_YPOS] == 0 && parameter[GFX_ARG_VERTICAL])
1559 g->anim_frames = anim_frames_per_col;
1563 if (g->anim_frames < 1) // frames must be at least 1
1566 g->anim_frames_per_line =
1567 (parameter[GFX_ARG_FRAMES_PER_LINE] != ARG_UNDEFINED_VALUE ?
1568 parameter[GFX_ARG_FRAMES_PER_LINE] : anim_frames_per_line);
1570 g->anim_delay = parameter[GFX_ARG_DELAY];
1571 if (g->anim_delay < 1) // delay must be at least 1
1574 g->anim_mode = parameter[GFX_ARG_ANIM_MODE];
1576 // automatically determine correct start frame, if not defined
1577 if (parameter[GFX_ARG_START_FRAME] == ARG_UNDEFINED_VALUE)
1578 g->anim_start_frame = 0;
1579 else if (g->anim_mode & ANIM_REVERSE)
1580 g->anim_start_frame = g->anim_frames - parameter[GFX_ARG_START_FRAME] - 1;
1582 g->anim_start_frame = parameter[GFX_ARG_START_FRAME];
1584 // animation synchronized with global frame counter, not move position
1585 g->anim_global_sync = parameter[GFX_ARG_GLOBAL_SYNC];
1587 // animation synchronized with global anim frame counter, not move position
1588 g->anim_global_anim_sync = parameter[GFX_ARG_GLOBAL_ANIM_SYNC];
1590 // optional element for cloning crumble graphics
1591 if (parameter[GFX_ARG_CRUMBLED_LIKE] != ARG_UNDEFINED_VALUE)
1592 g->crumbled_like = parameter[GFX_ARG_CRUMBLED_LIKE];
1594 // optional element for cloning digging graphics
1595 if (parameter[GFX_ARG_DIGGABLE_LIKE] != ARG_UNDEFINED_VALUE)
1596 g->diggable_like = parameter[GFX_ARG_DIGGABLE_LIKE];
1598 // optional border size for "crumbling" diggable graphics
1599 if (parameter[GFX_ARG_BORDER_SIZE] != ARG_UNDEFINED_VALUE)
1600 g->border_size = parameter[GFX_ARG_BORDER_SIZE];
1602 // used for global animations and player "boring" and "sleeping" actions
1603 if (parameter[GFX_ARG_INIT_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1604 g->init_delay_fixed = parameter[GFX_ARG_INIT_DELAY_FIXED];
1605 if (parameter[GFX_ARG_INIT_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1606 g->init_delay_random = parameter[GFX_ARG_INIT_DELAY_RANDOM];
1607 if (parameter[GFX_ARG_ANIM_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1608 g->anim_delay_fixed = parameter[GFX_ARG_ANIM_DELAY_FIXED];
1609 if (parameter[GFX_ARG_ANIM_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1610 g->anim_delay_random = parameter[GFX_ARG_ANIM_DELAY_RANDOM];
1611 if (parameter[GFX_ARG_POST_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1612 g->post_delay_fixed = parameter[GFX_ARG_POST_DELAY_FIXED];
1613 if (parameter[GFX_ARG_POST_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1614 g->post_delay_random = parameter[GFX_ARG_POST_DELAY_RANDOM];
1616 // used for global animations
1617 if (parameter[GFX_ARG_INIT_EVENT] != ARG_UNDEFINED_VALUE)
1618 g->init_event = parameter[GFX_ARG_INIT_EVENT];
1619 if (parameter[GFX_ARG_ANIM_EVENT] != ARG_UNDEFINED_VALUE)
1620 g->anim_event = parameter[GFX_ARG_ANIM_EVENT];
1621 if (parameter[GFX_ARG_INIT_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1622 g->init_event_action = parameter[GFX_ARG_INIT_EVENT_ACTION];
1623 if (parameter[GFX_ARG_ANIM_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1624 g->anim_event_action = parameter[GFX_ARG_ANIM_EVENT_ACTION];
1625 if (parameter[GFX_ARG_INIT_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1626 g->init_delay_action = parameter[GFX_ARG_INIT_DELAY_ACTION];
1627 if (parameter[GFX_ARG_ANIM_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1628 g->anim_delay_action = parameter[GFX_ARG_ANIM_DELAY_ACTION];
1629 if (parameter[GFX_ARG_POST_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1630 g->post_delay_action = parameter[GFX_ARG_POST_DELAY_ACTION];
1632 // used for toon animations and global animations
1633 g->step_offset = parameter[GFX_ARG_STEP_OFFSET];
1634 g->step_xoffset = parameter[GFX_ARG_STEP_XOFFSET];
1635 g->step_yoffset = parameter[GFX_ARG_STEP_YOFFSET];
1636 g->step_delay = parameter[GFX_ARG_STEP_DELAY];
1637 g->direction = parameter[GFX_ARG_DIRECTION];
1638 g->position = parameter[GFX_ARG_POSITION];
1639 g->x = parameter[GFX_ARG_X]; // (may be uninitialized,
1640 g->y = parameter[GFX_ARG_Y]; // unlike src_x and src_y)
1642 if (g->step_delay < 1) // delay must be at least 1
1645 // this is only used for drawing font characters
1646 g->draw_xoffset = parameter[GFX_ARG_DRAW_XOFFSET];
1647 g->draw_yoffset = parameter[GFX_ARG_DRAW_YOFFSET];
1649 // use a different default value for global animations and toons
1650 if ((graphic >= IMG_GFX_GLOBAL_ANIM_1 && graphic <= IMG_GFX_GLOBAL_ANIM_8) ||
1651 (graphic >= IMG_TOON_1 && graphic <= IMG_TOON_20))
1652 g->draw_masked = TRUE;
1654 // this is used for drawing envelopes, global animations and toons
1655 if (parameter[GFX_ARG_DRAW_MASKED] != ARG_UNDEFINED_VALUE)
1656 g->draw_masked = parameter[GFX_ARG_DRAW_MASKED];
1658 // used for toon animations and global animations
1659 if (parameter[GFX_ARG_DRAW_ORDER] != ARG_UNDEFINED_VALUE)
1660 g->draw_order = parameter[GFX_ARG_DRAW_ORDER];
1662 // optional graphic for cloning all graphics settings
1663 if (parameter[GFX_ARG_CLONE_FROM] != ARG_UNDEFINED_VALUE)
1664 g->clone_from = parameter[GFX_ARG_CLONE_FROM];
1666 // optional settings for drawing title screens and title messages
1667 if (parameter[GFX_ARG_FADE_MODE] != ARG_UNDEFINED_VALUE)
1668 g->fade_mode = parameter[GFX_ARG_FADE_MODE];
1669 if (parameter[GFX_ARG_FADE_DELAY] != ARG_UNDEFINED_VALUE)
1670 g->fade_delay = parameter[GFX_ARG_FADE_DELAY];
1671 if (parameter[GFX_ARG_POST_DELAY] != ARG_UNDEFINED_VALUE)
1672 g->post_delay = parameter[GFX_ARG_POST_DELAY];
1673 if (parameter[GFX_ARG_AUTO_DELAY] != ARG_UNDEFINED_VALUE)
1674 g->auto_delay = parameter[GFX_ARG_AUTO_DELAY];
1675 if (parameter[GFX_ARG_AUTO_DELAY_UNIT] != ARG_UNDEFINED_VALUE)
1676 g->auto_delay_unit = parameter[GFX_ARG_AUTO_DELAY_UNIT];
1677 if (parameter[GFX_ARG_ALIGN] != ARG_UNDEFINED_VALUE)
1678 g->align = parameter[GFX_ARG_ALIGN];
1679 if (parameter[GFX_ARG_VALIGN] != ARG_UNDEFINED_VALUE)
1680 g->valign = parameter[GFX_ARG_VALIGN];
1681 if (parameter[GFX_ARG_SORT_PRIORITY] != ARG_UNDEFINED_VALUE)
1682 g->sort_priority = parameter[GFX_ARG_SORT_PRIORITY];
1684 if (parameter[GFX_ARG_CLASS] != ARG_UNDEFINED_VALUE)
1685 g->class = parameter[GFX_ARG_CLASS];
1686 if (parameter[GFX_ARG_STYLE] != ARG_UNDEFINED_VALUE)
1687 g->style = parameter[GFX_ARG_STYLE];
1689 // this is only used for drawing menu buttons and text
1690 g->active_xoffset = parameter[GFX_ARG_ACTIVE_XOFFSET];
1691 g->active_yoffset = parameter[GFX_ARG_ACTIVE_YOFFSET];
1692 g->pressed_xoffset = parameter[GFX_ARG_PRESSED_XOFFSET];
1693 g->pressed_yoffset = parameter[GFX_ARG_PRESSED_YOFFSET];
1696 static void set_graphic_parameters(int graphic)
1698 struct FileInfo *image = getImageListEntryFromImageID(graphic);
1699 char **parameter_raw = image->parameter;
1700 Bitmap **src_bitmaps = getBitmapsFromImageID(graphic);
1701 int parameter[NUM_GFX_ARGS];
1704 // if fallback to default artwork is done, also use the default parameters
1705 if (image->fallback_to_default)
1706 parameter_raw = image->default_parameter;
1708 // get integer values from string parameters
1709 for (i = 0; i < NUM_GFX_ARGS; i++)
1710 parameter[i] = get_graphic_parameter_value(parameter_raw[i],
1711 image_config_suffix[i].token,
1712 image_config_suffix[i].type);
1714 set_graphic_parameters_ext(graphic, parameter, src_bitmaps);
1716 UPDATE_BUSY_STATE();
1719 static void set_cloned_graphic_parameters(int graphic)
1721 int fallback_graphic = IMG_CHAR_EXCLAM;
1722 int max_num_images = getImageListSize();
1723 int clone_graphic = graphic_info[graphic].clone_from;
1724 int num_references_followed = 1;
1726 while (graphic_info[clone_graphic].clone_from != -1 &&
1727 num_references_followed < max_num_images)
1729 clone_graphic = graphic_info[clone_graphic].clone_from;
1731 num_references_followed++;
1734 if (num_references_followed >= max_num_images)
1737 Warn("error found in config file:");
1738 Warn("- config file: '%s'", getImageConfigFilename());
1739 Warn("- config token: '%s'", getTokenFromImageID(graphic));
1740 Warn("error: loop discovered when resolving cloned graphics");
1741 Warn("custom graphic rejected for this element/action");
1743 if (graphic == fallback_graphic)
1744 Fail("no fallback graphic available");
1746 Warn("fallback done to 'char_exclam' for this graphic");
1749 graphic_info[graphic] = graphic_info[fallback_graphic];
1753 graphic_info[graphic] = graphic_info[clone_graphic];
1754 graphic_info[graphic].clone_from = clone_graphic;
1758 static void InitGraphicInfo(void)
1760 int fallback_graphic = IMG_CHAR_EXCLAM;
1761 int num_images = getImageListSize();
1764 // use image size as default values for width and height for these images
1765 static int full_size_graphics[] =
1768 IMG_GLOBAL_BORDER_MAIN,
1769 IMG_GLOBAL_BORDER_SCORES,
1770 IMG_GLOBAL_BORDER_EDITOR,
1771 IMG_GLOBAL_BORDER_PLAYING,
1774 IMG_BACKGROUND_ENVELOPE_1,
1775 IMG_BACKGROUND_ENVELOPE_2,
1776 IMG_BACKGROUND_ENVELOPE_3,
1777 IMG_BACKGROUND_ENVELOPE_4,
1778 IMG_BACKGROUND_REQUEST,
1781 IMG_BACKGROUND_LOADING_INITIAL,
1782 IMG_BACKGROUND_LOADING,
1783 IMG_BACKGROUND_TITLE_INITIAL,
1784 IMG_BACKGROUND_TITLE,
1785 IMG_BACKGROUND_MAIN,
1786 IMG_BACKGROUND_NAMES,
1787 IMG_BACKGROUND_LEVELS,
1788 IMG_BACKGROUND_LEVELNR,
1789 IMG_BACKGROUND_SCORES,
1790 IMG_BACKGROUND_SCOREINFO,
1791 IMG_BACKGROUND_EDITOR,
1792 IMG_BACKGROUND_INFO,
1793 IMG_BACKGROUND_INFO_ELEMENTS,
1794 IMG_BACKGROUND_INFO_MUSIC,
1795 IMG_BACKGROUND_INFO_CREDITS,
1796 IMG_BACKGROUND_INFO_PROGRAM,
1797 IMG_BACKGROUND_INFO_VERSION,
1798 IMG_BACKGROUND_INFO_LEVELSET,
1799 IMG_BACKGROUND_SETUP,
1800 IMG_BACKGROUND_PLAYING,
1801 IMG_BACKGROUND_DOOR,
1802 IMG_BACKGROUND_TAPE,
1803 IMG_BACKGROUND_PANEL,
1804 IMG_BACKGROUND_PALETTE,
1805 IMG_BACKGROUND_TOOLBOX,
1807 IMG_TITLESCREEN_INITIAL_1,
1808 IMG_TITLESCREEN_INITIAL_2,
1809 IMG_TITLESCREEN_INITIAL_3,
1810 IMG_TITLESCREEN_INITIAL_4,
1811 IMG_TITLESCREEN_INITIAL_5,
1818 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_1,
1819 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_2,
1820 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_3,
1821 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_4,
1822 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_5,
1823 IMG_BACKGROUND_TITLEMESSAGE_1,
1824 IMG_BACKGROUND_TITLEMESSAGE_2,
1825 IMG_BACKGROUND_TITLEMESSAGE_3,
1826 IMG_BACKGROUND_TITLEMESSAGE_4,
1827 IMG_BACKGROUND_TITLEMESSAGE_5,
1832 FreeGlobalAnimEventInfo();
1834 checked_free(graphic_info);
1836 graphic_info = checked_calloc(num_images * sizeof(struct GraphicInfo));
1838 // initialize "use_image_size" flag with default value
1839 for (i = 0; i < num_images; i++)
1840 graphic_info[i].use_image_size = FALSE;
1842 // initialize "use_image_size" flag from static configuration above
1843 for (i = 0; full_size_graphics[i] != -1; i++)
1844 graphic_info[full_size_graphics[i]].use_image_size = TRUE;
1846 // first set all graphic paramaters ...
1847 for (i = 0; i < num_images; i++)
1848 set_graphic_parameters(i);
1850 // ... then copy these parameters for cloned graphics
1851 for (i = 0; i < num_images; i++)
1852 if (graphic_info[i].clone_from != -1)
1853 set_cloned_graphic_parameters(i);
1855 for (i = 0; i < num_images; i++)
1857 Bitmap *src_bitmap = graphic_info[i].bitmap;
1861 int src_bitmap_width, src_bitmap_height;
1863 // now check if no animation frames are outside of the loaded image
1865 if (graphic_info[i].bitmap == NULL)
1866 continue; // skip check for optional images that are undefined
1868 // get image size (this can differ from the standard element tile size!)
1869 width = graphic_info[i].width;
1870 height = graphic_info[i].height;
1872 // get final bitmap size (with scaling, but without small images)
1873 src_bitmap_width = graphic_info[i].src_image_width;
1874 src_bitmap_height = graphic_info[i].src_image_height;
1876 // check if first animation frame is inside specified bitmap
1878 // do not use getGraphicSourceXY() here to get position of first frame;
1879 // this avoids calculating wrong start position for out-of-bounds frame
1880 src_x = graphic_info[i].src_x;
1881 src_y = graphic_info[i].src_y;
1883 if (program.headless)
1886 if (src_x < 0 || src_y < 0 ||
1887 src_x + width > src_bitmap_width ||
1888 src_y + height > src_bitmap_height)
1891 Warn("error found in config file:");
1892 Warn("- config file: '%s'", getImageConfigFilename());
1893 Warn("- config token: '%s'", getTokenFromImageID(i));
1894 Warn("- image file: '%s'", src_bitmap->source_filename);
1895 Warn("- frame size: %d, %d", width, height);
1896 Warn("error: first animation frame out of bounds (%d, %d) [%d, %d]",
1897 src_x, src_y, src_bitmap_width, src_bitmap_height);
1898 Warn("custom graphic rejected for this element/action");
1900 if (i == fallback_graphic)
1901 Fail("no fallback graphic available");
1903 Warn("fallback done to 'char_exclam' for this graphic");
1906 graphic_info[i] = graphic_info[fallback_graphic];
1908 // if first frame out of bounds, do not check last frame anymore
1912 // check if last animation frame is inside specified bitmap
1914 last_frame = graphic_info[i].anim_frames - 1;
1915 getGraphicSourceXY(i, last_frame, &src_x, &src_y, FALSE);
1917 if (src_x < 0 || src_y < 0 ||
1918 src_x + width > src_bitmap_width ||
1919 src_y + height > src_bitmap_height)
1922 Warn("error found in config file:");
1923 Warn("- config file: '%s'", getImageConfigFilename());
1924 Warn("- config token: '%s'", getTokenFromImageID(i));
1925 Warn("- image file: '%s'", src_bitmap->source_filename);
1926 Warn("- frame size: %d, %d", width, height);
1927 Warn("error: last animation frame (%d) out of bounds (%d, %d) [%d, %d]",
1928 last_frame, src_x, src_y, src_bitmap_width, src_bitmap_height);
1929 Warn("custom graphic rejected for this element/action");
1931 if (i == fallback_graphic)
1932 Fail("no fallback graphic available");
1934 Warn("fallback done to 'char_exclam' for this graphic");
1937 graphic_info[i] = graphic_info[fallback_graphic];
1942 static void InitGraphicCompatibilityInfo(void)
1944 struct FileInfo *fi_global_door =
1945 getImageListEntryFromImageID(IMG_GLOBAL_DOOR);
1946 int num_images = getImageListSize();
1949 /* the following compatibility handling is needed for the following case:
1950 versions up to 3.3.0.0 used one large bitmap "global.door" for various
1951 graphics mainly used for door and panel graphics, like editor, tape and
1952 in-game buttons with hard-coded bitmap positions and button sizes; as
1953 these graphics now have individual definitions, redefining "global.door"
1954 to change all these graphics at once like before does not work anymore
1955 (because all those individual definitions still have their default values);
1956 to solve this, remap all those individual definitions that are not
1957 redefined to the new bitmap of "global.door" if it was redefined */
1959 // special compatibility handling if image "global.door" was redefined
1960 if (fi_global_door->redefined)
1962 for (i = 0; i < num_images; i++)
1964 struct FileInfo *fi = getImageListEntryFromImageID(i);
1966 // process only those images that still use the default settings
1969 // process all images which default to same image as "global.door"
1970 if (strEqual(fi->default_filename, fi_global_door->default_filename))
1972 // skip all images that are cloned from images that default to same
1973 // image as "global.door", but that are redefined to something else
1974 if (graphic_info[i].clone_from != -1)
1976 int cloned_graphic = graphic_info[i].clone_from;
1978 if (getImageListEntryFromImageID(cloned_graphic)->redefined)
1983 Debug("init:InitGraphicCompatibilityInfo",
1984 "special treatment needed for token '%s'", fi->token);
1987 graphic_info[i].bitmaps = graphic_info[IMG_GLOBAL_DOOR].bitmaps;
1988 graphic_info[i].bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
1994 // special compatibility handling for "Snake Bite" graphics set
1995 if (strPrefix(leveldir_current->identifier, "snake_bite"))
1997 Bitmap *bitmap = graphic_info[IMG_BACKGROUND_SCORES].bitmap;
1999 BlitBitmap(bitmap, bitmap, 18, 66, 32, 480, 50, 66);
2000 BlitBitmap(bitmap, bitmap, 466, 66, 32, 480, 434, 66);
2002 ClearRectangle(bitmap, 2, 66, 32, 480);
2003 ClearRectangle(bitmap, 514, 66, 32, 480);
2006 // special compatibility handling for "Jue" graphics sets (2007 and 2019)
2007 boolean supports_score_info = (menu.draw_xoffset[GAME_MODE_SCOREINFO] != 0);
2008 if (strPrefix(artwork.gfx_current_identifier, "jue") && !supports_score_info)
2026 int mode_old = GAME_MODE_SCORES;
2027 int mode_new = GAME_MODE_SCOREINFO;
2030 // adjust title screens on score info page
2031 for (i = 0; font_title[i] != -1; i++)
2033 struct FontInfo *fi = &font_info[font_title[i]];
2035 fi->special_graphic[mode_new] = fi->special_graphic[mode_old];
2036 fi->special_bitmap_id[mode_new] = fi->special_bitmap_id[mode_old];
2039 // adjust vertical text and button positions on scores page
2040 for (i = 0; font_text[i] != -1; i++)
2042 for (j = 0; j < 2; j++)
2044 boolean jue0 = strEqual(artwork.gfx_current_identifier, "jue0");
2045 int font_nr = (j == 0 ? font_text[i] : FONT_ACTIVE(font_text[i]));
2046 int font_bitmap_id = font_info[font_nr].special_bitmap_id[mode_old];
2047 int font_yoffset = (jue0 ? 10 : 5);
2049 gfx.font_bitmap_info[font_bitmap_id].draw_yoffset = font_yoffset;
2053 // adjust page offsets on score info page
2054 menu.draw_xoffset[mode_new] = menu.draw_xoffset[mode_old];
2055 menu.draw_yoffset[mode_new] = menu.draw_yoffset[mode_old];
2058 InitGraphicCompatibilityInfo_Doors();
2061 static void InitElementSoundInfo(void)
2063 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
2064 int num_property_mappings = getSoundListPropertyMappingSize();
2067 // set values to -1 to identify later as "uninitialized" values
2068 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2069 for (act = 0; act < NUM_ACTIONS; act++)
2070 element_info[i].sound[act] = -1;
2072 // initialize element/sound mapping from static configuration
2073 for (i = 0; element_to_sound[i].element > -1; i++)
2075 int element = element_to_sound[i].element;
2076 int action = element_to_sound[i].action;
2077 int sound = element_to_sound[i].sound;
2078 boolean is_class = element_to_sound[i].is_class;
2081 action = ACTION_DEFAULT;
2084 element_info[element].sound[action] = sound;
2086 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2087 if (strEqual(element_info[j].class_name,
2088 element_info[element].class_name))
2089 element_info[j].sound[action] = sound;
2092 // initialize element class/sound mapping from dynamic configuration
2093 for (i = 0; i < num_property_mappings; i++)
2095 int element_class = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
2096 int action = property_mapping[i].ext1_index;
2097 int sound = property_mapping[i].artwork_index;
2099 if (element_class < 0 || element_class >= MAX_NUM_ELEMENTS)
2103 action = ACTION_DEFAULT;
2105 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2106 if (strEqual(element_info[j].class_name,
2107 element_info[element_class].class_name))
2108 element_info[j].sound[action] = sound;
2111 // initialize element/sound mapping from dynamic configuration
2112 for (i = 0; i < num_property_mappings; i++)
2114 int element = property_mapping[i].base_index;
2115 int action = property_mapping[i].ext1_index;
2116 int sound = property_mapping[i].artwork_index;
2118 if (element >= MAX_NUM_ELEMENTS)
2122 action = ACTION_DEFAULT;
2124 element_info[element].sound[action] = sound;
2127 // now set all '-1' values to element specific default values
2128 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2130 for (act = 0; act < NUM_ACTIONS; act++)
2132 // generic default action sound (defined by "[default]" directive)
2133 int default_action_sound = element_info[EL_DEFAULT].sound[act];
2135 // look for special default action sound (classic game specific)
2136 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].sound[act] != -1)
2137 default_action_sound = element_info[EL_BD_DEFAULT].sound[act];
2138 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].sound[act] != -1)
2139 default_action_sound = element_info[EL_SP_DEFAULT].sound[act];
2140 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].sound[act] != -1)
2141 default_action_sound = element_info[EL_SB_DEFAULT].sound[act];
2142 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].sound[act] != -1)
2143 default_action_sound = element_info[EL_MM_DEFAULT].sound[act];
2145 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
2146 // !!! make this better !!!
2147 if (i == EL_EMPTY_SPACE)
2148 default_action_sound = element_info[EL_DEFAULT].sound[act];
2150 // no sound for this specific action -- use default action sound
2151 if (element_info[i].sound[act] == -1)
2152 element_info[i].sound[act] = default_action_sound;
2156 // copy sound settings to some elements that are only stored in level file
2157 // in native R'n'D levels, but are used by game engine in native EM levels
2158 for (i = 0; copy_properties[i][0] != -1; i++)
2159 for (j = 1; j <= 4; j++)
2160 for (act = 0; act < NUM_ACTIONS; act++)
2161 element_info[copy_properties[i][j]].sound[act] =
2162 element_info[copy_properties[i][0]].sound[act];
2165 static void InitGameModeSoundInfo(void)
2169 // set values to -1 to identify later as "uninitialized" values
2170 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2173 // initialize gamemode/sound mapping from static configuration
2174 for (i = 0; gamemode_to_sound[i].sound > -1; i++)
2176 int gamemode = gamemode_to_sound[i].gamemode;
2177 int sound = gamemode_to_sound[i].sound;
2180 gamemode = GAME_MODE_DEFAULT;
2182 menu.sound[gamemode] = sound;
2185 // now set all '-1' values to levelset specific default values
2186 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2187 if (menu.sound[i] == -1)
2188 menu.sound[i] = menu.sound[GAME_MODE_DEFAULT];
2191 static void set_sound_parameters(int sound, char **parameter_raw)
2193 int parameter[NUM_SND_ARGS];
2196 // get integer values from string parameters
2197 for (i = 0; i < NUM_SND_ARGS; i++)
2199 get_parameter_value(parameter_raw[i],
2200 sound_config_suffix[i].token,
2201 sound_config_suffix[i].type);
2203 // explicit loop mode setting in configuration overrides default value
2204 if (parameter[SND_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2205 sound_info[sound].loop = parameter[SND_ARG_MODE_LOOP];
2207 // sound volume to change the original volume when loading the sound file
2208 sound_info[sound].volume = parameter[SND_ARG_VOLUME];
2210 // sound priority to give certain sounds a higher or lower priority
2211 sound_info[sound].priority = parameter[SND_ARG_PRIORITY];
2214 static void InitSoundInfo(void)
2216 int *sound_effect_properties;
2217 int num_sounds = getSoundListSize();
2220 checked_free(sound_info);
2222 sound_effect_properties = checked_calloc(num_sounds * sizeof(int));
2223 sound_info = checked_calloc(num_sounds * sizeof(struct SoundInfo));
2225 // initialize sound effect for all elements to "no sound"
2226 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2227 for (j = 0; j < NUM_ACTIONS; j++)
2228 element_info[i].sound[j] = SND_UNDEFINED;
2230 for (i = 0; i < num_sounds; i++)
2232 struct FileInfo *sound = getSoundListEntry(i);
2233 int len_effect_text = strlen(sound->token);
2235 sound_effect_properties[i] = ACTION_OTHER;
2236 sound_info[i].loop = FALSE; // default: play sound only once
2238 // determine all loop sounds and identify certain sound classes
2240 for (j = 0; element_action_info[j].suffix; j++)
2242 int len_action_text = strlen(element_action_info[j].suffix);
2244 if (len_action_text < len_effect_text &&
2245 strEqual(&sound->token[len_effect_text - len_action_text],
2246 element_action_info[j].suffix))
2248 sound_effect_properties[i] = element_action_info[j].value;
2249 sound_info[i].loop = element_action_info[j].is_loop_sound;
2255 // associate elements and some selected sound actions
2257 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2259 if (element_info[j].class_name)
2261 int len_class_text = strlen(element_info[j].class_name);
2263 if (len_class_text + 1 < len_effect_text &&
2264 strncmp(sound->token,
2265 element_info[j].class_name, len_class_text) == 0 &&
2266 sound->token[len_class_text] == '.')
2268 int sound_action_value = sound_effect_properties[i];
2270 element_info[j].sound[sound_action_value] = i;
2275 set_sound_parameters(i, sound->parameter);
2278 free(sound_effect_properties);
2281 static void InitGameModeMusicInfo(void)
2283 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
2284 int num_property_mappings = getMusicListPropertyMappingSize();
2285 int default_levelset_music = -1;
2288 // set values to -1 to identify later as "uninitialized" values
2289 for (i = 0; i < MAX_LEVELS; i++)
2290 levelset.music[i] = -1;
2291 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2294 // initialize gamemode/music mapping from static configuration
2295 for (i = 0; gamemode_to_music[i].music > -1; i++)
2297 int gamemode = gamemode_to_music[i].gamemode;
2298 int music = gamemode_to_music[i].music;
2301 gamemode = GAME_MODE_DEFAULT;
2303 menu.music[gamemode] = music;
2306 // initialize gamemode/music mapping from dynamic configuration
2307 for (i = 0; i < num_property_mappings; i++)
2309 int prefix = property_mapping[i].base_index;
2310 int gamemode = property_mapping[i].ext2_index;
2311 int level = property_mapping[i].ext3_index;
2312 int music = property_mapping[i].artwork_index;
2314 if (prefix < 0 || prefix >= NUM_MUSIC_PREFIXES)
2318 gamemode = GAME_MODE_DEFAULT;
2320 // level specific music only allowed for in-game music
2321 if (level != -1 && gamemode == GAME_MODE_DEFAULT)
2322 gamemode = GAME_MODE_PLAYING;
2327 default_levelset_music = music;
2330 if (gamemode == GAME_MODE_PLAYING || gamemode == GAME_MODE_DEFAULT)
2331 levelset.music[level] = music;
2332 if (gamemode != GAME_MODE_PLAYING)
2333 menu.music[gamemode] = music;
2336 // now set all '-1' values to menu specific default values
2337 // (undefined values of "levelset.music[]" might stay at "-1" to
2338 // allow dynamic selection of music files from music directory!)
2339 for (i = 0; i < MAX_LEVELS; i++)
2340 if (levelset.music[i] == -1)
2341 levelset.music[i] = default_levelset_music;
2342 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2343 if (menu.music[i] == -1)
2344 menu.music[i] = menu.music[GAME_MODE_DEFAULT];
2347 static void set_music_parameters(int music, char **parameter_raw)
2349 int parameter[NUM_MUS_ARGS];
2352 // get integer values from string parameters
2353 for (i = 0; i < NUM_MUS_ARGS; i++)
2355 get_parameter_value(parameter_raw[i],
2356 music_config_suffix[i].token,
2357 music_config_suffix[i].type);
2359 // explicit loop mode setting in configuration overrides default value
2360 if (parameter[MUS_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2361 music_info[music].loop = parameter[MUS_ARG_MODE_LOOP];
2364 static void InitMusicInfo(void)
2366 int num_music = getMusicListSize();
2369 checked_free(music_info);
2371 music_info = checked_calloc(num_music * sizeof(struct MusicInfo));
2373 for (i = 0; i < num_music; i++)
2375 struct FileInfo *music = getMusicListEntry(i);
2376 int len_music_text = strlen(music->token);
2378 music_info[i].loop = TRUE; // default: play music in loop mode
2380 // determine all loop music
2382 for (j = 0; music_prefix_info[j].prefix; j++)
2384 int len_prefix_text = strlen(music_prefix_info[j].prefix);
2386 if (len_prefix_text < len_music_text &&
2387 strncmp(music->token,
2388 music_prefix_info[j].prefix, len_prefix_text) == 0)
2390 music_info[i].loop = music_prefix_info[j].is_loop_music;
2396 set_music_parameters(i, music->parameter);
2401 static void InitGameInfoFromArtworkInfo(void)
2403 // special case: store initial value of custom artwork setting
2404 game.use_masked_elements_initial = game.use_masked_elements;
2407 static void ReinitializeGraphics(void)
2409 print_timestamp_init("ReinitializeGraphics");
2411 InitGfxTileSizeInfo(game.tile_size, TILESIZE);
2413 InitGraphicInfo(); // graphic properties mapping
2414 print_timestamp_time("InitGraphicInfo");
2415 InitElementGraphicInfo(); // element game graphic mapping
2416 print_timestamp_time("InitElementGraphicInfo");
2417 InitElementSpecialGraphicInfo(); // element special graphic mapping
2418 print_timestamp_time("InitElementSpecialGraphicInfo");
2420 InitElementSmallImages(); // scale elements to all needed sizes
2421 print_timestamp_time("InitElementSmallImages");
2422 InitScaledImages(); // scale all other images, if needed
2423 print_timestamp_time("InitScaledImages");
2424 InitBitmapPointers(); // set standard size bitmap pointers
2425 print_timestamp_time("InitBitmapPointers");
2426 InitFontGraphicInfo(); // initialize text drawing functions
2427 print_timestamp_time("InitFontGraphicInfo");
2428 InitGlobalAnimGraphicInfo(); // initialize global animation config
2429 print_timestamp_time("InitGlobalAnimGraphicInfo");
2431 InitImageTextures(); // create textures for certain images
2432 print_timestamp_time("InitImageTextures");
2434 InitGraphicInfo_EM(); // graphic mapping for EM engine
2435 print_timestamp_time("InitGraphicInfo_EM");
2437 InitGraphicCompatibilityInfo();
2438 print_timestamp_time("InitGraphicCompatibilityInfo");
2441 print_timestamp_time("InitGadgets");
2443 print_timestamp_time("InitDoors");
2445 InitGameInfoFromArtworkInfo();
2447 print_timestamp_done("ReinitializeGraphics");
2450 static void ReinitializeSounds(void)
2452 InitSoundInfo(); // sound properties mapping
2453 InitElementSoundInfo(); // element game sound mapping
2454 InitGameModeSoundInfo(); // game mode sound mapping
2455 InitGlobalAnimSoundInfo(); // global animation sound settings
2457 InitPlayLevelSound(); // internal game sound settings
2460 static void ReinitializeMusic(void)
2462 InitMusicInfo(); // music properties mapping
2463 InitGameModeMusicInfo(); // game mode music mapping
2464 InitGlobalAnimMusicInfo(); // global animation music settings
2467 static int get_special_property_bit(int element, int property_bit_nr)
2469 struct PropertyBitInfo
2475 static struct PropertyBitInfo pb_can_move_into_acid[] =
2477 // the player may be able fall into acid when gravity is activated
2482 { EL_SP_MURPHY, 0 },
2483 { EL_SOKOBAN_FIELD_PLAYER, 0 },
2485 // all elements that can move may be able to also move into acid
2488 { EL_BUG_RIGHT, 1 },
2491 { EL_SPACESHIP, 2 },
2492 { EL_SPACESHIP_LEFT, 2 },
2493 { EL_SPACESHIP_RIGHT, 2 },
2494 { EL_SPACESHIP_UP, 2 },
2495 { EL_SPACESHIP_DOWN, 2 },
2496 { EL_BD_BUTTERFLY, 3 },
2497 { EL_BD_BUTTERFLY_LEFT, 3 },
2498 { EL_BD_BUTTERFLY_RIGHT, 3 },
2499 { EL_BD_BUTTERFLY_UP, 3 },
2500 { EL_BD_BUTTERFLY_DOWN, 3 },
2501 { EL_BD_FIREFLY, 4 },
2502 { EL_BD_FIREFLY_LEFT, 4 },
2503 { EL_BD_FIREFLY_RIGHT, 4 },
2504 { EL_BD_FIREFLY_UP, 4 },
2505 { EL_BD_FIREFLY_DOWN, 4 },
2507 { EL_YAMYAM_LEFT, 5 },
2508 { EL_YAMYAM_RIGHT, 5 },
2509 { EL_YAMYAM_UP, 5 },
2510 { EL_YAMYAM_DOWN, 5 },
2511 { EL_DARK_YAMYAM, 6 },
2514 { EL_PACMAN_LEFT, 8 },
2515 { EL_PACMAN_RIGHT, 8 },
2516 { EL_PACMAN_UP, 8 },
2517 { EL_PACMAN_DOWN, 8 },
2519 { EL_MOLE_LEFT, 9 },
2520 { EL_MOLE_RIGHT, 9 },
2522 { EL_MOLE_DOWN, 9 },
2526 { EL_SATELLITE, 13 },
2527 { EL_SP_SNIKSNAK, 14 },
2528 { EL_SP_ELECTRON, 15 },
2531 { EL_SPRING_LEFT, 17 },
2532 { EL_SPRING_RIGHT, 17 },
2533 { EL_EMC_ANDROID, 18 },
2538 static struct PropertyBitInfo pb_dont_collide_with[] =
2540 { EL_SP_SNIKSNAK, 0 },
2541 { EL_SP_ELECTRON, 1 },
2549 struct PropertyBitInfo *pb_info;
2552 { EP_CAN_MOVE_INTO_ACID, pb_can_move_into_acid },
2553 { EP_DONT_COLLIDE_WITH, pb_dont_collide_with },
2558 struct PropertyBitInfo *pb_info = NULL;
2561 for (i = 0; pb_definition[i].bit_nr != -1; i++)
2562 if (pb_definition[i].bit_nr == property_bit_nr)
2563 pb_info = pb_definition[i].pb_info;
2565 if (pb_info == NULL)
2568 for (i = 0; pb_info[i].element != -1; i++)
2569 if (pb_info[i].element == element)
2570 return pb_info[i].bit_nr;
2575 void setBitfieldProperty(int *bitfield, int property_bit_nr, int element,
2576 boolean property_value)
2578 int bit_nr = get_special_property_bit(element, property_bit_nr);
2583 *bitfield |= (1 << bit_nr);
2585 *bitfield &= ~(1 << bit_nr);
2589 boolean getBitfieldProperty(int *bitfield, int property_bit_nr, int element)
2591 int bit_nr = get_special_property_bit(element, property_bit_nr);
2594 return ((*bitfield & (1 << bit_nr)) != 0);
2599 static void ResolveGroupElementExt(int group_element, int recursion_depth)
2601 static int group_nr;
2602 static struct ElementGroupInfo *group;
2603 struct ElementGroupInfo *actual_group = element_info[group_element].group;
2606 if (actual_group == NULL) // not yet initialized
2609 if (recursion_depth > NUM_GROUP_ELEMENTS) // recursion too deep
2611 Warn("recursion too deep when resolving group element %d",
2612 group_element - EL_GROUP_START + 1);
2614 // replace element which caused too deep recursion by question mark
2615 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
2620 if (recursion_depth == 0) // initialization
2622 group = actual_group;
2623 group_nr = GROUP_NR(group_element);
2625 group->num_elements_resolved = 0;
2626 group->choice_pos = 0;
2628 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2629 element_info[i].in_group[group_nr] = FALSE;
2632 for (i = 0; i < actual_group->num_elements; i++)
2634 int element = actual_group->element[i];
2636 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
2639 if (IS_GROUP_ELEMENT(element))
2640 ResolveGroupElementExt(element, recursion_depth + 1);
2643 group->element_resolved[group->num_elements_resolved++] = element;
2644 element_info[element].in_group[group_nr] = TRUE;
2649 void ResolveGroupElement(int group_element)
2651 ResolveGroupElementExt(group_element, 0);
2654 void InitElementPropertiesStatic(void)
2656 static boolean clipboard_elements_initialized = FALSE;
2658 static int ep_diggable[] =
2663 EL_SP_BUGGY_BASE_ACTIVATING,
2666 EL_INVISIBLE_SAND_ACTIVE,
2669 // !!! currently not diggable, but handled by 'ep_dont_run_into' !!!
2670 // (if amoeba can grow into anything diggable, maybe keep these out)
2675 EL_SP_BUGGY_BASE_ACTIVE,
2682 static int ep_collectible_only[] =
2704 EL_DYNABOMB_INCREASE_NUMBER,
2705 EL_DYNABOMB_INCREASE_SIZE,
2706 EL_DYNABOMB_INCREASE_POWER,
2724 // !!! handle separately !!!
2725 EL_DC_LANDMINE, // deadly when running into, but can be snapped
2731 static int ep_dont_run_into[] =
2733 // same elements as in 'ep_dont_touch'
2739 // same elements as in 'ep_dont_collide_with'
2751 // !!! maybe this should better be handled by 'ep_diggable' !!!
2756 EL_SP_BUGGY_BASE_ACTIVE,
2763 static int ep_dont_collide_with[] =
2765 // same elements as in 'ep_dont_touch'
2782 static int ep_dont_touch[] =
2792 static int ep_indestructible[] =
2796 EL_ACID_POOL_TOPLEFT,
2797 EL_ACID_POOL_TOPRIGHT,
2798 EL_ACID_POOL_BOTTOMLEFT,
2799 EL_ACID_POOL_BOTTOM,
2800 EL_ACID_POOL_BOTTOMRIGHT,
2801 EL_SP_HARDWARE_GRAY,
2802 EL_SP_HARDWARE_GREEN,
2803 EL_SP_HARDWARE_BLUE,
2805 EL_SP_HARDWARE_YELLOW,
2806 EL_SP_HARDWARE_BASE_1,
2807 EL_SP_HARDWARE_BASE_2,
2808 EL_SP_HARDWARE_BASE_3,
2809 EL_SP_HARDWARE_BASE_4,
2810 EL_SP_HARDWARE_BASE_5,
2811 EL_SP_HARDWARE_BASE_6,
2812 EL_INVISIBLE_STEELWALL,
2813 EL_INVISIBLE_STEELWALL_ACTIVE,
2814 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2815 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
2816 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
2817 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2818 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
2819 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
2820 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2821 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
2822 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
2823 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
2824 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
2825 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
2827 EL_LIGHT_SWITCH_ACTIVE,
2828 EL_SIGN_EXCLAMATION,
2829 EL_SIGN_RADIOACTIVITY,
2836 EL_SIGN_ENTRY_FORBIDDEN,
2837 EL_SIGN_EMERGENCY_EXIT,
2845 EL_STEEL_EXIT_CLOSED,
2847 EL_STEEL_EXIT_OPENING,
2848 EL_STEEL_EXIT_CLOSING,
2849 EL_EM_STEEL_EXIT_CLOSED,
2850 EL_EM_STEEL_EXIT_OPEN,
2851 EL_EM_STEEL_EXIT_OPENING,
2852 EL_EM_STEEL_EXIT_CLOSING,
2853 EL_DC_STEELWALL_1_LEFT,
2854 EL_DC_STEELWALL_1_RIGHT,
2855 EL_DC_STEELWALL_1_TOP,
2856 EL_DC_STEELWALL_1_BOTTOM,
2857 EL_DC_STEELWALL_1_HORIZONTAL,
2858 EL_DC_STEELWALL_1_VERTICAL,
2859 EL_DC_STEELWALL_1_TOPLEFT,
2860 EL_DC_STEELWALL_1_TOPRIGHT,
2861 EL_DC_STEELWALL_1_BOTTOMLEFT,
2862 EL_DC_STEELWALL_1_BOTTOMRIGHT,
2863 EL_DC_STEELWALL_1_TOPLEFT_2,
2864 EL_DC_STEELWALL_1_TOPRIGHT_2,
2865 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
2866 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
2867 EL_DC_STEELWALL_2_LEFT,
2868 EL_DC_STEELWALL_2_RIGHT,
2869 EL_DC_STEELWALL_2_TOP,
2870 EL_DC_STEELWALL_2_BOTTOM,
2871 EL_DC_STEELWALL_2_HORIZONTAL,
2872 EL_DC_STEELWALL_2_VERTICAL,
2873 EL_DC_STEELWALL_2_MIDDLE,
2874 EL_DC_STEELWALL_2_SINGLE,
2875 EL_STEELWALL_SLIPPERY,
2889 EL_GATE_1_GRAY_ACTIVE,
2890 EL_GATE_2_GRAY_ACTIVE,
2891 EL_GATE_3_GRAY_ACTIVE,
2892 EL_GATE_4_GRAY_ACTIVE,
2901 EL_EM_GATE_1_GRAY_ACTIVE,
2902 EL_EM_GATE_2_GRAY_ACTIVE,
2903 EL_EM_GATE_3_GRAY_ACTIVE,
2904 EL_EM_GATE_4_GRAY_ACTIVE,
2913 EL_EMC_GATE_5_GRAY_ACTIVE,
2914 EL_EMC_GATE_6_GRAY_ACTIVE,
2915 EL_EMC_GATE_7_GRAY_ACTIVE,
2916 EL_EMC_GATE_8_GRAY_ACTIVE,
2918 EL_DC_GATE_WHITE_GRAY,
2919 EL_DC_GATE_WHITE_GRAY_ACTIVE,
2920 EL_DC_GATE_FAKE_GRAY,
2922 EL_SWITCHGATE_OPENING,
2923 EL_SWITCHGATE_CLOSED,
2924 EL_SWITCHGATE_CLOSING,
2925 EL_DC_SWITCHGATE_SWITCH_UP,
2926 EL_DC_SWITCHGATE_SWITCH_DOWN,
2928 EL_TIMEGATE_OPENING,
2930 EL_TIMEGATE_CLOSING,
2931 EL_DC_TIMEGATE_SWITCH,
2932 EL_DC_TIMEGATE_SWITCH_ACTIVE,
2936 EL_TUBE_VERTICAL_LEFT,
2937 EL_TUBE_VERTICAL_RIGHT,
2938 EL_TUBE_HORIZONTAL_UP,
2939 EL_TUBE_HORIZONTAL_DOWN,
2944 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
2945 EL_EXPANDABLE_STEELWALL_VERTICAL,
2946 EL_EXPANDABLE_STEELWALL_ANY,
2951 static int ep_slippery[] =
2965 EL_ROBOT_WHEEL_ACTIVE,
2971 EL_ACID_POOL_TOPLEFT,
2972 EL_ACID_POOL_TOPRIGHT,
2982 EL_STEELWALL_SLIPPERY,
2985 EL_EMC_WALL_SLIPPERY_1,
2986 EL_EMC_WALL_SLIPPERY_2,
2987 EL_EMC_WALL_SLIPPERY_3,
2988 EL_EMC_WALL_SLIPPERY_4,
2990 EL_EMC_MAGIC_BALL_ACTIVE,
2995 static int ep_can_change[] =
3000 static int ep_can_move[] =
3002 // same elements as in 'pb_can_move_into_acid'
3025 static int ep_can_fall[] =
3040 EL_QUICKSAND_FAST_FULL,
3042 EL_BD_MAGIC_WALL_FULL,
3043 EL_DC_MAGIC_WALL_FULL,
3057 static int ep_can_smash_player[] =
3083 static int ep_can_smash_enemies[] =
3092 static int ep_can_smash_everything[] =
3101 static int ep_explodes_by_fire[] =
3103 // same elements as in 'ep_explodes_impact'
3108 // same elements as in 'ep_explodes_smashed'
3118 EL_EM_DYNAMITE_ACTIVE,
3119 EL_DYNABOMB_PLAYER_1_ACTIVE,
3120 EL_DYNABOMB_PLAYER_2_ACTIVE,
3121 EL_DYNABOMB_PLAYER_3_ACTIVE,
3122 EL_DYNABOMB_PLAYER_4_ACTIVE,
3123 EL_DYNABOMB_INCREASE_NUMBER,
3124 EL_DYNABOMB_INCREASE_SIZE,
3125 EL_DYNABOMB_INCREASE_POWER,
3126 EL_SP_DISK_RED_ACTIVE,
3140 static int ep_explodes_smashed[] =
3142 // same elements as in 'ep_explodes_impact'
3156 static int ep_explodes_impact[] =
3165 static int ep_walkable_over[] =
3185 EL_SOKOBAN_FIELD_EMPTY,
3192 EL_EM_STEEL_EXIT_OPEN,
3193 EL_EM_STEEL_EXIT_OPENING,
3202 EL_GATE_1_GRAY_ACTIVE,
3203 EL_GATE_2_GRAY_ACTIVE,
3204 EL_GATE_3_GRAY_ACTIVE,
3205 EL_GATE_4_GRAY_ACTIVE,
3213 static int ep_walkable_inside[] =
3218 EL_TUBE_VERTICAL_LEFT,
3219 EL_TUBE_VERTICAL_RIGHT,
3220 EL_TUBE_HORIZONTAL_UP,
3221 EL_TUBE_HORIZONTAL_DOWN,
3230 static int ep_walkable_under[] =
3235 static int ep_passable_over[] =
3245 EL_EM_GATE_1_GRAY_ACTIVE,
3246 EL_EM_GATE_2_GRAY_ACTIVE,
3247 EL_EM_GATE_3_GRAY_ACTIVE,
3248 EL_EM_GATE_4_GRAY_ACTIVE,
3257 EL_EMC_GATE_5_GRAY_ACTIVE,
3258 EL_EMC_GATE_6_GRAY_ACTIVE,
3259 EL_EMC_GATE_7_GRAY_ACTIVE,
3260 EL_EMC_GATE_8_GRAY_ACTIVE,
3262 EL_DC_GATE_WHITE_GRAY,
3263 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3270 static int ep_passable_inside[] =
3276 EL_SP_PORT_HORIZONTAL,
3277 EL_SP_PORT_VERTICAL,
3279 EL_SP_GRAVITY_PORT_LEFT,
3280 EL_SP_GRAVITY_PORT_RIGHT,
3281 EL_SP_GRAVITY_PORT_UP,
3282 EL_SP_GRAVITY_PORT_DOWN,
3283 EL_SP_GRAVITY_ON_PORT_LEFT,
3284 EL_SP_GRAVITY_ON_PORT_RIGHT,
3285 EL_SP_GRAVITY_ON_PORT_UP,
3286 EL_SP_GRAVITY_ON_PORT_DOWN,
3287 EL_SP_GRAVITY_OFF_PORT_LEFT,
3288 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3289 EL_SP_GRAVITY_OFF_PORT_UP,
3290 EL_SP_GRAVITY_OFF_PORT_DOWN,
3295 static int ep_passable_under[] =
3300 static int ep_droppable[] =
3305 static int ep_explodes_1x1_old[] =
3310 static int ep_pushable[] =
3322 EL_SOKOBAN_FIELD_FULL,
3331 static int ep_explodes_cross_old[] =
3336 static int ep_protected[] =
3338 // same elements as in 'ep_walkable_inside'
3342 EL_TUBE_VERTICAL_LEFT,
3343 EL_TUBE_VERTICAL_RIGHT,
3344 EL_TUBE_HORIZONTAL_UP,
3345 EL_TUBE_HORIZONTAL_DOWN,
3351 // same elements as in 'ep_passable_over'
3360 EL_EM_GATE_1_GRAY_ACTIVE,
3361 EL_EM_GATE_2_GRAY_ACTIVE,
3362 EL_EM_GATE_3_GRAY_ACTIVE,
3363 EL_EM_GATE_4_GRAY_ACTIVE,
3372 EL_EMC_GATE_5_GRAY_ACTIVE,
3373 EL_EMC_GATE_6_GRAY_ACTIVE,
3374 EL_EMC_GATE_7_GRAY_ACTIVE,
3375 EL_EMC_GATE_8_GRAY_ACTIVE,
3377 EL_DC_GATE_WHITE_GRAY,
3378 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3382 // same elements as in 'ep_passable_inside'
3387 EL_SP_PORT_HORIZONTAL,
3388 EL_SP_PORT_VERTICAL,
3390 EL_SP_GRAVITY_PORT_LEFT,
3391 EL_SP_GRAVITY_PORT_RIGHT,
3392 EL_SP_GRAVITY_PORT_UP,
3393 EL_SP_GRAVITY_PORT_DOWN,
3394 EL_SP_GRAVITY_ON_PORT_LEFT,
3395 EL_SP_GRAVITY_ON_PORT_RIGHT,
3396 EL_SP_GRAVITY_ON_PORT_UP,
3397 EL_SP_GRAVITY_ON_PORT_DOWN,
3398 EL_SP_GRAVITY_OFF_PORT_LEFT,
3399 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3400 EL_SP_GRAVITY_OFF_PORT_UP,
3401 EL_SP_GRAVITY_OFF_PORT_DOWN,
3406 static int ep_throwable[] =
3411 static int ep_can_explode[] =
3413 // same elements as in 'ep_explodes_impact'
3418 // same elements as in 'ep_explodes_smashed'
3424 // elements that can explode by explosion or by dragonfire
3428 EL_EM_DYNAMITE_ACTIVE,
3429 EL_DYNABOMB_PLAYER_1_ACTIVE,
3430 EL_DYNABOMB_PLAYER_2_ACTIVE,
3431 EL_DYNABOMB_PLAYER_3_ACTIVE,
3432 EL_DYNABOMB_PLAYER_4_ACTIVE,
3433 EL_DYNABOMB_INCREASE_NUMBER,
3434 EL_DYNABOMB_INCREASE_SIZE,
3435 EL_DYNABOMB_INCREASE_POWER,
3436 EL_SP_DISK_RED_ACTIVE,
3444 // elements that can explode only by explosion
3450 static int ep_gravity_reachable[] =
3456 EL_INVISIBLE_SAND_ACTIVE,
3461 EL_SP_PORT_HORIZONTAL,
3462 EL_SP_PORT_VERTICAL,
3464 EL_SP_GRAVITY_PORT_LEFT,
3465 EL_SP_GRAVITY_PORT_RIGHT,
3466 EL_SP_GRAVITY_PORT_UP,
3467 EL_SP_GRAVITY_PORT_DOWN,
3468 EL_SP_GRAVITY_ON_PORT_LEFT,
3469 EL_SP_GRAVITY_ON_PORT_RIGHT,
3470 EL_SP_GRAVITY_ON_PORT_UP,
3471 EL_SP_GRAVITY_ON_PORT_DOWN,
3472 EL_SP_GRAVITY_OFF_PORT_LEFT,
3473 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3474 EL_SP_GRAVITY_OFF_PORT_UP,
3475 EL_SP_GRAVITY_OFF_PORT_DOWN,
3481 static int ep_empty_space[] =
3504 static int ep_player[] =
3511 EL_SOKOBAN_FIELD_PLAYER,
3517 static int ep_can_pass_magic_wall[] =
3531 static int ep_can_pass_dc_magic_wall[] =
3547 static int ep_switchable[] =
3551 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3552 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3553 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3554 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3555 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3556 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3557 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3558 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3559 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3560 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3561 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3562 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3563 EL_SWITCHGATE_SWITCH_UP,
3564 EL_SWITCHGATE_SWITCH_DOWN,
3565 EL_DC_SWITCHGATE_SWITCH_UP,
3566 EL_DC_SWITCHGATE_SWITCH_DOWN,
3568 EL_LIGHT_SWITCH_ACTIVE,
3570 EL_DC_TIMEGATE_SWITCH,
3571 EL_BALLOON_SWITCH_LEFT,
3572 EL_BALLOON_SWITCH_RIGHT,
3573 EL_BALLOON_SWITCH_UP,
3574 EL_BALLOON_SWITCH_DOWN,
3575 EL_BALLOON_SWITCH_ANY,
3576 EL_BALLOON_SWITCH_NONE,
3579 EL_EMC_MAGIC_BALL_SWITCH,
3580 EL_EMC_MAGIC_BALL_SWITCH_ACTIVE,
3585 static int ep_bd_element[] =
3619 static int ep_sp_element[] =
3621 // should always be valid
3624 // standard classic Supaplex elements
3631 EL_SP_HARDWARE_GRAY,
3639 EL_SP_GRAVITY_PORT_RIGHT,
3640 EL_SP_GRAVITY_PORT_DOWN,
3641 EL_SP_GRAVITY_PORT_LEFT,
3642 EL_SP_GRAVITY_PORT_UP,
3647 EL_SP_PORT_VERTICAL,
3648 EL_SP_PORT_HORIZONTAL,
3654 EL_SP_HARDWARE_BASE_1,
3655 EL_SP_HARDWARE_GREEN,
3656 EL_SP_HARDWARE_BLUE,
3658 EL_SP_HARDWARE_YELLOW,
3659 EL_SP_HARDWARE_BASE_2,
3660 EL_SP_HARDWARE_BASE_3,
3661 EL_SP_HARDWARE_BASE_4,
3662 EL_SP_HARDWARE_BASE_5,
3663 EL_SP_HARDWARE_BASE_6,
3667 // additional elements that appeared in newer Supaplex levels
3670 // additional gravity port elements (not switching, but setting gravity)
3671 EL_SP_GRAVITY_ON_PORT_LEFT,
3672 EL_SP_GRAVITY_ON_PORT_RIGHT,
3673 EL_SP_GRAVITY_ON_PORT_UP,
3674 EL_SP_GRAVITY_ON_PORT_DOWN,
3675 EL_SP_GRAVITY_OFF_PORT_LEFT,
3676 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3677 EL_SP_GRAVITY_OFF_PORT_UP,
3678 EL_SP_GRAVITY_OFF_PORT_DOWN,
3680 // more than one Murphy in a level results in an inactive clone
3683 // runtime Supaplex elements
3684 EL_SP_DISK_RED_ACTIVE,
3685 EL_SP_TERMINAL_ACTIVE,
3686 EL_SP_BUGGY_BASE_ACTIVATING,
3687 EL_SP_BUGGY_BASE_ACTIVE,
3694 static int ep_sb_element[] =
3699 EL_SOKOBAN_FIELD_EMPTY,
3700 EL_SOKOBAN_FIELD_FULL,
3701 EL_SOKOBAN_FIELD_PLAYER,
3706 EL_INVISIBLE_STEELWALL,
3711 static int ep_gem[] =
3723 static int ep_food_dark_yamyam[] =
3751 static int ep_food_penguin[] =
3765 static int ep_food_pig[] =
3777 static int ep_historic_wall[] =
3788 EL_GATE_1_GRAY_ACTIVE,
3789 EL_GATE_2_GRAY_ACTIVE,
3790 EL_GATE_3_GRAY_ACTIVE,
3791 EL_GATE_4_GRAY_ACTIVE,
3800 EL_EM_GATE_1_GRAY_ACTIVE,
3801 EL_EM_GATE_2_GRAY_ACTIVE,
3802 EL_EM_GATE_3_GRAY_ACTIVE,
3803 EL_EM_GATE_4_GRAY_ACTIVE,
3810 EL_EXPANDABLE_WALL_HORIZONTAL,
3811 EL_EXPANDABLE_WALL_VERTICAL,
3812 EL_EXPANDABLE_WALL_ANY,
3813 EL_EXPANDABLE_WALL_GROWING,
3814 EL_BD_EXPANDABLE_WALL,
3821 EL_SP_HARDWARE_GRAY,
3822 EL_SP_HARDWARE_GREEN,
3823 EL_SP_HARDWARE_BLUE,
3825 EL_SP_HARDWARE_YELLOW,
3826 EL_SP_HARDWARE_BASE_1,
3827 EL_SP_HARDWARE_BASE_2,
3828 EL_SP_HARDWARE_BASE_3,
3829 EL_SP_HARDWARE_BASE_4,
3830 EL_SP_HARDWARE_BASE_5,
3831 EL_SP_HARDWARE_BASE_6,
3833 EL_SP_TERMINAL_ACTIVE,
3836 EL_INVISIBLE_STEELWALL,
3837 EL_INVISIBLE_STEELWALL_ACTIVE,
3839 EL_INVISIBLE_WALL_ACTIVE,
3840 EL_STEELWALL_SLIPPERY,
3857 static int ep_historic_solid[] =
3861 EL_EXPANDABLE_WALL_HORIZONTAL,
3862 EL_EXPANDABLE_WALL_VERTICAL,
3863 EL_EXPANDABLE_WALL_ANY,
3864 EL_BD_EXPANDABLE_WALL,
3877 EL_QUICKSAND_FILLING,
3878 EL_QUICKSAND_EMPTYING,
3880 EL_MAGIC_WALL_ACTIVE,
3881 EL_MAGIC_WALL_EMPTYING,
3882 EL_MAGIC_WALL_FILLING,
3886 EL_BD_MAGIC_WALL_ACTIVE,
3887 EL_BD_MAGIC_WALL_EMPTYING,
3888 EL_BD_MAGIC_WALL_FULL,
3889 EL_BD_MAGIC_WALL_FILLING,
3890 EL_BD_MAGIC_WALL_DEAD,
3899 EL_SP_TERMINAL_ACTIVE,
3903 EL_INVISIBLE_WALL_ACTIVE,
3904 EL_SWITCHGATE_SWITCH_UP,
3905 EL_SWITCHGATE_SWITCH_DOWN,
3907 EL_TIMEGATE_SWITCH_ACTIVE,
3919 // the following elements are a direct copy of "indestructible" elements,
3920 // except "EL_ACID", which is "indestructible", but not "solid"!
3925 EL_ACID_POOL_TOPLEFT,
3926 EL_ACID_POOL_TOPRIGHT,
3927 EL_ACID_POOL_BOTTOMLEFT,
3928 EL_ACID_POOL_BOTTOM,
3929 EL_ACID_POOL_BOTTOMRIGHT,
3930 EL_SP_HARDWARE_GRAY,
3931 EL_SP_HARDWARE_GREEN,
3932 EL_SP_HARDWARE_BLUE,
3934 EL_SP_HARDWARE_YELLOW,
3935 EL_SP_HARDWARE_BASE_1,
3936 EL_SP_HARDWARE_BASE_2,
3937 EL_SP_HARDWARE_BASE_3,
3938 EL_SP_HARDWARE_BASE_4,
3939 EL_SP_HARDWARE_BASE_5,
3940 EL_SP_HARDWARE_BASE_6,
3941 EL_INVISIBLE_STEELWALL,
3942 EL_INVISIBLE_STEELWALL_ACTIVE,
3943 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3944 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3945 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3946 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3947 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3948 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3949 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3950 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3951 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3952 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3953 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3954 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3956 EL_LIGHT_SWITCH_ACTIVE,
3957 EL_SIGN_EXCLAMATION,
3958 EL_SIGN_RADIOACTIVITY,
3965 EL_SIGN_ENTRY_FORBIDDEN,
3966 EL_SIGN_EMERGENCY_EXIT,
3974 EL_STEEL_EXIT_CLOSED,
3976 EL_STEEL_EXIT_OPENING,
3977 EL_STEEL_EXIT_CLOSING,
3978 EL_EM_STEEL_EXIT_CLOSED,
3979 EL_EM_STEEL_EXIT_OPEN,
3980 EL_EM_STEEL_EXIT_OPENING,
3981 EL_EM_STEEL_EXIT_CLOSING,
3982 EL_DC_STEELWALL_1_LEFT,
3983 EL_DC_STEELWALL_1_RIGHT,
3984 EL_DC_STEELWALL_1_TOP,
3985 EL_DC_STEELWALL_1_BOTTOM,
3986 EL_DC_STEELWALL_1_HORIZONTAL,
3987 EL_DC_STEELWALL_1_VERTICAL,
3988 EL_DC_STEELWALL_1_TOPLEFT,
3989 EL_DC_STEELWALL_1_TOPRIGHT,
3990 EL_DC_STEELWALL_1_BOTTOMLEFT,
3991 EL_DC_STEELWALL_1_BOTTOMRIGHT,
3992 EL_DC_STEELWALL_1_TOPLEFT_2,
3993 EL_DC_STEELWALL_1_TOPRIGHT_2,
3994 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
3995 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
3996 EL_DC_STEELWALL_2_LEFT,
3997 EL_DC_STEELWALL_2_RIGHT,
3998 EL_DC_STEELWALL_2_TOP,
3999 EL_DC_STEELWALL_2_BOTTOM,
4000 EL_DC_STEELWALL_2_HORIZONTAL,
4001 EL_DC_STEELWALL_2_VERTICAL,
4002 EL_DC_STEELWALL_2_MIDDLE,
4003 EL_DC_STEELWALL_2_SINGLE,
4004 EL_STEELWALL_SLIPPERY,
4018 EL_GATE_1_GRAY_ACTIVE,
4019 EL_GATE_2_GRAY_ACTIVE,
4020 EL_GATE_3_GRAY_ACTIVE,
4021 EL_GATE_4_GRAY_ACTIVE,
4030 EL_EM_GATE_1_GRAY_ACTIVE,
4031 EL_EM_GATE_2_GRAY_ACTIVE,
4032 EL_EM_GATE_3_GRAY_ACTIVE,
4033 EL_EM_GATE_4_GRAY_ACTIVE,
4042 EL_EMC_GATE_5_GRAY_ACTIVE,
4043 EL_EMC_GATE_6_GRAY_ACTIVE,
4044 EL_EMC_GATE_7_GRAY_ACTIVE,
4045 EL_EMC_GATE_8_GRAY_ACTIVE,
4047 EL_DC_GATE_WHITE_GRAY,
4048 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4049 EL_DC_GATE_FAKE_GRAY,
4051 EL_SWITCHGATE_OPENING,
4052 EL_SWITCHGATE_CLOSED,
4053 EL_SWITCHGATE_CLOSING,
4054 EL_DC_SWITCHGATE_SWITCH_UP,
4055 EL_DC_SWITCHGATE_SWITCH_DOWN,
4057 EL_TIMEGATE_OPENING,
4059 EL_TIMEGATE_CLOSING,
4060 EL_DC_TIMEGATE_SWITCH,
4061 EL_DC_TIMEGATE_SWITCH_ACTIVE,
4065 EL_TUBE_VERTICAL_LEFT,
4066 EL_TUBE_VERTICAL_RIGHT,
4067 EL_TUBE_HORIZONTAL_UP,
4068 EL_TUBE_HORIZONTAL_DOWN,
4073 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
4074 EL_EXPANDABLE_STEELWALL_VERTICAL,
4075 EL_EXPANDABLE_STEELWALL_ANY,
4080 static int ep_classic_enemy[] =
4097 static int ep_belt[] =
4099 EL_CONVEYOR_BELT_1_LEFT,
4100 EL_CONVEYOR_BELT_1_MIDDLE,
4101 EL_CONVEYOR_BELT_1_RIGHT,
4102 EL_CONVEYOR_BELT_2_LEFT,
4103 EL_CONVEYOR_BELT_2_MIDDLE,
4104 EL_CONVEYOR_BELT_2_RIGHT,
4105 EL_CONVEYOR_BELT_3_LEFT,
4106 EL_CONVEYOR_BELT_3_MIDDLE,
4107 EL_CONVEYOR_BELT_3_RIGHT,
4108 EL_CONVEYOR_BELT_4_LEFT,
4109 EL_CONVEYOR_BELT_4_MIDDLE,
4110 EL_CONVEYOR_BELT_4_RIGHT,
4115 static int ep_belt_active[] =
4117 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4118 EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE,
4119 EL_CONVEYOR_BELT_1_RIGHT_ACTIVE,
4120 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4121 EL_CONVEYOR_BELT_2_MIDDLE_ACTIVE,
4122 EL_CONVEYOR_BELT_2_RIGHT_ACTIVE,
4123 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4124 EL_CONVEYOR_BELT_3_MIDDLE_ACTIVE,
4125 EL_CONVEYOR_BELT_3_RIGHT_ACTIVE,
4126 EL_CONVEYOR_BELT_4_LEFT_ACTIVE,
4127 EL_CONVEYOR_BELT_4_MIDDLE_ACTIVE,
4128 EL_CONVEYOR_BELT_4_RIGHT_ACTIVE,
4133 static int ep_belt_switch[] =
4135 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4136 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4137 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4138 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4139 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4140 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4141 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4142 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4143 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4144 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4145 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4146 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4151 static int ep_tube[] =
4158 EL_TUBE_HORIZONTAL_UP,
4159 EL_TUBE_HORIZONTAL_DOWN,
4161 EL_TUBE_VERTICAL_LEFT,
4162 EL_TUBE_VERTICAL_RIGHT,
4168 static int ep_acid_pool[] =
4170 EL_ACID_POOL_TOPLEFT,
4171 EL_ACID_POOL_TOPRIGHT,
4172 EL_ACID_POOL_BOTTOMLEFT,
4173 EL_ACID_POOL_BOTTOM,
4174 EL_ACID_POOL_BOTTOMRIGHT,
4179 static int ep_keygate[] =
4189 EL_GATE_1_GRAY_ACTIVE,
4190 EL_GATE_2_GRAY_ACTIVE,
4191 EL_GATE_3_GRAY_ACTIVE,
4192 EL_GATE_4_GRAY_ACTIVE,
4201 EL_EM_GATE_1_GRAY_ACTIVE,
4202 EL_EM_GATE_2_GRAY_ACTIVE,
4203 EL_EM_GATE_3_GRAY_ACTIVE,
4204 EL_EM_GATE_4_GRAY_ACTIVE,
4213 EL_EMC_GATE_5_GRAY_ACTIVE,
4214 EL_EMC_GATE_6_GRAY_ACTIVE,
4215 EL_EMC_GATE_7_GRAY_ACTIVE,
4216 EL_EMC_GATE_8_GRAY_ACTIVE,
4218 EL_DC_GATE_WHITE_GRAY,
4219 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4224 static int ep_amoeboid[] =
4236 static int ep_amoebalive[] =
4247 static int ep_has_editor_content[] =
4253 EL_SOKOBAN_FIELD_PLAYER,
4271 static int ep_can_turn_each_move[] =
4273 // !!! do something with this one !!!
4277 static int ep_can_grow[] =
4291 static int ep_active_bomb[] =
4294 EL_EM_DYNAMITE_ACTIVE,
4295 EL_DYNABOMB_PLAYER_1_ACTIVE,
4296 EL_DYNABOMB_PLAYER_2_ACTIVE,
4297 EL_DYNABOMB_PLAYER_3_ACTIVE,
4298 EL_DYNABOMB_PLAYER_4_ACTIVE,
4299 EL_SP_DISK_RED_ACTIVE,
4304 static int ep_inactive[] =
4330 EL_QUICKSAND_FAST_EMPTY,
4353 EL_GATE_1_GRAY_ACTIVE,
4354 EL_GATE_2_GRAY_ACTIVE,
4355 EL_GATE_3_GRAY_ACTIVE,
4356 EL_GATE_4_GRAY_ACTIVE,
4365 EL_EM_GATE_1_GRAY_ACTIVE,
4366 EL_EM_GATE_2_GRAY_ACTIVE,
4367 EL_EM_GATE_3_GRAY_ACTIVE,
4368 EL_EM_GATE_4_GRAY_ACTIVE,
4377 EL_EMC_GATE_5_GRAY_ACTIVE,
4378 EL_EMC_GATE_6_GRAY_ACTIVE,
4379 EL_EMC_GATE_7_GRAY_ACTIVE,
4380 EL_EMC_GATE_8_GRAY_ACTIVE,
4382 EL_DC_GATE_WHITE_GRAY,
4383 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4384 EL_DC_GATE_FAKE_GRAY,
4387 EL_INVISIBLE_STEELWALL,
4395 EL_WALL_EMERALD_YELLOW,
4396 EL_DYNABOMB_INCREASE_NUMBER,
4397 EL_DYNABOMB_INCREASE_SIZE,
4398 EL_DYNABOMB_INCREASE_POWER,
4402 EL_SOKOBAN_FIELD_EMPTY,
4403 EL_SOKOBAN_FIELD_FULL,
4404 EL_WALL_EMERALD_RED,
4405 EL_WALL_EMERALD_PURPLE,
4406 EL_ACID_POOL_TOPLEFT,
4407 EL_ACID_POOL_TOPRIGHT,
4408 EL_ACID_POOL_BOTTOMLEFT,
4409 EL_ACID_POOL_BOTTOM,
4410 EL_ACID_POOL_BOTTOMRIGHT,
4414 EL_BD_MAGIC_WALL_DEAD,
4416 EL_DC_MAGIC_WALL_DEAD,
4417 EL_AMOEBA_TO_DIAMOND,
4425 EL_SP_GRAVITY_PORT_RIGHT,
4426 EL_SP_GRAVITY_PORT_DOWN,
4427 EL_SP_GRAVITY_PORT_LEFT,
4428 EL_SP_GRAVITY_PORT_UP,
4429 EL_SP_PORT_HORIZONTAL,
4430 EL_SP_PORT_VERTICAL,
4441 EL_SP_HARDWARE_GRAY,
4442 EL_SP_HARDWARE_GREEN,
4443 EL_SP_HARDWARE_BLUE,
4445 EL_SP_HARDWARE_YELLOW,
4446 EL_SP_HARDWARE_BASE_1,
4447 EL_SP_HARDWARE_BASE_2,
4448 EL_SP_HARDWARE_BASE_3,
4449 EL_SP_HARDWARE_BASE_4,
4450 EL_SP_HARDWARE_BASE_5,
4451 EL_SP_HARDWARE_BASE_6,
4452 EL_SP_GRAVITY_ON_PORT_LEFT,
4453 EL_SP_GRAVITY_ON_PORT_RIGHT,
4454 EL_SP_GRAVITY_ON_PORT_UP,
4455 EL_SP_GRAVITY_ON_PORT_DOWN,
4456 EL_SP_GRAVITY_OFF_PORT_LEFT,
4457 EL_SP_GRAVITY_OFF_PORT_RIGHT,
4458 EL_SP_GRAVITY_OFF_PORT_UP,
4459 EL_SP_GRAVITY_OFF_PORT_DOWN,
4460 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4461 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4462 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4463 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4464 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4465 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4466 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4467 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4468 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4469 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4470 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4471 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4472 EL_SIGN_EXCLAMATION,
4473 EL_SIGN_RADIOACTIVITY,
4480 EL_SIGN_ENTRY_FORBIDDEN,
4481 EL_SIGN_EMERGENCY_EXIT,
4489 EL_DC_STEELWALL_1_LEFT,
4490 EL_DC_STEELWALL_1_RIGHT,
4491 EL_DC_STEELWALL_1_TOP,
4492 EL_DC_STEELWALL_1_BOTTOM,
4493 EL_DC_STEELWALL_1_HORIZONTAL,
4494 EL_DC_STEELWALL_1_VERTICAL,
4495 EL_DC_STEELWALL_1_TOPLEFT,
4496 EL_DC_STEELWALL_1_TOPRIGHT,
4497 EL_DC_STEELWALL_1_BOTTOMLEFT,
4498 EL_DC_STEELWALL_1_BOTTOMRIGHT,
4499 EL_DC_STEELWALL_1_TOPLEFT_2,
4500 EL_DC_STEELWALL_1_TOPRIGHT_2,
4501 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
4502 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
4503 EL_DC_STEELWALL_2_LEFT,
4504 EL_DC_STEELWALL_2_RIGHT,
4505 EL_DC_STEELWALL_2_TOP,
4506 EL_DC_STEELWALL_2_BOTTOM,
4507 EL_DC_STEELWALL_2_HORIZONTAL,
4508 EL_DC_STEELWALL_2_VERTICAL,
4509 EL_DC_STEELWALL_2_MIDDLE,
4510 EL_DC_STEELWALL_2_SINGLE,
4511 EL_STEELWALL_SLIPPERY,
4516 EL_EMC_WALL_SLIPPERY_1,
4517 EL_EMC_WALL_SLIPPERY_2,
4518 EL_EMC_WALL_SLIPPERY_3,
4519 EL_EMC_WALL_SLIPPERY_4,
4540 static int ep_em_slippery_wall[] =
4545 static int ep_gfx_crumbled[] =
4556 static int ep_editor_cascade_active[] =
4558 EL_INTERNAL_CASCADE_BD_ACTIVE,
4559 EL_INTERNAL_CASCADE_EM_ACTIVE,
4560 EL_INTERNAL_CASCADE_EMC_ACTIVE,
4561 EL_INTERNAL_CASCADE_RND_ACTIVE,
4562 EL_INTERNAL_CASCADE_SB_ACTIVE,
4563 EL_INTERNAL_CASCADE_SP_ACTIVE,
4564 EL_INTERNAL_CASCADE_DC_ACTIVE,
4565 EL_INTERNAL_CASCADE_DX_ACTIVE,
4566 EL_INTERNAL_CASCADE_MM_ACTIVE,
4567 EL_INTERNAL_CASCADE_DF_ACTIVE,
4568 EL_INTERNAL_CASCADE_CHARS_ACTIVE,
4569 EL_INTERNAL_CASCADE_STEEL_CHARS_ACTIVE,
4570 EL_INTERNAL_CASCADE_CE_ACTIVE,
4571 EL_INTERNAL_CASCADE_GE_ACTIVE,
4572 EL_INTERNAL_CASCADE_ES_ACTIVE,
4573 EL_INTERNAL_CASCADE_REF_ACTIVE,
4574 EL_INTERNAL_CASCADE_USER_ACTIVE,
4575 EL_INTERNAL_CASCADE_DYNAMIC_ACTIVE,
4580 static int ep_editor_cascade_inactive[] =
4582 EL_INTERNAL_CASCADE_BD,
4583 EL_INTERNAL_CASCADE_EM,
4584 EL_INTERNAL_CASCADE_EMC,
4585 EL_INTERNAL_CASCADE_RND,
4586 EL_INTERNAL_CASCADE_SB,
4587 EL_INTERNAL_CASCADE_SP,
4588 EL_INTERNAL_CASCADE_DC,
4589 EL_INTERNAL_CASCADE_DX,
4590 EL_INTERNAL_CASCADE_MM,
4591 EL_INTERNAL_CASCADE_DF,
4592 EL_INTERNAL_CASCADE_CHARS,
4593 EL_INTERNAL_CASCADE_STEEL_CHARS,
4594 EL_INTERNAL_CASCADE_CE,
4595 EL_INTERNAL_CASCADE_GE,
4596 EL_INTERNAL_CASCADE_ES,
4597 EL_INTERNAL_CASCADE_REF,
4598 EL_INTERNAL_CASCADE_USER,
4599 EL_INTERNAL_CASCADE_DYNAMIC,
4604 static int ep_obsolete[] =
4608 EL_EM_KEY_1_FILE_OBSOLETE,
4609 EL_EM_KEY_2_FILE_OBSOLETE,
4610 EL_EM_KEY_3_FILE_OBSOLETE,
4611 EL_EM_KEY_4_FILE_OBSOLETE,
4612 EL_ENVELOPE_OBSOLETE,
4621 } element_properties[] =
4623 { ep_diggable, EP_DIGGABLE },
4624 { ep_collectible_only, EP_COLLECTIBLE_ONLY },
4625 { ep_dont_run_into, EP_DONT_RUN_INTO },
4626 { ep_dont_collide_with, EP_DONT_COLLIDE_WITH },
4627 { ep_dont_touch, EP_DONT_TOUCH },
4628 { ep_indestructible, EP_INDESTRUCTIBLE },
4629 { ep_slippery, EP_SLIPPERY },
4630 { ep_can_change, EP_CAN_CHANGE },
4631 { ep_can_move, EP_CAN_MOVE },
4632 { ep_can_fall, EP_CAN_FALL },
4633 { ep_can_smash_player, EP_CAN_SMASH_PLAYER },
4634 { ep_can_smash_enemies, EP_CAN_SMASH_ENEMIES },
4635 { ep_can_smash_everything, EP_CAN_SMASH_EVERYTHING },
4636 { ep_explodes_by_fire, EP_EXPLODES_BY_FIRE },
4637 { ep_explodes_smashed, EP_EXPLODES_SMASHED },
4638 { ep_explodes_impact, EP_EXPLODES_IMPACT },
4639 { ep_walkable_over, EP_WALKABLE_OVER },
4640 { ep_walkable_inside, EP_WALKABLE_INSIDE },
4641 { ep_walkable_under, EP_WALKABLE_UNDER },
4642 { ep_passable_over, EP_PASSABLE_OVER },
4643 { ep_passable_inside, EP_PASSABLE_INSIDE },
4644 { ep_passable_under, EP_PASSABLE_UNDER },
4645 { ep_droppable, EP_DROPPABLE },
4646 { ep_explodes_1x1_old, EP_EXPLODES_1X1_OLD },
4647 { ep_pushable, EP_PUSHABLE },
4648 { ep_explodes_cross_old, EP_EXPLODES_CROSS_OLD },
4649 { ep_protected, EP_PROTECTED },
4650 { ep_throwable, EP_THROWABLE },
4651 { ep_can_explode, EP_CAN_EXPLODE },
4652 { ep_gravity_reachable, EP_GRAVITY_REACHABLE },
4654 { ep_empty_space, EP_EMPTY_SPACE },
4655 { ep_player, EP_PLAYER },
4656 { ep_can_pass_magic_wall, EP_CAN_PASS_MAGIC_WALL },
4657 { ep_can_pass_dc_magic_wall, EP_CAN_PASS_DC_MAGIC_WALL },
4658 { ep_switchable, EP_SWITCHABLE },
4659 { ep_bd_element, EP_BD_ELEMENT },
4660 { ep_sp_element, EP_SP_ELEMENT },
4661 { ep_sb_element, EP_SB_ELEMENT },
4663 { ep_food_dark_yamyam, EP_FOOD_DARK_YAMYAM },
4664 { ep_food_penguin, EP_FOOD_PENGUIN },
4665 { ep_food_pig, EP_FOOD_PIG },
4666 { ep_historic_wall, EP_HISTORIC_WALL },
4667 { ep_historic_solid, EP_HISTORIC_SOLID },
4668 { ep_classic_enemy, EP_CLASSIC_ENEMY },
4669 { ep_belt, EP_BELT },
4670 { ep_belt_active, EP_BELT_ACTIVE },
4671 { ep_belt_switch, EP_BELT_SWITCH },
4672 { ep_tube, EP_TUBE },
4673 { ep_acid_pool, EP_ACID_POOL },
4674 { ep_keygate, EP_KEYGATE },
4675 { ep_amoeboid, EP_AMOEBOID },
4676 { ep_amoebalive, EP_AMOEBALIVE },
4677 { ep_has_editor_content, EP_HAS_EDITOR_CONTENT },
4678 { ep_can_turn_each_move, EP_CAN_TURN_EACH_MOVE },
4679 { ep_can_grow, EP_CAN_GROW },
4680 { ep_active_bomb, EP_ACTIVE_BOMB },
4681 { ep_inactive, EP_INACTIVE },
4683 { ep_em_slippery_wall, EP_EM_SLIPPERY_WALL },
4685 { ep_gfx_crumbled, EP_GFX_CRUMBLED },
4687 { ep_editor_cascade_active, EP_EDITOR_CASCADE_ACTIVE },
4688 { ep_editor_cascade_inactive, EP_EDITOR_CASCADE_INACTIVE },
4690 { ep_obsolete, EP_OBSOLETE },
4697 // always start with reliable default values (element has no properties)
4698 // (but never initialize clipboard elements after the very first time)
4699 // (to be able to use clipboard elements between several levels)
4700 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4701 if (!IS_CLIPBOARD_ELEMENT(i) || !clipboard_elements_initialized)
4702 for (j = 0; j < NUM_ELEMENT_PROPERTIES; j++)
4703 SET_PROPERTY(i, j, FALSE);
4705 // set all base element properties from above array definitions
4706 for (i = 0; element_properties[i].elements != NULL; i++)
4707 for (j = 0; (element_properties[i].elements)[j] != -1; j++)
4708 SET_PROPERTY((element_properties[i].elements)[j],
4709 element_properties[i].property, TRUE);
4711 // copy properties to some elements that are only stored in level file
4712 for (i = 0; i < NUM_ELEMENT_PROPERTIES; i++)
4713 for (j = 0; copy_properties[j][0] != -1; j++)
4714 if (HAS_PROPERTY(copy_properties[j][0], i))
4715 for (k = 1; k <= 4; k++)
4716 SET_PROPERTY(copy_properties[j][k], i, TRUE);
4718 // set static element properties that are not listed in array definitions
4719 for (i = EL_STEEL_CHAR_START; i <= EL_STEEL_CHAR_END; i++)
4720 SET_PROPERTY(i, EP_INDESTRUCTIBLE, TRUE);
4722 clipboard_elements_initialized = TRUE;
4725 void InitElementPropertiesEngine(int engine_version)
4727 static int no_wall_properties[] =
4730 EP_COLLECTIBLE_ONLY,
4732 EP_DONT_COLLIDE_WITH,
4735 EP_CAN_SMASH_PLAYER,
4736 EP_CAN_SMASH_ENEMIES,
4737 EP_CAN_SMASH_EVERYTHING,
4742 EP_FOOD_DARK_YAMYAM,
4758 /* important: after initialization in InitElementPropertiesStatic(), the
4759 elements are not again initialized to a default value; therefore all
4760 changes have to make sure that they leave the element with a defined
4761 property (which means that conditional property changes must be set to
4762 a reliable default value before) */
4764 // resolve group elements
4765 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
4766 ResolveGroupElement(EL_GROUP_START + i);
4768 // set all special, combined or engine dependent element properties
4769 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4771 // do not change (already initialized) clipboard elements here
4772 if (IS_CLIPBOARD_ELEMENT(i))
4775 // ---------- INACTIVE ----------------------------------------------------
4776 SET_PROPERTY(i, EP_INACTIVE, ((i >= EL_CHAR_START &&
4777 i <= EL_CHAR_END) ||
4778 (i >= EL_STEEL_CHAR_START &&
4779 i <= EL_STEEL_CHAR_END)));
4781 // ---------- WALKABLE, PASSABLE, ACCESSIBLE ------------------------------
4782 SET_PROPERTY(i, EP_WALKABLE, (IS_WALKABLE_OVER(i) ||
4783 IS_WALKABLE_INSIDE(i) ||
4784 IS_WALKABLE_UNDER(i)));
4786 SET_PROPERTY(i, EP_PASSABLE, (IS_PASSABLE_OVER(i) ||
4787 IS_PASSABLE_INSIDE(i) ||
4788 IS_PASSABLE_UNDER(i)));
4790 SET_PROPERTY(i, EP_ACCESSIBLE_OVER, (IS_WALKABLE_OVER(i) ||
4791 IS_PASSABLE_OVER(i)));
4793 SET_PROPERTY(i, EP_ACCESSIBLE_INSIDE, (IS_WALKABLE_INSIDE(i) ||
4794 IS_PASSABLE_INSIDE(i)));
4796 SET_PROPERTY(i, EP_ACCESSIBLE_UNDER, (IS_WALKABLE_UNDER(i) ||
4797 IS_PASSABLE_UNDER(i)));
4799 SET_PROPERTY(i, EP_ACCESSIBLE, (IS_WALKABLE(i) ||
4802 // ---------- COLLECTIBLE -------------------------------------------------
4803 SET_PROPERTY(i, EP_COLLECTIBLE, (IS_COLLECTIBLE_ONLY(i) ||
4807 // ---------- SNAPPABLE ---------------------------------------------------
4808 SET_PROPERTY(i, EP_SNAPPABLE, (IS_DIGGABLE(i) ||
4809 IS_COLLECTIBLE(i) ||
4813 // ---------- WALL --------------------------------------------------------
4814 SET_PROPERTY(i, EP_WALL, TRUE); // default: element is wall
4816 for (j = 0; no_wall_properties[j] != -1; j++)
4817 if (HAS_PROPERTY(i, no_wall_properties[j]) ||
4818 i >= EL_FIRST_RUNTIME_UNREAL)
4819 SET_PROPERTY(i, EP_WALL, FALSE);
4821 if (IS_HISTORIC_WALL(i))
4822 SET_PROPERTY(i, EP_WALL, TRUE);
4824 // ---------- SOLID_FOR_PUSHING -------------------------------------------
4825 if (engine_version < VERSION_IDENT(2,2,0,0))
4826 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, IS_HISTORIC_SOLID(i));
4828 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, (!IS_WALKABLE(i) &&
4830 !IS_COLLECTIBLE(i)));
4832 // ---------- DRAGONFIRE_PROOF --------------------------------------------
4833 if (IS_HISTORIC_SOLID(i) || i == EL_EXPLOSION)
4834 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, TRUE);
4836 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, (IS_INDESTRUCTIBLE(i) &&
4839 // ---------- EXPLOSION_PROOF ---------------------------------------------
4841 SET_PROPERTY(i, EP_EXPLOSION_PROOF, TRUE);
4842 else if (engine_version < VERSION_IDENT(2,2,0,0))
4843 SET_PROPERTY(i, EP_EXPLOSION_PROOF, IS_INDESTRUCTIBLE(i));
4845 SET_PROPERTY(i, EP_EXPLOSION_PROOF, (IS_INDESTRUCTIBLE(i) &&
4849 if (IS_CUSTOM_ELEMENT(i))
4851 // these are additional properties which are initially false when set
4853 // ---------- DONT_COLLIDE_WITH / DONT_RUN_INTO -------------------------
4855 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH, TRUE);
4856 if (DONT_COLLIDE_WITH(i))
4857 SET_PROPERTY(i, EP_DONT_RUN_INTO, TRUE);
4859 // ---------- CAN_SMASH_ENEMIES / CAN_SMASH_PLAYER ----------------------
4860 if (CAN_SMASH_EVERYTHING(i))
4861 SET_PROPERTY(i, EP_CAN_SMASH_ENEMIES, TRUE);
4862 if (CAN_SMASH_ENEMIES(i))
4863 SET_PROPERTY(i, EP_CAN_SMASH_PLAYER, TRUE);
4866 // ---------- CAN_SMASH ---------------------------------------------------
4867 SET_PROPERTY(i, EP_CAN_SMASH, (CAN_SMASH_PLAYER(i) ||
4868 CAN_SMASH_ENEMIES(i) ||
4869 CAN_SMASH_EVERYTHING(i)));
4871 // ---------- CAN_EXPLODE_BY_FIRE -----------------------------------------
4872 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_FIRE, (CAN_EXPLODE(i) &&
4873 EXPLODES_BY_FIRE(i)));
4875 // ---------- CAN_EXPLODE_SMASHED -----------------------------------------
4876 SET_PROPERTY(i, EP_CAN_EXPLODE_SMASHED, (CAN_EXPLODE(i) &&
4877 EXPLODES_SMASHED(i)));
4879 // ---------- CAN_EXPLODE_IMPACT ------------------------------------------
4880 SET_PROPERTY(i, EP_CAN_EXPLODE_IMPACT, (CAN_EXPLODE(i) &&
4881 EXPLODES_IMPACT(i)));
4883 // ---------- CAN_EXPLODE_BY_DRAGONFIRE -----------------------------------
4884 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_DRAGONFIRE, CAN_EXPLODE_BY_FIRE(i));
4886 // ---------- CAN_EXPLODE_BY_EXPLOSION ------------------------------------
4887 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_EXPLOSION, (CAN_EXPLODE_BY_FIRE(i) ||
4888 i == EL_BLACK_ORB));
4890 // ---------- COULD_MOVE_INTO_ACID ----------------------------------------
4891 SET_PROPERTY(i, EP_COULD_MOVE_INTO_ACID, (IS_PLAYER_ELEMENT(i) ||
4893 IS_CUSTOM_ELEMENT(i)));
4895 // ---------- MAYBE_DONT_COLLIDE_WITH -------------------------------------
4896 SET_PROPERTY(i, EP_MAYBE_DONT_COLLIDE_WITH, (i == EL_SP_SNIKSNAK ||
4897 i == EL_SP_ELECTRON));
4899 // ---------- CAN_MOVE_INTO_ACID ------------------------------------------
4900 if (COULD_MOVE_INTO_ACID(i) && !IS_CUSTOM_ELEMENT(i))
4901 SET_PROPERTY(i, EP_CAN_MOVE_INTO_ACID,
4902 getMoveIntoAcidProperty(&level, i));
4904 // ---------- DONT_COLLIDE_WITH -------------------------------------------
4905 if (MAYBE_DONT_COLLIDE_WITH(i))
4906 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH,
4907 getDontCollideWithProperty(&level, i));
4909 // ---------- SP_PORT -----------------------------------------------------
4910 SET_PROPERTY(i, EP_SP_PORT, (IS_SP_ELEMENT(i) &&
4911 IS_PASSABLE_INSIDE(i)));
4913 // ---------- CAN_BE_CLONED_BY_ANDROID ------------------------------------
4914 for (j = 0; j < level.num_android_clone_elements; j++)
4915 SET_PROPERTY(i, EP_CAN_BE_CLONED_BY_ANDROID,
4917 IS_EQUAL_OR_IN_GROUP(i, level.android_clone_element[j])));
4919 // ---------- CAN_CHANGE --------------------------------------------------
4920 SET_PROPERTY(i, EP_CAN_CHANGE, FALSE); // default: cannot change
4921 for (j = 0; j < element_info[i].num_change_pages; j++)
4922 if (element_info[i].change_page[j].can_change)
4923 SET_PROPERTY(i, EP_CAN_CHANGE, TRUE);
4925 // ---------- HAS_ACTION --------------------------------------------------
4926 SET_PROPERTY(i, EP_HAS_ACTION, FALSE); // default: has no action
4927 for (j = 0; j < element_info[i].num_change_pages; j++)
4928 if (element_info[i].change_page[j].has_action)
4929 SET_PROPERTY(i, EP_HAS_ACTION, TRUE);
4931 // ---------- CAN_CHANGE_OR_HAS_ACTION ------------------------------------
4932 SET_PROPERTY(i, EP_CAN_CHANGE_OR_HAS_ACTION, (CAN_CHANGE(i) ||
4935 // ---------- GFX_CRUMBLED ------------------------------------------------
4936 SET_PROPERTY(i, EP_GFX_CRUMBLED,
4937 element_info[i].crumbled[ACTION_DEFAULT] !=
4938 element_info[i].graphic[ACTION_DEFAULT]);
4940 // ---------- EDITOR_CASCADE ----------------------------------------------
4941 SET_PROPERTY(i, EP_EDITOR_CASCADE, (IS_EDITOR_CASCADE_ACTIVE(i) ||
4942 IS_EDITOR_CASCADE_INACTIVE(i)));
4945 // dynamically adjust element properties according to game engine version
4947 static int ep_em_slippery_wall[] =
4952 EL_EXPANDABLE_WALL_HORIZONTAL,
4953 EL_EXPANDABLE_WALL_VERTICAL,
4954 EL_EXPANDABLE_WALL_ANY,
4955 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
4956 EL_EXPANDABLE_STEELWALL_VERTICAL,
4957 EL_EXPANDABLE_STEELWALL_ANY,
4958 EL_EXPANDABLE_STEELWALL_GROWING,
4962 static int ep_em_explodes_by_fire[] =
4965 EL_EM_DYNAMITE_ACTIVE,
4970 // special EM style gems behaviour
4971 for (i = 0; ep_em_slippery_wall[i] != -1; i++)
4972 SET_PROPERTY(ep_em_slippery_wall[i], EP_EM_SLIPPERY_WALL,
4973 level.em_slippery_gems);
4975 // "EL_EXPANDABLE_WALL_GROWING" wasn't slippery for EM gems in 2.0.1
4976 SET_PROPERTY(EL_EXPANDABLE_WALL_GROWING, EP_EM_SLIPPERY_WALL,
4977 (level.em_slippery_gems &&
4978 engine_version > VERSION_IDENT(2,0,1,0)));
4980 // special EM style explosion behaviour regarding chain reactions
4981 for (i = 0; ep_em_explodes_by_fire[i] != -1; i++)
4982 SET_PROPERTY(ep_em_explodes_by_fire[i], EP_EXPLODES_BY_FIRE,
4983 level.em_explodes_by_fire);
4986 // this is needed because some graphics depend on element properties
4987 if (game_status == GAME_MODE_PLAYING)
4988 InitElementGraphicInfo();
4991 void InitElementPropertiesGfxElement(void)
4995 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4997 struct ElementInfo *ei = &element_info[i];
4999 ei->gfx_element = (ei->use_gfx_element ? ei->gfx_element_initial : i);
5003 static void InitGlobal(void)
5008 for (i = 0; i < MAX_NUM_ELEMENTS + 1; i++)
5010 // check if element_name_info entry defined for each element in "main.h"
5011 if (i < MAX_NUM_ELEMENTS && element_name_info[i].token_name == NULL)
5012 Fail("undefined 'element_name_info' entry for element %d", i);
5014 element_info[i].token_name = element_name_info[i].token_name;
5015 element_info[i].class_name = element_name_info[i].class_name;
5016 element_info[i].editor_description= element_name_info[i].editor_description;
5019 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS + 1; i++)
5021 // check if global_anim_name_info defined for each entry in "main.h"
5022 if (i < NUM_GLOBAL_ANIM_TOKENS &&
5023 global_anim_name_info[i].token_name == NULL)
5024 Fail("undefined 'global_anim_name_info' entry for anim %d", i);
5026 global_anim_info[i].token_name = global_anim_name_info[i].token_name;
5029 // create hash to store URLs for global animations
5030 anim_url_hash = newSetupFileHash();
5032 // create hash from image config list
5033 image_config_hash = newSetupFileHash();
5034 for (i = 0; image_config[i].token != NULL; i++)
5035 setHashEntry(image_config_hash,
5036 image_config[i].token,
5037 image_config[i].value);
5039 // create hash from element token list
5040 element_token_hash = newSetupFileHash();
5041 for (i = 0; element_name_info[i].token_name != NULL; i++)
5042 setHashEntry(element_token_hash,
5043 element_name_info[i].token_name,
5046 // create hash from graphic token list
5047 graphic_token_hash = newSetupFileHash();
5048 for (graphic = 0, i = 0; image_config[i].token != NULL; i++)
5049 if (strSuffix(image_config[i].value, ".png") ||
5050 strSuffix(image_config[i].value, ".pcx") ||
5051 strSuffix(image_config[i].value, ".wav") ||
5052 strEqual(image_config[i].value, UNDEFINED_FILENAME))
5053 setHashEntry(graphic_token_hash,
5054 image_config[i].token,
5055 int2str(graphic++, 0));
5057 // create hash from font token list
5058 font_token_hash = newSetupFileHash();
5059 for (i = 0; font_info[i].token_name != NULL; i++)
5060 setHashEntry(font_token_hash,
5061 font_info[i].token_name,
5064 // set default filenames for all cloned graphics in static configuration
5065 for (i = 0; image_config[i].token != NULL; i++)
5067 if (strEqual(image_config[i].value, UNDEFINED_FILENAME))
5069 char *token = image_config[i].token;
5070 char *token_clone_from = getStringCat2(token, ".clone_from");
5071 char *token_cloned = getHashEntry(image_config_hash, token_clone_from);
5073 if (token_cloned != NULL)
5075 char *value_cloned = getHashEntry(image_config_hash, token_cloned);
5077 if (value_cloned != NULL)
5079 // set default filename in static configuration
5080 image_config[i].value = value_cloned;
5082 // set default filename in image config hash
5083 setHashEntry(image_config_hash, token, value_cloned);
5087 free(token_clone_from);
5091 // always start with reliable default values (all elements)
5092 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5093 ActiveElement[i] = i;
5095 // now add all entries that have an active state (active elements)
5096 for (i = 0; element_with_active_state[i].element != -1; i++)
5098 int element = element_with_active_state[i].element;
5099 int element_active = element_with_active_state[i].element_active;
5101 ActiveElement[element] = element_active;
5104 // always start with reliable default values (all buttons)
5105 for (i = 0; i < NUM_IMAGE_FILES; i++)
5106 ActiveButton[i] = i;
5108 // now add all entries that have an active state (active buttons)
5109 for (i = 0; button_with_active_state[i].button != -1; i++)
5111 int button = button_with_active_state[i].button;
5112 int button_active = button_with_active_state[i].button_active;
5114 ActiveButton[button] = button_active;
5117 // always start with reliable default values (all fonts)
5118 for (i = 0; i < NUM_FONTS; i++)
5121 // now add all entries that have an active state (active fonts)
5122 for (i = 0; font_with_active_state[i].font_nr != -1; i++)
5124 int font = font_with_active_state[i].font_nr;
5125 int font_active = font_with_active_state[i].font_nr_active;
5127 ActiveFont[font] = font_active;
5130 global.autoplay_leveldir = NULL;
5131 global.patchtapes_leveldir = NULL;
5132 global.convert_leveldir = NULL;
5133 global.dumplevel_leveldir = NULL;
5134 global.dumptape_leveldir = NULL;
5135 global.create_sketch_images_dir = NULL;
5136 global.create_collect_images_dir = NULL;
5138 global.frames_per_second = 0;
5139 global.show_frames_per_second = FALSE;
5141 global.border_status = GAME_MODE_LOADING;
5142 global.anim_status = global.anim_status_next = GAME_MODE_LOADING;
5144 global.use_envelope_request = FALSE;
5146 global.user_names = NULL;
5149 static void Execute_Command(char *command)
5153 if (strEqual(command, "print graphicsinfo.conf"))
5155 Print("# You can configure additional/alternative image files here.\n");
5156 Print("# (The entries below are default and therefore commented out.)\n");
5158 Print("%s\n", getFormattedSetupEntry("name", "Classic Graphics"));
5160 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5163 for (i = 0; image_config[i].token != NULL; i++)
5164 Print("# %s\n", getFormattedSetupEntry(image_config[i].token,
5165 image_config[i].value));
5169 else if (strEqual(command, "print soundsinfo.conf"))
5171 Print("# You can configure additional/alternative sound files here.\n");
5172 Print("# (The entries below are default and therefore commented out.)\n");
5174 Print("%s\n", getFormattedSetupEntry("name", "Classic Sounds"));
5176 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5179 for (i = 0; sound_config[i].token != NULL; i++)
5180 Print("# %s\n", getFormattedSetupEntry(sound_config[i].token,
5181 sound_config[i].value));
5185 else if (strEqual(command, "print musicinfo.conf"))
5187 Print("# You can configure additional/alternative music files here.\n");
5188 Print("# (The entries below are default and therefore commented out.)\n");
5190 Print("%s\n", getFormattedSetupEntry("name", "Classic Music"));
5192 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5195 for (i = 0; music_config[i].token != NULL; i++)
5196 Print("# %s\n", getFormattedSetupEntry(music_config[i].token,
5197 music_config[i].value));
5201 else if (strEqual(command, "print editorsetup.conf"))
5203 Print("# You can configure your personal editor element list here.\n");
5204 Print("# (The entries below are default and therefore commented out.)\n");
5207 // this is needed to be able to check element list for cascade elements
5208 InitElementPropertiesStatic();
5209 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
5211 PrintEditorElementList();
5215 else if (strEqual(command, "print helpanim.conf"))
5217 Print("# You can configure different element help animations here.\n");
5218 Print("# (The entries below are default and therefore commented out.)\n");
5221 for (i = 0; helpanim_config[i].token != NULL; i++)
5223 Print("# %s\n", getFormattedSetupEntry(helpanim_config[i].token,
5224 helpanim_config[i].value));
5226 if (strEqual(helpanim_config[i].token, "end"))
5232 else if (strEqual(command, "print helptext.conf"))
5234 Print("# You can configure different element help text here.\n");
5235 Print("# (The entries below are default and therefore commented out.)\n");
5238 for (i = 0; helptext_config[i].token != NULL; i++)
5239 Print("# %s\n", getFormattedSetupEntry(helptext_config[i].token,
5240 helptext_config[i].value));
5244 else if (strPrefix(command, "dump level "))
5246 char *filename = &command[11];
5248 if (fileExists(filename))
5250 LoadLevelFromFilename(&level, filename);
5256 char *leveldir = getStringCopy(filename); // read command parameters
5257 char *level_nr = strchr(leveldir, ' ');
5259 if (level_nr == NULL)
5260 Fail("cannot open file '%s'", filename);
5264 global.dumplevel_leveldir = leveldir;
5265 global.dumplevel_level_nr = atoi(level_nr);
5267 program.headless = TRUE;
5269 else if (strPrefix(command, "dump tape "))
5271 char *filename = &command[10];
5273 if (fileExists(filename))
5275 LoadTapeFromFilename(filename);
5281 char *leveldir = getStringCopy(filename); // read command parameters
5282 char *level_nr = strchr(leveldir, ' ');
5284 if (level_nr == NULL)
5285 Fail("cannot open file '%s'", filename);
5289 global.dumptape_leveldir = leveldir;
5290 global.dumptape_level_nr = atoi(level_nr);
5292 program.headless = TRUE;
5294 else if (strPrefix(command, "autoplay ") ||
5295 strPrefix(command, "autoffwd ") ||
5296 strPrefix(command, "autowarp ") ||
5297 strPrefix(command, "autotest ") ||
5298 strPrefix(command, "autosave ") ||
5299 strPrefix(command, "autoupload ") ||
5300 strPrefix(command, "autofix "))
5302 char *arg_ptr = strchr(command, ' ');
5303 char *str_ptr = getStringCopy(arg_ptr); // read command parameters
5305 global.autoplay_mode =
5306 (strPrefix(command, "autoplay") ? AUTOPLAY_MODE_PLAY :
5307 strPrefix(command, "autoffwd") ? AUTOPLAY_MODE_FFWD :
5308 strPrefix(command, "autowarp") ? AUTOPLAY_MODE_WARP :
5309 strPrefix(command, "autotest") ? AUTOPLAY_MODE_TEST :
5310 strPrefix(command, "autosave") ? AUTOPLAY_MODE_SAVE :
5311 strPrefix(command, "autoupload") ? AUTOPLAY_MODE_UPLOAD :
5312 strPrefix(command, "autofix") ? AUTOPLAY_MODE_FIX :
5313 AUTOPLAY_MODE_NONE);
5315 while (*str_ptr != '\0') // continue parsing string
5317 // cut leading whitespace from string, replace it by string terminator
5318 while (*str_ptr == ' ' || *str_ptr == '\t')
5321 if (*str_ptr == '\0') // end of string reached
5324 if (global.autoplay_leveldir == NULL) // read level set string
5326 global.autoplay_leveldir = str_ptr;
5327 global.autoplay_all = TRUE; // default: play all tapes
5329 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5330 global.autoplay_level[i] = FALSE;
5332 else // read level number string
5334 int level_nr = atoi(str_ptr); // get level_nr value
5336 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5337 global.autoplay_level[level_nr] = TRUE;
5339 global.autoplay_all = FALSE;
5342 // advance string pointer to the next whitespace (or end of string)
5343 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5347 if (global.autoplay_mode & AUTOPLAY_WARP_NO_DISPLAY)
5348 program.headless = TRUE;
5350 else if (strPrefix(command, "patch tapes "))
5352 char *str_ptr = getStringCopy(&command[12]); // read command parameters
5354 // skip leading whitespace
5355 while (*str_ptr == ' ' || *str_ptr == '\t')
5358 if (*str_ptr == '\0')
5359 Fail("cannot find MODE in command '%s'", command);
5361 global.patchtapes_mode = str_ptr; // store patch mode
5363 // advance to next whitespace (or end of string)
5364 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5367 while (*str_ptr != '\0') // continue parsing string
5369 // cut leading whitespace from string, replace it by string terminator
5370 while (*str_ptr == ' ' || *str_ptr == '\t')
5373 if (*str_ptr == '\0') // end of string reached
5376 if (global.patchtapes_leveldir == NULL) // read level set string
5378 global.patchtapes_leveldir = str_ptr;
5379 global.patchtapes_all = TRUE; // default: patch all tapes
5381 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5382 global.patchtapes_level[i] = FALSE;
5384 else // read level number string
5386 int level_nr = atoi(str_ptr); // get level_nr value
5388 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5389 global.patchtapes_level[level_nr] = TRUE;
5391 global.patchtapes_all = FALSE;
5394 // advance string pointer to the next whitespace (or end of string)
5395 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5399 if (global.patchtapes_leveldir == NULL)
5401 if (strEqual(global.patchtapes_mode, "help"))
5402 global.patchtapes_leveldir = UNDEFINED_LEVELSET;
5404 Fail("cannot find LEVELDIR in command '%s'", command);
5407 program.headless = TRUE;
5409 else if (strPrefix(command, "convert "))
5411 char *str_copy = getStringCopy(strchr(command, ' ') + 1);
5412 char *str_ptr = strchr(str_copy, ' ');
5414 global.convert_leveldir = str_copy;
5415 global.convert_level_nr = -1;
5417 if (str_ptr != NULL) // level number follows
5419 *str_ptr++ = '\0'; // terminate leveldir string
5420 global.convert_level_nr = atoi(str_ptr); // get level_nr value
5423 program.headless = TRUE;
5425 else if (strPrefix(command, "create sketch images "))
5427 global.create_sketch_images_dir = getStringCopy(&command[21]);
5429 if (access(global.create_sketch_images_dir, W_OK) != 0)
5430 Fail("image target directory '%s' not found or not writable",
5431 global.create_sketch_images_dir);
5433 else if (strPrefix(command, "create collect image "))
5435 global.create_collect_images_dir = getStringCopy(&command[21]);
5437 if (access(global.create_collect_images_dir, W_OK) != 0)
5438 Fail("image target directory '%s' not found or not writable",
5439 global.create_collect_images_dir);
5441 else if (strPrefix(command, "create CE image "))
5443 CreateCustomElementImages(&command[16]);
5449 FailWithHelp("unrecognized command '%s'", command);
5452 // disable networking if any valid command was recognized
5453 options.network = setup.network_mode = FALSE;
5456 static void InitSetup(void)
5458 LoadUserNames(); // global user names
5459 LoadUserSetup(); // global user number
5461 LoadSetup(); // global setup info
5463 // set some options from setup file
5465 if (setup.options.verbose)
5466 options.verbose = TRUE;
5468 if (setup.debug.show_frames_per_second)
5469 global.show_frames_per_second = TRUE;
5472 static void InitGameInfo(void)
5474 game.restart_level = FALSE;
5475 game.restart_game_message = NULL;
5477 game.request_active = FALSE;
5478 game.request_active_or_moving = FALSE;
5480 game.use_masked_elements_initial = FALSE;
5483 static void InitPlayerInfo(void)
5487 // choose default local player
5488 local_player = &stored_player[0];
5490 for (i = 0; i < MAX_PLAYERS; i++)
5492 stored_player[i].connected_locally = FALSE;
5493 stored_player[i].connected_network = FALSE;
5496 local_player->connected_locally = TRUE;
5499 static void InitArtworkInfo(void)
5504 static char *get_string_in_brackets(char *string)
5506 char *string_in_brackets = checked_malloc(strlen(string) + 3);
5508 sprintf(string_in_brackets, "[%s]", string);
5510 return string_in_brackets;
5513 static char *get_level_id_suffix(int id_nr)
5515 char *id_suffix = checked_malloc(1 + 3 + 1);
5517 if (id_nr < 0 || id_nr > 999)
5520 sprintf(id_suffix, ".%03d", id_nr);
5525 static void InitArtworkConfig(void)
5527 static char *image_id_prefix[MAX_NUM_ELEMENTS +
5529 NUM_GLOBAL_ANIM_TOKENS + 1];
5530 static char *sound_id_prefix[2 * MAX_NUM_ELEMENTS +
5531 NUM_GLOBAL_ANIM_TOKENS + 1];
5532 static char *music_id_prefix[NUM_MUSIC_PREFIXES +
5533 NUM_GLOBAL_ANIM_TOKENS + 1];
5534 static char *action_id_suffix[NUM_ACTIONS + 1];
5535 static char *direction_id_suffix[NUM_DIRECTIONS_FULL + 1];
5536 static char *special_id_suffix[NUM_SPECIAL_GFX_ARGS + 1];
5537 static char *level_id_suffix[MAX_LEVELS + 1];
5538 static char *dummy[1] = { NULL };
5539 static char *ignore_generic_tokens[] =
5544 "program_copyright",
5549 static char **ignore_image_tokens;
5550 static char **ignore_sound_tokens;
5551 static char **ignore_music_tokens;
5552 int num_ignore_generic_tokens;
5553 int num_ignore_image_tokens;
5554 int num_ignore_sound_tokens;
5555 int num_ignore_music_tokens;
5558 // dynamically determine list of generic tokens to be ignored
5559 num_ignore_generic_tokens = 0;
5560 for (i = 0; ignore_generic_tokens[i] != NULL; i++)
5561 num_ignore_generic_tokens++;
5563 // dynamically determine list of image tokens to be ignored
5564 num_ignore_image_tokens = num_ignore_generic_tokens;
5565 for (i = 0; image_config_vars[i].token != NULL; i++)
5566 num_ignore_image_tokens++;
5567 ignore_image_tokens =
5568 checked_malloc((num_ignore_image_tokens + 1) * sizeof(char *));
5569 for (i = 0; i < num_ignore_generic_tokens; i++)
5570 ignore_image_tokens[i] = ignore_generic_tokens[i];
5571 for (i = 0; i < num_ignore_image_tokens - num_ignore_generic_tokens; i++)
5572 ignore_image_tokens[num_ignore_generic_tokens + i] =
5573 image_config_vars[i].token;
5574 ignore_image_tokens[num_ignore_image_tokens] = NULL;
5576 // dynamically determine list of sound tokens to be ignored
5577 num_ignore_sound_tokens = num_ignore_generic_tokens;
5578 ignore_sound_tokens =
5579 checked_malloc((num_ignore_sound_tokens + 1) * sizeof(char *));
5580 for (i = 0; i < num_ignore_generic_tokens; i++)
5581 ignore_sound_tokens[i] = ignore_generic_tokens[i];
5582 ignore_sound_tokens[num_ignore_sound_tokens] = NULL;
5584 // dynamically determine list of music tokens to be ignored
5585 num_ignore_music_tokens = num_ignore_generic_tokens;
5586 ignore_music_tokens =
5587 checked_malloc((num_ignore_music_tokens + 1) * sizeof(char *));
5588 for (i = 0; i < num_ignore_generic_tokens; i++)
5589 ignore_music_tokens[i] = ignore_generic_tokens[i];
5590 ignore_music_tokens[num_ignore_music_tokens] = NULL;
5592 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5593 image_id_prefix[i] = element_info[i].token_name;
5594 for (i = 0; i < NUM_FONTS; i++)
5595 image_id_prefix[MAX_NUM_ELEMENTS + i] = font_info[i].token_name;
5596 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5597 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + i] =
5598 global_anim_info[i].token_name;
5599 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5601 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5602 sound_id_prefix[i] = element_info[i].token_name;
5603 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5604 sound_id_prefix[MAX_NUM_ELEMENTS + i] =
5605 get_string_in_brackets(element_info[i].class_name);
5606 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5607 sound_id_prefix[2 * MAX_NUM_ELEMENTS + i] =
5608 global_anim_info[i].token_name;
5609 sound_id_prefix[2 * MAX_NUM_ELEMENTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5611 for (i = 0; i < NUM_MUSIC_PREFIXES; i++)
5612 music_id_prefix[i] = music_prefix_info[i].prefix;
5613 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5614 music_id_prefix[NUM_MUSIC_PREFIXES + i] =
5615 global_anim_info[i].token_name;
5616 music_id_prefix[NUM_MUSIC_PREFIXES + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5618 for (i = 0; i < NUM_ACTIONS; i++)
5619 action_id_suffix[i] = element_action_info[i].suffix;
5620 action_id_suffix[NUM_ACTIONS] = NULL;
5622 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
5623 direction_id_suffix[i] = element_direction_info[i].suffix;
5624 direction_id_suffix[NUM_DIRECTIONS_FULL] = NULL;
5626 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
5627 special_id_suffix[i] = special_suffix_info[i].suffix;
5628 special_id_suffix[NUM_SPECIAL_GFX_ARGS] = NULL;
5630 for (i = 0; i < MAX_LEVELS; i++)
5631 level_id_suffix[i] = get_level_id_suffix(i);
5632 level_id_suffix[MAX_LEVELS] = NULL;
5634 InitImageList(image_config, NUM_IMAGE_FILES, image_config_suffix,
5635 image_id_prefix, action_id_suffix, direction_id_suffix,
5636 special_id_suffix, ignore_image_tokens);
5637 InitSoundList(sound_config, NUM_SOUND_FILES, sound_config_suffix,
5638 sound_id_prefix, action_id_suffix, dummy,
5639 special_id_suffix, ignore_sound_tokens);
5640 InitMusicList(music_config, NUM_MUSIC_FILES, music_config_suffix,
5641 music_id_prefix, action_id_suffix, special_id_suffix,
5642 level_id_suffix, ignore_music_tokens);
5645 static void InitMixer(void)
5652 static void InitVideoOverlay(void)
5654 // if virtual buttons are not loaded from setup file, repeat initializing
5655 // virtual buttons grid with default values now that video is initialized
5656 if (!setup.touch.grid_initialized)
5659 InitTileCursorInfo();
5663 void InitGfxBuffers(void)
5665 static int win_xsize_last = -1;
5666 static int win_ysize_last = -1;
5668 // create additional image buffers for double-buffering and cross-fading
5670 if (WIN_XSIZE != win_xsize_last || WIN_YSIZE != win_ysize_last)
5672 // used to temporarily store the backbuffer -- only re-create if changed
5673 ReCreateBitmap(&bitmap_db_store_1, WIN_XSIZE, WIN_YSIZE);
5674 ReCreateBitmap(&bitmap_db_store_2, WIN_XSIZE, WIN_YSIZE);
5676 win_xsize_last = WIN_XSIZE;
5677 win_ysize_last = WIN_YSIZE;
5680 ReCreateBitmap(&bitmap_db_field, FXSIZE, FYSIZE);
5681 ReCreateBitmap(&bitmap_db_door_1, 3 * DXSIZE, DYSIZE);
5682 ReCreateBitmap(&bitmap_db_door_2, 3 * VXSIZE, VYSIZE);
5684 // initialize screen properties
5685 InitGfxFieldInfo(SX, SY, SXSIZE, SYSIZE,
5686 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
5688 InitGfxDoor1Info(DX, DY, DXSIZE, DYSIZE);
5689 InitGfxDoor2Info(VX, VY, VXSIZE, VYSIZE);
5690 InitGfxDoor3Info(EX, EY, EXSIZE, EYSIZE);
5691 InitGfxWindowInfo(WIN_XSIZE, WIN_YSIZE);
5692 InitGfxScrollbufferInfo(FXSIZE, FYSIZE);
5693 InitGfxClipRegion(FALSE, -1, -1, -1, -1);
5695 // required if door size definitions have changed
5696 InitGraphicCompatibilityInfo_Doors();
5698 InitGfxBuffers_EM();
5699 InitGfxBuffers_SP();
5702 static void InitGfx(void)
5704 struct GraphicInfo *graphic_info_last = graphic_info;
5705 char *filename_font_initial = NULL;
5706 char *filename_image_initial[NUM_INITIAL_IMAGES] = { NULL };
5707 char *image_token[NUM_INITIAL_IMAGES] =
5709 CONFIG_TOKEN_GLOBAL_BUSY_INITIAL,
5710 CONFIG_TOKEN_GLOBAL_BUSY,
5711 CONFIG_TOKEN_GLOBAL_BUSY_PLAYFIELD,
5712 CONFIG_TOKEN_BACKGROUND,
5713 CONFIG_TOKEN_BACKGROUND_LOADING_INITIAL,
5714 CONFIG_TOKEN_BACKGROUND_LOADING
5716 struct MenuPosInfo *init_busy[NUM_INITIAL_IMAGES_BUSY] =
5720 &init.busy_playfield
5722 Bitmap *bitmap_font_initial = NULL;
5723 int parameter[NUM_INITIAL_IMAGES][NUM_GFX_ARGS];
5726 // determine settings for initial font (for displaying startup messages)
5727 for (i = 0; image_config[i].token != NULL; i++)
5729 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5731 char font_token[128];
5734 sprintf(font_token, "%s_%d", CONFIG_TOKEN_FONT_INITIAL, j + 1);
5735 len_font_token = strlen(font_token);
5737 if (strEqual(image_config[i].token, font_token))
5739 filename_font_initial = image_config[i].value;
5741 else if (strlen(image_config[i].token) > len_font_token &&
5742 strncmp(image_config[i].token, font_token, len_font_token) == 0)
5744 if (strEqual(&image_config[i].token[len_font_token], ".x"))
5745 font_initial[j].src_x = atoi(image_config[i].value);
5746 else if (strEqual(&image_config[i].token[len_font_token], ".y"))
5747 font_initial[j].src_y = atoi(image_config[i].value);
5748 else if (strEqual(&image_config[i].token[len_font_token], ".width"))
5749 font_initial[j].width = atoi(image_config[i].value);
5750 else if (strEqual(&image_config[i].token[len_font_token], ".height"))
5751 font_initial[j].height = atoi(image_config[i].value);
5756 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5758 font_initial[j].num_chars = DEFAULT_NUM_CHARS_PER_FONT;
5759 font_initial[j].num_chars_per_line = DEFAULT_NUM_CHARS_PER_LINE;
5762 if (filename_font_initial == NULL) // should not happen
5763 Fail("cannot get filename for '%s'", CONFIG_TOKEN_FONT_INITIAL);
5766 InitGfxCustomArtworkInfo();
5767 InitGfxOtherSettings();
5769 InitGfxTileSizeInfo(TILESIZE, TILESIZE);
5771 bitmap_font_initial = LoadCustomImage(filename_font_initial);
5773 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5774 font_initial[j].bitmap = bitmap_font_initial;
5776 InitFontGraphicInfo();
5780 DrawInitTextHead("Loading graphics");
5782 InitMenuDesignSettings_Static();
5784 // initialize settings for initial images with default values
5785 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5786 for (j = 0; j < NUM_GFX_ARGS; j++)
5788 get_graphic_parameter_value(image_config_suffix[j].value,
5789 image_config_suffix[j].token,
5790 image_config_suffix[j].type);
5792 // read settings for initial images from default custom artwork config
5793 char *gfx_config_filename = getPath3(options.graphics_directory,
5795 GRAPHICSINFO_FILENAME);
5797 if (fileExists(gfx_config_filename))
5799 SetupFileHash *setup_file_hash = loadSetupFileHash(gfx_config_filename);
5801 if (setup_file_hash)
5803 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5805 char *filename = getHashEntry(setup_file_hash, image_token[i]);
5809 filename_image_initial[i] = getStringCopy(filename);
5811 for (j = 0; image_config_suffix[j].token != NULL; j++)
5813 int type = image_config_suffix[j].type;
5814 char *suffix = image_config_suffix[j].token;
5815 char *token = getStringCat2(image_token[i], suffix);
5816 char *value = getHashEntry(setup_file_hash, token);
5818 checked_free(token);
5822 get_graphic_parameter_value(value, suffix, type);
5827 // read values from custom graphics config file
5828 InitMenuDesignSettings_FromHash(setup_file_hash, FALSE);
5830 freeSetupFileHash(setup_file_hash);
5834 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5836 if (filename_image_initial[i] == NULL)
5838 int len_token = strlen(image_token[i]);
5840 // read settings for initial images from static default artwork config
5841 for (j = 0; image_config[j].token != NULL; j++)
5843 if (strEqual(image_config[j].token, image_token[i]))
5845 filename_image_initial[i] = getStringCopy(image_config[j].value);
5847 else if (strlen(image_config[j].token) > len_token &&
5848 strncmp(image_config[j].token, image_token[i], len_token) == 0)
5850 for (k = 0; image_config_suffix[k].token != NULL; k++)
5852 if (strEqual(&image_config[j].token[len_token],
5853 image_config_suffix[k].token))
5855 get_graphic_parameter_value(image_config[j].value,
5856 image_config_suffix[k].token,
5857 image_config_suffix[k].type);
5864 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5866 if (filename_image_initial[i] == NULL) // should not happen
5867 Fail("cannot get filename for '%s'", image_token[i]);
5869 image_initial[i].bitmaps =
5870 checked_calloc(sizeof(Bitmap *) * NUM_IMG_BITMAP_POINTERS);
5872 if (!strEqual(filename_image_initial[i], UNDEFINED_FILENAME))
5873 image_initial[i].bitmaps[IMG_BITMAP_STANDARD] =
5874 LoadCustomImage(filename_image_initial[i]);
5876 checked_free(filename_image_initial[i]);
5879 graphic_info = image_initial; // graphic == 0 => image_initial
5881 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5882 set_graphic_parameters_ext(i, parameter[i], image_initial[i].bitmaps);
5884 graphic_info = graphic_info_last;
5886 for (i = 0; i < NUM_INITIAL_IMAGES_BUSY; i++)
5888 // set image size for busy animations
5889 init_busy[i]->width = image_initial[i].width;
5890 init_busy[i]->height = image_initial[i].height;
5893 SetLoadingBackgroundImage();
5895 ClearRectangleOnBackground(window, 0, 0, WIN_XSIZE, WIN_YSIZE);
5897 InitGfxDrawBusyAnimFunction(DrawInitAnim);
5898 InitGfxDrawGlobalAnimFunction(DrawGlobalAnimations);
5899 InitGfxDrawGlobalBorderFunction(DrawMaskedBorderToTarget);
5900 InitGfxDrawTileCursorFunction(DrawTileCursor);
5902 gfx.fade_border_source_status = global.border_status;
5903 gfx.fade_border_target_status = global.border_status;
5904 gfx.masked_border_bitmap_ptr = backbuffer;
5906 // use copy of busy animation to prevent change while reloading artwork
5910 static void InitGfxBackground(void)
5912 fieldbuffer = bitmap_db_field;
5913 SetDrawtoField(DRAW_TO_BACKBUFFER);
5915 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
5917 redraw_mask = REDRAW_ALL;
5920 static void InitLevelInfo(void)
5922 LoadLevelInfo(); // global level info
5923 LoadLevelSetup_LastSeries(); // last played series info
5924 LoadLevelSetup_SeriesInfo(); // last played level info
5926 if (global.autoplay_leveldir &&
5927 global.autoplay_mode != AUTOPLAY_MODE_TEST)
5929 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
5930 global.autoplay_leveldir);
5931 if (leveldir_current == NULL)
5932 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
5935 SetLevelSetInfo(leveldir_current->identifier, level_nr);
5938 static void InitLevelArtworkInfo(void)
5940 LoadLevelArtworkInfo();
5943 static void InitImages(void)
5945 print_timestamp_init("InitImages");
5948 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
5949 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5950 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
5951 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5952 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
5953 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5954 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
5955 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5958 setLevelArtworkDir(artwork.gfx_first);
5961 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
5962 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5963 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
5964 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5965 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
5966 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5967 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
5968 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5972 Debug("init:InitImages", "InitImages for '%s' ['%s', '%s'] ['%s', '%s']",
5973 leveldir_current->identifier,
5974 artwork.gfx_current_identifier,
5975 artwork.gfx_current->identifier,
5976 leveldir_current->graphics_set,
5977 leveldir_current->graphics_path);
5980 UPDATE_BUSY_STATE();
5982 ReloadCustomImages();
5983 print_timestamp_time("ReloadCustomImages");
5985 UPDATE_BUSY_STATE();
5987 LoadCustomElementDescriptions();
5988 print_timestamp_time("LoadCustomElementDescriptions");
5990 UPDATE_BUSY_STATE();
5992 LoadMenuDesignSettings();
5993 print_timestamp_time("LoadMenuDesignSettings");
5995 UPDATE_BUSY_STATE();
5997 ReinitializeGraphics();
5998 print_timestamp_time("ReinitializeGraphics");
6000 LoadMenuDesignSettings_AfterGraphics();
6001 print_timestamp_time("LoadMenuDesignSettings_AfterGraphics");
6003 UPDATE_BUSY_STATE();
6005 print_timestamp_done("InitImages");
6008 static void InitSound(void)
6010 print_timestamp_init("InitSound");
6012 // set artwork path to send it to the sound server process
6013 setLevelArtworkDir(artwork.snd_first);
6015 InitReloadCustomSounds();
6016 print_timestamp_time("InitReloadCustomSounds");
6018 ReinitializeSounds();
6019 print_timestamp_time("ReinitializeSounds");
6021 print_timestamp_done("InitSound");
6024 static void InitMusic(void)
6026 print_timestamp_init("InitMusic");
6028 // set artwork path to send it to the sound server process
6029 setLevelArtworkDir(artwork.mus_first);
6031 InitReloadCustomMusic();
6032 print_timestamp_time("InitReloadCustomMusic");
6034 ReinitializeMusic();
6035 print_timestamp_time("ReinitializeMusic");
6037 print_timestamp_done("InitMusic");
6040 static void InitArtworkDone(void)
6042 if (program.headless)
6045 InitGlobalAnimations();
6048 static void InitNetworkSettings(void)
6050 boolean network_enabled = (options.network || setup.network_mode);
6051 char *network_server = (options.server_host != NULL ? options.server_host :
6052 setup.network_server_hostname);
6054 if (strEqual(network_server, STR_NETWORK_AUTO_DETECT))
6055 network_server = NULL;
6057 InitNetworkInfo(network_enabled,
6061 options.server_port);
6064 void InitNetworkServer(void)
6066 if (!network.enabled || network.connected)
6069 LimitScreenUpdates(FALSE);
6071 if (game_status == GAME_MODE_LOADING)
6074 if (!ConnectToServer(network.server_host, network.server_port))
6076 network.enabled = FALSE;
6078 setup.network_mode = FALSE;
6082 SendToServer_ProtocolVersion();
6083 SendToServer_PlayerName(setup.player_name);
6084 SendToServer_NrWanted(setup.network_player_nr + 1);
6086 network.connected = TRUE;
6089 // short time to recognize result of network initialization
6090 if (game_status == GAME_MODE_LOADING)
6091 Delay_WithScreenUpdates(1000);
6094 static boolean CheckArtworkConfigForCustomElements(char *filename)
6096 SetupFileHash *setup_file_hash;
6097 boolean redefined_ce_found = FALSE;
6099 // !!! CACHE THIS BY USING HASH 'filename' => 'true/false' !!!
6101 if ((setup_file_hash = loadSetupFileHash(filename)) != NULL)
6103 BEGIN_HASH_ITERATION(setup_file_hash, itr)
6105 char *token = HASH_ITERATION_TOKEN(itr);
6107 if (strPrefix(token, "custom_"))
6109 redefined_ce_found = TRUE;
6114 END_HASH_ITERATION(setup_file_hash, itr)
6116 freeSetupFileHash(setup_file_hash);
6119 return redefined_ce_found;
6122 static boolean CheckArtworkTypeForRedefinedCustomElements(int type)
6124 char *filename_base, *filename_local;
6125 boolean redefined_ce_found = FALSE;
6127 setLevelArtworkDir(ARTWORK_FIRST_NODE(artwork, type));
6130 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6131 "leveldir_current->identifier == '%s'",
6132 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
6133 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6134 "leveldir_current->graphics_path == '%s'",
6135 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
6136 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6137 "leveldir_current->graphics_set == '%s'",
6138 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
6139 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6140 "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
6141 leveldir_current == NULL ? "[NULL]" :
6142 LEVELDIR_ARTWORK_SET(leveldir_current, type));
6145 // first look for special artwork configured in level series config
6146 filename_base = getCustomArtworkLevelConfigFilename(type);
6149 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6150 "filename_base == '%s'", filename_base);
6153 if (fileExists(filename_base))
6154 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_base);
6156 filename_local = getCustomArtworkConfigFilename(type);
6159 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6160 "filename_local == '%s'", filename_local);
6163 if (filename_local != NULL && !strEqual(filename_base, filename_local))
6164 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_local);
6167 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6168 "redefined_ce_found == %d", redefined_ce_found);
6171 return redefined_ce_found;
6174 static void InitOverrideArtwork(void)
6176 boolean redefined_ce_found = FALSE;
6178 // to check if this level set redefines any CEs, do not use overriding
6179 gfx.override_level_graphics = FALSE;
6180 gfx.override_level_sounds = FALSE;
6181 gfx.override_level_music = FALSE;
6183 // now check if this level set has definitions for custom elements
6184 if (setup.override_level_graphics == AUTO ||
6185 setup.override_level_sounds == AUTO ||
6186 setup.override_level_music == AUTO)
6187 redefined_ce_found =
6188 (CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_GRAPHICS) |
6189 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_SOUNDS) |
6190 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_MUSIC));
6193 Debug("init:InitOverrideArtwork", "redefined_ce_found == %d",
6194 redefined_ce_found);
6197 if (redefined_ce_found)
6199 // this level set has CE definitions: change "AUTO" to "FALSE"
6200 gfx.override_level_graphics = (setup.override_level_graphics == TRUE);
6201 gfx.override_level_sounds = (setup.override_level_sounds == TRUE);
6202 gfx.override_level_music = (setup.override_level_music == TRUE);
6206 // this level set has no CE definitions: change "AUTO" to "TRUE"
6207 gfx.override_level_graphics = (setup.override_level_graphics != FALSE);
6208 gfx.override_level_sounds = (setup.override_level_sounds != FALSE);
6209 gfx.override_level_music = (setup.override_level_music != FALSE);
6213 Debug("init:InitOverrideArtwork", "%d, %d, %d",
6214 gfx.override_level_graphics,
6215 gfx.override_level_sounds,
6216 gfx.override_level_music);
6220 static char *setNewArtworkIdentifier(int type)
6222 static char *last_leveldir_identifier[3] = { NULL, NULL, NULL };
6223 static char *last_artwork_identifier[3] = { NULL, NULL, NULL };
6224 static boolean last_override_level_artwork[3] = { FALSE, FALSE, FALSE };
6225 static boolean last_has_custom_artwork_set[3] = { FALSE, FALSE, FALSE };
6226 static boolean initialized[3] = { FALSE, FALSE, FALSE };
6227 TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type);
6228 boolean setup_override_artwork = GFX_OVERRIDE_ARTWORK(type);
6229 char *setup_artwork_set = SETUP_ARTWORK_SET(setup, type);
6230 char *leveldir_identifier = leveldir_current->identifier;
6231 // !!! setLevelArtworkDir() should be moved to an earlier stage !!!
6232 char *leveldir_artwork_set = setLevelArtworkDir(artwork_first_node);
6233 boolean has_level_artwork_set = (leveldir_artwork_set != NULL);
6234 TreeInfo *custom_artwork_set =
6235 getTreeInfoFromIdentifier(artwork_first_node, leveldir_identifier);
6236 boolean has_custom_artwork_set = (custom_artwork_set != NULL);
6237 char *artwork_current_identifier;
6238 char *artwork_new_identifier = NULL; // default: nothing has changed
6240 // leveldir_current may be invalid (level group, parent link)
6241 if (!validLevelSeries(leveldir_current))
6244 /* 1st step: determine artwork set to be activated in descending order:
6245 --------------------------------------------------------------------
6246 1. setup artwork (when configured to override everything else)
6247 2. artwork set configured in "levelinfo.conf" of current level set
6248 (artwork in level directory will have priority when loading later)
6249 3. artwork in level directory (stored in artwork sub-directory)
6250 4. setup artwork (currently configured in setup menu) */
6252 if (setup_override_artwork)
6253 artwork_current_identifier = setup_artwork_set;
6254 else if (has_level_artwork_set)
6255 artwork_current_identifier = leveldir_artwork_set;
6256 else if (has_custom_artwork_set)
6257 artwork_current_identifier = leveldir_identifier;
6259 artwork_current_identifier = setup_artwork_set;
6261 /* 2nd step: check if it is really needed to reload artwork set
6262 ------------------------------------------------------------ */
6264 // ---------- reload if level set and also artwork set has changed ----------
6265 if (last_leveldir_identifier[type] != leveldir_identifier &&
6266 (last_has_custom_artwork_set[type] || has_custom_artwork_set))
6267 artwork_new_identifier = artwork_current_identifier;
6269 last_leveldir_identifier[type] = leveldir_identifier;
6270 last_has_custom_artwork_set[type] = has_custom_artwork_set;
6272 // ---------- reload if "override artwork" setting has changed --------------
6273 if (last_override_level_artwork[type] != setup_override_artwork)
6274 artwork_new_identifier = artwork_current_identifier;
6276 last_override_level_artwork[type] = setup_override_artwork;
6278 // ---------- reload if current artwork identifier has changed --------------
6279 if (!strEqual(last_artwork_identifier[type], artwork_current_identifier))
6280 artwork_new_identifier = artwork_current_identifier;
6282 // (we cannot compare string pointers here, so copy string content itself)
6283 setString(&last_artwork_identifier[type], artwork_current_identifier);
6285 // ---------- set new artwork identifier ----------
6286 *(ARTWORK_CURRENT_IDENTIFIER_PTR(artwork, type)) = artwork_current_identifier;
6288 // ---------- do not reload directly after starting -------------------------
6289 if (!initialized[type])
6290 artwork_new_identifier = NULL;
6292 initialized[type] = TRUE;
6294 return artwork_new_identifier;
6297 static void InitArtworkIdentifier(void)
6299 setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6300 setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6301 setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6304 void ReloadCustomArtwork(int force_reload)
6306 int last_game_status = game_status; // save current game status
6307 char *gfx_new_identifier;
6308 char *snd_new_identifier;
6309 char *mus_new_identifier;
6310 boolean force_reload_gfx = (force_reload & (1 << ARTWORK_TYPE_GRAPHICS));
6311 boolean force_reload_snd = (force_reload & (1 << ARTWORK_TYPE_SOUNDS));
6312 boolean force_reload_mus = (force_reload & (1 << ARTWORK_TYPE_MUSIC));
6313 boolean reload_needed;
6315 InitOverrideArtwork();
6317 AdjustGraphicsForEMC();
6318 AdjustSoundsForEMC();
6320 gfx_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6321 snd_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6322 mus_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6324 reload_needed = (gfx_new_identifier != NULL || force_reload_gfx ||
6325 snd_new_identifier != NULL || force_reload_snd ||
6326 mus_new_identifier != NULL || force_reload_mus);
6331 print_timestamp_init("ReloadCustomArtwork");
6333 SetGameStatus(GAME_MODE_LOADING);
6335 FadeOut(REDRAW_ALL);
6337 SetLoadingBackgroundImage();
6339 ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6340 print_timestamp_time("ClearRectangleOnBackground");
6344 UPDATE_BUSY_STATE();
6346 InitMissingFileHash();
6348 if (gfx_new_identifier != NULL || force_reload_gfx)
6351 Debug("init:ReloadCustomArtwork",
6352 "RELOADING GRAPHICS '%s' -> '%s' ['%s', '%s']",
6353 artwork.gfx_current_identifier,
6355 artwork.gfx_current->identifier,
6356 leveldir_current->graphics_set);
6360 print_timestamp_time("InitImages");
6363 if (snd_new_identifier != NULL || force_reload_snd)
6366 print_timestamp_time("InitSound");
6369 if (mus_new_identifier != NULL || force_reload_mus)
6372 print_timestamp_time("InitMusic");
6377 SetGameStatus(last_game_status); // restore current game status
6379 FadeOut(REDRAW_ALL);
6381 RedrawGlobalBorder();
6383 // force redraw of (open or closed) door graphics
6384 SetDoorState(DOOR_OPEN_ALL);
6385 CloseDoor(DOOR_CLOSE_ALL | DOOR_NO_DELAY);
6387 FadeSetEnterScreen();
6388 FadeSkipNextFadeOut();
6390 print_timestamp_done("ReloadCustomArtwork");
6392 LimitScreenUpdates(FALSE);
6395 void KeyboardAutoRepeatOffUnlessAutoplay(void)
6397 if (global.autoplay_leveldir == NULL)
6398 KeyboardAutoRepeatOff();
6401 void DisplayExitMessage(char *format, va_list ap)
6403 // also check for initialized video (headless flag may be temporarily unset)
6404 if (program.headless || !video.initialized)
6407 // check if draw buffer and fonts for exit message are already available
6408 if (drawto == NULL || font_initial[NUM_INITIAL_FONTS - 1].bitmap == NULL)
6411 int font_1 = FC_RED;
6412 int font_2 = FC_YELLOW;
6413 int font_3 = FC_BLUE;
6414 int font_width = getFontWidth(font_2);
6415 int font_height = getFontHeight(font_2);
6418 int sxsize = WIN_XSIZE - 2 * sx;
6419 int sysize = WIN_YSIZE - 2 * sy;
6420 int line_length = sxsize / font_width;
6421 int max_lines = sysize / font_height;
6422 int num_lines_printed;
6426 gfx.sxsize = sxsize;
6427 gfx.sysize = sysize;
6431 ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6433 DrawTextSCentered(sy, font_1, "Fatal error:");
6434 sy += 3 * font_height;;
6437 DrawTextBufferVA(sx, sy, format, ap, font_2,
6438 line_length, line_length, max_lines,
6439 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6440 sy += (num_lines_printed + 3) * font_height;
6442 DrawTextSCentered(sy, font_1, "For details, see the following error file:");
6443 sy += 3 * font_height;
6446 DrawTextBuffer(sx, sy, program.log_filename[LOG_ERR_ID], font_2,
6447 line_length, line_length, max_lines,
6448 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6450 DrawTextSCentered(SYSIZE - 20, font_3, "Press any key or button to exit");
6452 redraw_mask = REDRAW_ALL;
6454 // force drawing exit message even if screen updates are currently limited
6455 LimitScreenUpdates(FALSE);
6459 // deactivate toons on error message screen
6460 setup.toons = FALSE;
6462 WaitForEventToContinue();
6466 // ============================================================================
6468 // ============================================================================
6472 print_timestamp_init("OpenAll");
6474 SetGameStatus(GAME_MODE_LOADING);
6478 InitGlobal(); // initialize some global variables
6480 InitRND(NEW_RANDOMIZE);
6481 InitSimpleRandom(NEW_RANDOMIZE);
6482 InitBetterRandom(NEW_RANDOMIZE);
6484 InitMissingFileHash();
6486 print_timestamp_time("[init global stuff]");
6490 print_timestamp_time("[init setup/config stuff (1)]");
6492 if (options.execute_command)
6493 Execute_Command(options.execute_command);
6495 InitNetworkSettings();
6499 if (network.serveronly)
6501 #if defined(PLATFORM_UNIX)
6502 NetworkServer(network.server_port, TRUE);
6504 Warn("networking only supported in Unix version");
6507 exit(0); // never reached, server loops forever
6511 print_timestamp_time("[init setup/config stuff (2)]");
6513 print_timestamp_time("[init setup/config stuff (3)]");
6514 InitArtworkInfo(); // needed before loading gfx, sound & music
6515 print_timestamp_time("[init setup/config stuff (4)]");
6516 InitArtworkConfig(); // needed before forking sound child process
6517 print_timestamp_time("[init setup/config stuff (5)]");
6519 print_timestamp_time("[init setup/config stuff (6)]");
6523 print_timestamp_time("[init setup/config stuff]");
6525 InitVideoDefaults();
6527 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
6530 InitEventFilter(FilterMouseMotionEvents);
6532 print_timestamp_time("[init video stuff]");
6534 InitElementPropertiesStatic();
6535 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
6536 InitElementPropertiesGfxElement();
6538 print_timestamp_time("[init element properties stuff]");
6542 print_timestamp_time("InitGfx");
6545 print_timestamp_time("InitLevelInfo");
6547 InitLevelArtworkInfo();
6548 print_timestamp_time("InitLevelArtworkInfo");
6550 InitOverrideArtwork(); // needs to know current level directory
6551 print_timestamp_time("InitOverrideArtwork");
6553 InitArtworkIdentifier(); // needs to know current level directory
6554 print_timestamp_time("InitArtworkIdentifier");
6556 InitImages(); // needs to know current level directory
6557 print_timestamp_time("InitImages");
6559 InitSound(); // needs to know current level directory
6560 print_timestamp_time("InitSound");
6562 InitMusic(); // needs to know current level directory
6563 print_timestamp_time("InitMusic");
6567 InitGfxBackground();
6573 if (global.autoplay_leveldir)
6578 else if (global.patchtapes_leveldir)
6583 else if (global.convert_leveldir)
6588 else if (global.dumplevel_leveldir)
6593 else if (global.dumptape_leveldir)
6598 else if (global.create_sketch_images_dir)
6600 CreateLevelSketchImages();
6603 else if (global.create_collect_images_dir)
6605 CreateCollectElementImages();
6609 InitNetworkServer();
6611 SetGameStatus(GAME_MODE_MAIN);
6613 FadeSetEnterScreen();
6614 if (!(fading.fade_mode & FADE_TYPE_TRANSFORM))
6615 FadeSkipNextFadeOut();
6617 print_timestamp_time("[post-artwork]");
6619 print_timestamp_done("OpenAll");
6621 if (setup.ask_for_remaining_tapes)
6622 setup.ask_for_uploading_tapes = TRUE;
6627 Debug("internal:path", "SDL_GetBasePath() == '%s'",
6629 Debug("internal:path", "SDL_GetPrefPath() == '%s'",
6630 SDL_GetPrefPath("artsoft", "rocksndiamonds"));
6631 #if defined(PLATFORM_ANDROID)
6632 Debug("internal:path", "SDL_AndroidGetInternalStoragePath() == '%s'",
6633 SDL_AndroidGetInternalStoragePath());
6634 Debug("internal:path", "SDL_AndroidGetExternalStoragePath() == '%s'",
6635 SDL_AndroidGetExternalStoragePath());
6636 Debug("internal:path", "SDL_AndroidGetExternalStorageState() == '%s'",
6637 (SDL_AndroidGetExternalStorageState() &
6638 SDL_ANDROID_EXTERNAL_STORAGE_WRITE ? "writable" :
6639 SDL_AndroidGetExternalStorageState() &
6640 SDL_ANDROID_EXTERNAL_STORAGE_READ ? "readable" : "not available"));
6645 static boolean WaitForApiThreads(void)
6647 DelayCounter thread_delay = { 10000 };
6649 if (program.api_thread_count == 0)
6652 // deactivate global animations (not accessible in game state "loading")
6653 setup.toons = FALSE;
6655 // set game state to "loading" to be able to show busy animation
6656 SetGameStatus(GAME_MODE_LOADING);
6658 ResetDelayCounter(&thread_delay);
6660 // wait for threads to finish (and fail on timeout)
6661 while (program.api_thread_count > 0)
6663 if (DelayReached(&thread_delay))
6665 Error("failed waiting for threads - TIMEOUT");
6670 UPDATE_BUSY_STATE();
6678 void CloseAllAndExit(int exit_value)
6680 WaitForApiThreads();
6685 CloseAudio(); // called after freeing sounds (needed for SDL)
6693 // set a flag to tell the network server thread to quit and wait for it
6694 // using SDL_WaitThread()
6696 // Code used with SDL 1.2:
6697 // if (network.server_thread) // terminate network server
6698 // SDL_KillThread(network.server_thread);
6700 CloseVideoDisplay();
6701 ClosePlatformDependentStuff();
6703 if (exit_value != 0 && !options.execute_command)
6705 // fall back to default level set (current set may have caused an error)
6706 SaveLevelSetup_LastSeries_Deactivate();
6708 // tell user where to find error log file which may contain more details
6709 // (error notification now directly displayed on screen inside R'n'D
6710 // NotifyUserAboutErrorFile(); // currently only works for Windows