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_BACKGROUND_LOADING_INITIAL "background.LOADING_INITIAL"
40 #define CONFIG_TOKEN_BACKGROUND_LOADING "background.LOADING"
42 #define INITIAL_IMG_GLOBAL_BUSY_INITIAL 0
43 #define INITIAL_IMG_GLOBAL_BUSY 1
45 #define NUM_INITIAL_IMAGES_BUSY 2
47 #define INITIAL_IMG_BACKGROUND_LOADING_INITIAL 2
48 #define INITIAL_IMG_BACKGROUND_LOADING 3
50 #define NUM_INITIAL_IMAGES 4
53 static struct FontBitmapInfo font_initial[NUM_INITIAL_FONTS];
54 static struct GraphicInfo image_initial[NUM_INITIAL_IMAGES];
56 static int copy_properties[][5] =
60 EL_BUG_LEFT, EL_BUG_RIGHT,
61 EL_BUG_UP, EL_BUG_DOWN
65 EL_SPACESHIP_LEFT, EL_SPACESHIP_RIGHT,
66 EL_SPACESHIP_UP, EL_SPACESHIP_DOWN
70 EL_BD_BUTTERFLY_LEFT, EL_BD_BUTTERFLY_RIGHT,
71 EL_BD_BUTTERFLY_UP, EL_BD_BUTTERFLY_DOWN
75 EL_BD_FIREFLY_LEFT, EL_BD_FIREFLY_RIGHT,
76 EL_BD_FIREFLY_UP, EL_BD_FIREFLY_DOWN
80 EL_PACMAN_LEFT, EL_PACMAN_RIGHT,
81 EL_PACMAN_UP, EL_PACMAN_DOWN
85 EL_YAMYAM_LEFT, EL_YAMYAM_RIGHT,
86 EL_YAMYAM_UP, EL_YAMYAM_DOWN
90 EL_MOLE_LEFT, EL_MOLE_RIGHT,
91 EL_MOLE_UP, EL_MOLE_DOWN
95 EL_SPRING_LEFT, EL_SPRING_RIGHT,
96 EL_SPRING_LEFT, EL_SPRING_RIGHT, // (to match array size)
105 // forward declaration for internal use
106 static int get_graphic_parameter_value(char *, char *, int);
109 static void SetLoadingBackgroundImage(void)
111 struct GraphicInfo *graphic_info_last = graphic_info;
112 int background_image = (game_status_last_screen == -1 ?
113 INITIAL_IMG_BACKGROUND_LOADING_INITIAL :
114 INITIAL_IMG_BACKGROUND_LOADING);
116 graphic_info = image_initial;
118 SetDrawDeactivationMask(REDRAW_NONE);
119 SetDrawBackgroundMask(REDRAW_ALL);
121 SetWindowBackgroundImage(background_image);
123 graphic_info = graphic_info_last;
126 static void DrawInitAnim(void)
128 struct GraphicInfo *graphic_info_last = graphic_info;
129 int graphic = (game_status_last_screen == -1 ?
130 INITIAL_IMG_GLOBAL_BUSY_INITIAL :
131 INITIAL_IMG_GLOBAL_BUSY);
132 struct MenuPosInfo *busy = (game_status_last_screen == -1 ?
133 &init_last.busy_initial :
135 static unsigned int action_delay = 0;
136 unsigned int action_delay_value = GameFrameDelay;
137 int sync_frame = FrameCounter;
141 // prevent OS (Windows) from complaining about program not responding
144 if (game_status != GAME_MODE_LOADING)
147 if (image_initial[graphic].bitmap == NULL || window == NULL)
150 if (!DelayReached(&action_delay, action_delay_value))
154 busy->x = WIN_XSIZE / 2;
156 busy->y = WIN_YSIZE / 2;
158 x = ALIGNED_TEXT_XPOS(busy);
159 y = ALIGNED_TEXT_YPOS(busy);
161 graphic_info = image_initial;
163 if (sync_frame % image_initial[graphic].anim_delay == 0)
167 int width = graphic_info[graphic].width;
168 int height = graphic_info[graphic].height;
169 int frame = getGraphicAnimationFrame(graphic, sync_frame);
171 ClearRectangleOnBackground(drawto, x, y, width, height);
173 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
174 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height, x, y);
176 BlitBitmap(drawto, window, x, y, width, height, x, y);
179 graphic_info = graphic_info_last;
184 static void DrawProgramInfo(void)
186 int font1_nr = FC_YELLOW;
187 int font2_nr = FC_RED;
188 int font2_height = getFontHeight(font2_nr);
191 int ypos3 = WIN_YSIZE - 20 - font2_height;
193 DrawInitText(getProgramInitString(), ypos1, font1_nr);
194 DrawInitText(setup.internal.program_copyright, ypos2, font2_nr);
195 DrawInitText(setup.internal.program_website, ypos3, font2_nr);
198 static void FreeGadgets(void)
200 FreeLevelEditorGadgets();
207 void InitGadgets(void)
209 static boolean gadgets_initialized = FALSE;
211 if (gadgets_initialized)
214 CreateLevelEditorGadgets();
218 CreateScreenGadgets();
220 InitGadgetsSoundCallback(PlaySoundActivating, PlaySoundSelecting);
222 gadgets_initialized = TRUE;
225 static void InitElementSmallImagesScaledUp(int graphic)
227 struct GraphicInfo *g = &graphic_info[graphic];
229 // create small and game tile sized bitmaps (and scale up, if needed)
230 CreateImageWithSmallImages(graphic, g->scale_up_factor, g->tile_size);
233 static void InitElementSmallImages(void)
235 print_timestamp_init("InitElementSmallImages");
237 static int special_graphics[] =
251 IMG_EDITOR_ELEMENT_BORDER,
252 IMG_EDITOR_ELEMENT_BORDER_INPUT,
253 IMG_EDITOR_CASCADE_LIST,
254 IMG_EDITOR_CASCADE_LIST_ACTIVE,
257 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
258 int num_property_mappings = getImageListPropertyMappingSize();
261 print_timestamp_time("getImageListPropertyMapping/Size");
263 print_timestamp_init("InitElementSmallImagesScaledUp (1)");
264 // initialize normal element images from static configuration
265 for (i = 0; element_to_graphic[i].element > -1; i++)
266 InitElementSmallImagesScaledUp(element_to_graphic[i].graphic);
267 print_timestamp_done("InitElementSmallImagesScaledUp (1)");
269 // initialize special element images from static configuration
270 for (i = 0; element_to_special_graphic[i].element > -1; i++)
271 InitElementSmallImagesScaledUp(element_to_special_graphic[i].graphic);
272 print_timestamp_time("InitElementSmallImagesScaledUp (2)");
274 // initialize element images from dynamic configuration
275 for (i = 0; i < num_property_mappings; i++)
276 if (property_mapping[i].base_index < MAX_NUM_ELEMENTS)
277 InitElementSmallImagesScaledUp(property_mapping[i].artwork_index);
278 print_timestamp_time("InitElementSmallImagesScaledUp (3)");
280 // initialize special non-element images from above list
281 for (i = 0; special_graphics[i] > -1; i++)
282 InitElementSmallImagesScaledUp(special_graphics[i]);
283 print_timestamp_time("InitElementSmallImagesScaledUp (4)");
285 print_timestamp_done("InitElementSmallImages");
288 static void InitScaledImagesScaledUp(int graphic)
290 struct GraphicInfo *g = &graphic_info[graphic];
292 ScaleImage(graphic, g->scale_up_factor);
295 static void InitScaledImages(void)
297 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
298 int num_property_mappings = getImageListPropertyMappingSize();
301 // scale normal images from static configuration, if not already scaled
302 for (i = 0; i < NUM_IMAGE_FILES; i++)
303 InitScaledImagesScaledUp(i);
305 // scale images from dynamic configuration, if not already scaled
306 for (i = 0; i < num_property_mappings; i++)
307 InitScaledImagesScaledUp(property_mapping[i].artwork_index);
310 static void InitBitmapPointers(void)
312 int num_images = getImageListSize();
315 // standard size bitmap may have changed -- update default bitmap pointer
316 for (i = 0; i < num_images; i++)
317 if (graphic_info[i].bitmaps)
318 graphic_info[i].bitmap = graphic_info[i].bitmaps[IMG_BITMAP_STANDARD];
321 void InitImageTextures(void)
323 static int texture_graphics[] =
325 IMG_GFX_REQUEST_BUTTON_TOUCH_YES,
326 IMG_GFX_REQUEST_BUTTON_TOUCH_NO,
327 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM,
328 IMG_GFX_GAME_BUTTON_TOUCH_STOP,
329 IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,
330 IMG_MENU_BUTTON_TOUCH_BACK,
331 IMG_MENU_BUTTON_TOUCH_NEXT,
332 IMG_MENU_BUTTON_TOUCH_BACK2,
333 IMG_MENU_BUTTON_TOUCH_NEXT2,
338 FreeAllImageTextures();
340 for (i = IMG_GLOBAL_BORDER_FIRST; i <= IMG_GLOBAL_BORDER_LAST; i++)
341 CreateImageTextures(i);
343 for (i = 0; i < MAX_NUM_TOONS; i++)
344 CreateImageTextures(IMG_TOON_1 + i);
346 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
348 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
350 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
352 int graphic = global_anim_info[i].graphic[j][k];
354 if (graphic == IMG_UNDEFINED)
357 CreateImageTextures(graphic);
362 for (i = 0; texture_graphics[i] > -1; i++)
363 CreateImageTextures(texture_graphics[i]);
366 static int getFontBitmapID(int font_nr)
370 // (special case: do not use special font for GAME_MODE_LOADING)
371 if (game_status >= GAME_MODE_TITLE_INITIAL &&
372 game_status <= GAME_MODE_PSEUDO_PREVIEW)
373 special = game_status;
374 else if (game_status == GAME_MODE_PSEUDO_TYPENAME)
375 special = GFX_SPECIAL_ARG_MAIN;
376 else if (game_status == GAME_MODE_PSEUDO_TYPENAMES)
377 special = GFX_SPECIAL_ARG_NAMES;
380 return font_info[font_nr].special_bitmap_id[special];
385 static int getFontFromToken(char *token)
387 char *value = getHashEntry(font_token_hash, token);
392 // if font not found, use reliable default value
393 return FONT_INITIAL_1;
396 static void InitFontGraphicInfo(void)
398 static struct FontBitmapInfo *font_bitmap_info = NULL;
399 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
400 int num_property_mappings = getImageListPropertyMappingSize();
401 int num_font_bitmaps = NUM_FONTS;
404 if (graphic_info == NULL) // still at startup phase
406 InitFontInfo(font_initial, NUM_INITIAL_FONTS,
407 getFontBitmapID, getFontFromToken);
412 // ---------- initialize font graphic definitions ----------
414 // always start with reliable default values (normal font graphics)
415 for (i = 0; i < NUM_FONTS; i++)
416 font_info[i].graphic = IMG_FONT_INITIAL_1;
418 // initialize normal font/graphic mapping from static configuration
419 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
421 int font_nr = font_to_graphic[i].font_nr;
422 int special = font_to_graphic[i].special;
423 int graphic = font_to_graphic[i].graphic;
428 font_info[font_nr].graphic = graphic;
431 // always start with reliable default values (special font graphics)
432 for (i = 0; i < NUM_FONTS; i++)
434 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
436 font_info[i].special_graphic[j] = font_info[i].graphic;
437 font_info[i].special_bitmap_id[j] = i;
441 // initialize special font/graphic mapping from static configuration
442 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
444 int font_nr = font_to_graphic[i].font_nr;
445 int special = font_to_graphic[i].special;
446 int graphic = font_to_graphic[i].graphic;
447 int base_graphic = font2baseimg(font_nr);
449 if (IS_SPECIAL_GFX_ARG(special))
451 boolean base_redefined =
452 getImageListEntryFromImageID(base_graphic)->redefined;
453 boolean special_redefined =
454 getImageListEntryFromImageID(graphic)->redefined;
455 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
457 /* if the base font ("font.title_1", for example) has been redefined,
458 but not the special font ("font.title_1.LEVELS", for example), do not
459 use an existing (in this case considered obsolete) special font
460 anymore, but use the automatically determined default font */
461 /* special case: cloned special fonts must be explicitly redefined,
462 but are not automatically redefined by redefining base font */
463 if (base_redefined && !special_redefined && !special_cloned)
466 font_info[font_nr].special_graphic[special] = graphic;
467 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
472 // initialize special font/graphic mapping from dynamic configuration
473 for (i = 0; i < num_property_mappings; i++)
475 int font_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
476 int special = property_mapping[i].ext3_index;
477 int graphic = property_mapping[i].artwork_index;
479 if (font_nr < 0 || font_nr >= NUM_FONTS)
482 if (IS_SPECIAL_GFX_ARG(special))
484 font_info[font_nr].special_graphic[special] = graphic;
485 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
490 /* correct special font/graphic mapping for cloned fonts for downwards
491 compatibility of PREVIEW fonts -- this is only needed for implicit
492 redefinition of special font by redefined base font, and only if other
493 fonts are cloned from this special font (like in the "Zelda" level set) */
494 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
496 int font_nr = font_to_graphic[i].font_nr;
497 int special = font_to_graphic[i].special;
498 int graphic = font_to_graphic[i].graphic;
500 if (IS_SPECIAL_GFX_ARG(special))
502 boolean special_redefined =
503 getImageListEntryFromImageID(graphic)->redefined;
504 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
506 if (special_cloned && !special_redefined)
510 for (j = 0; font_to_graphic[j].font_nr > -1; j++)
512 int font_nr2 = font_to_graphic[j].font_nr;
513 int special2 = font_to_graphic[j].special;
514 int graphic2 = font_to_graphic[j].graphic;
516 if (IS_SPECIAL_GFX_ARG(special2) &&
517 graphic2 == graphic_info[graphic].clone_from)
519 font_info[font_nr].special_graphic[special] =
520 font_info[font_nr2].special_graphic[special2];
521 font_info[font_nr].special_bitmap_id[special] =
522 font_info[font_nr2].special_bitmap_id[special2];
529 // reset non-redefined ".active" font graphics if normal font is redefined
530 // (this different treatment is needed because normal and active fonts are
531 // independently defined ("active" is not a property of font definitions!)
532 for (i = 0; i < NUM_FONTS; i++)
534 int font_nr_base = i;
535 int font_nr_active = FONT_ACTIVE(font_nr_base);
537 // check only those fonts with exist as normal and ".active" variant
538 if (font_nr_base != font_nr_active)
540 int base_graphic = font_info[font_nr_base].graphic;
541 int active_graphic = font_info[font_nr_active].graphic;
542 boolean base_redefined =
543 getImageListEntryFromImageID(base_graphic)->redefined;
544 boolean active_redefined =
545 getImageListEntryFromImageID(active_graphic)->redefined;
547 /* if the base font ("font.menu_1", for example) has been redefined,
548 but not the active font ("font.menu_1.active", for example), do not
549 use an existing (in this case considered obsolete) active font
550 anymore, but use the automatically determined default font */
551 if (base_redefined && !active_redefined)
552 font_info[font_nr_active].graphic = base_graphic;
554 // now also check each "special" font (which may be the same as above)
555 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
557 int base_graphic = font_info[font_nr_base].special_graphic[j];
558 int active_graphic = font_info[font_nr_active].special_graphic[j];
559 boolean base_redefined =
560 getImageListEntryFromImageID(base_graphic)->redefined;
561 boolean active_redefined =
562 getImageListEntryFromImageID(active_graphic)->redefined;
564 // same as above, but check special graphic definitions, for example:
565 // redefined "font.menu_1.MAIN" invalidates "font.menu_1.active.MAIN"
566 if (base_redefined && !active_redefined)
568 font_info[font_nr_active].special_graphic[j] =
569 font_info[font_nr_base].special_graphic[j];
570 font_info[font_nr_active].special_bitmap_id[j] =
571 font_info[font_nr_base].special_bitmap_id[j];
577 // ---------- initialize font bitmap array ----------
579 if (font_bitmap_info != NULL)
580 FreeFontInfo(font_bitmap_info);
583 checked_calloc(num_font_bitmaps * sizeof(struct FontBitmapInfo));
585 // ---------- initialize font bitmap definitions ----------
587 for (i = 0; i < NUM_FONTS; i++)
589 if (i < NUM_INITIAL_FONTS)
591 font_bitmap_info[i] = font_initial[i];
595 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
597 int font_bitmap_id = font_info[i].special_bitmap_id[j];
598 int graphic = font_info[i].special_graphic[j];
600 // set 'graphic_info' for font entries, if uninitialized (guessed)
601 if (graphic_info[graphic].anim_frames < MIN_NUM_CHARS_PER_FONT)
603 graphic_info[graphic].anim_frames = DEFAULT_NUM_CHARS_PER_FONT;
604 graphic_info[graphic].anim_frames_per_line = DEFAULT_NUM_CHARS_PER_LINE;
607 // copy font relevant information from graphics information
608 font_bitmap_info[font_bitmap_id].bitmap = graphic_info[graphic].bitmap;
609 font_bitmap_info[font_bitmap_id].src_x = graphic_info[graphic].src_x;
610 font_bitmap_info[font_bitmap_id].src_y = graphic_info[graphic].src_y;
611 font_bitmap_info[font_bitmap_id].width = graphic_info[graphic].width;
612 font_bitmap_info[font_bitmap_id].height = graphic_info[graphic].height;
614 font_bitmap_info[font_bitmap_id].offset_x =
615 graphic_info[graphic].offset_x;
616 font_bitmap_info[font_bitmap_id].offset_y =
617 graphic_info[graphic].offset_y;
619 font_bitmap_info[font_bitmap_id].draw_xoffset =
620 graphic_info[graphic].draw_xoffset;
621 font_bitmap_info[font_bitmap_id].draw_yoffset =
622 graphic_info[graphic].draw_yoffset;
624 font_bitmap_info[font_bitmap_id].num_chars =
625 graphic_info[graphic].anim_frames;
626 font_bitmap_info[font_bitmap_id].num_chars_per_line =
627 graphic_info[graphic].anim_frames_per_line;
631 InitFontInfo(font_bitmap_info, num_font_bitmaps,
632 getFontBitmapID, getFontFromToken);
635 static void InitGlobalAnimGraphicInfo(void)
637 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
638 int num_property_mappings = getImageListPropertyMappingSize();
641 if (graphic_info == NULL) // still at startup phase
644 // always start with reliable default values (no global animations)
645 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
646 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
647 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
648 global_anim_info[i].graphic[j][k] = IMG_UNDEFINED;
650 // initialize global animation definitions from static configuration
651 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
653 int j = GLOBAL_ANIM_ID_PART_BASE;
654 int k = GFX_SPECIAL_ARG_DEFAULT;
656 global_anim_info[i].graphic[j][k] = IMG_GFX_GLOBAL_ANIM_1 + i;
659 // initialize global animation definitions from dynamic configuration
660 for (i = 0; i < num_property_mappings; i++)
662 int anim_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS - NUM_FONTS;
663 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
664 int special = property_mapping[i].ext3_index;
665 int graphic = property_mapping[i].artwork_index;
667 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
670 // set animation part to base part, if not specified
671 if (!IS_GLOBAL_ANIM_PART(part_nr))
672 part_nr = GLOBAL_ANIM_ID_PART_BASE;
674 // set animation screen to default, if not specified
675 if (!IS_SPECIAL_GFX_ARG(special))
676 special = GFX_SPECIAL_ARG_DEFAULT;
678 global_anim_info[anim_nr].graphic[part_nr][special] = graphic;
680 // fix default value for ".draw_masked" (for backward compatibility)
681 struct GraphicInfo *g = &graphic_info[graphic];
682 struct FileInfo *image = getImageListEntryFromImageID(graphic);
683 char **parameter_raw = image->parameter;
684 int p = GFX_ARG_DRAW_MASKED;
685 int draw_masked = get_graphic_parameter_value(parameter_raw[p],
686 image_config_suffix[p].token,
687 image_config_suffix[p].type);
689 // if ".draw_masked" parameter is undefined, use default value "TRUE"
690 if (draw_masked == ARG_UNDEFINED_VALUE)
691 g->draw_masked = TRUE;
695 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
696 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
697 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
698 if (global_anim_info[i].graphic[j][k] != IMG_UNDEFINED &&
699 graphic_info[global_anim_info[i].graphic[j][k]].bitmap != NULL)
700 Debug("init:InitGlobalAnimGraphicInfo",
701 "anim %d, part %d, mode %d => %d",
702 i, j, k, global_anim_info[i].graphic[j][k]);
706 static void InitGlobalAnimSoundInfo(void)
708 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
709 int num_property_mappings = getSoundListPropertyMappingSize();
712 // always start with reliable default values (no global animation sounds)
713 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
714 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
715 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
716 global_anim_info[i].sound[j][k] = SND_UNDEFINED;
718 // initialize global animation sound definitions from dynamic configuration
719 for (i = 0; i < num_property_mappings; i++)
721 int anim_nr = property_mapping[i].base_index - 2 * MAX_NUM_ELEMENTS;
722 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
723 int special = property_mapping[i].ext3_index;
724 int sound = property_mapping[i].artwork_index;
726 // sound uses control definition; map it to position of graphic (artwork)
727 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
729 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
732 // set animation part to base part, if not specified
733 if (!IS_GLOBAL_ANIM_PART(part_nr))
734 part_nr = GLOBAL_ANIM_ID_PART_BASE;
736 // set animation screen to default, if not specified
737 if (!IS_SPECIAL_GFX_ARG(special))
738 special = GFX_SPECIAL_ARG_DEFAULT;
740 global_anim_info[anim_nr].sound[part_nr][special] = sound;
744 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
745 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
746 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
747 if (global_anim_info[i].sound[j][k] != SND_UNDEFINED)
748 Debug("init:InitGlobalAnimSoundInfo",
749 "anim %d, part %d, mode %d => %d",
750 i, j, k, global_anim_info[i].sound[j][k]);
754 static void InitGlobalAnimMusicInfo(void)
756 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
757 int num_property_mappings = getMusicListPropertyMappingSize();
760 // always start with reliable default values (no global animation music)
761 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
762 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
763 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
764 global_anim_info[i].music[j][k] = MUS_UNDEFINED;
766 // initialize global animation music definitions from dynamic configuration
767 for (i = 0; i < num_property_mappings; i++)
769 int anim_nr = property_mapping[i].base_index - NUM_MUSIC_PREFIXES;
770 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
771 int special = property_mapping[i].ext2_index;
772 int music = property_mapping[i].artwork_index;
774 // music uses control definition; map it to position of graphic (artwork)
775 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
777 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
780 // set animation part to base part, if not specified
781 if (!IS_GLOBAL_ANIM_PART(part_nr))
782 part_nr = GLOBAL_ANIM_ID_PART_BASE;
784 // set animation screen to default, if not specified
785 if (!IS_SPECIAL_GFX_ARG(special))
786 special = GFX_SPECIAL_ARG_DEFAULT;
788 global_anim_info[anim_nr].music[part_nr][special] = music;
792 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
793 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
794 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
795 if (global_anim_info[i].music[j][k] != MUS_UNDEFINED)
796 Debug("init:InitGlobalAnimMusicInfo",
797 "anim %d, part %d, mode %d => %d",
798 i, j, k, global_anim_info[i].music[j][k]);
802 static void InitElementGraphicInfo(void)
804 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
805 int num_property_mappings = getImageListPropertyMappingSize();
808 if (graphic_info == NULL) // still at startup phase
811 // set values to -1 to identify later as "uninitialized" values
812 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
814 for (act = 0; act < NUM_ACTIONS; act++)
816 element_info[i].graphic[act] = -1;
817 element_info[i].crumbled[act] = -1;
819 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
821 element_info[i].direction_graphic[act][dir] = -1;
822 element_info[i].direction_crumbled[act][dir] = -1;
829 // initialize normal element/graphic mapping from static configuration
830 for (i = 0; element_to_graphic[i].element > -1; i++)
832 int element = element_to_graphic[i].element;
833 int action = element_to_graphic[i].action;
834 int direction = element_to_graphic[i].direction;
835 boolean crumbled = element_to_graphic[i].crumbled;
836 int graphic = element_to_graphic[i].graphic;
837 int base_graphic = el2baseimg(element);
839 if (graphic_info[graphic].bitmap == NULL)
842 if ((action > -1 || direction > -1 || crumbled == TRUE) &&
845 boolean base_redefined =
846 getImageListEntryFromImageID(base_graphic)->redefined;
847 boolean act_dir_redefined =
848 getImageListEntryFromImageID(graphic)->redefined;
850 /* if the base graphic ("emerald", for example) has been redefined,
851 but not the action graphic ("emerald.falling", for example), do not
852 use an existing (in this case considered obsolete) action graphic
853 anymore, but use the automatically determined default graphic */
854 if (base_redefined && !act_dir_redefined)
859 action = ACTION_DEFAULT;
864 element_info[element].direction_crumbled[action][direction] = graphic;
866 element_info[element].crumbled[action] = graphic;
871 element_info[element].direction_graphic[action][direction] = graphic;
873 element_info[element].graphic[action] = graphic;
877 // initialize normal element/graphic mapping from dynamic configuration
878 for (i = 0; i < num_property_mappings; i++)
880 int element = property_mapping[i].base_index;
881 int action = property_mapping[i].ext1_index;
882 int direction = property_mapping[i].ext2_index;
883 int special = property_mapping[i].ext3_index;
884 int graphic = property_mapping[i].artwork_index;
885 boolean crumbled = FALSE;
887 if (special == GFX_SPECIAL_ARG_CRUMBLED)
893 if (graphic_info[graphic].bitmap == NULL)
896 if (element >= MAX_NUM_ELEMENTS || special != -1)
900 action = ACTION_DEFAULT;
905 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
906 element_info[element].direction_crumbled[action][dir] = -1;
909 element_info[element].direction_crumbled[action][direction] = graphic;
911 element_info[element].crumbled[action] = graphic;
916 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
917 element_info[element].direction_graphic[action][dir] = -1;
920 element_info[element].direction_graphic[action][direction] = graphic;
922 element_info[element].graphic[action] = graphic;
926 // now copy all graphics that are defined to be cloned from other graphics
927 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
929 int graphic = element_info[i].graphic[ACTION_DEFAULT];
930 int crumbled_like, diggable_like;
935 crumbled_like = graphic_info[graphic].crumbled_like;
936 diggable_like = graphic_info[graphic].diggable_like;
938 if (crumbled_like != -1 && element_info[i].crumbled[ACTION_DEFAULT] == -1)
940 for (act = 0; act < NUM_ACTIONS; act++)
941 element_info[i].crumbled[act] =
942 element_info[crumbled_like].crumbled[act];
943 for (act = 0; act < NUM_ACTIONS; act++)
944 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
945 element_info[i].direction_crumbled[act][dir] =
946 element_info[crumbled_like].direction_crumbled[act][dir];
949 if (diggable_like != -1 && element_info[i].graphic[ACTION_DIGGING] == -1)
951 element_info[i].graphic[ACTION_DIGGING] =
952 element_info[diggable_like].graphic[ACTION_DIGGING];
953 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
954 element_info[i].direction_graphic[ACTION_DIGGING][dir] =
955 element_info[diggable_like].direction_graphic[ACTION_DIGGING][dir];
959 // set hardcoded definitions for some runtime elements without graphic
960 element_info[EL_AMOEBA_TO_DIAMOND].graphic[ACTION_DEFAULT] = IMG_AMOEBA_DEAD;
962 // set hardcoded definitions for some internal elements without graphic
963 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
965 if (IS_EDITOR_CASCADE_INACTIVE(i))
966 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST;
967 else if (IS_EDITOR_CASCADE_ACTIVE(i))
968 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST_ACTIVE;
971 // now set all undefined/invalid graphics to -1 to set to default after it
972 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
974 for (act = 0; act < NUM_ACTIONS; act++)
978 graphic = element_info[i].graphic[act];
979 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
980 element_info[i].graphic[act] = -1;
982 graphic = element_info[i].crumbled[act];
983 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
984 element_info[i].crumbled[act] = -1;
986 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
988 graphic = element_info[i].direction_graphic[act][dir];
989 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
990 element_info[i].direction_graphic[act][dir] = -1;
992 graphic = element_info[i].direction_crumbled[act][dir];
993 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
994 element_info[i].direction_crumbled[act][dir] = -1;
1001 // adjust graphics with 2nd tile for movement according to direction
1002 // (do this before correcting '-1' values to minimize calculations)
1003 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1005 for (act = 0; act < NUM_ACTIONS; act++)
1007 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1009 int graphic = element_info[i].direction_graphic[act][dir];
1010 int move_dir = (act == ACTION_FALLING ? MV_BIT_DOWN : dir);
1012 if (act == ACTION_FALLING) // special case
1013 graphic = element_info[i].graphic[act];
1015 if (graphic != -1 &&
1016 graphic_info[graphic].double_movement &&
1017 graphic_info[graphic].swap_double_tiles != 0)
1019 struct GraphicInfo *g = &graphic_info[graphic];
1020 int src_x_front = g->src_x;
1021 int src_y_front = g->src_y;
1022 int src_x_back = g->src_x + g->offset2_x;
1023 int src_y_back = g->src_y + g->offset2_y;
1024 boolean frames_are_ordered_diagonally = (g->offset_x != 0 &&
1026 boolean front_is_left_or_upper = (src_x_front < src_x_back ||
1027 src_y_front < src_y_back);
1028 boolean swap_movement_tiles_always = (g->swap_double_tiles == 1);
1029 boolean swap_movement_tiles_autodetected =
1030 (!frames_are_ordered_diagonally &&
1031 ((move_dir == MV_BIT_LEFT && !front_is_left_or_upper) ||
1032 (move_dir == MV_BIT_UP && !front_is_left_or_upper) ||
1033 (move_dir == MV_BIT_RIGHT && front_is_left_or_upper) ||
1034 (move_dir == MV_BIT_DOWN && front_is_left_or_upper)));
1036 // swap frontside and backside graphic tile coordinates, if needed
1037 if (swap_movement_tiles_always || swap_movement_tiles_autodetected)
1039 // get current (wrong) backside tile coordinates
1040 getGraphicSourceXY(graphic, 0, &src_x_back, &src_y_back, TRUE);
1042 // set frontside tile coordinates to backside tile coordinates
1043 g->src_x = src_x_back;
1044 g->src_y = src_y_back;
1046 // invert tile offset to point to new backside tile coordinates
1050 // do not swap front and backside tiles again after correction
1051 g->swap_double_tiles = 0;
1058 UPDATE_BUSY_STATE();
1060 // now set all '-1' values to element specific default values
1061 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1063 int default_graphic = element_info[i].graphic[ACTION_DEFAULT];
1064 int default_crumbled = element_info[i].crumbled[ACTION_DEFAULT];
1065 int default_direction_graphic[NUM_DIRECTIONS_FULL];
1066 int default_direction_crumbled[NUM_DIRECTIONS_FULL];
1068 if (default_graphic == -1)
1069 default_graphic = IMG_UNKNOWN;
1071 if (default_crumbled == -1)
1072 default_crumbled = default_graphic;
1074 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1076 default_direction_graphic[dir] =
1077 element_info[i].direction_graphic[ACTION_DEFAULT][dir];
1078 default_direction_crumbled[dir] =
1079 element_info[i].direction_crumbled[ACTION_DEFAULT][dir];
1081 if (default_direction_graphic[dir] == -1)
1082 default_direction_graphic[dir] = default_graphic;
1084 if (default_direction_crumbled[dir] == -1)
1085 default_direction_crumbled[dir] = default_direction_graphic[dir];
1088 for (act = 0; act < NUM_ACTIONS; act++)
1090 boolean act_remove = ((IS_DIGGABLE(i) && act == ACTION_DIGGING) ||
1091 (IS_SNAPPABLE(i) && act == ACTION_SNAPPING) ||
1092 (IS_COLLECTIBLE(i) && act == ACTION_COLLECTING));
1093 boolean act_turning = (act == ACTION_TURNING_FROM_LEFT ||
1094 act == ACTION_TURNING_FROM_RIGHT ||
1095 act == ACTION_TURNING_FROM_UP ||
1096 act == ACTION_TURNING_FROM_DOWN);
1098 // generic default action graphic (defined by "[default]" directive)
1099 int default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1100 int default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1101 int default_remove_graphic = IMG_EMPTY;
1103 if (act_remove && default_action_graphic != -1)
1104 default_remove_graphic = default_action_graphic;
1106 // look for special default action graphic (classic game specific)
1107 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].graphic[act] != -1)
1108 default_action_graphic = element_info[EL_BD_DEFAULT].graphic[act];
1109 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].graphic[act] != -1)
1110 default_action_graphic = element_info[EL_SP_DEFAULT].graphic[act];
1111 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].graphic[act] != -1)
1112 default_action_graphic = element_info[EL_SB_DEFAULT].graphic[act];
1113 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].graphic[act] != -1)
1114 default_action_graphic = element_info[EL_MM_DEFAULT].graphic[act];
1116 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].crumbled[act] != -1)
1117 default_action_crumbled = element_info[EL_BD_DEFAULT].crumbled[act];
1118 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].crumbled[act] != -1)
1119 default_action_crumbled = element_info[EL_SP_DEFAULT].crumbled[act];
1120 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].crumbled[act] != -1)
1121 default_action_crumbled = element_info[EL_SB_DEFAULT].crumbled[act];
1122 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].crumbled[act] != -1)
1123 default_action_crumbled = element_info[EL_MM_DEFAULT].crumbled[act];
1125 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
1126 // !!! make this better !!!
1127 if (i == EL_EMPTY_SPACE)
1129 default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1130 default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1133 if (default_action_graphic == -1)
1134 default_action_graphic = default_graphic;
1136 if (default_action_crumbled == -1)
1137 default_action_crumbled = default_action_graphic;
1139 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1141 // use action graphic as the default direction graphic, if undefined
1142 int default_action_direction_graphic = element_info[i].graphic[act];
1143 int default_action_direction_crumbled = element_info[i].crumbled[act];
1145 // no graphic for current action -- use default direction graphic
1146 if (default_action_direction_graphic == -1)
1147 default_action_direction_graphic =
1148 (act_remove ? default_remove_graphic :
1150 element_info[i].direction_graphic[ACTION_TURNING][dir] :
1151 default_action_graphic != default_graphic ?
1152 default_action_graphic :
1153 default_direction_graphic[dir]);
1155 if (element_info[i].direction_graphic[act][dir] == -1)
1156 element_info[i].direction_graphic[act][dir] =
1157 default_action_direction_graphic;
1159 if (default_action_direction_crumbled == -1)
1160 default_action_direction_crumbled =
1161 element_info[i].direction_graphic[act][dir];
1163 if (element_info[i].direction_crumbled[act][dir] == -1)
1164 element_info[i].direction_crumbled[act][dir] =
1165 default_action_direction_crumbled;
1168 // no graphic for this specific action -- use default action graphic
1169 if (element_info[i].graphic[act] == -1)
1170 element_info[i].graphic[act] =
1171 (act_remove ? default_remove_graphic :
1172 act_turning ? element_info[i].graphic[ACTION_TURNING] :
1173 default_action_graphic);
1175 if (element_info[i].crumbled[act] == -1)
1176 element_info[i].crumbled[act] = element_info[i].graphic[act];
1180 UPDATE_BUSY_STATE();
1183 static void InitElementSpecialGraphicInfo(void)
1185 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
1186 int num_property_mappings = getImageListPropertyMappingSize();
1189 // always start with reliable default values
1190 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1191 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1192 element_info[i].special_graphic[j] =
1193 element_info[i].graphic[ACTION_DEFAULT];
1195 // initialize special element/graphic mapping from static configuration
1196 for (i = 0; element_to_special_graphic[i].element > -1; i++)
1198 int element = element_to_special_graphic[i].element;
1199 int special = element_to_special_graphic[i].special;
1200 int graphic = element_to_special_graphic[i].graphic;
1201 int base_graphic = el2baseimg(element);
1202 boolean base_redefined =
1203 getImageListEntryFromImageID(base_graphic)->redefined;
1204 boolean special_redefined =
1205 getImageListEntryFromImageID(graphic)->redefined;
1207 /* if the base graphic ("emerald", for example) has been redefined,
1208 but not the special graphic ("emerald.EDITOR", for example), do not
1209 use an existing (in this case considered obsolete) special graphic
1210 anymore, but use the automatically created (down-scaled) graphic */
1211 if (base_redefined && !special_redefined)
1214 element_info[element].special_graphic[special] = graphic;
1217 // initialize special element/graphic mapping from dynamic configuration
1218 for (i = 0; i < num_property_mappings; i++)
1220 int element = property_mapping[i].base_index;
1221 int action = property_mapping[i].ext1_index;
1222 int direction = property_mapping[i].ext2_index;
1223 int special = property_mapping[i].ext3_index;
1224 int graphic = property_mapping[i].artwork_index;
1226 // for action ".active", replace element with active element, if exists
1227 if (action == ACTION_ACTIVE && element != ELEMENT_ACTIVE(element))
1229 element = ELEMENT_ACTIVE(element);
1233 if (element >= MAX_NUM_ELEMENTS)
1236 // do not change special graphic if action or direction was specified
1237 if (action != -1 || direction != -1)
1240 if (IS_SPECIAL_GFX_ARG(special))
1241 element_info[element].special_graphic[special] = graphic;
1244 // now set all undefined/invalid graphics to default
1245 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1246 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1247 if (graphic_info[element_info[i].special_graphic[j]].bitmap == NULL)
1248 element_info[i].special_graphic[j] =
1249 element_info[i].graphic[ACTION_DEFAULT];
1252 static int get_graphic_parameter_value(char *value_raw, char *suffix, int type)
1254 if (type != TYPE_ELEMENT && type != TYPE_GRAPHIC)
1255 return get_parameter_value(value_raw, suffix, type);
1257 if (strEqual(value_raw, ARG_UNDEFINED))
1258 return ARG_UNDEFINED_VALUE;
1260 if (type == TYPE_ELEMENT)
1262 char *value = getHashEntry(element_token_hash, value_raw);
1267 Warn("error found in config file:");
1268 Warn("- config file: '%s'", getImageConfigFilename());
1269 Warn("error: invalid element token '%s'", value_raw);
1270 Warn("custom graphic rejected for this element/action");
1271 Warn("fallback done to undefined element for this graphic");
1275 return (value != NULL ? atoi(value) : EL_UNDEFINED);
1277 else if (type == TYPE_GRAPHIC)
1279 char *value = getHashEntry(graphic_token_hash, value_raw);
1280 int fallback_graphic = IMG_CHAR_EXCLAM;
1285 Warn("error found in config file:");
1286 Warn("- config file: '%s'", getImageConfigFilename());
1287 Warn("error: invalid graphic token '%s'", value_raw);
1288 Warn("custom graphic rejected for this element/action");
1289 Warn("fallback done to 'char_exclam' for this graphic");
1293 return (value != NULL ? atoi(value) : fallback_graphic);
1299 static int get_scaled_graphic_width(int graphic)
1301 int original_width = getOriginalImageWidthFromImageID(graphic);
1302 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1304 return original_width * scale_up_factor;
1307 static int get_scaled_graphic_height(int graphic)
1309 int original_height = getOriginalImageHeightFromImageID(graphic);
1310 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1312 return original_height * scale_up_factor;
1315 static void set_graphic_parameters_ext(int graphic, int *parameter,
1316 Bitmap **src_bitmaps)
1318 struct GraphicInfo *g = &graphic_info[graphic];
1319 Bitmap *src_bitmap = (src_bitmaps ? src_bitmaps[IMG_BITMAP_STANDARD] : NULL);
1320 int anim_frames_per_row = 1, anim_frames_per_col = 1;
1321 int anim_frames_per_line = 1;
1323 // always start with reliable default values
1324 g->src_image_width = 0;
1325 g->src_image_height = 0;
1328 g->width = TILEX; // default for element graphics
1329 g->height = TILEY; // default for element graphics
1330 g->offset_x = 0; // one or both of these values ...
1331 g->offset_y = 0; // ... will be corrected later
1332 g->offset2_x = 0; // one or both of these values ...
1333 g->offset2_y = 0; // ... will be corrected later
1334 g->swap_double_tiles = -1; // auto-detect tile swapping
1335 g->crumbled_like = -1; // do not use clone element
1336 g->diggable_like = -1; // do not use clone element
1337 g->border_size = TILEX / 8; // "CRUMBLED" border size
1338 g->scale_up_factor = 1; // default: no scaling up
1339 g->tile_size = TILESIZE; // default: standard tile size
1340 g->clone_from = -1; // do not use clone graphic
1341 g->init_delay_fixed = 0;
1342 g->init_delay_random = 0;
1343 g->init_delay_action = -1;
1344 g->anim_delay_fixed = 0;
1345 g->anim_delay_random = 0;
1346 g->anim_delay_action = -1;
1347 g->post_delay_fixed = 0;
1348 g->post_delay_random = 0;
1349 g->post_delay_action = -1;
1350 g->init_event = ANIM_EVENT_UNDEFINED;
1351 g->anim_event = ANIM_EVENT_UNDEFINED;
1352 g->init_event_action = -1;
1353 g->anim_event_action = -1;
1354 g->draw_masked = FALSE;
1356 g->fade_mode = FADE_MODE_DEFAULT;
1360 g->auto_delay_unit = AUTO_DELAY_UNIT_DEFAULT;
1361 g->align = ALIGN_CENTER; // default for title screens
1362 g->valign = VALIGN_MIDDLE; // default for title screens
1363 g->sort_priority = 0; // default for title screens
1365 g->style = STYLE_DEFAULT;
1367 g->bitmaps = src_bitmaps;
1368 g->bitmap = src_bitmap;
1370 // optional zoom factor for scaling up the image to a larger size
1371 if (parameter[GFX_ARG_SCALE_UP_FACTOR] != ARG_UNDEFINED_VALUE)
1372 g->scale_up_factor = parameter[GFX_ARG_SCALE_UP_FACTOR];
1373 if (g->scale_up_factor < 1)
1374 g->scale_up_factor = 1; // no scaling
1376 // optional tile size for using non-standard image size
1377 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1379 g->tile_size = parameter[GFX_ARG_TILE_SIZE];
1382 // CHECK: should tile sizes less than standard tile size be allowed?
1383 if (g->tile_size < TILESIZE)
1384 g->tile_size = TILESIZE; // standard tile size
1387 // when setting tile size, also set width and height accordingly
1388 g->width = g->tile_size;
1389 g->height = g->tile_size;
1392 if (g->use_image_size)
1394 // set new default bitmap size (with scaling, but without small images)
1395 g->width = get_scaled_graphic_width(graphic);
1396 g->height = get_scaled_graphic_height(graphic);
1399 // optional width and height of each animation frame
1400 if (parameter[GFX_ARG_WIDTH] != ARG_UNDEFINED_VALUE)
1401 g->width = parameter[GFX_ARG_WIDTH];
1402 if (parameter[GFX_ARG_HEIGHT] != ARG_UNDEFINED_VALUE)
1403 g->height = parameter[GFX_ARG_HEIGHT];
1405 // optional x and y tile position of animation frame sequence
1406 if (parameter[GFX_ARG_XPOS] != ARG_UNDEFINED_VALUE)
1407 g->src_x = parameter[GFX_ARG_XPOS] * g->width;
1408 if (parameter[GFX_ARG_YPOS] != ARG_UNDEFINED_VALUE)
1409 g->src_y = parameter[GFX_ARG_YPOS] * g->height;
1411 // optional x and y pixel position of animation frame sequence
1412 if (parameter[GFX_ARG_X] != ARG_UNDEFINED_VALUE)
1413 g->src_x = parameter[GFX_ARG_X];
1414 if (parameter[GFX_ARG_Y] != ARG_UNDEFINED_VALUE)
1415 g->src_y = parameter[GFX_ARG_Y];
1422 Warn("invalid value %d for '%s.width' (fallback done to %d)",
1423 g->width, getTokenFromImageID(graphic), TILEX);
1426 g->width = TILEX; // will be checked to be inside bitmap later
1432 Warn("invalid value %d for '%s.height' (fallback done to %d)",
1433 g->height, getTokenFromImageID(graphic), TILEY);
1436 g->height = TILEY; // will be checked to be inside bitmap later
1442 // get final bitmap size (with scaling, but without small images)
1443 int src_image_width = get_scaled_graphic_width(graphic);
1444 int src_image_height = get_scaled_graphic_height(graphic);
1446 if (src_image_width == 0 || src_image_height == 0)
1448 // only happens when loaded outside artwork system (like "global.busy")
1449 src_image_width = src_bitmap->width;
1450 src_image_height = src_bitmap->height;
1453 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1455 anim_frames_per_row = MAX(1, src_image_width / g->tile_size);
1456 anim_frames_per_col = MAX(1, src_image_height / g->tile_size);
1460 anim_frames_per_row = MAX(1, src_image_width / g->width);
1461 anim_frames_per_col = MAX(1, src_image_height / g->height);
1464 g->src_image_width = src_image_width;
1465 g->src_image_height = src_image_height;
1468 // correct x or y offset dependent of vertical or horizontal frame order
1469 if (parameter[GFX_ARG_VERTICAL]) // frames are ordered vertically
1471 g->offset_y = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1472 parameter[GFX_ARG_OFFSET] : g->height);
1473 anim_frames_per_line = anim_frames_per_col;
1475 else // frames are ordered horizontally
1477 g->offset_x = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1478 parameter[GFX_ARG_OFFSET] : g->width);
1479 anim_frames_per_line = anim_frames_per_row;
1482 // optionally, the x and y offset of frames can be specified directly
1483 if (parameter[GFX_ARG_XOFFSET] != ARG_UNDEFINED_VALUE)
1484 g->offset_x = parameter[GFX_ARG_XOFFSET];
1485 if (parameter[GFX_ARG_YOFFSET] != ARG_UNDEFINED_VALUE)
1486 g->offset_y = parameter[GFX_ARG_YOFFSET];
1488 // optionally, moving animations may have separate start and end graphics
1489 g->double_movement = parameter[GFX_ARG_2ND_MOVEMENT_TILE];
1491 if (parameter[GFX_ARG_2ND_VERTICAL] == ARG_UNDEFINED_VALUE)
1492 parameter[GFX_ARG_2ND_VERTICAL] = !parameter[GFX_ARG_VERTICAL];
1494 // correct x or y offset2 dependent of vertical or horizontal frame order
1495 if (parameter[GFX_ARG_2ND_VERTICAL]) // frames are ordered vertically
1496 g->offset2_y = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1497 parameter[GFX_ARG_2ND_OFFSET] : g->height);
1498 else // frames are ordered horizontally
1499 g->offset2_x = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1500 parameter[GFX_ARG_2ND_OFFSET] : g->width);
1502 // optionally, the x and y offset of 2nd graphic can be specified directly
1503 if (parameter[GFX_ARG_2ND_XOFFSET] != ARG_UNDEFINED_VALUE)
1504 g->offset2_x = parameter[GFX_ARG_2ND_XOFFSET];
1505 if (parameter[GFX_ARG_2ND_YOFFSET] != ARG_UNDEFINED_VALUE)
1506 g->offset2_y = parameter[GFX_ARG_2ND_YOFFSET];
1508 // optionally, the second movement tile can be specified as start tile
1509 if (parameter[GFX_ARG_2ND_SWAP_TILES] != ARG_UNDEFINED_VALUE)
1510 g->swap_double_tiles= parameter[GFX_ARG_2ND_SWAP_TILES];
1512 // automatically determine correct number of frames, if not defined
1513 if (parameter[GFX_ARG_FRAMES] != ARG_UNDEFINED_VALUE)
1514 g->anim_frames = parameter[GFX_ARG_FRAMES];
1515 else if (parameter[GFX_ARG_XPOS] == 0 && !parameter[GFX_ARG_VERTICAL])
1516 g->anim_frames = anim_frames_per_row;
1517 else if (parameter[GFX_ARG_YPOS] == 0 && parameter[GFX_ARG_VERTICAL])
1518 g->anim_frames = anim_frames_per_col;
1522 if (g->anim_frames < 1) // frames must be at least 1
1525 g->anim_frames_per_line =
1526 (parameter[GFX_ARG_FRAMES_PER_LINE] != ARG_UNDEFINED_VALUE ?
1527 parameter[GFX_ARG_FRAMES_PER_LINE] : anim_frames_per_line);
1529 g->anim_delay = parameter[GFX_ARG_DELAY];
1530 if (g->anim_delay < 1) // delay must be at least 1
1533 g->anim_mode = parameter[GFX_ARG_ANIM_MODE];
1535 // automatically determine correct start frame, if not defined
1536 if (parameter[GFX_ARG_START_FRAME] == ARG_UNDEFINED_VALUE)
1537 g->anim_start_frame = 0;
1538 else if (g->anim_mode & ANIM_REVERSE)
1539 g->anim_start_frame = g->anim_frames - parameter[GFX_ARG_START_FRAME] - 1;
1541 g->anim_start_frame = parameter[GFX_ARG_START_FRAME];
1543 // animation synchronized with global frame counter, not move position
1544 g->anim_global_sync = parameter[GFX_ARG_GLOBAL_SYNC];
1546 // optional element for cloning crumble graphics
1547 if (parameter[GFX_ARG_CRUMBLED_LIKE] != ARG_UNDEFINED_VALUE)
1548 g->crumbled_like = parameter[GFX_ARG_CRUMBLED_LIKE];
1550 // optional element for cloning digging graphics
1551 if (parameter[GFX_ARG_DIGGABLE_LIKE] != ARG_UNDEFINED_VALUE)
1552 g->diggable_like = parameter[GFX_ARG_DIGGABLE_LIKE];
1554 // optional border size for "crumbling" diggable graphics
1555 if (parameter[GFX_ARG_BORDER_SIZE] != ARG_UNDEFINED_VALUE)
1556 g->border_size = parameter[GFX_ARG_BORDER_SIZE];
1558 // used for global animations and player "boring" and "sleeping" actions
1559 if (parameter[GFX_ARG_INIT_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1560 g->init_delay_fixed = parameter[GFX_ARG_INIT_DELAY_FIXED];
1561 if (parameter[GFX_ARG_INIT_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1562 g->init_delay_random = parameter[GFX_ARG_INIT_DELAY_RANDOM];
1563 if (parameter[GFX_ARG_ANIM_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1564 g->anim_delay_fixed = parameter[GFX_ARG_ANIM_DELAY_FIXED];
1565 if (parameter[GFX_ARG_ANIM_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1566 g->anim_delay_random = parameter[GFX_ARG_ANIM_DELAY_RANDOM];
1567 if (parameter[GFX_ARG_POST_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1568 g->post_delay_fixed = parameter[GFX_ARG_POST_DELAY_FIXED];
1569 if (parameter[GFX_ARG_POST_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1570 g->post_delay_random = parameter[GFX_ARG_POST_DELAY_RANDOM];
1572 // used for global animations
1573 if (parameter[GFX_ARG_INIT_EVENT] != ARG_UNDEFINED_VALUE)
1574 g->init_event = parameter[GFX_ARG_INIT_EVENT];
1575 if (parameter[GFX_ARG_ANIM_EVENT] != ARG_UNDEFINED_VALUE)
1576 g->anim_event = parameter[GFX_ARG_ANIM_EVENT];
1577 if (parameter[GFX_ARG_INIT_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1578 g->init_event_action = parameter[GFX_ARG_INIT_EVENT_ACTION];
1579 if (parameter[GFX_ARG_ANIM_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1580 g->anim_event_action = parameter[GFX_ARG_ANIM_EVENT_ACTION];
1581 if (parameter[GFX_ARG_INIT_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1582 g->init_delay_action = parameter[GFX_ARG_INIT_DELAY_ACTION];
1583 if (parameter[GFX_ARG_ANIM_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1584 g->anim_delay_action = parameter[GFX_ARG_ANIM_DELAY_ACTION];
1585 if (parameter[GFX_ARG_POST_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1586 g->post_delay_action = parameter[GFX_ARG_POST_DELAY_ACTION];
1588 // used for toon animations and global animations
1589 g->step_offset = parameter[GFX_ARG_STEP_OFFSET];
1590 g->step_xoffset = parameter[GFX_ARG_STEP_XOFFSET];
1591 g->step_yoffset = parameter[GFX_ARG_STEP_YOFFSET];
1592 g->step_delay = parameter[GFX_ARG_STEP_DELAY];
1593 g->direction = parameter[GFX_ARG_DIRECTION];
1594 g->position = parameter[GFX_ARG_POSITION];
1595 g->x = parameter[GFX_ARG_X]; // (may be uninitialized,
1596 g->y = parameter[GFX_ARG_Y]; // unlike src_x and src_y)
1598 if (g->step_delay < 1) // delay must be at least 1
1601 // this is only used for drawing font characters
1602 g->draw_xoffset = parameter[GFX_ARG_DRAW_XOFFSET];
1603 g->draw_yoffset = parameter[GFX_ARG_DRAW_YOFFSET];
1605 // use a different default value for global animations and toons
1606 if ((graphic >= IMG_GFX_GLOBAL_ANIM_1 && graphic <= IMG_GFX_GLOBAL_ANIM_8) ||
1607 (graphic >= IMG_TOON_1 && graphic <= IMG_TOON_20))
1608 g->draw_masked = TRUE;
1610 // this is used for drawing envelopes, global animations and toons
1611 if (parameter[GFX_ARG_DRAW_MASKED] != ARG_UNDEFINED_VALUE)
1612 g->draw_masked = parameter[GFX_ARG_DRAW_MASKED];
1614 // used for toon animations and global animations
1615 if (parameter[GFX_ARG_DRAW_ORDER] != ARG_UNDEFINED_VALUE)
1616 g->draw_order = parameter[GFX_ARG_DRAW_ORDER];
1618 // optional graphic for cloning all graphics settings
1619 if (parameter[GFX_ARG_CLONE_FROM] != ARG_UNDEFINED_VALUE)
1620 g->clone_from = parameter[GFX_ARG_CLONE_FROM];
1622 // optional settings for drawing title screens and title messages
1623 if (parameter[GFX_ARG_FADE_MODE] != ARG_UNDEFINED_VALUE)
1624 g->fade_mode = parameter[GFX_ARG_FADE_MODE];
1625 if (parameter[GFX_ARG_FADE_DELAY] != ARG_UNDEFINED_VALUE)
1626 g->fade_delay = parameter[GFX_ARG_FADE_DELAY];
1627 if (parameter[GFX_ARG_POST_DELAY] != ARG_UNDEFINED_VALUE)
1628 g->post_delay = parameter[GFX_ARG_POST_DELAY];
1629 if (parameter[GFX_ARG_AUTO_DELAY] != ARG_UNDEFINED_VALUE)
1630 g->auto_delay = parameter[GFX_ARG_AUTO_DELAY];
1631 if (parameter[GFX_ARG_AUTO_DELAY_UNIT] != ARG_UNDEFINED_VALUE)
1632 g->auto_delay_unit = parameter[GFX_ARG_AUTO_DELAY_UNIT];
1633 if (parameter[GFX_ARG_ALIGN] != ARG_UNDEFINED_VALUE)
1634 g->align = parameter[GFX_ARG_ALIGN];
1635 if (parameter[GFX_ARG_VALIGN] != ARG_UNDEFINED_VALUE)
1636 g->valign = parameter[GFX_ARG_VALIGN];
1637 if (parameter[GFX_ARG_SORT_PRIORITY] != ARG_UNDEFINED_VALUE)
1638 g->sort_priority = parameter[GFX_ARG_SORT_PRIORITY];
1640 if (parameter[GFX_ARG_CLASS] != ARG_UNDEFINED_VALUE)
1641 g->class = parameter[GFX_ARG_CLASS];
1642 if (parameter[GFX_ARG_STYLE] != ARG_UNDEFINED_VALUE)
1643 g->style = parameter[GFX_ARG_STYLE];
1645 // this is only used for drawing menu buttons and text
1646 g->active_xoffset = parameter[GFX_ARG_ACTIVE_XOFFSET];
1647 g->active_yoffset = parameter[GFX_ARG_ACTIVE_YOFFSET];
1648 g->pressed_xoffset = parameter[GFX_ARG_PRESSED_XOFFSET];
1649 g->pressed_yoffset = parameter[GFX_ARG_PRESSED_YOFFSET];
1652 static void set_graphic_parameters(int graphic)
1654 struct FileInfo *image = getImageListEntryFromImageID(graphic);
1655 char **parameter_raw = image->parameter;
1656 Bitmap **src_bitmaps = getBitmapsFromImageID(graphic);
1657 int parameter[NUM_GFX_ARGS];
1660 // if fallback to default artwork is done, also use the default parameters
1661 if (image->fallback_to_default)
1662 parameter_raw = image->default_parameter;
1664 // get integer values from string parameters
1665 for (i = 0; i < NUM_GFX_ARGS; i++)
1666 parameter[i] = get_graphic_parameter_value(parameter_raw[i],
1667 image_config_suffix[i].token,
1668 image_config_suffix[i].type);
1670 set_graphic_parameters_ext(graphic, parameter, src_bitmaps);
1672 UPDATE_BUSY_STATE();
1675 static void set_cloned_graphic_parameters(int graphic)
1677 int fallback_graphic = IMG_CHAR_EXCLAM;
1678 int max_num_images = getImageListSize();
1679 int clone_graphic = graphic_info[graphic].clone_from;
1680 int num_references_followed = 1;
1682 while (graphic_info[clone_graphic].clone_from != -1 &&
1683 num_references_followed < max_num_images)
1685 clone_graphic = graphic_info[clone_graphic].clone_from;
1687 num_references_followed++;
1690 if (num_references_followed >= max_num_images)
1693 Warn("error found in config file:");
1694 Warn("- config file: '%s'", getImageConfigFilename());
1695 Warn("- config token: '%s'", getTokenFromImageID(graphic));
1696 Warn("error: loop discovered when resolving cloned graphics");
1697 Warn("custom graphic rejected for this element/action");
1699 if (graphic == fallback_graphic)
1700 Fail("no fallback graphic available");
1702 Warn("fallback done to 'char_exclam' for this graphic");
1705 graphic_info[graphic] = graphic_info[fallback_graphic];
1709 graphic_info[graphic] = graphic_info[clone_graphic];
1710 graphic_info[graphic].clone_from = clone_graphic;
1714 static void InitGraphicInfo(void)
1716 int fallback_graphic = IMG_CHAR_EXCLAM;
1717 int num_images = getImageListSize();
1720 // use image size as default values for width and height for these images
1721 static int full_size_graphics[] =
1724 IMG_GLOBAL_BORDER_MAIN,
1725 IMG_GLOBAL_BORDER_SCORES,
1726 IMG_GLOBAL_BORDER_EDITOR,
1727 IMG_GLOBAL_BORDER_PLAYING,
1730 IMG_BACKGROUND_ENVELOPE_1,
1731 IMG_BACKGROUND_ENVELOPE_2,
1732 IMG_BACKGROUND_ENVELOPE_3,
1733 IMG_BACKGROUND_ENVELOPE_4,
1734 IMG_BACKGROUND_REQUEST,
1737 IMG_BACKGROUND_LOADING_INITIAL,
1738 IMG_BACKGROUND_LOADING,
1739 IMG_BACKGROUND_TITLE_INITIAL,
1740 IMG_BACKGROUND_TITLE,
1741 IMG_BACKGROUND_MAIN,
1742 IMG_BACKGROUND_NAMES,
1743 IMG_BACKGROUND_LEVELS,
1744 IMG_BACKGROUND_LEVELNR,
1745 IMG_BACKGROUND_SCORES,
1746 IMG_BACKGROUND_SCOREINFO,
1747 IMG_BACKGROUND_EDITOR,
1748 IMG_BACKGROUND_INFO,
1749 IMG_BACKGROUND_INFO_ELEMENTS,
1750 IMG_BACKGROUND_INFO_MUSIC,
1751 IMG_BACKGROUND_INFO_CREDITS,
1752 IMG_BACKGROUND_INFO_PROGRAM,
1753 IMG_BACKGROUND_INFO_VERSION,
1754 IMG_BACKGROUND_INFO_LEVELSET,
1755 IMG_BACKGROUND_SETUP,
1756 IMG_BACKGROUND_PLAYING,
1757 IMG_BACKGROUND_DOOR,
1758 IMG_BACKGROUND_TAPE,
1759 IMG_BACKGROUND_PANEL,
1760 IMG_BACKGROUND_PALETTE,
1761 IMG_BACKGROUND_TOOLBOX,
1763 IMG_TITLESCREEN_INITIAL_1,
1764 IMG_TITLESCREEN_INITIAL_2,
1765 IMG_TITLESCREEN_INITIAL_3,
1766 IMG_TITLESCREEN_INITIAL_4,
1767 IMG_TITLESCREEN_INITIAL_5,
1774 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_1,
1775 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_2,
1776 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_3,
1777 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_4,
1778 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_5,
1779 IMG_BACKGROUND_TITLEMESSAGE_1,
1780 IMG_BACKGROUND_TITLEMESSAGE_2,
1781 IMG_BACKGROUND_TITLEMESSAGE_3,
1782 IMG_BACKGROUND_TITLEMESSAGE_4,
1783 IMG_BACKGROUND_TITLEMESSAGE_5,
1788 FreeGlobalAnimEventInfo();
1790 checked_free(graphic_info);
1792 graphic_info = checked_calloc(num_images * sizeof(struct GraphicInfo));
1794 // initialize "use_image_size" flag with default value
1795 for (i = 0; i < num_images; i++)
1796 graphic_info[i].use_image_size = FALSE;
1798 // initialize "use_image_size" flag from static configuration above
1799 for (i = 0; full_size_graphics[i] != -1; i++)
1800 graphic_info[full_size_graphics[i]].use_image_size = TRUE;
1802 // first set all graphic paramaters ...
1803 for (i = 0; i < num_images; i++)
1804 set_graphic_parameters(i);
1806 // ... then copy these parameters for cloned graphics
1807 for (i = 0; i < num_images; i++)
1808 if (graphic_info[i].clone_from != -1)
1809 set_cloned_graphic_parameters(i);
1811 for (i = 0; i < num_images; i++)
1813 Bitmap *src_bitmap = graphic_info[i].bitmap;
1817 int src_bitmap_width, src_bitmap_height;
1819 // now check if no animation frames are outside of the loaded image
1821 if (graphic_info[i].bitmap == NULL)
1822 continue; // skip check for optional images that are undefined
1824 // get image size (this can differ from the standard element tile size!)
1825 width = graphic_info[i].width;
1826 height = graphic_info[i].height;
1828 // get final bitmap size (with scaling, but without small images)
1829 src_bitmap_width = graphic_info[i].src_image_width;
1830 src_bitmap_height = graphic_info[i].src_image_height;
1832 // check if first animation frame is inside specified bitmap
1834 // do not use getGraphicSourceXY() here to get position of first frame;
1835 // this avoids calculating wrong start position for out-of-bounds frame
1836 src_x = graphic_info[i].src_x;
1837 src_y = graphic_info[i].src_y;
1839 if (program.headless)
1842 if (src_x < 0 || src_y < 0 ||
1843 src_x + width > src_bitmap_width ||
1844 src_y + height > src_bitmap_height)
1847 Warn("error found in config file:");
1848 Warn("- config file: '%s'", getImageConfigFilename());
1849 Warn("- config token: '%s'", getTokenFromImageID(i));
1850 Warn("- image file: '%s'", src_bitmap->source_filename);
1851 Warn("- frame size: %d, %d", width, height);
1852 Warn("error: first animation frame out of bounds (%d, %d) [%d, %d]",
1853 src_x, src_y, src_bitmap_width, src_bitmap_height);
1854 Warn("custom graphic rejected for this element/action");
1856 if (i == fallback_graphic)
1857 Fail("no fallback graphic available");
1859 Warn("fallback done to 'char_exclam' for this graphic");
1862 graphic_info[i] = graphic_info[fallback_graphic];
1864 // if first frame out of bounds, do not check last frame anymore
1868 // check if last animation frame is inside specified bitmap
1870 last_frame = graphic_info[i].anim_frames - 1;
1871 getGraphicSourceXY(i, last_frame, &src_x, &src_y, FALSE);
1873 if (src_x < 0 || src_y < 0 ||
1874 src_x + width > src_bitmap_width ||
1875 src_y + height > src_bitmap_height)
1878 Warn("error found in config file:");
1879 Warn("- config file: '%s'", getImageConfigFilename());
1880 Warn("- config token: '%s'", getTokenFromImageID(i));
1881 Warn("- image file: '%s'", src_bitmap->source_filename);
1882 Warn("- frame size: %d, %d", width, height);
1883 Warn("error: last animation frame (%d) out of bounds (%d, %d) [%d, %d]",
1884 last_frame, src_x, src_y, src_bitmap_width, src_bitmap_height);
1885 Warn("custom graphic rejected for this element/action");
1887 if (i == fallback_graphic)
1888 Fail("no fallback graphic available");
1890 Warn("fallback done to 'char_exclam' for this graphic");
1893 graphic_info[i] = graphic_info[fallback_graphic];
1898 static void InitGraphicCompatibilityInfo(void)
1900 struct FileInfo *fi_global_door =
1901 getImageListEntryFromImageID(IMG_GLOBAL_DOOR);
1902 int num_images = getImageListSize();
1905 /* the following compatibility handling is needed for the following case:
1906 versions up to 3.3.0.0 used one large bitmap "global.door" for various
1907 graphics mainly used for door and panel graphics, like editor, tape and
1908 in-game buttons with hard-coded bitmap positions and button sizes; as
1909 these graphics now have individual definitions, redefining "global.door"
1910 to change all these graphics at once like before does not work anymore
1911 (because all those individual definitions still have their default values);
1912 to solve this, remap all those individual definitions that are not
1913 redefined to the new bitmap of "global.door" if it was redefined */
1915 // special compatibility handling if image "global.door" was redefined
1916 if (fi_global_door->redefined)
1918 for (i = 0; i < num_images; i++)
1920 struct FileInfo *fi = getImageListEntryFromImageID(i);
1922 // process only those images that still use the default settings
1925 // process all images which default to same image as "global.door"
1926 if (strEqual(fi->default_filename, fi_global_door->default_filename))
1929 Debug("init:InitGraphicCompatibilityInfo",
1930 "special treatment needed for token '%s'", fi->token);
1933 graphic_info[i].bitmaps = graphic_info[IMG_GLOBAL_DOOR].bitmaps;
1934 graphic_info[i].bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
1940 InitGraphicCompatibilityInfo_Doors();
1943 static void InitElementSoundInfo(void)
1945 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
1946 int num_property_mappings = getSoundListPropertyMappingSize();
1949 // set values to -1 to identify later as "uninitialized" values
1950 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1951 for (act = 0; act < NUM_ACTIONS; act++)
1952 element_info[i].sound[act] = -1;
1954 // initialize element/sound mapping from static configuration
1955 for (i = 0; element_to_sound[i].element > -1; i++)
1957 int element = element_to_sound[i].element;
1958 int action = element_to_sound[i].action;
1959 int sound = element_to_sound[i].sound;
1960 boolean is_class = element_to_sound[i].is_class;
1963 action = ACTION_DEFAULT;
1966 element_info[element].sound[action] = sound;
1968 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
1969 if (strEqual(element_info[j].class_name,
1970 element_info[element].class_name))
1971 element_info[j].sound[action] = sound;
1974 // initialize element class/sound mapping from dynamic configuration
1975 for (i = 0; i < num_property_mappings; i++)
1977 int element_class = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
1978 int action = property_mapping[i].ext1_index;
1979 int sound = property_mapping[i].artwork_index;
1981 if (element_class < 0 || element_class >= MAX_NUM_ELEMENTS)
1985 action = ACTION_DEFAULT;
1987 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
1988 if (strEqual(element_info[j].class_name,
1989 element_info[element_class].class_name))
1990 element_info[j].sound[action] = sound;
1993 // initialize element/sound mapping from dynamic configuration
1994 for (i = 0; i < num_property_mappings; i++)
1996 int element = property_mapping[i].base_index;
1997 int action = property_mapping[i].ext1_index;
1998 int sound = property_mapping[i].artwork_index;
2000 if (element >= MAX_NUM_ELEMENTS)
2004 action = ACTION_DEFAULT;
2006 element_info[element].sound[action] = sound;
2009 // now set all '-1' values to element specific default values
2010 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2012 for (act = 0; act < NUM_ACTIONS; act++)
2014 // generic default action sound (defined by "[default]" directive)
2015 int default_action_sound = element_info[EL_DEFAULT].sound[act];
2017 // look for special default action sound (classic game specific)
2018 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].sound[act] != -1)
2019 default_action_sound = element_info[EL_BD_DEFAULT].sound[act];
2020 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].sound[act] != -1)
2021 default_action_sound = element_info[EL_SP_DEFAULT].sound[act];
2022 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].sound[act] != -1)
2023 default_action_sound = element_info[EL_SB_DEFAULT].sound[act];
2024 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].sound[act] != -1)
2025 default_action_sound = element_info[EL_MM_DEFAULT].sound[act];
2027 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
2028 // !!! make this better !!!
2029 if (i == EL_EMPTY_SPACE)
2030 default_action_sound = element_info[EL_DEFAULT].sound[act];
2032 // no sound for this specific action -- use default action sound
2033 if (element_info[i].sound[act] == -1)
2034 element_info[i].sound[act] = default_action_sound;
2038 // copy sound settings to some elements that are only stored in level file
2039 // in native R'n'D levels, but are used by game engine in native EM levels
2040 for (i = 0; copy_properties[i][0] != -1; i++)
2041 for (j = 1; j <= 4; j++)
2042 for (act = 0; act < NUM_ACTIONS; act++)
2043 element_info[copy_properties[i][j]].sound[act] =
2044 element_info[copy_properties[i][0]].sound[act];
2047 static void InitGameModeSoundInfo(void)
2051 // set values to -1 to identify later as "uninitialized" values
2052 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2055 // initialize gamemode/sound mapping from static configuration
2056 for (i = 0; gamemode_to_sound[i].sound > -1; i++)
2058 int gamemode = gamemode_to_sound[i].gamemode;
2059 int sound = gamemode_to_sound[i].sound;
2062 gamemode = GAME_MODE_DEFAULT;
2064 menu.sound[gamemode] = sound;
2067 // now set all '-1' values to levelset specific default values
2068 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2069 if (menu.sound[i] == -1)
2070 menu.sound[i] = menu.sound[GAME_MODE_DEFAULT];
2073 static void set_sound_parameters(int sound, char **parameter_raw)
2075 int parameter[NUM_SND_ARGS];
2078 // get integer values from string parameters
2079 for (i = 0; i < NUM_SND_ARGS; i++)
2081 get_parameter_value(parameter_raw[i],
2082 sound_config_suffix[i].token,
2083 sound_config_suffix[i].type);
2085 // explicit loop mode setting in configuration overrides default value
2086 if (parameter[SND_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2087 sound_info[sound].loop = parameter[SND_ARG_MODE_LOOP];
2089 // sound volume to change the original volume when loading the sound file
2090 sound_info[sound].volume = parameter[SND_ARG_VOLUME];
2092 // sound priority to give certain sounds a higher or lower priority
2093 sound_info[sound].priority = parameter[SND_ARG_PRIORITY];
2096 static void InitSoundInfo(void)
2098 int *sound_effect_properties;
2099 int num_sounds = getSoundListSize();
2102 checked_free(sound_info);
2104 sound_effect_properties = checked_calloc(num_sounds * sizeof(int));
2105 sound_info = checked_calloc(num_sounds * sizeof(struct SoundInfo));
2107 // initialize sound effect for all elements to "no sound"
2108 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2109 for (j = 0; j < NUM_ACTIONS; j++)
2110 element_info[i].sound[j] = SND_UNDEFINED;
2112 for (i = 0; i < num_sounds; i++)
2114 struct FileInfo *sound = getSoundListEntry(i);
2115 int len_effect_text = strlen(sound->token);
2117 sound_effect_properties[i] = ACTION_OTHER;
2118 sound_info[i].loop = FALSE; // default: play sound only once
2120 // determine all loop sounds and identify certain sound classes
2122 for (j = 0; element_action_info[j].suffix; j++)
2124 int len_action_text = strlen(element_action_info[j].suffix);
2126 if (len_action_text < len_effect_text &&
2127 strEqual(&sound->token[len_effect_text - len_action_text],
2128 element_action_info[j].suffix))
2130 sound_effect_properties[i] = element_action_info[j].value;
2131 sound_info[i].loop = element_action_info[j].is_loop_sound;
2137 // associate elements and some selected sound actions
2139 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2141 if (element_info[j].class_name)
2143 int len_class_text = strlen(element_info[j].class_name);
2145 if (len_class_text + 1 < len_effect_text &&
2146 strncmp(sound->token,
2147 element_info[j].class_name, len_class_text) == 0 &&
2148 sound->token[len_class_text] == '.')
2150 int sound_action_value = sound_effect_properties[i];
2152 element_info[j].sound[sound_action_value] = i;
2157 set_sound_parameters(i, sound->parameter);
2160 free(sound_effect_properties);
2163 static void InitGameModeMusicInfo(void)
2165 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
2166 int num_property_mappings = getMusicListPropertyMappingSize();
2167 int default_levelset_music = -1;
2170 // set values to -1 to identify later as "uninitialized" values
2171 for (i = 0; i < MAX_LEVELS; i++)
2172 levelset.music[i] = -1;
2173 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2176 // initialize gamemode/music mapping from static configuration
2177 for (i = 0; gamemode_to_music[i].music > -1; i++)
2179 int gamemode = gamemode_to_music[i].gamemode;
2180 int music = gamemode_to_music[i].music;
2183 gamemode = GAME_MODE_DEFAULT;
2185 menu.music[gamemode] = music;
2188 // initialize gamemode/music mapping from dynamic configuration
2189 for (i = 0; i < num_property_mappings; i++)
2191 int prefix = property_mapping[i].base_index;
2192 int gamemode = property_mapping[i].ext2_index;
2193 int level = property_mapping[i].ext3_index;
2194 int music = property_mapping[i].artwork_index;
2196 if (prefix < 0 || prefix >= NUM_MUSIC_PREFIXES)
2200 gamemode = GAME_MODE_DEFAULT;
2202 // level specific music only allowed for in-game music
2203 if (level != -1 && gamemode == GAME_MODE_DEFAULT)
2204 gamemode = GAME_MODE_PLAYING;
2209 default_levelset_music = music;
2212 if (gamemode == GAME_MODE_PLAYING || gamemode == GAME_MODE_DEFAULT)
2213 levelset.music[level] = music;
2214 if (gamemode != GAME_MODE_PLAYING)
2215 menu.music[gamemode] = music;
2218 // now set all '-1' values to menu specific default values
2219 // (undefined values of "levelset.music[]" might stay at "-1" to
2220 // allow dynamic selection of music files from music directory!)
2221 for (i = 0; i < MAX_LEVELS; i++)
2222 if (levelset.music[i] == -1)
2223 levelset.music[i] = default_levelset_music;
2224 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2225 if (menu.music[i] == -1)
2226 menu.music[i] = menu.music[GAME_MODE_DEFAULT];
2229 static void set_music_parameters(int music, char **parameter_raw)
2231 int parameter[NUM_MUS_ARGS];
2234 // get integer values from string parameters
2235 for (i = 0; i < NUM_MUS_ARGS; i++)
2237 get_parameter_value(parameter_raw[i],
2238 music_config_suffix[i].token,
2239 music_config_suffix[i].type);
2241 // explicit loop mode setting in configuration overrides default value
2242 if (parameter[MUS_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2243 music_info[music].loop = parameter[MUS_ARG_MODE_LOOP];
2246 static void InitMusicInfo(void)
2248 int num_music = getMusicListSize();
2251 checked_free(music_info);
2253 music_info = checked_calloc(num_music * sizeof(struct MusicInfo));
2255 for (i = 0; i < num_music; i++)
2257 struct FileInfo *music = getMusicListEntry(i);
2258 int len_music_text = strlen(music->token);
2260 music_info[i].loop = TRUE; // default: play music in loop mode
2262 // determine all loop music
2264 for (j = 0; music_prefix_info[j].prefix; j++)
2266 int len_prefix_text = strlen(music_prefix_info[j].prefix);
2268 if (len_prefix_text < len_music_text &&
2269 strncmp(music->token,
2270 music_prefix_info[j].prefix, len_prefix_text) == 0)
2272 music_info[i].loop = music_prefix_info[j].is_loop_music;
2278 set_music_parameters(i, music->parameter);
2283 static void InitGameInfoFromArtworkInfo(void)
2285 // special case: store initial value of custom artwork setting
2286 game.use_masked_elements_initial = game.use_masked_elements;
2289 static void ReinitializeGraphics(void)
2291 print_timestamp_init("ReinitializeGraphics");
2293 InitGfxTileSizeInfo(game.tile_size, TILESIZE);
2295 InitGraphicInfo(); // graphic properties mapping
2296 print_timestamp_time("InitGraphicInfo");
2297 InitElementGraphicInfo(); // element game graphic mapping
2298 print_timestamp_time("InitElementGraphicInfo");
2299 InitElementSpecialGraphicInfo(); // element special graphic mapping
2300 print_timestamp_time("InitElementSpecialGraphicInfo");
2302 InitElementSmallImages(); // scale elements to all needed sizes
2303 print_timestamp_time("InitElementSmallImages");
2304 InitScaledImages(); // scale all other images, if needed
2305 print_timestamp_time("InitScaledImages");
2306 InitBitmapPointers(); // set standard size bitmap pointers
2307 print_timestamp_time("InitBitmapPointers");
2308 InitFontGraphicInfo(); // initialize text drawing functions
2309 print_timestamp_time("InitFontGraphicInfo");
2310 InitGlobalAnimGraphicInfo(); // initialize global animation config
2311 print_timestamp_time("InitGlobalAnimGraphicInfo");
2313 InitImageTextures(); // create textures for certain images
2314 print_timestamp_time("InitImageTextures");
2316 InitGraphicInfo_EM(); // graphic mapping for EM engine
2317 print_timestamp_time("InitGraphicInfo_EM");
2319 InitGraphicCompatibilityInfo();
2320 print_timestamp_time("InitGraphicCompatibilityInfo");
2323 print_timestamp_time("InitGadgets");
2325 print_timestamp_time("InitDoors");
2327 InitGameInfoFromArtworkInfo();
2329 print_timestamp_done("ReinitializeGraphics");
2332 static void ReinitializeSounds(void)
2334 InitSoundInfo(); // sound properties mapping
2335 InitElementSoundInfo(); // element game sound mapping
2336 InitGameModeSoundInfo(); // game mode sound mapping
2337 InitGlobalAnimSoundInfo(); // global animation sound settings
2339 InitPlayLevelSound(); // internal game sound settings
2342 static void ReinitializeMusic(void)
2344 InitMusicInfo(); // music properties mapping
2345 InitGameModeMusicInfo(); // game mode music mapping
2346 InitGlobalAnimMusicInfo(); // global animation music settings
2349 static int get_special_property_bit(int element, int property_bit_nr)
2351 struct PropertyBitInfo
2357 static struct PropertyBitInfo pb_can_move_into_acid[] =
2359 // the player may be able fall into acid when gravity is activated
2364 { EL_SP_MURPHY, 0 },
2365 { EL_SOKOBAN_FIELD_PLAYER, 0 },
2367 // all elements that can move may be able to also move into acid
2370 { EL_BUG_RIGHT, 1 },
2373 { EL_SPACESHIP, 2 },
2374 { EL_SPACESHIP_LEFT, 2 },
2375 { EL_SPACESHIP_RIGHT, 2 },
2376 { EL_SPACESHIP_UP, 2 },
2377 { EL_SPACESHIP_DOWN, 2 },
2378 { EL_BD_BUTTERFLY, 3 },
2379 { EL_BD_BUTTERFLY_LEFT, 3 },
2380 { EL_BD_BUTTERFLY_RIGHT, 3 },
2381 { EL_BD_BUTTERFLY_UP, 3 },
2382 { EL_BD_BUTTERFLY_DOWN, 3 },
2383 { EL_BD_FIREFLY, 4 },
2384 { EL_BD_FIREFLY_LEFT, 4 },
2385 { EL_BD_FIREFLY_RIGHT, 4 },
2386 { EL_BD_FIREFLY_UP, 4 },
2387 { EL_BD_FIREFLY_DOWN, 4 },
2389 { EL_YAMYAM_LEFT, 5 },
2390 { EL_YAMYAM_RIGHT, 5 },
2391 { EL_YAMYAM_UP, 5 },
2392 { EL_YAMYAM_DOWN, 5 },
2393 { EL_DARK_YAMYAM, 6 },
2396 { EL_PACMAN_LEFT, 8 },
2397 { EL_PACMAN_RIGHT, 8 },
2398 { EL_PACMAN_UP, 8 },
2399 { EL_PACMAN_DOWN, 8 },
2401 { EL_MOLE_LEFT, 9 },
2402 { EL_MOLE_RIGHT, 9 },
2404 { EL_MOLE_DOWN, 9 },
2408 { EL_SATELLITE, 13 },
2409 { EL_SP_SNIKSNAK, 14 },
2410 { EL_SP_ELECTRON, 15 },
2413 { EL_SPRING_LEFT, 17 },
2414 { EL_SPRING_RIGHT, 17 },
2415 { EL_EMC_ANDROID, 18 },
2420 static struct PropertyBitInfo pb_dont_collide_with[] =
2422 { EL_SP_SNIKSNAK, 0 },
2423 { EL_SP_ELECTRON, 1 },
2431 struct PropertyBitInfo *pb_info;
2434 { EP_CAN_MOVE_INTO_ACID, pb_can_move_into_acid },
2435 { EP_DONT_COLLIDE_WITH, pb_dont_collide_with },
2440 struct PropertyBitInfo *pb_info = NULL;
2443 for (i = 0; pb_definition[i].bit_nr != -1; i++)
2444 if (pb_definition[i].bit_nr == property_bit_nr)
2445 pb_info = pb_definition[i].pb_info;
2447 if (pb_info == NULL)
2450 for (i = 0; pb_info[i].element != -1; i++)
2451 if (pb_info[i].element == element)
2452 return pb_info[i].bit_nr;
2457 void setBitfieldProperty(int *bitfield, int property_bit_nr, int element,
2458 boolean property_value)
2460 int bit_nr = get_special_property_bit(element, property_bit_nr);
2465 *bitfield |= (1 << bit_nr);
2467 *bitfield &= ~(1 << bit_nr);
2471 boolean getBitfieldProperty(int *bitfield, int property_bit_nr, int element)
2473 int bit_nr = get_special_property_bit(element, property_bit_nr);
2476 return ((*bitfield & (1 << bit_nr)) != 0);
2481 static void ResolveGroupElementExt(int group_element, int recursion_depth)
2483 static int group_nr;
2484 static struct ElementGroupInfo *group;
2485 struct ElementGroupInfo *actual_group = element_info[group_element].group;
2488 if (actual_group == NULL) // not yet initialized
2491 if (recursion_depth > NUM_GROUP_ELEMENTS) // recursion too deep
2493 Warn("recursion too deep when resolving group element %d",
2494 group_element - EL_GROUP_START + 1);
2496 // replace element which caused too deep recursion by question mark
2497 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
2502 if (recursion_depth == 0) // initialization
2504 group = actual_group;
2505 group_nr = GROUP_NR(group_element);
2507 group->num_elements_resolved = 0;
2508 group->choice_pos = 0;
2510 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2511 element_info[i].in_group[group_nr] = FALSE;
2514 for (i = 0; i < actual_group->num_elements; i++)
2516 int element = actual_group->element[i];
2518 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
2521 if (IS_GROUP_ELEMENT(element))
2522 ResolveGroupElementExt(element, recursion_depth + 1);
2525 group->element_resolved[group->num_elements_resolved++] = element;
2526 element_info[element].in_group[group_nr] = TRUE;
2531 void ResolveGroupElement(int group_element)
2533 ResolveGroupElementExt(group_element, 0);
2536 void InitElementPropertiesStatic(void)
2538 static boolean clipboard_elements_initialized = FALSE;
2540 static int ep_diggable[] =
2545 EL_SP_BUGGY_BASE_ACTIVATING,
2548 EL_INVISIBLE_SAND_ACTIVE,
2551 // !!! currently not diggable, but handled by 'ep_dont_run_into' !!!
2552 // (if amoeba can grow into anything diggable, maybe keep these out)
2557 EL_SP_BUGGY_BASE_ACTIVE,
2564 static int ep_collectible_only[] =
2586 EL_DYNABOMB_INCREASE_NUMBER,
2587 EL_DYNABOMB_INCREASE_SIZE,
2588 EL_DYNABOMB_INCREASE_POWER,
2606 // !!! handle separately !!!
2607 EL_DC_LANDMINE, // deadly when running into, but can be snapped
2613 static int ep_dont_run_into[] =
2615 // same elements as in 'ep_dont_touch'
2621 // same elements as in 'ep_dont_collide_with'
2633 // !!! maybe this should better be handled by 'ep_diggable' !!!
2638 EL_SP_BUGGY_BASE_ACTIVE,
2645 static int ep_dont_collide_with[] =
2647 // same elements as in 'ep_dont_touch'
2664 static int ep_dont_touch[] =
2674 static int ep_indestructible[] =
2678 EL_ACID_POOL_TOPLEFT,
2679 EL_ACID_POOL_TOPRIGHT,
2680 EL_ACID_POOL_BOTTOMLEFT,
2681 EL_ACID_POOL_BOTTOM,
2682 EL_ACID_POOL_BOTTOMRIGHT,
2683 EL_SP_HARDWARE_GRAY,
2684 EL_SP_HARDWARE_GREEN,
2685 EL_SP_HARDWARE_BLUE,
2687 EL_SP_HARDWARE_YELLOW,
2688 EL_SP_HARDWARE_BASE_1,
2689 EL_SP_HARDWARE_BASE_2,
2690 EL_SP_HARDWARE_BASE_3,
2691 EL_SP_HARDWARE_BASE_4,
2692 EL_SP_HARDWARE_BASE_5,
2693 EL_SP_HARDWARE_BASE_6,
2694 EL_INVISIBLE_STEELWALL,
2695 EL_INVISIBLE_STEELWALL_ACTIVE,
2696 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2697 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
2698 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
2699 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2700 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
2701 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
2702 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2703 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
2704 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
2705 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
2706 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
2707 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
2709 EL_LIGHT_SWITCH_ACTIVE,
2710 EL_SIGN_EXCLAMATION,
2711 EL_SIGN_RADIOACTIVITY,
2718 EL_SIGN_ENTRY_FORBIDDEN,
2719 EL_SIGN_EMERGENCY_EXIT,
2727 EL_STEEL_EXIT_CLOSED,
2729 EL_STEEL_EXIT_OPENING,
2730 EL_STEEL_EXIT_CLOSING,
2731 EL_EM_STEEL_EXIT_CLOSED,
2732 EL_EM_STEEL_EXIT_OPEN,
2733 EL_EM_STEEL_EXIT_OPENING,
2734 EL_EM_STEEL_EXIT_CLOSING,
2735 EL_DC_STEELWALL_1_LEFT,
2736 EL_DC_STEELWALL_1_RIGHT,
2737 EL_DC_STEELWALL_1_TOP,
2738 EL_DC_STEELWALL_1_BOTTOM,
2739 EL_DC_STEELWALL_1_HORIZONTAL,
2740 EL_DC_STEELWALL_1_VERTICAL,
2741 EL_DC_STEELWALL_1_TOPLEFT,
2742 EL_DC_STEELWALL_1_TOPRIGHT,
2743 EL_DC_STEELWALL_1_BOTTOMLEFT,
2744 EL_DC_STEELWALL_1_BOTTOMRIGHT,
2745 EL_DC_STEELWALL_1_TOPLEFT_2,
2746 EL_DC_STEELWALL_1_TOPRIGHT_2,
2747 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
2748 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
2749 EL_DC_STEELWALL_2_LEFT,
2750 EL_DC_STEELWALL_2_RIGHT,
2751 EL_DC_STEELWALL_2_TOP,
2752 EL_DC_STEELWALL_2_BOTTOM,
2753 EL_DC_STEELWALL_2_HORIZONTAL,
2754 EL_DC_STEELWALL_2_VERTICAL,
2755 EL_DC_STEELWALL_2_MIDDLE,
2756 EL_DC_STEELWALL_2_SINGLE,
2757 EL_STEELWALL_SLIPPERY,
2771 EL_GATE_1_GRAY_ACTIVE,
2772 EL_GATE_2_GRAY_ACTIVE,
2773 EL_GATE_3_GRAY_ACTIVE,
2774 EL_GATE_4_GRAY_ACTIVE,
2783 EL_EM_GATE_1_GRAY_ACTIVE,
2784 EL_EM_GATE_2_GRAY_ACTIVE,
2785 EL_EM_GATE_3_GRAY_ACTIVE,
2786 EL_EM_GATE_4_GRAY_ACTIVE,
2795 EL_EMC_GATE_5_GRAY_ACTIVE,
2796 EL_EMC_GATE_6_GRAY_ACTIVE,
2797 EL_EMC_GATE_7_GRAY_ACTIVE,
2798 EL_EMC_GATE_8_GRAY_ACTIVE,
2800 EL_DC_GATE_WHITE_GRAY,
2801 EL_DC_GATE_WHITE_GRAY_ACTIVE,
2802 EL_DC_GATE_FAKE_GRAY,
2804 EL_SWITCHGATE_OPENING,
2805 EL_SWITCHGATE_CLOSED,
2806 EL_SWITCHGATE_CLOSING,
2807 EL_DC_SWITCHGATE_SWITCH_UP,
2808 EL_DC_SWITCHGATE_SWITCH_DOWN,
2810 EL_TIMEGATE_OPENING,
2812 EL_TIMEGATE_CLOSING,
2813 EL_DC_TIMEGATE_SWITCH,
2814 EL_DC_TIMEGATE_SWITCH_ACTIVE,
2818 EL_TUBE_VERTICAL_LEFT,
2819 EL_TUBE_VERTICAL_RIGHT,
2820 EL_TUBE_HORIZONTAL_UP,
2821 EL_TUBE_HORIZONTAL_DOWN,
2826 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
2827 EL_EXPANDABLE_STEELWALL_VERTICAL,
2828 EL_EXPANDABLE_STEELWALL_ANY,
2833 static int ep_slippery[] =
2847 EL_ROBOT_WHEEL_ACTIVE,
2853 EL_ACID_POOL_TOPLEFT,
2854 EL_ACID_POOL_TOPRIGHT,
2864 EL_STEELWALL_SLIPPERY,
2867 EL_EMC_WALL_SLIPPERY_1,
2868 EL_EMC_WALL_SLIPPERY_2,
2869 EL_EMC_WALL_SLIPPERY_3,
2870 EL_EMC_WALL_SLIPPERY_4,
2872 EL_EMC_MAGIC_BALL_ACTIVE,
2877 static int ep_can_change[] =
2882 static int ep_can_move[] =
2884 // same elements as in 'pb_can_move_into_acid'
2907 static int ep_can_fall[] =
2922 EL_QUICKSAND_FAST_FULL,
2924 EL_BD_MAGIC_WALL_FULL,
2925 EL_DC_MAGIC_WALL_FULL,
2939 static int ep_can_smash_player[] =
2965 static int ep_can_smash_enemies[] =
2974 static int ep_can_smash_everything[] =
2983 static int ep_explodes_by_fire[] =
2985 // same elements as in 'ep_explodes_impact'
2990 // same elements as in 'ep_explodes_smashed'
3000 EL_EM_DYNAMITE_ACTIVE,
3001 EL_DYNABOMB_PLAYER_1_ACTIVE,
3002 EL_DYNABOMB_PLAYER_2_ACTIVE,
3003 EL_DYNABOMB_PLAYER_3_ACTIVE,
3004 EL_DYNABOMB_PLAYER_4_ACTIVE,
3005 EL_DYNABOMB_INCREASE_NUMBER,
3006 EL_DYNABOMB_INCREASE_SIZE,
3007 EL_DYNABOMB_INCREASE_POWER,
3008 EL_SP_DISK_RED_ACTIVE,
3022 static int ep_explodes_smashed[] =
3024 // same elements as in 'ep_explodes_impact'
3038 static int ep_explodes_impact[] =
3047 static int ep_walkable_over[] =
3067 EL_SOKOBAN_FIELD_EMPTY,
3074 EL_EM_STEEL_EXIT_OPEN,
3075 EL_EM_STEEL_EXIT_OPENING,
3084 EL_GATE_1_GRAY_ACTIVE,
3085 EL_GATE_2_GRAY_ACTIVE,
3086 EL_GATE_3_GRAY_ACTIVE,
3087 EL_GATE_4_GRAY_ACTIVE,
3095 static int ep_walkable_inside[] =
3100 EL_TUBE_VERTICAL_LEFT,
3101 EL_TUBE_VERTICAL_RIGHT,
3102 EL_TUBE_HORIZONTAL_UP,
3103 EL_TUBE_HORIZONTAL_DOWN,
3112 static int ep_walkable_under[] =
3117 static int ep_passable_over[] =
3127 EL_EM_GATE_1_GRAY_ACTIVE,
3128 EL_EM_GATE_2_GRAY_ACTIVE,
3129 EL_EM_GATE_3_GRAY_ACTIVE,
3130 EL_EM_GATE_4_GRAY_ACTIVE,
3139 EL_EMC_GATE_5_GRAY_ACTIVE,
3140 EL_EMC_GATE_6_GRAY_ACTIVE,
3141 EL_EMC_GATE_7_GRAY_ACTIVE,
3142 EL_EMC_GATE_8_GRAY_ACTIVE,
3144 EL_DC_GATE_WHITE_GRAY,
3145 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3152 static int ep_passable_inside[] =
3158 EL_SP_PORT_HORIZONTAL,
3159 EL_SP_PORT_VERTICAL,
3161 EL_SP_GRAVITY_PORT_LEFT,
3162 EL_SP_GRAVITY_PORT_RIGHT,
3163 EL_SP_GRAVITY_PORT_UP,
3164 EL_SP_GRAVITY_PORT_DOWN,
3165 EL_SP_GRAVITY_ON_PORT_LEFT,
3166 EL_SP_GRAVITY_ON_PORT_RIGHT,
3167 EL_SP_GRAVITY_ON_PORT_UP,
3168 EL_SP_GRAVITY_ON_PORT_DOWN,
3169 EL_SP_GRAVITY_OFF_PORT_LEFT,
3170 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3171 EL_SP_GRAVITY_OFF_PORT_UP,
3172 EL_SP_GRAVITY_OFF_PORT_DOWN,
3177 static int ep_passable_under[] =
3182 static int ep_droppable[] =
3187 static int ep_explodes_1x1_old[] =
3192 static int ep_pushable[] =
3204 EL_SOKOBAN_FIELD_FULL,
3213 static int ep_explodes_cross_old[] =
3218 static int ep_protected[] =
3220 // same elements as in 'ep_walkable_inside'
3224 EL_TUBE_VERTICAL_LEFT,
3225 EL_TUBE_VERTICAL_RIGHT,
3226 EL_TUBE_HORIZONTAL_UP,
3227 EL_TUBE_HORIZONTAL_DOWN,
3233 // same elements as in 'ep_passable_over'
3242 EL_EM_GATE_1_GRAY_ACTIVE,
3243 EL_EM_GATE_2_GRAY_ACTIVE,
3244 EL_EM_GATE_3_GRAY_ACTIVE,
3245 EL_EM_GATE_4_GRAY_ACTIVE,
3254 EL_EMC_GATE_5_GRAY_ACTIVE,
3255 EL_EMC_GATE_6_GRAY_ACTIVE,
3256 EL_EMC_GATE_7_GRAY_ACTIVE,
3257 EL_EMC_GATE_8_GRAY_ACTIVE,
3259 EL_DC_GATE_WHITE_GRAY,
3260 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3264 // same elements as in 'ep_passable_inside'
3269 EL_SP_PORT_HORIZONTAL,
3270 EL_SP_PORT_VERTICAL,
3272 EL_SP_GRAVITY_PORT_LEFT,
3273 EL_SP_GRAVITY_PORT_RIGHT,
3274 EL_SP_GRAVITY_PORT_UP,
3275 EL_SP_GRAVITY_PORT_DOWN,
3276 EL_SP_GRAVITY_ON_PORT_LEFT,
3277 EL_SP_GRAVITY_ON_PORT_RIGHT,
3278 EL_SP_GRAVITY_ON_PORT_UP,
3279 EL_SP_GRAVITY_ON_PORT_DOWN,
3280 EL_SP_GRAVITY_OFF_PORT_LEFT,
3281 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3282 EL_SP_GRAVITY_OFF_PORT_UP,
3283 EL_SP_GRAVITY_OFF_PORT_DOWN,
3288 static int ep_throwable[] =
3293 static int ep_can_explode[] =
3295 // same elements as in 'ep_explodes_impact'
3300 // same elements as in 'ep_explodes_smashed'
3306 // elements that can explode by explosion or by dragonfire
3310 EL_EM_DYNAMITE_ACTIVE,
3311 EL_DYNABOMB_PLAYER_1_ACTIVE,
3312 EL_DYNABOMB_PLAYER_2_ACTIVE,
3313 EL_DYNABOMB_PLAYER_3_ACTIVE,
3314 EL_DYNABOMB_PLAYER_4_ACTIVE,
3315 EL_DYNABOMB_INCREASE_NUMBER,
3316 EL_DYNABOMB_INCREASE_SIZE,
3317 EL_DYNABOMB_INCREASE_POWER,
3318 EL_SP_DISK_RED_ACTIVE,
3326 // elements that can explode only by explosion
3332 static int ep_gravity_reachable[] =
3338 EL_INVISIBLE_SAND_ACTIVE,
3343 EL_SP_PORT_HORIZONTAL,
3344 EL_SP_PORT_VERTICAL,
3346 EL_SP_GRAVITY_PORT_LEFT,
3347 EL_SP_GRAVITY_PORT_RIGHT,
3348 EL_SP_GRAVITY_PORT_UP,
3349 EL_SP_GRAVITY_PORT_DOWN,
3350 EL_SP_GRAVITY_ON_PORT_LEFT,
3351 EL_SP_GRAVITY_ON_PORT_RIGHT,
3352 EL_SP_GRAVITY_ON_PORT_UP,
3353 EL_SP_GRAVITY_ON_PORT_DOWN,
3354 EL_SP_GRAVITY_OFF_PORT_LEFT,
3355 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3356 EL_SP_GRAVITY_OFF_PORT_UP,
3357 EL_SP_GRAVITY_OFF_PORT_DOWN,
3363 static int ep_empty_space[] =
3386 static int ep_player[] =
3393 EL_SOKOBAN_FIELD_PLAYER,
3399 static int ep_can_pass_magic_wall[] =
3413 static int ep_can_pass_dc_magic_wall[] =
3429 static int ep_switchable[] =
3433 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3434 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3435 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3436 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3437 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3438 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3439 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3440 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3441 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3442 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3443 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3444 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3445 EL_SWITCHGATE_SWITCH_UP,
3446 EL_SWITCHGATE_SWITCH_DOWN,
3447 EL_DC_SWITCHGATE_SWITCH_UP,
3448 EL_DC_SWITCHGATE_SWITCH_DOWN,
3450 EL_LIGHT_SWITCH_ACTIVE,
3452 EL_DC_TIMEGATE_SWITCH,
3453 EL_BALLOON_SWITCH_LEFT,
3454 EL_BALLOON_SWITCH_RIGHT,
3455 EL_BALLOON_SWITCH_UP,
3456 EL_BALLOON_SWITCH_DOWN,
3457 EL_BALLOON_SWITCH_ANY,
3458 EL_BALLOON_SWITCH_NONE,
3461 EL_EMC_MAGIC_BALL_SWITCH,
3462 EL_EMC_MAGIC_BALL_SWITCH_ACTIVE,
3467 static int ep_bd_element[] =
3501 static int ep_sp_element[] =
3503 // should always be valid
3506 // standard classic Supaplex elements
3513 EL_SP_HARDWARE_GRAY,
3521 EL_SP_GRAVITY_PORT_RIGHT,
3522 EL_SP_GRAVITY_PORT_DOWN,
3523 EL_SP_GRAVITY_PORT_LEFT,
3524 EL_SP_GRAVITY_PORT_UP,
3529 EL_SP_PORT_VERTICAL,
3530 EL_SP_PORT_HORIZONTAL,
3536 EL_SP_HARDWARE_BASE_1,
3537 EL_SP_HARDWARE_GREEN,
3538 EL_SP_HARDWARE_BLUE,
3540 EL_SP_HARDWARE_YELLOW,
3541 EL_SP_HARDWARE_BASE_2,
3542 EL_SP_HARDWARE_BASE_3,
3543 EL_SP_HARDWARE_BASE_4,
3544 EL_SP_HARDWARE_BASE_5,
3545 EL_SP_HARDWARE_BASE_6,
3549 // additional elements that appeared in newer Supaplex levels
3552 // additional gravity port elements (not switching, but setting gravity)
3553 EL_SP_GRAVITY_ON_PORT_LEFT,
3554 EL_SP_GRAVITY_ON_PORT_RIGHT,
3555 EL_SP_GRAVITY_ON_PORT_UP,
3556 EL_SP_GRAVITY_ON_PORT_DOWN,
3557 EL_SP_GRAVITY_OFF_PORT_LEFT,
3558 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3559 EL_SP_GRAVITY_OFF_PORT_UP,
3560 EL_SP_GRAVITY_OFF_PORT_DOWN,
3562 // more than one Murphy in a level results in an inactive clone
3565 // runtime Supaplex elements
3566 EL_SP_DISK_RED_ACTIVE,
3567 EL_SP_TERMINAL_ACTIVE,
3568 EL_SP_BUGGY_BASE_ACTIVATING,
3569 EL_SP_BUGGY_BASE_ACTIVE,
3576 static int ep_sb_element[] =
3581 EL_SOKOBAN_FIELD_EMPTY,
3582 EL_SOKOBAN_FIELD_FULL,
3583 EL_SOKOBAN_FIELD_PLAYER,
3588 EL_INVISIBLE_STEELWALL,
3593 static int ep_gem[] =
3605 static int ep_food_dark_yamyam[] =
3633 static int ep_food_penguin[] =
3647 static int ep_food_pig[] =
3659 static int ep_historic_wall[] =
3670 EL_GATE_1_GRAY_ACTIVE,
3671 EL_GATE_2_GRAY_ACTIVE,
3672 EL_GATE_3_GRAY_ACTIVE,
3673 EL_GATE_4_GRAY_ACTIVE,
3682 EL_EM_GATE_1_GRAY_ACTIVE,
3683 EL_EM_GATE_2_GRAY_ACTIVE,
3684 EL_EM_GATE_3_GRAY_ACTIVE,
3685 EL_EM_GATE_4_GRAY_ACTIVE,
3692 EL_EXPANDABLE_WALL_HORIZONTAL,
3693 EL_EXPANDABLE_WALL_VERTICAL,
3694 EL_EXPANDABLE_WALL_ANY,
3695 EL_EXPANDABLE_WALL_GROWING,
3696 EL_BD_EXPANDABLE_WALL,
3703 EL_SP_HARDWARE_GRAY,
3704 EL_SP_HARDWARE_GREEN,
3705 EL_SP_HARDWARE_BLUE,
3707 EL_SP_HARDWARE_YELLOW,
3708 EL_SP_HARDWARE_BASE_1,
3709 EL_SP_HARDWARE_BASE_2,
3710 EL_SP_HARDWARE_BASE_3,
3711 EL_SP_HARDWARE_BASE_4,
3712 EL_SP_HARDWARE_BASE_5,
3713 EL_SP_HARDWARE_BASE_6,
3715 EL_SP_TERMINAL_ACTIVE,
3718 EL_INVISIBLE_STEELWALL,
3719 EL_INVISIBLE_STEELWALL_ACTIVE,
3721 EL_INVISIBLE_WALL_ACTIVE,
3722 EL_STEELWALL_SLIPPERY,
3739 static int ep_historic_solid[] =
3743 EL_EXPANDABLE_WALL_HORIZONTAL,
3744 EL_EXPANDABLE_WALL_VERTICAL,
3745 EL_EXPANDABLE_WALL_ANY,
3746 EL_BD_EXPANDABLE_WALL,
3759 EL_QUICKSAND_FILLING,
3760 EL_QUICKSAND_EMPTYING,
3762 EL_MAGIC_WALL_ACTIVE,
3763 EL_MAGIC_WALL_EMPTYING,
3764 EL_MAGIC_WALL_FILLING,
3768 EL_BD_MAGIC_WALL_ACTIVE,
3769 EL_BD_MAGIC_WALL_EMPTYING,
3770 EL_BD_MAGIC_WALL_FULL,
3771 EL_BD_MAGIC_WALL_FILLING,
3772 EL_BD_MAGIC_WALL_DEAD,
3781 EL_SP_TERMINAL_ACTIVE,
3785 EL_INVISIBLE_WALL_ACTIVE,
3786 EL_SWITCHGATE_SWITCH_UP,
3787 EL_SWITCHGATE_SWITCH_DOWN,
3789 EL_TIMEGATE_SWITCH_ACTIVE,
3801 // the following elements are a direct copy of "indestructible" elements,
3802 // except "EL_ACID", which is "indestructible", but not "solid"!
3807 EL_ACID_POOL_TOPLEFT,
3808 EL_ACID_POOL_TOPRIGHT,
3809 EL_ACID_POOL_BOTTOMLEFT,
3810 EL_ACID_POOL_BOTTOM,
3811 EL_ACID_POOL_BOTTOMRIGHT,
3812 EL_SP_HARDWARE_GRAY,
3813 EL_SP_HARDWARE_GREEN,
3814 EL_SP_HARDWARE_BLUE,
3816 EL_SP_HARDWARE_YELLOW,
3817 EL_SP_HARDWARE_BASE_1,
3818 EL_SP_HARDWARE_BASE_2,
3819 EL_SP_HARDWARE_BASE_3,
3820 EL_SP_HARDWARE_BASE_4,
3821 EL_SP_HARDWARE_BASE_5,
3822 EL_SP_HARDWARE_BASE_6,
3823 EL_INVISIBLE_STEELWALL,
3824 EL_INVISIBLE_STEELWALL_ACTIVE,
3825 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3826 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3827 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3828 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3829 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3830 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3831 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3832 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3833 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3834 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3835 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3836 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3838 EL_LIGHT_SWITCH_ACTIVE,
3839 EL_SIGN_EXCLAMATION,
3840 EL_SIGN_RADIOACTIVITY,
3847 EL_SIGN_ENTRY_FORBIDDEN,
3848 EL_SIGN_EMERGENCY_EXIT,
3856 EL_STEEL_EXIT_CLOSED,
3858 EL_STEEL_EXIT_OPENING,
3859 EL_STEEL_EXIT_CLOSING,
3860 EL_EM_STEEL_EXIT_CLOSED,
3861 EL_EM_STEEL_EXIT_OPEN,
3862 EL_EM_STEEL_EXIT_OPENING,
3863 EL_EM_STEEL_EXIT_CLOSING,
3864 EL_DC_STEELWALL_1_LEFT,
3865 EL_DC_STEELWALL_1_RIGHT,
3866 EL_DC_STEELWALL_1_TOP,
3867 EL_DC_STEELWALL_1_BOTTOM,
3868 EL_DC_STEELWALL_1_HORIZONTAL,
3869 EL_DC_STEELWALL_1_VERTICAL,
3870 EL_DC_STEELWALL_1_TOPLEFT,
3871 EL_DC_STEELWALL_1_TOPRIGHT,
3872 EL_DC_STEELWALL_1_BOTTOMLEFT,
3873 EL_DC_STEELWALL_1_BOTTOMRIGHT,
3874 EL_DC_STEELWALL_1_TOPLEFT_2,
3875 EL_DC_STEELWALL_1_TOPRIGHT_2,
3876 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
3877 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
3878 EL_DC_STEELWALL_2_LEFT,
3879 EL_DC_STEELWALL_2_RIGHT,
3880 EL_DC_STEELWALL_2_TOP,
3881 EL_DC_STEELWALL_2_BOTTOM,
3882 EL_DC_STEELWALL_2_HORIZONTAL,
3883 EL_DC_STEELWALL_2_VERTICAL,
3884 EL_DC_STEELWALL_2_MIDDLE,
3885 EL_DC_STEELWALL_2_SINGLE,
3886 EL_STEELWALL_SLIPPERY,
3900 EL_GATE_1_GRAY_ACTIVE,
3901 EL_GATE_2_GRAY_ACTIVE,
3902 EL_GATE_3_GRAY_ACTIVE,
3903 EL_GATE_4_GRAY_ACTIVE,
3912 EL_EM_GATE_1_GRAY_ACTIVE,
3913 EL_EM_GATE_2_GRAY_ACTIVE,
3914 EL_EM_GATE_3_GRAY_ACTIVE,
3915 EL_EM_GATE_4_GRAY_ACTIVE,
3924 EL_EMC_GATE_5_GRAY_ACTIVE,
3925 EL_EMC_GATE_6_GRAY_ACTIVE,
3926 EL_EMC_GATE_7_GRAY_ACTIVE,
3927 EL_EMC_GATE_8_GRAY_ACTIVE,
3929 EL_DC_GATE_WHITE_GRAY,
3930 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3931 EL_DC_GATE_FAKE_GRAY,
3933 EL_SWITCHGATE_OPENING,
3934 EL_SWITCHGATE_CLOSED,
3935 EL_SWITCHGATE_CLOSING,
3936 EL_DC_SWITCHGATE_SWITCH_UP,
3937 EL_DC_SWITCHGATE_SWITCH_DOWN,
3939 EL_TIMEGATE_OPENING,
3941 EL_TIMEGATE_CLOSING,
3942 EL_DC_TIMEGATE_SWITCH,
3943 EL_DC_TIMEGATE_SWITCH_ACTIVE,
3947 EL_TUBE_VERTICAL_LEFT,
3948 EL_TUBE_VERTICAL_RIGHT,
3949 EL_TUBE_HORIZONTAL_UP,
3950 EL_TUBE_HORIZONTAL_DOWN,
3955 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
3956 EL_EXPANDABLE_STEELWALL_VERTICAL,
3957 EL_EXPANDABLE_STEELWALL_ANY,
3962 static int ep_classic_enemy[] =
3979 static int ep_belt[] =
3981 EL_CONVEYOR_BELT_1_LEFT,
3982 EL_CONVEYOR_BELT_1_MIDDLE,
3983 EL_CONVEYOR_BELT_1_RIGHT,
3984 EL_CONVEYOR_BELT_2_LEFT,
3985 EL_CONVEYOR_BELT_2_MIDDLE,
3986 EL_CONVEYOR_BELT_2_RIGHT,
3987 EL_CONVEYOR_BELT_3_LEFT,
3988 EL_CONVEYOR_BELT_3_MIDDLE,
3989 EL_CONVEYOR_BELT_3_RIGHT,
3990 EL_CONVEYOR_BELT_4_LEFT,
3991 EL_CONVEYOR_BELT_4_MIDDLE,
3992 EL_CONVEYOR_BELT_4_RIGHT,
3997 static int ep_belt_active[] =
3999 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4000 EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE,
4001 EL_CONVEYOR_BELT_1_RIGHT_ACTIVE,
4002 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4003 EL_CONVEYOR_BELT_2_MIDDLE_ACTIVE,
4004 EL_CONVEYOR_BELT_2_RIGHT_ACTIVE,
4005 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4006 EL_CONVEYOR_BELT_3_MIDDLE_ACTIVE,
4007 EL_CONVEYOR_BELT_3_RIGHT_ACTIVE,
4008 EL_CONVEYOR_BELT_4_LEFT_ACTIVE,
4009 EL_CONVEYOR_BELT_4_MIDDLE_ACTIVE,
4010 EL_CONVEYOR_BELT_4_RIGHT_ACTIVE,
4015 static int ep_belt_switch[] =
4017 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4018 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4019 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4020 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4021 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4022 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4023 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4024 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4025 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4026 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4027 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4028 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4033 static int ep_tube[] =
4040 EL_TUBE_HORIZONTAL_UP,
4041 EL_TUBE_HORIZONTAL_DOWN,
4043 EL_TUBE_VERTICAL_LEFT,
4044 EL_TUBE_VERTICAL_RIGHT,
4050 static int ep_acid_pool[] =
4052 EL_ACID_POOL_TOPLEFT,
4053 EL_ACID_POOL_TOPRIGHT,
4054 EL_ACID_POOL_BOTTOMLEFT,
4055 EL_ACID_POOL_BOTTOM,
4056 EL_ACID_POOL_BOTTOMRIGHT,
4061 static int ep_keygate[] =
4071 EL_GATE_1_GRAY_ACTIVE,
4072 EL_GATE_2_GRAY_ACTIVE,
4073 EL_GATE_3_GRAY_ACTIVE,
4074 EL_GATE_4_GRAY_ACTIVE,
4083 EL_EM_GATE_1_GRAY_ACTIVE,
4084 EL_EM_GATE_2_GRAY_ACTIVE,
4085 EL_EM_GATE_3_GRAY_ACTIVE,
4086 EL_EM_GATE_4_GRAY_ACTIVE,
4095 EL_EMC_GATE_5_GRAY_ACTIVE,
4096 EL_EMC_GATE_6_GRAY_ACTIVE,
4097 EL_EMC_GATE_7_GRAY_ACTIVE,
4098 EL_EMC_GATE_8_GRAY_ACTIVE,
4100 EL_DC_GATE_WHITE_GRAY,
4101 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4106 static int ep_amoeboid[] =
4118 static int ep_amoebalive[] =
4129 static int ep_has_editor_content[] =
4135 EL_SOKOBAN_FIELD_PLAYER,
4152 static int ep_can_turn_each_move[] =
4154 // !!! do something with this one !!!
4158 static int ep_can_grow[] =
4172 static int ep_active_bomb[] =
4175 EL_EM_DYNAMITE_ACTIVE,
4176 EL_DYNABOMB_PLAYER_1_ACTIVE,
4177 EL_DYNABOMB_PLAYER_2_ACTIVE,
4178 EL_DYNABOMB_PLAYER_3_ACTIVE,
4179 EL_DYNABOMB_PLAYER_4_ACTIVE,
4180 EL_SP_DISK_RED_ACTIVE,
4185 static int ep_inactive[] =
4211 EL_QUICKSAND_FAST_EMPTY,
4234 EL_GATE_1_GRAY_ACTIVE,
4235 EL_GATE_2_GRAY_ACTIVE,
4236 EL_GATE_3_GRAY_ACTIVE,
4237 EL_GATE_4_GRAY_ACTIVE,
4246 EL_EM_GATE_1_GRAY_ACTIVE,
4247 EL_EM_GATE_2_GRAY_ACTIVE,
4248 EL_EM_GATE_3_GRAY_ACTIVE,
4249 EL_EM_GATE_4_GRAY_ACTIVE,
4258 EL_EMC_GATE_5_GRAY_ACTIVE,
4259 EL_EMC_GATE_6_GRAY_ACTIVE,
4260 EL_EMC_GATE_7_GRAY_ACTIVE,
4261 EL_EMC_GATE_8_GRAY_ACTIVE,
4263 EL_DC_GATE_WHITE_GRAY,
4264 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4265 EL_DC_GATE_FAKE_GRAY,
4268 EL_INVISIBLE_STEELWALL,
4276 EL_WALL_EMERALD_YELLOW,
4277 EL_DYNABOMB_INCREASE_NUMBER,
4278 EL_DYNABOMB_INCREASE_SIZE,
4279 EL_DYNABOMB_INCREASE_POWER,
4283 EL_SOKOBAN_FIELD_EMPTY,
4284 EL_SOKOBAN_FIELD_FULL,
4285 EL_WALL_EMERALD_RED,
4286 EL_WALL_EMERALD_PURPLE,
4287 EL_ACID_POOL_TOPLEFT,
4288 EL_ACID_POOL_TOPRIGHT,
4289 EL_ACID_POOL_BOTTOMLEFT,
4290 EL_ACID_POOL_BOTTOM,
4291 EL_ACID_POOL_BOTTOMRIGHT,
4295 EL_BD_MAGIC_WALL_DEAD,
4297 EL_DC_MAGIC_WALL_DEAD,
4298 EL_AMOEBA_TO_DIAMOND,
4306 EL_SP_GRAVITY_PORT_RIGHT,
4307 EL_SP_GRAVITY_PORT_DOWN,
4308 EL_SP_GRAVITY_PORT_LEFT,
4309 EL_SP_GRAVITY_PORT_UP,
4310 EL_SP_PORT_HORIZONTAL,
4311 EL_SP_PORT_VERTICAL,
4322 EL_SP_HARDWARE_GRAY,
4323 EL_SP_HARDWARE_GREEN,
4324 EL_SP_HARDWARE_BLUE,
4326 EL_SP_HARDWARE_YELLOW,
4327 EL_SP_HARDWARE_BASE_1,
4328 EL_SP_HARDWARE_BASE_2,
4329 EL_SP_HARDWARE_BASE_3,
4330 EL_SP_HARDWARE_BASE_4,
4331 EL_SP_HARDWARE_BASE_5,
4332 EL_SP_HARDWARE_BASE_6,
4333 EL_SP_GRAVITY_ON_PORT_LEFT,
4334 EL_SP_GRAVITY_ON_PORT_RIGHT,
4335 EL_SP_GRAVITY_ON_PORT_UP,
4336 EL_SP_GRAVITY_ON_PORT_DOWN,
4337 EL_SP_GRAVITY_OFF_PORT_LEFT,
4338 EL_SP_GRAVITY_OFF_PORT_RIGHT,
4339 EL_SP_GRAVITY_OFF_PORT_UP,
4340 EL_SP_GRAVITY_OFF_PORT_DOWN,
4341 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4342 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4343 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4344 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4345 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4346 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4347 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4348 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4349 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4350 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4351 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4352 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4353 EL_SIGN_EXCLAMATION,
4354 EL_SIGN_RADIOACTIVITY,
4361 EL_SIGN_ENTRY_FORBIDDEN,
4362 EL_SIGN_EMERGENCY_EXIT,
4370 EL_DC_STEELWALL_1_LEFT,
4371 EL_DC_STEELWALL_1_RIGHT,
4372 EL_DC_STEELWALL_1_TOP,
4373 EL_DC_STEELWALL_1_BOTTOM,
4374 EL_DC_STEELWALL_1_HORIZONTAL,
4375 EL_DC_STEELWALL_1_VERTICAL,
4376 EL_DC_STEELWALL_1_TOPLEFT,
4377 EL_DC_STEELWALL_1_TOPRIGHT,
4378 EL_DC_STEELWALL_1_BOTTOMLEFT,
4379 EL_DC_STEELWALL_1_BOTTOMRIGHT,
4380 EL_DC_STEELWALL_1_TOPLEFT_2,
4381 EL_DC_STEELWALL_1_TOPRIGHT_2,
4382 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
4383 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
4384 EL_DC_STEELWALL_2_LEFT,
4385 EL_DC_STEELWALL_2_RIGHT,
4386 EL_DC_STEELWALL_2_TOP,
4387 EL_DC_STEELWALL_2_BOTTOM,
4388 EL_DC_STEELWALL_2_HORIZONTAL,
4389 EL_DC_STEELWALL_2_VERTICAL,
4390 EL_DC_STEELWALL_2_MIDDLE,
4391 EL_DC_STEELWALL_2_SINGLE,
4392 EL_STEELWALL_SLIPPERY,
4397 EL_EMC_WALL_SLIPPERY_1,
4398 EL_EMC_WALL_SLIPPERY_2,
4399 EL_EMC_WALL_SLIPPERY_3,
4400 EL_EMC_WALL_SLIPPERY_4,
4421 static int ep_em_slippery_wall[] =
4426 static int ep_gfx_crumbled[] =
4437 static int ep_editor_cascade_active[] =
4439 EL_INTERNAL_CASCADE_BD_ACTIVE,
4440 EL_INTERNAL_CASCADE_EM_ACTIVE,
4441 EL_INTERNAL_CASCADE_EMC_ACTIVE,
4442 EL_INTERNAL_CASCADE_RND_ACTIVE,
4443 EL_INTERNAL_CASCADE_SB_ACTIVE,
4444 EL_INTERNAL_CASCADE_SP_ACTIVE,
4445 EL_INTERNAL_CASCADE_DC_ACTIVE,
4446 EL_INTERNAL_CASCADE_DX_ACTIVE,
4447 EL_INTERNAL_CASCADE_MM_ACTIVE,
4448 EL_INTERNAL_CASCADE_DF_ACTIVE,
4449 EL_INTERNAL_CASCADE_CHARS_ACTIVE,
4450 EL_INTERNAL_CASCADE_STEEL_CHARS_ACTIVE,
4451 EL_INTERNAL_CASCADE_CE_ACTIVE,
4452 EL_INTERNAL_CASCADE_GE_ACTIVE,
4453 EL_INTERNAL_CASCADE_ES_ACTIVE,
4454 EL_INTERNAL_CASCADE_REF_ACTIVE,
4455 EL_INTERNAL_CASCADE_USER_ACTIVE,
4456 EL_INTERNAL_CASCADE_DYNAMIC_ACTIVE,
4461 static int ep_editor_cascade_inactive[] =
4463 EL_INTERNAL_CASCADE_BD,
4464 EL_INTERNAL_CASCADE_EM,
4465 EL_INTERNAL_CASCADE_EMC,
4466 EL_INTERNAL_CASCADE_RND,
4467 EL_INTERNAL_CASCADE_SB,
4468 EL_INTERNAL_CASCADE_SP,
4469 EL_INTERNAL_CASCADE_DC,
4470 EL_INTERNAL_CASCADE_DX,
4471 EL_INTERNAL_CASCADE_MM,
4472 EL_INTERNAL_CASCADE_DF,
4473 EL_INTERNAL_CASCADE_CHARS,
4474 EL_INTERNAL_CASCADE_STEEL_CHARS,
4475 EL_INTERNAL_CASCADE_CE,
4476 EL_INTERNAL_CASCADE_GE,
4477 EL_INTERNAL_CASCADE_ES,
4478 EL_INTERNAL_CASCADE_REF,
4479 EL_INTERNAL_CASCADE_USER,
4480 EL_INTERNAL_CASCADE_DYNAMIC,
4485 static int ep_obsolete[] =
4489 EL_EM_KEY_1_FILE_OBSOLETE,
4490 EL_EM_KEY_2_FILE_OBSOLETE,
4491 EL_EM_KEY_3_FILE_OBSOLETE,
4492 EL_EM_KEY_4_FILE_OBSOLETE,
4493 EL_ENVELOPE_OBSOLETE,
4502 } element_properties[] =
4504 { ep_diggable, EP_DIGGABLE },
4505 { ep_collectible_only, EP_COLLECTIBLE_ONLY },
4506 { ep_dont_run_into, EP_DONT_RUN_INTO },
4507 { ep_dont_collide_with, EP_DONT_COLLIDE_WITH },
4508 { ep_dont_touch, EP_DONT_TOUCH },
4509 { ep_indestructible, EP_INDESTRUCTIBLE },
4510 { ep_slippery, EP_SLIPPERY },
4511 { ep_can_change, EP_CAN_CHANGE },
4512 { ep_can_move, EP_CAN_MOVE },
4513 { ep_can_fall, EP_CAN_FALL },
4514 { ep_can_smash_player, EP_CAN_SMASH_PLAYER },
4515 { ep_can_smash_enemies, EP_CAN_SMASH_ENEMIES },
4516 { ep_can_smash_everything, EP_CAN_SMASH_EVERYTHING },
4517 { ep_explodes_by_fire, EP_EXPLODES_BY_FIRE },
4518 { ep_explodes_smashed, EP_EXPLODES_SMASHED },
4519 { ep_explodes_impact, EP_EXPLODES_IMPACT },
4520 { ep_walkable_over, EP_WALKABLE_OVER },
4521 { ep_walkable_inside, EP_WALKABLE_INSIDE },
4522 { ep_walkable_under, EP_WALKABLE_UNDER },
4523 { ep_passable_over, EP_PASSABLE_OVER },
4524 { ep_passable_inside, EP_PASSABLE_INSIDE },
4525 { ep_passable_under, EP_PASSABLE_UNDER },
4526 { ep_droppable, EP_DROPPABLE },
4527 { ep_explodes_1x1_old, EP_EXPLODES_1X1_OLD },
4528 { ep_pushable, EP_PUSHABLE },
4529 { ep_explodes_cross_old, EP_EXPLODES_CROSS_OLD },
4530 { ep_protected, EP_PROTECTED },
4531 { ep_throwable, EP_THROWABLE },
4532 { ep_can_explode, EP_CAN_EXPLODE },
4533 { ep_gravity_reachable, EP_GRAVITY_REACHABLE },
4535 { ep_empty_space, EP_EMPTY_SPACE },
4536 { ep_player, EP_PLAYER },
4537 { ep_can_pass_magic_wall, EP_CAN_PASS_MAGIC_WALL },
4538 { ep_can_pass_dc_magic_wall, EP_CAN_PASS_DC_MAGIC_WALL },
4539 { ep_switchable, EP_SWITCHABLE },
4540 { ep_bd_element, EP_BD_ELEMENT },
4541 { ep_sp_element, EP_SP_ELEMENT },
4542 { ep_sb_element, EP_SB_ELEMENT },
4544 { ep_food_dark_yamyam, EP_FOOD_DARK_YAMYAM },
4545 { ep_food_penguin, EP_FOOD_PENGUIN },
4546 { ep_food_pig, EP_FOOD_PIG },
4547 { ep_historic_wall, EP_HISTORIC_WALL },
4548 { ep_historic_solid, EP_HISTORIC_SOLID },
4549 { ep_classic_enemy, EP_CLASSIC_ENEMY },
4550 { ep_belt, EP_BELT },
4551 { ep_belt_active, EP_BELT_ACTIVE },
4552 { ep_belt_switch, EP_BELT_SWITCH },
4553 { ep_tube, EP_TUBE },
4554 { ep_acid_pool, EP_ACID_POOL },
4555 { ep_keygate, EP_KEYGATE },
4556 { ep_amoeboid, EP_AMOEBOID },
4557 { ep_amoebalive, EP_AMOEBALIVE },
4558 { ep_has_editor_content, EP_HAS_EDITOR_CONTENT },
4559 { ep_can_turn_each_move, EP_CAN_TURN_EACH_MOVE },
4560 { ep_can_grow, EP_CAN_GROW },
4561 { ep_active_bomb, EP_ACTIVE_BOMB },
4562 { ep_inactive, EP_INACTIVE },
4564 { ep_em_slippery_wall, EP_EM_SLIPPERY_WALL },
4566 { ep_gfx_crumbled, EP_GFX_CRUMBLED },
4568 { ep_editor_cascade_active, EP_EDITOR_CASCADE_ACTIVE },
4569 { ep_editor_cascade_inactive, EP_EDITOR_CASCADE_INACTIVE },
4571 { ep_obsolete, EP_OBSOLETE },
4578 // always start with reliable default values (element has no properties)
4579 // (but never initialize clipboard elements after the very first time)
4580 // (to be able to use clipboard elements between several levels)
4581 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4582 if (!IS_CLIPBOARD_ELEMENT(i) || !clipboard_elements_initialized)
4583 for (j = 0; j < NUM_ELEMENT_PROPERTIES; j++)
4584 SET_PROPERTY(i, j, FALSE);
4586 // set all base element properties from above array definitions
4587 for (i = 0; element_properties[i].elements != NULL; i++)
4588 for (j = 0; (element_properties[i].elements)[j] != -1; j++)
4589 SET_PROPERTY((element_properties[i].elements)[j],
4590 element_properties[i].property, TRUE);
4592 // copy properties to some elements that are only stored in level file
4593 for (i = 0; i < NUM_ELEMENT_PROPERTIES; i++)
4594 for (j = 0; copy_properties[j][0] != -1; j++)
4595 if (HAS_PROPERTY(copy_properties[j][0], i))
4596 for (k = 1; k <= 4; k++)
4597 SET_PROPERTY(copy_properties[j][k], i, TRUE);
4599 // set static element properties that are not listed in array definitions
4600 for (i = EL_STEEL_CHAR_START; i <= EL_STEEL_CHAR_END; i++)
4601 SET_PROPERTY(i, EP_INDESTRUCTIBLE, TRUE);
4603 clipboard_elements_initialized = TRUE;
4606 void InitElementPropertiesEngine(int engine_version)
4608 static int no_wall_properties[] =
4611 EP_COLLECTIBLE_ONLY,
4613 EP_DONT_COLLIDE_WITH,
4616 EP_CAN_SMASH_PLAYER,
4617 EP_CAN_SMASH_ENEMIES,
4618 EP_CAN_SMASH_EVERYTHING,
4623 EP_FOOD_DARK_YAMYAM,
4639 /* important: after initialization in InitElementPropertiesStatic(), the
4640 elements are not again initialized to a default value; therefore all
4641 changes have to make sure that they leave the element with a defined
4642 property (which means that conditional property changes must be set to
4643 a reliable default value before) */
4645 // resolve group elements
4646 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
4647 ResolveGroupElement(EL_GROUP_START + i);
4649 // set all special, combined or engine dependent element properties
4650 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4652 // do not change (already initialized) clipboard elements here
4653 if (IS_CLIPBOARD_ELEMENT(i))
4656 // ---------- INACTIVE ----------------------------------------------------
4657 SET_PROPERTY(i, EP_INACTIVE, ((i >= EL_CHAR_START &&
4658 i <= EL_CHAR_END) ||
4659 (i >= EL_STEEL_CHAR_START &&
4660 i <= EL_STEEL_CHAR_END)));
4662 // ---------- WALKABLE, PASSABLE, ACCESSIBLE ------------------------------
4663 SET_PROPERTY(i, EP_WALKABLE, (IS_WALKABLE_OVER(i) ||
4664 IS_WALKABLE_INSIDE(i) ||
4665 IS_WALKABLE_UNDER(i)));
4667 SET_PROPERTY(i, EP_PASSABLE, (IS_PASSABLE_OVER(i) ||
4668 IS_PASSABLE_INSIDE(i) ||
4669 IS_PASSABLE_UNDER(i)));
4671 SET_PROPERTY(i, EP_ACCESSIBLE_OVER, (IS_WALKABLE_OVER(i) ||
4672 IS_PASSABLE_OVER(i)));
4674 SET_PROPERTY(i, EP_ACCESSIBLE_INSIDE, (IS_WALKABLE_INSIDE(i) ||
4675 IS_PASSABLE_INSIDE(i)));
4677 SET_PROPERTY(i, EP_ACCESSIBLE_UNDER, (IS_WALKABLE_UNDER(i) ||
4678 IS_PASSABLE_UNDER(i)));
4680 SET_PROPERTY(i, EP_ACCESSIBLE, (IS_WALKABLE(i) ||
4683 // ---------- COLLECTIBLE -------------------------------------------------
4684 SET_PROPERTY(i, EP_COLLECTIBLE, (IS_COLLECTIBLE_ONLY(i) ||
4688 // ---------- SNAPPABLE ---------------------------------------------------
4689 SET_PROPERTY(i, EP_SNAPPABLE, (IS_DIGGABLE(i) ||
4690 IS_COLLECTIBLE(i) ||
4694 // ---------- WALL --------------------------------------------------------
4695 SET_PROPERTY(i, EP_WALL, TRUE); // default: element is wall
4697 for (j = 0; no_wall_properties[j] != -1; j++)
4698 if (HAS_PROPERTY(i, no_wall_properties[j]) ||
4699 i >= EL_FIRST_RUNTIME_UNREAL)
4700 SET_PROPERTY(i, EP_WALL, FALSE);
4702 if (IS_HISTORIC_WALL(i))
4703 SET_PROPERTY(i, EP_WALL, TRUE);
4705 // ---------- SOLID_FOR_PUSHING -------------------------------------------
4706 if (engine_version < VERSION_IDENT(2,2,0,0))
4707 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, IS_HISTORIC_SOLID(i));
4709 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, (!IS_WALKABLE(i) &&
4711 !IS_COLLECTIBLE(i)));
4713 // ---------- DRAGONFIRE_PROOF --------------------------------------------
4714 if (IS_HISTORIC_SOLID(i) || i == EL_EXPLOSION)
4715 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, TRUE);
4717 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, (IS_INDESTRUCTIBLE(i) &&
4720 // ---------- EXPLOSION_PROOF ---------------------------------------------
4722 SET_PROPERTY(i, EP_EXPLOSION_PROOF, TRUE);
4723 else if (engine_version < VERSION_IDENT(2,2,0,0))
4724 SET_PROPERTY(i, EP_EXPLOSION_PROOF, IS_INDESTRUCTIBLE(i));
4726 SET_PROPERTY(i, EP_EXPLOSION_PROOF, (IS_INDESTRUCTIBLE(i) &&
4730 if (IS_CUSTOM_ELEMENT(i))
4732 // these are additional properties which are initially false when set
4734 // ---------- DONT_COLLIDE_WITH / DONT_RUN_INTO -------------------------
4736 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH, TRUE);
4737 if (DONT_COLLIDE_WITH(i))
4738 SET_PROPERTY(i, EP_DONT_RUN_INTO, TRUE);
4740 // ---------- CAN_SMASH_ENEMIES / CAN_SMASH_PLAYER ----------------------
4741 if (CAN_SMASH_EVERYTHING(i))
4742 SET_PROPERTY(i, EP_CAN_SMASH_ENEMIES, TRUE);
4743 if (CAN_SMASH_ENEMIES(i))
4744 SET_PROPERTY(i, EP_CAN_SMASH_PLAYER, TRUE);
4747 // ---------- CAN_SMASH ---------------------------------------------------
4748 SET_PROPERTY(i, EP_CAN_SMASH, (CAN_SMASH_PLAYER(i) ||
4749 CAN_SMASH_ENEMIES(i) ||
4750 CAN_SMASH_EVERYTHING(i)));
4752 // ---------- CAN_EXPLODE_BY_FIRE -----------------------------------------
4753 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_FIRE, (CAN_EXPLODE(i) &&
4754 EXPLODES_BY_FIRE(i)));
4756 // ---------- CAN_EXPLODE_SMASHED -----------------------------------------
4757 SET_PROPERTY(i, EP_CAN_EXPLODE_SMASHED, (CAN_EXPLODE(i) &&
4758 EXPLODES_SMASHED(i)));
4760 // ---------- CAN_EXPLODE_IMPACT ------------------------------------------
4761 SET_PROPERTY(i, EP_CAN_EXPLODE_IMPACT, (CAN_EXPLODE(i) &&
4762 EXPLODES_IMPACT(i)));
4764 // ---------- CAN_EXPLODE_BY_DRAGONFIRE -----------------------------------
4765 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_DRAGONFIRE, CAN_EXPLODE_BY_FIRE(i));
4767 // ---------- CAN_EXPLODE_BY_EXPLOSION ------------------------------------
4768 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_EXPLOSION, (CAN_EXPLODE_BY_FIRE(i) ||
4769 i == EL_BLACK_ORB));
4771 // ---------- COULD_MOVE_INTO_ACID ----------------------------------------
4772 SET_PROPERTY(i, EP_COULD_MOVE_INTO_ACID, (IS_PLAYER_ELEMENT(i) ||
4774 IS_CUSTOM_ELEMENT(i)));
4776 // ---------- MAYBE_DONT_COLLIDE_WITH -------------------------------------
4777 SET_PROPERTY(i, EP_MAYBE_DONT_COLLIDE_WITH, (i == EL_SP_SNIKSNAK ||
4778 i == EL_SP_ELECTRON));
4780 // ---------- CAN_MOVE_INTO_ACID ------------------------------------------
4781 if (COULD_MOVE_INTO_ACID(i) && !IS_CUSTOM_ELEMENT(i))
4782 SET_PROPERTY(i, EP_CAN_MOVE_INTO_ACID,
4783 getMoveIntoAcidProperty(&level, i));
4785 // ---------- DONT_COLLIDE_WITH -------------------------------------------
4786 if (MAYBE_DONT_COLLIDE_WITH(i))
4787 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH,
4788 getDontCollideWithProperty(&level, i));
4790 // ---------- SP_PORT -----------------------------------------------------
4791 SET_PROPERTY(i, EP_SP_PORT, (IS_SP_ELEMENT(i) &&
4792 IS_PASSABLE_INSIDE(i)));
4794 // ---------- CAN_BE_CLONED_BY_ANDROID ------------------------------------
4795 for (j = 0; j < level.num_android_clone_elements; j++)
4796 SET_PROPERTY(i, EP_CAN_BE_CLONED_BY_ANDROID,
4798 IS_EQUAL_OR_IN_GROUP(i, level.android_clone_element[j])));
4800 // ---------- CAN_CHANGE --------------------------------------------------
4801 SET_PROPERTY(i, EP_CAN_CHANGE, FALSE); // default: cannot change
4802 for (j = 0; j < element_info[i].num_change_pages; j++)
4803 if (element_info[i].change_page[j].can_change)
4804 SET_PROPERTY(i, EP_CAN_CHANGE, TRUE);
4806 // ---------- HAS_ACTION --------------------------------------------------
4807 SET_PROPERTY(i, EP_HAS_ACTION, FALSE); // default: has no action
4808 for (j = 0; j < element_info[i].num_change_pages; j++)
4809 if (element_info[i].change_page[j].has_action)
4810 SET_PROPERTY(i, EP_HAS_ACTION, TRUE);
4812 // ---------- CAN_CHANGE_OR_HAS_ACTION ------------------------------------
4813 SET_PROPERTY(i, EP_CAN_CHANGE_OR_HAS_ACTION, (CAN_CHANGE(i) ||
4816 // ---------- GFX_CRUMBLED ------------------------------------------------
4817 SET_PROPERTY(i, EP_GFX_CRUMBLED,
4818 element_info[i].crumbled[ACTION_DEFAULT] !=
4819 element_info[i].graphic[ACTION_DEFAULT]);
4821 // ---------- EDITOR_CASCADE ----------------------------------------------
4822 SET_PROPERTY(i, EP_EDITOR_CASCADE, (IS_EDITOR_CASCADE_ACTIVE(i) ||
4823 IS_EDITOR_CASCADE_INACTIVE(i)));
4826 // dynamically adjust element properties according to game engine version
4828 static int ep_em_slippery_wall[] =
4833 EL_EXPANDABLE_WALL_HORIZONTAL,
4834 EL_EXPANDABLE_WALL_VERTICAL,
4835 EL_EXPANDABLE_WALL_ANY,
4836 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
4837 EL_EXPANDABLE_STEELWALL_VERTICAL,
4838 EL_EXPANDABLE_STEELWALL_ANY,
4839 EL_EXPANDABLE_STEELWALL_GROWING,
4843 static int ep_em_explodes_by_fire[] =
4846 EL_EM_DYNAMITE_ACTIVE,
4851 // special EM style gems behaviour
4852 for (i = 0; ep_em_slippery_wall[i] != -1; i++)
4853 SET_PROPERTY(ep_em_slippery_wall[i], EP_EM_SLIPPERY_WALL,
4854 level.em_slippery_gems);
4856 // "EL_EXPANDABLE_WALL_GROWING" wasn't slippery for EM gems in 2.0.1
4857 SET_PROPERTY(EL_EXPANDABLE_WALL_GROWING, EP_EM_SLIPPERY_WALL,
4858 (level.em_slippery_gems &&
4859 engine_version > VERSION_IDENT(2,0,1,0)));
4861 // special EM style explosion behaviour regarding chain reactions
4862 for (i = 0; ep_em_explodes_by_fire[i] != -1; i++)
4863 SET_PROPERTY(ep_em_explodes_by_fire[i], EP_EXPLODES_BY_FIRE,
4864 level.em_explodes_by_fire);
4867 // this is needed because some graphics depend on element properties
4868 if (game_status == GAME_MODE_PLAYING)
4869 InitElementGraphicInfo();
4872 void InitElementPropertiesGfxElement(void)
4876 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4878 struct ElementInfo *ei = &element_info[i];
4880 ei->gfx_element = (ei->use_gfx_element ? ei->gfx_element_initial : i);
4884 static void InitGlobal(void)
4889 for (i = 0; i < MAX_NUM_ELEMENTS + 1; i++)
4891 // check if element_name_info entry defined for each element in "main.h"
4892 if (i < MAX_NUM_ELEMENTS && element_name_info[i].token_name == NULL)
4893 Fail("undefined 'element_name_info' entry for element %d", i);
4895 element_info[i].token_name = element_name_info[i].token_name;
4896 element_info[i].class_name = element_name_info[i].class_name;
4897 element_info[i].editor_description= element_name_info[i].editor_description;
4900 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS + 1; i++)
4902 // check if global_anim_name_info defined for each entry in "main.h"
4903 if (i < NUM_GLOBAL_ANIM_TOKENS &&
4904 global_anim_name_info[i].token_name == NULL)
4905 Fail("undefined 'global_anim_name_info' entry for anim %d", i);
4907 global_anim_info[i].token_name = global_anim_name_info[i].token_name;
4910 // create hash from image config list
4911 image_config_hash = newSetupFileHash();
4912 for (i = 0; image_config[i].token != NULL; i++)
4913 setHashEntry(image_config_hash,
4914 image_config[i].token,
4915 image_config[i].value);
4917 // create hash from element token list
4918 element_token_hash = newSetupFileHash();
4919 for (i = 0; element_name_info[i].token_name != NULL; i++)
4920 setHashEntry(element_token_hash,
4921 element_name_info[i].token_name,
4924 // create hash from graphic token list
4925 graphic_token_hash = newSetupFileHash();
4926 for (graphic = 0, i = 0; image_config[i].token != NULL; i++)
4927 if (strSuffix(image_config[i].value, ".png") ||
4928 strSuffix(image_config[i].value, ".pcx") ||
4929 strSuffix(image_config[i].value, ".wav") ||
4930 strEqual(image_config[i].value, UNDEFINED_FILENAME))
4931 setHashEntry(graphic_token_hash,
4932 image_config[i].token,
4933 int2str(graphic++, 0));
4935 // create hash from font token list
4936 font_token_hash = newSetupFileHash();
4937 for (i = 0; font_info[i].token_name != NULL; i++)
4938 setHashEntry(font_token_hash,
4939 font_info[i].token_name,
4942 // set default filenames for all cloned graphics in static configuration
4943 for (i = 0; image_config[i].token != NULL; i++)
4945 if (strEqual(image_config[i].value, UNDEFINED_FILENAME))
4947 char *token = image_config[i].token;
4948 char *token_clone_from = getStringCat2(token, ".clone_from");
4949 char *token_cloned = getHashEntry(image_config_hash, token_clone_from);
4951 if (token_cloned != NULL)
4953 char *value_cloned = getHashEntry(image_config_hash, token_cloned);
4955 if (value_cloned != NULL)
4957 // set default filename in static configuration
4958 image_config[i].value = value_cloned;
4960 // set default filename in image config hash
4961 setHashEntry(image_config_hash, token, value_cloned);
4965 free(token_clone_from);
4969 // always start with reliable default values (all elements)
4970 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4971 ActiveElement[i] = i;
4973 // now add all entries that have an active state (active elements)
4974 for (i = 0; element_with_active_state[i].element != -1; i++)
4976 int element = element_with_active_state[i].element;
4977 int element_active = element_with_active_state[i].element_active;
4979 ActiveElement[element] = element_active;
4982 // always start with reliable default values (all buttons)
4983 for (i = 0; i < NUM_IMAGE_FILES; i++)
4984 ActiveButton[i] = i;
4986 // now add all entries that have an active state (active buttons)
4987 for (i = 0; button_with_active_state[i].button != -1; i++)
4989 int button = button_with_active_state[i].button;
4990 int button_active = button_with_active_state[i].button_active;
4992 ActiveButton[button] = button_active;
4995 // always start with reliable default values (all fonts)
4996 for (i = 0; i < NUM_FONTS; i++)
4999 // now add all entries that have an active state (active fonts)
5000 for (i = 0; font_with_active_state[i].font_nr != -1; i++)
5002 int font = font_with_active_state[i].font_nr;
5003 int font_active = font_with_active_state[i].font_nr_active;
5005 ActiveFont[font] = font_active;
5008 global.autoplay_leveldir = NULL;
5009 global.patchtapes_leveldir = NULL;
5010 global.convert_leveldir = NULL;
5011 global.dumplevel_leveldir = NULL;
5012 global.dumptape_leveldir = NULL;
5013 global.create_sketch_images_dir = NULL;
5014 global.create_collect_images_dir = NULL;
5016 global.frames_per_second = 0;
5017 global.show_frames_per_second = FALSE;
5019 global.border_status = GAME_MODE_LOADING;
5020 global.anim_status = global.anim_status_next = GAME_MODE_LOADING;
5022 global.use_envelope_request = FALSE;
5024 global.user_names = NULL;
5027 static void Execute_Command(char *command)
5031 if (strEqual(command, "print graphicsinfo.conf"))
5033 Print("# You can configure additional/alternative image files here.\n");
5034 Print("# (The entries below are default and therefore commented out.)\n");
5036 Print("%s\n", getFormattedSetupEntry("name", "Classic Graphics"));
5038 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5041 for (i = 0; image_config[i].token != NULL; i++)
5042 Print("# %s\n", getFormattedSetupEntry(image_config[i].token,
5043 image_config[i].value));
5047 else if (strEqual(command, "print soundsinfo.conf"))
5049 Print("# You can configure additional/alternative sound files here.\n");
5050 Print("# (The entries below are default and therefore commented out.)\n");
5052 Print("%s\n", getFormattedSetupEntry("name", "Classic Sounds"));
5054 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5057 for (i = 0; sound_config[i].token != NULL; i++)
5058 Print("# %s\n", getFormattedSetupEntry(sound_config[i].token,
5059 sound_config[i].value));
5063 else if (strEqual(command, "print musicinfo.conf"))
5065 Print("# You can configure additional/alternative music files here.\n");
5066 Print("# (The entries below are default and therefore commented out.)\n");
5068 Print("%s\n", getFormattedSetupEntry("name", "Classic Music"));
5070 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5073 for (i = 0; music_config[i].token != NULL; i++)
5074 Print("# %s\n", getFormattedSetupEntry(music_config[i].token,
5075 music_config[i].value));
5079 else if (strEqual(command, "print editorsetup.conf"))
5081 Print("# You can configure your personal editor element list here.\n");
5082 Print("# (The entries below are default and therefore commented out.)\n");
5085 // this is needed to be able to check element list for cascade elements
5086 InitElementPropertiesStatic();
5087 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
5089 PrintEditorElementList();
5093 else if (strEqual(command, "print helpanim.conf"))
5095 Print("# You can configure different element help animations here.\n");
5096 Print("# (The entries below are default and therefore commented out.)\n");
5099 for (i = 0; helpanim_config[i].token != NULL; i++)
5101 Print("# %s\n", getFormattedSetupEntry(helpanim_config[i].token,
5102 helpanim_config[i].value));
5104 if (strEqual(helpanim_config[i].token, "end"))
5110 else if (strEqual(command, "print helptext.conf"))
5112 Print("# You can configure different element help text here.\n");
5113 Print("# (The entries below are default and therefore commented out.)\n");
5116 for (i = 0; helptext_config[i].token != NULL; i++)
5117 Print("# %s\n", getFormattedSetupEntry(helptext_config[i].token,
5118 helptext_config[i].value));
5122 else if (strPrefix(command, "dump level "))
5124 char *filename = &command[11];
5126 if (fileExists(filename))
5128 LoadLevelFromFilename(&level, filename);
5134 char *leveldir = getStringCopy(filename); // read command parameters
5135 char *level_nr = strchr(leveldir, ' ');
5137 if (level_nr == NULL)
5138 Fail("cannot open file '%s'", filename);
5142 global.dumplevel_leveldir = leveldir;
5143 global.dumplevel_level_nr = atoi(level_nr);
5145 program.headless = TRUE;
5147 else if (strPrefix(command, "dump tape "))
5149 char *filename = &command[10];
5151 if (fileExists(filename))
5153 LoadTapeFromFilename(filename);
5159 char *leveldir = getStringCopy(filename); // read command parameters
5160 char *level_nr = strchr(leveldir, ' ');
5162 if (level_nr == NULL)
5163 Fail("cannot open file '%s'", filename);
5167 global.dumptape_leveldir = leveldir;
5168 global.dumptape_level_nr = atoi(level_nr);
5170 program.headless = TRUE;
5172 else if (strPrefix(command, "autoplay ") ||
5173 strPrefix(command, "autoffwd ") ||
5174 strPrefix(command, "autowarp ") ||
5175 strPrefix(command, "autotest ") ||
5176 strPrefix(command, "autosave ") ||
5177 strPrefix(command, "autoupload ") ||
5178 strPrefix(command, "autofix "))
5180 char *arg_ptr = strchr(command, ' ');
5181 char *str_ptr = getStringCopy(arg_ptr); // read command parameters
5183 global.autoplay_mode =
5184 (strPrefix(command, "autoplay") ? AUTOPLAY_MODE_PLAY :
5185 strPrefix(command, "autoffwd") ? AUTOPLAY_MODE_FFWD :
5186 strPrefix(command, "autowarp") ? AUTOPLAY_MODE_WARP :
5187 strPrefix(command, "autotest") ? AUTOPLAY_MODE_TEST :
5188 strPrefix(command, "autosave") ? AUTOPLAY_MODE_SAVE :
5189 strPrefix(command, "autoupload") ? AUTOPLAY_MODE_UPLOAD :
5190 strPrefix(command, "autofix") ? AUTOPLAY_MODE_FIX :
5191 AUTOPLAY_MODE_NONE);
5193 while (*str_ptr != '\0') // continue parsing string
5195 // cut leading whitespace from string, replace it by string terminator
5196 while (*str_ptr == ' ' || *str_ptr == '\t')
5199 if (*str_ptr == '\0') // end of string reached
5202 if (global.autoplay_leveldir == NULL) // read level set string
5204 global.autoplay_leveldir = str_ptr;
5205 global.autoplay_all = TRUE; // default: play all tapes
5207 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5208 global.autoplay_level[i] = FALSE;
5210 else // read level number string
5212 int level_nr = atoi(str_ptr); // get level_nr value
5214 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5215 global.autoplay_level[level_nr] = TRUE;
5217 global.autoplay_all = FALSE;
5220 // advance string pointer to the next whitespace (or end of string)
5221 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5225 if (global.autoplay_mode & AUTOPLAY_WARP_NO_DISPLAY)
5226 program.headless = TRUE;
5228 else if (strPrefix(command, "patch tapes "))
5230 char *str_ptr = getStringCopy(&command[12]); // read command parameters
5232 // skip leading whitespace
5233 while (*str_ptr == ' ' || *str_ptr == '\t')
5236 if (*str_ptr == '\0')
5237 Fail("cannot find MODE in command '%s'", command);
5239 global.patchtapes_mode = str_ptr; // store patch mode
5241 // advance to next whitespace (or end of string)
5242 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5245 while (*str_ptr != '\0') // continue parsing string
5247 // cut leading whitespace from string, replace it by string terminator
5248 while (*str_ptr == ' ' || *str_ptr == '\t')
5251 if (*str_ptr == '\0') // end of string reached
5254 if (global.patchtapes_leveldir == NULL) // read level set string
5256 global.patchtapes_leveldir = str_ptr;
5257 global.patchtapes_all = TRUE; // default: patch all tapes
5259 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5260 global.patchtapes_level[i] = FALSE;
5262 else // read level number string
5264 int level_nr = atoi(str_ptr); // get level_nr value
5266 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5267 global.patchtapes_level[level_nr] = TRUE;
5269 global.patchtapes_all = FALSE;
5272 // advance string pointer to the next whitespace (or end of string)
5273 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5277 if (global.patchtapes_leveldir == NULL)
5279 if (strEqual(global.patchtapes_mode, "help"))
5280 global.patchtapes_leveldir = UNDEFINED_LEVELSET;
5282 Fail("cannot find LEVELDIR in command '%s'", command);
5285 program.headless = TRUE;
5287 else if (strPrefix(command, "convert "))
5289 char *str_copy = getStringCopy(strchr(command, ' ') + 1);
5290 char *str_ptr = strchr(str_copy, ' ');
5292 global.convert_leveldir = str_copy;
5293 global.convert_level_nr = -1;
5295 if (str_ptr != NULL) // level number follows
5297 *str_ptr++ = '\0'; // terminate leveldir string
5298 global.convert_level_nr = atoi(str_ptr); // get level_nr value
5301 program.headless = TRUE;
5303 else if (strPrefix(command, "create sketch images "))
5305 global.create_sketch_images_dir = getStringCopy(&command[21]);
5307 if (access(global.create_sketch_images_dir, W_OK) != 0)
5308 Fail("image target directory '%s' not found or not writable",
5309 global.create_sketch_images_dir);
5311 else if (strPrefix(command, "create collect image "))
5313 global.create_collect_images_dir = getStringCopy(&command[21]);
5315 if (access(global.create_collect_images_dir, W_OK) != 0)
5316 Fail("image target directory '%s' not found or not writable",
5317 global.create_collect_images_dir);
5319 else if (strPrefix(command, "create CE image "))
5321 CreateCustomElementImages(&command[16]);
5327 FailWithHelp("unrecognized command '%s'", command);
5330 // disable networking if any valid command was recognized
5331 options.network = setup.network_mode = FALSE;
5334 static void InitSetup(void)
5336 LoadUserNames(); // global user names
5337 LoadUserSetup(); // global user number
5339 LoadSetup(); // global setup info
5341 // set some options from setup file
5343 if (setup.options.verbose)
5344 options.verbose = TRUE;
5346 if (setup.debug.show_frames_per_second)
5347 global.show_frames_per_second = TRUE;
5350 static void InitGameInfo(void)
5352 game.restart_level = FALSE;
5353 game.restart_game_message = NULL;
5355 game.request_active = FALSE;
5356 game.request_active_or_moving = FALSE;
5358 game.use_masked_elements_initial = FALSE;
5361 static void InitPlayerInfo(void)
5365 // choose default local player
5366 local_player = &stored_player[0];
5368 for (i = 0; i < MAX_PLAYERS; i++)
5370 stored_player[i].connected_locally = FALSE;
5371 stored_player[i].connected_network = FALSE;
5374 local_player->connected_locally = TRUE;
5377 static void InitArtworkInfo(void)
5382 static char *get_string_in_brackets(char *string)
5384 char *string_in_brackets = checked_malloc(strlen(string) + 3);
5386 sprintf(string_in_brackets, "[%s]", string);
5388 return string_in_brackets;
5391 static char *get_level_id_suffix(int id_nr)
5393 char *id_suffix = checked_malloc(1 + 3 + 1);
5395 if (id_nr < 0 || id_nr > 999)
5398 sprintf(id_suffix, ".%03d", id_nr);
5403 static void InitArtworkConfig(void)
5405 static char *image_id_prefix[MAX_NUM_ELEMENTS +
5407 NUM_GLOBAL_ANIM_TOKENS + 1];
5408 static char *sound_id_prefix[2 * MAX_NUM_ELEMENTS +
5409 NUM_GLOBAL_ANIM_TOKENS + 1];
5410 static char *music_id_prefix[NUM_MUSIC_PREFIXES +
5411 NUM_GLOBAL_ANIM_TOKENS + 1];
5412 static char *action_id_suffix[NUM_ACTIONS + 1];
5413 static char *direction_id_suffix[NUM_DIRECTIONS_FULL + 1];
5414 static char *special_id_suffix[NUM_SPECIAL_GFX_ARGS + 1];
5415 static char *level_id_suffix[MAX_LEVELS + 1];
5416 static char *dummy[1] = { NULL };
5417 static char *ignore_generic_tokens[] =
5422 "program_copyright",
5427 static char **ignore_image_tokens;
5428 static char **ignore_sound_tokens;
5429 static char **ignore_music_tokens;
5430 int num_ignore_generic_tokens;
5431 int num_ignore_image_tokens;
5432 int num_ignore_sound_tokens;
5433 int num_ignore_music_tokens;
5436 // dynamically determine list of generic tokens to be ignored
5437 num_ignore_generic_tokens = 0;
5438 for (i = 0; ignore_generic_tokens[i] != NULL; i++)
5439 num_ignore_generic_tokens++;
5441 // dynamically determine list of image tokens to be ignored
5442 num_ignore_image_tokens = num_ignore_generic_tokens;
5443 for (i = 0; image_config_vars[i].token != NULL; i++)
5444 num_ignore_image_tokens++;
5445 ignore_image_tokens =
5446 checked_malloc((num_ignore_image_tokens + 1) * sizeof(char *));
5447 for (i = 0; i < num_ignore_generic_tokens; i++)
5448 ignore_image_tokens[i] = ignore_generic_tokens[i];
5449 for (i = 0; i < num_ignore_image_tokens - num_ignore_generic_tokens; i++)
5450 ignore_image_tokens[num_ignore_generic_tokens + i] =
5451 image_config_vars[i].token;
5452 ignore_image_tokens[num_ignore_image_tokens] = NULL;
5454 // dynamically determine list of sound tokens to be ignored
5455 num_ignore_sound_tokens = num_ignore_generic_tokens;
5456 ignore_sound_tokens =
5457 checked_malloc((num_ignore_sound_tokens + 1) * sizeof(char *));
5458 for (i = 0; i < num_ignore_generic_tokens; i++)
5459 ignore_sound_tokens[i] = ignore_generic_tokens[i];
5460 ignore_sound_tokens[num_ignore_sound_tokens] = NULL;
5462 // dynamically determine list of music tokens to be ignored
5463 num_ignore_music_tokens = num_ignore_generic_tokens;
5464 ignore_music_tokens =
5465 checked_malloc((num_ignore_music_tokens + 1) * sizeof(char *));
5466 for (i = 0; i < num_ignore_generic_tokens; i++)
5467 ignore_music_tokens[i] = ignore_generic_tokens[i];
5468 ignore_music_tokens[num_ignore_music_tokens] = NULL;
5470 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5471 image_id_prefix[i] = element_info[i].token_name;
5472 for (i = 0; i < NUM_FONTS; i++)
5473 image_id_prefix[MAX_NUM_ELEMENTS + i] = font_info[i].token_name;
5474 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5475 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + i] =
5476 global_anim_info[i].token_name;
5477 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5479 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5480 sound_id_prefix[i] = element_info[i].token_name;
5481 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5482 sound_id_prefix[MAX_NUM_ELEMENTS + i] =
5483 get_string_in_brackets(element_info[i].class_name);
5484 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5485 sound_id_prefix[2 * MAX_NUM_ELEMENTS + i] =
5486 global_anim_info[i].token_name;
5487 sound_id_prefix[2 * MAX_NUM_ELEMENTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5489 for (i = 0; i < NUM_MUSIC_PREFIXES; i++)
5490 music_id_prefix[i] = music_prefix_info[i].prefix;
5491 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5492 music_id_prefix[NUM_MUSIC_PREFIXES + i] =
5493 global_anim_info[i].token_name;
5494 music_id_prefix[NUM_MUSIC_PREFIXES + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5496 for (i = 0; i < NUM_ACTIONS; i++)
5497 action_id_suffix[i] = element_action_info[i].suffix;
5498 action_id_suffix[NUM_ACTIONS] = NULL;
5500 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
5501 direction_id_suffix[i] = element_direction_info[i].suffix;
5502 direction_id_suffix[NUM_DIRECTIONS_FULL] = NULL;
5504 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
5505 special_id_suffix[i] = special_suffix_info[i].suffix;
5506 special_id_suffix[NUM_SPECIAL_GFX_ARGS] = NULL;
5508 for (i = 0; i < MAX_LEVELS; i++)
5509 level_id_suffix[i] = get_level_id_suffix(i);
5510 level_id_suffix[MAX_LEVELS] = NULL;
5512 InitImageList(image_config, NUM_IMAGE_FILES, image_config_suffix,
5513 image_id_prefix, action_id_suffix, direction_id_suffix,
5514 special_id_suffix, ignore_image_tokens);
5515 InitSoundList(sound_config, NUM_SOUND_FILES, sound_config_suffix,
5516 sound_id_prefix, action_id_suffix, dummy,
5517 special_id_suffix, ignore_sound_tokens);
5518 InitMusicList(music_config, NUM_MUSIC_FILES, music_config_suffix,
5519 music_id_prefix, action_id_suffix, special_id_suffix,
5520 level_id_suffix, ignore_music_tokens);
5523 static void InitMixer(void)
5530 static void InitVideoOverlay(void)
5532 // if virtual buttons are not loaded from setup file, repeat initializing
5533 // virtual buttons grid with default values now that video is initialized
5534 if (!setup.touch.grid_initialized)
5537 InitTileCursorInfo();
5541 void InitGfxBuffers(void)
5543 static int win_xsize_last = -1;
5544 static int win_ysize_last = -1;
5546 // create additional image buffers for double-buffering and cross-fading
5548 if (WIN_XSIZE != win_xsize_last || WIN_YSIZE != win_ysize_last)
5550 // used to temporarily store the backbuffer -- only re-create if changed
5551 ReCreateBitmap(&bitmap_db_store_1, WIN_XSIZE, WIN_YSIZE);
5552 ReCreateBitmap(&bitmap_db_store_2, WIN_XSIZE, WIN_YSIZE);
5554 win_xsize_last = WIN_XSIZE;
5555 win_ysize_last = WIN_YSIZE;
5558 ReCreateBitmap(&bitmap_db_field, FXSIZE, FYSIZE);
5559 ReCreateBitmap(&bitmap_db_panel, DXSIZE, DYSIZE);
5560 ReCreateBitmap(&bitmap_db_door_1, 3 * DXSIZE, DYSIZE);
5561 ReCreateBitmap(&bitmap_db_door_2, 3 * VXSIZE, VYSIZE);
5563 // initialize screen properties
5564 InitGfxFieldInfo(SX, SY, SXSIZE, SYSIZE,
5565 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
5567 InitGfxDoor1Info(DX, DY, DXSIZE, DYSIZE);
5568 InitGfxDoor2Info(VX, VY, VXSIZE, VYSIZE);
5569 InitGfxDoor3Info(EX, EY, EXSIZE, EYSIZE);
5570 InitGfxWindowInfo(WIN_XSIZE, WIN_YSIZE);
5571 InitGfxScrollbufferInfo(FXSIZE, FYSIZE);
5572 InitGfxClipRegion(FALSE, -1, -1, -1, -1);
5574 // required if door size definitions have changed
5575 InitGraphicCompatibilityInfo_Doors();
5577 InitGfxBuffers_EM();
5578 InitGfxBuffers_SP();
5581 static void InitGfx(void)
5583 struct GraphicInfo *graphic_info_last = graphic_info;
5584 char *filename_font_initial = NULL;
5585 char *filename_image_initial[NUM_INITIAL_IMAGES] = { NULL };
5586 char *image_token[NUM_INITIAL_IMAGES] =
5588 CONFIG_TOKEN_GLOBAL_BUSY_INITIAL,
5589 CONFIG_TOKEN_GLOBAL_BUSY,
5590 CONFIG_TOKEN_BACKGROUND_LOADING_INITIAL,
5591 CONFIG_TOKEN_BACKGROUND_LOADING
5593 struct MenuPosInfo *init_busy[NUM_INITIAL_IMAGES_BUSY] =
5598 Bitmap *bitmap_font_initial = NULL;
5599 int parameter[NUM_INITIAL_IMAGES][NUM_GFX_ARGS];
5602 // determine settings for initial font (for displaying startup messages)
5603 for (i = 0; image_config[i].token != NULL; i++)
5605 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5607 char font_token[128];
5610 sprintf(font_token, "%s_%d", CONFIG_TOKEN_FONT_INITIAL, j + 1);
5611 len_font_token = strlen(font_token);
5613 if (strEqual(image_config[i].token, font_token))
5615 filename_font_initial = image_config[i].value;
5617 else if (strlen(image_config[i].token) > len_font_token &&
5618 strncmp(image_config[i].token, font_token, len_font_token) == 0)
5620 if (strEqual(&image_config[i].token[len_font_token], ".x"))
5621 font_initial[j].src_x = atoi(image_config[i].value);
5622 else if (strEqual(&image_config[i].token[len_font_token], ".y"))
5623 font_initial[j].src_y = atoi(image_config[i].value);
5624 else if (strEqual(&image_config[i].token[len_font_token], ".width"))
5625 font_initial[j].width = atoi(image_config[i].value);
5626 else if (strEqual(&image_config[i].token[len_font_token], ".height"))
5627 font_initial[j].height = atoi(image_config[i].value);
5632 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5634 font_initial[j].num_chars = DEFAULT_NUM_CHARS_PER_FONT;
5635 font_initial[j].num_chars_per_line = DEFAULT_NUM_CHARS_PER_LINE;
5638 if (filename_font_initial == NULL) // should not happen
5639 Fail("cannot get filename for '%s'", CONFIG_TOKEN_FONT_INITIAL);
5642 InitGfxCustomArtworkInfo();
5643 InitGfxOtherSettings();
5645 InitGfxTileSizeInfo(TILESIZE, TILESIZE);
5647 bitmap_font_initial = LoadCustomImage(filename_font_initial);
5649 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5650 font_initial[j].bitmap = bitmap_font_initial;
5652 InitFontGraphicInfo();
5656 DrawInitTextHead("Loading graphics");
5658 InitMenuDesignSettings_Static();
5660 // initialize settings for initial images with default values
5661 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5662 for (j = 0; j < NUM_GFX_ARGS; j++)
5664 get_graphic_parameter_value(image_config_suffix[j].value,
5665 image_config_suffix[j].token,
5666 image_config_suffix[j].type);
5668 // read settings for initial images from default custom artwork config
5669 char *gfx_config_filename = getPath3(options.graphics_directory,
5671 GRAPHICSINFO_FILENAME);
5673 if (fileExists(gfx_config_filename))
5675 SetupFileHash *setup_file_hash = loadSetupFileHash(gfx_config_filename);
5677 if (setup_file_hash)
5679 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5681 char *filename = getHashEntry(setup_file_hash, image_token[i]);
5685 filename_image_initial[i] = getStringCopy(filename);
5687 for (j = 0; image_config_suffix[j].token != NULL; j++)
5689 int type = image_config_suffix[j].type;
5690 char *suffix = image_config_suffix[j].token;
5691 char *token = getStringCat2(image_token[i], suffix);
5692 char *value = getHashEntry(setup_file_hash, token);
5694 checked_free(token);
5698 get_graphic_parameter_value(value, suffix, type);
5703 // read values from custom graphics config file
5704 InitMenuDesignSettings_FromHash(setup_file_hash, FALSE);
5706 freeSetupFileHash(setup_file_hash);
5710 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5712 if (filename_image_initial[i] == NULL)
5714 int len_token = strlen(image_token[i]);
5716 // read settings for initial images from static default artwork config
5717 for (j = 0; image_config[j].token != NULL; j++)
5719 if (strEqual(image_config[j].token, image_token[i]))
5721 filename_image_initial[i] = getStringCopy(image_config[j].value);
5723 else if (strlen(image_config[j].token) > len_token &&
5724 strncmp(image_config[j].token, image_token[i], len_token) == 0)
5726 for (k = 0; image_config_suffix[k].token != NULL; k++)
5728 if (strEqual(&image_config[j].token[len_token],
5729 image_config_suffix[k].token))
5731 get_graphic_parameter_value(image_config[j].value,
5732 image_config_suffix[k].token,
5733 image_config_suffix[k].type);
5740 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5742 if (filename_image_initial[i] == NULL) // should not happen
5743 Fail("cannot get filename for '%s'", image_token[i]);
5745 image_initial[i].bitmaps =
5746 checked_calloc(sizeof(Bitmap *) * NUM_IMG_BITMAP_POINTERS);
5748 if (!strEqual(filename_image_initial[i], UNDEFINED_FILENAME))
5749 image_initial[i].bitmaps[IMG_BITMAP_STANDARD] =
5750 LoadCustomImage(filename_image_initial[i]);
5752 checked_free(filename_image_initial[i]);
5755 graphic_info = image_initial; // graphic == 0 => image_initial
5757 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5758 set_graphic_parameters_ext(i, parameter[i], image_initial[i].bitmaps);
5760 graphic_info = graphic_info_last;
5762 for (i = 0; i < NUM_INITIAL_IMAGES_BUSY; i++)
5764 // set image size for busy animations
5765 init_busy[i]->width = image_initial[i].width;
5766 init_busy[i]->height = image_initial[i].height;
5769 SetLoadingBackgroundImage();
5771 ClearRectangleOnBackground(window, 0, 0, WIN_XSIZE, WIN_YSIZE);
5773 InitGfxDrawBusyAnimFunction(DrawInitAnim);
5774 InitGfxDrawGlobalAnimFunction(DrawGlobalAnimations);
5775 InitGfxDrawGlobalBorderFunction(DrawMaskedBorderToTarget);
5776 InitGfxDrawTileCursorFunction(DrawTileCursor);
5778 gfx.fade_border_source_status = global.border_status;
5779 gfx.fade_border_target_status = global.border_status;
5780 gfx.masked_border_bitmap_ptr = backbuffer;
5782 // use copy of busy animation to prevent change while reloading artwork
5786 static void InitGfxBackground(void)
5788 fieldbuffer = bitmap_db_field;
5789 SetDrawtoField(DRAW_TO_BACKBUFFER);
5791 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
5793 redraw_mask = REDRAW_ALL;
5796 static void InitLevelInfo(void)
5798 LoadLevelInfo(); // global level info
5799 LoadLevelSetup_LastSeries(); // last played series info
5800 LoadLevelSetup_SeriesInfo(); // last played level info
5802 if (global.autoplay_leveldir &&
5803 global.autoplay_mode != AUTOPLAY_MODE_TEST)
5805 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
5806 global.autoplay_leveldir);
5807 if (leveldir_current == NULL)
5808 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
5811 SetLevelSetInfo(leveldir_current->identifier, level_nr);
5814 static void InitLevelArtworkInfo(void)
5816 LoadLevelArtworkInfo();
5819 static void InitImages(void)
5821 print_timestamp_init("InitImages");
5824 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
5825 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5826 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
5827 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5828 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
5829 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5830 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
5831 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5834 setLevelArtworkDir(artwork.gfx_first);
5837 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
5838 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5839 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
5840 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5841 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
5842 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5843 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
5844 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5848 Debug("init:InitImages", "InitImages for '%s' ['%s', '%s'] ['%s', '%s']",
5849 leveldir_current->identifier,
5850 artwork.gfx_current_identifier,
5851 artwork.gfx_current->identifier,
5852 leveldir_current->graphics_set,
5853 leveldir_current->graphics_path);
5856 UPDATE_BUSY_STATE();
5858 ReloadCustomImages();
5859 print_timestamp_time("ReloadCustomImages");
5861 UPDATE_BUSY_STATE();
5863 LoadCustomElementDescriptions();
5864 print_timestamp_time("LoadCustomElementDescriptions");
5866 UPDATE_BUSY_STATE();
5868 LoadMenuDesignSettings();
5869 print_timestamp_time("LoadMenuDesignSettings");
5871 UPDATE_BUSY_STATE();
5873 ReinitializeGraphics();
5874 print_timestamp_time("ReinitializeGraphics");
5876 LoadMenuDesignSettings_AfterGraphics();
5877 print_timestamp_time("LoadMenuDesignSettings_AfterGraphics");
5879 UPDATE_BUSY_STATE();
5881 print_timestamp_done("InitImages");
5884 static void InitSound(char *identifier)
5886 print_timestamp_init("InitSound");
5888 if (identifier == NULL)
5889 identifier = artwork.snd_current->identifier;
5891 // set artwork path to send it to the sound server process
5892 setLevelArtworkDir(artwork.snd_first);
5894 InitReloadCustomSounds(identifier);
5895 print_timestamp_time("InitReloadCustomSounds");
5897 ReinitializeSounds();
5898 print_timestamp_time("ReinitializeSounds");
5900 print_timestamp_done("InitSound");
5903 static void InitMusic(char *identifier)
5905 print_timestamp_init("InitMusic");
5907 if (identifier == NULL)
5908 identifier = artwork.mus_current->identifier;
5910 // set artwork path to send it to the sound server process
5911 setLevelArtworkDir(artwork.mus_first);
5913 InitReloadCustomMusic(identifier);
5914 print_timestamp_time("InitReloadCustomMusic");
5916 ReinitializeMusic();
5917 print_timestamp_time("ReinitializeMusic");
5919 print_timestamp_done("InitMusic");
5922 static void InitArtworkDone(void)
5924 if (program.headless)
5927 InitGlobalAnimations();
5930 static void InitNetworkSettings(void)
5932 boolean network_enabled = (options.network || setup.network_mode);
5933 char *network_server = (options.server_host != NULL ? options.server_host :
5934 setup.network_server_hostname);
5936 if (strEqual(network_server, STR_NETWORK_AUTO_DETECT))
5937 network_server = NULL;
5939 InitNetworkInfo(network_enabled,
5943 options.server_port);
5946 void InitNetworkServer(void)
5948 if (!network.enabled || network.connected)
5951 LimitScreenUpdates(FALSE);
5953 if (game_status == GAME_MODE_LOADING)
5956 if (!ConnectToServer(network.server_host, network.server_port))
5958 network.enabled = FALSE;
5960 setup.network_mode = FALSE;
5964 SendToServer_ProtocolVersion();
5965 SendToServer_PlayerName(setup.player_name);
5966 SendToServer_NrWanted(setup.network_player_nr + 1);
5968 network.connected = TRUE;
5971 // short time to recognize result of network initialization
5972 if (game_status == GAME_MODE_LOADING)
5973 Delay_WithScreenUpdates(1000);
5976 static boolean CheckArtworkConfigForCustomElements(char *filename)
5978 SetupFileHash *setup_file_hash;
5979 boolean redefined_ce_found = FALSE;
5981 // !!! CACHE THIS BY USING HASH 'filename' => 'true/false' !!!
5983 if ((setup_file_hash = loadSetupFileHash(filename)) != NULL)
5985 BEGIN_HASH_ITERATION(setup_file_hash, itr)
5987 char *token = HASH_ITERATION_TOKEN(itr);
5989 if (strPrefix(token, "custom_"))
5991 redefined_ce_found = TRUE;
5996 END_HASH_ITERATION(setup_file_hash, itr)
5998 freeSetupFileHash(setup_file_hash);
6001 return redefined_ce_found;
6004 static boolean CheckArtworkTypeForRedefinedCustomElements(int type)
6006 char *filename_base, *filename_local;
6007 boolean redefined_ce_found = FALSE;
6009 setLevelArtworkDir(ARTWORK_FIRST_NODE(artwork, type));
6012 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6013 "leveldir_current->identifier == '%s'",
6014 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
6015 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6016 "leveldir_current->graphics_path == '%s'",
6017 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
6018 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6019 "leveldir_current->graphics_set == '%s'",
6020 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
6021 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6022 "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
6023 leveldir_current == NULL ? "[NULL]" :
6024 LEVELDIR_ARTWORK_SET(leveldir_current, type));
6027 // first look for special artwork configured in level series config
6028 filename_base = getCustomArtworkLevelConfigFilename(type);
6031 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6032 "filename_base == '%s'", filename_base);
6035 if (fileExists(filename_base))
6036 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_base);
6038 filename_local = getCustomArtworkConfigFilename(type);
6041 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6042 "filename_local == '%s'", filename_local);
6045 if (filename_local != NULL && !strEqual(filename_base, filename_local))
6046 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_local);
6049 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6050 "redefined_ce_found == %d", redefined_ce_found);
6053 return redefined_ce_found;
6056 static void InitOverrideArtwork(void)
6058 boolean redefined_ce_found = FALSE;
6060 // to check if this level set redefines any CEs, do not use overriding
6061 gfx.override_level_graphics = FALSE;
6062 gfx.override_level_sounds = FALSE;
6063 gfx.override_level_music = FALSE;
6065 // now check if this level set has definitions for custom elements
6066 if (setup.override_level_graphics == AUTO ||
6067 setup.override_level_sounds == AUTO ||
6068 setup.override_level_music == AUTO)
6069 redefined_ce_found =
6070 (CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_GRAPHICS) |
6071 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_SOUNDS) |
6072 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_MUSIC));
6075 Debug("init:InitOverrideArtwork", "redefined_ce_found == %d",
6076 redefined_ce_found);
6079 if (redefined_ce_found)
6081 // this level set has CE definitions: change "AUTO" to "FALSE"
6082 gfx.override_level_graphics = (setup.override_level_graphics == TRUE);
6083 gfx.override_level_sounds = (setup.override_level_sounds == TRUE);
6084 gfx.override_level_music = (setup.override_level_music == TRUE);
6088 // this level set has no CE definitions: change "AUTO" to "TRUE"
6089 gfx.override_level_graphics = (setup.override_level_graphics != FALSE);
6090 gfx.override_level_sounds = (setup.override_level_sounds != FALSE);
6091 gfx.override_level_music = (setup.override_level_music != FALSE);
6095 Debug("init:InitOverrideArtwork", "%d, %d, %d",
6096 gfx.override_level_graphics,
6097 gfx.override_level_sounds,
6098 gfx.override_level_music);
6102 static char *getNewArtworkIdentifier(int type)
6104 static char *last_leveldir_identifier[3] = { NULL, NULL, NULL };
6105 static char *last_artwork_identifier[3] = { NULL, NULL, NULL };
6106 static boolean last_override_level_artwork[3] = { FALSE, FALSE, FALSE };
6107 static boolean last_has_custom_artwork_set[3] = { FALSE, FALSE, FALSE };
6108 static boolean initialized[3] = { FALSE, FALSE, FALSE };
6109 TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type);
6110 boolean setup_override_artwork = GFX_OVERRIDE_ARTWORK(type);
6111 char *setup_artwork_set = SETUP_ARTWORK_SET(setup, type);
6112 char *leveldir_identifier = leveldir_current->identifier;
6113 // !!! setLevelArtworkDir() should be moved to an earlier stage !!!
6114 char *leveldir_artwork_set = setLevelArtworkDir(artwork_first_node);
6115 boolean has_level_artwork_set = (leveldir_artwork_set != NULL);
6116 TreeInfo *custom_artwork_set =
6117 getTreeInfoFromIdentifier(artwork_first_node, leveldir_identifier);
6118 boolean has_custom_artwork_set = (custom_artwork_set != NULL);
6119 char *artwork_current_identifier;
6120 char *artwork_new_identifier = NULL; // default: nothing has changed
6122 // leveldir_current may be invalid (level group, parent link)
6123 if (!validLevelSeries(leveldir_current))
6126 /* 1st step: determine artwork set to be activated in descending order:
6127 --------------------------------------------------------------------
6128 1. setup artwork (when configured to override everything else)
6129 2. artwork set configured in "levelinfo.conf" of current level set
6130 (artwork in level directory will have priority when loading later)
6131 3. artwork in level directory (stored in artwork sub-directory)
6132 4. setup artwork (currently configured in setup menu) */
6134 if (setup_override_artwork)
6135 artwork_current_identifier = setup_artwork_set;
6136 else if (has_level_artwork_set)
6137 artwork_current_identifier = leveldir_artwork_set;
6138 else if (has_custom_artwork_set)
6139 artwork_current_identifier = leveldir_identifier;
6141 artwork_current_identifier = setup_artwork_set;
6143 /* 2nd step: check if it is really needed to reload artwork set
6144 ------------------------------------------------------------ */
6146 // ---------- reload if level set and also artwork set has changed ----------
6147 if (last_leveldir_identifier[type] != leveldir_identifier &&
6148 (last_has_custom_artwork_set[type] || has_custom_artwork_set))
6149 artwork_new_identifier = artwork_current_identifier;
6151 last_leveldir_identifier[type] = leveldir_identifier;
6152 last_has_custom_artwork_set[type] = has_custom_artwork_set;
6154 // ---------- reload if "override artwork" setting has changed --------------
6155 if (last_override_level_artwork[type] != setup_override_artwork)
6156 artwork_new_identifier = artwork_current_identifier;
6158 last_override_level_artwork[type] = setup_override_artwork;
6160 // ---------- reload if current artwork identifier has changed --------------
6161 if (!strEqual(last_artwork_identifier[type], artwork_current_identifier))
6162 artwork_new_identifier = artwork_current_identifier;
6164 // (we cannot compare string pointers here, so copy string content itself)
6165 setString(&last_artwork_identifier[type], artwork_current_identifier);
6167 *(ARTWORK_CURRENT_IDENTIFIER_PTR(artwork, type)) = artwork_current_identifier;
6169 // ---------- do not reload directly after starting -------------------------
6170 if (!initialized[type])
6171 artwork_new_identifier = NULL;
6173 initialized[type] = TRUE;
6175 return artwork_new_identifier;
6178 void ReloadCustomArtwork(int force_reload)
6180 int last_game_status = game_status; // save current game status
6181 char *gfx_new_identifier;
6182 char *snd_new_identifier;
6183 char *mus_new_identifier;
6184 boolean force_reload_gfx = (force_reload & (1 << ARTWORK_TYPE_GRAPHICS));
6185 boolean force_reload_snd = (force_reload & (1 << ARTWORK_TYPE_SOUNDS));
6186 boolean force_reload_mus = (force_reload & (1 << ARTWORK_TYPE_MUSIC));
6187 boolean reload_needed;
6189 InitOverrideArtwork();
6191 AdjustGraphicsForEMC();
6192 AdjustSoundsForEMC();
6194 gfx_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6195 snd_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6196 mus_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6198 reload_needed = (gfx_new_identifier != NULL || force_reload_gfx ||
6199 snd_new_identifier != NULL || force_reload_snd ||
6200 mus_new_identifier != NULL || force_reload_mus);
6205 print_timestamp_init("ReloadCustomArtwork");
6207 SetGameStatus(GAME_MODE_LOADING);
6209 FadeOut(REDRAW_ALL);
6211 SetLoadingBackgroundImage();
6213 ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6214 print_timestamp_time("ClearRectangleOnBackground");
6218 UPDATE_BUSY_STATE();
6220 if (gfx_new_identifier != NULL || force_reload_gfx)
6223 Debug("init:ReloadCustomArtwork",
6224 "RELOADING GRAPHICS '%s' -> '%s' ['%s', '%s']",
6225 artwork.gfx_current_identifier,
6227 artwork.gfx_current->identifier,
6228 leveldir_current->graphics_set);
6232 print_timestamp_time("InitImages");
6235 if (snd_new_identifier != NULL || force_reload_snd)
6237 InitSound(snd_new_identifier);
6238 print_timestamp_time("InitSound");
6241 if (mus_new_identifier != NULL || force_reload_mus)
6243 InitMusic(mus_new_identifier);
6244 print_timestamp_time("InitMusic");
6249 SetGameStatus(last_game_status); // restore current game status
6251 FadeOut(REDRAW_ALL);
6253 RedrawGlobalBorder();
6255 // force redraw of (open or closed) door graphics
6256 SetDoorState(DOOR_OPEN_ALL);
6257 CloseDoor(DOOR_CLOSE_ALL | DOOR_NO_DELAY);
6259 FadeSetEnterScreen();
6260 FadeSkipNextFadeOut();
6262 print_timestamp_done("ReloadCustomArtwork");
6264 LimitScreenUpdates(FALSE);
6267 void KeyboardAutoRepeatOffUnlessAutoplay(void)
6269 if (global.autoplay_leveldir == NULL)
6270 KeyboardAutoRepeatOff();
6273 void DisplayExitMessage(char *format, va_list ap)
6275 // also check for initialized video (headless flag may be temporarily unset)
6276 if (program.headless || !video.initialized)
6279 // check if draw buffer and fonts for exit message are already available
6280 if (drawto == NULL || font_initial[NUM_INITIAL_FONTS - 1].bitmap == NULL)
6283 int font_1 = FC_RED;
6284 int font_2 = FC_YELLOW;
6285 int font_3 = FC_BLUE;
6286 int font_width = getFontWidth(font_2);
6287 int font_height = getFontHeight(font_2);
6290 int sxsize = WIN_XSIZE - 2 * sx;
6291 int sysize = WIN_YSIZE - 2 * sy;
6292 int line_length = sxsize / font_width;
6293 int max_lines = sysize / font_height;
6294 int num_lines_printed;
6298 gfx.sxsize = sxsize;
6299 gfx.sysize = sysize;
6303 ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6305 DrawTextSCentered(sy, font_1, "Fatal error:");
6306 sy += 3 * font_height;;
6309 DrawTextBufferVA(sx, sy, format, ap, font_2,
6310 line_length, line_length, max_lines,
6311 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6312 sy += (num_lines_printed + 3) * font_height;
6314 DrawTextSCentered(sy, font_1, "For details, see the following error file:");
6315 sy += 3 * font_height;
6318 DrawTextBuffer(sx, sy, program.log_filename[LOG_ERR_ID], font_2,
6319 line_length, line_length, max_lines,
6320 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6322 DrawTextSCentered(SYSIZE - 20, font_3, "Press any key or button to exit");
6324 redraw_mask = REDRAW_ALL;
6326 // force drawing exit message even if screen updates are currently limited
6327 LimitScreenUpdates(FALSE);
6331 // deactivate toons on error message screen
6332 setup.toons = FALSE;
6334 WaitForEventToContinue();
6338 // ============================================================================
6340 // ============================================================================
6344 print_timestamp_init("OpenAll");
6346 SetGameStatus(GAME_MODE_LOADING);
6350 InitGlobal(); // initialize some global variables
6352 InitRND(NEW_RANDOMIZE);
6353 InitSimpleRandom(NEW_RANDOMIZE);
6354 InitBetterRandom(NEW_RANDOMIZE);
6356 print_timestamp_time("[init global stuff]");
6360 print_timestamp_time("[init setup/config stuff (1)]");
6362 if (options.execute_command)
6363 Execute_Command(options.execute_command);
6365 InitNetworkSettings();
6369 if (network.serveronly)
6371 #if defined(PLATFORM_UNIX)
6372 NetworkServer(network.server_port, TRUE);
6374 Warn("networking only supported in Unix version");
6377 exit(0); // never reached, server loops forever
6381 print_timestamp_time("[init setup/config stuff (2)]");
6383 print_timestamp_time("[init setup/config stuff (3)]");
6384 InitArtworkInfo(); // needed before loading gfx, sound & music
6385 print_timestamp_time("[init setup/config stuff (4)]");
6386 InitArtworkConfig(); // needed before forking sound child process
6387 print_timestamp_time("[init setup/config stuff (5)]");
6389 print_timestamp_time("[init setup/config stuff (6)]");
6393 print_timestamp_time("[init setup/config stuff]");
6395 InitVideoDefaults();
6397 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
6400 InitEventFilter(FilterMouseMotionEvents);
6402 print_timestamp_time("[init video stuff]");
6404 InitElementPropertiesStatic();
6405 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
6406 InitElementPropertiesGfxElement();
6408 print_timestamp_time("[init element properties stuff]");
6412 print_timestamp_time("InitGfx");
6415 print_timestamp_time("InitLevelInfo");
6417 InitLevelArtworkInfo();
6418 print_timestamp_time("InitLevelArtworkInfo");
6420 InitOverrideArtwork(); // needs to know current level directory
6421 print_timestamp_time("InitOverrideArtwork");
6423 InitImages(); // needs to know current level directory
6424 print_timestamp_time("InitImages");
6426 InitSound(NULL); // needs to know current level directory
6427 print_timestamp_time("InitSound");
6429 InitMusic(NULL); // needs to know current level directory
6430 print_timestamp_time("InitMusic");
6434 InitGfxBackground();
6440 if (global.autoplay_leveldir)
6445 else if (global.patchtapes_leveldir)
6450 else if (global.convert_leveldir)
6455 else if (global.dumplevel_leveldir)
6460 else if (global.dumptape_leveldir)
6465 else if (global.create_sketch_images_dir)
6467 CreateLevelSketchImages();
6470 else if (global.create_collect_images_dir)
6472 CreateCollectElementImages();
6476 InitNetworkServer();
6478 SetGameStatus(GAME_MODE_MAIN);
6480 FadeSetEnterScreen();
6481 if (!(fading.fade_mode & FADE_TYPE_TRANSFORM))
6482 FadeSkipNextFadeOut();
6484 print_timestamp_time("[post-artwork]");
6486 print_timestamp_done("OpenAll");
6488 if (setup.ask_for_remaining_tapes)
6489 setup.ask_for_uploading_tapes = TRUE;
6494 Debug("internal:path", "SDL_GetBasePath() == '%s'",
6496 Debug("internal:path", "SDL_GetPrefPath() == '%s'",
6497 SDL_GetPrefPath("artsoft", "rocksndiamonds"));
6498 #if defined(PLATFORM_ANDROID)
6499 Debug("internal:path", "SDL_AndroidGetInternalStoragePath() == '%s'",
6500 SDL_AndroidGetInternalStoragePath());
6501 Debug("internal:path", "SDL_AndroidGetExternalStoragePath() == '%s'",
6502 SDL_AndroidGetExternalStoragePath());
6503 Debug("internal:path", "SDL_AndroidGetExternalStorageState() == '%s'",
6504 (SDL_AndroidGetExternalStorageState() &
6505 SDL_ANDROID_EXTERNAL_STORAGE_WRITE ? "writable" :
6506 SDL_AndroidGetExternalStorageState() &
6507 SDL_ANDROID_EXTERNAL_STORAGE_READ ? "readable" : "not available"));
6512 static boolean WaitForApiThreads(void)
6514 unsigned int thread_delay = 0;
6515 unsigned int thread_delay_value = 10000;
6517 if (program.api_thread_count == 0)
6520 // deactivate global animations (not accessible in game state "loading")
6521 setup.toons = FALSE;
6523 // set game state to "loading" to be able to show busy animation
6524 SetGameStatus(GAME_MODE_LOADING);
6526 ResetDelayCounter(&thread_delay);
6528 // wait for threads to finish (and fail on timeout)
6529 while (program.api_thread_count > 0)
6531 if (DelayReached(&thread_delay, thread_delay_value))
6533 Error("failed waiting for threads - TIMEOUT");
6538 UPDATE_BUSY_STATE();
6546 void CloseAllAndExit(int exit_value)
6548 WaitForApiThreads();
6553 CloseAudio(); // called after freeing sounds (needed for SDL)
6561 // set a flag to tell the network server thread to quit and wait for it
6562 // using SDL_WaitThread()
6564 // Code used with SDL 1.2:
6565 // if (network.server_thread) // terminate network server
6566 // SDL_KillThread(network.server_thread);
6568 CloseVideoDisplay();
6569 ClosePlatformDependentStuff();
6571 if (exit_value != 0 && !options.execute_command)
6573 // fall back to default level set (current set may have caused an error)
6574 SaveLevelSetup_LastSeries_Deactivate();
6576 // tell user where to find error log file which may contain more details
6577 // (error notification now directly displayed on screen inside R'n'D
6578 // NotifyUserAboutErrorFile(); // currently only works for Windows