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 "global.busy"
38 #define CONFIG_TOKEN_BACKGROUND_LOADING_INITIAL "background.LOADING_INITIAL"
39 #define CONFIG_TOKEN_BACKGROUND_LOADING "background.LOADING"
41 #define INITIAL_IMG_GLOBAL_BUSY 0
42 #define INITIAL_IMG_BACKGROUND_LOADING_INITIAL 1
43 #define INITIAL_IMG_BACKGROUND_LOADING 2
45 #define NUM_INITIAL_IMAGES 3
48 static struct FontBitmapInfo font_initial[NUM_INITIAL_FONTS];
49 static struct GraphicInfo image_initial[NUM_INITIAL_IMAGES];
51 static int copy_properties[][5] =
55 EL_BUG_LEFT, EL_BUG_RIGHT,
56 EL_BUG_UP, EL_BUG_DOWN
60 EL_SPACESHIP_LEFT, EL_SPACESHIP_RIGHT,
61 EL_SPACESHIP_UP, EL_SPACESHIP_DOWN
65 EL_BD_BUTTERFLY_LEFT, EL_BD_BUTTERFLY_RIGHT,
66 EL_BD_BUTTERFLY_UP, EL_BD_BUTTERFLY_DOWN
70 EL_BD_FIREFLY_LEFT, EL_BD_FIREFLY_RIGHT,
71 EL_BD_FIREFLY_UP, EL_BD_FIREFLY_DOWN
75 EL_PACMAN_LEFT, EL_PACMAN_RIGHT,
76 EL_PACMAN_UP, EL_PACMAN_DOWN
80 EL_YAMYAM_LEFT, EL_YAMYAM_RIGHT,
81 EL_YAMYAM_UP, EL_YAMYAM_DOWN
85 EL_MOLE_LEFT, EL_MOLE_RIGHT,
86 EL_MOLE_UP, EL_MOLE_DOWN
90 EL_SPRING_LEFT, EL_SPRING_RIGHT,
91 EL_SPRING_LEFT, EL_SPRING_RIGHT, // (to match array size)
100 // forward declaration for internal use
101 static int get_graphic_parameter_value(char *, char *, int);
104 static void SetLoadingBackgroundImage(void)
106 struct GraphicInfo *graphic_info_last = graphic_info;
107 int background_image = (game_status_last_screen == -1 ?
108 INITIAL_IMG_BACKGROUND_LOADING_INITIAL :
109 INITIAL_IMG_BACKGROUND_LOADING);
111 graphic_info = image_initial;
113 SetDrawDeactivationMask(REDRAW_NONE);
114 SetDrawBackgroundMask(REDRAW_ALL);
116 SetWindowBackgroundImage(background_image);
118 graphic_info = graphic_info_last;
121 static void DrawInitAnim(void)
123 struct GraphicInfo *graphic_info_last = graphic_info;
125 static unsigned int action_delay = 0;
126 unsigned int action_delay_value = GameFrameDelay;
127 int sync_frame = FrameCounter;
130 // prevent OS (Windows) from complaining about program not responding
133 if (game_status != GAME_MODE_LOADING)
136 if (image_initial[INITIAL_IMG_GLOBAL_BUSY].bitmap == NULL || window == NULL)
139 if (!DelayReached(&action_delay, action_delay_value))
142 if (init_last.busy.x == -1)
143 init_last.busy.x = WIN_XSIZE / 2;
144 if (init_last.busy.y == -1)
145 init_last.busy.y = WIN_YSIZE / 2;
147 x = ALIGNED_TEXT_XPOS(&init_last.busy);
148 y = ALIGNED_TEXT_YPOS(&init_last.busy);
150 graphic_info = image_initial; // graphic == 0 => image_initial
152 if (sync_frame % image_initial[INITIAL_IMG_GLOBAL_BUSY].anim_delay == 0)
156 int width = graphic_info[graphic].width;
157 int height = graphic_info[graphic].height;
158 int frame = getGraphicAnimationFrame(graphic, sync_frame);
160 ClearRectangleOnBackground(drawto, x, y, width, height);
162 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
163 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height, x, y);
165 BlitBitmap(drawto, window, x, y, width, height, x, y);
168 graphic_info = graphic_info_last;
173 static void DrawProgramInfo(void)
175 int font1_nr = FC_YELLOW;
176 int font2_nr = FC_RED;
177 int font2_height = getFontHeight(font2_nr);
180 int ypos3 = WIN_YSIZE - 20 - font2_height;
182 DrawInitText(getProgramInitString(), ypos1, font1_nr);
183 DrawInitText(setup.internal.program_copyright, ypos2, font2_nr);
184 DrawInitText(setup.internal.program_website, ypos3, font2_nr);
187 static void FreeGadgets(void)
189 FreeLevelEditorGadgets();
196 void InitGadgets(void)
198 static boolean gadgets_initialized = FALSE;
200 if (gadgets_initialized)
203 CreateLevelEditorGadgets();
207 CreateScreenGadgets();
209 InitGadgetsSoundCallback(PlaySoundActivating, PlaySoundSelecting);
211 gadgets_initialized = TRUE;
214 static void InitElementSmallImagesScaledUp(int graphic)
216 struct GraphicInfo *g = &graphic_info[graphic];
218 // create small and game tile sized bitmaps (and scale up, if needed)
219 CreateImageWithSmallImages(graphic, g->scale_up_factor, g->tile_size);
222 static void InitElementSmallImages(void)
224 print_timestamp_init("InitElementSmallImages");
226 static int special_graphics[] =
240 IMG_EDITOR_ELEMENT_BORDER,
241 IMG_EDITOR_ELEMENT_BORDER_INPUT,
242 IMG_EDITOR_CASCADE_LIST,
243 IMG_EDITOR_CASCADE_LIST_ACTIVE,
246 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
247 int num_property_mappings = getImageListPropertyMappingSize();
250 print_timestamp_time("getImageListPropertyMapping/Size");
252 print_timestamp_init("InitElementSmallImagesScaledUp (1)");
253 // initialize normal element images from static configuration
254 for (i = 0; element_to_graphic[i].element > -1; i++)
255 InitElementSmallImagesScaledUp(element_to_graphic[i].graphic);
256 print_timestamp_done("InitElementSmallImagesScaledUp (1)");
258 // initialize special element images from static configuration
259 for (i = 0; element_to_special_graphic[i].element > -1; i++)
260 InitElementSmallImagesScaledUp(element_to_special_graphic[i].graphic);
261 print_timestamp_time("InitElementSmallImagesScaledUp (2)");
263 // initialize element images from dynamic configuration
264 for (i = 0; i < num_property_mappings; i++)
265 if (property_mapping[i].base_index < MAX_NUM_ELEMENTS)
266 InitElementSmallImagesScaledUp(property_mapping[i].artwork_index);
267 print_timestamp_time("InitElementSmallImagesScaledUp (3)");
269 // initialize special non-element images from above list
270 for (i = 0; special_graphics[i] > -1; i++)
271 InitElementSmallImagesScaledUp(special_graphics[i]);
272 print_timestamp_time("InitElementSmallImagesScaledUp (4)");
274 print_timestamp_done("InitElementSmallImages");
277 static void InitScaledImagesScaledUp(int graphic)
279 struct GraphicInfo *g = &graphic_info[graphic];
281 ScaleImage(graphic, g->scale_up_factor);
284 static void InitScaledImages(void)
286 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
287 int num_property_mappings = getImageListPropertyMappingSize();
290 // scale normal images from static configuration, if not already scaled
291 for (i = 0; i < NUM_IMAGE_FILES; i++)
292 InitScaledImagesScaledUp(i);
294 // scale images from dynamic configuration, if not already scaled
295 for (i = 0; i < num_property_mappings; i++)
296 InitScaledImagesScaledUp(property_mapping[i].artwork_index);
299 static void InitBitmapPointers(void)
301 int num_images = getImageListSize();
304 // standard size bitmap may have changed -- update default bitmap pointer
305 for (i = 0; i < num_images; i++)
306 if (graphic_info[i].bitmaps)
307 graphic_info[i].bitmap = graphic_info[i].bitmaps[IMG_BITMAP_STANDARD];
310 void InitImageTextures(void)
312 static int texture_graphics[] =
314 IMG_GFX_REQUEST_BUTTON_TOUCH_YES,
315 IMG_GFX_REQUEST_BUTTON_TOUCH_NO,
316 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM,
317 IMG_GFX_GAME_BUTTON_TOUCH_STOP,
318 IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,
319 IMG_MENU_BUTTON_TOUCH_BACK,
320 IMG_MENU_BUTTON_TOUCH_NEXT,
321 IMG_MENU_BUTTON_TOUCH_BACK2,
322 IMG_MENU_BUTTON_TOUCH_NEXT2,
327 FreeAllImageTextures();
329 for (i = IMG_GLOBAL_BORDER_FIRST; i <= IMG_GLOBAL_BORDER_LAST; i++)
330 CreateImageTextures(i);
332 for (i = 0; i < MAX_NUM_TOONS; i++)
333 CreateImageTextures(IMG_TOON_1 + i);
335 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
337 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
339 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
341 int graphic = global_anim_info[i].graphic[j][k];
343 if (graphic == IMG_UNDEFINED)
346 CreateImageTextures(graphic);
351 for (i = 0; texture_graphics[i] > -1; i++)
352 CreateImageTextures(texture_graphics[i]);
355 static int getFontBitmapID(int font_nr)
359 // (special case: do not use special font for GAME_MODE_LOADING)
360 if (game_status >= GAME_MODE_TITLE_INITIAL &&
361 game_status <= GAME_MODE_PSEUDO_PREVIEW)
362 special = game_status;
363 else if (game_status == GAME_MODE_PSEUDO_TYPENAME)
364 special = GFX_SPECIAL_ARG_MAIN;
365 else if (game_status == GAME_MODE_PSEUDO_TYPENAMES)
366 special = GFX_SPECIAL_ARG_NAMES;
369 return font_info[font_nr].special_bitmap_id[special];
374 static int getFontFromToken(char *token)
376 char *value = getHashEntry(font_token_hash, token);
381 // if font not found, use reliable default value
382 return FONT_INITIAL_1;
385 static void InitFontGraphicInfo(void)
387 static struct FontBitmapInfo *font_bitmap_info = NULL;
388 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
389 int num_property_mappings = getImageListPropertyMappingSize();
390 int num_font_bitmaps = NUM_FONTS;
393 if (graphic_info == NULL) // still at startup phase
395 InitFontInfo(font_initial, NUM_INITIAL_FONTS,
396 getFontBitmapID, getFontFromToken);
401 // ---------- initialize font graphic definitions ----------
403 // always start with reliable default values (normal font graphics)
404 for (i = 0; i < NUM_FONTS; i++)
405 font_info[i].graphic = IMG_FONT_INITIAL_1;
407 // initialize normal font/graphic mapping from static configuration
408 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
410 int font_nr = font_to_graphic[i].font_nr;
411 int special = font_to_graphic[i].special;
412 int graphic = font_to_graphic[i].graphic;
417 font_info[font_nr].graphic = graphic;
420 // always start with reliable default values (special font graphics)
421 for (i = 0; i < NUM_FONTS; i++)
423 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
425 font_info[i].special_graphic[j] = font_info[i].graphic;
426 font_info[i].special_bitmap_id[j] = i;
430 // initialize special font/graphic mapping from static configuration
431 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
433 int font_nr = font_to_graphic[i].font_nr;
434 int special = font_to_graphic[i].special;
435 int graphic = font_to_graphic[i].graphic;
436 int base_graphic = font2baseimg(font_nr);
438 if (IS_SPECIAL_GFX_ARG(special))
440 boolean base_redefined =
441 getImageListEntryFromImageID(base_graphic)->redefined;
442 boolean special_redefined =
443 getImageListEntryFromImageID(graphic)->redefined;
444 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
446 /* if the base font ("font.title_1", for example) has been redefined,
447 but not the special font ("font.title_1.LEVELS", for example), do not
448 use an existing (in this case considered obsolete) special font
449 anymore, but use the automatically determined default font */
450 /* special case: cloned special fonts must be explicitly redefined,
451 but are not automatically redefined by redefining base font */
452 if (base_redefined && !special_redefined && !special_cloned)
455 font_info[font_nr].special_graphic[special] = graphic;
456 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
461 // initialize special font/graphic mapping from dynamic configuration
462 for (i = 0; i < num_property_mappings; i++)
464 int font_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
465 int special = property_mapping[i].ext3_index;
466 int graphic = property_mapping[i].artwork_index;
468 if (font_nr < 0 || font_nr >= NUM_FONTS)
471 if (IS_SPECIAL_GFX_ARG(special))
473 font_info[font_nr].special_graphic[special] = graphic;
474 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
479 /* correct special font/graphic mapping for cloned fonts for downwards
480 compatibility of PREVIEW fonts -- this is only needed for implicit
481 redefinition of special font by redefined base font, and only if other
482 fonts are cloned from this special font (like in the "Zelda" level set) */
483 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
485 int font_nr = font_to_graphic[i].font_nr;
486 int special = font_to_graphic[i].special;
487 int graphic = font_to_graphic[i].graphic;
489 if (IS_SPECIAL_GFX_ARG(special))
491 boolean special_redefined =
492 getImageListEntryFromImageID(graphic)->redefined;
493 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
495 if (special_cloned && !special_redefined)
499 for (j = 0; font_to_graphic[j].font_nr > -1; j++)
501 int font_nr2 = font_to_graphic[j].font_nr;
502 int special2 = font_to_graphic[j].special;
503 int graphic2 = font_to_graphic[j].graphic;
505 if (IS_SPECIAL_GFX_ARG(special2) &&
506 graphic2 == graphic_info[graphic].clone_from)
508 font_info[font_nr].special_graphic[special] =
509 font_info[font_nr2].special_graphic[special2];
510 font_info[font_nr].special_bitmap_id[special] =
511 font_info[font_nr2].special_bitmap_id[special2];
518 // reset non-redefined ".active" font graphics if normal font is redefined
519 // (this different treatment is needed because normal and active fonts are
520 // independently defined ("active" is not a property of font definitions!)
521 for (i = 0; i < NUM_FONTS; i++)
523 int font_nr_base = i;
524 int font_nr_active = FONT_ACTIVE(font_nr_base);
526 // check only those fonts with exist as normal and ".active" variant
527 if (font_nr_base != font_nr_active)
529 int base_graphic = font_info[font_nr_base].graphic;
530 int active_graphic = font_info[font_nr_active].graphic;
531 boolean base_redefined =
532 getImageListEntryFromImageID(base_graphic)->redefined;
533 boolean active_redefined =
534 getImageListEntryFromImageID(active_graphic)->redefined;
536 /* if the base font ("font.menu_1", for example) has been redefined,
537 but not the active font ("font.menu_1.active", for example), do not
538 use an existing (in this case considered obsolete) active font
539 anymore, but use the automatically determined default font */
540 if (base_redefined && !active_redefined)
541 font_info[font_nr_active].graphic = base_graphic;
543 // now also check each "special" font (which may be the same as above)
544 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
546 int base_graphic = font_info[font_nr_base].special_graphic[j];
547 int active_graphic = font_info[font_nr_active].special_graphic[j];
548 boolean base_redefined =
549 getImageListEntryFromImageID(base_graphic)->redefined;
550 boolean active_redefined =
551 getImageListEntryFromImageID(active_graphic)->redefined;
553 // same as above, but check special graphic definitions, for example:
554 // redefined "font.menu_1.MAIN" invalidates "font.menu_1.active.MAIN"
555 if (base_redefined && !active_redefined)
557 font_info[font_nr_active].special_graphic[j] =
558 font_info[font_nr_base].special_graphic[j];
559 font_info[font_nr_active].special_bitmap_id[j] =
560 font_info[font_nr_base].special_bitmap_id[j];
566 // ---------- initialize font bitmap array ----------
568 if (font_bitmap_info != NULL)
569 FreeFontInfo(font_bitmap_info);
572 checked_calloc(num_font_bitmaps * sizeof(struct FontBitmapInfo));
574 // ---------- initialize font bitmap definitions ----------
576 for (i = 0; i < NUM_FONTS; i++)
578 if (i < NUM_INITIAL_FONTS)
580 font_bitmap_info[i] = font_initial[i];
584 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
586 int font_bitmap_id = font_info[i].special_bitmap_id[j];
587 int graphic = font_info[i].special_graphic[j];
589 // set 'graphic_info' for font entries, if uninitialized (guessed)
590 if (graphic_info[graphic].anim_frames < MIN_NUM_CHARS_PER_FONT)
592 graphic_info[graphic].anim_frames = DEFAULT_NUM_CHARS_PER_FONT;
593 graphic_info[graphic].anim_frames_per_line = DEFAULT_NUM_CHARS_PER_LINE;
596 // copy font relevant information from graphics information
597 font_bitmap_info[font_bitmap_id].bitmap = graphic_info[graphic].bitmap;
598 font_bitmap_info[font_bitmap_id].src_x = graphic_info[graphic].src_x;
599 font_bitmap_info[font_bitmap_id].src_y = graphic_info[graphic].src_y;
600 font_bitmap_info[font_bitmap_id].width = graphic_info[graphic].width;
601 font_bitmap_info[font_bitmap_id].height = graphic_info[graphic].height;
603 font_bitmap_info[font_bitmap_id].offset_x =
604 graphic_info[graphic].offset_x;
605 font_bitmap_info[font_bitmap_id].offset_y =
606 graphic_info[graphic].offset_y;
608 font_bitmap_info[font_bitmap_id].draw_xoffset =
609 graphic_info[graphic].draw_xoffset;
610 font_bitmap_info[font_bitmap_id].draw_yoffset =
611 graphic_info[graphic].draw_yoffset;
613 font_bitmap_info[font_bitmap_id].num_chars =
614 graphic_info[graphic].anim_frames;
615 font_bitmap_info[font_bitmap_id].num_chars_per_line =
616 graphic_info[graphic].anim_frames_per_line;
620 InitFontInfo(font_bitmap_info, num_font_bitmaps,
621 getFontBitmapID, getFontFromToken);
624 static void InitGlobalAnimGraphicInfo(void)
626 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
627 int num_property_mappings = getImageListPropertyMappingSize();
630 if (graphic_info == NULL) // still at startup phase
633 // always start with reliable default values (no global animations)
634 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
635 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
636 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
637 global_anim_info[i].graphic[j][k] = IMG_UNDEFINED;
639 // initialize global animation definitions from static configuration
640 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
642 int j = GLOBAL_ANIM_ID_PART_BASE;
643 int k = GFX_SPECIAL_ARG_DEFAULT;
645 global_anim_info[i].graphic[j][k] = IMG_GFX_GLOBAL_ANIM_1 + i;
648 // initialize global animation definitions from dynamic configuration
649 for (i = 0; i < num_property_mappings; i++)
651 int anim_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS - NUM_FONTS;
652 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
653 int special = property_mapping[i].ext3_index;
654 int graphic = property_mapping[i].artwork_index;
656 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
659 // set animation part to base part, if not specified
660 if (!IS_GLOBAL_ANIM_PART(part_nr))
661 part_nr = GLOBAL_ANIM_ID_PART_BASE;
663 // set animation screen to default, if not specified
664 if (!IS_SPECIAL_GFX_ARG(special))
665 special = GFX_SPECIAL_ARG_DEFAULT;
667 global_anim_info[anim_nr].graphic[part_nr][special] = graphic;
669 // fix default value for ".draw_masked" (for backward compatibility)
670 struct GraphicInfo *g = &graphic_info[graphic];
671 struct FileInfo *image = getImageListEntryFromImageID(graphic);
672 char **parameter_raw = image->parameter;
673 int p = GFX_ARG_DRAW_MASKED;
674 int draw_masked = get_graphic_parameter_value(parameter_raw[p],
675 image_config_suffix[p].token,
676 image_config_suffix[p].type);
678 // if ".draw_masked" parameter is undefined, use default value "TRUE"
679 if (draw_masked == ARG_UNDEFINED_VALUE)
680 g->draw_masked = TRUE;
684 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
685 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
686 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
687 if (global_anim_info[i].graphic[j][k] != IMG_UNDEFINED &&
688 graphic_info[global_anim_info[i].graphic[j][k]].bitmap != NULL)
689 Debug("init:InitGlobalAnimGraphicInfo",
690 "anim %d, part %d, mode %d => %d",
691 i, j, k, global_anim_info[i].graphic[j][k]);
695 static void InitGlobalAnimSoundInfo(void)
697 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
698 int num_property_mappings = getSoundListPropertyMappingSize();
701 // always start with reliable default values (no global animation sounds)
702 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
703 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
704 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
705 global_anim_info[i].sound[j][k] = SND_UNDEFINED;
707 // initialize global animation sound definitions from dynamic configuration
708 for (i = 0; i < num_property_mappings; i++)
710 int anim_nr = property_mapping[i].base_index - 2 * MAX_NUM_ELEMENTS;
711 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
712 int special = property_mapping[i].ext3_index;
713 int sound = property_mapping[i].artwork_index;
715 // sound uses control definition; map it to position of graphic (artwork)
716 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
718 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
721 // set animation part to base part, if not specified
722 if (!IS_GLOBAL_ANIM_PART(part_nr))
723 part_nr = GLOBAL_ANIM_ID_PART_BASE;
725 // set animation screen to default, if not specified
726 if (!IS_SPECIAL_GFX_ARG(special))
727 special = GFX_SPECIAL_ARG_DEFAULT;
729 global_anim_info[anim_nr].sound[part_nr][special] = sound;
733 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
734 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
735 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
736 if (global_anim_info[i].sound[j][k] != SND_UNDEFINED)
737 Debug("init:InitGlobalAnimSoundInfo",
738 "anim %d, part %d, mode %d => %d",
739 i, j, k, global_anim_info[i].sound[j][k]);
743 static void InitGlobalAnimMusicInfo(void)
745 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
746 int num_property_mappings = getMusicListPropertyMappingSize();
749 // always start with reliable default values (no global animation music)
750 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
751 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
752 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
753 global_anim_info[i].music[j][k] = MUS_UNDEFINED;
755 // initialize global animation music definitions from dynamic configuration
756 for (i = 0; i < num_property_mappings; i++)
758 int anim_nr = property_mapping[i].base_index - NUM_MUSIC_PREFIXES;
759 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
760 int special = property_mapping[i].ext2_index;
761 int music = property_mapping[i].artwork_index;
763 // music uses control definition; map it to position of graphic (artwork)
764 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
766 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
769 // set animation part to base part, if not specified
770 if (!IS_GLOBAL_ANIM_PART(part_nr))
771 part_nr = GLOBAL_ANIM_ID_PART_BASE;
773 // set animation screen to default, if not specified
774 if (!IS_SPECIAL_GFX_ARG(special))
775 special = GFX_SPECIAL_ARG_DEFAULT;
777 global_anim_info[anim_nr].music[part_nr][special] = music;
781 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
782 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
783 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
784 if (global_anim_info[i].music[j][k] != MUS_UNDEFINED)
785 Debug("init:InitGlobalAnimMusicInfo",
786 "anim %d, part %d, mode %d => %d",
787 i, j, k, global_anim_info[i].music[j][k]);
791 static void InitElementGraphicInfo(void)
793 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
794 int num_property_mappings = getImageListPropertyMappingSize();
797 if (graphic_info == NULL) // still at startup phase
800 // set values to -1 to identify later as "uninitialized" values
801 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
803 for (act = 0; act < NUM_ACTIONS; act++)
805 element_info[i].graphic[act] = -1;
806 element_info[i].crumbled[act] = -1;
808 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
810 element_info[i].direction_graphic[act][dir] = -1;
811 element_info[i].direction_crumbled[act][dir] = -1;
818 // initialize normal element/graphic mapping from static configuration
819 for (i = 0; element_to_graphic[i].element > -1; i++)
821 int element = element_to_graphic[i].element;
822 int action = element_to_graphic[i].action;
823 int direction = element_to_graphic[i].direction;
824 boolean crumbled = element_to_graphic[i].crumbled;
825 int graphic = element_to_graphic[i].graphic;
826 int base_graphic = el2baseimg(element);
828 if (graphic_info[graphic].bitmap == NULL)
831 if ((action > -1 || direction > -1 || crumbled == TRUE) &&
834 boolean base_redefined =
835 getImageListEntryFromImageID(base_graphic)->redefined;
836 boolean act_dir_redefined =
837 getImageListEntryFromImageID(graphic)->redefined;
839 /* if the base graphic ("emerald", for example) has been redefined,
840 but not the action graphic ("emerald.falling", for example), do not
841 use an existing (in this case considered obsolete) action graphic
842 anymore, but use the automatically determined default graphic */
843 if (base_redefined && !act_dir_redefined)
848 action = ACTION_DEFAULT;
853 element_info[element].direction_crumbled[action][direction] = graphic;
855 element_info[element].crumbled[action] = graphic;
860 element_info[element].direction_graphic[action][direction] = graphic;
862 element_info[element].graphic[action] = graphic;
866 // initialize normal element/graphic mapping from dynamic configuration
867 for (i = 0; i < num_property_mappings; i++)
869 int element = property_mapping[i].base_index;
870 int action = property_mapping[i].ext1_index;
871 int direction = property_mapping[i].ext2_index;
872 int special = property_mapping[i].ext3_index;
873 int graphic = property_mapping[i].artwork_index;
874 boolean crumbled = FALSE;
876 if (special == GFX_SPECIAL_ARG_CRUMBLED)
882 if (graphic_info[graphic].bitmap == NULL)
885 if (element >= MAX_NUM_ELEMENTS || special != -1)
889 action = ACTION_DEFAULT;
894 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
895 element_info[element].direction_crumbled[action][dir] = -1;
898 element_info[element].direction_crumbled[action][direction] = graphic;
900 element_info[element].crumbled[action] = graphic;
905 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
906 element_info[element].direction_graphic[action][dir] = -1;
909 element_info[element].direction_graphic[action][direction] = graphic;
911 element_info[element].graphic[action] = graphic;
915 // now copy all graphics that are defined to be cloned from other graphics
916 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
918 int graphic = element_info[i].graphic[ACTION_DEFAULT];
919 int crumbled_like, diggable_like;
924 crumbled_like = graphic_info[graphic].crumbled_like;
925 diggable_like = graphic_info[graphic].diggable_like;
927 if (crumbled_like != -1 && element_info[i].crumbled[ACTION_DEFAULT] == -1)
929 for (act = 0; act < NUM_ACTIONS; act++)
930 element_info[i].crumbled[act] =
931 element_info[crumbled_like].crumbled[act];
932 for (act = 0; act < NUM_ACTIONS; act++)
933 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
934 element_info[i].direction_crumbled[act][dir] =
935 element_info[crumbled_like].direction_crumbled[act][dir];
938 if (diggable_like != -1 && element_info[i].graphic[ACTION_DIGGING] == -1)
940 element_info[i].graphic[ACTION_DIGGING] =
941 element_info[diggable_like].graphic[ACTION_DIGGING];
942 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
943 element_info[i].direction_graphic[ACTION_DIGGING][dir] =
944 element_info[diggable_like].direction_graphic[ACTION_DIGGING][dir];
948 // set hardcoded definitions for some runtime elements without graphic
949 element_info[EL_AMOEBA_TO_DIAMOND].graphic[ACTION_DEFAULT] = IMG_AMOEBA_DEAD;
951 // set hardcoded definitions for some internal elements without graphic
952 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
954 if (IS_EDITOR_CASCADE_INACTIVE(i))
955 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST;
956 else if (IS_EDITOR_CASCADE_ACTIVE(i))
957 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST_ACTIVE;
960 // now set all undefined/invalid graphics to -1 to set to default after it
961 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
963 for (act = 0; act < NUM_ACTIONS; act++)
967 graphic = element_info[i].graphic[act];
968 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
969 element_info[i].graphic[act] = -1;
971 graphic = element_info[i].crumbled[act];
972 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
973 element_info[i].crumbled[act] = -1;
975 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
977 graphic = element_info[i].direction_graphic[act][dir];
978 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
979 element_info[i].direction_graphic[act][dir] = -1;
981 graphic = element_info[i].direction_crumbled[act][dir];
982 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
983 element_info[i].direction_crumbled[act][dir] = -1;
990 // adjust graphics with 2nd tile for movement according to direction
991 // (do this before correcting '-1' values to minimize calculations)
992 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
994 for (act = 0; act < NUM_ACTIONS; act++)
996 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
998 int graphic = element_info[i].direction_graphic[act][dir];
999 int move_dir = (act == ACTION_FALLING ? MV_BIT_DOWN : dir);
1001 if (act == ACTION_FALLING) // special case
1002 graphic = element_info[i].graphic[act];
1004 if (graphic != -1 &&
1005 graphic_info[graphic].double_movement &&
1006 graphic_info[graphic].swap_double_tiles != 0)
1008 struct GraphicInfo *g = &graphic_info[graphic];
1009 int src_x_front = g->src_x;
1010 int src_y_front = g->src_y;
1011 int src_x_back = g->src_x + g->offset2_x;
1012 int src_y_back = g->src_y + g->offset2_y;
1013 boolean frames_are_ordered_diagonally = (g->offset_x != 0 &&
1015 boolean front_is_left_or_upper = (src_x_front < src_x_back ||
1016 src_y_front < src_y_back);
1017 boolean swap_movement_tiles_always = (g->swap_double_tiles == 1);
1018 boolean swap_movement_tiles_autodetected =
1019 (!frames_are_ordered_diagonally &&
1020 ((move_dir == MV_BIT_LEFT && !front_is_left_or_upper) ||
1021 (move_dir == MV_BIT_UP && !front_is_left_or_upper) ||
1022 (move_dir == MV_BIT_RIGHT && front_is_left_or_upper) ||
1023 (move_dir == MV_BIT_DOWN && front_is_left_or_upper)));
1025 // swap frontside and backside graphic tile coordinates, if needed
1026 if (swap_movement_tiles_always || swap_movement_tiles_autodetected)
1028 // get current (wrong) backside tile coordinates
1029 getGraphicSourceXY(graphic, 0, &src_x_back, &src_y_back, TRUE);
1031 // set frontside tile coordinates to backside tile coordinates
1032 g->src_x = src_x_back;
1033 g->src_y = src_y_back;
1035 // invert tile offset to point to new backside tile coordinates
1039 // do not swap front and backside tiles again after correction
1040 g->swap_double_tiles = 0;
1047 UPDATE_BUSY_STATE();
1049 // now set all '-1' values to element specific default values
1050 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1052 int default_graphic = element_info[i].graphic[ACTION_DEFAULT];
1053 int default_crumbled = element_info[i].crumbled[ACTION_DEFAULT];
1054 int default_direction_graphic[NUM_DIRECTIONS_FULL];
1055 int default_direction_crumbled[NUM_DIRECTIONS_FULL];
1057 if (default_graphic == -1)
1058 default_graphic = IMG_UNKNOWN;
1060 if (default_crumbled == -1)
1061 default_crumbled = default_graphic;
1063 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1065 default_direction_graphic[dir] =
1066 element_info[i].direction_graphic[ACTION_DEFAULT][dir];
1067 default_direction_crumbled[dir] =
1068 element_info[i].direction_crumbled[ACTION_DEFAULT][dir];
1070 if (default_direction_graphic[dir] == -1)
1071 default_direction_graphic[dir] = default_graphic;
1073 if (default_direction_crumbled[dir] == -1)
1074 default_direction_crumbled[dir] = default_direction_graphic[dir];
1077 for (act = 0; act < NUM_ACTIONS; act++)
1079 boolean act_remove = ((IS_DIGGABLE(i) && act == ACTION_DIGGING) ||
1080 (IS_SNAPPABLE(i) && act == ACTION_SNAPPING) ||
1081 (IS_COLLECTIBLE(i) && act == ACTION_COLLECTING));
1082 boolean act_turning = (act == ACTION_TURNING_FROM_LEFT ||
1083 act == ACTION_TURNING_FROM_RIGHT ||
1084 act == ACTION_TURNING_FROM_UP ||
1085 act == ACTION_TURNING_FROM_DOWN);
1087 // generic default action graphic (defined by "[default]" directive)
1088 int default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1089 int default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1090 int default_remove_graphic = IMG_EMPTY;
1092 if (act_remove && default_action_graphic != -1)
1093 default_remove_graphic = default_action_graphic;
1095 // look for special default action graphic (classic game specific)
1096 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].graphic[act] != -1)
1097 default_action_graphic = element_info[EL_BD_DEFAULT].graphic[act];
1098 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].graphic[act] != -1)
1099 default_action_graphic = element_info[EL_SP_DEFAULT].graphic[act];
1100 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].graphic[act] != -1)
1101 default_action_graphic = element_info[EL_SB_DEFAULT].graphic[act];
1102 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].graphic[act] != -1)
1103 default_action_graphic = element_info[EL_MM_DEFAULT].graphic[act];
1105 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].crumbled[act] != -1)
1106 default_action_crumbled = element_info[EL_BD_DEFAULT].crumbled[act];
1107 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].crumbled[act] != -1)
1108 default_action_crumbled = element_info[EL_SP_DEFAULT].crumbled[act];
1109 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].crumbled[act] != -1)
1110 default_action_crumbled = element_info[EL_SB_DEFAULT].crumbled[act];
1111 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].crumbled[act] != -1)
1112 default_action_crumbled = element_info[EL_MM_DEFAULT].crumbled[act];
1114 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
1115 // !!! make this better !!!
1116 if (i == EL_EMPTY_SPACE)
1118 default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1119 default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1122 if (default_action_graphic == -1)
1123 default_action_graphic = default_graphic;
1125 if (default_action_crumbled == -1)
1126 default_action_crumbled = default_action_graphic;
1128 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1130 // use action graphic as the default direction graphic, if undefined
1131 int default_action_direction_graphic = element_info[i].graphic[act];
1132 int default_action_direction_crumbled = element_info[i].crumbled[act];
1134 // no graphic for current action -- use default direction graphic
1135 if (default_action_direction_graphic == -1)
1136 default_action_direction_graphic =
1137 (act_remove ? default_remove_graphic :
1139 element_info[i].direction_graphic[ACTION_TURNING][dir] :
1140 default_action_graphic != default_graphic ?
1141 default_action_graphic :
1142 default_direction_graphic[dir]);
1144 if (element_info[i].direction_graphic[act][dir] == -1)
1145 element_info[i].direction_graphic[act][dir] =
1146 default_action_direction_graphic;
1148 if (default_action_direction_crumbled == -1)
1149 default_action_direction_crumbled =
1150 element_info[i].direction_graphic[act][dir];
1152 if (element_info[i].direction_crumbled[act][dir] == -1)
1153 element_info[i].direction_crumbled[act][dir] =
1154 default_action_direction_crumbled;
1157 // no graphic for this specific action -- use default action graphic
1158 if (element_info[i].graphic[act] == -1)
1159 element_info[i].graphic[act] =
1160 (act_remove ? default_remove_graphic :
1161 act_turning ? element_info[i].graphic[ACTION_TURNING] :
1162 default_action_graphic);
1164 if (element_info[i].crumbled[act] == -1)
1165 element_info[i].crumbled[act] = element_info[i].graphic[act];
1169 UPDATE_BUSY_STATE();
1172 static void InitElementSpecialGraphicInfo(void)
1174 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
1175 int num_property_mappings = getImageListPropertyMappingSize();
1178 // always start with reliable default values
1179 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1180 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1181 element_info[i].special_graphic[j] =
1182 element_info[i].graphic[ACTION_DEFAULT];
1184 // initialize special element/graphic mapping from static configuration
1185 for (i = 0; element_to_special_graphic[i].element > -1; i++)
1187 int element = element_to_special_graphic[i].element;
1188 int special = element_to_special_graphic[i].special;
1189 int graphic = element_to_special_graphic[i].graphic;
1190 int base_graphic = el2baseimg(element);
1191 boolean base_redefined =
1192 getImageListEntryFromImageID(base_graphic)->redefined;
1193 boolean special_redefined =
1194 getImageListEntryFromImageID(graphic)->redefined;
1196 /* if the base graphic ("emerald", for example) has been redefined,
1197 but not the special graphic ("emerald.EDITOR", for example), do not
1198 use an existing (in this case considered obsolete) special graphic
1199 anymore, but use the automatically created (down-scaled) graphic */
1200 if (base_redefined && !special_redefined)
1203 element_info[element].special_graphic[special] = graphic;
1206 // initialize special element/graphic mapping from dynamic configuration
1207 for (i = 0; i < num_property_mappings; i++)
1209 int element = property_mapping[i].base_index;
1210 int action = property_mapping[i].ext1_index;
1211 int direction = property_mapping[i].ext2_index;
1212 int special = property_mapping[i].ext3_index;
1213 int graphic = property_mapping[i].artwork_index;
1215 // for action ".active", replace element with active element, if exists
1216 if (action == ACTION_ACTIVE && element != ELEMENT_ACTIVE(element))
1218 element = ELEMENT_ACTIVE(element);
1222 if (element >= MAX_NUM_ELEMENTS)
1225 // do not change special graphic if action or direction was specified
1226 if (action != -1 || direction != -1)
1229 if (IS_SPECIAL_GFX_ARG(special))
1230 element_info[element].special_graphic[special] = graphic;
1233 // now set all undefined/invalid graphics to default
1234 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1235 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1236 if (graphic_info[element_info[i].special_graphic[j]].bitmap == NULL)
1237 element_info[i].special_graphic[j] =
1238 element_info[i].graphic[ACTION_DEFAULT];
1241 static int get_graphic_parameter_value(char *value_raw, char *suffix, int type)
1243 if (type != TYPE_ELEMENT && type != TYPE_GRAPHIC)
1244 return get_parameter_value(value_raw, suffix, type);
1246 if (strEqual(value_raw, ARG_UNDEFINED))
1247 return ARG_UNDEFINED_VALUE;
1249 if (type == TYPE_ELEMENT)
1251 char *value = getHashEntry(element_token_hash, value_raw);
1256 Warn("error found in config file:");
1257 Warn("- config file: '%s'", getImageConfigFilename());
1258 Warn("error: invalid element token '%s'", value_raw);
1259 Warn("custom graphic rejected for this element/action");
1260 Warn("fallback done to undefined element for this graphic");
1264 return (value != NULL ? atoi(value) : EL_UNDEFINED);
1266 else if (type == TYPE_GRAPHIC)
1268 char *value = getHashEntry(graphic_token_hash, value_raw);
1269 int fallback_graphic = IMG_CHAR_EXCLAM;
1274 Warn("error found in config file:");
1275 Warn("- config file: '%s'", getImageConfigFilename());
1276 Warn("error: invalid graphic token '%s'", value_raw);
1277 Warn("custom graphic rejected for this element/action");
1278 Warn("fallback done to 'char_exclam' for this graphic");
1282 return (value != NULL ? atoi(value) : fallback_graphic);
1288 static int get_scaled_graphic_width(int graphic)
1290 int original_width = getOriginalImageWidthFromImageID(graphic);
1291 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1293 return original_width * scale_up_factor;
1296 static int get_scaled_graphic_height(int graphic)
1298 int original_height = getOriginalImageHeightFromImageID(graphic);
1299 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1301 return original_height * scale_up_factor;
1304 static void set_graphic_parameters_ext(int graphic, int *parameter,
1305 Bitmap **src_bitmaps)
1307 struct GraphicInfo *g = &graphic_info[graphic];
1308 Bitmap *src_bitmap = (src_bitmaps ? src_bitmaps[IMG_BITMAP_STANDARD] : NULL);
1309 int anim_frames_per_row = 1, anim_frames_per_col = 1;
1310 int anim_frames_per_line = 1;
1312 // always start with reliable default values
1313 g->src_image_width = 0;
1314 g->src_image_height = 0;
1317 g->width = TILEX; // default for element graphics
1318 g->height = TILEY; // default for element graphics
1319 g->offset_x = 0; // one or both of these values ...
1320 g->offset_y = 0; // ... will be corrected later
1321 g->offset2_x = 0; // one or both of these values ...
1322 g->offset2_y = 0; // ... will be corrected later
1323 g->swap_double_tiles = -1; // auto-detect tile swapping
1324 g->crumbled_like = -1; // do not use clone element
1325 g->diggable_like = -1; // do not use clone element
1326 g->border_size = TILEX / 8; // "CRUMBLED" border size
1327 g->scale_up_factor = 1; // default: no scaling up
1328 g->tile_size = TILESIZE; // default: standard tile size
1329 g->clone_from = -1; // do not use clone graphic
1330 g->init_delay_fixed = 0;
1331 g->init_delay_random = 0;
1332 g->init_delay_action = -1;
1333 g->anim_delay_fixed = 0;
1334 g->anim_delay_random = 0;
1335 g->anim_delay_action = -1;
1336 g->post_delay_fixed = 0;
1337 g->post_delay_random = 0;
1338 g->post_delay_action = -1;
1339 g->init_event = ANIM_EVENT_UNDEFINED;
1340 g->anim_event = ANIM_EVENT_UNDEFINED;
1341 g->init_event_action = -1;
1342 g->anim_event_action = -1;
1343 g->draw_masked = FALSE;
1345 g->fade_mode = FADE_MODE_DEFAULT;
1349 g->auto_delay_unit = AUTO_DELAY_UNIT_DEFAULT;
1350 g->align = ALIGN_CENTER; // default for title screens
1351 g->valign = VALIGN_MIDDLE; // default for title screens
1352 g->sort_priority = 0; // default for title screens
1354 g->style = STYLE_DEFAULT;
1356 g->bitmaps = src_bitmaps;
1357 g->bitmap = src_bitmap;
1359 // optional zoom factor for scaling up the image to a larger size
1360 if (parameter[GFX_ARG_SCALE_UP_FACTOR] != ARG_UNDEFINED_VALUE)
1361 g->scale_up_factor = parameter[GFX_ARG_SCALE_UP_FACTOR];
1362 if (g->scale_up_factor < 1)
1363 g->scale_up_factor = 1; // no scaling
1365 // optional tile size for using non-standard image size
1366 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1368 g->tile_size = parameter[GFX_ARG_TILE_SIZE];
1371 // CHECK: should tile sizes less than standard tile size be allowed?
1372 if (g->tile_size < TILESIZE)
1373 g->tile_size = TILESIZE; // standard tile size
1376 // when setting tile size, also set width and height accordingly
1377 g->width = g->tile_size;
1378 g->height = g->tile_size;
1381 if (g->use_image_size)
1383 // set new default bitmap size (with scaling, but without small images)
1384 g->width = get_scaled_graphic_width(graphic);
1385 g->height = get_scaled_graphic_height(graphic);
1388 // optional width and height of each animation frame
1389 if (parameter[GFX_ARG_WIDTH] != ARG_UNDEFINED_VALUE)
1390 g->width = parameter[GFX_ARG_WIDTH];
1391 if (parameter[GFX_ARG_HEIGHT] != ARG_UNDEFINED_VALUE)
1392 g->height = parameter[GFX_ARG_HEIGHT];
1394 // optional x and y tile position of animation frame sequence
1395 if (parameter[GFX_ARG_XPOS] != ARG_UNDEFINED_VALUE)
1396 g->src_x = parameter[GFX_ARG_XPOS] * g->width;
1397 if (parameter[GFX_ARG_YPOS] != ARG_UNDEFINED_VALUE)
1398 g->src_y = parameter[GFX_ARG_YPOS] * g->height;
1400 // optional x and y pixel position of animation frame sequence
1401 if (parameter[GFX_ARG_X] != ARG_UNDEFINED_VALUE)
1402 g->src_x = parameter[GFX_ARG_X];
1403 if (parameter[GFX_ARG_Y] != ARG_UNDEFINED_VALUE)
1404 g->src_y = parameter[GFX_ARG_Y];
1411 Warn("invalid value %d for '%s.width' (fallback done to %d)",
1412 g->width, getTokenFromImageID(graphic), TILEX);
1415 g->width = TILEX; // will be checked to be inside bitmap later
1421 Warn("invalid value %d for '%s.height' (fallback done to %d)",
1422 g->height, getTokenFromImageID(graphic), TILEY);
1425 g->height = TILEY; // will be checked to be inside bitmap later
1431 // get final bitmap size (with scaling, but without small images)
1432 int src_image_width = get_scaled_graphic_width(graphic);
1433 int src_image_height = get_scaled_graphic_height(graphic);
1435 if (src_image_width == 0 || src_image_height == 0)
1437 // only happens when loaded outside artwork system (like "global.busy")
1438 src_image_width = src_bitmap->width;
1439 src_image_height = src_bitmap->height;
1442 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1444 anim_frames_per_row = MAX(1, src_image_width / g->tile_size);
1445 anim_frames_per_col = MAX(1, src_image_height / g->tile_size);
1449 anim_frames_per_row = MAX(1, src_image_width / g->width);
1450 anim_frames_per_col = MAX(1, src_image_height / g->height);
1453 g->src_image_width = src_image_width;
1454 g->src_image_height = src_image_height;
1457 // correct x or y offset dependent of vertical or horizontal frame order
1458 if (parameter[GFX_ARG_VERTICAL]) // frames are ordered vertically
1460 g->offset_y = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1461 parameter[GFX_ARG_OFFSET] : g->height);
1462 anim_frames_per_line = anim_frames_per_col;
1464 else // frames are ordered horizontally
1466 g->offset_x = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1467 parameter[GFX_ARG_OFFSET] : g->width);
1468 anim_frames_per_line = anim_frames_per_row;
1471 // optionally, the x and y offset of frames can be specified directly
1472 if (parameter[GFX_ARG_XOFFSET] != ARG_UNDEFINED_VALUE)
1473 g->offset_x = parameter[GFX_ARG_XOFFSET];
1474 if (parameter[GFX_ARG_YOFFSET] != ARG_UNDEFINED_VALUE)
1475 g->offset_y = parameter[GFX_ARG_YOFFSET];
1477 // optionally, moving animations may have separate start and end graphics
1478 g->double_movement = parameter[GFX_ARG_2ND_MOVEMENT_TILE];
1480 if (parameter[GFX_ARG_2ND_VERTICAL] == ARG_UNDEFINED_VALUE)
1481 parameter[GFX_ARG_2ND_VERTICAL] = !parameter[GFX_ARG_VERTICAL];
1483 // correct x or y offset2 dependent of vertical or horizontal frame order
1484 if (parameter[GFX_ARG_2ND_VERTICAL]) // frames are ordered vertically
1485 g->offset2_y = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1486 parameter[GFX_ARG_2ND_OFFSET] : g->height);
1487 else // frames are ordered horizontally
1488 g->offset2_x = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1489 parameter[GFX_ARG_2ND_OFFSET] : g->width);
1491 // optionally, the x and y offset of 2nd graphic can be specified directly
1492 if (parameter[GFX_ARG_2ND_XOFFSET] != ARG_UNDEFINED_VALUE)
1493 g->offset2_x = parameter[GFX_ARG_2ND_XOFFSET];
1494 if (parameter[GFX_ARG_2ND_YOFFSET] != ARG_UNDEFINED_VALUE)
1495 g->offset2_y = parameter[GFX_ARG_2ND_YOFFSET];
1497 // optionally, the second movement tile can be specified as start tile
1498 if (parameter[GFX_ARG_2ND_SWAP_TILES] != ARG_UNDEFINED_VALUE)
1499 g->swap_double_tiles= parameter[GFX_ARG_2ND_SWAP_TILES];
1501 // automatically determine correct number of frames, if not defined
1502 if (parameter[GFX_ARG_FRAMES] != ARG_UNDEFINED_VALUE)
1503 g->anim_frames = parameter[GFX_ARG_FRAMES];
1504 else if (parameter[GFX_ARG_XPOS] == 0 && !parameter[GFX_ARG_VERTICAL])
1505 g->anim_frames = anim_frames_per_row;
1506 else if (parameter[GFX_ARG_YPOS] == 0 && parameter[GFX_ARG_VERTICAL])
1507 g->anim_frames = anim_frames_per_col;
1511 if (g->anim_frames < 1) // frames must be at least 1
1514 g->anim_frames_per_line =
1515 (parameter[GFX_ARG_FRAMES_PER_LINE] != ARG_UNDEFINED_VALUE ?
1516 parameter[GFX_ARG_FRAMES_PER_LINE] : anim_frames_per_line);
1518 g->anim_delay = parameter[GFX_ARG_DELAY];
1519 if (g->anim_delay < 1) // delay must be at least 1
1522 g->anim_mode = parameter[GFX_ARG_ANIM_MODE];
1524 // automatically determine correct start frame, if not defined
1525 if (parameter[GFX_ARG_START_FRAME] == ARG_UNDEFINED_VALUE)
1526 g->anim_start_frame = 0;
1527 else if (g->anim_mode & ANIM_REVERSE)
1528 g->anim_start_frame = g->anim_frames - parameter[GFX_ARG_START_FRAME] - 1;
1530 g->anim_start_frame = parameter[GFX_ARG_START_FRAME];
1532 // animation synchronized with global frame counter, not move position
1533 g->anim_global_sync = parameter[GFX_ARG_GLOBAL_SYNC];
1535 // optional element for cloning crumble graphics
1536 if (parameter[GFX_ARG_CRUMBLED_LIKE] != ARG_UNDEFINED_VALUE)
1537 g->crumbled_like = parameter[GFX_ARG_CRUMBLED_LIKE];
1539 // optional element for cloning digging graphics
1540 if (parameter[GFX_ARG_DIGGABLE_LIKE] != ARG_UNDEFINED_VALUE)
1541 g->diggable_like = parameter[GFX_ARG_DIGGABLE_LIKE];
1543 // optional border size for "crumbling" diggable graphics
1544 if (parameter[GFX_ARG_BORDER_SIZE] != ARG_UNDEFINED_VALUE)
1545 g->border_size = parameter[GFX_ARG_BORDER_SIZE];
1547 // used for global animations and player "boring" and "sleeping" actions
1548 if (parameter[GFX_ARG_INIT_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1549 g->init_delay_fixed = parameter[GFX_ARG_INIT_DELAY_FIXED];
1550 if (parameter[GFX_ARG_INIT_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1551 g->init_delay_random = parameter[GFX_ARG_INIT_DELAY_RANDOM];
1552 if (parameter[GFX_ARG_ANIM_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1553 g->anim_delay_fixed = parameter[GFX_ARG_ANIM_DELAY_FIXED];
1554 if (parameter[GFX_ARG_ANIM_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1555 g->anim_delay_random = parameter[GFX_ARG_ANIM_DELAY_RANDOM];
1556 if (parameter[GFX_ARG_POST_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1557 g->post_delay_fixed = parameter[GFX_ARG_POST_DELAY_FIXED];
1558 if (parameter[GFX_ARG_POST_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1559 g->post_delay_random = parameter[GFX_ARG_POST_DELAY_RANDOM];
1561 // used for global animations
1562 if (parameter[GFX_ARG_INIT_EVENT] != ARG_UNDEFINED_VALUE)
1563 g->init_event = parameter[GFX_ARG_INIT_EVENT];
1564 if (parameter[GFX_ARG_ANIM_EVENT] != ARG_UNDEFINED_VALUE)
1565 g->anim_event = parameter[GFX_ARG_ANIM_EVENT];
1566 if (parameter[GFX_ARG_INIT_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1567 g->init_event_action = parameter[GFX_ARG_INIT_EVENT_ACTION];
1568 if (parameter[GFX_ARG_ANIM_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1569 g->anim_event_action = parameter[GFX_ARG_ANIM_EVENT_ACTION];
1570 if (parameter[GFX_ARG_INIT_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1571 g->init_delay_action = parameter[GFX_ARG_INIT_DELAY_ACTION];
1572 if (parameter[GFX_ARG_ANIM_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1573 g->anim_delay_action = parameter[GFX_ARG_ANIM_DELAY_ACTION];
1574 if (parameter[GFX_ARG_POST_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1575 g->post_delay_action = parameter[GFX_ARG_POST_DELAY_ACTION];
1577 // used for toon animations and global animations
1578 g->step_offset = parameter[GFX_ARG_STEP_OFFSET];
1579 g->step_xoffset = parameter[GFX_ARG_STEP_XOFFSET];
1580 g->step_yoffset = parameter[GFX_ARG_STEP_YOFFSET];
1581 g->step_delay = parameter[GFX_ARG_STEP_DELAY];
1582 g->direction = parameter[GFX_ARG_DIRECTION];
1583 g->position = parameter[GFX_ARG_POSITION];
1584 g->x = parameter[GFX_ARG_X]; // (may be uninitialized,
1585 g->y = parameter[GFX_ARG_Y]; // unlike src_x and src_y)
1587 if (g->step_delay < 1) // delay must be at least 1
1590 // this is only used for drawing font characters
1591 g->draw_xoffset = parameter[GFX_ARG_DRAW_XOFFSET];
1592 g->draw_yoffset = parameter[GFX_ARG_DRAW_YOFFSET];
1594 // use a different default value for global animations and toons
1595 if ((graphic >= IMG_GFX_GLOBAL_ANIM_1 && graphic <= IMG_GFX_GLOBAL_ANIM_8) ||
1596 (graphic >= IMG_TOON_1 && graphic <= IMG_TOON_20))
1597 g->draw_masked = TRUE;
1599 // this is used for drawing envelopes, global animations and toons
1600 if (parameter[GFX_ARG_DRAW_MASKED] != ARG_UNDEFINED_VALUE)
1601 g->draw_masked = parameter[GFX_ARG_DRAW_MASKED];
1603 // used for toon animations and global animations
1604 if (parameter[GFX_ARG_DRAW_ORDER] != ARG_UNDEFINED_VALUE)
1605 g->draw_order = parameter[GFX_ARG_DRAW_ORDER];
1607 // optional graphic for cloning all graphics settings
1608 if (parameter[GFX_ARG_CLONE_FROM] != ARG_UNDEFINED_VALUE)
1609 g->clone_from = parameter[GFX_ARG_CLONE_FROM];
1611 // optional settings for drawing title screens and title messages
1612 if (parameter[GFX_ARG_FADE_MODE] != ARG_UNDEFINED_VALUE)
1613 g->fade_mode = parameter[GFX_ARG_FADE_MODE];
1614 if (parameter[GFX_ARG_FADE_DELAY] != ARG_UNDEFINED_VALUE)
1615 g->fade_delay = parameter[GFX_ARG_FADE_DELAY];
1616 if (parameter[GFX_ARG_POST_DELAY] != ARG_UNDEFINED_VALUE)
1617 g->post_delay = parameter[GFX_ARG_POST_DELAY];
1618 if (parameter[GFX_ARG_AUTO_DELAY] != ARG_UNDEFINED_VALUE)
1619 g->auto_delay = parameter[GFX_ARG_AUTO_DELAY];
1620 if (parameter[GFX_ARG_AUTO_DELAY_UNIT] != ARG_UNDEFINED_VALUE)
1621 g->auto_delay_unit = parameter[GFX_ARG_AUTO_DELAY_UNIT];
1622 if (parameter[GFX_ARG_ALIGN] != ARG_UNDEFINED_VALUE)
1623 g->align = parameter[GFX_ARG_ALIGN];
1624 if (parameter[GFX_ARG_VALIGN] != ARG_UNDEFINED_VALUE)
1625 g->valign = parameter[GFX_ARG_VALIGN];
1626 if (parameter[GFX_ARG_SORT_PRIORITY] != ARG_UNDEFINED_VALUE)
1627 g->sort_priority = parameter[GFX_ARG_SORT_PRIORITY];
1629 if (parameter[GFX_ARG_CLASS] != ARG_UNDEFINED_VALUE)
1630 g->class = parameter[GFX_ARG_CLASS];
1631 if (parameter[GFX_ARG_STYLE] != ARG_UNDEFINED_VALUE)
1632 g->style = parameter[GFX_ARG_STYLE];
1634 // this is only used for drawing menu buttons and text
1635 g->active_xoffset = parameter[GFX_ARG_ACTIVE_XOFFSET];
1636 g->active_yoffset = parameter[GFX_ARG_ACTIVE_YOFFSET];
1637 g->pressed_xoffset = parameter[GFX_ARG_PRESSED_XOFFSET];
1638 g->pressed_yoffset = parameter[GFX_ARG_PRESSED_YOFFSET];
1641 static void set_graphic_parameters(int graphic)
1643 struct FileInfo *image = getImageListEntryFromImageID(graphic);
1644 char **parameter_raw = image->parameter;
1645 Bitmap **src_bitmaps = getBitmapsFromImageID(graphic);
1646 int parameter[NUM_GFX_ARGS];
1649 // if fallback to default artwork is done, also use the default parameters
1650 if (image->fallback_to_default)
1651 parameter_raw = image->default_parameter;
1653 // get integer values from string parameters
1654 for (i = 0; i < NUM_GFX_ARGS; i++)
1655 parameter[i] = get_graphic_parameter_value(parameter_raw[i],
1656 image_config_suffix[i].token,
1657 image_config_suffix[i].type);
1659 set_graphic_parameters_ext(graphic, parameter, src_bitmaps);
1661 UPDATE_BUSY_STATE();
1664 static void set_cloned_graphic_parameters(int graphic)
1666 int fallback_graphic = IMG_CHAR_EXCLAM;
1667 int max_num_images = getImageListSize();
1668 int clone_graphic = graphic_info[graphic].clone_from;
1669 int num_references_followed = 1;
1671 while (graphic_info[clone_graphic].clone_from != -1 &&
1672 num_references_followed < max_num_images)
1674 clone_graphic = graphic_info[clone_graphic].clone_from;
1676 num_references_followed++;
1679 if (num_references_followed >= max_num_images)
1682 Warn("error found in config file:");
1683 Warn("- config file: '%s'", getImageConfigFilename());
1684 Warn("- config token: '%s'", getTokenFromImageID(graphic));
1685 Warn("error: loop discovered when resolving cloned graphics");
1686 Warn("custom graphic rejected for this element/action");
1688 if (graphic == fallback_graphic)
1689 Fail("no fallback graphic available");
1691 Warn("fallback done to 'char_exclam' for this graphic");
1694 graphic_info[graphic] = graphic_info[fallback_graphic];
1698 graphic_info[graphic] = graphic_info[clone_graphic];
1699 graphic_info[graphic].clone_from = clone_graphic;
1703 static void InitGraphicInfo(void)
1705 int fallback_graphic = IMG_CHAR_EXCLAM;
1706 int num_images = getImageListSize();
1709 // use image size as default values for width and height for these images
1710 static int full_size_graphics[] =
1713 IMG_GLOBAL_BORDER_MAIN,
1714 IMG_GLOBAL_BORDER_SCORES,
1715 IMG_GLOBAL_BORDER_EDITOR,
1716 IMG_GLOBAL_BORDER_PLAYING,
1719 IMG_BACKGROUND_ENVELOPE_1,
1720 IMG_BACKGROUND_ENVELOPE_2,
1721 IMG_BACKGROUND_ENVELOPE_3,
1722 IMG_BACKGROUND_ENVELOPE_4,
1723 IMG_BACKGROUND_REQUEST,
1726 IMG_BACKGROUND_LOADING_INITIAL,
1727 IMG_BACKGROUND_LOADING,
1728 IMG_BACKGROUND_TITLE_INITIAL,
1729 IMG_BACKGROUND_TITLE,
1730 IMG_BACKGROUND_MAIN,
1731 IMG_BACKGROUND_NAMES,
1732 IMG_BACKGROUND_LEVELS,
1733 IMG_BACKGROUND_LEVELNR,
1734 IMG_BACKGROUND_SCORES,
1735 IMG_BACKGROUND_SCOREINFO,
1736 IMG_BACKGROUND_EDITOR,
1737 IMG_BACKGROUND_INFO,
1738 IMG_BACKGROUND_INFO_ELEMENTS,
1739 IMG_BACKGROUND_INFO_MUSIC,
1740 IMG_BACKGROUND_INFO_CREDITS,
1741 IMG_BACKGROUND_INFO_PROGRAM,
1742 IMG_BACKGROUND_INFO_VERSION,
1743 IMG_BACKGROUND_INFO_LEVELSET,
1744 IMG_BACKGROUND_SETUP,
1745 IMG_BACKGROUND_PLAYING,
1746 IMG_BACKGROUND_DOOR,
1747 IMG_BACKGROUND_TAPE,
1748 IMG_BACKGROUND_PANEL,
1749 IMG_BACKGROUND_PALETTE,
1750 IMG_BACKGROUND_TOOLBOX,
1752 IMG_TITLESCREEN_INITIAL_1,
1753 IMG_TITLESCREEN_INITIAL_2,
1754 IMG_TITLESCREEN_INITIAL_3,
1755 IMG_TITLESCREEN_INITIAL_4,
1756 IMG_TITLESCREEN_INITIAL_5,
1763 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_1,
1764 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_2,
1765 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_3,
1766 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_4,
1767 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_5,
1768 IMG_BACKGROUND_TITLEMESSAGE_1,
1769 IMG_BACKGROUND_TITLEMESSAGE_2,
1770 IMG_BACKGROUND_TITLEMESSAGE_3,
1771 IMG_BACKGROUND_TITLEMESSAGE_4,
1772 IMG_BACKGROUND_TITLEMESSAGE_5,
1777 FreeGlobalAnimEventInfo();
1779 checked_free(graphic_info);
1781 graphic_info = checked_calloc(num_images * sizeof(struct GraphicInfo));
1783 // initialize "use_image_size" flag with default value
1784 for (i = 0; i < num_images; i++)
1785 graphic_info[i].use_image_size = FALSE;
1787 // initialize "use_image_size" flag from static configuration above
1788 for (i = 0; full_size_graphics[i] != -1; i++)
1789 graphic_info[full_size_graphics[i]].use_image_size = TRUE;
1791 // first set all graphic paramaters ...
1792 for (i = 0; i < num_images; i++)
1793 set_graphic_parameters(i);
1795 // ... then copy these parameters for cloned graphics
1796 for (i = 0; i < num_images; i++)
1797 if (graphic_info[i].clone_from != -1)
1798 set_cloned_graphic_parameters(i);
1800 for (i = 0; i < num_images; i++)
1802 Bitmap *src_bitmap = graphic_info[i].bitmap;
1806 int src_bitmap_width, src_bitmap_height;
1808 // now check if no animation frames are outside of the loaded image
1810 if (graphic_info[i].bitmap == NULL)
1811 continue; // skip check for optional images that are undefined
1813 // get image size (this can differ from the standard element tile size!)
1814 width = graphic_info[i].width;
1815 height = graphic_info[i].height;
1817 // get final bitmap size (with scaling, but without small images)
1818 src_bitmap_width = graphic_info[i].src_image_width;
1819 src_bitmap_height = graphic_info[i].src_image_height;
1821 // check if first animation frame is inside specified bitmap
1823 // do not use getGraphicSourceXY() here to get position of first frame;
1824 // this avoids calculating wrong start position for out-of-bounds frame
1825 src_x = graphic_info[i].src_x;
1826 src_y = graphic_info[i].src_y;
1828 if (program.headless)
1831 if (src_x < 0 || src_y < 0 ||
1832 src_x + width > src_bitmap_width ||
1833 src_y + height > src_bitmap_height)
1836 Warn("error found in config file:");
1837 Warn("- config file: '%s'", getImageConfigFilename());
1838 Warn("- config token: '%s'", getTokenFromImageID(i));
1839 Warn("- image file: '%s'", src_bitmap->source_filename);
1840 Warn("- frame size: %d, %d", width, height);
1841 Warn("error: first animation frame out of bounds (%d, %d) [%d, %d]",
1842 src_x, src_y, src_bitmap_width, src_bitmap_height);
1843 Warn("custom graphic rejected for this element/action");
1845 if (i == fallback_graphic)
1846 Fail("no fallback graphic available");
1848 Warn("fallback done to 'char_exclam' for this graphic");
1851 graphic_info[i] = graphic_info[fallback_graphic];
1853 // if first frame out of bounds, do not check last frame anymore
1857 // check if last animation frame is inside specified bitmap
1859 last_frame = graphic_info[i].anim_frames - 1;
1860 getGraphicSourceXY(i, last_frame, &src_x, &src_y, FALSE);
1862 if (src_x < 0 || src_y < 0 ||
1863 src_x + width > src_bitmap_width ||
1864 src_y + height > src_bitmap_height)
1867 Warn("error found in config file:");
1868 Warn("- config file: '%s'", getImageConfigFilename());
1869 Warn("- config token: '%s'", getTokenFromImageID(i));
1870 Warn("- image file: '%s'", src_bitmap->source_filename);
1871 Warn("- frame size: %d, %d", width, height);
1872 Warn("error: last animation frame (%d) out of bounds (%d, %d) [%d, %d]",
1873 last_frame, src_x, src_y, src_bitmap_width, src_bitmap_height);
1874 Warn("custom graphic rejected for this element/action");
1876 if (i == fallback_graphic)
1877 Fail("no fallback graphic available");
1879 Warn("fallback done to 'char_exclam' for this graphic");
1882 graphic_info[i] = graphic_info[fallback_graphic];
1887 static void InitGraphicCompatibilityInfo(void)
1889 struct FileInfo *fi_global_door =
1890 getImageListEntryFromImageID(IMG_GLOBAL_DOOR);
1891 int num_images = getImageListSize();
1894 /* the following compatibility handling is needed for the following case:
1895 versions up to 3.3.0.0 used one large bitmap "global.door" for various
1896 graphics mainly used for door and panel graphics, like editor, tape and
1897 in-game buttons with hard-coded bitmap positions and button sizes; as
1898 these graphics now have individual definitions, redefining "global.door"
1899 to change all these graphics at once like before does not work anymore
1900 (because all those individual definitions still have their default values);
1901 to solve this, remap all those individual definitions that are not
1902 redefined to the new bitmap of "global.door" if it was redefined */
1904 // special compatibility handling if image "global.door" was redefined
1905 if (fi_global_door->redefined)
1907 for (i = 0; i < num_images; i++)
1909 struct FileInfo *fi = getImageListEntryFromImageID(i);
1911 // process only those images that still use the default settings
1914 // process all images which default to same image as "global.door"
1915 if (strEqual(fi->default_filename, fi_global_door->default_filename))
1918 Debug("init:InitGraphicCompatibilityInfo",
1919 "special treatment needed for token '%s'", fi->token);
1922 graphic_info[i].bitmaps = graphic_info[IMG_GLOBAL_DOOR].bitmaps;
1923 graphic_info[i].bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
1929 InitGraphicCompatibilityInfo_Doors();
1932 static void InitElementSoundInfo(void)
1934 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
1935 int num_property_mappings = getSoundListPropertyMappingSize();
1938 // set values to -1 to identify later as "uninitialized" values
1939 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1940 for (act = 0; act < NUM_ACTIONS; act++)
1941 element_info[i].sound[act] = -1;
1943 // initialize element/sound mapping from static configuration
1944 for (i = 0; element_to_sound[i].element > -1; i++)
1946 int element = element_to_sound[i].element;
1947 int action = element_to_sound[i].action;
1948 int sound = element_to_sound[i].sound;
1949 boolean is_class = element_to_sound[i].is_class;
1952 action = ACTION_DEFAULT;
1955 element_info[element].sound[action] = sound;
1957 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
1958 if (strEqual(element_info[j].class_name,
1959 element_info[element].class_name))
1960 element_info[j].sound[action] = sound;
1963 // initialize element class/sound mapping from dynamic configuration
1964 for (i = 0; i < num_property_mappings; i++)
1966 int element_class = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
1967 int action = property_mapping[i].ext1_index;
1968 int sound = property_mapping[i].artwork_index;
1970 if (element_class < 0 || element_class >= MAX_NUM_ELEMENTS)
1974 action = ACTION_DEFAULT;
1976 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
1977 if (strEqual(element_info[j].class_name,
1978 element_info[element_class].class_name))
1979 element_info[j].sound[action] = sound;
1982 // initialize element/sound mapping from dynamic configuration
1983 for (i = 0; i < num_property_mappings; i++)
1985 int element = property_mapping[i].base_index;
1986 int action = property_mapping[i].ext1_index;
1987 int sound = property_mapping[i].artwork_index;
1989 if (element >= MAX_NUM_ELEMENTS)
1993 action = ACTION_DEFAULT;
1995 element_info[element].sound[action] = sound;
1998 // now set all '-1' values to element specific default values
1999 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2001 for (act = 0; act < NUM_ACTIONS; act++)
2003 // generic default action sound (defined by "[default]" directive)
2004 int default_action_sound = element_info[EL_DEFAULT].sound[act];
2006 // look for special default action sound (classic game specific)
2007 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].sound[act] != -1)
2008 default_action_sound = element_info[EL_BD_DEFAULT].sound[act];
2009 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].sound[act] != -1)
2010 default_action_sound = element_info[EL_SP_DEFAULT].sound[act];
2011 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].sound[act] != -1)
2012 default_action_sound = element_info[EL_SB_DEFAULT].sound[act];
2013 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].sound[act] != -1)
2014 default_action_sound = element_info[EL_MM_DEFAULT].sound[act];
2016 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
2017 // !!! make this better !!!
2018 if (i == EL_EMPTY_SPACE)
2019 default_action_sound = element_info[EL_DEFAULT].sound[act];
2021 // no sound for this specific action -- use default action sound
2022 if (element_info[i].sound[act] == -1)
2023 element_info[i].sound[act] = default_action_sound;
2027 // copy sound settings to some elements that are only stored in level file
2028 // in native R'n'D levels, but are used by game engine in native EM levels
2029 for (i = 0; copy_properties[i][0] != -1; i++)
2030 for (j = 1; j <= 4; j++)
2031 for (act = 0; act < NUM_ACTIONS; act++)
2032 element_info[copy_properties[i][j]].sound[act] =
2033 element_info[copy_properties[i][0]].sound[act];
2036 static void InitGameModeSoundInfo(void)
2040 // set values to -1 to identify later as "uninitialized" values
2041 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2044 // initialize gamemode/sound mapping from static configuration
2045 for (i = 0; gamemode_to_sound[i].sound > -1; i++)
2047 int gamemode = gamemode_to_sound[i].gamemode;
2048 int sound = gamemode_to_sound[i].sound;
2051 gamemode = GAME_MODE_DEFAULT;
2053 menu.sound[gamemode] = sound;
2056 // now set all '-1' values to levelset specific default values
2057 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2058 if (menu.sound[i] == -1)
2059 menu.sound[i] = menu.sound[GAME_MODE_DEFAULT];
2062 static void set_sound_parameters(int sound, char **parameter_raw)
2064 int parameter[NUM_SND_ARGS];
2067 // get integer values from string parameters
2068 for (i = 0; i < NUM_SND_ARGS; i++)
2070 get_parameter_value(parameter_raw[i],
2071 sound_config_suffix[i].token,
2072 sound_config_suffix[i].type);
2074 // explicit loop mode setting in configuration overrides default value
2075 if (parameter[SND_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2076 sound_info[sound].loop = parameter[SND_ARG_MODE_LOOP];
2078 // sound volume to change the original volume when loading the sound file
2079 sound_info[sound].volume = parameter[SND_ARG_VOLUME];
2081 // sound priority to give certain sounds a higher or lower priority
2082 sound_info[sound].priority = parameter[SND_ARG_PRIORITY];
2085 static void InitSoundInfo(void)
2087 int *sound_effect_properties;
2088 int num_sounds = getSoundListSize();
2091 checked_free(sound_info);
2093 sound_effect_properties = checked_calloc(num_sounds * sizeof(int));
2094 sound_info = checked_calloc(num_sounds * sizeof(struct SoundInfo));
2096 // initialize sound effect for all elements to "no sound"
2097 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2098 for (j = 0; j < NUM_ACTIONS; j++)
2099 element_info[i].sound[j] = SND_UNDEFINED;
2101 for (i = 0; i < num_sounds; i++)
2103 struct FileInfo *sound = getSoundListEntry(i);
2104 int len_effect_text = strlen(sound->token);
2106 sound_effect_properties[i] = ACTION_OTHER;
2107 sound_info[i].loop = FALSE; // default: play sound only once
2109 // determine all loop sounds and identify certain sound classes
2111 for (j = 0; element_action_info[j].suffix; j++)
2113 int len_action_text = strlen(element_action_info[j].suffix);
2115 if (len_action_text < len_effect_text &&
2116 strEqual(&sound->token[len_effect_text - len_action_text],
2117 element_action_info[j].suffix))
2119 sound_effect_properties[i] = element_action_info[j].value;
2120 sound_info[i].loop = element_action_info[j].is_loop_sound;
2126 // associate elements and some selected sound actions
2128 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2130 if (element_info[j].class_name)
2132 int len_class_text = strlen(element_info[j].class_name);
2134 if (len_class_text + 1 < len_effect_text &&
2135 strncmp(sound->token,
2136 element_info[j].class_name, len_class_text) == 0 &&
2137 sound->token[len_class_text] == '.')
2139 int sound_action_value = sound_effect_properties[i];
2141 element_info[j].sound[sound_action_value] = i;
2146 set_sound_parameters(i, sound->parameter);
2149 free(sound_effect_properties);
2152 static void InitGameModeMusicInfo(void)
2154 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
2155 int num_property_mappings = getMusicListPropertyMappingSize();
2156 int default_levelset_music = -1;
2159 // set values to -1 to identify later as "uninitialized" values
2160 for (i = 0; i < MAX_LEVELS; i++)
2161 levelset.music[i] = -1;
2162 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2165 // initialize gamemode/music mapping from static configuration
2166 for (i = 0; gamemode_to_music[i].music > -1; i++)
2168 int gamemode = gamemode_to_music[i].gamemode;
2169 int music = gamemode_to_music[i].music;
2172 gamemode = GAME_MODE_DEFAULT;
2174 menu.music[gamemode] = music;
2177 // initialize gamemode/music mapping from dynamic configuration
2178 for (i = 0; i < num_property_mappings; i++)
2180 int prefix = property_mapping[i].base_index;
2181 int gamemode = property_mapping[i].ext2_index;
2182 int level = property_mapping[i].ext3_index;
2183 int music = property_mapping[i].artwork_index;
2185 if (prefix < 0 || prefix >= NUM_MUSIC_PREFIXES)
2189 gamemode = GAME_MODE_DEFAULT;
2191 // level specific music only allowed for in-game music
2192 if (level != -1 && gamemode == GAME_MODE_DEFAULT)
2193 gamemode = GAME_MODE_PLAYING;
2198 default_levelset_music = music;
2201 if (gamemode == GAME_MODE_PLAYING || gamemode == GAME_MODE_DEFAULT)
2202 levelset.music[level] = music;
2203 if (gamemode != GAME_MODE_PLAYING)
2204 menu.music[gamemode] = music;
2207 // now set all '-1' values to menu specific default values
2208 // (undefined values of "levelset.music[]" might stay at "-1" to
2209 // allow dynamic selection of music files from music directory!)
2210 for (i = 0; i < MAX_LEVELS; i++)
2211 if (levelset.music[i] == -1)
2212 levelset.music[i] = default_levelset_music;
2213 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2214 if (menu.music[i] == -1)
2215 menu.music[i] = menu.music[GAME_MODE_DEFAULT];
2218 static void set_music_parameters(int music, char **parameter_raw)
2220 int parameter[NUM_MUS_ARGS];
2223 // get integer values from string parameters
2224 for (i = 0; i < NUM_MUS_ARGS; i++)
2226 get_parameter_value(parameter_raw[i],
2227 music_config_suffix[i].token,
2228 music_config_suffix[i].type);
2230 // explicit loop mode setting in configuration overrides default value
2231 if (parameter[MUS_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2232 music_info[music].loop = parameter[MUS_ARG_MODE_LOOP];
2235 static void InitMusicInfo(void)
2237 int num_music = getMusicListSize();
2240 checked_free(music_info);
2242 music_info = checked_calloc(num_music * sizeof(struct MusicInfo));
2244 for (i = 0; i < num_music; i++)
2246 struct FileInfo *music = getMusicListEntry(i);
2247 int len_music_text = strlen(music->token);
2249 music_info[i].loop = TRUE; // default: play music in loop mode
2251 // determine all loop music
2253 for (j = 0; music_prefix_info[j].prefix; j++)
2255 int len_prefix_text = strlen(music_prefix_info[j].prefix);
2257 if (len_prefix_text < len_music_text &&
2258 strncmp(music->token,
2259 music_prefix_info[j].prefix, len_prefix_text) == 0)
2261 music_info[i].loop = music_prefix_info[j].is_loop_music;
2267 set_music_parameters(i, music->parameter);
2272 static void InitGameInfoFromArtworkInfo(void)
2274 // special case: store initial value of custom artwork setting
2275 game.use_masked_elements_initial = game.use_masked_elements;
2278 static void ReinitializeGraphics(void)
2280 print_timestamp_init("ReinitializeGraphics");
2282 InitGfxTileSizeInfo(game.tile_size, TILESIZE);
2284 InitGraphicInfo(); // graphic properties mapping
2285 print_timestamp_time("InitGraphicInfo");
2286 InitElementGraphicInfo(); // element game graphic mapping
2287 print_timestamp_time("InitElementGraphicInfo");
2288 InitElementSpecialGraphicInfo(); // element special graphic mapping
2289 print_timestamp_time("InitElementSpecialGraphicInfo");
2291 InitElementSmallImages(); // scale elements to all needed sizes
2292 print_timestamp_time("InitElementSmallImages");
2293 InitScaledImages(); // scale all other images, if needed
2294 print_timestamp_time("InitScaledImages");
2295 InitBitmapPointers(); // set standard size bitmap pointers
2296 print_timestamp_time("InitBitmapPointers");
2297 InitFontGraphicInfo(); // initialize text drawing functions
2298 print_timestamp_time("InitFontGraphicInfo");
2299 InitGlobalAnimGraphicInfo(); // initialize global animation config
2300 print_timestamp_time("InitGlobalAnimGraphicInfo");
2302 InitImageTextures(); // create textures for certain images
2303 print_timestamp_time("InitImageTextures");
2305 InitGraphicInfo_EM(); // graphic mapping for EM engine
2306 print_timestamp_time("InitGraphicInfo_EM");
2308 InitGraphicCompatibilityInfo();
2309 print_timestamp_time("InitGraphicCompatibilityInfo");
2312 print_timestamp_time("InitGadgets");
2314 print_timestamp_time("InitDoors");
2316 InitGameInfoFromArtworkInfo();
2318 print_timestamp_done("ReinitializeGraphics");
2321 static void ReinitializeSounds(void)
2323 InitSoundInfo(); // sound properties mapping
2324 InitElementSoundInfo(); // element game sound mapping
2325 InitGameModeSoundInfo(); // game mode sound mapping
2326 InitGlobalAnimSoundInfo(); // global animation sound settings
2328 InitPlayLevelSound(); // internal game sound settings
2331 static void ReinitializeMusic(void)
2333 InitMusicInfo(); // music properties mapping
2334 InitGameModeMusicInfo(); // game mode music mapping
2335 InitGlobalAnimMusicInfo(); // global animation music settings
2338 static int get_special_property_bit(int element, int property_bit_nr)
2340 struct PropertyBitInfo
2346 static struct PropertyBitInfo pb_can_move_into_acid[] =
2348 // the player may be able fall into acid when gravity is activated
2353 { EL_SP_MURPHY, 0 },
2354 { EL_SOKOBAN_FIELD_PLAYER, 0 },
2356 // all elements that can move may be able to also move into acid
2359 { EL_BUG_RIGHT, 1 },
2362 { EL_SPACESHIP, 2 },
2363 { EL_SPACESHIP_LEFT, 2 },
2364 { EL_SPACESHIP_RIGHT, 2 },
2365 { EL_SPACESHIP_UP, 2 },
2366 { EL_SPACESHIP_DOWN, 2 },
2367 { EL_BD_BUTTERFLY, 3 },
2368 { EL_BD_BUTTERFLY_LEFT, 3 },
2369 { EL_BD_BUTTERFLY_RIGHT, 3 },
2370 { EL_BD_BUTTERFLY_UP, 3 },
2371 { EL_BD_BUTTERFLY_DOWN, 3 },
2372 { EL_BD_FIREFLY, 4 },
2373 { EL_BD_FIREFLY_LEFT, 4 },
2374 { EL_BD_FIREFLY_RIGHT, 4 },
2375 { EL_BD_FIREFLY_UP, 4 },
2376 { EL_BD_FIREFLY_DOWN, 4 },
2378 { EL_YAMYAM_LEFT, 5 },
2379 { EL_YAMYAM_RIGHT, 5 },
2380 { EL_YAMYAM_UP, 5 },
2381 { EL_YAMYAM_DOWN, 5 },
2382 { EL_DARK_YAMYAM, 6 },
2385 { EL_PACMAN_LEFT, 8 },
2386 { EL_PACMAN_RIGHT, 8 },
2387 { EL_PACMAN_UP, 8 },
2388 { EL_PACMAN_DOWN, 8 },
2390 { EL_MOLE_LEFT, 9 },
2391 { EL_MOLE_RIGHT, 9 },
2393 { EL_MOLE_DOWN, 9 },
2397 { EL_SATELLITE, 13 },
2398 { EL_SP_SNIKSNAK, 14 },
2399 { EL_SP_ELECTRON, 15 },
2402 { EL_SPRING_LEFT, 17 },
2403 { EL_SPRING_RIGHT, 17 },
2404 { EL_EMC_ANDROID, 18 },
2409 static struct PropertyBitInfo pb_dont_collide_with[] =
2411 { EL_SP_SNIKSNAK, 0 },
2412 { EL_SP_ELECTRON, 1 },
2420 struct PropertyBitInfo *pb_info;
2423 { EP_CAN_MOVE_INTO_ACID, pb_can_move_into_acid },
2424 { EP_DONT_COLLIDE_WITH, pb_dont_collide_with },
2429 struct PropertyBitInfo *pb_info = NULL;
2432 for (i = 0; pb_definition[i].bit_nr != -1; i++)
2433 if (pb_definition[i].bit_nr == property_bit_nr)
2434 pb_info = pb_definition[i].pb_info;
2436 if (pb_info == NULL)
2439 for (i = 0; pb_info[i].element != -1; i++)
2440 if (pb_info[i].element == element)
2441 return pb_info[i].bit_nr;
2446 void setBitfieldProperty(int *bitfield, int property_bit_nr, int element,
2447 boolean property_value)
2449 int bit_nr = get_special_property_bit(element, property_bit_nr);
2454 *bitfield |= (1 << bit_nr);
2456 *bitfield &= ~(1 << bit_nr);
2460 boolean getBitfieldProperty(int *bitfield, int property_bit_nr, int element)
2462 int bit_nr = get_special_property_bit(element, property_bit_nr);
2465 return ((*bitfield & (1 << bit_nr)) != 0);
2470 static void ResolveGroupElementExt(int group_element, int recursion_depth)
2472 static int group_nr;
2473 static struct ElementGroupInfo *group;
2474 struct ElementGroupInfo *actual_group = element_info[group_element].group;
2477 if (actual_group == NULL) // not yet initialized
2480 if (recursion_depth > NUM_GROUP_ELEMENTS) // recursion too deep
2482 Warn("recursion too deep when resolving group element %d",
2483 group_element - EL_GROUP_START + 1);
2485 // replace element which caused too deep recursion by question mark
2486 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
2491 if (recursion_depth == 0) // initialization
2493 group = actual_group;
2494 group_nr = GROUP_NR(group_element);
2496 group->num_elements_resolved = 0;
2497 group->choice_pos = 0;
2499 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2500 element_info[i].in_group[group_nr] = FALSE;
2503 for (i = 0; i < actual_group->num_elements; i++)
2505 int element = actual_group->element[i];
2507 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
2510 if (IS_GROUP_ELEMENT(element))
2511 ResolveGroupElementExt(element, recursion_depth + 1);
2514 group->element_resolved[group->num_elements_resolved++] = element;
2515 element_info[element].in_group[group_nr] = TRUE;
2520 void ResolveGroupElement(int group_element)
2522 ResolveGroupElementExt(group_element, 0);
2525 void InitElementPropertiesStatic(void)
2527 static boolean clipboard_elements_initialized = FALSE;
2529 static int ep_diggable[] =
2534 EL_SP_BUGGY_BASE_ACTIVATING,
2537 EL_INVISIBLE_SAND_ACTIVE,
2540 // !!! currently not diggable, but handled by 'ep_dont_run_into' !!!
2541 // (if amoeba can grow into anything diggable, maybe keep these out)
2546 EL_SP_BUGGY_BASE_ACTIVE,
2553 static int ep_collectible_only[] =
2575 EL_DYNABOMB_INCREASE_NUMBER,
2576 EL_DYNABOMB_INCREASE_SIZE,
2577 EL_DYNABOMB_INCREASE_POWER,
2595 // !!! handle separately !!!
2596 EL_DC_LANDMINE, // deadly when running into, but can be snapped
2602 static int ep_dont_run_into[] =
2604 // same elements as in 'ep_dont_touch'
2610 // same elements as in 'ep_dont_collide_with'
2622 // !!! maybe this should better be handled by 'ep_diggable' !!!
2627 EL_SP_BUGGY_BASE_ACTIVE,
2634 static int ep_dont_collide_with[] =
2636 // same elements as in 'ep_dont_touch'
2653 static int ep_dont_touch[] =
2663 static int ep_indestructible[] =
2667 EL_ACID_POOL_TOPLEFT,
2668 EL_ACID_POOL_TOPRIGHT,
2669 EL_ACID_POOL_BOTTOMLEFT,
2670 EL_ACID_POOL_BOTTOM,
2671 EL_ACID_POOL_BOTTOMRIGHT,
2672 EL_SP_HARDWARE_GRAY,
2673 EL_SP_HARDWARE_GREEN,
2674 EL_SP_HARDWARE_BLUE,
2676 EL_SP_HARDWARE_YELLOW,
2677 EL_SP_HARDWARE_BASE_1,
2678 EL_SP_HARDWARE_BASE_2,
2679 EL_SP_HARDWARE_BASE_3,
2680 EL_SP_HARDWARE_BASE_4,
2681 EL_SP_HARDWARE_BASE_5,
2682 EL_SP_HARDWARE_BASE_6,
2683 EL_INVISIBLE_STEELWALL,
2684 EL_INVISIBLE_STEELWALL_ACTIVE,
2685 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2686 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
2687 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
2688 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2689 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
2690 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
2691 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2692 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
2693 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
2694 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
2695 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
2696 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
2698 EL_LIGHT_SWITCH_ACTIVE,
2699 EL_SIGN_EXCLAMATION,
2700 EL_SIGN_RADIOACTIVITY,
2707 EL_SIGN_ENTRY_FORBIDDEN,
2708 EL_SIGN_EMERGENCY_EXIT,
2716 EL_STEEL_EXIT_CLOSED,
2718 EL_STEEL_EXIT_OPENING,
2719 EL_STEEL_EXIT_CLOSING,
2720 EL_EM_STEEL_EXIT_CLOSED,
2721 EL_EM_STEEL_EXIT_OPEN,
2722 EL_EM_STEEL_EXIT_OPENING,
2723 EL_EM_STEEL_EXIT_CLOSING,
2724 EL_DC_STEELWALL_1_LEFT,
2725 EL_DC_STEELWALL_1_RIGHT,
2726 EL_DC_STEELWALL_1_TOP,
2727 EL_DC_STEELWALL_1_BOTTOM,
2728 EL_DC_STEELWALL_1_HORIZONTAL,
2729 EL_DC_STEELWALL_1_VERTICAL,
2730 EL_DC_STEELWALL_1_TOPLEFT,
2731 EL_DC_STEELWALL_1_TOPRIGHT,
2732 EL_DC_STEELWALL_1_BOTTOMLEFT,
2733 EL_DC_STEELWALL_1_BOTTOMRIGHT,
2734 EL_DC_STEELWALL_1_TOPLEFT_2,
2735 EL_DC_STEELWALL_1_TOPRIGHT_2,
2736 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
2737 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
2738 EL_DC_STEELWALL_2_LEFT,
2739 EL_DC_STEELWALL_2_RIGHT,
2740 EL_DC_STEELWALL_2_TOP,
2741 EL_DC_STEELWALL_2_BOTTOM,
2742 EL_DC_STEELWALL_2_HORIZONTAL,
2743 EL_DC_STEELWALL_2_VERTICAL,
2744 EL_DC_STEELWALL_2_MIDDLE,
2745 EL_DC_STEELWALL_2_SINGLE,
2746 EL_STEELWALL_SLIPPERY,
2760 EL_GATE_1_GRAY_ACTIVE,
2761 EL_GATE_2_GRAY_ACTIVE,
2762 EL_GATE_3_GRAY_ACTIVE,
2763 EL_GATE_4_GRAY_ACTIVE,
2772 EL_EM_GATE_1_GRAY_ACTIVE,
2773 EL_EM_GATE_2_GRAY_ACTIVE,
2774 EL_EM_GATE_3_GRAY_ACTIVE,
2775 EL_EM_GATE_4_GRAY_ACTIVE,
2784 EL_EMC_GATE_5_GRAY_ACTIVE,
2785 EL_EMC_GATE_6_GRAY_ACTIVE,
2786 EL_EMC_GATE_7_GRAY_ACTIVE,
2787 EL_EMC_GATE_8_GRAY_ACTIVE,
2789 EL_DC_GATE_WHITE_GRAY,
2790 EL_DC_GATE_WHITE_GRAY_ACTIVE,
2791 EL_DC_GATE_FAKE_GRAY,
2793 EL_SWITCHGATE_OPENING,
2794 EL_SWITCHGATE_CLOSED,
2795 EL_SWITCHGATE_CLOSING,
2796 EL_DC_SWITCHGATE_SWITCH_UP,
2797 EL_DC_SWITCHGATE_SWITCH_DOWN,
2799 EL_TIMEGATE_OPENING,
2801 EL_TIMEGATE_CLOSING,
2802 EL_DC_TIMEGATE_SWITCH,
2803 EL_DC_TIMEGATE_SWITCH_ACTIVE,
2807 EL_TUBE_VERTICAL_LEFT,
2808 EL_TUBE_VERTICAL_RIGHT,
2809 EL_TUBE_HORIZONTAL_UP,
2810 EL_TUBE_HORIZONTAL_DOWN,
2815 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
2816 EL_EXPANDABLE_STEELWALL_VERTICAL,
2817 EL_EXPANDABLE_STEELWALL_ANY,
2822 static int ep_slippery[] =
2836 EL_ROBOT_WHEEL_ACTIVE,
2842 EL_ACID_POOL_TOPLEFT,
2843 EL_ACID_POOL_TOPRIGHT,
2853 EL_STEELWALL_SLIPPERY,
2856 EL_EMC_WALL_SLIPPERY_1,
2857 EL_EMC_WALL_SLIPPERY_2,
2858 EL_EMC_WALL_SLIPPERY_3,
2859 EL_EMC_WALL_SLIPPERY_4,
2861 EL_EMC_MAGIC_BALL_ACTIVE,
2866 static int ep_can_change[] =
2871 static int ep_can_move[] =
2873 // same elements as in 'pb_can_move_into_acid'
2896 static int ep_can_fall[] =
2911 EL_QUICKSAND_FAST_FULL,
2913 EL_BD_MAGIC_WALL_FULL,
2914 EL_DC_MAGIC_WALL_FULL,
2928 static int ep_can_smash_player[] =
2954 static int ep_can_smash_enemies[] =
2963 static int ep_can_smash_everything[] =
2972 static int ep_explodes_by_fire[] =
2974 // same elements as in 'ep_explodes_impact'
2979 // same elements as in 'ep_explodes_smashed'
2989 EL_EM_DYNAMITE_ACTIVE,
2990 EL_DYNABOMB_PLAYER_1_ACTIVE,
2991 EL_DYNABOMB_PLAYER_2_ACTIVE,
2992 EL_DYNABOMB_PLAYER_3_ACTIVE,
2993 EL_DYNABOMB_PLAYER_4_ACTIVE,
2994 EL_DYNABOMB_INCREASE_NUMBER,
2995 EL_DYNABOMB_INCREASE_SIZE,
2996 EL_DYNABOMB_INCREASE_POWER,
2997 EL_SP_DISK_RED_ACTIVE,
3011 static int ep_explodes_smashed[] =
3013 // same elements as in 'ep_explodes_impact'
3027 static int ep_explodes_impact[] =
3036 static int ep_walkable_over[] =
3056 EL_SOKOBAN_FIELD_EMPTY,
3063 EL_EM_STEEL_EXIT_OPEN,
3064 EL_EM_STEEL_EXIT_OPENING,
3073 EL_GATE_1_GRAY_ACTIVE,
3074 EL_GATE_2_GRAY_ACTIVE,
3075 EL_GATE_3_GRAY_ACTIVE,
3076 EL_GATE_4_GRAY_ACTIVE,
3084 static int ep_walkable_inside[] =
3089 EL_TUBE_VERTICAL_LEFT,
3090 EL_TUBE_VERTICAL_RIGHT,
3091 EL_TUBE_HORIZONTAL_UP,
3092 EL_TUBE_HORIZONTAL_DOWN,
3101 static int ep_walkable_under[] =
3106 static int ep_passable_over[] =
3116 EL_EM_GATE_1_GRAY_ACTIVE,
3117 EL_EM_GATE_2_GRAY_ACTIVE,
3118 EL_EM_GATE_3_GRAY_ACTIVE,
3119 EL_EM_GATE_4_GRAY_ACTIVE,
3128 EL_EMC_GATE_5_GRAY_ACTIVE,
3129 EL_EMC_GATE_6_GRAY_ACTIVE,
3130 EL_EMC_GATE_7_GRAY_ACTIVE,
3131 EL_EMC_GATE_8_GRAY_ACTIVE,
3133 EL_DC_GATE_WHITE_GRAY,
3134 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3141 static int ep_passable_inside[] =
3147 EL_SP_PORT_HORIZONTAL,
3148 EL_SP_PORT_VERTICAL,
3150 EL_SP_GRAVITY_PORT_LEFT,
3151 EL_SP_GRAVITY_PORT_RIGHT,
3152 EL_SP_GRAVITY_PORT_UP,
3153 EL_SP_GRAVITY_PORT_DOWN,
3154 EL_SP_GRAVITY_ON_PORT_LEFT,
3155 EL_SP_GRAVITY_ON_PORT_RIGHT,
3156 EL_SP_GRAVITY_ON_PORT_UP,
3157 EL_SP_GRAVITY_ON_PORT_DOWN,
3158 EL_SP_GRAVITY_OFF_PORT_LEFT,
3159 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3160 EL_SP_GRAVITY_OFF_PORT_UP,
3161 EL_SP_GRAVITY_OFF_PORT_DOWN,
3166 static int ep_passable_under[] =
3171 static int ep_droppable[] =
3176 static int ep_explodes_1x1_old[] =
3181 static int ep_pushable[] =
3193 EL_SOKOBAN_FIELD_FULL,
3202 static int ep_explodes_cross_old[] =
3207 static int ep_protected[] =
3209 // same elements as in 'ep_walkable_inside'
3213 EL_TUBE_VERTICAL_LEFT,
3214 EL_TUBE_VERTICAL_RIGHT,
3215 EL_TUBE_HORIZONTAL_UP,
3216 EL_TUBE_HORIZONTAL_DOWN,
3222 // same elements as in 'ep_passable_over'
3231 EL_EM_GATE_1_GRAY_ACTIVE,
3232 EL_EM_GATE_2_GRAY_ACTIVE,
3233 EL_EM_GATE_3_GRAY_ACTIVE,
3234 EL_EM_GATE_4_GRAY_ACTIVE,
3243 EL_EMC_GATE_5_GRAY_ACTIVE,
3244 EL_EMC_GATE_6_GRAY_ACTIVE,
3245 EL_EMC_GATE_7_GRAY_ACTIVE,
3246 EL_EMC_GATE_8_GRAY_ACTIVE,
3248 EL_DC_GATE_WHITE_GRAY,
3249 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3253 // same elements as in 'ep_passable_inside'
3258 EL_SP_PORT_HORIZONTAL,
3259 EL_SP_PORT_VERTICAL,
3261 EL_SP_GRAVITY_PORT_LEFT,
3262 EL_SP_GRAVITY_PORT_RIGHT,
3263 EL_SP_GRAVITY_PORT_UP,
3264 EL_SP_GRAVITY_PORT_DOWN,
3265 EL_SP_GRAVITY_ON_PORT_LEFT,
3266 EL_SP_GRAVITY_ON_PORT_RIGHT,
3267 EL_SP_GRAVITY_ON_PORT_UP,
3268 EL_SP_GRAVITY_ON_PORT_DOWN,
3269 EL_SP_GRAVITY_OFF_PORT_LEFT,
3270 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3271 EL_SP_GRAVITY_OFF_PORT_UP,
3272 EL_SP_GRAVITY_OFF_PORT_DOWN,
3277 static int ep_throwable[] =
3282 static int ep_can_explode[] =
3284 // same elements as in 'ep_explodes_impact'
3289 // same elements as in 'ep_explodes_smashed'
3295 // elements that can explode by explosion or by dragonfire
3299 EL_EM_DYNAMITE_ACTIVE,
3300 EL_DYNABOMB_PLAYER_1_ACTIVE,
3301 EL_DYNABOMB_PLAYER_2_ACTIVE,
3302 EL_DYNABOMB_PLAYER_3_ACTIVE,
3303 EL_DYNABOMB_PLAYER_4_ACTIVE,
3304 EL_DYNABOMB_INCREASE_NUMBER,
3305 EL_DYNABOMB_INCREASE_SIZE,
3306 EL_DYNABOMB_INCREASE_POWER,
3307 EL_SP_DISK_RED_ACTIVE,
3315 // elements that can explode only by explosion
3321 static int ep_gravity_reachable[] =
3327 EL_INVISIBLE_SAND_ACTIVE,
3332 EL_SP_PORT_HORIZONTAL,
3333 EL_SP_PORT_VERTICAL,
3335 EL_SP_GRAVITY_PORT_LEFT,
3336 EL_SP_GRAVITY_PORT_RIGHT,
3337 EL_SP_GRAVITY_PORT_UP,
3338 EL_SP_GRAVITY_PORT_DOWN,
3339 EL_SP_GRAVITY_ON_PORT_LEFT,
3340 EL_SP_GRAVITY_ON_PORT_RIGHT,
3341 EL_SP_GRAVITY_ON_PORT_UP,
3342 EL_SP_GRAVITY_ON_PORT_DOWN,
3343 EL_SP_GRAVITY_OFF_PORT_LEFT,
3344 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3345 EL_SP_GRAVITY_OFF_PORT_UP,
3346 EL_SP_GRAVITY_OFF_PORT_DOWN,
3352 static int ep_empty_space[] =
3375 static int ep_player[] =
3382 EL_SOKOBAN_FIELD_PLAYER,
3388 static int ep_can_pass_magic_wall[] =
3402 static int ep_can_pass_dc_magic_wall[] =
3418 static int ep_switchable[] =
3422 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3423 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3424 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3425 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3426 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3427 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3428 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3429 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3430 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3431 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3432 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3433 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3434 EL_SWITCHGATE_SWITCH_UP,
3435 EL_SWITCHGATE_SWITCH_DOWN,
3436 EL_DC_SWITCHGATE_SWITCH_UP,
3437 EL_DC_SWITCHGATE_SWITCH_DOWN,
3439 EL_LIGHT_SWITCH_ACTIVE,
3441 EL_DC_TIMEGATE_SWITCH,
3442 EL_BALLOON_SWITCH_LEFT,
3443 EL_BALLOON_SWITCH_RIGHT,
3444 EL_BALLOON_SWITCH_UP,
3445 EL_BALLOON_SWITCH_DOWN,
3446 EL_BALLOON_SWITCH_ANY,
3447 EL_BALLOON_SWITCH_NONE,
3450 EL_EMC_MAGIC_BALL_SWITCH,
3451 EL_EMC_MAGIC_BALL_SWITCH_ACTIVE,
3456 static int ep_bd_element[] =
3490 static int ep_sp_element[] =
3492 // should always be valid
3495 // standard classic Supaplex elements
3502 EL_SP_HARDWARE_GRAY,
3510 EL_SP_GRAVITY_PORT_RIGHT,
3511 EL_SP_GRAVITY_PORT_DOWN,
3512 EL_SP_GRAVITY_PORT_LEFT,
3513 EL_SP_GRAVITY_PORT_UP,
3518 EL_SP_PORT_VERTICAL,
3519 EL_SP_PORT_HORIZONTAL,
3525 EL_SP_HARDWARE_BASE_1,
3526 EL_SP_HARDWARE_GREEN,
3527 EL_SP_HARDWARE_BLUE,
3529 EL_SP_HARDWARE_YELLOW,
3530 EL_SP_HARDWARE_BASE_2,
3531 EL_SP_HARDWARE_BASE_3,
3532 EL_SP_HARDWARE_BASE_4,
3533 EL_SP_HARDWARE_BASE_5,
3534 EL_SP_HARDWARE_BASE_6,
3538 // additional elements that appeared in newer Supaplex levels
3541 // additional gravity port elements (not switching, but setting gravity)
3542 EL_SP_GRAVITY_ON_PORT_LEFT,
3543 EL_SP_GRAVITY_ON_PORT_RIGHT,
3544 EL_SP_GRAVITY_ON_PORT_UP,
3545 EL_SP_GRAVITY_ON_PORT_DOWN,
3546 EL_SP_GRAVITY_OFF_PORT_LEFT,
3547 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3548 EL_SP_GRAVITY_OFF_PORT_UP,
3549 EL_SP_GRAVITY_OFF_PORT_DOWN,
3551 // more than one Murphy in a level results in an inactive clone
3554 // runtime Supaplex elements
3555 EL_SP_DISK_RED_ACTIVE,
3556 EL_SP_TERMINAL_ACTIVE,
3557 EL_SP_BUGGY_BASE_ACTIVATING,
3558 EL_SP_BUGGY_BASE_ACTIVE,
3565 static int ep_sb_element[] =
3570 EL_SOKOBAN_FIELD_EMPTY,
3571 EL_SOKOBAN_FIELD_FULL,
3572 EL_SOKOBAN_FIELD_PLAYER,
3577 EL_INVISIBLE_STEELWALL,
3582 static int ep_gem[] =
3594 static int ep_food_dark_yamyam[] =
3622 static int ep_food_penguin[] =
3636 static int ep_food_pig[] =
3648 static int ep_historic_wall[] =
3659 EL_GATE_1_GRAY_ACTIVE,
3660 EL_GATE_2_GRAY_ACTIVE,
3661 EL_GATE_3_GRAY_ACTIVE,
3662 EL_GATE_4_GRAY_ACTIVE,
3671 EL_EM_GATE_1_GRAY_ACTIVE,
3672 EL_EM_GATE_2_GRAY_ACTIVE,
3673 EL_EM_GATE_3_GRAY_ACTIVE,
3674 EL_EM_GATE_4_GRAY_ACTIVE,
3681 EL_EXPANDABLE_WALL_HORIZONTAL,
3682 EL_EXPANDABLE_WALL_VERTICAL,
3683 EL_EXPANDABLE_WALL_ANY,
3684 EL_EXPANDABLE_WALL_GROWING,
3685 EL_BD_EXPANDABLE_WALL,
3692 EL_SP_HARDWARE_GRAY,
3693 EL_SP_HARDWARE_GREEN,
3694 EL_SP_HARDWARE_BLUE,
3696 EL_SP_HARDWARE_YELLOW,
3697 EL_SP_HARDWARE_BASE_1,
3698 EL_SP_HARDWARE_BASE_2,
3699 EL_SP_HARDWARE_BASE_3,
3700 EL_SP_HARDWARE_BASE_4,
3701 EL_SP_HARDWARE_BASE_5,
3702 EL_SP_HARDWARE_BASE_6,
3704 EL_SP_TERMINAL_ACTIVE,
3707 EL_INVISIBLE_STEELWALL,
3708 EL_INVISIBLE_STEELWALL_ACTIVE,
3710 EL_INVISIBLE_WALL_ACTIVE,
3711 EL_STEELWALL_SLIPPERY,
3728 static int ep_historic_solid[] =
3732 EL_EXPANDABLE_WALL_HORIZONTAL,
3733 EL_EXPANDABLE_WALL_VERTICAL,
3734 EL_EXPANDABLE_WALL_ANY,
3735 EL_BD_EXPANDABLE_WALL,
3748 EL_QUICKSAND_FILLING,
3749 EL_QUICKSAND_EMPTYING,
3751 EL_MAGIC_WALL_ACTIVE,
3752 EL_MAGIC_WALL_EMPTYING,
3753 EL_MAGIC_WALL_FILLING,
3757 EL_BD_MAGIC_WALL_ACTIVE,
3758 EL_BD_MAGIC_WALL_EMPTYING,
3759 EL_BD_MAGIC_WALL_FULL,
3760 EL_BD_MAGIC_WALL_FILLING,
3761 EL_BD_MAGIC_WALL_DEAD,
3770 EL_SP_TERMINAL_ACTIVE,
3774 EL_INVISIBLE_WALL_ACTIVE,
3775 EL_SWITCHGATE_SWITCH_UP,
3776 EL_SWITCHGATE_SWITCH_DOWN,
3778 EL_TIMEGATE_SWITCH_ACTIVE,
3790 // the following elements are a direct copy of "indestructible" elements,
3791 // except "EL_ACID", which is "indestructible", but not "solid"!
3796 EL_ACID_POOL_TOPLEFT,
3797 EL_ACID_POOL_TOPRIGHT,
3798 EL_ACID_POOL_BOTTOMLEFT,
3799 EL_ACID_POOL_BOTTOM,
3800 EL_ACID_POOL_BOTTOMRIGHT,
3801 EL_SP_HARDWARE_GRAY,
3802 EL_SP_HARDWARE_GREEN,
3803 EL_SP_HARDWARE_BLUE,
3805 EL_SP_HARDWARE_YELLOW,
3806 EL_SP_HARDWARE_BASE_1,
3807 EL_SP_HARDWARE_BASE_2,
3808 EL_SP_HARDWARE_BASE_3,
3809 EL_SP_HARDWARE_BASE_4,
3810 EL_SP_HARDWARE_BASE_5,
3811 EL_SP_HARDWARE_BASE_6,
3812 EL_INVISIBLE_STEELWALL,
3813 EL_INVISIBLE_STEELWALL_ACTIVE,
3814 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3815 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3816 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3817 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3818 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3819 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3820 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3821 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3822 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3823 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3824 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3825 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3827 EL_LIGHT_SWITCH_ACTIVE,
3828 EL_SIGN_EXCLAMATION,
3829 EL_SIGN_RADIOACTIVITY,
3836 EL_SIGN_ENTRY_FORBIDDEN,
3837 EL_SIGN_EMERGENCY_EXIT,
3845 EL_STEEL_EXIT_CLOSED,
3847 EL_STEEL_EXIT_OPENING,
3848 EL_STEEL_EXIT_CLOSING,
3849 EL_EM_STEEL_EXIT_CLOSED,
3850 EL_EM_STEEL_EXIT_OPEN,
3851 EL_EM_STEEL_EXIT_OPENING,
3852 EL_EM_STEEL_EXIT_CLOSING,
3853 EL_DC_STEELWALL_1_LEFT,
3854 EL_DC_STEELWALL_1_RIGHT,
3855 EL_DC_STEELWALL_1_TOP,
3856 EL_DC_STEELWALL_1_BOTTOM,
3857 EL_DC_STEELWALL_1_HORIZONTAL,
3858 EL_DC_STEELWALL_1_VERTICAL,
3859 EL_DC_STEELWALL_1_TOPLEFT,
3860 EL_DC_STEELWALL_1_TOPRIGHT,
3861 EL_DC_STEELWALL_1_BOTTOMLEFT,
3862 EL_DC_STEELWALL_1_BOTTOMRIGHT,
3863 EL_DC_STEELWALL_1_TOPLEFT_2,
3864 EL_DC_STEELWALL_1_TOPRIGHT_2,
3865 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
3866 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
3867 EL_DC_STEELWALL_2_LEFT,
3868 EL_DC_STEELWALL_2_RIGHT,
3869 EL_DC_STEELWALL_2_TOP,
3870 EL_DC_STEELWALL_2_BOTTOM,
3871 EL_DC_STEELWALL_2_HORIZONTAL,
3872 EL_DC_STEELWALL_2_VERTICAL,
3873 EL_DC_STEELWALL_2_MIDDLE,
3874 EL_DC_STEELWALL_2_SINGLE,
3875 EL_STEELWALL_SLIPPERY,
3889 EL_GATE_1_GRAY_ACTIVE,
3890 EL_GATE_2_GRAY_ACTIVE,
3891 EL_GATE_3_GRAY_ACTIVE,
3892 EL_GATE_4_GRAY_ACTIVE,
3901 EL_EM_GATE_1_GRAY_ACTIVE,
3902 EL_EM_GATE_2_GRAY_ACTIVE,
3903 EL_EM_GATE_3_GRAY_ACTIVE,
3904 EL_EM_GATE_4_GRAY_ACTIVE,
3913 EL_EMC_GATE_5_GRAY_ACTIVE,
3914 EL_EMC_GATE_6_GRAY_ACTIVE,
3915 EL_EMC_GATE_7_GRAY_ACTIVE,
3916 EL_EMC_GATE_8_GRAY_ACTIVE,
3918 EL_DC_GATE_WHITE_GRAY,
3919 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3920 EL_DC_GATE_FAKE_GRAY,
3922 EL_SWITCHGATE_OPENING,
3923 EL_SWITCHGATE_CLOSED,
3924 EL_SWITCHGATE_CLOSING,
3925 EL_DC_SWITCHGATE_SWITCH_UP,
3926 EL_DC_SWITCHGATE_SWITCH_DOWN,
3928 EL_TIMEGATE_OPENING,
3930 EL_TIMEGATE_CLOSING,
3931 EL_DC_TIMEGATE_SWITCH,
3932 EL_DC_TIMEGATE_SWITCH_ACTIVE,
3936 EL_TUBE_VERTICAL_LEFT,
3937 EL_TUBE_VERTICAL_RIGHT,
3938 EL_TUBE_HORIZONTAL_UP,
3939 EL_TUBE_HORIZONTAL_DOWN,
3944 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
3945 EL_EXPANDABLE_STEELWALL_VERTICAL,
3946 EL_EXPANDABLE_STEELWALL_ANY,
3951 static int ep_classic_enemy[] =
3968 static int ep_belt[] =
3970 EL_CONVEYOR_BELT_1_LEFT,
3971 EL_CONVEYOR_BELT_1_MIDDLE,
3972 EL_CONVEYOR_BELT_1_RIGHT,
3973 EL_CONVEYOR_BELT_2_LEFT,
3974 EL_CONVEYOR_BELT_2_MIDDLE,
3975 EL_CONVEYOR_BELT_2_RIGHT,
3976 EL_CONVEYOR_BELT_3_LEFT,
3977 EL_CONVEYOR_BELT_3_MIDDLE,
3978 EL_CONVEYOR_BELT_3_RIGHT,
3979 EL_CONVEYOR_BELT_4_LEFT,
3980 EL_CONVEYOR_BELT_4_MIDDLE,
3981 EL_CONVEYOR_BELT_4_RIGHT,
3986 static int ep_belt_active[] =
3988 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3989 EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE,
3990 EL_CONVEYOR_BELT_1_RIGHT_ACTIVE,
3991 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3992 EL_CONVEYOR_BELT_2_MIDDLE_ACTIVE,
3993 EL_CONVEYOR_BELT_2_RIGHT_ACTIVE,
3994 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3995 EL_CONVEYOR_BELT_3_MIDDLE_ACTIVE,
3996 EL_CONVEYOR_BELT_3_RIGHT_ACTIVE,
3997 EL_CONVEYOR_BELT_4_LEFT_ACTIVE,
3998 EL_CONVEYOR_BELT_4_MIDDLE_ACTIVE,
3999 EL_CONVEYOR_BELT_4_RIGHT_ACTIVE,
4004 static int ep_belt_switch[] =
4006 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4007 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4008 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4009 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4010 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4011 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4012 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4013 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4014 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4015 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4016 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4017 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4022 static int ep_tube[] =
4029 EL_TUBE_HORIZONTAL_UP,
4030 EL_TUBE_HORIZONTAL_DOWN,
4032 EL_TUBE_VERTICAL_LEFT,
4033 EL_TUBE_VERTICAL_RIGHT,
4039 static int ep_acid_pool[] =
4041 EL_ACID_POOL_TOPLEFT,
4042 EL_ACID_POOL_TOPRIGHT,
4043 EL_ACID_POOL_BOTTOMLEFT,
4044 EL_ACID_POOL_BOTTOM,
4045 EL_ACID_POOL_BOTTOMRIGHT,
4050 static int ep_keygate[] =
4060 EL_GATE_1_GRAY_ACTIVE,
4061 EL_GATE_2_GRAY_ACTIVE,
4062 EL_GATE_3_GRAY_ACTIVE,
4063 EL_GATE_4_GRAY_ACTIVE,
4072 EL_EM_GATE_1_GRAY_ACTIVE,
4073 EL_EM_GATE_2_GRAY_ACTIVE,
4074 EL_EM_GATE_3_GRAY_ACTIVE,
4075 EL_EM_GATE_4_GRAY_ACTIVE,
4084 EL_EMC_GATE_5_GRAY_ACTIVE,
4085 EL_EMC_GATE_6_GRAY_ACTIVE,
4086 EL_EMC_GATE_7_GRAY_ACTIVE,
4087 EL_EMC_GATE_8_GRAY_ACTIVE,
4089 EL_DC_GATE_WHITE_GRAY,
4090 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4095 static int ep_amoeboid[] =
4107 static int ep_amoebalive[] =
4118 static int ep_has_editor_content[] =
4124 EL_SOKOBAN_FIELD_PLAYER,
4141 static int ep_can_turn_each_move[] =
4143 // !!! do something with this one !!!
4147 static int ep_can_grow[] =
4161 static int ep_active_bomb[] =
4164 EL_EM_DYNAMITE_ACTIVE,
4165 EL_DYNABOMB_PLAYER_1_ACTIVE,
4166 EL_DYNABOMB_PLAYER_2_ACTIVE,
4167 EL_DYNABOMB_PLAYER_3_ACTIVE,
4168 EL_DYNABOMB_PLAYER_4_ACTIVE,
4169 EL_SP_DISK_RED_ACTIVE,
4174 static int ep_inactive[] =
4200 EL_QUICKSAND_FAST_EMPTY,
4223 EL_GATE_1_GRAY_ACTIVE,
4224 EL_GATE_2_GRAY_ACTIVE,
4225 EL_GATE_3_GRAY_ACTIVE,
4226 EL_GATE_4_GRAY_ACTIVE,
4235 EL_EM_GATE_1_GRAY_ACTIVE,
4236 EL_EM_GATE_2_GRAY_ACTIVE,
4237 EL_EM_GATE_3_GRAY_ACTIVE,
4238 EL_EM_GATE_4_GRAY_ACTIVE,
4247 EL_EMC_GATE_5_GRAY_ACTIVE,
4248 EL_EMC_GATE_6_GRAY_ACTIVE,
4249 EL_EMC_GATE_7_GRAY_ACTIVE,
4250 EL_EMC_GATE_8_GRAY_ACTIVE,
4252 EL_DC_GATE_WHITE_GRAY,
4253 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4254 EL_DC_GATE_FAKE_GRAY,
4257 EL_INVISIBLE_STEELWALL,
4265 EL_WALL_EMERALD_YELLOW,
4266 EL_DYNABOMB_INCREASE_NUMBER,
4267 EL_DYNABOMB_INCREASE_SIZE,
4268 EL_DYNABOMB_INCREASE_POWER,
4272 EL_SOKOBAN_FIELD_EMPTY,
4273 EL_SOKOBAN_FIELD_FULL,
4274 EL_WALL_EMERALD_RED,
4275 EL_WALL_EMERALD_PURPLE,
4276 EL_ACID_POOL_TOPLEFT,
4277 EL_ACID_POOL_TOPRIGHT,
4278 EL_ACID_POOL_BOTTOMLEFT,
4279 EL_ACID_POOL_BOTTOM,
4280 EL_ACID_POOL_BOTTOMRIGHT,
4284 EL_BD_MAGIC_WALL_DEAD,
4286 EL_DC_MAGIC_WALL_DEAD,
4287 EL_AMOEBA_TO_DIAMOND,
4295 EL_SP_GRAVITY_PORT_RIGHT,
4296 EL_SP_GRAVITY_PORT_DOWN,
4297 EL_SP_GRAVITY_PORT_LEFT,
4298 EL_SP_GRAVITY_PORT_UP,
4299 EL_SP_PORT_HORIZONTAL,
4300 EL_SP_PORT_VERTICAL,
4311 EL_SP_HARDWARE_GRAY,
4312 EL_SP_HARDWARE_GREEN,
4313 EL_SP_HARDWARE_BLUE,
4315 EL_SP_HARDWARE_YELLOW,
4316 EL_SP_HARDWARE_BASE_1,
4317 EL_SP_HARDWARE_BASE_2,
4318 EL_SP_HARDWARE_BASE_3,
4319 EL_SP_HARDWARE_BASE_4,
4320 EL_SP_HARDWARE_BASE_5,
4321 EL_SP_HARDWARE_BASE_6,
4322 EL_SP_GRAVITY_ON_PORT_LEFT,
4323 EL_SP_GRAVITY_ON_PORT_RIGHT,
4324 EL_SP_GRAVITY_ON_PORT_UP,
4325 EL_SP_GRAVITY_ON_PORT_DOWN,
4326 EL_SP_GRAVITY_OFF_PORT_LEFT,
4327 EL_SP_GRAVITY_OFF_PORT_RIGHT,
4328 EL_SP_GRAVITY_OFF_PORT_UP,
4329 EL_SP_GRAVITY_OFF_PORT_DOWN,
4330 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4331 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4332 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4333 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4334 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4335 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4336 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4337 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4338 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4339 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4340 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4341 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4342 EL_SIGN_EXCLAMATION,
4343 EL_SIGN_RADIOACTIVITY,
4350 EL_SIGN_ENTRY_FORBIDDEN,
4351 EL_SIGN_EMERGENCY_EXIT,
4359 EL_DC_STEELWALL_1_LEFT,
4360 EL_DC_STEELWALL_1_RIGHT,
4361 EL_DC_STEELWALL_1_TOP,
4362 EL_DC_STEELWALL_1_BOTTOM,
4363 EL_DC_STEELWALL_1_HORIZONTAL,
4364 EL_DC_STEELWALL_1_VERTICAL,
4365 EL_DC_STEELWALL_1_TOPLEFT,
4366 EL_DC_STEELWALL_1_TOPRIGHT,
4367 EL_DC_STEELWALL_1_BOTTOMLEFT,
4368 EL_DC_STEELWALL_1_BOTTOMRIGHT,
4369 EL_DC_STEELWALL_1_TOPLEFT_2,
4370 EL_DC_STEELWALL_1_TOPRIGHT_2,
4371 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
4372 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
4373 EL_DC_STEELWALL_2_LEFT,
4374 EL_DC_STEELWALL_2_RIGHT,
4375 EL_DC_STEELWALL_2_TOP,
4376 EL_DC_STEELWALL_2_BOTTOM,
4377 EL_DC_STEELWALL_2_HORIZONTAL,
4378 EL_DC_STEELWALL_2_VERTICAL,
4379 EL_DC_STEELWALL_2_MIDDLE,
4380 EL_DC_STEELWALL_2_SINGLE,
4381 EL_STEELWALL_SLIPPERY,
4386 EL_EMC_WALL_SLIPPERY_1,
4387 EL_EMC_WALL_SLIPPERY_2,
4388 EL_EMC_WALL_SLIPPERY_3,
4389 EL_EMC_WALL_SLIPPERY_4,
4410 static int ep_em_slippery_wall[] =
4415 static int ep_gfx_crumbled[] =
4426 static int ep_editor_cascade_active[] =
4428 EL_INTERNAL_CASCADE_BD_ACTIVE,
4429 EL_INTERNAL_CASCADE_EM_ACTIVE,
4430 EL_INTERNAL_CASCADE_EMC_ACTIVE,
4431 EL_INTERNAL_CASCADE_RND_ACTIVE,
4432 EL_INTERNAL_CASCADE_SB_ACTIVE,
4433 EL_INTERNAL_CASCADE_SP_ACTIVE,
4434 EL_INTERNAL_CASCADE_DC_ACTIVE,
4435 EL_INTERNAL_CASCADE_DX_ACTIVE,
4436 EL_INTERNAL_CASCADE_MM_ACTIVE,
4437 EL_INTERNAL_CASCADE_DF_ACTIVE,
4438 EL_INTERNAL_CASCADE_CHARS_ACTIVE,
4439 EL_INTERNAL_CASCADE_STEEL_CHARS_ACTIVE,
4440 EL_INTERNAL_CASCADE_CE_ACTIVE,
4441 EL_INTERNAL_CASCADE_GE_ACTIVE,
4442 EL_INTERNAL_CASCADE_ES_ACTIVE,
4443 EL_INTERNAL_CASCADE_REF_ACTIVE,
4444 EL_INTERNAL_CASCADE_USER_ACTIVE,
4445 EL_INTERNAL_CASCADE_DYNAMIC_ACTIVE,
4450 static int ep_editor_cascade_inactive[] =
4452 EL_INTERNAL_CASCADE_BD,
4453 EL_INTERNAL_CASCADE_EM,
4454 EL_INTERNAL_CASCADE_EMC,
4455 EL_INTERNAL_CASCADE_RND,
4456 EL_INTERNAL_CASCADE_SB,
4457 EL_INTERNAL_CASCADE_SP,
4458 EL_INTERNAL_CASCADE_DC,
4459 EL_INTERNAL_CASCADE_DX,
4460 EL_INTERNAL_CASCADE_MM,
4461 EL_INTERNAL_CASCADE_DF,
4462 EL_INTERNAL_CASCADE_CHARS,
4463 EL_INTERNAL_CASCADE_STEEL_CHARS,
4464 EL_INTERNAL_CASCADE_CE,
4465 EL_INTERNAL_CASCADE_GE,
4466 EL_INTERNAL_CASCADE_ES,
4467 EL_INTERNAL_CASCADE_REF,
4468 EL_INTERNAL_CASCADE_USER,
4469 EL_INTERNAL_CASCADE_DYNAMIC,
4474 static int ep_obsolete[] =
4478 EL_EM_KEY_1_FILE_OBSOLETE,
4479 EL_EM_KEY_2_FILE_OBSOLETE,
4480 EL_EM_KEY_3_FILE_OBSOLETE,
4481 EL_EM_KEY_4_FILE_OBSOLETE,
4482 EL_ENVELOPE_OBSOLETE,
4491 } element_properties[] =
4493 { ep_diggable, EP_DIGGABLE },
4494 { ep_collectible_only, EP_COLLECTIBLE_ONLY },
4495 { ep_dont_run_into, EP_DONT_RUN_INTO },
4496 { ep_dont_collide_with, EP_DONT_COLLIDE_WITH },
4497 { ep_dont_touch, EP_DONT_TOUCH },
4498 { ep_indestructible, EP_INDESTRUCTIBLE },
4499 { ep_slippery, EP_SLIPPERY },
4500 { ep_can_change, EP_CAN_CHANGE },
4501 { ep_can_move, EP_CAN_MOVE },
4502 { ep_can_fall, EP_CAN_FALL },
4503 { ep_can_smash_player, EP_CAN_SMASH_PLAYER },
4504 { ep_can_smash_enemies, EP_CAN_SMASH_ENEMIES },
4505 { ep_can_smash_everything, EP_CAN_SMASH_EVERYTHING },
4506 { ep_explodes_by_fire, EP_EXPLODES_BY_FIRE },
4507 { ep_explodes_smashed, EP_EXPLODES_SMASHED },
4508 { ep_explodes_impact, EP_EXPLODES_IMPACT },
4509 { ep_walkable_over, EP_WALKABLE_OVER },
4510 { ep_walkable_inside, EP_WALKABLE_INSIDE },
4511 { ep_walkable_under, EP_WALKABLE_UNDER },
4512 { ep_passable_over, EP_PASSABLE_OVER },
4513 { ep_passable_inside, EP_PASSABLE_INSIDE },
4514 { ep_passable_under, EP_PASSABLE_UNDER },
4515 { ep_droppable, EP_DROPPABLE },
4516 { ep_explodes_1x1_old, EP_EXPLODES_1X1_OLD },
4517 { ep_pushable, EP_PUSHABLE },
4518 { ep_explodes_cross_old, EP_EXPLODES_CROSS_OLD },
4519 { ep_protected, EP_PROTECTED },
4520 { ep_throwable, EP_THROWABLE },
4521 { ep_can_explode, EP_CAN_EXPLODE },
4522 { ep_gravity_reachable, EP_GRAVITY_REACHABLE },
4524 { ep_empty_space, EP_EMPTY_SPACE },
4525 { ep_player, EP_PLAYER },
4526 { ep_can_pass_magic_wall, EP_CAN_PASS_MAGIC_WALL },
4527 { ep_can_pass_dc_magic_wall, EP_CAN_PASS_DC_MAGIC_WALL },
4528 { ep_switchable, EP_SWITCHABLE },
4529 { ep_bd_element, EP_BD_ELEMENT },
4530 { ep_sp_element, EP_SP_ELEMENT },
4531 { ep_sb_element, EP_SB_ELEMENT },
4533 { ep_food_dark_yamyam, EP_FOOD_DARK_YAMYAM },
4534 { ep_food_penguin, EP_FOOD_PENGUIN },
4535 { ep_food_pig, EP_FOOD_PIG },
4536 { ep_historic_wall, EP_HISTORIC_WALL },
4537 { ep_historic_solid, EP_HISTORIC_SOLID },
4538 { ep_classic_enemy, EP_CLASSIC_ENEMY },
4539 { ep_belt, EP_BELT },
4540 { ep_belt_active, EP_BELT_ACTIVE },
4541 { ep_belt_switch, EP_BELT_SWITCH },
4542 { ep_tube, EP_TUBE },
4543 { ep_acid_pool, EP_ACID_POOL },
4544 { ep_keygate, EP_KEYGATE },
4545 { ep_amoeboid, EP_AMOEBOID },
4546 { ep_amoebalive, EP_AMOEBALIVE },
4547 { ep_has_editor_content, EP_HAS_EDITOR_CONTENT },
4548 { ep_can_turn_each_move, EP_CAN_TURN_EACH_MOVE },
4549 { ep_can_grow, EP_CAN_GROW },
4550 { ep_active_bomb, EP_ACTIVE_BOMB },
4551 { ep_inactive, EP_INACTIVE },
4553 { ep_em_slippery_wall, EP_EM_SLIPPERY_WALL },
4555 { ep_gfx_crumbled, EP_GFX_CRUMBLED },
4557 { ep_editor_cascade_active, EP_EDITOR_CASCADE_ACTIVE },
4558 { ep_editor_cascade_inactive, EP_EDITOR_CASCADE_INACTIVE },
4560 { ep_obsolete, EP_OBSOLETE },
4567 // always start with reliable default values (element has no properties)
4568 // (but never initialize clipboard elements after the very first time)
4569 // (to be able to use clipboard elements between several levels)
4570 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4571 if (!IS_CLIPBOARD_ELEMENT(i) || !clipboard_elements_initialized)
4572 for (j = 0; j < NUM_ELEMENT_PROPERTIES; j++)
4573 SET_PROPERTY(i, j, FALSE);
4575 // set all base element properties from above array definitions
4576 for (i = 0; element_properties[i].elements != NULL; i++)
4577 for (j = 0; (element_properties[i].elements)[j] != -1; j++)
4578 SET_PROPERTY((element_properties[i].elements)[j],
4579 element_properties[i].property, TRUE);
4581 // copy properties to some elements that are only stored in level file
4582 for (i = 0; i < NUM_ELEMENT_PROPERTIES; i++)
4583 for (j = 0; copy_properties[j][0] != -1; j++)
4584 if (HAS_PROPERTY(copy_properties[j][0], i))
4585 for (k = 1; k <= 4; k++)
4586 SET_PROPERTY(copy_properties[j][k], i, TRUE);
4588 // set static element properties that are not listed in array definitions
4589 for (i = EL_STEEL_CHAR_START; i <= EL_STEEL_CHAR_END; i++)
4590 SET_PROPERTY(i, EP_INDESTRUCTIBLE, TRUE);
4592 clipboard_elements_initialized = TRUE;
4595 void InitElementPropertiesEngine(int engine_version)
4597 static int no_wall_properties[] =
4600 EP_COLLECTIBLE_ONLY,
4602 EP_DONT_COLLIDE_WITH,
4605 EP_CAN_SMASH_PLAYER,
4606 EP_CAN_SMASH_ENEMIES,
4607 EP_CAN_SMASH_EVERYTHING,
4612 EP_FOOD_DARK_YAMYAM,
4628 /* important: after initialization in InitElementPropertiesStatic(), the
4629 elements are not again initialized to a default value; therefore all
4630 changes have to make sure that they leave the element with a defined
4631 property (which means that conditional property changes must be set to
4632 a reliable default value before) */
4634 // resolve group elements
4635 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
4636 ResolveGroupElement(EL_GROUP_START + i);
4638 // set all special, combined or engine dependent element properties
4639 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4641 // do not change (already initialized) clipboard elements here
4642 if (IS_CLIPBOARD_ELEMENT(i))
4645 // ---------- INACTIVE ----------------------------------------------------
4646 SET_PROPERTY(i, EP_INACTIVE, ((i >= EL_CHAR_START &&
4647 i <= EL_CHAR_END) ||
4648 (i >= EL_STEEL_CHAR_START &&
4649 i <= EL_STEEL_CHAR_END)));
4651 // ---------- WALKABLE, PASSABLE, ACCESSIBLE ------------------------------
4652 SET_PROPERTY(i, EP_WALKABLE, (IS_WALKABLE_OVER(i) ||
4653 IS_WALKABLE_INSIDE(i) ||
4654 IS_WALKABLE_UNDER(i)));
4656 SET_PROPERTY(i, EP_PASSABLE, (IS_PASSABLE_OVER(i) ||
4657 IS_PASSABLE_INSIDE(i) ||
4658 IS_PASSABLE_UNDER(i)));
4660 SET_PROPERTY(i, EP_ACCESSIBLE_OVER, (IS_WALKABLE_OVER(i) ||
4661 IS_PASSABLE_OVER(i)));
4663 SET_PROPERTY(i, EP_ACCESSIBLE_INSIDE, (IS_WALKABLE_INSIDE(i) ||
4664 IS_PASSABLE_INSIDE(i)));
4666 SET_PROPERTY(i, EP_ACCESSIBLE_UNDER, (IS_WALKABLE_UNDER(i) ||
4667 IS_PASSABLE_UNDER(i)));
4669 SET_PROPERTY(i, EP_ACCESSIBLE, (IS_WALKABLE(i) ||
4672 // ---------- COLLECTIBLE -------------------------------------------------
4673 SET_PROPERTY(i, EP_COLLECTIBLE, (IS_COLLECTIBLE_ONLY(i) ||
4677 // ---------- SNAPPABLE ---------------------------------------------------
4678 SET_PROPERTY(i, EP_SNAPPABLE, (IS_DIGGABLE(i) ||
4679 IS_COLLECTIBLE(i) ||
4683 // ---------- WALL --------------------------------------------------------
4684 SET_PROPERTY(i, EP_WALL, TRUE); // default: element is wall
4686 for (j = 0; no_wall_properties[j] != -1; j++)
4687 if (HAS_PROPERTY(i, no_wall_properties[j]) ||
4688 i >= EL_FIRST_RUNTIME_UNREAL)
4689 SET_PROPERTY(i, EP_WALL, FALSE);
4691 if (IS_HISTORIC_WALL(i))
4692 SET_PROPERTY(i, EP_WALL, TRUE);
4694 // ---------- SOLID_FOR_PUSHING -------------------------------------------
4695 if (engine_version < VERSION_IDENT(2,2,0,0))
4696 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, IS_HISTORIC_SOLID(i));
4698 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, (!IS_WALKABLE(i) &&
4700 !IS_COLLECTIBLE(i)));
4702 // ---------- DRAGONFIRE_PROOF --------------------------------------------
4703 if (IS_HISTORIC_SOLID(i) || i == EL_EXPLOSION)
4704 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, TRUE);
4706 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, (IS_INDESTRUCTIBLE(i) &&
4709 // ---------- EXPLOSION_PROOF ---------------------------------------------
4711 SET_PROPERTY(i, EP_EXPLOSION_PROOF, TRUE);
4712 else if (engine_version < VERSION_IDENT(2,2,0,0))
4713 SET_PROPERTY(i, EP_EXPLOSION_PROOF, IS_INDESTRUCTIBLE(i));
4715 SET_PROPERTY(i, EP_EXPLOSION_PROOF, (IS_INDESTRUCTIBLE(i) &&
4719 if (IS_CUSTOM_ELEMENT(i))
4721 // these are additional properties which are initially false when set
4723 // ---------- DONT_COLLIDE_WITH / DONT_RUN_INTO -------------------------
4725 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH, TRUE);
4726 if (DONT_COLLIDE_WITH(i))
4727 SET_PROPERTY(i, EP_DONT_RUN_INTO, TRUE);
4729 // ---------- CAN_SMASH_ENEMIES / CAN_SMASH_PLAYER ----------------------
4730 if (CAN_SMASH_EVERYTHING(i))
4731 SET_PROPERTY(i, EP_CAN_SMASH_ENEMIES, TRUE);
4732 if (CAN_SMASH_ENEMIES(i))
4733 SET_PROPERTY(i, EP_CAN_SMASH_PLAYER, TRUE);
4736 // ---------- CAN_SMASH ---------------------------------------------------
4737 SET_PROPERTY(i, EP_CAN_SMASH, (CAN_SMASH_PLAYER(i) ||
4738 CAN_SMASH_ENEMIES(i) ||
4739 CAN_SMASH_EVERYTHING(i)));
4741 // ---------- CAN_EXPLODE_BY_FIRE -----------------------------------------
4742 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_FIRE, (CAN_EXPLODE(i) &&
4743 EXPLODES_BY_FIRE(i)));
4745 // ---------- CAN_EXPLODE_SMASHED -----------------------------------------
4746 SET_PROPERTY(i, EP_CAN_EXPLODE_SMASHED, (CAN_EXPLODE(i) &&
4747 EXPLODES_SMASHED(i)));
4749 // ---------- CAN_EXPLODE_IMPACT ------------------------------------------
4750 SET_PROPERTY(i, EP_CAN_EXPLODE_IMPACT, (CAN_EXPLODE(i) &&
4751 EXPLODES_IMPACT(i)));
4753 // ---------- CAN_EXPLODE_BY_DRAGONFIRE -----------------------------------
4754 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_DRAGONFIRE, CAN_EXPLODE_BY_FIRE(i));
4756 // ---------- CAN_EXPLODE_BY_EXPLOSION ------------------------------------
4757 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_EXPLOSION, (CAN_EXPLODE_BY_FIRE(i) ||
4758 i == EL_BLACK_ORB));
4760 // ---------- COULD_MOVE_INTO_ACID ----------------------------------------
4761 SET_PROPERTY(i, EP_COULD_MOVE_INTO_ACID, (IS_PLAYER_ELEMENT(i) ||
4763 IS_CUSTOM_ELEMENT(i)));
4765 // ---------- MAYBE_DONT_COLLIDE_WITH -------------------------------------
4766 SET_PROPERTY(i, EP_MAYBE_DONT_COLLIDE_WITH, (i == EL_SP_SNIKSNAK ||
4767 i == EL_SP_ELECTRON));
4769 // ---------- CAN_MOVE_INTO_ACID ------------------------------------------
4770 if (COULD_MOVE_INTO_ACID(i) && !IS_CUSTOM_ELEMENT(i))
4771 SET_PROPERTY(i, EP_CAN_MOVE_INTO_ACID,
4772 getMoveIntoAcidProperty(&level, i));
4774 // ---------- DONT_COLLIDE_WITH -------------------------------------------
4775 if (MAYBE_DONT_COLLIDE_WITH(i))
4776 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH,
4777 getDontCollideWithProperty(&level, i));
4779 // ---------- SP_PORT -----------------------------------------------------
4780 SET_PROPERTY(i, EP_SP_PORT, (IS_SP_ELEMENT(i) &&
4781 IS_PASSABLE_INSIDE(i)));
4783 // ---------- CAN_BE_CLONED_BY_ANDROID ------------------------------------
4784 for (j = 0; j < level.num_android_clone_elements; j++)
4785 SET_PROPERTY(i, EP_CAN_BE_CLONED_BY_ANDROID,
4787 IS_EQUAL_OR_IN_GROUP(i, level.android_clone_element[j])));
4789 // ---------- CAN_CHANGE --------------------------------------------------
4790 SET_PROPERTY(i, EP_CAN_CHANGE, FALSE); // default: cannot change
4791 for (j = 0; j < element_info[i].num_change_pages; j++)
4792 if (element_info[i].change_page[j].can_change)
4793 SET_PROPERTY(i, EP_CAN_CHANGE, TRUE);
4795 // ---------- HAS_ACTION --------------------------------------------------
4796 SET_PROPERTY(i, EP_HAS_ACTION, FALSE); // default: has no action
4797 for (j = 0; j < element_info[i].num_change_pages; j++)
4798 if (element_info[i].change_page[j].has_action)
4799 SET_PROPERTY(i, EP_HAS_ACTION, TRUE);
4801 // ---------- CAN_CHANGE_OR_HAS_ACTION ------------------------------------
4802 SET_PROPERTY(i, EP_CAN_CHANGE_OR_HAS_ACTION, (CAN_CHANGE(i) ||
4805 // ---------- GFX_CRUMBLED ------------------------------------------------
4806 SET_PROPERTY(i, EP_GFX_CRUMBLED,
4807 element_info[i].crumbled[ACTION_DEFAULT] !=
4808 element_info[i].graphic[ACTION_DEFAULT]);
4810 // ---------- EDITOR_CASCADE ----------------------------------------------
4811 SET_PROPERTY(i, EP_EDITOR_CASCADE, (IS_EDITOR_CASCADE_ACTIVE(i) ||
4812 IS_EDITOR_CASCADE_INACTIVE(i)));
4815 // dynamically adjust element properties according to game engine version
4817 static int ep_em_slippery_wall[] =
4822 EL_EXPANDABLE_WALL_HORIZONTAL,
4823 EL_EXPANDABLE_WALL_VERTICAL,
4824 EL_EXPANDABLE_WALL_ANY,
4825 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
4826 EL_EXPANDABLE_STEELWALL_VERTICAL,
4827 EL_EXPANDABLE_STEELWALL_ANY,
4828 EL_EXPANDABLE_STEELWALL_GROWING,
4832 static int ep_em_explodes_by_fire[] =
4835 EL_EM_DYNAMITE_ACTIVE,
4840 // special EM style gems behaviour
4841 for (i = 0; ep_em_slippery_wall[i] != -1; i++)
4842 SET_PROPERTY(ep_em_slippery_wall[i], EP_EM_SLIPPERY_WALL,
4843 level.em_slippery_gems);
4845 // "EL_EXPANDABLE_WALL_GROWING" wasn't slippery for EM gems in 2.0.1
4846 SET_PROPERTY(EL_EXPANDABLE_WALL_GROWING, EP_EM_SLIPPERY_WALL,
4847 (level.em_slippery_gems &&
4848 engine_version > VERSION_IDENT(2,0,1,0)));
4850 // special EM style explosion behaviour regarding chain reactions
4851 for (i = 0; ep_em_explodes_by_fire[i] != -1; i++)
4852 SET_PROPERTY(ep_em_explodes_by_fire[i], EP_EXPLODES_BY_FIRE,
4853 level.em_explodes_by_fire);
4856 // this is needed because some graphics depend on element properties
4857 if (game_status == GAME_MODE_PLAYING)
4858 InitElementGraphicInfo();
4861 void InitElementPropertiesGfxElement(void)
4865 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4867 struct ElementInfo *ei = &element_info[i];
4869 ei->gfx_element = (ei->use_gfx_element ? ei->gfx_element_initial : i);
4873 static void InitGlobal(void)
4878 for (i = 0; i < MAX_NUM_ELEMENTS + 1; i++)
4880 // check if element_name_info entry defined for each element in "main.h"
4881 if (i < MAX_NUM_ELEMENTS && element_name_info[i].token_name == NULL)
4882 Fail("undefined 'element_name_info' entry for element %d", i);
4884 element_info[i].token_name = element_name_info[i].token_name;
4885 element_info[i].class_name = element_name_info[i].class_name;
4886 element_info[i].editor_description= element_name_info[i].editor_description;
4889 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS + 1; i++)
4891 // check if global_anim_name_info defined for each entry in "main.h"
4892 if (i < NUM_GLOBAL_ANIM_TOKENS &&
4893 global_anim_name_info[i].token_name == NULL)
4894 Fail("undefined 'global_anim_name_info' entry for anim %d", i);
4896 global_anim_info[i].token_name = global_anim_name_info[i].token_name;
4899 // create hash from image config list
4900 image_config_hash = newSetupFileHash();
4901 for (i = 0; image_config[i].token != NULL; i++)
4902 setHashEntry(image_config_hash,
4903 image_config[i].token,
4904 image_config[i].value);
4906 // create hash from element token list
4907 element_token_hash = newSetupFileHash();
4908 for (i = 0; element_name_info[i].token_name != NULL; i++)
4909 setHashEntry(element_token_hash,
4910 element_name_info[i].token_name,
4913 // create hash from graphic token list
4914 graphic_token_hash = newSetupFileHash();
4915 for (graphic = 0, i = 0; image_config[i].token != NULL; i++)
4916 if (strSuffix(image_config[i].value, ".png") ||
4917 strSuffix(image_config[i].value, ".pcx") ||
4918 strSuffix(image_config[i].value, ".wav") ||
4919 strEqual(image_config[i].value, UNDEFINED_FILENAME))
4920 setHashEntry(graphic_token_hash,
4921 image_config[i].token,
4922 int2str(graphic++, 0));
4924 // create hash from font token list
4925 font_token_hash = newSetupFileHash();
4926 for (i = 0; font_info[i].token_name != NULL; i++)
4927 setHashEntry(font_token_hash,
4928 font_info[i].token_name,
4931 // set default filenames for all cloned graphics in static configuration
4932 for (i = 0; image_config[i].token != NULL; i++)
4934 if (strEqual(image_config[i].value, UNDEFINED_FILENAME))
4936 char *token = image_config[i].token;
4937 char *token_clone_from = getStringCat2(token, ".clone_from");
4938 char *token_cloned = getHashEntry(image_config_hash, token_clone_from);
4940 if (token_cloned != NULL)
4942 char *value_cloned = getHashEntry(image_config_hash, token_cloned);
4944 if (value_cloned != NULL)
4946 // set default filename in static configuration
4947 image_config[i].value = value_cloned;
4949 // set default filename in image config hash
4950 setHashEntry(image_config_hash, token, value_cloned);
4954 free(token_clone_from);
4958 // always start with reliable default values (all elements)
4959 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4960 ActiveElement[i] = i;
4962 // now add all entries that have an active state (active elements)
4963 for (i = 0; element_with_active_state[i].element != -1; i++)
4965 int element = element_with_active_state[i].element;
4966 int element_active = element_with_active_state[i].element_active;
4968 ActiveElement[element] = element_active;
4971 // always start with reliable default values (all buttons)
4972 for (i = 0; i < NUM_IMAGE_FILES; i++)
4973 ActiveButton[i] = i;
4975 // now add all entries that have an active state (active buttons)
4976 for (i = 0; button_with_active_state[i].button != -1; i++)
4978 int button = button_with_active_state[i].button;
4979 int button_active = button_with_active_state[i].button_active;
4981 ActiveButton[button] = button_active;
4984 // always start with reliable default values (all fonts)
4985 for (i = 0; i < NUM_FONTS; i++)
4988 // now add all entries that have an active state (active fonts)
4989 for (i = 0; font_with_active_state[i].font_nr != -1; i++)
4991 int font = font_with_active_state[i].font_nr;
4992 int font_active = font_with_active_state[i].font_nr_active;
4994 ActiveFont[font] = font_active;
4997 global.autoplay_leveldir = NULL;
4998 global.patchtapes_leveldir = NULL;
4999 global.convert_leveldir = NULL;
5000 global.dumplevel_leveldir = NULL;
5001 global.dumptape_leveldir = NULL;
5002 global.create_sketch_images_dir = NULL;
5003 global.create_collect_images_dir = NULL;
5005 global.frames_per_second = 0;
5006 global.show_frames_per_second = FALSE;
5008 global.border_status = GAME_MODE_LOADING;
5009 global.anim_status = global.anim_status_next = GAME_MODE_LOADING;
5011 global.use_envelope_request = FALSE;
5013 global.user_names = NULL;
5016 static void Execute_Command(char *command)
5020 if (strEqual(command, "print graphicsinfo.conf"))
5022 Print("# You can configure additional/alternative image files here.\n");
5023 Print("# (The entries below are default and therefore commented out.)\n");
5025 Print("%s\n", getFormattedSetupEntry("name", "Classic Graphics"));
5027 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5030 for (i = 0; image_config[i].token != NULL; i++)
5031 Print("# %s\n", getFormattedSetupEntry(image_config[i].token,
5032 image_config[i].value));
5036 else if (strEqual(command, "print soundsinfo.conf"))
5038 Print("# You can configure additional/alternative sound files here.\n");
5039 Print("# (The entries below are default and therefore commented out.)\n");
5041 Print("%s\n", getFormattedSetupEntry("name", "Classic Sounds"));
5043 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5046 for (i = 0; sound_config[i].token != NULL; i++)
5047 Print("# %s\n", getFormattedSetupEntry(sound_config[i].token,
5048 sound_config[i].value));
5052 else if (strEqual(command, "print musicinfo.conf"))
5054 Print("# You can configure additional/alternative music files here.\n");
5055 Print("# (The entries below are default and therefore commented out.)\n");
5057 Print("%s\n", getFormattedSetupEntry("name", "Classic Music"));
5059 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5062 for (i = 0; music_config[i].token != NULL; i++)
5063 Print("# %s\n", getFormattedSetupEntry(music_config[i].token,
5064 music_config[i].value));
5068 else if (strEqual(command, "print editorsetup.conf"))
5070 Print("# You can configure your personal editor element list here.\n");
5071 Print("# (The entries below are default and therefore commented out.)\n");
5074 // this is needed to be able to check element list for cascade elements
5075 InitElementPropertiesStatic();
5076 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
5078 PrintEditorElementList();
5082 else if (strEqual(command, "print helpanim.conf"))
5084 Print("# You can configure different element help animations here.\n");
5085 Print("# (The entries below are default and therefore commented out.)\n");
5088 for (i = 0; helpanim_config[i].token != NULL; i++)
5090 Print("# %s\n", getFormattedSetupEntry(helpanim_config[i].token,
5091 helpanim_config[i].value));
5093 if (strEqual(helpanim_config[i].token, "end"))
5099 else if (strEqual(command, "print helptext.conf"))
5101 Print("# You can configure different element help text here.\n");
5102 Print("# (The entries below are default and therefore commented out.)\n");
5105 for (i = 0; helptext_config[i].token != NULL; i++)
5106 Print("# %s\n", getFormattedSetupEntry(helptext_config[i].token,
5107 helptext_config[i].value));
5111 else if (strPrefix(command, "dump level "))
5113 char *filename = &command[11];
5115 if (fileExists(filename))
5117 LoadLevelFromFilename(&level, filename);
5123 char *leveldir = getStringCopy(filename); // read command parameters
5124 char *level_nr = strchr(leveldir, ' ');
5126 if (level_nr == NULL)
5127 Fail("cannot open file '%s'", filename);
5131 global.dumplevel_leveldir = leveldir;
5132 global.dumplevel_level_nr = atoi(level_nr);
5134 program.headless = TRUE;
5136 else if (strPrefix(command, "dump tape "))
5138 char *filename = &command[10];
5140 if (fileExists(filename))
5142 LoadTapeFromFilename(filename);
5148 char *leveldir = getStringCopy(filename); // read command parameters
5149 char *level_nr = strchr(leveldir, ' ');
5151 if (level_nr == NULL)
5152 Fail("cannot open file '%s'", filename);
5156 global.dumptape_leveldir = leveldir;
5157 global.dumptape_level_nr = atoi(level_nr);
5159 program.headless = TRUE;
5161 else if (strPrefix(command, "autoplay ") ||
5162 strPrefix(command, "autoffwd ") ||
5163 strPrefix(command, "autowarp ") ||
5164 strPrefix(command, "autotest ") ||
5165 strPrefix(command, "autosave ") ||
5166 strPrefix(command, "autoupload ") ||
5167 strPrefix(command, "autofix "))
5169 char *arg_ptr = strchr(command, ' ');
5170 char *str_ptr = getStringCopy(arg_ptr); // read command parameters
5172 global.autoplay_mode =
5173 (strPrefix(command, "autoplay") ? AUTOPLAY_MODE_PLAY :
5174 strPrefix(command, "autoffwd") ? AUTOPLAY_MODE_FFWD :
5175 strPrefix(command, "autowarp") ? AUTOPLAY_MODE_WARP :
5176 strPrefix(command, "autotest") ? AUTOPLAY_MODE_TEST :
5177 strPrefix(command, "autosave") ? AUTOPLAY_MODE_SAVE :
5178 strPrefix(command, "autoupload") ? AUTOPLAY_MODE_UPLOAD :
5179 strPrefix(command, "autofix") ? AUTOPLAY_MODE_FIX :
5180 AUTOPLAY_MODE_NONE);
5182 while (*str_ptr != '\0') // continue parsing string
5184 // cut leading whitespace from string, replace it by string terminator
5185 while (*str_ptr == ' ' || *str_ptr == '\t')
5188 if (*str_ptr == '\0') // end of string reached
5191 if (global.autoplay_leveldir == NULL) // read level set string
5193 global.autoplay_leveldir = str_ptr;
5194 global.autoplay_all = TRUE; // default: play all tapes
5196 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5197 global.autoplay_level[i] = FALSE;
5199 else // read level number string
5201 int level_nr = atoi(str_ptr); // get level_nr value
5203 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5204 global.autoplay_level[level_nr] = TRUE;
5206 global.autoplay_all = FALSE;
5209 // advance string pointer to the next whitespace (or end of string)
5210 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5214 if (global.autoplay_mode & AUTOPLAY_WARP_NO_DISPLAY)
5215 program.headless = TRUE;
5217 else if (strPrefix(command, "patch tapes "))
5219 char *str_ptr = getStringCopy(&command[12]); // read command parameters
5221 // skip leading whitespace
5222 while (*str_ptr == ' ' || *str_ptr == '\t')
5225 if (*str_ptr == '\0')
5226 Fail("cannot find MODE in command '%s'", command);
5228 global.patchtapes_mode = str_ptr; // store patch mode
5230 // advance to next whitespace (or end of string)
5231 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5234 while (*str_ptr != '\0') // continue parsing string
5236 // cut leading whitespace from string, replace it by string terminator
5237 while (*str_ptr == ' ' || *str_ptr == '\t')
5240 if (*str_ptr == '\0') // end of string reached
5243 if (global.patchtapes_leveldir == NULL) // read level set string
5245 global.patchtapes_leveldir = str_ptr;
5246 global.patchtapes_all = TRUE; // default: patch all tapes
5248 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5249 global.patchtapes_level[i] = FALSE;
5251 else // read level number string
5253 int level_nr = atoi(str_ptr); // get level_nr value
5255 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5256 global.patchtapes_level[level_nr] = TRUE;
5258 global.patchtapes_all = FALSE;
5261 // advance string pointer to the next whitespace (or end of string)
5262 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5266 if (global.patchtapes_leveldir == NULL)
5268 if (strEqual(global.patchtapes_mode, "help"))
5269 global.patchtapes_leveldir = UNDEFINED_LEVELSET;
5271 Fail("cannot find LEVELDIR in command '%s'", command);
5274 program.headless = TRUE;
5276 else if (strPrefix(command, "convert "))
5278 char *str_copy = getStringCopy(strchr(command, ' ') + 1);
5279 char *str_ptr = strchr(str_copy, ' ');
5281 global.convert_leveldir = str_copy;
5282 global.convert_level_nr = -1;
5284 if (str_ptr != NULL) // level number follows
5286 *str_ptr++ = '\0'; // terminate leveldir string
5287 global.convert_level_nr = atoi(str_ptr); // get level_nr value
5290 program.headless = TRUE;
5292 else if (strPrefix(command, "create sketch images "))
5294 global.create_sketch_images_dir = getStringCopy(&command[21]);
5296 if (access(global.create_sketch_images_dir, W_OK) != 0)
5297 Fail("image target directory '%s' not found or not writable",
5298 global.create_sketch_images_dir);
5300 else if (strPrefix(command, "create collect image "))
5302 global.create_collect_images_dir = getStringCopy(&command[21]);
5304 if (access(global.create_collect_images_dir, W_OK) != 0)
5305 Fail("image target directory '%s' not found or not writable",
5306 global.create_collect_images_dir);
5308 else if (strPrefix(command, "create CE image "))
5310 CreateCustomElementImages(&command[16]);
5316 FailWithHelp("unrecognized command '%s'", command);
5319 // disable networking if any valid command was recognized
5320 options.network = setup.network_mode = FALSE;
5323 static void InitSetup(void)
5325 LoadUserNames(); // global user names
5326 LoadUserSetup(); // global user number
5328 LoadSetup(); // global setup info
5330 // set some options from setup file
5332 if (setup.options.verbose)
5333 options.verbose = TRUE;
5335 if (setup.debug.show_frames_per_second)
5336 global.show_frames_per_second = TRUE;
5339 static void InitGameInfo(void)
5341 game.restart_level = FALSE;
5342 game.restart_game_message = NULL;
5344 game.request_active = FALSE;
5345 game.request_active_or_moving = FALSE;
5347 game.use_masked_elements_initial = FALSE;
5350 static void InitPlayerInfo(void)
5354 // choose default local player
5355 local_player = &stored_player[0];
5357 for (i = 0; i < MAX_PLAYERS; i++)
5359 stored_player[i].connected_locally = FALSE;
5360 stored_player[i].connected_network = FALSE;
5363 local_player->connected_locally = TRUE;
5366 static void InitArtworkInfo(void)
5371 static char *get_string_in_brackets(char *string)
5373 char *string_in_brackets = checked_malloc(strlen(string) + 3);
5375 sprintf(string_in_brackets, "[%s]", string);
5377 return string_in_brackets;
5380 static char *get_level_id_suffix(int id_nr)
5382 char *id_suffix = checked_malloc(1 + 3 + 1);
5384 if (id_nr < 0 || id_nr > 999)
5387 sprintf(id_suffix, ".%03d", id_nr);
5392 static void InitArtworkConfig(void)
5394 static char *image_id_prefix[MAX_NUM_ELEMENTS +
5396 NUM_GLOBAL_ANIM_TOKENS + 1];
5397 static char *sound_id_prefix[2 * MAX_NUM_ELEMENTS +
5398 NUM_GLOBAL_ANIM_TOKENS + 1];
5399 static char *music_id_prefix[NUM_MUSIC_PREFIXES +
5400 NUM_GLOBAL_ANIM_TOKENS + 1];
5401 static char *action_id_suffix[NUM_ACTIONS + 1];
5402 static char *direction_id_suffix[NUM_DIRECTIONS_FULL + 1];
5403 static char *special_id_suffix[NUM_SPECIAL_GFX_ARGS + 1];
5404 static char *level_id_suffix[MAX_LEVELS + 1];
5405 static char *dummy[1] = { NULL };
5406 static char *ignore_generic_tokens[] =
5411 "program_copyright",
5416 static char **ignore_image_tokens;
5417 static char **ignore_sound_tokens;
5418 static char **ignore_music_tokens;
5419 int num_ignore_generic_tokens;
5420 int num_ignore_image_tokens;
5421 int num_ignore_sound_tokens;
5422 int num_ignore_music_tokens;
5425 // dynamically determine list of generic tokens to be ignored
5426 num_ignore_generic_tokens = 0;
5427 for (i = 0; ignore_generic_tokens[i] != NULL; i++)
5428 num_ignore_generic_tokens++;
5430 // dynamically determine list of image tokens to be ignored
5431 num_ignore_image_tokens = num_ignore_generic_tokens;
5432 for (i = 0; image_config_vars[i].token != NULL; i++)
5433 num_ignore_image_tokens++;
5434 ignore_image_tokens =
5435 checked_malloc((num_ignore_image_tokens + 1) * sizeof(char *));
5436 for (i = 0; i < num_ignore_generic_tokens; i++)
5437 ignore_image_tokens[i] = ignore_generic_tokens[i];
5438 for (i = 0; i < num_ignore_image_tokens - num_ignore_generic_tokens; i++)
5439 ignore_image_tokens[num_ignore_generic_tokens + i] =
5440 image_config_vars[i].token;
5441 ignore_image_tokens[num_ignore_image_tokens] = NULL;
5443 // dynamically determine list of sound tokens to be ignored
5444 num_ignore_sound_tokens = num_ignore_generic_tokens;
5445 ignore_sound_tokens =
5446 checked_malloc((num_ignore_sound_tokens + 1) * sizeof(char *));
5447 for (i = 0; i < num_ignore_generic_tokens; i++)
5448 ignore_sound_tokens[i] = ignore_generic_tokens[i];
5449 ignore_sound_tokens[num_ignore_sound_tokens] = NULL;
5451 // dynamically determine list of music tokens to be ignored
5452 num_ignore_music_tokens = num_ignore_generic_tokens;
5453 ignore_music_tokens =
5454 checked_malloc((num_ignore_music_tokens + 1) * sizeof(char *));
5455 for (i = 0; i < num_ignore_generic_tokens; i++)
5456 ignore_music_tokens[i] = ignore_generic_tokens[i];
5457 ignore_music_tokens[num_ignore_music_tokens] = NULL;
5459 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5460 image_id_prefix[i] = element_info[i].token_name;
5461 for (i = 0; i < NUM_FONTS; i++)
5462 image_id_prefix[MAX_NUM_ELEMENTS + i] = font_info[i].token_name;
5463 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5464 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + i] =
5465 global_anim_info[i].token_name;
5466 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5468 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5469 sound_id_prefix[i] = element_info[i].token_name;
5470 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5471 sound_id_prefix[MAX_NUM_ELEMENTS + i] =
5472 get_string_in_brackets(element_info[i].class_name);
5473 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5474 sound_id_prefix[2 * MAX_NUM_ELEMENTS + i] =
5475 global_anim_info[i].token_name;
5476 sound_id_prefix[2 * MAX_NUM_ELEMENTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5478 for (i = 0; i < NUM_MUSIC_PREFIXES; i++)
5479 music_id_prefix[i] = music_prefix_info[i].prefix;
5480 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5481 music_id_prefix[NUM_MUSIC_PREFIXES + i] =
5482 global_anim_info[i].token_name;
5483 music_id_prefix[NUM_MUSIC_PREFIXES + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5485 for (i = 0; i < NUM_ACTIONS; i++)
5486 action_id_suffix[i] = element_action_info[i].suffix;
5487 action_id_suffix[NUM_ACTIONS] = NULL;
5489 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
5490 direction_id_suffix[i] = element_direction_info[i].suffix;
5491 direction_id_suffix[NUM_DIRECTIONS_FULL] = NULL;
5493 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
5494 special_id_suffix[i] = special_suffix_info[i].suffix;
5495 special_id_suffix[NUM_SPECIAL_GFX_ARGS] = NULL;
5497 for (i = 0; i < MAX_LEVELS; i++)
5498 level_id_suffix[i] = get_level_id_suffix(i);
5499 level_id_suffix[MAX_LEVELS] = NULL;
5501 InitImageList(image_config, NUM_IMAGE_FILES, image_config_suffix,
5502 image_id_prefix, action_id_suffix, direction_id_suffix,
5503 special_id_suffix, ignore_image_tokens);
5504 InitSoundList(sound_config, NUM_SOUND_FILES, sound_config_suffix,
5505 sound_id_prefix, action_id_suffix, dummy,
5506 special_id_suffix, ignore_sound_tokens);
5507 InitMusicList(music_config, NUM_MUSIC_FILES, music_config_suffix,
5508 music_id_prefix, action_id_suffix, special_id_suffix,
5509 level_id_suffix, ignore_music_tokens);
5512 static void InitMixer(void)
5519 static void InitVideoOverlay(void)
5521 // if virtual buttons are not loaded from setup file, repeat initializing
5522 // virtual buttons grid with default values now that video is initialized
5523 if (!setup.touch.grid_initialized)
5526 InitTileCursorInfo();
5530 void InitGfxBuffers(void)
5532 static int win_xsize_last = -1;
5533 static int win_ysize_last = -1;
5535 // create additional image buffers for double-buffering and cross-fading
5537 if (WIN_XSIZE != win_xsize_last || WIN_YSIZE != win_ysize_last)
5539 // used to temporarily store the backbuffer -- only re-create if changed
5540 ReCreateBitmap(&bitmap_db_store_1, WIN_XSIZE, WIN_YSIZE);
5541 ReCreateBitmap(&bitmap_db_store_2, WIN_XSIZE, WIN_YSIZE);
5543 win_xsize_last = WIN_XSIZE;
5544 win_ysize_last = WIN_YSIZE;
5547 ReCreateBitmap(&bitmap_db_field, FXSIZE, FYSIZE);
5548 ReCreateBitmap(&bitmap_db_panel, DXSIZE, DYSIZE);
5549 ReCreateBitmap(&bitmap_db_door_1, 3 * DXSIZE, DYSIZE);
5550 ReCreateBitmap(&bitmap_db_door_2, 3 * VXSIZE, VYSIZE);
5552 // initialize screen properties
5553 InitGfxFieldInfo(SX, SY, SXSIZE, SYSIZE,
5554 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
5556 InitGfxDoor1Info(DX, DY, DXSIZE, DYSIZE);
5557 InitGfxDoor2Info(VX, VY, VXSIZE, VYSIZE);
5558 InitGfxDoor3Info(EX, EY, EXSIZE, EYSIZE);
5559 InitGfxWindowInfo(WIN_XSIZE, WIN_YSIZE);
5560 InitGfxScrollbufferInfo(FXSIZE, FYSIZE);
5561 InitGfxClipRegion(FALSE, -1, -1, -1, -1);
5563 // required if door size definitions have changed
5564 InitGraphicCompatibilityInfo_Doors();
5566 InitGfxBuffers_EM();
5567 InitGfxBuffers_SP();
5570 static void InitGfx(void)
5572 struct GraphicInfo *graphic_info_last = graphic_info;
5573 char *filename_font_initial = NULL;
5574 char *filename_image_initial[NUM_INITIAL_IMAGES] = { NULL };
5575 char *image_token[NUM_INITIAL_IMAGES] =
5577 CONFIG_TOKEN_GLOBAL_BUSY,
5578 CONFIG_TOKEN_BACKGROUND_LOADING_INITIAL,
5579 CONFIG_TOKEN_BACKGROUND_LOADING
5581 Bitmap *bitmap_font_initial = NULL;
5582 int parameter[NUM_INITIAL_IMAGES][NUM_GFX_ARGS];
5585 // determine settings for initial font (for displaying startup messages)
5586 for (i = 0; image_config[i].token != NULL; i++)
5588 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5590 char font_token[128];
5593 sprintf(font_token, "%s_%d", CONFIG_TOKEN_FONT_INITIAL, j + 1);
5594 len_font_token = strlen(font_token);
5596 if (strEqual(image_config[i].token, font_token))
5598 filename_font_initial = image_config[i].value;
5600 else if (strlen(image_config[i].token) > len_font_token &&
5601 strncmp(image_config[i].token, font_token, len_font_token) == 0)
5603 if (strEqual(&image_config[i].token[len_font_token], ".x"))
5604 font_initial[j].src_x = atoi(image_config[i].value);
5605 else if (strEqual(&image_config[i].token[len_font_token], ".y"))
5606 font_initial[j].src_y = atoi(image_config[i].value);
5607 else if (strEqual(&image_config[i].token[len_font_token], ".width"))
5608 font_initial[j].width = atoi(image_config[i].value);
5609 else if (strEqual(&image_config[i].token[len_font_token], ".height"))
5610 font_initial[j].height = atoi(image_config[i].value);
5615 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5617 font_initial[j].num_chars = DEFAULT_NUM_CHARS_PER_FONT;
5618 font_initial[j].num_chars_per_line = DEFAULT_NUM_CHARS_PER_LINE;
5621 if (filename_font_initial == NULL) // should not happen
5622 Fail("cannot get filename for '%s'", CONFIG_TOKEN_FONT_INITIAL);
5625 InitGfxCustomArtworkInfo();
5626 InitGfxOtherSettings();
5628 InitGfxTileSizeInfo(TILESIZE, TILESIZE);
5630 bitmap_font_initial = LoadCustomImage(filename_font_initial);
5632 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5633 font_initial[j].bitmap = bitmap_font_initial;
5635 InitFontGraphicInfo();
5639 DrawInitTextHead("Loading graphics");
5641 InitMenuDesignSettings_Static();
5643 // initialize settings for initial images with default values
5644 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5645 for (j = 0; j < NUM_GFX_ARGS; j++)
5647 get_graphic_parameter_value(image_config_suffix[j].value,
5648 image_config_suffix[j].token,
5649 image_config_suffix[j].type);
5651 // read settings for initial images from default custom artwork config
5652 char *gfx_config_filename = getPath3(options.graphics_directory,
5654 GRAPHICSINFO_FILENAME);
5656 if (fileExists(gfx_config_filename))
5658 SetupFileHash *setup_file_hash = loadSetupFileHash(gfx_config_filename);
5660 if (setup_file_hash)
5662 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5664 char *filename = getHashEntry(setup_file_hash, image_token[i]);
5668 filename_image_initial[i] = getStringCopy(filename);
5670 for (j = 0; image_config_suffix[j].token != NULL; j++)
5672 int type = image_config_suffix[j].type;
5673 char *suffix = image_config_suffix[j].token;
5674 char *token = getStringCat2(image_token[i], suffix);
5675 char *value = getHashEntry(setup_file_hash, token);
5677 checked_free(token);
5681 get_graphic_parameter_value(value, suffix, type);
5686 // read values from custom graphics config file
5687 InitMenuDesignSettings_FromHash(setup_file_hash, FALSE);
5689 freeSetupFileHash(setup_file_hash);
5693 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5695 if (filename_image_initial[i] == NULL)
5697 int len_token = strlen(image_token[i]);
5699 // read settings for initial images from static default artwork config
5700 for (j = 0; image_config[j].token != NULL; j++)
5702 if (strEqual(image_config[j].token, image_token[i]))
5704 filename_image_initial[i] = getStringCopy(image_config[j].value);
5706 else if (strlen(image_config[j].token) > len_token &&
5707 strncmp(image_config[j].token, image_token[i], len_token) == 0)
5709 for (k = 0; image_config_suffix[k].token != NULL; k++)
5711 if (strEqual(&image_config[j].token[len_token],
5712 image_config_suffix[k].token))
5714 get_graphic_parameter_value(image_config[j].value,
5715 image_config_suffix[k].token,
5716 image_config_suffix[k].type);
5723 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5725 if (filename_image_initial[i] == NULL) // should not happen
5726 Fail("cannot get filename for '%s'", image_token[i]);
5728 image_initial[i].bitmaps =
5729 checked_calloc(sizeof(Bitmap *) * NUM_IMG_BITMAP_POINTERS);
5731 if (!strEqual(filename_image_initial[i], UNDEFINED_FILENAME))
5732 image_initial[i].bitmaps[IMG_BITMAP_STANDARD] =
5733 LoadCustomImage(filename_image_initial[i]);
5735 checked_free(filename_image_initial[i]);
5738 graphic_info = image_initial; // graphic == 0 => image_initial
5740 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5741 set_graphic_parameters_ext(i, parameter[i], image_initial[i].bitmaps);
5743 graphic_info = graphic_info_last;
5745 SetLoadingBackgroundImage();
5747 ClearRectangleOnBackground(window, 0, 0, WIN_XSIZE, WIN_YSIZE);
5749 InitGfxDrawBusyAnimFunction(DrawInitAnim);
5750 InitGfxDrawGlobalAnimFunction(DrawGlobalAnimations);
5751 InitGfxDrawGlobalBorderFunction(DrawMaskedBorderToTarget);
5752 InitGfxDrawTileCursorFunction(DrawTileCursor);
5754 gfx.fade_border_source_status = global.border_status;
5755 gfx.fade_border_target_status = global.border_status;
5756 gfx.masked_border_bitmap_ptr = backbuffer;
5758 // use copy of busy animation to prevent change while reloading artwork
5762 static void InitGfxBackground(void)
5764 fieldbuffer = bitmap_db_field;
5765 SetDrawtoField(DRAW_TO_BACKBUFFER);
5767 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
5769 redraw_mask = REDRAW_ALL;
5772 static void InitLevelInfo(void)
5774 LoadLevelInfo(); // global level info
5775 LoadLevelSetup_LastSeries(); // last played series info
5776 LoadLevelSetup_SeriesInfo(); // last played level info
5778 if (global.autoplay_leveldir &&
5779 global.autoplay_mode != AUTOPLAY_MODE_TEST)
5781 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
5782 global.autoplay_leveldir);
5783 if (leveldir_current == NULL)
5784 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
5787 SetLevelSetInfo(leveldir_current->identifier, level_nr);
5790 static void InitLevelArtworkInfo(void)
5792 LoadLevelArtworkInfo();
5795 static void InitImages(void)
5797 print_timestamp_init("InitImages");
5800 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
5801 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5802 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
5803 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5804 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
5805 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5806 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
5807 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5810 setLevelArtworkDir(artwork.gfx_first);
5813 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
5814 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5815 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
5816 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5817 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
5818 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5819 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
5820 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5824 Debug("init:InitImages", "InitImages for '%s' ['%s', '%s'] ['%s', '%s']",
5825 leveldir_current->identifier,
5826 artwork.gfx_current_identifier,
5827 artwork.gfx_current->identifier,
5828 leveldir_current->graphics_set,
5829 leveldir_current->graphics_path);
5832 UPDATE_BUSY_STATE();
5834 ReloadCustomImages();
5835 print_timestamp_time("ReloadCustomImages");
5837 UPDATE_BUSY_STATE();
5839 LoadCustomElementDescriptions();
5840 print_timestamp_time("LoadCustomElementDescriptions");
5842 UPDATE_BUSY_STATE();
5844 LoadMenuDesignSettings();
5845 print_timestamp_time("LoadMenuDesignSettings");
5847 UPDATE_BUSY_STATE();
5849 ReinitializeGraphics();
5850 print_timestamp_time("ReinitializeGraphics");
5852 LoadMenuDesignSettings_AfterGraphics();
5853 print_timestamp_time("LoadMenuDesignSettings_AfterGraphics");
5855 UPDATE_BUSY_STATE();
5857 print_timestamp_done("InitImages");
5860 static void InitSound(char *identifier)
5862 print_timestamp_init("InitSound");
5864 if (identifier == NULL)
5865 identifier = artwork.snd_current->identifier;
5867 // set artwork path to send it to the sound server process
5868 setLevelArtworkDir(artwork.snd_first);
5870 InitReloadCustomSounds(identifier);
5871 print_timestamp_time("InitReloadCustomSounds");
5873 ReinitializeSounds();
5874 print_timestamp_time("ReinitializeSounds");
5876 print_timestamp_done("InitSound");
5879 static void InitMusic(char *identifier)
5881 print_timestamp_init("InitMusic");
5883 if (identifier == NULL)
5884 identifier = artwork.mus_current->identifier;
5886 // set artwork path to send it to the sound server process
5887 setLevelArtworkDir(artwork.mus_first);
5889 InitReloadCustomMusic(identifier);
5890 print_timestamp_time("InitReloadCustomMusic");
5892 ReinitializeMusic();
5893 print_timestamp_time("ReinitializeMusic");
5895 print_timestamp_done("InitMusic");
5898 static void InitArtworkDone(void)
5900 if (program.headless)
5903 InitGlobalAnimations();
5906 static void InitNetworkSettings(void)
5908 boolean network_enabled = (options.network || setup.network_mode);
5909 char *network_server = (options.server_host != NULL ? options.server_host :
5910 setup.network_server_hostname);
5912 if (strEqual(network_server, STR_NETWORK_AUTO_DETECT))
5913 network_server = NULL;
5915 InitNetworkInfo(network_enabled,
5919 options.server_port);
5922 void InitNetworkServer(void)
5924 if (!network.enabled || network.connected)
5927 LimitScreenUpdates(FALSE);
5929 if (game_status == GAME_MODE_LOADING)
5932 if (!ConnectToServer(network.server_host, network.server_port))
5934 network.enabled = FALSE;
5936 setup.network_mode = FALSE;
5940 SendToServer_ProtocolVersion();
5941 SendToServer_PlayerName(setup.player_name);
5942 SendToServer_NrWanted(setup.network_player_nr + 1);
5944 network.connected = TRUE;
5947 // short time to recognize result of network initialization
5948 if (game_status == GAME_MODE_LOADING)
5949 Delay_WithScreenUpdates(1000);
5952 static boolean CheckArtworkConfigForCustomElements(char *filename)
5954 SetupFileHash *setup_file_hash;
5955 boolean redefined_ce_found = FALSE;
5957 // !!! CACHE THIS BY USING HASH 'filename' => 'true/false' !!!
5959 if ((setup_file_hash = loadSetupFileHash(filename)) != NULL)
5961 BEGIN_HASH_ITERATION(setup_file_hash, itr)
5963 char *token = HASH_ITERATION_TOKEN(itr);
5965 if (strPrefix(token, "custom_"))
5967 redefined_ce_found = TRUE;
5972 END_HASH_ITERATION(setup_file_hash, itr)
5974 freeSetupFileHash(setup_file_hash);
5977 return redefined_ce_found;
5980 static boolean CheckArtworkTypeForRedefinedCustomElements(int type)
5982 char *filename_base, *filename_local;
5983 boolean redefined_ce_found = FALSE;
5985 setLevelArtworkDir(ARTWORK_FIRST_NODE(artwork, type));
5988 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
5989 "leveldir_current->identifier == '%s'",
5990 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5991 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
5992 "leveldir_current->graphics_path == '%s'",
5993 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5994 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
5995 "leveldir_current->graphics_set == '%s'",
5996 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5997 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
5998 "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
5999 leveldir_current == NULL ? "[NULL]" :
6000 LEVELDIR_ARTWORK_SET(leveldir_current, type));
6003 // first look for special artwork configured in level series config
6004 filename_base = getCustomArtworkLevelConfigFilename(type);
6007 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6008 "filename_base == '%s'", filename_base);
6011 if (fileExists(filename_base))
6012 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_base);
6014 filename_local = getCustomArtworkConfigFilename(type);
6017 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6018 "filename_local == '%s'", filename_local);
6021 if (filename_local != NULL && !strEqual(filename_base, filename_local))
6022 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_local);
6025 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6026 "redefined_ce_found == %d", redefined_ce_found);
6029 return redefined_ce_found;
6032 static void InitOverrideArtwork(void)
6034 boolean redefined_ce_found = FALSE;
6036 // to check if this level set redefines any CEs, do not use overriding
6037 gfx.override_level_graphics = FALSE;
6038 gfx.override_level_sounds = FALSE;
6039 gfx.override_level_music = FALSE;
6041 // now check if this level set has definitions for custom elements
6042 if (setup.override_level_graphics == AUTO ||
6043 setup.override_level_sounds == AUTO ||
6044 setup.override_level_music == AUTO)
6045 redefined_ce_found =
6046 (CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_GRAPHICS) |
6047 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_SOUNDS) |
6048 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_MUSIC));
6051 Debug("init:InitOverrideArtwork", "redefined_ce_found == %d",
6052 redefined_ce_found);
6055 if (redefined_ce_found)
6057 // this level set has CE definitions: change "AUTO" to "FALSE"
6058 gfx.override_level_graphics = (setup.override_level_graphics == TRUE);
6059 gfx.override_level_sounds = (setup.override_level_sounds == TRUE);
6060 gfx.override_level_music = (setup.override_level_music == TRUE);
6064 // this level set has no CE definitions: change "AUTO" to "TRUE"
6065 gfx.override_level_graphics = (setup.override_level_graphics != FALSE);
6066 gfx.override_level_sounds = (setup.override_level_sounds != FALSE);
6067 gfx.override_level_music = (setup.override_level_music != FALSE);
6071 Debug("init:InitOverrideArtwork", "%d, %d, %d",
6072 gfx.override_level_graphics,
6073 gfx.override_level_sounds,
6074 gfx.override_level_music);
6078 static char *getNewArtworkIdentifier(int type)
6080 static char *last_leveldir_identifier[3] = { NULL, NULL, NULL };
6081 static char *last_artwork_identifier[3] = { NULL, NULL, NULL };
6082 static boolean last_override_level_artwork[3] = { FALSE, FALSE, FALSE };
6083 static boolean last_has_custom_artwork_set[3] = { FALSE, FALSE, FALSE };
6084 static boolean initialized[3] = { FALSE, FALSE, FALSE };
6085 TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type);
6086 boolean setup_override_artwork = GFX_OVERRIDE_ARTWORK(type);
6087 char *setup_artwork_set = SETUP_ARTWORK_SET(setup, type);
6088 char *leveldir_identifier = leveldir_current->identifier;
6089 // !!! setLevelArtworkDir() should be moved to an earlier stage !!!
6090 char *leveldir_artwork_set = setLevelArtworkDir(artwork_first_node);
6091 boolean has_level_artwork_set = (leveldir_artwork_set != NULL);
6092 TreeInfo *custom_artwork_set =
6093 getTreeInfoFromIdentifier(artwork_first_node, leveldir_identifier);
6094 boolean has_custom_artwork_set = (custom_artwork_set != NULL);
6095 char *artwork_current_identifier;
6096 char *artwork_new_identifier = NULL; // default: nothing has changed
6098 // leveldir_current may be invalid (level group, parent link)
6099 if (!validLevelSeries(leveldir_current))
6102 /* 1st step: determine artwork set to be activated in descending order:
6103 --------------------------------------------------------------------
6104 1. setup artwork (when configured to override everything else)
6105 2. artwork set configured in "levelinfo.conf" of current level set
6106 (artwork in level directory will have priority when loading later)
6107 3. artwork in level directory (stored in artwork sub-directory)
6108 4. setup artwork (currently configured in setup menu) */
6110 if (setup_override_artwork)
6111 artwork_current_identifier = setup_artwork_set;
6112 else if (has_level_artwork_set)
6113 artwork_current_identifier = leveldir_artwork_set;
6114 else if (has_custom_artwork_set)
6115 artwork_current_identifier = leveldir_identifier;
6117 artwork_current_identifier = setup_artwork_set;
6119 /* 2nd step: check if it is really needed to reload artwork set
6120 ------------------------------------------------------------ */
6122 // ---------- reload if level set and also artwork set has changed ----------
6123 if (last_leveldir_identifier[type] != leveldir_identifier &&
6124 (last_has_custom_artwork_set[type] || has_custom_artwork_set))
6125 artwork_new_identifier = artwork_current_identifier;
6127 last_leveldir_identifier[type] = leveldir_identifier;
6128 last_has_custom_artwork_set[type] = has_custom_artwork_set;
6130 // ---------- reload if "override artwork" setting has changed --------------
6131 if (last_override_level_artwork[type] != setup_override_artwork)
6132 artwork_new_identifier = artwork_current_identifier;
6134 last_override_level_artwork[type] = setup_override_artwork;
6136 // ---------- reload if current artwork identifier has changed --------------
6137 if (!strEqual(last_artwork_identifier[type], artwork_current_identifier))
6138 artwork_new_identifier = artwork_current_identifier;
6140 // (we cannot compare string pointers here, so copy string content itself)
6141 setString(&last_artwork_identifier[type], artwork_current_identifier);
6143 *(ARTWORK_CURRENT_IDENTIFIER_PTR(artwork, type)) = artwork_current_identifier;
6145 // ---------- do not reload directly after starting -------------------------
6146 if (!initialized[type])
6147 artwork_new_identifier = NULL;
6149 initialized[type] = TRUE;
6151 return artwork_new_identifier;
6154 void ReloadCustomArtwork(int force_reload)
6156 int last_game_status = game_status; // save current game status
6157 char *gfx_new_identifier;
6158 char *snd_new_identifier;
6159 char *mus_new_identifier;
6160 boolean force_reload_gfx = (force_reload & (1 << ARTWORK_TYPE_GRAPHICS));
6161 boolean force_reload_snd = (force_reload & (1 << ARTWORK_TYPE_SOUNDS));
6162 boolean force_reload_mus = (force_reload & (1 << ARTWORK_TYPE_MUSIC));
6163 boolean reload_needed;
6165 InitOverrideArtwork();
6167 AdjustGraphicsForEMC();
6168 AdjustSoundsForEMC();
6170 gfx_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6171 snd_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6172 mus_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6174 reload_needed = (gfx_new_identifier != NULL || force_reload_gfx ||
6175 snd_new_identifier != NULL || force_reload_snd ||
6176 mus_new_identifier != NULL || force_reload_mus);
6181 print_timestamp_init("ReloadCustomArtwork");
6183 SetGameStatus(GAME_MODE_LOADING);
6185 FadeOut(REDRAW_ALL);
6187 SetLoadingBackgroundImage();
6189 ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6190 print_timestamp_time("ClearRectangleOnBackground");
6194 UPDATE_BUSY_STATE();
6196 if (gfx_new_identifier != NULL || force_reload_gfx)
6199 Debug("init:ReloadCustomArtwork",
6200 "RELOADING GRAPHICS '%s' -> '%s' ['%s', '%s']",
6201 artwork.gfx_current_identifier,
6203 artwork.gfx_current->identifier,
6204 leveldir_current->graphics_set);
6208 print_timestamp_time("InitImages");
6211 if (snd_new_identifier != NULL || force_reload_snd)
6213 InitSound(snd_new_identifier);
6214 print_timestamp_time("InitSound");
6217 if (mus_new_identifier != NULL || force_reload_mus)
6219 InitMusic(mus_new_identifier);
6220 print_timestamp_time("InitMusic");
6225 SetGameStatus(last_game_status); // restore current game status
6227 FadeOut(REDRAW_ALL);
6229 RedrawGlobalBorder();
6231 // force redraw of (open or closed) door graphics
6232 SetDoorState(DOOR_OPEN_ALL);
6233 CloseDoor(DOOR_CLOSE_ALL | DOOR_NO_DELAY);
6235 FadeSetEnterScreen();
6236 FadeSkipNextFadeOut();
6238 print_timestamp_done("ReloadCustomArtwork");
6240 LimitScreenUpdates(FALSE);
6243 void KeyboardAutoRepeatOffUnlessAutoplay(void)
6245 if (global.autoplay_leveldir == NULL)
6246 KeyboardAutoRepeatOff();
6249 void DisplayExitMessage(char *format, va_list ap)
6251 // also check for initialized video (headless flag may be temporarily unset)
6252 if (program.headless || !video.initialized)
6255 // check if draw buffer and fonts for exit message are already available
6256 if (drawto == NULL || font_initial[NUM_INITIAL_FONTS - 1].bitmap == NULL)
6259 int font_1 = FC_RED;
6260 int font_2 = FC_YELLOW;
6261 int font_3 = FC_BLUE;
6262 int font_width = getFontWidth(font_2);
6263 int font_height = getFontHeight(font_2);
6266 int sxsize = WIN_XSIZE - 2 * sx;
6267 int sysize = WIN_YSIZE - 2 * sy;
6268 int line_length = sxsize / font_width;
6269 int max_lines = sysize / font_height;
6270 int num_lines_printed;
6274 gfx.sxsize = sxsize;
6275 gfx.sysize = sysize;
6279 ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6281 DrawTextSCentered(sy, font_1, "Fatal error:");
6282 sy += 3 * font_height;;
6285 DrawTextBufferVA(sx, sy, format, ap, font_2,
6286 line_length, line_length, max_lines,
6287 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6288 sy += (num_lines_printed + 3) * font_height;
6290 DrawTextSCentered(sy, font_1, "For details, see the following error file:");
6291 sy += 3 * font_height;
6294 DrawTextBuffer(sx, sy, program.log_filename[LOG_ERR_ID], font_2,
6295 line_length, line_length, max_lines,
6296 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6298 DrawTextSCentered(SYSIZE - 20, font_3, "Press any key or button to exit");
6300 redraw_mask = REDRAW_ALL;
6302 // force drawing exit message even if screen updates are currently limited
6303 LimitScreenUpdates(FALSE);
6307 // deactivate toons on error message screen
6308 setup.toons = FALSE;
6310 WaitForEventToContinue();
6314 // ============================================================================
6316 // ============================================================================
6320 print_timestamp_init("OpenAll");
6322 SetGameStatus(GAME_MODE_LOADING);
6326 InitGlobal(); // initialize some global variables
6328 InitRND(NEW_RANDOMIZE);
6329 InitSimpleRandom(NEW_RANDOMIZE);
6330 InitBetterRandom(NEW_RANDOMIZE);
6332 print_timestamp_time("[init global stuff]");
6336 print_timestamp_time("[init setup/config stuff (1)]");
6338 if (options.execute_command)
6339 Execute_Command(options.execute_command);
6341 InitNetworkSettings();
6345 if (network.serveronly)
6347 #if defined(PLATFORM_UNIX)
6348 NetworkServer(network.server_port, TRUE);
6350 Warn("networking only supported in Unix version");
6353 exit(0); // never reached, server loops forever
6357 print_timestamp_time("[init setup/config stuff (2)]");
6359 print_timestamp_time("[init setup/config stuff (3)]");
6360 InitArtworkInfo(); // needed before loading gfx, sound & music
6361 print_timestamp_time("[init setup/config stuff (4)]");
6362 InitArtworkConfig(); // needed before forking sound child process
6363 print_timestamp_time("[init setup/config stuff (5)]");
6365 print_timestamp_time("[init setup/config stuff (6)]");
6369 print_timestamp_time("[init setup/config stuff]");
6371 InitVideoDefaults();
6373 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
6376 InitEventFilter(FilterMouseMotionEvents);
6378 print_timestamp_time("[init video stuff]");
6380 InitElementPropertiesStatic();
6381 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
6382 InitElementPropertiesGfxElement();
6384 print_timestamp_time("[init element properties stuff]");
6388 print_timestamp_time("InitGfx");
6391 print_timestamp_time("InitLevelInfo");
6393 InitLevelArtworkInfo();
6394 print_timestamp_time("InitLevelArtworkInfo");
6396 InitOverrideArtwork(); // needs to know current level directory
6397 print_timestamp_time("InitOverrideArtwork");
6399 InitImages(); // needs to know current level directory
6400 print_timestamp_time("InitImages");
6402 InitSound(NULL); // needs to know current level directory
6403 print_timestamp_time("InitSound");
6405 InitMusic(NULL); // needs to know current level directory
6406 print_timestamp_time("InitMusic");
6410 InitGfxBackground();
6416 if (global.autoplay_leveldir)
6421 else if (global.patchtapes_leveldir)
6426 else if (global.convert_leveldir)
6431 else if (global.dumplevel_leveldir)
6436 else if (global.dumptape_leveldir)
6441 else if (global.create_sketch_images_dir)
6443 CreateLevelSketchImages();
6446 else if (global.create_collect_images_dir)
6448 CreateCollectElementImages();
6452 InitNetworkServer();
6454 SetGameStatus(GAME_MODE_MAIN);
6456 FadeSetEnterScreen();
6457 if (!(fading.fade_mode & FADE_TYPE_TRANSFORM))
6458 FadeSkipNextFadeOut();
6460 print_timestamp_time("[post-artwork]");
6462 print_timestamp_done("OpenAll");
6464 if (setup.ask_for_remaining_tapes)
6465 setup.ask_for_uploading_tapes = TRUE;
6470 Debug("internal:path", "SDL_GetBasePath() == '%s'",
6472 Debug("internal:path", "SDL_GetPrefPath() == '%s'",
6473 SDL_GetPrefPath("artsoft", "rocksndiamonds"));
6474 #if defined(PLATFORM_ANDROID)
6475 Debug("internal:path", "SDL_AndroidGetInternalStoragePath() == '%s'",
6476 SDL_AndroidGetInternalStoragePath());
6477 Debug("internal:path", "SDL_AndroidGetExternalStoragePath() == '%s'",
6478 SDL_AndroidGetExternalStoragePath());
6479 Debug("internal:path", "SDL_AndroidGetExternalStorageState() == '%s'",
6480 (SDL_AndroidGetExternalStorageState() &
6481 SDL_ANDROID_EXTERNAL_STORAGE_WRITE ? "writable" :
6482 SDL_AndroidGetExternalStorageState() &
6483 SDL_ANDROID_EXTERNAL_STORAGE_READ ? "readable" : "not available"));
6488 static boolean WaitForApiThreads(void)
6490 unsigned int thread_delay = 0;
6491 unsigned int thread_delay_value = 10000;
6493 if (program.api_thread_count == 0)
6496 // deactivate global animations (not accessible in game state "loading")
6497 setup.toons = FALSE;
6499 // set game state to "loading" to be able to show busy animation
6500 SetGameStatus(GAME_MODE_LOADING);
6502 ResetDelayCounter(&thread_delay);
6504 // wait for threads to finish (and fail on timeout)
6505 while (program.api_thread_count > 0)
6507 if (DelayReached(&thread_delay, thread_delay_value))
6509 Error("failed waiting for threads - TIMEOUT");
6514 UPDATE_BUSY_STATE();
6522 void CloseAllAndExit(int exit_value)
6524 WaitForApiThreads();
6529 CloseAudio(); // called after freeing sounds (needed for SDL)
6537 // set a flag to tell the network server thread to quit and wait for it
6538 // using SDL_WaitThread()
6540 // Code used with SDL 1.2:
6541 // if (network.server_thread) // terminate network server
6542 // SDL_KillThread(network.server_thread);
6544 CloseVideoDisplay();
6545 ClosePlatformDependentStuff();
6547 if (exit_value != 0 && !options.execute_command)
6549 // fall back to default level set (current set may have caused an error)
6550 SaveLevelSetup_LastSeries_Deactivate();
6552 // tell user where to find error log file which may contain more details
6553 // (error notification now directly displayed on screen inside R'n'D
6554 // NotifyUserAboutErrorFile(); // currently only works for Windows