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 Bitmap *getLoadingBackgroundBitmap(int graphic)
115 return getBitmapFromGraphicOrDefault(graphic, INITIAL_IMG_BACKGROUND);
118 static void SetLoadingWindowBackgroundImage(int graphic)
120 SetWindowBackgroundBitmap(getLoadingBackgroundBitmap(graphic));
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 getFontBitmapID(int font_nr)
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;
398 return font_info[font_nr].special_bitmap_id[special];
403 static int getFontFromToken(char *token)
405 char *value = getHashEntry(font_token_hash, token);
410 // if font not found, use reliable default value
411 return FONT_INITIAL_1;
414 static void InitFontGraphicInfo(void)
416 static struct FontBitmapInfo *font_bitmap_info = NULL;
417 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
418 int num_property_mappings = getImageListPropertyMappingSize();
419 int num_font_bitmaps = NUM_FONTS;
422 if (graphic_info == NULL) // still at startup phase
424 InitFontInfo(font_initial, NUM_INITIAL_FONTS,
425 getFontBitmapID, getFontFromToken);
430 // ---------- initialize font graphic definitions ----------
432 // always start with reliable default values (normal font graphics)
433 for (i = 0; i < NUM_FONTS; i++)
434 font_info[i].graphic = IMG_FONT_INITIAL_1;
436 // initialize normal font/graphic mapping from static configuration
437 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
439 int font_nr = font_to_graphic[i].font_nr;
440 int special = font_to_graphic[i].special;
441 int graphic = font_to_graphic[i].graphic;
446 font_info[font_nr].graphic = graphic;
449 // always start with reliable default values (special font graphics)
450 for (i = 0; i < NUM_FONTS; i++)
452 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
454 font_info[i].special_graphic[j] = font_info[i].graphic;
455 font_info[i].special_bitmap_id[j] = i;
459 // initialize special 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;
465 int base_graphic = font2baseimg(font_nr);
467 if (IS_SPECIAL_GFX_ARG(special))
469 boolean base_redefined =
470 getImageListEntryFromImageID(base_graphic)->redefined;
471 boolean special_redefined =
472 getImageListEntryFromImageID(graphic)->redefined;
473 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
475 /* if the base font ("font.title_1", for example) has been redefined,
476 but not the special font ("font.title_1.LEVELS", for example), do not
477 use an existing (in this case considered obsolete) special font
478 anymore, but use the automatically determined default font */
479 /* special case: cloned special fonts must be explicitly redefined,
480 but are not automatically redefined by redefining base font */
481 if (base_redefined && !special_redefined && !special_cloned)
484 font_info[font_nr].special_graphic[special] = graphic;
485 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
490 // initialize special font/graphic mapping from dynamic configuration
491 for (i = 0; i < num_property_mappings; i++)
493 int font_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
494 int special = property_mapping[i].ext3_index;
495 int graphic = property_mapping[i].artwork_index;
497 if (font_nr < 0 || font_nr >= NUM_FONTS)
500 if (IS_SPECIAL_GFX_ARG(special))
502 font_info[font_nr].special_graphic[special] = graphic;
503 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
508 /* correct special font/graphic mapping for cloned fonts for downwards
509 compatibility of PREVIEW fonts -- this is only needed for implicit
510 redefinition of special font by redefined base font, and only if other
511 fonts are cloned from this special font (like in the "Zelda" level set) */
512 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
514 int font_nr = font_to_graphic[i].font_nr;
515 int special = font_to_graphic[i].special;
516 int graphic = font_to_graphic[i].graphic;
518 if (IS_SPECIAL_GFX_ARG(special))
520 boolean special_redefined =
521 getImageListEntryFromImageID(graphic)->redefined;
522 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
524 if (special_cloned && !special_redefined)
528 for (j = 0; font_to_graphic[j].font_nr > -1; j++)
530 int font_nr2 = font_to_graphic[j].font_nr;
531 int special2 = font_to_graphic[j].special;
532 int graphic2 = font_to_graphic[j].graphic;
534 if (IS_SPECIAL_GFX_ARG(special2) &&
535 graphic2 == graphic_info[graphic].clone_from)
537 font_info[font_nr].special_graphic[special] =
538 font_info[font_nr2].special_graphic[special2];
539 font_info[font_nr].special_bitmap_id[special] =
540 font_info[font_nr2].special_bitmap_id[special2];
547 // reset non-redefined ".active" font graphics if normal font is redefined
548 // (this different treatment is needed because normal and active fonts are
549 // independently defined ("active" is not a property of font definitions!)
550 for (i = 0; i < NUM_FONTS; i++)
552 int font_nr_base = i;
553 int font_nr_active = FONT_ACTIVE(font_nr_base);
555 // check only those fonts with exist as normal and ".active" variant
556 if (font_nr_base != font_nr_active)
558 int base_graphic = font_info[font_nr_base].graphic;
559 int active_graphic = font_info[font_nr_active].graphic;
560 boolean base_redefined =
561 getImageListEntryFromImageID(base_graphic)->redefined;
562 boolean active_redefined =
563 getImageListEntryFromImageID(active_graphic)->redefined;
565 /* if the base font ("font.menu_1", for example) has been redefined,
566 but not the active font ("font.menu_1.active", for example), do not
567 use an existing (in this case considered obsolete) active font
568 anymore, but use the automatically determined default font */
569 if (base_redefined && !active_redefined)
570 font_info[font_nr_active].graphic = base_graphic;
572 // now also check each "special" font (which may be the same as above)
573 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
575 int base_graphic = font_info[font_nr_base].special_graphic[j];
576 int active_graphic = font_info[font_nr_active].special_graphic[j];
577 boolean base_redefined =
578 getImageListEntryFromImageID(base_graphic)->redefined;
579 boolean active_redefined =
580 getImageListEntryFromImageID(active_graphic)->redefined;
582 // same as above, but check special graphic definitions, for example:
583 // redefined "font.menu_1.MAIN" invalidates "font.menu_1.active.MAIN"
584 if (base_redefined && !active_redefined)
586 font_info[font_nr_active].special_graphic[j] =
587 font_info[font_nr_base].special_graphic[j];
588 font_info[font_nr_active].special_bitmap_id[j] =
589 font_info[font_nr_base].special_bitmap_id[j];
595 // ---------- initialize font bitmap array ----------
597 if (font_bitmap_info != NULL)
598 FreeFontInfo(font_bitmap_info);
601 checked_calloc(num_font_bitmaps * sizeof(struct FontBitmapInfo));
603 // ---------- initialize font bitmap definitions ----------
605 for (i = 0; i < NUM_FONTS; i++)
607 if (i < NUM_INITIAL_FONTS)
609 font_bitmap_info[i] = font_initial[i];
613 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
615 int font_bitmap_id = font_info[i].special_bitmap_id[j];
616 int graphic = font_info[i].special_graphic[j];
618 // set 'graphic_info' for font entries, if uninitialized (guessed)
619 if (graphic_info[graphic].anim_frames < MIN_NUM_CHARS_PER_FONT)
621 graphic_info[graphic].anim_frames = DEFAULT_NUM_CHARS_PER_FONT;
622 graphic_info[graphic].anim_frames_per_line = DEFAULT_NUM_CHARS_PER_LINE;
625 // copy font relevant information from graphics information
626 font_bitmap_info[font_bitmap_id].bitmap = graphic_info[graphic].bitmap;
627 font_bitmap_info[font_bitmap_id].src_x = graphic_info[graphic].src_x;
628 font_bitmap_info[font_bitmap_id].src_y = graphic_info[graphic].src_y;
629 font_bitmap_info[font_bitmap_id].width = graphic_info[graphic].width;
630 font_bitmap_info[font_bitmap_id].height = graphic_info[graphic].height;
632 font_bitmap_info[font_bitmap_id].offset_x =
633 graphic_info[graphic].offset_x;
634 font_bitmap_info[font_bitmap_id].offset_y =
635 graphic_info[graphic].offset_y;
637 font_bitmap_info[font_bitmap_id].draw_xoffset =
638 graphic_info[graphic].draw_xoffset;
639 font_bitmap_info[font_bitmap_id].draw_yoffset =
640 graphic_info[graphic].draw_yoffset;
642 font_bitmap_info[font_bitmap_id].num_chars =
643 graphic_info[graphic].anim_frames;
644 font_bitmap_info[font_bitmap_id].num_chars_per_line =
645 graphic_info[graphic].anim_frames_per_line;
649 InitFontInfo(font_bitmap_info, num_font_bitmaps,
650 getFontBitmapID, getFontFromToken);
653 static void InitGlobalAnimGraphicInfo(void)
655 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
656 int num_property_mappings = getImageListPropertyMappingSize();
659 if (graphic_info == NULL) // still at startup phase
662 // always start with reliable default values (no global animations)
663 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
664 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
665 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
666 global_anim_info[i].graphic[j][k] = IMG_UNDEFINED;
668 // initialize global animation definitions from static configuration
669 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
671 int j = GLOBAL_ANIM_ID_PART_BASE;
672 int k = GFX_SPECIAL_ARG_DEFAULT;
674 global_anim_info[i].graphic[j][k] = IMG_GFX_GLOBAL_ANIM_1 + i;
677 // initialize global animation definitions from dynamic configuration
678 for (i = 0; i < num_property_mappings; i++)
680 int anim_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS - NUM_FONTS;
681 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
682 int special = property_mapping[i].ext3_index;
683 int graphic = property_mapping[i].artwork_index;
685 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
688 // set animation part to base part, if not specified
689 if (!IS_GLOBAL_ANIM_PART(part_nr))
690 part_nr = GLOBAL_ANIM_ID_PART_BASE;
692 // set animation screen to default, if not specified
693 if (!IS_SPECIAL_GFX_ARG(special))
694 special = GFX_SPECIAL_ARG_DEFAULT;
696 global_anim_info[anim_nr].graphic[part_nr][special] = graphic;
698 // fix default value for ".draw_masked" (for backward compatibility)
699 struct GraphicInfo *g = &graphic_info[graphic];
700 struct FileInfo *image = getImageListEntryFromImageID(graphic);
701 char **parameter_raw = image->parameter;
702 int p = GFX_ARG_DRAW_MASKED;
703 int draw_masked = get_graphic_parameter_value(parameter_raw[p],
704 image_config_suffix[p].token,
705 image_config_suffix[p].type);
707 // if ".draw_masked" parameter is undefined, use default value "TRUE"
708 if (draw_masked == ARG_UNDEFINED_VALUE)
709 g->draw_masked = TRUE;
713 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
714 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
715 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
716 if (global_anim_info[i].graphic[j][k] != IMG_UNDEFINED &&
717 graphic_info[global_anim_info[i].graphic[j][k]].bitmap != NULL)
718 Debug("init:InitGlobalAnimGraphicInfo",
719 "anim %d, part %d, mode %d => %d",
720 i, j, k, global_anim_info[i].graphic[j][k]);
724 static void InitGlobalAnimSoundInfo(void)
726 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
727 int num_property_mappings = getSoundListPropertyMappingSize();
730 // always start with reliable default values (no global animation sounds)
731 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
732 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
733 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
734 global_anim_info[i].sound[j][k] = SND_UNDEFINED;
736 // initialize global animation sound definitions from dynamic configuration
737 for (i = 0; i < num_property_mappings; i++)
739 int anim_nr = property_mapping[i].base_index - 2 * MAX_NUM_ELEMENTS;
740 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
741 int special = property_mapping[i].ext3_index;
742 int sound = property_mapping[i].artwork_index;
744 // sound uses control definition; map it to position of graphic (artwork)
745 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
747 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
750 // set animation part to base part, if not specified
751 if (!IS_GLOBAL_ANIM_PART(part_nr))
752 part_nr = GLOBAL_ANIM_ID_PART_BASE;
754 // set animation screen to default, if not specified
755 if (!IS_SPECIAL_GFX_ARG(special))
756 special = GFX_SPECIAL_ARG_DEFAULT;
758 global_anim_info[anim_nr].sound[part_nr][special] = sound;
762 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
763 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
764 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
765 if (global_anim_info[i].sound[j][k] != SND_UNDEFINED)
766 Debug("init:InitGlobalAnimSoundInfo",
767 "anim %d, part %d, mode %d => %d",
768 i, j, k, global_anim_info[i].sound[j][k]);
772 static void InitGlobalAnimMusicInfo(void)
774 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
775 int num_property_mappings = getMusicListPropertyMappingSize();
778 // always start with reliable default values (no global animation music)
779 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
780 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
781 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
782 global_anim_info[i].music[j][k] = MUS_UNDEFINED;
784 // initialize global animation music definitions from dynamic configuration
785 for (i = 0; i < num_property_mappings; i++)
787 int anim_nr = property_mapping[i].base_index - NUM_MUSIC_PREFIXES;
788 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
789 int special = property_mapping[i].ext2_index;
790 int music = property_mapping[i].artwork_index;
792 // music uses control definition; map it to position of graphic (artwork)
793 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
795 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
798 // set animation part to base part, if not specified
799 if (!IS_GLOBAL_ANIM_PART(part_nr))
800 part_nr = GLOBAL_ANIM_ID_PART_BASE;
802 // set animation screen to default, if not specified
803 if (!IS_SPECIAL_GFX_ARG(special))
804 special = GFX_SPECIAL_ARG_DEFAULT;
806 global_anim_info[anim_nr].music[part_nr][special] = music;
810 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
811 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
812 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
813 if (global_anim_info[i].music[j][k] != MUS_UNDEFINED)
814 Debug("init:InitGlobalAnimMusicInfo",
815 "anim %d, part %d, mode %d => %d",
816 i, j, k, global_anim_info[i].music[j][k]);
820 static void InitElementGraphicInfo(void)
822 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
823 int num_property_mappings = getImageListPropertyMappingSize();
826 if (graphic_info == NULL) // still at startup phase
829 // set values to -1 to identify later as "uninitialized" values
830 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
832 for (act = 0; act < NUM_ACTIONS; act++)
834 element_info[i].graphic[act] = -1;
835 element_info[i].crumbled[act] = -1;
837 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
839 element_info[i].direction_graphic[act][dir] = -1;
840 element_info[i].direction_crumbled[act][dir] = -1;
847 // initialize normal element/graphic mapping from static configuration
848 for (i = 0; element_to_graphic[i].element > -1; i++)
850 int element = element_to_graphic[i].element;
851 int action = element_to_graphic[i].action;
852 int direction = element_to_graphic[i].direction;
853 boolean crumbled = element_to_graphic[i].crumbled;
854 int graphic = element_to_graphic[i].graphic;
855 int base_graphic = el2baseimg(element);
857 if (graphic_info[graphic].bitmap == NULL)
860 if ((action > -1 || direction > -1 || crumbled == TRUE) &&
863 boolean base_redefined =
864 getImageListEntryFromImageID(base_graphic)->redefined;
865 boolean act_dir_redefined =
866 getImageListEntryFromImageID(graphic)->redefined;
868 /* if the base graphic ("emerald", for example) has been redefined,
869 but not the action graphic ("emerald.falling", for example), do not
870 use an existing (in this case considered obsolete) action graphic
871 anymore, but use the automatically determined default graphic */
872 if (base_redefined && !act_dir_redefined)
877 action = ACTION_DEFAULT;
882 element_info[element].direction_crumbled[action][direction] = graphic;
884 element_info[element].crumbled[action] = graphic;
889 element_info[element].direction_graphic[action][direction] = graphic;
891 element_info[element].graphic[action] = graphic;
895 // initialize normal element/graphic mapping from dynamic configuration
896 for (i = 0; i < num_property_mappings; i++)
898 int element = property_mapping[i].base_index;
899 int action = property_mapping[i].ext1_index;
900 int direction = property_mapping[i].ext2_index;
901 int special = property_mapping[i].ext3_index;
902 int graphic = property_mapping[i].artwork_index;
903 boolean crumbled = FALSE;
905 if (special == GFX_SPECIAL_ARG_CRUMBLED)
911 if (graphic_info[graphic].bitmap == NULL)
914 if (element >= MAX_NUM_ELEMENTS || special != -1)
918 action = ACTION_DEFAULT;
923 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
924 element_info[element].direction_crumbled[action][dir] = -1;
927 element_info[element].direction_crumbled[action][direction] = graphic;
929 element_info[element].crumbled[action] = graphic;
934 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
935 element_info[element].direction_graphic[action][dir] = -1;
938 element_info[element].direction_graphic[action][direction] = graphic;
940 element_info[element].graphic[action] = graphic;
944 // now copy all graphics that are defined to be cloned from other graphics
945 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
947 int graphic = element_info[i].graphic[ACTION_DEFAULT];
948 int crumbled_like, diggable_like;
953 crumbled_like = graphic_info[graphic].crumbled_like;
954 diggable_like = graphic_info[graphic].diggable_like;
956 if (crumbled_like != -1 && element_info[i].crumbled[ACTION_DEFAULT] == -1)
958 for (act = 0; act < NUM_ACTIONS; act++)
959 element_info[i].crumbled[act] =
960 element_info[crumbled_like].crumbled[act];
961 for (act = 0; act < NUM_ACTIONS; act++)
962 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
963 element_info[i].direction_crumbled[act][dir] =
964 element_info[crumbled_like].direction_crumbled[act][dir];
967 if (diggable_like != -1 && element_info[i].graphic[ACTION_DIGGING] == -1)
969 element_info[i].graphic[ACTION_DIGGING] =
970 element_info[diggable_like].graphic[ACTION_DIGGING];
971 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
972 element_info[i].direction_graphic[ACTION_DIGGING][dir] =
973 element_info[diggable_like].direction_graphic[ACTION_DIGGING][dir];
977 // set hardcoded definitions for some runtime elements without graphic
978 element_info[EL_AMOEBA_TO_DIAMOND].graphic[ACTION_DEFAULT] = IMG_AMOEBA_DEAD;
980 // set hardcoded definitions for some internal elements without graphic
981 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
983 if (IS_EDITOR_CASCADE_INACTIVE(i))
984 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST;
985 else if (IS_EDITOR_CASCADE_ACTIVE(i))
986 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST_ACTIVE;
989 // now set all undefined/invalid graphics to -1 to set to default after it
990 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
992 for (act = 0; act < NUM_ACTIONS; act++)
996 graphic = element_info[i].graphic[act];
997 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
998 element_info[i].graphic[act] = -1;
1000 graphic = element_info[i].crumbled[act];
1001 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1002 element_info[i].crumbled[act] = -1;
1004 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1006 graphic = element_info[i].direction_graphic[act][dir];
1007 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1008 element_info[i].direction_graphic[act][dir] = -1;
1010 graphic = element_info[i].direction_crumbled[act][dir];
1011 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
1012 element_info[i].direction_crumbled[act][dir] = -1;
1017 UPDATE_BUSY_STATE();
1019 // adjust graphics with 2nd tile for movement according to direction
1020 // (do this before correcting '-1' values to minimize calculations)
1021 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1023 for (act = 0; act < NUM_ACTIONS; act++)
1025 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1027 int graphic = element_info[i].direction_graphic[act][dir];
1028 int move_dir = (act == ACTION_FALLING ? MV_BIT_DOWN : dir);
1030 if (act == ACTION_FALLING) // special case
1031 graphic = element_info[i].graphic[act];
1033 if (graphic != -1 &&
1034 graphic_info[graphic].double_movement &&
1035 graphic_info[graphic].swap_double_tiles != 0)
1037 struct GraphicInfo *g = &graphic_info[graphic];
1038 int src_x_front = g->src_x;
1039 int src_y_front = g->src_y;
1040 int src_x_back = g->src_x + g->offset2_x;
1041 int src_y_back = g->src_y + g->offset2_y;
1042 boolean frames_are_ordered_diagonally = (g->offset_x != 0 &&
1044 boolean front_is_left_or_upper = (src_x_front < src_x_back ||
1045 src_y_front < src_y_back);
1046 boolean swap_movement_tiles_always = (g->swap_double_tiles == 1);
1047 boolean swap_movement_tiles_autodetected =
1048 (!frames_are_ordered_diagonally &&
1049 ((move_dir == MV_BIT_LEFT && !front_is_left_or_upper) ||
1050 (move_dir == MV_BIT_UP && !front_is_left_or_upper) ||
1051 (move_dir == MV_BIT_RIGHT && front_is_left_or_upper) ||
1052 (move_dir == MV_BIT_DOWN && front_is_left_or_upper)));
1054 // swap frontside and backside graphic tile coordinates, if needed
1055 if (swap_movement_tiles_always || swap_movement_tiles_autodetected)
1057 // get current (wrong) backside tile coordinates
1058 getGraphicSourceXY(graphic, 0, &src_x_back, &src_y_back, TRUE);
1060 // set frontside tile coordinates to backside tile coordinates
1061 g->src_x = src_x_back;
1062 g->src_y = src_y_back;
1064 // invert tile offset to point to new backside tile coordinates
1068 // do not swap front and backside tiles again after correction
1069 g->swap_double_tiles = 0;
1076 UPDATE_BUSY_STATE();
1078 // now set all '-1' values to element specific default values
1079 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1081 int default_graphic = element_info[i].graphic[ACTION_DEFAULT];
1082 int default_crumbled = element_info[i].crumbled[ACTION_DEFAULT];
1083 int default_direction_graphic[NUM_DIRECTIONS_FULL];
1084 int default_direction_crumbled[NUM_DIRECTIONS_FULL];
1086 if (default_graphic == -1)
1087 default_graphic = IMG_UNKNOWN;
1089 if (default_crumbled == -1)
1090 default_crumbled = default_graphic;
1092 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1094 default_direction_graphic[dir] =
1095 element_info[i].direction_graphic[ACTION_DEFAULT][dir];
1096 default_direction_crumbled[dir] =
1097 element_info[i].direction_crumbled[ACTION_DEFAULT][dir];
1099 if (default_direction_graphic[dir] == -1)
1100 default_direction_graphic[dir] = default_graphic;
1102 if (default_direction_crumbled[dir] == -1)
1103 default_direction_crumbled[dir] = default_direction_graphic[dir];
1106 for (act = 0; act < NUM_ACTIONS; act++)
1108 boolean act_remove = ((IS_DIGGABLE(i) && act == ACTION_DIGGING) ||
1109 (IS_SNAPPABLE(i) && act == ACTION_SNAPPING) ||
1110 (IS_COLLECTIBLE(i) && act == ACTION_COLLECTING));
1111 boolean act_turning = (act == ACTION_TURNING_FROM_LEFT ||
1112 act == ACTION_TURNING_FROM_RIGHT ||
1113 act == ACTION_TURNING_FROM_UP ||
1114 act == ACTION_TURNING_FROM_DOWN);
1116 // generic default action graphic (defined by "[default]" directive)
1117 int default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1118 int default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1119 int default_remove_graphic = IMG_EMPTY;
1121 if (act_remove && default_action_graphic != -1)
1122 default_remove_graphic = default_action_graphic;
1124 // look for special default action graphic (classic game specific)
1125 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].graphic[act] != -1)
1126 default_action_graphic = element_info[EL_BD_DEFAULT].graphic[act];
1127 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].graphic[act] != -1)
1128 default_action_graphic = element_info[EL_SP_DEFAULT].graphic[act];
1129 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].graphic[act] != -1)
1130 default_action_graphic = element_info[EL_SB_DEFAULT].graphic[act];
1131 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].graphic[act] != -1)
1132 default_action_graphic = element_info[EL_MM_DEFAULT].graphic[act];
1134 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].crumbled[act] != -1)
1135 default_action_crumbled = element_info[EL_BD_DEFAULT].crumbled[act];
1136 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].crumbled[act] != -1)
1137 default_action_crumbled = element_info[EL_SP_DEFAULT].crumbled[act];
1138 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].crumbled[act] != -1)
1139 default_action_crumbled = element_info[EL_SB_DEFAULT].crumbled[act];
1140 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].crumbled[act] != -1)
1141 default_action_crumbled = element_info[EL_MM_DEFAULT].crumbled[act];
1143 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
1144 // !!! make this better !!!
1145 if (i == EL_EMPTY_SPACE)
1147 default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1148 default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1151 if (default_action_graphic == -1)
1152 default_action_graphic = default_graphic;
1154 if (default_action_crumbled == -1)
1155 default_action_crumbled = default_action_graphic;
1157 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1159 // use action graphic as the default direction graphic, if undefined
1160 int default_action_direction_graphic = element_info[i].graphic[act];
1161 int default_action_direction_crumbled = element_info[i].crumbled[act];
1163 // no graphic for current action -- use default direction graphic
1164 if (default_action_direction_graphic == -1)
1165 default_action_direction_graphic =
1166 (act_remove ? default_remove_graphic :
1168 element_info[i].direction_graphic[ACTION_TURNING][dir] :
1169 default_action_graphic != default_graphic ?
1170 default_action_graphic :
1171 default_direction_graphic[dir]);
1173 if (element_info[i].direction_graphic[act][dir] == -1)
1174 element_info[i].direction_graphic[act][dir] =
1175 default_action_direction_graphic;
1177 if (default_action_direction_crumbled == -1)
1178 default_action_direction_crumbled =
1179 element_info[i].direction_graphic[act][dir];
1181 if (element_info[i].direction_crumbled[act][dir] == -1)
1182 element_info[i].direction_crumbled[act][dir] =
1183 default_action_direction_crumbled;
1186 // no graphic for this specific action -- use default action graphic
1187 if (element_info[i].graphic[act] == -1)
1188 element_info[i].graphic[act] =
1189 (act_remove ? default_remove_graphic :
1190 act_turning ? element_info[i].graphic[ACTION_TURNING] :
1191 default_action_graphic);
1193 if (element_info[i].crumbled[act] == -1)
1194 element_info[i].crumbled[act] = element_info[i].graphic[act];
1198 UPDATE_BUSY_STATE();
1201 static void InitElementSpecialGraphicInfo(void)
1203 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
1204 int num_property_mappings = getImageListPropertyMappingSize();
1207 // always start with reliable default values
1208 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1209 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1210 element_info[i].special_graphic[j] =
1211 element_info[i].graphic[ACTION_DEFAULT];
1213 // initialize special element/graphic mapping from static configuration
1214 for (i = 0; element_to_special_graphic[i].element > -1; i++)
1216 int element = element_to_special_graphic[i].element;
1217 int special = element_to_special_graphic[i].special;
1218 int graphic = element_to_special_graphic[i].graphic;
1219 int base_graphic = el2baseimg(element);
1220 boolean base_redefined =
1221 getImageListEntryFromImageID(base_graphic)->redefined;
1222 boolean special_redefined =
1223 getImageListEntryFromImageID(graphic)->redefined;
1225 /* if the base graphic ("emerald", for example) has been redefined,
1226 but not the special graphic ("emerald.EDITOR", for example), do not
1227 use an existing (in this case considered obsolete) special graphic
1228 anymore, but use the automatically created (down-scaled) graphic */
1229 if (base_redefined && !special_redefined)
1232 element_info[element].special_graphic[special] = graphic;
1235 // initialize special element/graphic mapping from dynamic configuration
1236 for (i = 0; i < num_property_mappings; i++)
1238 int element = property_mapping[i].base_index;
1239 int action = property_mapping[i].ext1_index;
1240 int direction = property_mapping[i].ext2_index;
1241 int special = property_mapping[i].ext3_index;
1242 int graphic = property_mapping[i].artwork_index;
1244 // for action ".active", replace element with active element, if exists
1245 if (action == ACTION_ACTIVE && element != ELEMENT_ACTIVE(element))
1247 element = ELEMENT_ACTIVE(element);
1251 if (element >= MAX_NUM_ELEMENTS)
1254 // do not change special graphic if action or direction was specified
1255 if (action != -1 || direction != -1)
1258 if (IS_SPECIAL_GFX_ARG(special))
1259 element_info[element].special_graphic[special] = graphic;
1262 // now set all undefined/invalid graphics to default
1263 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1264 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1265 if (graphic_info[element_info[i].special_graphic[j]].bitmap == NULL)
1266 element_info[i].special_graphic[j] =
1267 element_info[i].graphic[ACTION_DEFAULT];
1270 static int get_graphic_parameter_value(char *value_raw, char *suffix, int type)
1272 if (type != TYPE_ELEMENT && type != TYPE_GRAPHIC)
1273 return get_parameter_value(value_raw, suffix, type);
1275 if (strEqual(value_raw, ARG_UNDEFINED))
1276 return ARG_UNDEFINED_VALUE;
1278 if (type == TYPE_ELEMENT)
1280 char *value = getHashEntry(element_token_hash, value_raw);
1285 Warn("error found in config file:");
1286 Warn("- config file: '%s'", getImageConfigFilename());
1287 Warn("error: invalid element token '%s'", value_raw);
1288 Warn("custom graphic rejected for this element/action");
1289 Warn("fallback done to undefined element for this graphic");
1293 return (value != NULL ? atoi(value) : EL_UNDEFINED);
1295 else if (type == TYPE_GRAPHIC)
1297 char *value = getHashEntry(graphic_token_hash, value_raw);
1298 int fallback_graphic = IMG_CHAR_EXCLAM;
1303 Warn("error found in config file:");
1304 Warn("- config file: '%s'", getImageConfigFilename());
1305 Warn("error: invalid graphic token '%s'", value_raw);
1306 Warn("custom graphic rejected for this element/action");
1307 Warn("fallback done to 'char_exclam' for this graphic");
1311 return (value != NULL ? atoi(value) : fallback_graphic);
1317 static int get_scaled_graphic_width(int graphic)
1319 int original_width = getOriginalImageWidthFromImageID(graphic);
1320 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1322 return original_width * scale_up_factor;
1325 static int get_scaled_graphic_height(int graphic)
1327 int original_height = getOriginalImageHeightFromImageID(graphic);
1328 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1330 return original_height * scale_up_factor;
1333 static void set_graphic_parameters_ext(int graphic, int *parameter,
1334 Bitmap **src_bitmaps)
1336 struct GraphicInfo *g = &graphic_info[graphic];
1337 Bitmap *src_bitmap = (src_bitmaps ? src_bitmaps[IMG_BITMAP_STANDARD] : NULL);
1338 int anim_frames_per_row = 1, anim_frames_per_col = 1;
1339 int anim_frames_per_line = 1;
1341 // always start with reliable default values
1342 g->src_image_width = 0;
1343 g->src_image_height = 0;
1346 g->width = TILEX; // default for element graphics
1347 g->height = TILEY; // default for element graphics
1348 g->offset_x = 0; // one or both of these values ...
1349 g->offset_y = 0; // ... will be corrected later
1350 g->offset2_x = 0; // one or both of these values ...
1351 g->offset2_y = 0; // ... will be corrected later
1352 g->swap_double_tiles = -1; // auto-detect tile swapping
1353 g->crumbled_like = -1; // do not use clone element
1354 g->diggable_like = -1; // do not use clone element
1355 g->border_size = TILEX / 8; // "CRUMBLED" border size
1356 g->scale_up_factor = 1; // default: no scaling up
1357 g->tile_size = TILESIZE; // default: standard tile size
1358 g->clone_from = -1; // do not use clone graphic
1359 g->init_delay_fixed = 0;
1360 g->init_delay_random = 0;
1361 g->init_delay_action = -1;
1362 g->anim_delay_fixed = 0;
1363 g->anim_delay_random = 0;
1364 g->anim_delay_action = -1;
1365 g->post_delay_fixed = 0;
1366 g->post_delay_random = 0;
1367 g->post_delay_action = -1;
1368 g->init_event = ANIM_EVENT_UNDEFINED;
1369 g->anim_event = ANIM_EVENT_UNDEFINED;
1370 g->init_event_action = -1;
1371 g->anim_event_action = -1;
1372 g->draw_masked = FALSE;
1374 g->fade_mode = FADE_MODE_DEFAULT;
1378 g->auto_delay_unit = AUTO_DELAY_UNIT_DEFAULT;
1379 g->align = ALIGN_CENTER; // default for title screens
1380 g->valign = VALIGN_MIDDLE; // default for title screens
1381 g->sort_priority = 0; // default for title screens
1383 g->style = STYLE_DEFAULT;
1385 g->bitmaps = src_bitmaps;
1386 g->bitmap = src_bitmap;
1388 // optional zoom factor for scaling up the image to a larger size
1389 if (parameter[GFX_ARG_SCALE_UP_FACTOR] != ARG_UNDEFINED_VALUE)
1390 g->scale_up_factor = parameter[GFX_ARG_SCALE_UP_FACTOR];
1391 if (g->scale_up_factor < 1)
1392 g->scale_up_factor = 1; // no scaling
1394 // optional tile size for using non-standard image size
1395 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1397 g->tile_size = parameter[GFX_ARG_TILE_SIZE];
1400 // CHECK: should tile sizes less than standard tile size be allowed?
1401 if (g->tile_size < TILESIZE)
1402 g->tile_size = TILESIZE; // standard tile size
1405 // when setting tile size, also set width and height accordingly
1406 g->width = g->tile_size;
1407 g->height = g->tile_size;
1410 if (g->use_image_size)
1412 // set new default bitmap size (with scaling, but without small images)
1413 g->width = get_scaled_graphic_width(graphic);
1414 g->height = get_scaled_graphic_height(graphic);
1417 // optional width and height of each animation frame
1418 if (parameter[GFX_ARG_WIDTH] != ARG_UNDEFINED_VALUE)
1419 g->width = parameter[GFX_ARG_WIDTH];
1420 if (parameter[GFX_ARG_HEIGHT] != ARG_UNDEFINED_VALUE)
1421 g->height = parameter[GFX_ARG_HEIGHT];
1423 // optional x and y tile position of animation frame sequence
1424 if (parameter[GFX_ARG_XPOS] != ARG_UNDEFINED_VALUE)
1425 g->src_x = parameter[GFX_ARG_XPOS] * g->width;
1426 if (parameter[GFX_ARG_YPOS] != ARG_UNDEFINED_VALUE)
1427 g->src_y = parameter[GFX_ARG_YPOS] * g->height;
1429 // optional x and y pixel position of animation frame sequence
1430 if (parameter[GFX_ARG_X] != ARG_UNDEFINED_VALUE)
1431 g->src_x = parameter[GFX_ARG_X];
1432 if (parameter[GFX_ARG_Y] != ARG_UNDEFINED_VALUE)
1433 g->src_y = parameter[GFX_ARG_Y];
1440 Warn("invalid value %d for '%s.width' (fallback done to %d)",
1441 g->width, getTokenFromImageID(graphic), TILEX);
1444 g->width = TILEX; // will be checked to be inside bitmap later
1450 Warn("invalid value %d for '%s.height' (fallback done to %d)",
1451 g->height, getTokenFromImageID(graphic), TILEY);
1454 g->height = TILEY; // will be checked to be inside bitmap later
1460 // get final bitmap size (with scaling, but without small images)
1461 int src_image_width = get_scaled_graphic_width(graphic);
1462 int src_image_height = get_scaled_graphic_height(graphic);
1464 if (src_image_width == 0 || src_image_height == 0)
1466 // only happens when loaded outside artwork system (like "global.busy")
1467 src_image_width = src_bitmap->width;
1468 src_image_height = src_bitmap->height;
1471 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1473 anim_frames_per_row = MAX(1, src_image_width / g->tile_size);
1474 anim_frames_per_col = MAX(1, src_image_height / g->tile_size);
1478 anim_frames_per_row = MAX(1, src_image_width / g->width);
1479 anim_frames_per_col = MAX(1, src_image_height / g->height);
1482 g->src_image_width = src_image_width;
1483 g->src_image_height = src_image_height;
1486 // correct x or y offset dependent of vertical or horizontal frame order
1487 if (parameter[GFX_ARG_VERTICAL]) // frames are ordered vertically
1489 g->offset_y = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1490 parameter[GFX_ARG_OFFSET] : g->height);
1491 anim_frames_per_line = anim_frames_per_col;
1493 else // frames are ordered horizontally
1495 g->offset_x = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1496 parameter[GFX_ARG_OFFSET] : g->width);
1497 anim_frames_per_line = anim_frames_per_row;
1500 // optionally, the x and y offset of frames can be specified directly
1501 if (parameter[GFX_ARG_XOFFSET] != ARG_UNDEFINED_VALUE)
1502 g->offset_x = parameter[GFX_ARG_XOFFSET];
1503 if (parameter[GFX_ARG_YOFFSET] != ARG_UNDEFINED_VALUE)
1504 g->offset_y = parameter[GFX_ARG_YOFFSET];
1506 // optionally, moving animations may have separate start and end graphics
1507 g->double_movement = parameter[GFX_ARG_2ND_MOVEMENT_TILE];
1509 if (parameter[GFX_ARG_2ND_VERTICAL] == ARG_UNDEFINED_VALUE)
1510 parameter[GFX_ARG_2ND_VERTICAL] = !parameter[GFX_ARG_VERTICAL];
1512 // correct x or y offset2 dependent of vertical or horizontal frame order
1513 if (parameter[GFX_ARG_2ND_VERTICAL]) // frames are ordered vertically
1514 g->offset2_y = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1515 parameter[GFX_ARG_2ND_OFFSET] : g->height);
1516 else // frames are ordered horizontally
1517 g->offset2_x = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1518 parameter[GFX_ARG_2ND_OFFSET] : g->width);
1520 // optionally, the x and y offset of 2nd graphic can be specified directly
1521 if (parameter[GFX_ARG_2ND_XOFFSET] != ARG_UNDEFINED_VALUE)
1522 g->offset2_x = parameter[GFX_ARG_2ND_XOFFSET];
1523 if (parameter[GFX_ARG_2ND_YOFFSET] != ARG_UNDEFINED_VALUE)
1524 g->offset2_y = parameter[GFX_ARG_2ND_YOFFSET];
1526 // optionally, the second movement tile can be specified as start tile
1527 if (parameter[GFX_ARG_2ND_SWAP_TILES] != ARG_UNDEFINED_VALUE)
1528 g->swap_double_tiles= parameter[GFX_ARG_2ND_SWAP_TILES];
1530 // automatically determine correct number of frames, if not defined
1531 if (parameter[GFX_ARG_FRAMES] != ARG_UNDEFINED_VALUE)
1532 g->anim_frames = parameter[GFX_ARG_FRAMES];
1533 else if (parameter[GFX_ARG_XPOS] == 0 && !parameter[GFX_ARG_VERTICAL])
1534 g->anim_frames = anim_frames_per_row;
1535 else if (parameter[GFX_ARG_YPOS] == 0 && parameter[GFX_ARG_VERTICAL])
1536 g->anim_frames = anim_frames_per_col;
1540 if (g->anim_frames < 1) // frames must be at least 1
1543 g->anim_frames_per_line =
1544 (parameter[GFX_ARG_FRAMES_PER_LINE] != ARG_UNDEFINED_VALUE ?
1545 parameter[GFX_ARG_FRAMES_PER_LINE] : anim_frames_per_line);
1547 g->anim_delay = parameter[GFX_ARG_DELAY];
1548 if (g->anim_delay < 1) // delay must be at least 1
1551 g->anim_mode = parameter[GFX_ARG_ANIM_MODE];
1553 // automatically determine correct start frame, if not defined
1554 if (parameter[GFX_ARG_START_FRAME] == ARG_UNDEFINED_VALUE)
1555 g->anim_start_frame = 0;
1556 else if (g->anim_mode & ANIM_REVERSE)
1557 g->anim_start_frame = g->anim_frames - parameter[GFX_ARG_START_FRAME] - 1;
1559 g->anim_start_frame = parameter[GFX_ARG_START_FRAME];
1561 // animation synchronized with global frame counter, not move position
1562 g->anim_global_sync = parameter[GFX_ARG_GLOBAL_SYNC];
1564 // animation synchronized with global anim frame counter, not move position
1565 g->anim_global_anim_sync = parameter[GFX_ARG_GLOBAL_ANIM_SYNC];
1567 // optional element for cloning crumble graphics
1568 if (parameter[GFX_ARG_CRUMBLED_LIKE] != ARG_UNDEFINED_VALUE)
1569 g->crumbled_like = parameter[GFX_ARG_CRUMBLED_LIKE];
1571 // optional element for cloning digging graphics
1572 if (parameter[GFX_ARG_DIGGABLE_LIKE] != ARG_UNDEFINED_VALUE)
1573 g->diggable_like = parameter[GFX_ARG_DIGGABLE_LIKE];
1575 // optional border size for "crumbling" diggable graphics
1576 if (parameter[GFX_ARG_BORDER_SIZE] != ARG_UNDEFINED_VALUE)
1577 g->border_size = parameter[GFX_ARG_BORDER_SIZE];
1579 // used for global animations and player "boring" and "sleeping" actions
1580 if (parameter[GFX_ARG_INIT_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1581 g->init_delay_fixed = parameter[GFX_ARG_INIT_DELAY_FIXED];
1582 if (parameter[GFX_ARG_INIT_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1583 g->init_delay_random = parameter[GFX_ARG_INIT_DELAY_RANDOM];
1584 if (parameter[GFX_ARG_ANIM_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1585 g->anim_delay_fixed = parameter[GFX_ARG_ANIM_DELAY_FIXED];
1586 if (parameter[GFX_ARG_ANIM_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1587 g->anim_delay_random = parameter[GFX_ARG_ANIM_DELAY_RANDOM];
1588 if (parameter[GFX_ARG_POST_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1589 g->post_delay_fixed = parameter[GFX_ARG_POST_DELAY_FIXED];
1590 if (parameter[GFX_ARG_POST_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1591 g->post_delay_random = parameter[GFX_ARG_POST_DELAY_RANDOM];
1593 // used for global animations
1594 if (parameter[GFX_ARG_INIT_EVENT] != ARG_UNDEFINED_VALUE)
1595 g->init_event = parameter[GFX_ARG_INIT_EVENT];
1596 if (parameter[GFX_ARG_ANIM_EVENT] != ARG_UNDEFINED_VALUE)
1597 g->anim_event = parameter[GFX_ARG_ANIM_EVENT];
1598 if (parameter[GFX_ARG_INIT_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1599 g->init_event_action = parameter[GFX_ARG_INIT_EVENT_ACTION];
1600 if (parameter[GFX_ARG_ANIM_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1601 g->anim_event_action = parameter[GFX_ARG_ANIM_EVENT_ACTION];
1602 if (parameter[GFX_ARG_INIT_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1603 g->init_delay_action = parameter[GFX_ARG_INIT_DELAY_ACTION];
1604 if (parameter[GFX_ARG_ANIM_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1605 g->anim_delay_action = parameter[GFX_ARG_ANIM_DELAY_ACTION];
1606 if (parameter[GFX_ARG_POST_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1607 g->post_delay_action = parameter[GFX_ARG_POST_DELAY_ACTION];
1609 // used for toon animations and global animations
1610 g->step_offset = parameter[GFX_ARG_STEP_OFFSET];
1611 g->step_xoffset = parameter[GFX_ARG_STEP_XOFFSET];
1612 g->step_yoffset = parameter[GFX_ARG_STEP_YOFFSET];
1613 g->step_delay = parameter[GFX_ARG_STEP_DELAY];
1614 g->direction = parameter[GFX_ARG_DIRECTION];
1615 g->position = parameter[GFX_ARG_POSITION];
1616 g->x = parameter[GFX_ARG_X]; // (may be uninitialized,
1617 g->y = parameter[GFX_ARG_Y]; // unlike src_x and src_y)
1619 if (g->step_delay < 1) // delay must be at least 1
1622 // this is only used for drawing font characters
1623 g->draw_xoffset = parameter[GFX_ARG_DRAW_XOFFSET];
1624 g->draw_yoffset = parameter[GFX_ARG_DRAW_YOFFSET];
1626 // use a different default value for global animations and toons
1627 if ((graphic >= IMG_GFX_GLOBAL_ANIM_1 && graphic <= IMG_GFX_GLOBAL_ANIM_8) ||
1628 (graphic >= IMG_TOON_1 && graphic <= IMG_TOON_20))
1629 g->draw_masked = TRUE;
1631 // this is used for drawing envelopes, global animations and toons
1632 if (parameter[GFX_ARG_DRAW_MASKED] != ARG_UNDEFINED_VALUE)
1633 g->draw_masked = parameter[GFX_ARG_DRAW_MASKED];
1635 // used for toon animations and global animations
1636 if (parameter[GFX_ARG_DRAW_ORDER] != ARG_UNDEFINED_VALUE)
1637 g->draw_order = parameter[GFX_ARG_DRAW_ORDER];
1639 // optional graphic for cloning all graphics settings
1640 if (parameter[GFX_ARG_CLONE_FROM] != ARG_UNDEFINED_VALUE)
1641 g->clone_from = parameter[GFX_ARG_CLONE_FROM];
1643 // optional settings for drawing title screens and title messages
1644 if (parameter[GFX_ARG_FADE_MODE] != ARG_UNDEFINED_VALUE)
1645 g->fade_mode = parameter[GFX_ARG_FADE_MODE];
1646 if (parameter[GFX_ARG_FADE_DELAY] != ARG_UNDEFINED_VALUE)
1647 g->fade_delay = parameter[GFX_ARG_FADE_DELAY];
1648 if (parameter[GFX_ARG_POST_DELAY] != ARG_UNDEFINED_VALUE)
1649 g->post_delay = parameter[GFX_ARG_POST_DELAY];
1650 if (parameter[GFX_ARG_AUTO_DELAY] != ARG_UNDEFINED_VALUE)
1651 g->auto_delay = parameter[GFX_ARG_AUTO_DELAY];
1652 if (parameter[GFX_ARG_AUTO_DELAY_UNIT] != ARG_UNDEFINED_VALUE)
1653 g->auto_delay_unit = parameter[GFX_ARG_AUTO_DELAY_UNIT];
1654 if (parameter[GFX_ARG_ALIGN] != ARG_UNDEFINED_VALUE)
1655 g->align = parameter[GFX_ARG_ALIGN];
1656 if (parameter[GFX_ARG_VALIGN] != ARG_UNDEFINED_VALUE)
1657 g->valign = parameter[GFX_ARG_VALIGN];
1658 if (parameter[GFX_ARG_SORT_PRIORITY] != ARG_UNDEFINED_VALUE)
1659 g->sort_priority = parameter[GFX_ARG_SORT_PRIORITY];
1661 if (parameter[GFX_ARG_CLASS] != ARG_UNDEFINED_VALUE)
1662 g->class = parameter[GFX_ARG_CLASS];
1663 if (parameter[GFX_ARG_STYLE] != ARG_UNDEFINED_VALUE)
1664 g->style = parameter[GFX_ARG_STYLE];
1666 // this is only used for drawing menu buttons and text
1667 g->active_xoffset = parameter[GFX_ARG_ACTIVE_XOFFSET];
1668 g->active_yoffset = parameter[GFX_ARG_ACTIVE_YOFFSET];
1669 g->pressed_xoffset = parameter[GFX_ARG_PRESSED_XOFFSET];
1670 g->pressed_yoffset = parameter[GFX_ARG_PRESSED_YOFFSET];
1673 static void set_graphic_parameters(int graphic)
1675 struct FileInfo *image = getImageListEntryFromImageID(graphic);
1676 char **parameter_raw = image->parameter;
1677 Bitmap **src_bitmaps = getBitmapsFromImageID(graphic);
1678 int parameter[NUM_GFX_ARGS];
1681 // if fallback to default artwork is done, also use the default parameters
1682 if (image->fallback_to_default)
1683 parameter_raw = image->default_parameter;
1685 // get integer values from string parameters
1686 for (i = 0; i < NUM_GFX_ARGS; i++)
1687 parameter[i] = get_graphic_parameter_value(parameter_raw[i],
1688 image_config_suffix[i].token,
1689 image_config_suffix[i].type);
1691 set_graphic_parameters_ext(graphic, parameter, src_bitmaps);
1693 UPDATE_BUSY_STATE();
1696 static void set_cloned_graphic_parameters(int graphic)
1698 int fallback_graphic = IMG_CHAR_EXCLAM;
1699 int max_num_images = getImageListSize();
1700 int clone_graphic = graphic_info[graphic].clone_from;
1701 int num_references_followed = 1;
1703 while (graphic_info[clone_graphic].clone_from != -1 &&
1704 num_references_followed < max_num_images)
1706 clone_graphic = graphic_info[clone_graphic].clone_from;
1708 num_references_followed++;
1711 if (num_references_followed >= max_num_images)
1714 Warn("error found in config file:");
1715 Warn("- config file: '%s'", getImageConfigFilename());
1716 Warn("- config token: '%s'", getTokenFromImageID(graphic));
1717 Warn("error: loop discovered when resolving cloned graphics");
1718 Warn("custom graphic rejected for this element/action");
1720 if (graphic == fallback_graphic)
1721 Fail("no fallback graphic available");
1723 Warn("fallback done to 'char_exclam' for this graphic");
1726 graphic_info[graphic] = graphic_info[fallback_graphic];
1730 graphic_info[graphic] = graphic_info[clone_graphic];
1731 graphic_info[graphic].clone_from = clone_graphic;
1735 static void InitGraphicInfo(void)
1737 int fallback_graphic = IMG_CHAR_EXCLAM;
1738 int num_images = getImageListSize();
1741 // use image size as default values for width and height for these images
1742 static int full_size_graphics[] =
1745 IMG_GLOBAL_BORDER_MAIN,
1746 IMG_GLOBAL_BORDER_SCORES,
1747 IMG_GLOBAL_BORDER_EDITOR,
1748 IMG_GLOBAL_BORDER_PLAYING,
1751 IMG_BACKGROUND_ENVELOPE_1,
1752 IMG_BACKGROUND_ENVELOPE_2,
1753 IMG_BACKGROUND_ENVELOPE_3,
1754 IMG_BACKGROUND_ENVELOPE_4,
1755 IMG_BACKGROUND_REQUEST,
1758 IMG_BACKGROUND_LOADING_INITIAL,
1759 IMG_BACKGROUND_LOADING,
1760 IMG_BACKGROUND_TITLE_INITIAL,
1761 IMG_BACKGROUND_TITLE,
1762 IMG_BACKGROUND_MAIN,
1763 IMG_BACKGROUND_NAMES,
1764 IMG_BACKGROUND_LEVELS,
1765 IMG_BACKGROUND_LEVELNR,
1766 IMG_BACKGROUND_SCORES,
1767 IMG_BACKGROUND_SCOREINFO,
1768 IMG_BACKGROUND_EDITOR,
1769 IMG_BACKGROUND_INFO,
1770 IMG_BACKGROUND_INFO_ELEMENTS,
1771 IMG_BACKGROUND_INFO_MUSIC,
1772 IMG_BACKGROUND_INFO_CREDITS,
1773 IMG_BACKGROUND_INFO_PROGRAM,
1774 IMG_BACKGROUND_INFO_VERSION,
1775 IMG_BACKGROUND_INFO_LEVELSET,
1776 IMG_BACKGROUND_SETUP,
1777 IMG_BACKGROUND_PLAYING,
1778 IMG_BACKGROUND_DOOR,
1779 IMG_BACKGROUND_TAPE,
1780 IMG_BACKGROUND_PANEL,
1781 IMG_BACKGROUND_PALETTE,
1782 IMG_BACKGROUND_TOOLBOX,
1784 IMG_TITLESCREEN_INITIAL_1,
1785 IMG_TITLESCREEN_INITIAL_2,
1786 IMG_TITLESCREEN_INITIAL_3,
1787 IMG_TITLESCREEN_INITIAL_4,
1788 IMG_TITLESCREEN_INITIAL_5,
1795 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_1,
1796 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_2,
1797 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_3,
1798 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_4,
1799 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_5,
1800 IMG_BACKGROUND_TITLEMESSAGE_1,
1801 IMG_BACKGROUND_TITLEMESSAGE_2,
1802 IMG_BACKGROUND_TITLEMESSAGE_3,
1803 IMG_BACKGROUND_TITLEMESSAGE_4,
1804 IMG_BACKGROUND_TITLEMESSAGE_5,
1809 FreeGlobalAnimEventInfo();
1811 checked_free(graphic_info);
1813 graphic_info = checked_calloc(num_images * sizeof(struct GraphicInfo));
1815 // initialize "use_image_size" flag with default value
1816 for (i = 0; i < num_images; i++)
1817 graphic_info[i].use_image_size = FALSE;
1819 // initialize "use_image_size" flag from static configuration above
1820 for (i = 0; full_size_graphics[i] != -1; i++)
1821 graphic_info[full_size_graphics[i]].use_image_size = TRUE;
1823 // first set all graphic paramaters ...
1824 for (i = 0; i < num_images; i++)
1825 set_graphic_parameters(i);
1827 // ... then copy these parameters for cloned graphics
1828 for (i = 0; i < num_images; i++)
1829 if (graphic_info[i].clone_from != -1)
1830 set_cloned_graphic_parameters(i);
1832 for (i = 0; i < num_images; i++)
1834 Bitmap *src_bitmap = graphic_info[i].bitmap;
1838 int src_bitmap_width, src_bitmap_height;
1840 // now check if no animation frames are outside of the loaded image
1842 if (graphic_info[i].bitmap == NULL)
1843 continue; // skip check for optional images that are undefined
1845 // get image size (this can differ from the standard element tile size!)
1846 width = graphic_info[i].width;
1847 height = graphic_info[i].height;
1849 // get final bitmap size (with scaling, but without small images)
1850 src_bitmap_width = graphic_info[i].src_image_width;
1851 src_bitmap_height = graphic_info[i].src_image_height;
1853 // check if first animation frame is inside specified bitmap
1855 // do not use getGraphicSourceXY() here to get position of first frame;
1856 // this avoids calculating wrong start position for out-of-bounds frame
1857 src_x = graphic_info[i].src_x;
1858 src_y = graphic_info[i].src_y;
1860 if (program.headless)
1863 if (src_x < 0 || src_y < 0 ||
1864 src_x + width > src_bitmap_width ||
1865 src_y + height > src_bitmap_height)
1868 Warn("error found in config file:");
1869 Warn("- config file: '%s'", getImageConfigFilename());
1870 Warn("- config token: '%s'", getTokenFromImageID(i));
1871 Warn("- image file: '%s'", src_bitmap->source_filename);
1872 Warn("- frame size: %d, %d", width, height);
1873 Warn("error: first animation frame out of bounds (%d, %d) [%d, %d]",
1874 src_x, src_y, src_bitmap_width, src_bitmap_height);
1875 Warn("custom graphic rejected for this element/action");
1877 if (i == fallback_graphic)
1878 Fail("no fallback graphic available");
1880 Warn("fallback done to 'char_exclam' for this graphic");
1883 graphic_info[i] = graphic_info[fallback_graphic];
1885 // if first frame out of bounds, do not check last frame anymore
1889 // check if last animation frame is inside specified bitmap
1891 last_frame = graphic_info[i].anim_frames - 1;
1892 getGraphicSourceXY(i, last_frame, &src_x, &src_y, FALSE);
1894 if (src_x < 0 || src_y < 0 ||
1895 src_x + width > src_bitmap_width ||
1896 src_y + height > src_bitmap_height)
1899 Warn("error found in config file:");
1900 Warn("- config file: '%s'", getImageConfigFilename());
1901 Warn("- config token: '%s'", getTokenFromImageID(i));
1902 Warn("- image file: '%s'", src_bitmap->source_filename);
1903 Warn("- frame size: %d, %d", width, height);
1904 Warn("error: last animation frame (%d) out of bounds (%d, %d) [%d, %d]",
1905 last_frame, src_x, src_y, src_bitmap_width, src_bitmap_height);
1906 Warn("custom graphic rejected for this element/action");
1908 if (i == fallback_graphic)
1909 Fail("no fallback graphic available");
1911 Warn("fallback done to 'char_exclam' for this graphic");
1914 graphic_info[i] = graphic_info[fallback_graphic];
1919 static void InitGraphicCompatibilityInfo(void)
1921 struct FileInfo *fi_global_door =
1922 getImageListEntryFromImageID(IMG_GLOBAL_DOOR);
1923 int num_images = getImageListSize();
1926 /* the following compatibility handling is needed for the following case:
1927 versions up to 3.3.0.0 used one large bitmap "global.door" for various
1928 graphics mainly used for door and panel graphics, like editor, tape and
1929 in-game buttons with hard-coded bitmap positions and button sizes; as
1930 these graphics now have individual definitions, redefining "global.door"
1931 to change all these graphics at once like before does not work anymore
1932 (because all those individual definitions still have their default values);
1933 to solve this, remap all those individual definitions that are not
1934 redefined to the new bitmap of "global.door" if it was redefined */
1936 // special compatibility handling if image "global.door" was redefined
1937 if (fi_global_door->redefined)
1939 for (i = 0; i < num_images; i++)
1941 struct FileInfo *fi = getImageListEntryFromImageID(i);
1943 // process only those images that still use the default settings
1946 // process all images which default to same image as "global.door"
1947 if (strEqual(fi->default_filename, fi_global_door->default_filename))
1949 // skip all images that are cloned from images that default to same
1950 // image as "global.door", but that are redefined to something else
1951 if (graphic_info[i].clone_from != -1)
1953 int cloned_graphic = graphic_info[i].clone_from;
1955 if (getImageListEntryFromImageID(cloned_graphic)->redefined)
1960 Debug("init:InitGraphicCompatibilityInfo",
1961 "special treatment needed for token '%s'", fi->token);
1964 graphic_info[i].bitmaps = graphic_info[IMG_GLOBAL_DOOR].bitmaps;
1965 graphic_info[i].bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
1971 // special compatibility handling for "Snake Bite" graphics set
1972 if (strPrefix(leveldir_current->identifier, "snake_bite"))
1974 Bitmap *bitmap = graphic_info[IMG_BACKGROUND_SCORES].bitmap;
1976 BlitBitmap(bitmap, bitmap, 18, 66, 32, 480, 50, 66);
1977 BlitBitmap(bitmap, bitmap, 466, 66, 32, 480, 434, 66);
1979 ClearRectangle(bitmap, 2, 66, 32, 480);
1980 ClearRectangle(bitmap, 514, 66, 32, 480);
1983 // special compatibility handling for "Jue" graphics sets (2007 and 2019)
1984 boolean supports_score_info = (menu.draw_xoffset[GAME_MODE_SCOREINFO] != 0);
1985 if (strPrefix(artwork.gfx_current_identifier, "jue") && !supports_score_info)
2003 int mode_old = GAME_MODE_SCORES;
2004 int mode_new = GAME_MODE_SCOREINFO;
2007 // adjust title screens on score info page
2008 for (i = 0; font_title[i] != -1; i++)
2010 struct FontInfo *fi = &font_info[font_title[i]];
2012 fi->special_graphic[mode_new] = fi->special_graphic[mode_old];
2013 fi->special_bitmap_id[mode_new] = fi->special_bitmap_id[mode_old];
2016 // adjust vertical text and button positions on scores page
2017 for (i = 0; font_text[i] != -1; i++)
2019 for (j = 0; j < 2; j++)
2021 boolean jue0 = strEqual(artwork.gfx_current_identifier, "jue0");
2022 int font_nr = (j == 0 ? font_text[i] : FONT_ACTIVE(font_text[i]));
2023 int font_bitmap_id = font_info[font_nr].special_bitmap_id[mode_old];
2024 int font_yoffset = (jue0 ? 10 : 5);
2026 gfx.font_bitmap_info[font_bitmap_id].draw_yoffset = font_yoffset;
2030 // adjust page offsets on score info page
2031 menu.draw_xoffset[mode_new] = menu.draw_xoffset[mode_old];
2032 menu.draw_yoffset[mode_new] = menu.draw_yoffset[mode_old];
2035 InitGraphicCompatibilityInfo_Doors();
2038 static void InitElementSoundInfo(void)
2040 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
2041 int num_property_mappings = getSoundListPropertyMappingSize();
2044 // set values to -1 to identify later as "uninitialized" values
2045 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2046 for (act = 0; act < NUM_ACTIONS; act++)
2047 element_info[i].sound[act] = -1;
2049 // initialize element/sound mapping from static configuration
2050 for (i = 0; element_to_sound[i].element > -1; i++)
2052 int element = element_to_sound[i].element;
2053 int action = element_to_sound[i].action;
2054 int sound = element_to_sound[i].sound;
2055 boolean is_class = element_to_sound[i].is_class;
2058 action = ACTION_DEFAULT;
2061 element_info[element].sound[action] = sound;
2063 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2064 if (strEqual(element_info[j].class_name,
2065 element_info[element].class_name))
2066 element_info[j].sound[action] = sound;
2069 // initialize element class/sound mapping from dynamic configuration
2070 for (i = 0; i < num_property_mappings; i++)
2072 int element_class = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
2073 int action = property_mapping[i].ext1_index;
2074 int sound = property_mapping[i].artwork_index;
2076 if (element_class < 0 || element_class >= MAX_NUM_ELEMENTS)
2080 action = ACTION_DEFAULT;
2082 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2083 if (strEqual(element_info[j].class_name,
2084 element_info[element_class].class_name))
2085 element_info[j].sound[action] = sound;
2088 // initialize element/sound mapping from dynamic configuration
2089 for (i = 0; i < num_property_mappings; i++)
2091 int element = property_mapping[i].base_index;
2092 int action = property_mapping[i].ext1_index;
2093 int sound = property_mapping[i].artwork_index;
2095 if (element >= MAX_NUM_ELEMENTS)
2099 action = ACTION_DEFAULT;
2101 element_info[element].sound[action] = sound;
2104 // now set all '-1' values to element specific default values
2105 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2107 for (act = 0; act < NUM_ACTIONS; act++)
2109 // generic default action sound (defined by "[default]" directive)
2110 int default_action_sound = element_info[EL_DEFAULT].sound[act];
2112 // look for special default action sound (classic game specific)
2113 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].sound[act] != -1)
2114 default_action_sound = element_info[EL_BD_DEFAULT].sound[act];
2115 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].sound[act] != -1)
2116 default_action_sound = element_info[EL_SP_DEFAULT].sound[act];
2117 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].sound[act] != -1)
2118 default_action_sound = element_info[EL_SB_DEFAULT].sound[act];
2119 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].sound[act] != -1)
2120 default_action_sound = element_info[EL_MM_DEFAULT].sound[act];
2122 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
2123 // !!! make this better !!!
2124 if (i == EL_EMPTY_SPACE)
2125 default_action_sound = element_info[EL_DEFAULT].sound[act];
2127 // no sound for this specific action -- use default action sound
2128 if (element_info[i].sound[act] == -1)
2129 element_info[i].sound[act] = default_action_sound;
2133 // copy sound settings to some elements that are only stored in level file
2134 // in native R'n'D levels, but are used by game engine in native EM levels
2135 for (i = 0; copy_properties[i][0] != -1; i++)
2136 for (j = 1; j <= 4; j++)
2137 for (act = 0; act < NUM_ACTIONS; act++)
2138 element_info[copy_properties[i][j]].sound[act] =
2139 element_info[copy_properties[i][0]].sound[act];
2142 static void InitGameModeSoundInfo(void)
2146 // set values to -1 to identify later as "uninitialized" values
2147 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2150 // initialize gamemode/sound mapping from static configuration
2151 for (i = 0; gamemode_to_sound[i].sound > -1; i++)
2153 int gamemode = gamemode_to_sound[i].gamemode;
2154 int sound = gamemode_to_sound[i].sound;
2157 gamemode = GAME_MODE_DEFAULT;
2159 menu.sound[gamemode] = sound;
2162 // now set all '-1' values to levelset specific default values
2163 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2164 if (menu.sound[i] == -1)
2165 menu.sound[i] = menu.sound[GAME_MODE_DEFAULT];
2168 static void set_sound_parameters(int sound, char **parameter_raw)
2170 int parameter[NUM_SND_ARGS];
2173 // get integer values from string parameters
2174 for (i = 0; i < NUM_SND_ARGS; i++)
2176 get_parameter_value(parameter_raw[i],
2177 sound_config_suffix[i].token,
2178 sound_config_suffix[i].type);
2180 // explicit loop mode setting in configuration overrides default value
2181 if (parameter[SND_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2182 sound_info[sound].loop = parameter[SND_ARG_MODE_LOOP];
2184 // sound volume to change the original volume when loading the sound file
2185 sound_info[sound].volume = parameter[SND_ARG_VOLUME];
2187 // sound priority to give certain sounds a higher or lower priority
2188 sound_info[sound].priority = parameter[SND_ARG_PRIORITY];
2191 static void InitSoundInfo(void)
2193 int *sound_effect_properties;
2194 int num_sounds = getSoundListSize();
2197 checked_free(sound_info);
2199 sound_effect_properties = checked_calloc(num_sounds * sizeof(int));
2200 sound_info = checked_calloc(num_sounds * sizeof(struct SoundInfo));
2202 // initialize sound effect for all elements to "no sound"
2203 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2204 for (j = 0; j < NUM_ACTIONS; j++)
2205 element_info[i].sound[j] = SND_UNDEFINED;
2207 for (i = 0; i < num_sounds; i++)
2209 struct FileInfo *sound = getSoundListEntry(i);
2210 int len_effect_text = strlen(sound->token);
2212 sound_effect_properties[i] = ACTION_OTHER;
2213 sound_info[i].loop = FALSE; // default: play sound only once
2215 // determine all loop sounds and identify certain sound classes
2217 for (j = 0; element_action_info[j].suffix; j++)
2219 int len_action_text = strlen(element_action_info[j].suffix);
2221 if (len_action_text < len_effect_text &&
2222 strEqual(&sound->token[len_effect_text - len_action_text],
2223 element_action_info[j].suffix))
2225 sound_effect_properties[i] = element_action_info[j].value;
2226 sound_info[i].loop = element_action_info[j].is_loop_sound;
2232 // associate elements and some selected sound actions
2234 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2236 if (element_info[j].class_name)
2238 int len_class_text = strlen(element_info[j].class_name);
2240 if (len_class_text + 1 < len_effect_text &&
2241 strncmp(sound->token,
2242 element_info[j].class_name, len_class_text) == 0 &&
2243 sound->token[len_class_text] == '.')
2245 int sound_action_value = sound_effect_properties[i];
2247 element_info[j].sound[sound_action_value] = i;
2252 set_sound_parameters(i, sound->parameter);
2255 free(sound_effect_properties);
2258 static void InitGameModeMusicInfo(void)
2260 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
2261 int num_property_mappings = getMusicListPropertyMappingSize();
2262 int default_levelset_music = -1;
2265 // set values to -1 to identify later as "uninitialized" values
2266 for (i = 0; i < MAX_LEVELS; i++)
2267 levelset.music[i] = -1;
2268 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2271 // initialize gamemode/music mapping from static configuration
2272 for (i = 0; gamemode_to_music[i].music > -1; i++)
2274 int gamemode = gamemode_to_music[i].gamemode;
2275 int music = gamemode_to_music[i].music;
2278 gamemode = GAME_MODE_DEFAULT;
2280 menu.music[gamemode] = music;
2283 // initialize gamemode/music mapping from dynamic configuration
2284 for (i = 0; i < num_property_mappings; i++)
2286 int prefix = property_mapping[i].base_index;
2287 int gamemode = property_mapping[i].ext2_index;
2288 int level = property_mapping[i].ext3_index;
2289 int music = property_mapping[i].artwork_index;
2291 if (prefix < 0 || prefix >= NUM_MUSIC_PREFIXES)
2295 gamemode = GAME_MODE_DEFAULT;
2297 // level specific music only allowed for in-game music
2298 if (level != -1 && gamemode == GAME_MODE_DEFAULT)
2299 gamemode = GAME_MODE_PLAYING;
2304 default_levelset_music = music;
2307 if (gamemode == GAME_MODE_PLAYING || gamemode == GAME_MODE_DEFAULT)
2308 levelset.music[level] = music;
2309 if (gamemode != GAME_MODE_PLAYING)
2310 menu.music[gamemode] = music;
2313 // now set all '-1' values to menu specific default values
2314 // (undefined values of "levelset.music[]" might stay at "-1" to
2315 // allow dynamic selection of music files from music directory!)
2316 for (i = 0; i < MAX_LEVELS; i++)
2317 if (levelset.music[i] == -1)
2318 levelset.music[i] = default_levelset_music;
2319 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2320 if (menu.music[i] == -1)
2321 menu.music[i] = menu.music[GAME_MODE_DEFAULT];
2324 static void set_music_parameters(int music, char **parameter_raw)
2326 int parameter[NUM_MUS_ARGS];
2329 // get integer values from string parameters
2330 for (i = 0; i < NUM_MUS_ARGS; i++)
2332 get_parameter_value(parameter_raw[i],
2333 music_config_suffix[i].token,
2334 music_config_suffix[i].type);
2336 // explicit loop mode setting in configuration overrides default value
2337 if (parameter[MUS_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2338 music_info[music].loop = parameter[MUS_ARG_MODE_LOOP];
2341 static void InitMusicInfo(void)
2343 int num_music = getMusicListSize();
2346 checked_free(music_info);
2348 music_info = checked_calloc(num_music * sizeof(struct MusicInfo));
2350 for (i = 0; i < num_music; i++)
2352 struct FileInfo *music = getMusicListEntry(i);
2353 int len_music_text = strlen(music->token);
2355 music_info[i].loop = TRUE; // default: play music in loop mode
2357 // determine all loop music
2359 for (j = 0; music_prefix_info[j].prefix; j++)
2361 int len_prefix_text = strlen(music_prefix_info[j].prefix);
2363 if (len_prefix_text < len_music_text &&
2364 strncmp(music->token,
2365 music_prefix_info[j].prefix, len_prefix_text) == 0)
2367 music_info[i].loop = music_prefix_info[j].is_loop_music;
2373 set_music_parameters(i, music->parameter);
2378 static void InitGameInfoFromArtworkInfo(void)
2380 // special case: store initial value of custom artwork setting
2381 game.use_masked_elements_initial = game.use_masked_elements;
2384 static void ReinitializeGraphics(void)
2386 print_timestamp_init("ReinitializeGraphics");
2388 InitGfxTileSizeInfo(game.tile_size, TILESIZE);
2390 InitGraphicInfo(); // graphic properties mapping
2391 print_timestamp_time("InitGraphicInfo");
2392 InitElementGraphicInfo(); // element game graphic mapping
2393 print_timestamp_time("InitElementGraphicInfo");
2394 InitElementSpecialGraphicInfo(); // element special graphic mapping
2395 print_timestamp_time("InitElementSpecialGraphicInfo");
2397 InitElementSmallImages(); // scale elements to all needed sizes
2398 print_timestamp_time("InitElementSmallImages");
2399 InitScaledImages(); // scale all other images, if needed
2400 print_timestamp_time("InitScaledImages");
2401 InitBitmapPointers(); // set standard size bitmap pointers
2402 print_timestamp_time("InitBitmapPointers");
2403 InitFontGraphicInfo(); // initialize text drawing functions
2404 print_timestamp_time("InitFontGraphicInfo");
2405 InitGlobalAnimGraphicInfo(); // initialize global animation config
2406 print_timestamp_time("InitGlobalAnimGraphicInfo");
2408 InitImageTextures(); // create textures for certain images
2409 print_timestamp_time("InitImageTextures");
2411 InitGraphicInfo_EM(); // graphic mapping for EM engine
2412 print_timestamp_time("InitGraphicInfo_EM");
2414 InitGraphicCompatibilityInfo();
2415 print_timestamp_time("InitGraphicCompatibilityInfo");
2418 print_timestamp_time("InitGadgets");
2420 print_timestamp_time("InitDoors");
2422 InitGameInfoFromArtworkInfo();
2424 print_timestamp_done("ReinitializeGraphics");
2427 static void ReinitializeSounds(void)
2429 InitSoundInfo(); // sound properties mapping
2430 InitElementSoundInfo(); // element game sound mapping
2431 InitGameModeSoundInfo(); // game mode sound mapping
2432 InitGlobalAnimSoundInfo(); // global animation sound settings
2434 InitPlayLevelSound(); // internal game sound settings
2437 static void ReinitializeMusic(void)
2439 InitMusicInfo(); // music properties mapping
2440 InitGameModeMusicInfo(); // game mode music mapping
2441 InitGlobalAnimMusicInfo(); // global animation music settings
2444 static int get_special_property_bit(int element, int property_bit_nr)
2446 struct PropertyBitInfo
2452 static struct PropertyBitInfo pb_can_move_into_acid[] =
2454 // the player may be able fall into acid when gravity is activated
2459 { EL_SP_MURPHY, 0 },
2460 { EL_SOKOBAN_FIELD_PLAYER, 0 },
2462 // all elements that can move may be able to also move into acid
2465 { EL_BUG_RIGHT, 1 },
2468 { EL_SPACESHIP, 2 },
2469 { EL_SPACESHIP_LEFT, 2 },
2470 { EL_SPACESHIP_RIGHT, 2 },
2471 { EL_SPACESHIP_UP, 2 },
2472 { EL_SPACESHIP_DOWN, 2 },
2473 { EL_BD_BUTTERFLY, 3 },
2474 { EL_BD_BUTTERFLY_LEFT, 3 },
2475 { EL_BD_BUTTERFLY_RIGHT, 3 },
2476 { EL_BD_BUTTERFLY_UP, 3 },
2477 { EL_BD_BUTTERFLY_DOWN, 3 },
2478 { EL_BD_FIREFLY, 4 },
2479 { EL_BD_FIREFLY_LEFT, 4 },
2480 { EL_BD_FIREFLY_RIGHT, 4 },
2481 { EL_BD_FIREFLY_UP, 4 },
2482 { EL_BD_FIREFLY_DOWN, 4 },
2484 { EL_YAMYAM_LEFT, 5 },
2485 { EL_YAMYAM_RIGHT, 5 },
2486 { EL_YAMYAM_UP, 5 },
2487 { EL_YAMYAM_DOWN, 5 },
2488 { EL_DARK_YAMYAM, 6 },
2491 { EL_PACMAN_LEFT, 8 },
2492 { EL_PACMAN_RIGHT, 8 },
2493 { EL_PACMAN_UP, 8 },
2494 { EL_PACMAN_DOWN, 8 },
2496 { EL_MOLE_LEFT, 9 },
2497 { EL_MOLE_RIGHT, 9 },
2499 { EL_MOLE_DOWN, 9 },
2503 { EL_SATELLITE, 13 },
2504 { EL_SP_SNIKSNAK, 14 },
2505 { EL_SP_ELECTRON, 15 },
2508 { EL_SPRING_LEFT, 17 },
2509 { EL_SPRING_RIGHT, 17 },
2510 { EL_EMC_ANDROID, 18 },
2515 static struct PropertyBitInfo pb_dont_collide_with[] =
2517 { EL_SP_SNIKSNAK, 0 },
2518 { EL_SP_ELECTRON, 1 },
2526 struct PropertyBitInfo *pb_info;
2529 { EP_CAN_MOVE_INTO_ACID, pb_can_move_into_acid },
2530 { EP_DONT_COLLIDE_WITH, pb_dont_collide_with },
2535 struct PropertyBitInfo *pb_info = NULL;
2538 for (i = 0; pb_definition[i].bit_nr != -1; i++)
2539 if (pb_definition[i].bit_nr == property_bit_nr)
2540 pb_info = pb_definition[i].pb_info;
2542 if (pb_info == NULL)
2545 for (i = 0; pb_info[i].element != -1; i++)
2546 if (pb_info[i].element == element)
2547 return pb_info[i].bit_nr;
2552 void setBitfieldProperty(int *bitfield, int property_bit_nr, int element,
2553 boolean property_value)
2555 int bit_nr = get_special_property_bit(element, property_bit_nr);
2560 *bitfield |= (1 << bit_nr);
2562 *bitfield &= ~(1 << bit_nr);
2566 boolean getBitfieldProperty(int *bitfield, int property_bit_nr, int element)
2568 int bit_nr = get_special_property_bit(element, property_bit_nr);
2571 return ((*bitfield & (1 << bit_nr)) != 0);
2576 static void ResolveGroupElementExt(int group_element, int recursion_depth)
2578 static int group_nr;
2579 static struct ElementGroupInfo *group;
2580 struct ElementGroupInfo *actual_group = element_info[group_element].group;
2583 if (actual_group == NULL) // not yet initialized
2586 if (recursion_depth > NUM_GROUP_ELEMENTS) // recursion too deep
2588 Warn("recursion too deep when resolving group element %d",
2589 group_element - EL_GROUP_START + 1);
2591 // replace element which caused too deep recursion by question mark
2592 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
2597 if (recursion_depth == 0) // initialization
2599 group = actual_group;
2600 group_nr = GROUP_NR(group_element);
2602 group->num_elements_resolved = 0;
2603 group->choice_pos = 0;
2605 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2606 element_info[i].in_group[group_nr] = FALSE;
2609 for (i = 0; i < actual_group->num_elements; i++)
2611 int element = actual_group->element[i];
2613 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
2616 if (IS_GROUP_ELEMENT(element))
2617 ResolveGroupElementExt(element, recursion_depth + 1);
2620 group->element_resolved[group->num_elements_resolved++] = element;
2621 element_info[element].in_group[group_nr] = TRUE;
2626 void ResolveGroupElement(int group_element)
2628 ResolveGroupElementExt(group_element, 0);
2631 void InitElementPropertiesStatic(void)
2633 static boolean clipboard_elements_initialized = FALSE;
2635 static int ep_diggable[] =
2640 EL_SP_BUGGY_BASE_ACTIVATING,
2643 EL_INVISIBLE_SAND_ACTIVE,
2646 // !!! currently not diggable, but handled by 'ep_dont_run_into' !!!
2647 // (if amoeba can grow into anything diggable, maybe keep these out)
2652 EL_SP_BUGGY_BASE_ACTIVE,
2659 static int ep_collectible_only[] =
2681 EL_DYNABOMB_INCREASE_NUMBER,
2682 EL_DYNABOMB_INCREASE_SIZE,
2683 EL_DYNABOMB_INCREASE_POWER,
2701 // !!! handle separately !!!
2702 EL_DC_LANDMINE, // deadly when running into, but can be snapped
2708 static int ep_dont_run_into[] =
2710 // same elements as in 'ep_dont_touch'
2716 // same elements as in 'ep_dont_collide_with'
2728 // !!! maybe this should better be handled by 'ep_diggable' !!!
2733 EL_SP_BUGGY_BASE_ACTIVE,
2740 static int ep_dont_collide_with[] =
2742 // same elements as in 'ep_dont_touch'
2759 static int ep_dont_touch[] =
2769 static int ep_indestructible[] =
2773 EL_ACID_POOL_TOPLEFT,
2774 EL_ACID_POOL_TOPRIGHT,
2775 EL_ACID_POOL_BOTTOMLEFT,
2776 EL_ACID_POOL_BOTTOM,
2777 EL_ACID_POOL_BOTTOMRIGHT,
2778 EL_SP_HARDWARE_GRAY,
2779 EL_SP_HARDWARE_GREEN,
2780 EL_SP_HARDWARE_BLUE,
2782 EL_SP_HARDWARE_YELLOW,
2783 EL_SP_HARDWARE_BASE_1,
2784 EL_SP_HARDWARE_BASE_2,
2785 EL_SP_HARDWARE_BASE_3,
2786 EL_SP_HARDWARE_BASE_4,
2787 EL_SP_HARDWARE_BASE_5,
2788 EL_SP_HARDWARE_BASE_6,
2789 EL_INVISIBLE_STEELWALL,
2790 EL_INVISIBLE_STEELWALL_ACTIVE,
2791 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2792 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
2793 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
2794 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2795 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
2796 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
2797 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2798 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
2799 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
2800 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
2801 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
2802 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
2804 EL_LIGHT_SWITCH_ACTIVE,
2805 EL_SIGN_EXCLAMATION,
2806 EL_SIGN_RADIOACTIVITY,
2813 EL_SIGN_ENTRY_FORBIDDEN,
2814 EL_SIGN_EMERGENCY_EXIT,
2822 EL_STEEL_EXIT_CLOSED,
2824 EL_STEEL_EXIT_OPENING,
2825 EL_STEEL_EXIT_CLOSING,
2826 EL_EM_STEEL_EXIT_CLOSED,
2827 EL_EM_STEEL_EXIT_OPEN,
2828 EL_EM_STEEL_EXIT_OPENING,
2829 EL_EM_STEEL_EXIT_CLOSING,
2830 EL_DC_STEELWALL_1_LEFT,
2831 EL_DC_STEELWALL_1_RIGHT,
2832 EL_DC_STEELWALL_1_TOP,
2833 EL_DC_STEELWALL_1_BOTTOM,
2834 EL_DC_STEELWALL_1_HORIZONTAL,
2835 EL_DC_STEELWALL_1_VERTICAL,
2836 EL_DC_STEELWALL_1_TOPLEFT,
2837 EL_DC_STEELWALL_1_TOPRIGHT,
2838 EL_DC_STEELWALL_1_BOTTOMLEFT,
2839 EL_DC_STEELWALL_1_BOTTOMRIGHT,
2840 EL_DC_STEELWALL_1_TOPLEFT_2,
2841 EL_DC_STEELWALL_1_TOPRIGHT_2,
2842 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
2843 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
2844 EL_DC_STEELWALL_2_LEFT,
2845 EL_DC_STEELWALL_2_RIGHT,
2846 EL_DC_STEELWALL_2_TOP,
2847 EL_DC_STEELWALL_2_BOTTOM,
2848 EL_DC_STEELWALL_2_HORIZONTAL,
2849 EL_DC_STEELWALL_2_VERTICAL,
2850 EL_DC_STEELWALL_2_MIDDLE,
2851 EL_DC_STEELWALL_2_SINGLE,
2852 EL_STEELWALL_SLIPPERY,
2866 EL_GATE_1_GRAY_ACTIVE,
2867 EL_GATE_2_GRAY_ACTIVE,
2868 EL_GATE_3_GRAY_ACTIVE,
2869 EL_GATE_4_GRAY_ACTIVE,
2878 EL_EM_GATE_1_GRAY_ACTIVE,
2879 EL_EM_GATE_2_GRAY_ACTIVE,
2880 EL_EM_GATE_3_GRAY_ACTIVE,
2881 EL_EM_GATE_4_GRAY_ACTIVE,
2890 EL_EMC_GATE_5_GRAY_ACTIVE,
2891 EL_EMC_GATE_6_GRAY_ACTIVE,
2892 EL_EMC_GATE_7_GRAY_ACTIVE,
2893 EL_EMC_GATE_8_GRAY_ACTIVE,
2895 EL_DC_GATE_WHITE_GRAY,
2896 EL_DC_GATE_WHITE_GRAY_ACTIVE,
2897 EL_DC_GATE_FAKE_GRAY,
2899 EL_SWITCHGATE_OPENING,
2900 EL_SWITCHGATE_CLOSED,
2901 EL_SWITCHGATE_CLOSING,
2902 EL_DC_SWITCHGATE_SWITCH_UP,
2903 EL_DC_SWITCHGATE_SWITCH_DOWN,
2905 EL_TIMEGATE_OPENING,
2907 EL_TIMEGATE_CLOSING,
2908 EL_DC_TIMEGATE_SWITCH,
2909 EL_DC_TIMEGATE_SWITCH_ACTIVE,
2913 EL_TUBE_VERTICAL_LEFT,
2914 EL_TUBE_VERTICAL_RIGHT,
2915 EL_TUBE_HORIZONTAL_UP,
2916 EL_TUBE_HORIZONTAL_DOWN,
2921 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
2922 EL_EXPANDABLE_STEELWALL_VERTICAL,
2923 EL_EXPANDABLE_STEELWALL_ANY,
2928 static int ep_slippery[] =
2942 EL_ROBOT_WHEEL_ACTIVE,
2948 EL_ACID_POOL_TOPLEFT,
2949 EL_ACID_POOL_TOPRIGHT,
2959 EL_STEELWALL_SLIPPERY,
2962 EL_EMC_WALL_SLIPPERY_1,
2963 EL_EMC_WALL_SLIPPERY_2,
2964 EL_EMC_WALL_SLIPPERY_3,
2965 EL_EMC_WALL_SLIPPERY_4,
2967 EL_EMC_MAGIC_BALL_ACTIVE,
2972 static int ep_can_change[] =
2977 static int ep_can_move[] =
2979 // same elements as in 'pb_can_move_into_acid'
3002 static int ep_can_fall[] =
3017 EL_QUICKSAND_FAST_FULL,
3019 EL_BD_MAGIC_WALL_FULL,
3020 EL_DC_MAGIC_WALL_FULL,
3034 static int ep_can_smash_player[] =
3060 static int ep_can_smash_enemies[] =
3069 static int ep_can_smash_everything[] =
3078 static int ep_explodes_by_fire[] =
3080 // same elements as in 'ep_explodes_impact'
3085 // same elements as in 'ep_explodes_smashed'
3095 EL_EM_DYNAMITE_ACTIVE,
3096 EL_DYNABOMB_PLAYER_1_ACTIVE,
3097 EL_DYNABOMB_PLAYER_2_ACTIVE,
3098 EL_DYNABOMB_PLAYER_3_ACTIVE,
3099 EL_DYNABOMB_PLAYER_4_ACTIVE,
3100 EL_DYNABOMB_INCREASE_NUMBER,
3101 EL_DYNABOMB_INCREASE_SIZE,
3102 EL_DYNABOMB_INCREASE_POWER,
3103 EL_SP_DISK_RED_ACTIVE,
3117 static int ep_explodes_smashed[] =
3119 // same elements as in 'ep_explodes_impact'
3133 static int ep_explodes_impact[] =
3142 static int ep_walkable_over[] =
3162 EL_SOKOBAN_FIELD_EMPTY,
3169 EL_EM_STEEL_EXIT_OPEN,
3170 EL_EM_STEEL_EXIT_OPENING,
3179 EL_GATE_1_GRAY_ACTIVE,
3180 EL_GATE_2_GRAY_ACTIVE,
3181 EL_GATE_3_GRAY_ACTIVE,
3182 EL_GATE_4_GRAY_ACTIVE,
3190 static int ep_walkable_inside[] =
3195 EL_TUBE_VERTICAL_LEFT,
3196 EL_TUBE_VERTICAL_RIGHT,
3197 EL_TUBE_HORIZONTAL_UP,
3198 EL_TUBE_HORIZONTAL_DOWN,
3207 static int ep_walkable_under[] =
3212 static int ep_passable_over[] =
3222 EL_EM_GATE_1_GRAY_ACTIVE,
3223 EL_EM_GATE_2_GRAY_ACTIVE,
3224 EL_EM_GATE_3_GRAY_ACTIVE,
3225 EL_EM_GATE_4_GRAY_ACTIVE,
3234 EL_EMC_GATE_5_GRAY_ACTIVE,
3235 EL_EMC_GATE_6_GRAY_ACTIVE,
3236 EL_EMC_GATE_7_GRAY_ACTIVE,
3237 EL_EMC_GATE_8_GRAY_ACTIVE,
3239 EL_DC_GATE_WHITE_GRAY,
3240 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3247 static int ep_passable_inside[] =
3253 EL_SP_PORT_HORIZONTAL,
3254 EL_SP_PORT_VERTICAL,
3256 EL_SP_GRAVITY_PORT_LEFT,
3257 EL_SP_GRAVITY_PORT_RIGHT,
3258 EL_SP_GRAVITY_PORT_UP,
3259 EL_SP_GRAVITY_PORT_DOWN,
3260 EL_SP_GRAVITY_ON_PORT_LEFT,
3261 EL_SP_GRAVITY_ON_PORT_RIGHT,
3262 EL_SP_GRAVITY_ON_PORT_UP,
3263 EL_SP_GRAVITY_ON_PORT_DOWN,
3264 EL_SP_GRAVITY_OFF_PORT_LEFT,
3265 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3266 EL_SP_GRAVITY_OFF_PORT_UP,
3267 EL_SP_GRAVITY_OFF_PORT_DOWN,
3272 static int ep_passable_under[] =
3277 static int ep_droppable[] =
3282 static int ep_explodes_1x1_old[] =
3287 static int ep_pushable[] =
3299 EL_SOKOBAN_FIELD_FULL,
3308 static int ep_explodes_cross_old[] =
3313 static int ep_protected[] =
3315 // same elements as in 'ep_walkable_inside'
3319 EL_TUBE_VERTICAL_LEFT,
3320 EL_TUBE_VERTICAL_RIGHT,
3321 EL_TUBE_HORIZONTAL_UP,
3322 EL_TUBE_HORIZONTAL_DOWN,
3328 // same elements as in 'ep_passable_over'
3337 EL_EM_GATE_1_GRAY_ACTIVE,
3338 EL_EM_GATE_2_GRAY_ACTIVE,
3339 EL_EM_GATE_3_GRAY_ACTIVE,
3340 EL_EM_GATE_4_GRAY_ACTIVE,
3349 EL_EMC_GATE_5_GRAY_ACTIVE,
3350 EL_EMC_GATE_6_GRAY_ACTIVE,
3351 EL_EMC_GATE_7_GRAY_ACTIVE,
3352 EL_EMC_GATE_8_GRAY_ACTIVE,
3354 EL_DC_GATE_WHITE_GRAY,
3355 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3359 // same elements as in 'ep_passable_inside'
3364 EL_SP_PORT_HORIZONTAL,
3365 EL_SP_PORT_VERTICAL,
3367 EL_SP_GRAVITY_PORT_LEFT,
3368 EL_SP_GRAVITY_PORT_RIGHT,
3369 EL_SP_GRAVITY_PORT_UP,
3370 EL_SP_GRAVITY_PORT_DOWN,
3371 EL_SP_GRAVITY_ON_PORT_LEFT,
3372 EL_SP_GRAVITY_ON_PORT_RIGHT,
3373 EL_SP_GRAVITY_ON_PORT_UP,
3374 EL_SP_GRAVITY_ON_PORT_DOWN,
3375 EL_SP_GRAVITY_OFF_PORT_LEFT,
3376 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3377 EL_SP_GRAVITY_OFF_PORT_UP,
3378 EL_SP_GRAVITY_OFF_PORT_DOWN,
3383 static int ep_throwable[] =
3388 static int ep_can_explode[] =
3390 // same elements as in 'ep_explodes_impact'
3395 // same elements as in 'ep_explodes_smashed'
3401 // elements that can explode by explosion or by dragonfire
3405 EL_EM_DYNAMITE_ACTIVE,
3406 EL_DYNABOMB_PLAYER_1_ACTIVE,
3407 EL_DYNABOMB_PLAYER_2_ACTIVE,
3408 EL_DYNABOMB_PLAYER_3_ACTIVE,
3409 EL_DYNABOMB_PLAYER_4_ACTIVE,
3410 EL_DYNABOMB_INCREASE_NUMBER,
3411 EL_DYNABOMB_INCREASE_SIZE,
3412 EL_DYNABOMB_INCREASE_POWER,
3413 EL_SP_DISK_RED_ACTIVE,
3421 // elements that can explode only by explosion
3427 static int ep_gravity_reachable[] =
3433 EL_INVISIBLE_SAND_ACTIVE,
3438 EL_SP_PORT_HORIZONTAL,
3439 EL_SP_PORT_VERTICAL,
3441 EL_SP_GRAVITY_PORT_LEFT,
3442 EL_SP_GRAVITY_PORT_RIGHT,
3443 EL_SP_GRAVITY_PORT_UP,
3444 EL_SP_GRAVITY_PORT_DOWN,
3445 EL_SP_GRAVITY_ON_PORT_LEFT,
3446 EL_SP_GRAVITY_ON_PORT_RIGHT,
3447 EL_SP_GRAVITY_ON_PORT_UP,
3448 EL_SP_GRAVITY_ON_PORT_DOWN,
3449 EL_SP_GRAVITY_OFF_PORT_LEFT,
3450 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3451 EL_SP_GRAVITY_OFF_PORT_UP,
3452 EL_SP_GRAVITY_OFF_PORT_DOWN,
3458 static int ep_empty_space[] =
3481 static int ep_player[] =
3488 EL_SOKOBAN_FIELD_PLAYER,
3494 static int ep_can_pass_magic_wall[] =
3508 static int ep_can_pass_dc_magic_wall[] =
3524 static int ep_switchable[] =
3528 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3529 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3530 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3531 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3532 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3533 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3534 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3535 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3536 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3537 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3538 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3539 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3540 EL_SWITCHGATE_SWITCH_UP,
3541 EL_SWITCHGATE_SWITCH_DOWN,
3542 EL_DC_SWITCHGATE_SWITCH_UP,
3543 EL_DC_SWITCHGATE_SWITCH_DOWN,
3545 EL_LIGHT_SWITCH_ACTIVE,
3547 EL_DC_TIMEGATE_SWITCH,
3548 EL_BALLOON_SWITCH_LEFT,
3549 EL_BALLOON_SWITCH_RIGHT,
3550 EL_BALLOON_SWITCH_UP,
3551 EL_BALLOON_SWITCH_DOWN,
3552 EL_BALLOON_SWITCH_ANY,
3553 EL_BALLOON_SWITCH_NONE,
3556 EL_EMC_MAGIC_BALL_SWITCH,
3557 EL_EMC_MAGIC_BALL_SWITCH_ACTIVE,
3562 static int ep_bd_element[] =
3596 static int ep_sp_element[] =
3598 // should always be valid
3601 // standard classic Supaplex elements
3608 EL_SP_HARDWARE_GRAY,
3616 EL_SP_GRAVITY_PORT_RIGHT,
3617 EL_SP_GRAVITY_PORT_DOWN,
3618 EL_SP_GRAVITY_PORT_LEFT,
3619 EL_SP_GRAVITY_PORT_UP,
3624 EL_SP_PORT_VERTICAL,
3625 EL_SP_PORT_HORIZONTAL,
3631 EL_SP_HARDWARE_BASE_1,
3632 EL_SP_HARDWARE_GREEN,
3633 EL_SP_HARDWARE_BLUE,
3635 EL_SP_HARDWARE_YELLOW,
3636 EL_SP_HARDWARE_BASE_2,
3637 EL_SP_HARDWARE_BASE_3,
3638 EL_SP_HARDWARE_BASE_4,
3639 EL_SP_HARDWARE_BASE_5,
3640 EL_SP_HARDWARE_BASE_6,
3644 // additional elements that appeared in newer Supaplex levels
3647 // additional gravity port elements (not switching, but setting gravity)
3648 EL_SP_GRAVITY_ON_PORT_LEFT,
3649 EL_SP_GRAVITY_ON_PORT_RIGHT,
3650 EL_SP_GRAVITY_ON_PORT_UP,
3651 EL_SP_GRAVITY_ON_PORT_DOWN,
3652 EL_SP_GRAVITY_OFF_PORT_LEFT,
3653 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3654 EL_SP_GRAVITY_OFF_PORT_UP,
3655 EL_SP_GRAVITY_OFF_PORT_DOWN,
3657 // more than one Murphy in a level results in an inactive clone
3660 // runtime Supaplex elements
3661 EL_SP_DISK_RED_ACTIVE,
3662 EL_SP_TERMINAL_ACTIVE,
3663 EL_SP_BUGGY_BASE_ACTIVATING,
3664 EL_SP_BUGGY_BASE_ACTIVE,
3671 static int ep_sb_element[] =
3676 EL_SOKOBAN_FIELD_EMPTY,
3677 EL_SOKOBAN_FIELD_FULL,
3678 EL_SOKOBAN_FIELD_PLAYER,
3683 EL_INVISIBLE_STEELWALL,
3688 static int ep_gem[] =
3700 static int ep_food_dark_yamyam[] =
3728 static int ep_food_penguin[] =
3742 static int ep_food_pig[] =
3754 static int ep_historic_wall[] =
3765 EL_GATE_1_GRAY_ACTIVE,
3766 EL_GATE_2_GRAY_ACTIVE,
3767 EL_GATE_3_GRAY_ACTIVE,
3768 EL_GATE_4_GRAY_ACTIVE,
3777 EL_EM_GATE_1_GRAY_ACTIVE,
3778 EL_EM_GATE_2_GRAY_ACTIVE,
3779 EL_EM_GATE_3_GRAY_ACTIVE,
3780 EL_EM_GATE_4_GRAY_ACTIVE,
3787 EL_EXPANDABLE_WALL_HORIZONTAL,
3788 EL_EXPANDABLE_WALL_VERTICAL,
3789 EL_EXPANDABLE_WALL_ANY,
3790 EL_EXPANDABLE_WALL_GROWING,
3791 EL_BD_EXPANDABLE_WALL,
3798 EL_SP_HARDWARE_GRAY,
3799 EL_SP_HARDWARE_GREEN,
3800 EL_SP_HARDWARE_BLUE,
3802 EL_SP_HARDWARE_YELLOW,
3803 EL_SP_HARDWARE_BASE_1,
3804 EL_SP_HARDWARE_BASE_2,
3805 EL_SP_HARDWARE_BASE_3,
3806 EL_SP_HARDWARE_BASE_4,
3807 EL_SP_HARDWARE_BASE_5,
3808 EL_SP_HARDWARE_BASE_6,
3810 EL_SP_TERMINAL_ACTIVE,
3813 EL_INVISIBLE_STEELWALL,
3814 EL_INVISIBLE_STEELWALL_ACTIVE,
3816 EL_INVISIBLE_WALL_ACTIVE,
3817 EL_STEELWALL_SLIPPERY,
3834 static int ep_historic_solid[] =
3838 EL_EXPANDABLE_WALL_HORIZONTAL,
3839 EL_EXPANDABLE_WALL_VERTICAL,
3840 EL_EXPANDABLE_WALL_ANY,
3841 EL_BD_EXPANDABLE_WALL,
3854 EL_QUICKSAND_FILLING,
3855 EL_QUICKSAND_EMPTYING,
3857 EL_MAGIC_WALL_ACTIVE,
3858 EL_MAGIC_WALL_EMPTYING,
3859 EL_MAGIC_WALL_FILLING,
3863 EL_BD_MAGIC_WALL_ACTIVE,
3864 EL_BD_MAGIC_WALL_EMPTYING,
3865 EL_BD_MAGIC_WALL_FULL,
3866 EL_BD_MAGIC_WALL_FILLING,
3867 EL_BD_MAGIC_WALL_DEAD,
3876 EL_SP_TERMINAL_ACTIVE,
3880 EL_INVISIBLE_WALL_ACTIVE,
3881 EL_SWITCHGATE_SWITCH_UP,
3882 EL_SWITCHGATE_SWITCH_DOWN,
3884 EL_TIMEGATE_SWITCH_ACTIVE,
3896 // the following elements are a direct copy of "indestructible" elements,
3897 // except "EL_ACID", which is "indestructible", but not "solid"!
3902 EL_ACID_POOL_TOPLEFT,
3903 EL_ACID_POOL_TOPRIGHT,
3904 EL_ACID_POOL_BOTTOMLEFT,
3905 EL_ACID_POOL_BOTTOM,
3906 EL_ACID_POOL_BOTTOMRIGHT,
3907 EL_SP_HARDWARE_GRAY,
3908 EL_SP_HARDWARE_GREEN,
3909 EL_SP_HARDWARE_BLUE,
3911 EL_SP_HARDWARE_YELLOW,
3912 EL_SP_HARDWARE_BASE_1,
3913 EL_SP_HARDWARE_BASE_2,
3914 EL_SP_HARDWARE_BASE_3,
3915 EL_SP_HARDWARE_BASE_4,
3916 EL_SP_HARDWARE_BASE_5,
3917 EL_SP_HARDWARE_BASE_6,
3918 EL_INVISIBLE_STEELWALL,
3919 EL_INVISIBLE_STEELWALL_ACTIVE,
3920 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3921 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3922 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3923 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3924 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3925 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3926 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3927 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3928 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3929 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3930 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3931 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3933 EL_LIGHT_SWITCH_ACTIVE,
3934 EL_SIGN_EXCLAMATION,
3935 EL_SIGN_RADIOACTIVITY,
3942 EL_SIGN_ENTRY_FORBIDDEN,
3943 EL_SIGN_EMERGENCY_EXIT,
3951 EL_STEEL_EXIT_CLOSED,
3953 EL_STEEL_EXIT_OPENING,
3954 EL_STEEL_EXIT_CLOSING,
3955 EL_EM_STEEL_EXIT_CLOSED,
3956 EL_EM_STEEL_EXIT_OPEN,
3957 EL_EM_STEEL_EXIT_OPENING,
3958 EL_EM_STEEL_EXIT_CLOSING,
3959 EL_DC_STEELWALL_1_LEFT,
3960 EL_DC_STEELWALL_1_RIGHT,
3961 EL_DC_STEELWALL_1_TOP,
3962 EL_DC_STEELWALL_1_BOTTOM,
3963 EL_DC_STEELWALL_1_HORIZONTAL,
3964 EL_DC_STEELWALL_1_VERTICAL,
3965 EL_DC_STEELWALL_1_TOPLEFT,
3966 EL_DC_STEELWALL_1_TOPRIGHT,
3967 EL_DC_STEELWALL_1_BOTTOMLEFT,
3968 EL_DC_STEELWALL_1_BOTTOMRIGHT,
3969 EL_DC_STEELWALL_1_TOPLEFT_2,
3970 EL_DC_STEELWALL_1_TOPRIGHT_2,
3971 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
3972 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
3973 EL_DC_STEELWALL_2_LEFT,
3974 EL_DC_STEELWALL_2_RIGHT,
3975 EL_DC_STEELWALL_2_TOP,
3976 EL_DC_STEELWALL_2_BOTTOM,
3977 EL_DC_STEELWALL_2_HORIZONTAL,
3978 EL_DC_STEELWALL_2_VERTICAL,
3979 EL_DC_STEELWALL_2_MIDDLE,
3980 EL_DC_STEELWALL_2_SINGLE,
3981 EL_STEELWALL_SLIPPERY,
3995 EL_GATE_1_GRAY_ACTIVE,
3996 EL_GATE_2_GRAY_ACTIVE,
3997 EL_GATE_3_GRAY_ACTIVE,
3998 EL_GATE_4_GRAY_ACTIVE,
4007 EL_EM_GATE_1_GRAY_ACTIVE,
4008 EL_EM_GATE_2_GRAY_ACTIVE,
4009 EL_EM_GATE_3_GRAY_ACTIVE,
4010 EL_EM_GATE_4_GRAY_ACTIVE,
4019 EL_EMC_GATE_5_GRAY_ACTIVE,
4020 EL_EMC_GATE_6_GRAY_ACTIVE,
4021 EL_EMC_GATE_7_GRAY_ACTIVE,
4022 EL_EMC_GATE_8_GRAY_ACTIVE,
4024 EL_DC_GATE_WHITE_GRAY,
4025 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4026 EL_DC_GATE_FAKE_GRAY,
4028 EL_SWITCHGATE_OPENING,
4029 EL_SWITCHGATE_CLOSED,
4030 EL_SWITCHGATE_CLOSING,
4031 EL_DC_SWITCHGATE_SWITCH_UP,
4032 EL_DC_SWITCHGATE_SWITCH_DOWN,
4034 EL_TIMEGATE_OPENING,
4036 EL_TIMEGATE_CLOSING,
4037 EL_DC_TIMEGATE_SWITCH,
4038 EL_DC_TIMEGATE_SWITCH_ACTIVE,
4042 EL_TUBE_VERTICAL_LEFT,
4043 EL_TUBE_VERTICAL_RIGHT,
4044 EL_TUBE_HORIZONTAL_UP,
4045 EL_TUBE_HORIZONTAL_DOWN,
4050 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
4051 EL_EXPANDABLE_STEELWALL_VERTICAL,
4052 EL_EXPANDABLE_STEELWALL_ANY,
4057 static int ep_classic_enemy[] =
4074 static int ep_belt[] =
4076 EL_CONVEYOR_BELT_1_LEFT,
4077 EL_CONVEYOR_BELT_1_MIDDLE,
4078 EL_CONVEYOR_BELT_1_RIGHT,
4079 EL_CONVEYOR_BELT_2_LEFT,
4080 EL_CONVEYOR_BELT_2_MIDDLE,
4081 EL_CONVEYOR_BELT_2_RIGHT,
4082 EL_CONVEYOR_BELT_3_LEFT,
4083 EL_CONVEYOR_BELT_3_MIDDLE,
4084 EL_CONVEYOR_BELT_3_RIGHT,
4085 EL_CONVEYOR_BELT_4_LEFT,
4086 EL_CONVEYOR_BELT_4_MIDDLE,
4087 EL_CONVEYOR_BELT_4_RIGHT,
4092 static int ep_belt_active[] =
4094 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4095 EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE,
4096 EL_CONVEYOR_BELT_1_RIGHT_ACTIVE,
4097 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4098 EL_CONVEYOR_BELT_2_MIDDLE_ACTIVE,
4099 EL_CONVEYOR_BELT_2_RIGHT_ACTIVE,
4100 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4101 EL_CONVEYOR_BELT_3_MIDDLE_ACTIVE,
4102 EL_CONVEYOR_BELT_3_RIGHT_ACTIVE,
4103 EL_CONVEYOR_BELT_4_LEFT_ACTIVE,
4104 EL_CONVEYOR_BELT_4_MIDDLE_ACTIVE,
4105 EL_CONVEYOR_BELT_4_RIGHT_ACTIVE,
4110 static int ep_belt_switch[] =
4112 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4113 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4114 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4115 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4116 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4117 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4118 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4119 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4120 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4121 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4122 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4123 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4128 static int ep_tube[] =
4135 EL_TUBE_HORIZONTAL_UP,
4136 EL_TUBE_HORIZONTAL_DOWN,
4138 EL_TUBE_VERTICAL_LEFT,
4139 EL_TUBE_VERTICAL_RIGHT,
4145 static int ep_acid_pool[] =
4147 EL_ACID_POOL_TOPLEFT,
4148 EL_ACID_POOL_TOPRIGHT,
4149 EL_ACID_POOL_BOTTOMLEFT,
4150 EL_ACID_POOL_BOTTOM,
4151 EL_ACID_POOL_BOTTOMRIGHT,
4156 static int ep_keygate[] =
4166 EL_GATE_1_GRAY_ACTIVE,
4167 EL_GATE_2_GRAY_ACTIVE,
4168 EL_GATE_3_GRAY_ACTIVE,
4169 EL_GATE_4_GRAY_ACTIVE,
4178 EL_EM_GATE_1_GRAY_ACTIVE,
4179 EL_EM_GATE_2_GRAY_ACTIVE,
4180 EL_EM_GATE_3_GRAY_ACTIVE,
4181 EL_EM_GATE_4_GRAY_ACTIVE,
4190 EL_EMC_GATE_5_GRAY_ACTIVE,
4191 EL_EMC_GATE_6_GRAY_ACTIVE,
4192 EL_EMC_GATE_7_GRAY_ACTIVE,
4193 EL_EMC_GATE_8_GRAY_ACTIVE,
4195 EL_DC_GATE_WHITE_GRAY,
4196 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4201 static int ep_amoeboid[] =
4213 static int ep_amoebalive[] =
4224 static int ep_has_editor_content[] =
4230 EL_SOKOBAN_FIELD_PLAYER,
4248 static int ep_can_turn_each_move[] =
4250 // !!! do something with this one !!!
4254 static int ep_can_grow[] =
4268 static int ep_active_bomb[] =
4271 EL_EM_DYNAMITE_ACTIVE,
4272 EL_DYNABOMB_PLAYER_1_ACTIVE,
4273 EL_DYNABOMB_PLAYER_2_ACTIVE,
4274 EL_DYNABOMB_PLAYER_3_ACTIVE,
4275 EL_DYNABOMB_PLAYER_4_ACTIVE,
4276 EL_SP_DISK_RED_ACTIVE,
4281 static int ep_inactive[] =
4307 EL_QUICKSAND_FAST_EMPTY,
4330 EL_GATE_1_GRAY_ACTIVE,
4331 EL_GATE_2_GRAY_ACTIVE,
4332 EL_GATE_3_GRAY_ACTIVE,
4333 EL_GATE_4_GRAY_ACTIVE,
4342 EL_EM_GATE_1_GRAY_ACTIVE,
4343 EL_EM_GATE_2_GRAY_ACTIVE,
4344 EL_EM_GATE_3_GRAY_ACTIVE,
4345 EL_EM_GATE_4_GRAY_ACTIVE,
4354 EL_EMC_GATE_5_GRAY_ACTIVE,
4355 EL_EMC_GATE_6_GRAY_ACTIVE,
4356 EL_EMC_GATE_7_GRAY_ACTIVE,
4357 EL_EMC_GATE_8_GRAY_ACTIVE,
4359 EL_DC_GATE_WHITE_GRAY,
4360 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4361 EL_DC_GATE_FAKE_GRAY,
4364 EL_INVISIBLE_STEELWALL,
4372 EL_WALL_EMERALD_YELLOW,
4373 EL_DYNABOMB_INCREASE_NUMBER,
4374 EL_DYNABOMB_INCREASE_SIZE,
4375 EL_DYNABOMB_INCREASE_POWER,
4379 EL_SOKOBAN_FIELD_EMPTY,
4380 EL_SOKOBAN_FIELD_FULL,
4381 EL_WALL_EMERALD_RED,
4382 EL_WALL_EMERALD_PURPLE,
4383 EL_ACID_POOL_TOPLEFT,
4384 EL_ACID_POOL_TOPRIGHT,
4385 EL_ACID_POOL_BOTTOMLEFT,
4386 EL_ACID_POOL_BOTTOM,
4387 EL_ACID_POOL_BOTTOMRIGHT,
4391 EL_BD_MAGIC_WALL_DEAD,
4393 EL_DC_MAGIC_WALL_DEAD,
4394 EL_AMOEBA_TO_DIAMOND,
4402 EL_SP_GRAVITY_PORT_RIGHT,
4403 EL_SP_GRAVITY_PORT_DOWN,
4404 EL_SP_GRAVITY_PORT_LEFT,
4405 EL_SP_GRAVITY_PORT_UP,
4406 EL_SP_PORT_HORIZONTAL,
4407 EL_SP_PORT_VERTICAL,
4418 EL_SP_HARDWARE_GRAY,
4419 EL_SP_HARDWARE_GREEN,
4420 EL_SP_HARDWARE_BLUE,
4422 EL_SP_HARDWARE_YELLOW,
4423 EL_SP_HARDWARE_BASE_1,
4424 EL_SP_HARDWARE_BASE_2,
4425 EL_SP_HARDWARE_BASE_3,
4426 EL_SP_HARDWARE_BASE_4,
4427 EL_SP_HARDWARE_BASE_5,
4428 EL_SP_HARDWARE_BASE_6,
4429 EL_SP_GRAVITY_ON_PORT_LEFT,
4430 EL_SP_GRAVITY_ON_PORT_RIGHT,
4431 EL_SP_GRAVITY_ON_PORT_UP,
4432 EL_SP_GRAVITY_ON_PORT_DOWN,
4433 EL_SP_GRAVITY_OFF_PORT_LEFT,
4434 EL_SP_GRAVITY_OFF_PORT_RIGHT,
4435 EL_SP_GRAVITY_OFF_PORT_UP,
4436 EL_SP_GRAVITY_OFF_PORT_DOWN,
4437 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4438 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4439 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4440 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4441 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4442 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4443 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4444 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4445 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4446 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4447 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4448 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4449 EL_SIGN_EXCLAMATION,
4450 EL_SIGN_RADIOACTIVITY,
4457 EL_SIGN_ENTRY_FORBIDDEN,
4458 EL_SIGN_EMERGENCY_EXIT,
4466 EL_DC_STEELWALL_1_LEFT,
4467 EL_DC_STEELWALL_1_RIGHT,
4468 EL_DC_STEELWALL_1_TOP,
4469 EL_DC_STEELWALL_1_BOTTOM,
4470 EL_DC_STEELWALL_1_HORIZONTAL,
4471 EL_DC_STEELWALL_1_VERTICAL,
4472 EL_DC_STEELWALL_1_TOPLEFT,
4473 EL_DC_STEELWALL_1_TOPRIGHT,
4474 EL_DC_STEELWALL_1_BOTTOMLEFT,
4475 EL_DC_STEELWALL_1_BOTTOMRIGHT,
4476 EL_DC_STEELWALL_1_TOPLEFT_2,
4477 EL_DC_STEELWALL_1_TOPRIGHT_2,
4478 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
4479 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
4480 EL_DC_STEELWALL_2_LEFT,
4481 EL_DC_STEELWALL_2_RIGHT,
4482 EL_DC_STEELWALL_2_TOP,
4483 EL_DC_STEELWALL_2_BOTTOM,
4484 EL_DC_STEELWALL_2_HORIZONTAL,
4485 EL_DC_STEELWALL_2_VERTICAL,
4486 EL_DC_STEELWALL_2_MIDDLE,
4487 EL_DC_STEELWALL_2_SINGLE,
4488 EL_STEELWALL_SLIPPERY,
4493 EL_EMC_WALL_SLIPPERY_1,
4494 EL_EMC_WALL_SLIPPERY_2,
4495 EL_EMC_WALL_SLIPPERY_3,
4496 EL_EMC_WALL_SLIPPERY_4,
4517 static int ep_em_slippery_wall[] =
4522 static int ep_gfx_crumbled[] =
4533 static int ep_editor_cascade_active[] =
4535 EL_INTERNAL_CASCADE_BD_ACTIVE,
4536 EL_INTERNAL_CASCADE_EM_ACTIVE,
4537 EL_INTERNAL_CASCADE_EMC_ACTIVE,
4538 EL_INTERNAL_CASCADE_RND_ACTIVE,
4539 EL_INTERNAL_CASCADE_SB_ACTIVE,
4540 EL_INTERNAL_CASCADE_SP_ACTIVE,
4541 EL_INTERNAL_CASCADE_DC_ACTIVE,
4542 EL_INTERNAL_CASCADE_DX_ACTIVE,
4543 EL_INTERNAL_CASCADE_MM_ACTIVE,
4544 EL_INTERNAL_CASCADE_DF_ACTIVE,
4545 EL_INTERNAL_CASCADE_CHARS_ACTIVE,
4546 EL_INTERNAL_CASCADE_STEEL_CHARS_ACTIVE,
4547 EL_INTERNAL_CASCADE_CE_ACTIVE,
4548 EL_INTERNAL_CASCADE_GE_ACTIVE,
4549 EL_INTERNAL_CASCADE_ES_ACTIVE,
4550 EL_INTERNAL_CASCADE_REF_ACTIVE,
4551 EL_INTERNAL_CASCADE_USER_ACTIVE,
4552 EL_INTERNAL_CASCADE_DYNAMIC_ACTIVE,
4557 static int ep_editor_cascade_inactive[] =
4559 EL_INTERNAL_CASCADE_BD,
4560 EL_INTERNAL_CASCADE_EM,
4561 EL_INTERNAL_CASCADE_EMC,
4562 EL_INTERNAL_CASCADE_RND,
4563 EL_INTERNAL_CASCADE_SB,
4564 EL_INTERNAL_CASCADE_SP,
4565 EL_INTERNAL_CASCADE_DC,
4566 EL_INTERNAL_CASCADE_DX,
4567 EL_INTERNAL_CASCADE_MM,
4568 EL_INTERNAL_CASCADE_DF,
4569 EL_INTERNAL_CASCADE_CHARS,
4570 EL_INTERNAL_CASCADE_STEEL_CHARS,
4571 EL_INTERNAL_CASCADE_CE,
4572 EL_INTERNAL_CASCADE_GE,
4573 EL_INTERNAL_CASCADE_ES,
4574 EL_INTERNAL_CASCADE_REF,
4575 EL_INTERNAL_CASCADE_USER,
4576 EL_INTERNAL_CASCADE_DYNAMIC,
4581 static int ep_obsolete[] =
4585 EL_EM_KEY_1_FILE_OBSOLETE,
4586 EL_EM_KEY_2_FILE_OBSOLETE,
4587 EL_EM_KEY_3_FILE_OBSOLETE,
4588 EL_EM_KEY_4_FILE_OBSOLETE,
4589 EL_ENVELOPE_OBSOLETE,
4598 } element_properties[] =
4600 { ep_diggable, EP_DIGGABLE },
4601 { ep_collectible_only, EP_COLLECTIBLE_ONLY },
4602 { ep_dont_run_into, EP_DONT_RUN_INTO },
4603 { ep_dont_collide_with, EP_DONT_COLLIDE_WITH },
4604 { ep_dont_touch, EP_DONT_TOUCH },
4605 { ep_indestructible, EP_INDESTRUCTIBLE },
4606 { ep_slippery, EP_SLIPPERY },
4607 { ep_can_change, EP_CAN_CHANGE },
4608 { ep_can_move, EP_CAN_MOVE },
4609 { ep_can_fall, EP_CAN_FALL },
4610 { ep_can_smash_player, EP_CAN_SMASH_PLAYER },
4611 { ep_can_smash_enemies, EP_CAN_SMASH_ENEMIES },
4612 { ep_can_smash_everything, EP_CAN_SMASH_EVERYTHING },
4613 { ep_explodes_by_fire, EP_EXPLODES_BY_FIRE },
4614 { ep_explodes_smashed, EP_EXPLODES_SMASHED },
4615 { ep_explodes_impact, EP_EXPLODES_IMPACT },
4616 { ep_walkable_over, EP_WALKABLE_OVER },
4617 { ep_walkable_inside, EP_WALKABLE_INSIDE },
4618 { ep_walkable_under, EP_WALKABLE_UNDER },
4619 { ep_passable_over, EP_PASSABLE_OVER },
4620 { ep_passable_inside, EP_PASSABLE_INSIDE },
4621 { ep_passable_under, EP_PASSABLE_UNDER },
4622 { ep_droppable, EP_DROPPABLE },
4623 { ep_explodes_1x1_old, EP_EXPLODES_1X1_OLD },
4624 { ep_pushable, EP_PUSHABLE },
4625 { ep_explodes_cross_old, EP_EXPLODES_CROSS_OLD },
4626 { ep_protected, EP_PROTECTED },
4627 { ep_throwable, EP_THROWABLE },
4628 { ep_can_explode, EP_CAN_EXPLODE },
4629 { ep_gravity_reachable, EP_GRAVITY_REACHABLE },
4631 { ep_empty_space, EP_EMPTY_SPACE },
4632 { ep_player, EP_PLAYER },
4633 { ep_can_pass_magic_wall, EP_CAN_PASS_MAGIC_WALL },
4634 { ep_can_pass_dc_magic_wall, EP_CAN_PASS_DC_MAGIC_WALL },
4635 { ep_switchable, EP_SWITCHABLE },
4636 { ep_bd_element, EP_BD_ELEMENT },
4637 { ep_sp_element, EP_SP_ELEMENT },
4638 { ep_sb_element, EP_SB_ELEMENT },
4640 { ep_food_dark_yamyam, EP_FOOD_DARK_YAMYAM },
4641 { ep_food_penguin, EP_FOOD_PENGUIN },
4642 { ep_food_pig, EP_FOOD_PIG },
4643 { ep_historic_wall, EP_HISTORIC_WALL },
4644 { ep_historic_solid, EP_HISTORIC_SOLID },
4645 { ep_classic_enemy, EP_CLASSIC_ENEMY },
4646 { ep_belt, EP_BELT },
4647 { ep_belt_active, EP_BELT_ACTIVE },
4648 { ep_belt_switch, EP_BELT_SWITCH },
4649 { ep_tube, EP_TUBE },
4650 { ep_acid_pool, EP_ACID_POOL },
4651 { ep_keygate, EP_KEYGATE },
4652 { ep_amoeboid, EP_AMOEBOID },
4653 { ep_amoebalive, EP_AMOEBALIVE },
4654 { ep_has_editor_content, EP_HAS_EDITOR_CONTENT },
4655 { ep_can_turn_each_move, EP_CAN_TURN_EACH_MOVE },
4656 { ep_can_grow, EP_CAN_GROW },
4657 { ep_active_bomb, EP_ACTIVE_BOMB },
4658 { ep_inactive, EP_INACTIVE },
4660 { ep_em_slippery_wall, EP_EM_SLIPPERY_WALL },
4662 { ep_gfx_crumbled, EP_GFX_CRUMBLED },
4664 { ep_editor_cascade_active, EP_EDITOR_CASCADE_ACTIVE },
4665 { ep_editor_cascade_inactive, EP_EDITOR_CASCADE_INACTIVE },
4667 { ep_obsolete, EP_OBSOLETE },
4674 // always start with reliable default values (element has no properties)
4675 // (but never initialize clipboard elements after the very first time)
4676 // (to be able to use clipboard elements between several levels)
4677 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4678 if (!IS_CLIPBOARD_ELEMENT(i) || !clipboard_elements_initialized)
4679 for (j = 0; j < NUM_ELEMENT_PROPERTIES; j++)
4680 SET_PROPERTY(i, j, FALSE);
4682 // set all base element properties from above array definitions
4683 for (i = 0; element_properties[i].elements != NULL; i++)
4684 for (j = 0; (element_properties[i].elements)[j] != -1; j++)
4685 SET_PROPERTY((element_properties[i].elements)[j],
4686 element_properties[i].property, TRUE);
4688 // copy properties to some elements that are only stored in level file
4689 for (i = 0; i < NUM_ELEMENT_PROPERTIES; i++)
4690 for (j = 0; copy_properties[j][0] != -1; j++)
4691 if (HAS_PROPERTY(copy_properties[j][0], i))
4692 for (k = 1; k <= 4; k++)
4693 SET_PROPERTY(copy_properties[j][k], i, TRUE);
4695 // set static element properties that are not listed in array definitions
4696 for (i = EL_STEEL_CHAR_START; i <= EL_STEEL_CHAR_END; i++)
4697 SET_PROPERTY(i, EP_INDESTRUCTIBLE, TRUE);
4699 clipboard_elements_initialized = TRUE;
4702 void InitElementPropertiesEngine(int engine_version)
4704 static int no_wall_properties[] =
4707 EP_COLLECTIBLE_ONLY,
4709 EP_DONT_COLLIDE_WITH,
4712 EP_CAN_SMASH_PLAYER,
4713 EP_CAN_SMASH_ENEMIES,
4714 EP_CAN_SMASH_EVERYTHING,
4719 EP_FOOD_DARK_YAMYAM,
4735 /* important: after initialization in InitElementPropertiesStatic(), the
4736 elements are not again initialized to a default value; therefore all
4737 changes have to make sure that they leave the element with a defined
4738 property (which means that conditional property changes must be set to
4739 a reliable default value before) */
4741 // resolve group elements
4742 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
4743 ResolveGroupElement(EL_GROUP_START + i);
4745 // set all special, combined or engine dependent element properties
4746 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4748 // do not change (already initialized) clipboard elements here
4749 if (IS_CLIPBOARD_ELEMENT(i))
4752 // ---------- INACTIVE ----------------------------------------------------
4753 SET_PROPERTY(i, EP_INACTIVE, ((i >= EL_CHAR_START &&
4754 i <= EL_CHAR_END) ||
4755 (i >= EL_STEEL_CHAR_START &&
4756 i <= EL_STEEL_CHAR_END)));
4758 // ---------- WALKABLE, PASSABLE, ACCESSIBLE ------------------------------
4759 SET_PROPERTY(i, EP_WALKABLE, (IS_WALKABLE_OVER(i) ||
4760 IS_WALKABLE_INSIDE(i) ||
4761 IS_WALKABLE_UNDER(i)));
4763 SET_PROPERTY(i, EP_PASSABLE, (IS_PASSABLE_OVER(i) ||
4764 IS_PASSABLE_INSIDE(i) ||
4765 IS_PASSABLE_UNDER(i)));
4767 SET_PROPERTY(i, EP_ACCESSIBLE_OVER, (IS_WALKABLE_OVER(i) ||
4768 IS_PASSABLE_OVER(i)));
4770 SET_PROPERTY(i, EP_ACCESSIBLE_INSIDE, (IS_WALKABLE_INSIDE(i) ||
4771 IS_PASSABLE_INSIDE(i)));
4773 SET_PROPERTY(i, EP_ACCESSIBLE_UNDER, (IS_WALKABLE_UNDER(i) ||
4774 IS_PASSABLE_UNDER(i)));
4776 SET_PROPERTY(i, EP_ACCESSIBLE, (IS_WALKABLE(i) ||
4779 // ---------- COLLECTIBLE -------------------------------------------------
4780 SET_PROPERTY(i, EP_COLLECTIBLE, (IS_COLLECTIBLE_ONLY(i) ||
4784 // ---------- SNAPPABLE ---------------------------------------------------
4785 SET_PROPERTY(i, EP_SNAPPABLE, (IS_DIGGABLE(i) ||
4786 IS_COLLECTIBLE(i) ||
4790 // ---------- WALL --------------------------------------------------------
4791 SET_PROPERTY(i, EP_WALL, TRUE); // default: element is wall
4793 for (j = 0; no_wall_properties[j] != -1; j++)
4794 if (HAS_PROPERTY(i, no_wall_properties[j]) ||
4795 i >= EL_FIRST_RUNTIME_UNREAL)
4796 SET_PROPERTY(i, EP_WALL, FALSE);
4798 if (IS_HISTORIC_WALL(i))
4799 SET_PROPERTY(i, EP_WALL, TRUE);
4801 // ---------- SOLID_FOR_PUSHING -------------------------------------------
4802 if (engine_version < VERSION_IDENT(2,2,0,0))
4803 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, IS_HISTORIC_SOLID(i));
4805 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, (!IS_WALKABLE(i) &&
4807 !IS_COLLECTIBLE(i)));
4809 // ---------- DRAGONFIRE_PROOF --------------------------------------------
4810 if (IS_HISTORIC_SOLID(i) || i == EL_EXPLOSION)
4811 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, TRUE);
4813 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, (IS_INDESTRUCTIBLE(i) &&
4816 // ---------- EXPLOSION_PROOF ---------------------------------------------
4818 SET_PROPERTY(i, EP_EXPLOSION_PROOF, TRUE);
4819 else if (engine_version < VERSION_IDENT(2,2,0,0))
4820 SET_PROPERTY(i, EP_EXPLOSION_PROOF, IS_INDESTRUCTIBLE(i));
4822 SET_PROPERTY(i, EP_EXPLOSION_PROOF, (IS_INDESTRUCTIBLE(i) &&
4826 if (IS_CUSTOM_ELEMENT(i))
4828 // these are additional properties which are initially false when set
4830 // ---------- DONT_COLLIDE_WITH / DONT_RUN_INTO -------------------------
4832 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH, TRUE);
4833 if (DONT_COLLIDE_WITH(i))
4834 SET_PROPERTY(i, EP_DONT_RUN_INTO, TRUE);
4836 // ---------- CAN_SMASH_ENEMIES / CAN_SMASH_PLAYER ----------------------
4837 if (CAN_SMASH_EVERYTHING(i))
4838 SET_PROPERTY(i, EP_CAN_SMASH_ENEMIES, TRUE);
4839 if (CAN_SMASH_ENEMIES(i))
4840 SET_PROPERTY(i, EP_CAN_SMASH_PLAYER, TRUE);
4843 // ---------- CAN_SMASH ---------------------------------------------------
4844 SET_PROPERTY(i, EP_CAN_SMASH, (CAN_SMASH_PLAYER(i) ||
4845 CAN_SMASH_ENEMIES(i) ||
4846 CAN_SMASH_EVERYTHING(i)));
4848 // ---------- CAN_EXPLODE_BY_FIRE -----------------------------------------
4849 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_FIRE, (CAN_EXPLODE(i) &&
4850 EXPLODES_BY_FIRE(i)));
4852 // ---------- CAN_EXPLODE_SMASHED -----------------------------------------
4853 SET_PROPERTY(i, EP_CAN_EXPLODE_SMASHED, (CAN_EXPLODE(i) &&
4854 EXPLODES_SMASHED(i)));
4856 // ---------- CAN_EXPLODE_IMPACT ------------------------------------------
4857 SET_PROPERTY(i, EP_CAN_EXPLODE_IMPACT, (CAN_EXPLODE(i) &&
4858 EXPLODES_IMPACT(i)));
4860 // ---------- CAN_EXPLODE_BY_DRAGONFIRE -----------------------------------
4861 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_DRAGONFIRE, CAN_EXPLODE_BY_FIRE(i));
4863 // ---------- CAN_EXPLODE_BY_EXPLOSION ------------------------------------
4864 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_EXPLOSION, (CAN_EXPLODE_BY_FIRE(i) ||
4865 i == EL_BLACK_ORB));
4867 // ---------- COULD_MOVE_INTO_ACID ----------------------------------------
4868 SET_PROPERTY(i, EP_COULD_MOVE_INTO_ACID, (IS_PLAYER_ELEMENT(i) ||
4870 IS_CUSTOM_ELEMENT(i)));
4872 // ---------- MAYBE_DONT_COLLIDE_WITH -------------------------------------
4873 SET_PROPERTY(i, EP_MAYBE_DONT_COLLIDE_WITH, (i == EL_SP_SNIKSNAK ||
4874 i == EL_SP_ELECTRON));
4876 // ---------- CAN_MOVE_INTO_ACID ------------------------------------------
4877 if (COULD_MOVE_INTO_ACID(i) && !IS_CUSTOM_ELEMENT(i))
4878 SET_PROPERTY(i, EP_CAN_MOVE_INTO_ACID,
4879 getMoveIntoAcidProperty(&level, i));
4881 // ---------- DONT_COLLIDE_WITH -------------------------------------------
4882 if (MAYBE_DONT_COLLIDE_WITH(i))
4883 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH,
4884 getDontCollideWithProperty(&level, i));
4886 // ---------- SP_PORT -----------------------------------------------------
4887 SET_PROPERTY(i, EP_SP_PORT, (IS_SP_ELEMENT(i) &&
4888 IS_PASSABLE_INSIDE(i)));
4890 // ---------- CAN_BE_CLONED_BY_ANDROID ------------------------------------
4891 for (j = 0; j < level.num_android_clone_elements; j++)
4892 SET_PROPERTY(i, EP_CAN_BE_CLONED_BY_ANDROID,
4894 IS_EQUAL_OR_IN_GROUP(i, level.android_clone_element[j])));
4896 // ---------- CAN_CHANGE --------------------------------------------------
4897 SET_PROPERTY(i, EP_CAN_CHANGE, FALSE); // default: cannot change
4898 for (j = 0; j < element_info[i].num_change_pages; j++)
4899 if (element_info[i].change_page[j].can_change)
4900 SET_PROPERTY(i, EP_CAN_CHANGE, TRUE);
4902 // ---------- HAS_ACTION --------------------------------------------------
4903 SET_PROPERTY(i, EP_HAS_ACTION, FALSE); // default: has no action
4904 for (j = 0; j < element_info[i].num_change_pages; j++)
4905 if (element_info[i].change_page[j].has_action)
4906 SET_PROPERTY(i, EP_HAS_ACTION, TRUE);
4908 // ---------- CAN_CHANGE_OR_HAS_ACTION ------------------------------------
4909 SET_PROPERTY(i, EP_CAN_CHANGE_OR_HAS_ACTION, (CAN_CHANGE(i) ||
4912 // ---------- GFX_CRUMBLED ------------------------------------------------
4913 SET_PROPERTY(i, EP_GFX_CRUMBLED,
4914 element_info[i].crumbled[ACTION_DEFAULT] !=
4915 element_info[i].graphic[ACTION_DEFAULT]);
4917 // ---------- EDITOR_CASCADE ----------------------------------------------
4918 SET_PROPERTY(i, EP_EDITOR_CASCADE, (IS_EDITOR_CASCADE_ACTIVE(i) ||
4919 IS_EDITOR_CASCADE_INACTIVE(i)));
4922 // dynamically adjust element properties according to game engine version
4924 static int ep_em_slippery_wall[] =
4929 EL_EXPANDABLE_WALL_HORIZONTAL,
4930 EL_EXPANDABLE_WALL_VERTICAL,
4931 EL_EXPANDABLE_WALL_ANY,
4932 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
4933 EL_EXPANDABLE_STEELWALL_VERTICAL,
4934 EL_EXPANDABLE_STEELWALL_ANY,
4935 EL_EXPANDABLE_STEELWALL_GROWING,
4939 static int ep_em_explodes_by_fire[] =
4942 EL_EM_DYNAMITE_ACTIVE,
4947 // special EM style gems behaviour
4948 for (i = 0; ep_em_slippery_wall[i] != -1; i++)
4949 SET_PROPERTY(ep_em_slippery_wall[i], EP_EM_SLIPPERY_WALL,
4950 level.em_slippery_gems);
4952 // "EL_EXPANDABLE_WALL_GROWING" wasn't slippery for EM gems in 2.0.1
4953 SET_PROPERTY(EL_EXPANDABLE_WALL_GROWING, EP_EM_SLIPPERY_WALL,
4954 (level.em_slippery_gems &&
4955 engine_version > VERSION_IDENT(2,0,1,0)));
4957 // special EM style explosion behaviour regarding chain reactions
4958 for (i = 0; ep_em_explodes_by_fire[i] != -1; i++)
4959 SET_PROPERTY(ep_em_explodes_by_fire[i], EP_EXPLODES_BY_FIRE,
4960 level.em_explodes_by_fire);
4963 // this is needed because some graphics depend on element properties
4964 if (game_status == GAME_MODE_PLAYING)
4965 InitElementGraphicInfo();
4968 void InitElementPropertiesGfxElement(void)
4972 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4974 struct ElementInfo *ei = &element_info[i];
4976 ei->gfx_element = (ei->use_gfx_element ? ei->gfx_element_initial : i);
4980 static void InitGlobal(void)
4985 for (i = 0; i < MAX_NUM_ELEMENTS + 1; i++)
4987 // check if element_name_info entry defined for each element in "main.h"
4988 if (i < MAX_NUM_ELEMENTS && element_name_info[i].token_name == NULL)
4989 Fail("undefined 'element_name_info' entry for element %d", i);
4991 element_info[i].token_name = element_name_info[i].token_name;
4992 element_info[i].class_name = element_name_info[i].class_name;
4993 element_info[i].editor_description= element_name_info[i].editor_description;
4996 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS + 1; i++)
4998 // check if global_anim_name_info defined for each entry in "main.h"
4999 if (i < NUM_GLOBAL_ANIM_TOKENS &&
5000 global_anim_name_info[i].token_name == NULL)
5001 Fail("undefined 'global_anim_name_info' entry for anim %d", i);
5003 global_anim_info[i].token_name = global_anim_name_info[i].token_name;
5006 // create hash to store URLs for global animations
5007 anim_url_hash = newSetupFileHash();
5009 // create hash from image config list
5010 image_config_hash = newSetupFileHash();
5011 for (i = 0; image_config[i].token != NULL; i++)
5012 setHashEntry(image_config_hash,
5013 image_config[i].token,
5014 image_config[i].value);
5016 // create hash from element token list
5017 element_token_hash = newSetupFileHash();
5018 for (i = 0; element_name_info[i].token_name != NULL; i++)
5019 setHashEntry(element_token_hash,
5020 element_name_info[i].token_name,
5023 // create hash from graphic token list
5024 graphic_token_hash = newSetupFileHash();
5025 for (graphic = 0, i = 0; image_config[i].token != NULL; i++)
5026 if (strSuffix(image_config[i].value, ".png") ||
5027 strSuffix(image_config[i].value, ".pcx") ||
5028 strSuffix(image_config[i].value, ".wav") ||
5029 strEqual(image_config[i].value, UNDEFINED_FILENAME))
5030 setHashEntry(graphic_token_hash,
5031 image_config[i].token,
5032 int2str(graphic++, 0));
5034 // create hash from font token list
5035 font_token_hash = newSetupFileHash();
5036 for (i = 0; font_info[i].token_name != NULL; i++)
5037 setHashEntry(font_token_hash,
5038 font_info[i].token_name,
5041 // set default filenames for all cloned graphics in static configuration
5042 for (i = 0; image_config[i].token != NULL; i++)
5044 if (strEqual(image_config[i].value, UNDEFINED_FILENAME))
5046 char *token = image_config[i].token;
5047 char *token_clone_from = getStringCat2(token, ".clone_from");
5048 char *token_cloned = getHashEntry(image_config_hash, token_clone_from);
5050 if (token_cloned != NULL)
5052 char *value_cloned = getHashEntry(image_config_hash, token_cloned);
5054 if (value_cloned != NULL)
5056 // set default filename in static configuration
5057 image_config[i].value = value_cloned;
5059 // set default filename in image config hash
5060 setHashEntry(image_config_hash, token, value_cloned);
5064 free(token_clone_from);
5068 // always start with reliable default values (all elements)
5069 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5070 ActiveElement[i] = i;
5072 // now add all entries that have an active state (active elements)
5073 for (i = 0; element_with_active_state[i].element != -1; i++)
5075 int element = element_with_active_state[i].element;
5076 int element_active = element_with_active_state[i].element_active;
5078 ActiveElement[element] = element_active;
5081 // always start with reliable default values (all buttons)
5082 for (i = 0; i < NUM_IMAGE_FILES; i++)
5083 ActiveButton[i] = i;
5085 // now add all entries that have an active state (active buttons)
5086 for (i = 0; button_with_active_state[i].button != -1; i++)
5088 int button = button_with_active_state[i].button;
5089 int button_active = button_with_active_state[i].button_active;
5091 ActiveButton[button] = button_active;
5094 // always start with reliable default values (all fonts)
5095 for (i = 0; i < NUM_FONTS; i++)
5098 // now add all entries that have an active state (active fonts)
5099 for (i = 0; font_with_active_state[i].font_nr != -1; i++)
5101 int font = font_with_active_state[i].font_nr;
5102 int font_active = font_with_active_state[i].font_nr_active;
5104 ActiveFont[font] = font_active;
5107 global.autoplay_leveldir = NULL;
5108 global.patchtapes_leveldir = NULL;
5109 global.convert_leveldir = NULL;
5110 global.dumplevel_leveldir = NULL;
5111 global.dumptape_leveldir = NULL;
5112 global.create_sketch_images_dir = NULL;
5113 global.create_collect_images_dir = NULL;
5115 global.frames_per_second = 0;
5116 global.show_frames_per_second = FALSE;
5118 global.border_status = GAME_MODE_LOADING;
5119 global.anim_status = global.anim_status_next = GAME_MODE_LOADING;
5121 global.use_envelope_request = FALSE;
5123 global.user_names = NULL;
5126 static void Execute_Command(char *command)
5130 if (strEqual(command, "print graphicsinfo.conf"))
5132 Print("# You can configure additional/alternative image files here.\n");
5133 Print("# (The entries below are default and therefore commented out.)\n");
5135 Print("%s\n", getFormattedSetupEntry("name", "Classic Graphics"));
5137 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5140 for (i = 0; image_config[i].token != NULL; i++)
5141 Print("# %s\n", getFormattedSetupEntry(image_config[i].token,
5142 image_config[i].value));
5146 else if (strEqual(command, "print soundsinfo.conf"))
5148 Print("# You can configure additional/alternative sound files here.\n");
5149 Print("# (The entries below are default and therefore commented out.)\n");
5151 Print("%s\n", getFormattedSetupEntry("name", "Classic Sounds"));
5153 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5156 for (i = 0; sound_config[i].token != NULL; i++)
5157 Print("# %s\n", getFormattedSetupEntry(sound_config[i].token,
5158 sound_config[i].value));
5162 else if (strEqual(command, "print musicinfo.conf"))
5164 Print("# You can configure additional/alternative music files here.\n");
5165 Print("# (The entries below are default and therefore commented out.)\n");
5167 Print("%s\n", getFormattedSetupEntry("name", "Classic Music"));
5169 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5172 for (i = 0; music_config[i].token != NULL; i++)
5173 Print("# %s\n", getFormattedSetupEntry(music_config[i].token,
5174 music_config[i].value));
5178 else if (strEqual(command, "print editorsetup.conf"))
5180 Print("# You can configure your personal editor element list here.\n");
5181 Print("# (The entries below are default and therefore commented out.)\n");
5184 // this is needed to be able to check element list for cascade elements
5185 InitElementPropertiesStatic();
5186 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
5188 PrintEditorElementList();
5192 else if (strEqual(command, "print helpanim.conf"))
5194 Print("# You can configure different element help animations here.\n");
5195 Print("# (The entries below are default and therefore commented out.)\n");
5198 for (i = 0; helpanim_config[i].token != NULL; i++)
5200 Print("# %s\n", getFormattedSetupEntry(helpanim_config[i].token,
5201 helpanim_config[i].value));
5203 if (strEqual(helpanim_config[i].token, "end"))
5209 else if (strEqual(command, "print helptext.conf"))
5211 Print("# You can configure different element help text here.\n");
5212 Print("# (The entries below are default and therefore commented out.)\n");
5215 for (i = 0; helptext_config[i].token != NULL; i++)
5216 Print("# %s\n", getFormattedSetupEntry(helptext_config[i].token,
5217 helptext_config[i].value));
5221 else if (strPrefix(command, "dump level "))
5223 char *filename = &command[11];
5225 if (fileExists(filename))
5227 LoadLevelFromFilename(&level, filename);
5233 char *leveldir = getStringCopy(filename); // read command parameters
5234 char *level_nr = strchr(leveldir, ' ');
5236 if (level_nr == NULL)
5237 Fail("cannot open file '%s'", filename);
5241 global.dumplevel_leveldir = leveldir;
5242 global.dumplevel_level_nr = atoi(level_nr);
5244 program.headless = TRUE;
5246 else if (strPrefix(command, "dump tape "))
5248 char *filename = &command[10];
5250 if (fileExists(filename))
5252 LoadTapeFromFilename(filename);
5258 char *leveldir = getStringCopy(filename); // read command parameters
5259 char *level_nr = strchr(leveldir, ' ');
5261 if (level_nr == NULL)
5262 Fail("cannot open file '%s'", filename);
5266 global.dumptape_leveldir = leveldir;
5267 global.dumptape_level_nr = atoi(level_nr);
5269 program.headless = TRUE;
5271 else if (strPrefix(command, "autoplay ") ||
5272 strPrefix(command, "autoffwd ") ||
5273 strPrefix(command, "autowarp ") ||
5274 strPrefix(command, "autotest ") ||
5275 strPrefix(command, "autosave ") ||
5276 strPrefix(command, "autoupload ") ||
5277 strPrefix(command, "autofix "))
5279 char *arg_ptr = strchr(command, ' ');
5280 char *str_ptr = getStringCopy(arg_ptr); // read command parameters
5282 global.autoplay_mode =
5283 (strPrefix(command, "autoplay") ? AUTOPLAY_MODE_PLAY :
5284 strPrefix(command, "autoffwd") ? AUTOPLAY_MODE_FFWD :
5285 strPrefix(command, "autowarp") ? AUTOPLAY_MODE_WARP :
5286 strPrefix(command, "autotest") ? AUTOPLAY_MODE_TEST :
5287 strPrefix(command, "autosave") ? AUTOPLAY_MODE_SAVE :
5288 strPrefix(command, "autoupload") ? AUTOPLAY_MODE_UPLOAD :
5289 strPrefix(command, "autofix") ? AUTOPLAY_MODE_FIX :
5290 AUTOPLAY_MODE_NONE);
5292 while (*str_ptr != '\0') // continue parsing string
5294 // cut leading whitespace from string, replace it by string terminator
5295 while (*str_ptr == ' ' || *str_ptr == '\t')
5298 if (*str_ptr == '\0') // end of string reached
5301 if (global.autoplay_leveldir == NULL) // read level set string
5303 global.autoplay_leveldir = str_ptr;
5304 global.autoplay_all = TRUE; // default: play all tapes
5306 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5307 global.autoplay_level[i] = FALSE;
5309 else // read level number string
5311 int level_nr = atoi(str_ptr); // get level_nr value
5313 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5314 global.autoplay_level[level_nr] = TRUE;
5316 global.autoplay_all = FALSE;
5319 // advance string pointer to the next whitespace (or end of string)
5320 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5324 if (global.autoplay_mode & AUTOPLAY_WARP_NO_DISPLAY)
5325 program.headless = TRUE;
5327 else if (strPrefix(command, "patch tapes "))
5329 char *str_ptr = getStringCopy(&command[12]); // read command parameters
5331 // skip leading whitespace
5332 while (*str_ptr == ' ' || *str_ptr == '\t')
5335 if (*str_ptr == '\0')
5336 Fail("cannot find MODE in command '%s'", command);
5338 global.patchtapes_mode = str_ptr; // store patch mode
5340 // advance to next whitespace (or end of string)
5341 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5344 while (*str_ptr != '\0') // continue parsing string
5346 // cut leading whitespace from string, replace it by string terminator
5347 while (*str_ptr == ' ' || *str_ptr == '\t')
5350 if (*str_ptr == '\0') // end of string reached
5353 if (global.patchtapes_leveldir == NULL) // read level set string
5355 global.patchtapes_leveldir = str_ptr;
5356 global.patchtapes_all = TRUE; // default: patch all tapes
5358 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5359 global.patchtapes_level[i] = FALSE;
5361 else // read level number string
5363 int level_nr = atoi(str_ptr); // get level_nr value
5365 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5366 global.patchtapes_level[level_nr] = TRUE;
5368 global.patchtapes_all = FALSE;
5371 // advance string pointer to the next whitespace (or end of string)
5372 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5376 if (global.patchtapes_leveldir == NULL)
5378 if (strEqual(global.patchtapes_mode, "help"))
5379 global.patchtapes_leveldir = UNDEFINED_LEVELSET;
5381 Fail("cannot find LEVELDIR in command '%s'", command);
5384 program.headless = TRUE;
5386 else if (strPrefix(command, "convert "))
5388 char *str_copy = getStringCopy(strchr(command, ' ') + 1);
5389 char *str_ptr = strchr(str_copy, ' ');
5391 global.convert_leveldir = str_copy;
5392 global.convert_level_nr = -1;
5394 if (str_ptr != NULL) // level number follows
5396 *str_ptr++ = '\0'; // terminate leveldir string
5397 global.convert_level_nr = atoi(str_ptr); // get level_nr value
5400 program.headless = TRUE;
5402 else if (strPrefix(command, "create sketch images "))
5404 global.create_sketch_images_dir = getStringCopy(&command[21]);
5406 if (access(global.create_sketch_images_dir, W_OK) != 0)
5407 Fail("image target directory '%s' not found or not writable",
5408 global.create_sketch_images_dir);
5410 else if (strPrefix(command, "create collect image "))
5412 global.create_collect_images_dir = getStringCopy(&command[21]);
5414 if (access(global.create_collect_images_dir, W_OK) != 0)
5415 Fail("image target directory '%s' not found or not writable",
5416 global.create_collect_images_dir);
5418 else if (strPrefix(command, "create CE image "))
5420 CreateCustomElementImages(&command[16]);
5426 FailWithHelp("unrecognized command '%s'", command);
5429 // disable networking if any valid command was recognized
5430 options.network = setup.network_mode = FALSE;
5433 static void InitSetup(void)
5435 LoadUserNames(); // global user names
5436 LoadUserSetup(); // global user number
5438 LoadSetup(); // global setup info
5440 // set some options from setup file
5442 if (setup.options.verbose)
5443 options.verbose = TRUE;
5445 if (setup.debug.show_frames_per_second)
5446 global.show_frames_per_second = TRUE;
5449 static void InitGameInfo(void)
5451 game.restart_level = FALSE;
5452 game.restart_game_message = NULL;
5454 game.request_active = FALSE;
5455 game.request_active_or_moving = FALSE;
5457 game.use_masked_elements_initial = FALSE;
5460 static void InitPlayerInfo(void)
5464 // choose default local player
5465 local_player = &stored_player[0];
5467 for (i = 0; i < MAX_PLAYERS; i++)
5469 stored_player[i].connected_locally = FALSE;
5470 stored_player[i].connected_network = FALSE;
5473 local_player->connected_locally = TRUE;
5476 static void InitArtworkInfo(void)
5481 static char *get_string_in_brackets(char *string)
5483 char *string_in_brackets = checked_malloc(strlen(string) + 3);
5485 sprintf(string_in_brackets, "[%s]", string);
5487 return string_in_brackets;
5490 static char *get_level_id_suffix(int id_nr)
5492 char *id_suffix = checked_malloc(1 + 3 + 1);
5494 if (id_nr < 0 || id_nr > 999)
5497 sprintf(id_suffix, ".%03d", id_nr);
5502 static void InitArtworkConfig(void)
5504 static char *image_id_prefix[MAX_NUM_ELEMENTS +
5506 NUM_GLOBAL_ANIM_TOKENS + 1];
5507 static char *sound_id_prefix[2 * MAX_NUM_ELEMENTS +
5508 NUM_GLOBAL_ANIM_TOKENS + 1];
5509 static char *music_id_prefix[NUM_MUSIC_PREFIXES +
5510 NUM_GLOBAL_ANIM_TOKENS + 1];
5511 static char *action_id_suffix[NUM_ACTIONS + 1];
5512 static char *direction_id_suffix[NUM_DIRECTIONS_FULL + 1];
5513 static char *special_id_suffix[NUM_SPECIAL_GFX_ARGS + 1];
5514 static char *level_id_suffix[MAX_LEVELS + 1];
5515 static char *dummy[1] = { NULL };
5516 static char *ignore_generic_tokens[] =
5521 "program_copyright",
5526 static char **ignore_image_tokens;
5527 static char **ignore_sound_tokens;
5528 static char **ignore_music_tokens;
5529 int num_ignore_generic_tokens;
5530 int num_ignore_image_tokens;
5531 int num_ignore_sound_tokens;
5532 int num_ignore_music_tokens;
5535 // dynamically determine list of generic tokens to be ignored
5536 num_ignore_generic_tokens = 0;
5537 for (i = 0; ignore_generic_tokens[i] != NULL; i++)
5538 num_ignore_generic_tokens++;
5540 // dynamically determine list of image tokens to be ignored
5541 num_ignore_image_tokens = num_ignore_generic_tokens;
5542 for (i = 0; image_config_vars[i].token != NULL; i++)
5543 num_ignore_image_tokens++;
5544 ignore_image_tokens =
5545 checked_malloc((num_ignore_image_tokens + 1) * sizeof(char *));
5546 for (i = 0; i < num_ignore_generic_tokens; i++)
5547 ignore_image_tokens[i] = ignore_generic_tokens[i];
5548 for (i = 0; i < num_ignore_image_tokens - num_ignore_generic_tokens; i++)
5549 ignore_image_tokens[num_ignore_generic_tokens + i] =
5550 image_config_vars[i].token;
5551 ignore_image_tokens[num_ignore_image_tokens] = NULL;
5553 // dynamically determine list of sound tokens to be ignored
5554 num_ignore_sound_tokens = num_ignore_generic_tokens;
5555 ignore_sound_tokens =
5556 checked_malloc((num_ignore_sound_tokens + 1) * sizeof(char *));
5557 for (i = 0; i < num_ignore_generic_tokens; i++)
5558 ignore_sound_tokens[i] = ignore_generic_tokens[i];
5559 ignore_sound_tokens[num_ignore_sound_tokens] = NULL;
5561 // dynamically determine list of music tokens to be ignored
5562 num_ignore_music_tokens = num_ignore_generic_tokens;
5563 ignore_music_tokens =
5564 checked_malloc((num_ignore_music_tokens + 1) * sizeof(char *));
5565 for (i = 0; i < num_ignore_generic_tokens; i++)
5566 ignore_music_tokens[i] = ignore_generic_tokens[i];
5567 ignore_music_tokens[num_ignore_music_tokens] = NULL;
5569 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5570 image_id_prefix[i] = element_info[i].token_name;
5571 for (i = 0; i < NUM_FONTS; i++)
5572 image_id_prefix[MAX_NUM_ELEMENTS + i] = font_info[i].token_name;
5573 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5574 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + i] =
5575 global_anim_info[i].token_name;
5576 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5578 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5579 sound_id_prefix[i] = element_info[i].token_name;
5580 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5581 sound_id_prefix[MAX_NUM_ELEMENTS + i] =
5582 get_string_in_brackets(element_info[i].class_name);
5583 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5584 sound_id_prefix[2 * MAX_NUM_ELEMENTS + i] =
5585 global_anim_info[i].token_name;
5586 sound_id_prefix[2 * MAX_NUM_ELEMENTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5588 for (i = 0; i < NUM_MUSIC_PREFIXES; i++)
5589 music_id_prefix[i] = music_prefix_info[i].prefix;
5590 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5591 music_id_prefix[NUM_MUSIC_PREFIXES + i] =
5592 global_anim_info[i].token_name;
5593 music_id_prefix[NUM_MUSIC_PREFIXES + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5595 for (i = 0; i < NUM_ACTIONS; i++)
5596 action_id_suffix[i] = element_action_info[i].suffix;
5597 action_id_suffix[NUM_ACTIONS] = NULL;
5599 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
5600 direction_id_suffix[i] = element_direction_info[i].suffix;
5601 direction_id_suffix[NUM_DIRECTIONS_FULL] = NULL;
5603 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
5604 special_id_suffix[i] = special_suffix_info[i].suffix;
5605 special_id_suffix[NUM_SPECIAL_GFX_ARGS] = NULL;
5607 for (i = 0; i < MAX_LEVELS; i++)
5608 level_id_suffix[i] = get_level_id_suffix(i);
5609 level_id_suffix[MAX_LEVELS] = NULL;
5611 InitImageList(image_config, NUM_IMAGE_FILES, image_config_suffix,
5612 image_id_prefix, action_id_suffix, direction_id_suffix,
5613 special_id_suffix, ignore_image_tokens);
5614 InitSoundList(sound_config, NUM_SOUND_FILES, sound_config_suffix,
5615 sound_id_prefix, action_id_suffix, dummy,
5616 special_id_suffix, ignore_sound_tokens);
5617 InitMusicList(music_config, NUM_MUSIC_FILES, music_config_suffix,
5618 music_id_prefix, action_id_suffix, special_id_suffix,
5619 level_id_suffix, ignore_music_tokens);
5622 static void InitMixer(void)
5629 static void InitVideoOverlay(void)
5631 // if virtual buttons are not loaded from setup file, repeat initializing
5632 // virtual buttons grid with default values now that video is initialized
5633 if (!setup.touch.grid_initialized)
5636 InitTileCursorInfo();
5640 void InitGfxBuffers(void)
5642 static int win_xsize_last = -1;
5643 static int win_ysize_last = -1;
5645 // create additional image buffers for double-buffering and cross-fading
5647 if (WIN_XSIZE != win_xsize_last || WIN_YSIZE != win_ysize_last)
5649 // used to temporarily store the backbuffer -- only re-create if changed
5650 ReCreateBitmap(&bitmap_db_store_1, WIN_XSIZE, WIN_YSIZE);
5651 ReCreateBitmap(&bitmap_db_store_2, WIN_XSIZE, WIN_YSIZE);
5653 win_xsize_last = WIN_XSIZE;
5654 win_ysize_last = WIN_YSIZE;
5657 ReCreateBitmap(&bitmap_db_field, FXSIZE, FYSIZE);
5658 ReCreateBitmap(&bitmap_db_panel, DXSIZE, DYSIZE);
5659 ReCreateBitmap(&bitmap_db_door_1, 3 * DXSIZE, DYSIZE);
5660 ReCreateBitmap(&bitmap_db_door_2, 3 * VXSIZE, VYSIZE);
5662 // initialize screen properties
5663 InitGfxFieldInfo(SX, SY, SXSIZE, SYSIZE,
5664 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
5666 InitGfxDoor1Info(DX, DY, DXSIZE, DYSIZE);
5667 InitGfxDoor2Info(VX, VY, VXSIZE, VYSIZE);
5668 InitGfxDoor3Info(EX, EY, EXSIZE, EYSIZE);
5669 InitGfxWindowInfo(WIN_XSIZE, WIN_YSIZE);
5670 InitGfxScrollbufferInfo(FXSIZE, FYSIZE);
5671 InitGfxClipRegion(FALSE, -1, -1, -1, -1);
5673 // required if door size definitions have changed
5674 InitGraphicCompatibilityInfo_Doors();
5676 InitGfxBuffers_EM();
5677 InitGfxBuffers_SP();
5680 static void InitGfx(void)
5682 struct GraphicInfo *graphic_info_last = graphic_info;
5683 char *filename_font_initial = NULL;
5684 char *filename_image_initial[NUM_INITIAL_IMAGES] = { NULL };
5685 char *image_token[NUM_INITIAL_IMAGES] =
5687 CONFIG_TOKEN_GLOBAL_BUSY_INITIAL,
5688 CONFIG_TOKEN_GLOBAL_BUSY,
5689 CONFIG_TOKEN_GLOBAL_BUSY_PLAYFIELD,
5690 CONFIG_TOKEN_BACKGROUND,
5691 CONFIG_TOKEN_BACKGROUND_LOADING_INITIAL,
5692 CONFIG_TOKEN_BACKGROUND_LOADING
5694 struct MenuPosInfo *init_busy[NUM_INITIAL_IMAGES_BUSY] =
5698 &init.busy_playfield
5700 Bitmap *bitmap_font_initial = NULL;
5701 int parameter[NUM_INITIAL_IMAGES][NUM_GFX_ARGS];
5704 // determine settings for initial font (for displaying startup messages)
5705 for (i = 0; image_config[i].token != NULL; i++)
5707 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5709 char font_token[128];
5712 sprintf(font_token, "%s_%d", CONFIG_TOKEN_FONT_INITIAL, j + 1);
5713 len_font_token = strlen(font_token);
5715 if (strEqual(image_config[i].token, font_token))
5717 filename_font_initial = image_config[i].value;
5719 else if (strlen(image_config[i].token) > len_font_token &&
5720 strncmp(image_config[i].token, font_token, len_font_token) == 0)
5722 if (strEqual(&image_config[i].token[len_font_token], ".x"))
5723 font_initial[j].src_x = atoi(image_config[i].value);
5724 else if (strEqual(&image_config[i].token[len_font_token], ".y"))
5725 font_initial[j].src_y = atoi(image_config[i].value);
5726 else if (strEqual(&image_config[i].token[len_font_token], ".width"))
5727 font_initial[j].width = atoi(image_config[i].value);
5728 else if (strEqual(&image_config[i].token[len_font_token], ".height"))
5729 font_initial[j].height = atoi(image_config[i].value);
5734 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5736 font_initial[j].num_chars = DEFAULT_NUM_CHARS_PER_FONT;
5737 font_initial[j].num_chars_per_line = DEFAULT_NUM_CHARS_PER_LINE;
5740 if (filename_font_initial == NULL) // should not happen
5741 Fail("cannot get filename for '%s'", CONFIG_TOKEN_FONT_INITIAL);
5744 InitGfxCustomArtworkInfo();
5745 InitGfxOtherSettings();
5747 InitGfxTileSizeInfo(TILESIZE, TILESIZE);
5749 bitmap_font_initial = LoadCustomImage(filename_font_initial);
5751 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5752 font_initial[j].bitmap = bitmap_font_initial;
5754 InitFontGraphicInfo();
5758 DrawInitTextHead("Loading graphics");
5760 InitMenuDesignSettings_Static();
5762 // initialize settings for initial images with default values
5763 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5764 for (j = 0; j < NUM_GFX_ARGS; j++)
5766 get_graphic_parameter_value(image_config_suffix[j].value,
5767 image_config_suffix[j].token,
5768 image_config_suffix[j].type);
5770 // read settings for initial images from default custom artwork config
5771 char *gfx_config_filename = getPath3(options.graphics_directory,
5773 GRAPHICSINFO_FILENAME);
5775 if (fileExists(gfx_config_filename))
5777 SetupFileHash *setup_file_hash = loadSetupFileHash(gfx_config_filename);
5779 if (setup_file_hash)
5781 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5783 char *filename = getHashEntry(setup_file_hash, image_token[i]);
5787 filename_image_initial[i] = getStringCopy(filename);
5789 for (j = 0; image_config_suffix[j].token != NULL; j++)
5791 int type = image_config_suffix[j].type;
5792 char *suffix = image_config_suffix[j].token;
5793 char *token = getStringCat2(image_token[i], suffix);
5794 char *value = getHashEntry(setup_file_hash, token);
5796 checked_free(token);
5800 get_graphic_parameter_value(value, suffix, type);
5805 // read values from custom graphics config file
5806 InitMenuDesignSettings_FromHash(setup_file_hash, FALSE);
5808 freeSetupFileHash(setup_file_hash);
5812 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5814 if (filename_image_initial[i] == NULL)
5816 int len_token = strlen(image_token[i]);
5818 // read settings for initial images from static default artwork config
5819 for (j = 0; image_config[j].token != NULL; j++)
5821 if (strEqual(image_config[j].token, image_token[i]))
5823 filename_image_initial[i] = getStringCopy(image_config[j].value);
5825 else if (strlen(image_config[j].token) > len_token &&
5826 strncmp(image_config[j].token, image_token[i], len_token) == 0)
5828 for (k = 0; image_config_suffix[k].token != NULL; k++)
5830 if (strEqual(&image_config[j].token[len_token],
5831 image_config_suffix[k].token))
5833 get_graphic_parameter_value(image_config[j].value,
5834 image_config_suffix[k].token,
5835 image_config_suffix[k].type);
5842 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5844 if (filename_image_initial[i] == NULL) // should not happen
5845 Fail("cannot get filename for '%s'", image_token[i]);
5847 image_initial[i].bitmaps =
5848 checked_calloc(sizeof(Bitmap *) * NUM_IMG_BITMAP_POINTERS);
5850 if (!strEqual(filename_image_initial[i], UNDEFINED_FILENAME))
5851 image_initial[i].bitmaps[IMG_BITMAP_STANDARD] =
5852 LoadCustomImage(filename_image_initial[i]);
5854 checked_free(filename_image_initial[i]);
5857 graphic_info = image_initial; // graphic == 0 => image_initial
5859 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5860 set_graphic_parameters_ext(i, parameter[i], image_initial[i].bitmaps);
5862 graphic_info = graphic_info_last;
5864 for (i = 0; i < NUM_INITIAL_IMAGES_BUSY; i++)
5866 // set image size for busy animations
5867 init_busy[i]->width = image_initial[i].width;
5868 init_busy[i]->height = image_initial[i].height;
5871 SetLoadingBackgroundImage();
5873 ClearRectangleOnBackground(window, 0, 0, WIN_XSIZE, WIN_YSIZE);
5875 InitGfxDrawBusyAnimFunction(DrawInitAnim);
5876 InitGfxDrawGlobalAnimFunction(DrawGlobalAnimations);
5877 InitGfxDrawGlobalBorderFunction(DrawMaskedBorderToTarget);
5878 InitGfxDrawTileCursorFunction(DrawTileCursor);
5880 gfx.fade_border_source_status = global.border_status;
5881 gfx.fade_border_target_status = global.border_status;
5882 gfx.masked_border_bitmap_ptr = backbuffer;
5884 // use copy of busy animation to prevent change while reloading artwork
5888 static void InitGfxBackground(void)
5890 fieldbuffer = bitmap_db_field;
5891 SetDrawtoField(DRAW_TO_BACKBUFFER);
5893 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
5895 redraw_mask = REDRAW_ALL;
5898 static void InitLevelInfo(void)
5900 LoadLevelInfo(); // global level info
5901 LoadLevelSetup_LastSeries(); // last played series info
5902 LoadLevelSetup_SeriesInfo(); // last played level info
5904 if (global.autoplay_leveldir &&
5905 global.autoplay_mode != AUTOPLAY_MODE_TEST)
5907 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
5908 global.autoplay_leveldir);
5909 if (leveldir_current == NULL)
5910 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
5913 SetLevelSetInfo(leveldir_current->identifier, level_nr);
5916 static void InitLevelArtworkInfo(void)
5918 LoadLevelArtworkInfo();
5921 static void InitImages(void)
5923 print_timestamp_init("InitImages");
5926 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
5927 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5928 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
5929 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5930 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
5931 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5932 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
5933 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5936 setLevelArtworkDir(artwork.gfx_first);
5939 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
5940 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5941 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
5942 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5943 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
5944 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5945 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
5946 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5950 Debug("init:InitImages", "InitImages for '%s' ['%s', '%s'] ['%s', '%s']",
5951 leveldir_current->identifier,
5952 artwork.gfx_current_identifier,
5953 artwork.gfx_current->identifier,
5954 leveldir_current->graphics_set,
5955 leveldir_current->graphics_path);
5958 UPDATE_BUSY_STATE();
5960 ReloadCustomImages();
5961 print_timestamp_time("ReloadCustomImages");
5963 UPDATE_BUSY_STATE();
5965 LoadCustomElementDescriptions();
5966 print_timestamp_time("LoadCustomElementDescriptions");
5968 UPDATE_BUSY_STATE();
5970 LoadMenuDesignSettings();
5971 print_timestamp_time("LoadMenuDesignSettings");
5973 UPDATE_BUSY_STATE();
5975 ReinitializeGraphics();
5976 print_timestamp_time("ReinitializeGraphics");
5978 LoadMenuDesignSettings_AfterGraphics();
5979 print_timestamp_time("LoadMenuDesignSettings_AfterGraphics");
5981 UPDATE_BUSY_STATE();
5983 print_timestamp_done("InitImages");
5986 static void InitSound(void)
5988 print_timestamp_init("InitSound");
5990 // set artwork path to send it to the sound server process
5991 setLevelArtworkDir(artwork.snd_first);
5993 InitReloadCustomSounds();
5994 print_timestamp_time("InitReloadCustomSounds");
5996 ReinitializeSounds();
5997 print_timestamp_time("ReinitializeSounds");
5999 print_timestamp_done("InitSound");
6002 static void InitMusic(void)
6004 print_timestamp_init("InitMusic");
6006 // set artwork path to send it to the sound server process
6007 setLevelArtworkDir(artwork.mus_first);
6009 InitReloadCustomMusic();
6010 print_timestamp_time("InitReloadCustomMusic");
6012 ReinitializeMusic();
6013 print_timestamp_time("ReinitializeMusic");
6015 print_timestamp_done("InitMusic");
6018 static void InitArtworkDone(void)
6020 if (program.headless)
6023 InitGlobalAnimations();
6026 static void InitNetworkSettings(void)
6028 boolean network_enabled = (options.network || setup.network_mode);
6029 char *network_server = (options.server_host != NULL ? options.server_host :
6030 setup.network_server_hostname);
6032 if (strEqual(network_server, STR_NETWORK_AUTO_DETECT))
6033 network_server = NULL;
6035 InitNetworkInfo(network_enabled,
6039 options.server_port);
6042 void InitNetworkServer(void)
6044 if (!network.enabled || network.connected)
6047 LimitScreenUpdates(FALSE);
6049 if (game_status == GAME_MODE_LOADING)
6052 if (!ConnectToServer(network.server_host, network.server_port))
6054 network.enabled = FALSE;
6056 setup.network_mode = FALSE;
6060 SendToServer_ProtocolVersion();
6061 SendToServer_PlayerName(setup.player_name);
6062 SendToServer_NrWanted(setup.network_player_nr + 1);
6064 network.connected = TRUE;
6067 // short time to recognize result of network initialization
6068 if (game_status == GAME_MODE_LOADING)
6069 Delay_WithScreenUpdates(1000);
6072 static boolean CheckArtworkConfigForCustomElements(char *filename)
6074 SetupFileHash *setup_file_hash;
6075 boolean redefined_ce_found = FALSE;
6077 // !!! CACHE THIS BY USING HASH 'filename' => 'true/false' !!!
6079 if ((setup_file_hash = loadSetupFileHash(filename)) != NULL)
6081 BEGIN_HASH_ITERATION(setup_file_hash, itr)
6083 char *token = HASH_ITERATION_TOKEN(itr);
6085 if (strPrefix(token, "custom_"))
6087 redefined_ce_found = TRUE;
6092 END_HASH_ITERATION(setup_file_hash, itr)
6094 freeSetupFileHash(setup_file_hash);
6097 return redefined_ce_found;
6100 static boolean CheckArtworkTypeForRedefinedCustomElements(int type)
6102 char *filename_base, *filename_local;
6103 boolean redefined_ce_found = FALSE;
6105 setLevelArtworkDir(ARTWORK_FIRST_NODE(artwork, type));
6108 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6109 "leveldir_current->identifier == '%s'",
6110 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
6111 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6112 "leveldir_current->graphics_path == '%s'",
6113 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
6114 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6115 "leveldir_current->graphics_set == '%s'",
6116 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
6117 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6118 "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
6119 leveldir_current == NULL ? "[NULL]" :
6120 LEVELDIR_ARTWORK_SET(leveldir_current, type));
6123 // first look for special artwork configured in level series config
6124 filename_base = getCustomArtworkLevelConfigFilename(type);
6127 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6128 "filename_base == '%s'", filename_base);
6131 if (fileExists(filename_base))
6132 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_base);
6134 filename_local = getCustomArtworkConfigFilename(type);
6137 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6138 "filename_local == '%s'", filename_local);
6141 if (filename_local != NULL && !strEqual(filename_base, filename_local))
6142 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_local);
6145 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6146 "redefined_ce_found == %d", redefined_ce_found);
6149 return redefined_ce_found;
6152 static void InitOverrideArtwork(void)
6154 boolean redefined_ce_found = FALSE;
6156 // to check if this level set redefines any CEs, do not use overriding
6157 gfx.override_level_graphics = FALSE;
6158 gfx.override_level_sounds = FALSE;
6159 gfx.override_level_music = FALSE;
6161 // now check if this level set has definitions for custom elements
6162 if (setup.override_level_graphics == AUTO ||
6163 setup.override_level_sounds == AUTO ||
6164 setup.override_level_music == AUTO)
6165 redefined_ce_found =
6166 (CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_GRAPHICS) |
6167 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_SOUNDS) |
6168 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_MUSIC));
6171 Debug("init:InitOverrideArtwork", "redefined_ce_found == %d",
6172 redefined_ce_found);
6175 if (redefined_ce_found)
6177 // this level set has CE definitions: change "AUTO" to "FALSE"
6178 gfx.override_level_graphics = (setup.override_level_graphics == TRUE);
6179 gfx.override_level_sounds = (setup.override_level_sounds == TRUE);
6180 gfx.override_level_music = (setup.override_level_music == TRUE);
6184 // this level set has no CE definitions: change "AUTO" to "TRUE"
6185 gfx.override_level_graphics = (setup.override_level_graphics != FALSE);
6186 gfx.override_level_sounds = (setup.override_level_sounds != FALSE);
6187 gfx.override_level_music = (setup.override_level_music != FALSE);
6191 Debug("init:InitOverrideArtwork", "%d, %d, %d",
6192 gfx.override_level_graphics,
6193 gfx.override_level_sounds,
6194 gfx.override_level_music);
6198 static char *setNewArtworkIdentifier(int type)
6200 static char *last_leveldir_identifier[3] = { NULL, NULL, NULL };
6201 static char *last_artwork_identifier[3] = { NULL, NULL, NULL };
6202 static boolean last_override_level_artwork[3] = { FALSE, FALSE, FALSE };
6203 static boolean last_has_custom_artwork_set[3] = { FALSE, FALSE, FALSE };
6204 static boolean initialized[3] = { FALSE, FALSE, FALSE };
6205 TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type);
6206 boolean setup_override_artwork = GFX_OVERRIDE_ARTWORK(type);
6207 char *setup_artwork_set = SETUP_ARTWORK_SET(setup, type);
6208 char *leveldir_identifier = leveldir_current->identifier;
6209 // !!! setLevelArtworkDir() should be moved to an earlier stage !!!
6210 char *leveldir_artwork_set = setLevelArtworkDir(artwork_first_node);
6211 boolean has_level_artwork_set = (leveldir_artwork_set != NULL);
6212 TreeInfo *custom_artwork_set =
6213 getTreeInfoFromIdentifier(artwork_first_node, leveldir_identifier);
6214 boolean has_custom_artwork_set = (custom_artwork_set != NULL);
6215 char *artwork_current_identifier;
6216 char *artwork_new_identifier = NULL; // default: nothing has changed
6218 // leveldir_current may be invalid (level group, parent link)
6219 if (!validLevelSeries(leveldir_current))
6222 /* 1st step: determine artwork set to be activated in descending order:
6223 --------------------------------------------------------------------
6224 1. setup artwork (when configured to override everything else)
6225 2. artwork set configured in "levelinfo.conf" of current level set
6226 (artwork in level directory will have priority when loading later)
6227 3. artwork in level directory (stored in artwork sub-directory)
6228 4. setup artwork (currently configured in setup menu) */
6230 if (setup_override_artwork)
6231 artwork_current_identifier = setup_artwork_set;
6232 else if (has_level_artwork_set)
6233 artwork_current_identifier = leveldir_artwork_set;
6234 else if (has_custom_artwork_set)
6235 artwork_current_identifier = leveldir_identifier;
6237 artwork_current_identifier = setup_artwork_set;
6239 /* 2nd step: check if it is really needed to reload artwork set
6240 ------------------------------------------------------------ */
6242 // ---------- reload if level set and also artwork set has changed ----------
6243 if (last_leveldir_identifier[type] != leveldir_identifier &&
6244 (last_has_custom_artwork_set[type] || has_custom_artwork_set))
6245 artwork_new_identifier = artwork_current_identifier;
6247 last_leveldir_identifier[type] = leveldir_identifier;
6248 last_has_custom_artwork_set[type] = has_custom_artwork_set;
6250 // ---------- reload if "override artwork" setting has changed --------------
6251 if (last_override_level_artwork[type] != setup_override_artwork)
6252 artwork_new_identifier = artwork_current_identifier;
6254 last_override_level_artwork[type] = setup_override_artwork;
6256 // ---------- reload if current artwork identifier has changed --------------
6257 if (!strEqual(last_artwork_identifier[type], artwork_current_identifier))
6258 artwork_new_identifier = artwork_current_identifier;
6260 // (we cannot compare string pointers here, so copy string content itself)
6261 setString(&last_artwork_identifier[type], artwork_current_identifier);
6263 // ---------- set new artwork identifier ----------
6264 *(ARTWORK_CURRENT_IDENTIFIER_PTR(artwork, type)) = artwork_current_identifier;
6266 // ---------- do not reload directly after starting -------------------------
6267 if (!initialized[type])
6268 artwork_new_identifier = NULL;
6270 initialized[type] = TRUE;
6272 return artwork_new_identifier;
6275 static void InitArtworkIdentifier(void)
6277 setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6278 setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6279 setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6282 void ReloadCustomArtwork(int force_reload)
6284 int last_game_status = game_status; // save current game status
6285 char *gfx_new_identifier;
6286 char *snd_new_identifier;
6287 char *mus_new_identifier;
6288 boolean force_reload_gfx = (force_reload & (1 << ARTWORK_TYPE_GRAPHICS));
6289 boolean force_reload_snd = (force_reload & (1 << ARTWORK_TYPE_SOUNDS));
6290 boolean force_reload_mus = (force_reload & (1 << ARTWORK_TYPE_MUSIC));
6291 boolean reload_needed;
6293 InitOverrideArtwork();
6295 AdjustGraphicsForEMC();
6296 AdjustSoundsForEMC();
6298 gfx_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6299 snd_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6300 mus_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6302 reload_needed = (gfx_new_identifier != NULL || force_reload_gfx ||
6303 snd_new_identifier != NULL || force_reload_snd ||
6304 mus_new_identifier != NULL || force_reload_mus);
6309 print_timestamp_init("ReloadCustomArtwork");
6311 SetGameStatus(GAME_MODE_LOADING);
6313 FadeOut(REDRAW_ALL);
6315 SetLoadingBackgroundImage();
6317 ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6318 print_timestamp_time("ClearRectangleOnBackground");
6322 UPDATE_BUSY_STATE();
6324 InitMissingFileHash();
6326 if (gfx_new_identifier != NULL || force_reload_gfx)
6329 Debug("init:ReloadCustomArtwork",
6330 "RELOADING GRAPHICS '%s' -> '%s' ['%s', '%s']",
6331 artwork.gfx_current_identifier,
6333 artwork.gfx_current->identifier,
6334 leveldir_current->graphics_set);
6338 print_timestamp_time("InitImages");
6341 if (snd_new_identifier != NULL || force_reload_snd)
6344 print_timestamp_time("InitSound");
6347 if (mus_new_identifier != NULL || force_reload_mus)
6350 print_timestamp_time("InitMusic");
6355 SetGameStatus(last_game_status); // restore current game status
6357 FadeOut(REDRAW_ALL);
6359 RedrawGlobalBorder();
6361 // force redraw of (open or closed) door graphics
6362 SetDoorState(DOOR_OPEN_ALL);
6363 CloseDoor(DOOR_CLOSE_ALL | DOOR_NO_DELAY);
6365 FadeSetEnterScreen();
6366 FadeSkipNextFadeOut();
6368 print_timestamp_done("ReloadCustomArtwork");
6370 LimitScreenUpdates(FALSE);
6373 void KeyboardAutoRepeatOffUnlessAutoplay(void)
6375 if (global.autoplay_leveldir == NULL)
6376 KeyboardAutoRepeatOff();
6379 void DisplayExitMessage(char *format, va_list ap)
6381 // also check for initialized video (headless flag may be temporarily unset)
6382 if (program.headless || !video.initialized)
6385 // check if draw buffer and fonts for exit message are already available
6386 if (drawto == NULL || font_initial[NUM_INITIAL_FONTS - 1].bitmap == NULL)
6389 int font_1 = FC_RED;
6390 int font_2 = FC_YELLOW;
6391 int font_3 = FC_BLUE;
6392 int font_width = getFontWidth(font_2);
6393 int font_height = getFontHeight(font_2);
6396 int sxsize = WIN_XSIZE - 2 * sx;
6397 int sysize = WIN_YSIZE - 2 * sy;
6398 int line_length = sxsize / font_width;
6399 int max_lines = sysize / font_height;
6400 int num_lines_printed;
6404 gfx.sxsize = sxsize;
6405 gfx.sysize = sysize;
6409 ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6411 DrawTextSCentered(sy, font_1, "Fatal error:");
6412 sy += 3 * font_height;;
6415 DrawTextBufferVA(sx, sy, format, ap, font_2,
6416 line_length, line_length, max_lines,
6417 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6418 sy += (num_lines_printed + 3) * font_height;
6420 DrawTextSCentered(sy, font_1, "For details, see the following error file:");
6421 sy += 3 * font_height;
6424 DrawTextBuffer(sx, sy, program.log_filename[LOG_ERR_ID], font_2,
6425 line_length, line_length, max_lines,
6426 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6428 DrawTextSCentered(SYSIZE - 20, font_3, "Press any key or button to exit");
6430 redraw_mask = REDRAW_ALL;
6432 // force drawing exit message even if screen updates are currently limited
6433 LimitScreenUpdates(FALSE);
6437 // deactivate toons on error message screen
6438 setup.toons = FALSE;
6440 WaitForEventToContinue();
6444 // ============================================================================
6446 // ============================================================================
6450 print_timestamp_init("OpenAll");
6452 SetGameStatus(GAME_MODE_LOADING);
6456 InitGlobal(); // initialize some global variables
6458 InitRND(NEW_RANDOMIZE);
6459 InitSimpleRandom(NEW_RANDOMIZE);
6460 InitBetterRandom(NEW_RANDOMIZE);
6462 InitMissingFileHash();
6464 print_timestamp_time("[init global stuff]");
6468 print_timestamp_time("[init setup/config stuff (1)]");
6470 if (options.execute_command)
6471 Execute_Command(options.execute_command);
6473 InitNetworkSettings();
6477 if (network.serveronly)
6479 #if defined(PLATFORM_UNIX)
6480 NetworkServer(network.server_port, TRUE);
6482 Warn("networking only supported in Unix version");
6485 exit(0); // never reached, server loops forever
6489 print_timestamp_time("[init setup/config stuff (2)]");
6491 print_timestamp_time("[init setup/config stuff (3)]");
6492 InitArtworkInfo(); // needed before loading gfx, sound & music
6493 print_timestamp_time("[init setup/config stuff (4)]");
6494 InitArtworkConfig(); // needed before forking sound child process
6495 print_timestamp_time("[init setup/config stuff (5)]");
6497 print_timestamp_time("[init setup/config stuff (6)]");
6501 print_timestamp_time("[init setup/config stuff]");
6503 InitVideoDefaults();
6505 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
6508 InitEventFilter(FilterMouseMotionEvents);
6510 print_timestamp_time("[init video stuff]");
6512 InitElementPropertiesStatic();
6513 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
6514 InitElementPropertiesGfxElement();
6516 print_timestamp_time("[init element properties stuff]");
6520 print_timestamp_time("InitGfx");
6523 print_timestamp_time("InitLevelInfo");
6525 InitLevelArtworkInfo();
6526 print_timestamp_time("InitLevelArtworkInfo");
6528 InitOverrideArtwork(); // needs to know current level directory
6529 print_timestamp_time("InitOverrideArtwork");
6531 InitArtworkIdentifier(); // needs to know current level directory
6532 print_timestamp_time("InitArtworkIdentifier");
6534 InitImages(); // needs to know current level directory
6535 print_timestamp_time("InitImages");
6537 InitSound(); // needs to know current level directory
6538 print_timestamp_time("InitSound");
6540 InitMusic(); // needs to know current level directory
6541 print_timestamp_time("InitMusic");
6545 InitGfxBackground();
6551 if (global.autoplay_leveldir)
6556 else if (global.patchtapes_leveldir)
6561 else if (global.convert_leveldir)
6566 else if (global.dumplevel_leveldir)
6571 else if (global.dumptape_leveldir)
6576 else if (global.create_sketch_images_dir)
6578 CreateLevelSketchImages();
6581 else if (global.create_collect_images_dir)
6583 CreateCollectElementImages();
6587 InitNetworkServer();
6589 SetGameStatus(GAME_MODE_MAIN);
6591 FadeSetEnterScreen();
6592 if (!(fading.fade_mode & FADE_TYPE_TRANSFORM))
6593 FadeSkipNextFadeOut();
6595 print_timestamp_time("[post-artwork]");
6597 print_timestamp_done("OpenAll");
6599 if (setup.ask_for_remaining_tapes)
6600 setup.ask_for_uploading_tapes = TRUE;
6605 Debug("internal:path", "SDL_GetBasePath() == '%s'",
6607 Debug("internal:path", "SDL_GetPrefPath() == '%s'",
6608 SDL_GetPrefPath("artsoft", "rocksndiamonds"));
6609 #if defined(PLATFORM_ANDROID)
6610 Debug("internal:path", "SDL_AndroidGetInternalStoragePath() == '%s'",
6611 SDL_AndroidGetInternalStoragePath());
6612 Debug("internal:path", "SDL_AndroidGetExternalStoragePath() == '%s'",
6613 SDL_AndroidGetExternalStoragePath());
6614 Debug("internal:path", "SDL_AndroidGetExternalStorageState() == '%s'",
6615 (SDL_AndroidGetExternalStorageState() &
6616 SDL_ANDROID_EXTERNAL_STORAGE_WRITE ? "writable" :
6617 SDL_AndroidGetExternalStorageState() &
6618 SDL_ANDROID_EXTERNAL_STORAGE_READ ? "readable" : "not available"));
6623 static boolean WaitForApiThreads(void)
6625 DelayCounter thread_delay = { 10000 };
6627 if (program.api_thread_count == 0)
6630 // deactivate global animations (not accessible in game state "loading")
6631 setup.toons = FALSE;
6633 // set game state to "loading" to be able to show busy animation
6634 SetGameStatus(GAME_MODE_LOADING);
6636 ResetDelayCounter(&thread_delay);
6638 // wait for threads to finish (and fail on timeout)
6639 while (program.api_thread_count > 0)
6641 if (DelayReached(&thread_delay))
6643 Error("failed waiting for threads - TIMEOUT");
6648 UPDATE_BUSY_STATE();
6656 void CloseAllAndExit(int exit_value)
6658 WaitForApiThreads();
6663 CloseAudio(); // called after freeing sounds (needed for SDL)
6671 // set a flag to tell the network server thread to quit and wait for it
6672 // using SDL_WaitThread()
6674 // Code used with SDL 1.2:
6675 // if (network.server_thread) // terminate network server
6676 // SDL_KillThread(network.server_thread);
6678 CloseVideoDisplay();
6679 ClosePlatformDependentStuff();
6681 if (exit_value != 0 && !options.execute_command)
6683 // fall back to default level set (current set may have caused an error)
6684 SaveLevelSetup_LastSeries_Deactivate();
6686 // tell user where to find error log file which may contain more details
6687 // (error notification now directly displayed on screen inside R'n'D
6688 // NotifyUserAboutErrorFile(); // currently only works for Windows