1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
12 #include "libgame/libgame.h"
27 #include "conf_e2g.c" // include auto-generated data structure definitions
28 #include "conf_esg.c" // include auto-generated data structure definitions
29 #include "conf_e2s.c" // include auto-generated data structure definitions
30 #include "conf_fnt.c" // include auto-generated data structure definitions
31 #include "conf_g2s.c" // include auto-generated data structure definitions
32 #include "conf_g2m.c" // include auto-generated data structure definitions
33 #include "conf_act.c" // include auto-generated data structure definitions
36 #define CONFIG_TOKEN_FONT_INITIAL "font.initial"
37 #define CONFIG_TOKEN_GLOBAL_BUSY_INITIAL "global.busy_initial"
38 #define CONFIG_TOKEN_GLOBAL_BUSY "global.busy"
39 #define CONFIG_TOKEN_GLOBAL_BUSY_PLAYFIELD "global.busy_playfield"
40 #define CONFIG_TOKEN_BACKGROUND_LOADING_INITIAL "background.LOADING_INITIAL"
41 #define CONFIG_TOKEN_BACKGROUND_LOADING "background.LOADING"
43 #define INITIAL_IMG_GLOBAL_BUSY_INITIAL 0
44 #define INITIAL_IMG_GLOBAL_BUSY 1
45 #define INITIAL_IMG_GLOBAL_BUSY_PLAYFIELD 2
47 #define NUM_INITIAL_IMAGES_BUSY 3
49 #define INITIAL_IMG_BACKGROUND_LOADING_INITIAL 3
50 #define INITIAL_IMG_BACKGROUND_LOADING 4
52 #define NUM_INITIAL_IMAGES 5
55 static struct FontBitmapInfo font_initial[NUM_INITIAL_FONTS];
56 static struct GraphicInfo image_initial[NUM_INITIAL_IMAGES];
58 static int copy_properties[][5] =
62 EL_BUG_LEFT, EL_BUG_RIGHT,
63 EL_BUG_UP, EL_BUG_DOWN
67 EL_SPACESHIP_LEFT, EL_SPACESHIP_RIGHT,
68 EL_SPACESHIP_UP, EL_SPACESHIP_DOWN
72 EL_BD_BUTTERFLY_LEFT, EL_BD_BUTTERFLY_RIGHT,
73 EL_BD_BUTTERFLY_UP, EL_BD_BUTTERFLY_DOWN
77 EL_BD_FIREFLY_LEFT, EL_BD_FIREFLY_RIGHT,
78 EL_BD_FIREFLY_UP, EL_BD_FIREFLY_DOWN
82 EL_PACMAN_LEFT, EL_PACMAN_RIGHT,
83 EL_PACMAN_UP, EL_PACMAN_DOWN
87 EL_YAMYAM_LEFT, EL_YAMYAM_RIGHT,
88 EL_YAMYAM_UP, EL_YAMYAM_DOWN
92 EL_MOLE_LEFT, EL_MOLE_RIGHT,
93 EL_MOLE_UP, EL_MOLE_DOWN
97 EL_SPRING_LEFT, EL_SPRING_RIGHT,
98 EL_SPRING_LEFT, EL_SPRING_RIGHT, // (to match array size)
107 // forward declaration for internal use
108 static int get_graphic_parameter_value(char *, char *, int);
111 static void SetLoadingBackgroundImage(void)
113 struct GraphicInfo *graphic_info_last = graphic_info;
114 int background_image = (game_status_last_screen == -1 ?
115 INITIAL_IMG_BACKGROUND_LOADING_INITIAL :
116 INITIAL_IMG_BACKGROUND_LOADING);
118 graphic_info = image_initial;
120 SetDrawDeactivationMask(REDRAW_NONE);
121 SetDrawBackgroundMask(REDRAW_ALL);
123 SetWindowBackgroundImage(background_image);
125 graphic_info = graphic_info_last;
128 static void DrawInitAnim(boolean only_when_loading)
130 struct GraphicInfo *graphic_info_last = graphic_info;
131 int graphic = (game_status_last_screen == -1 ?
132 INITIAL_IMG_GLOBAL_BUSY_INITIAL :
133 game_status == GAME_MODE_LOADING ?
134 INITIAL_IMG_GLOBAL_BUSY :
135 INITIAL_IMG_GLOBAL_BUSY_PLAYFIELD);
136 struct MenuPosInfo *busy = (game_status_last_screen == -1 ?
137 &init_last.busy_initial :
138 game_status == GAME_MODE_LOADING ?
140 &init_last.busy_playfield);
141 static unsigned int action_delay = 0;
142 unsigned int action_delay_value = GameFrameDelay;
143 int sync_frame = FrameCounter;
146 // prevent OS (Windows) from complaining about program not responding
149 if (game_status != GAME_MODE_LOADING && only_when_loading)
152 if (image_initial[graphic].bitmap == NULL || window == NULL)
155 if (!DelayReached(&action_delay, action_delay_value))
159 busy->x = (game_status == GAME_MODE_LOADING ? WIN_XSIZE / 2 : SXSIZE / 2);
161 busy->y = (game_status == GAME_MODE_LOADING ? WIN_YSIZE / 2 : SYSIZE / 2);
163 x = (game_status == GAME_MODE_LOADING ? 0 : SX) + ALIGNED_TEXT_XPOS(busy);
164 y = (game_status == GAME_MODE_LOADING ? 0 : SY) + ALIGNED_TEXT_YPOS(busy);
166 graphic_info = image_initial;
168 if (sync_frame % image_initial[graphic].anim_delay == 0)
172 int width = graphic_info[graphic].width;
173 int height = graphic_info[graphic].height;
174 int frame = getGraphicAnimationFrame(graphic, sync_frame);
176 ClearRectangleOnBackground(drawto, x, y, width, height);
178 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
179 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height, x, y);
181 BlitBitmap(drawto, window, x, y, width, height, x, y);
184 graphic_info = graphic_info_last;
189 static void DrawProgramInfo(void)
191 int font1_nr = FC_YELLOW;
192 int font2_nr = FC_RED;
193 int font2_height = getFontHeight(font2_nr);
196 int ypos3 = WIN_YSIZE - 20 - font2_height;
198 DrawInitText(getProgramInitString(), ypos1, font1_nr);
199 DrawInitText(setup.internal.program_copyright, ypos2, font2_nr);
200 DrawInitText(setup.internal.program_website, ypos3, font2_nr);
203 static void FreeGadgets(void)
205 FreeLevelEditorGadgets();
212 void InitGadgets(void)
214 static boolean gadgets_initialized = FALSE;
216 if (gadgets_initialized)
219 CreateLevelEditorGadgets();
223 CreateScreenGadgets();
225 InitGadgetsSoundCallback(PlaySoundActivating, PlaySoundSelecting);
227 gadgets_initialized = TRUE;
230 static void InitElementSmallImagesScaledUp(int graphic)
232 struct GraphicInfo *g = &graphic_info[graphic];
234 // create small and game tile sized bitmaps (and scale up, if needed)
235 CreateImageWithSmallImages(graphic, g->scale_up_factor, g->tile_size);
238 static void InitElementSmallImages(void)
240 print_timestamp_init("InitElementSmallImages");
242 static int special_graphics[] =
256 IMG_EDITOR_ELEMENT_BORDER,
257 IMG_EDITOR_ELEMENT_BORDER_INPUT,
258 IMG_EDITOR_CASCADE_LIST,
259 IMG_EDITOR_CASCADE_LIST_ACTIVE,
262 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
263 int num_property_mappings = getImageListPropertyMappingSize();
266 print_timestamp_time("getImageListPropertyMapping/Size");
268 print_timestamp_init("InitElementSmallImagesScaledUp (1)");
269 // initialize normal element images from static configuration
270 for (i = 0; element_to_graphic[i].element > -1; i++)
271 InitElementSmallImagesScaledUp(element_to_graphic[i].graphic);
272 print_timestamp_done("InitElementSmallImagesScaledUp (1)");
274 // initialize special element images from static configuration
275 for (i = 0; element_to_special_graphic[i].element > -1; i++)
276 InitElementSmallImagesScaledUp(element_to_special_graphic[i].graphic);
277 print_timestamp_time("InitElementSmallImagesScaledUp (2)");
279 // initialize element images from dynamic configuration
280 for (i = 0; i < num_property_mappings; i++)
281 if (property_mapping[i].base_index < MAX_NUM_ELEMENTS)
282 InitElementSmallImagesScaledUp(property_mapping[i].artwork_index);
283 print_timestamp_time("InitElementSmallImagesScaledUp (3)");
285 // initialize special non-element images from above list
286 for (i = 0; special_graphics[i] > -1; i++)
287 InitElementSmallImagesScaledUp(special_graphics[i]);
288 print_timestamp_time("InitElementSmallImagesScaledUp (4)");
290 print_timestamp_done("InitElementSmallImages");
293 static void InitScaledImagesScaledUp(int graphic)
295 struct GraphicInfo *g = &graphic_info[graphic];
297 ScaleImage(graphic, g->scale_up_factor);
300 static void InitScaledImages(void)
302 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
303 int num_property_mappings = getImageListPropertyMappingSize();
306 // scale normal images from static configuration, if not already scaled
307 for (i = 0; i < NUM_IMAGE_FILES; i++)
308 InitScaledImagesScaledUp(i);
310 // scale images from dynamic configuration, if not already scaled
311 for (i = 0; i < num_property_mappings; i++)
312 InitScaledImagesScaledUp(property_mapping[i].artwork_index);
315 static void InitBitmapPointers(void)
317 int num_images = getImageListSize();
320 // standard size bitmap may have changed -- update default bitmap pointer
321 for (i = 0; i < num_images; i++)
322 if (graphic_info[i].bitmaps)
323 graphic_info[i].bitmap = graphic_info[i].bitmaps[IMG_BITMAP_STANDARD];
326 void InitImageTextures(void)
328 static int texture_graphics[] =
330 IMG_GFX_REQUEST_BUTTON_TOUCH_YES,
331 IMG_GFX_REQUEST_BUTTON_TOUCH_NO,
332 IMG_GFX_REQUEST_BUTTON_TOUCH_CONFIRM,
333 IMG_GFX_GAME_BUTTON_TOUCH_STOP,
334 IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,
335 IMG_MENU_BUTTON_TOUCH_BACK,
336 IMG_MENU_BUTTON_TOUCH_NEXT,
337 IMG_MENU_BUTTON_TOUCH_BACK2,
338 IMG_MENU_BUTTON_TOUCH_NEXT2,
343 FreeAllImageTextures();
345 for (i = IMG_GLOBAL_BORDER_FIRST; i <= IMG_GLOBAL_BORDER_LAST; i++)
346 CreateImageTextures(i);
348 for (i = 0; i < MAX_NUM_TOONS; i++)
349 CreateImageTextures(IMG_TOON_1 + i);
351 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
353 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
355 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
357 int graphic = global_anim_info[i].graphic[j][k];
359 if (graphic == IMG_UNDEFINED)
362 CreateImageTextures(graphic);
367 for (i = 0; texture_graphics[i] > -1; i++)
368 CreateImageTextures(texture_graphics[i]);
371 static int getFontBitmapID(int font_nr)
375 // (special case: do not use special font for GAME_MODE_LOADING)
376 if (game_status >= GAME_MODE_TITLE_INITIAL &&
377 game_status <= GAME_MODE_PSEUDO_PREVIEW)
378 special = game_status;
379 else if (game_status == GAME_MODE_PSEUDO_TYPENAME)
380 special = GFX_SPECIAL_ARG_MAIN;
381 else if (game_status == GAME_MODE_PSEUDO_TYPENAMES)
382 special = GFX_SPECIAL_ARG_NAMES;
385 return font_info[font_nr].special_bitmap_id[special];
390 static int getFontFromToken(char *token)
392 char *value = getHashEntry(font_token_hash, token);
397 // if font not found, use reliable default value
398 return FONT_INITIAL_1;
401 static void InitFontGraphicInfo(void)
403 static struct FontBitmapInfo *font_bitmap_info = NULL;
404 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
405 int num_property_mappings = getImageListPropertyMappingSize();
406 int num_font_bitmaps = NUM_FONTS;
409 if (graphic_info == NULL) // still at startup phase
411 InitFontInfo(font_initial, NUM_INITIAL_FONTS,
412 getFontBitmapID, getFontFromToken);
417 // ---------- initialize font graphic definitions ----------
419 // always start with reliable default values (normal font graphics)
420 for (i = 0; i < NUM_FONTS; i++)
421 font_info[i].graphic = IMG_FONT_INITIAL_1;
423 // initialize normal font/graphic mapping from static configuration
424 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
426 int font_nr = font_to_graphic[i].font_nr;
427 int special = font_to_graphic[i].special;
428 int graphic = font_to_graphic[i].graphic;
433 font_info[font_nr].graphic = graphic;
436 // always start with reliable default values (special font graphics)
437 for (i = 0; i < NUM_FONTS; i++)
439 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
441 font_info[i].special_graphic[j] = font_info[i].graphic;
442 font_info[i].special_bitmap_id[j] = i;
446 // initialize special font/graphic mapping from static configuration
447 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
449 int font_nr = font_to_graphic[i].font_nr;
450 int special = font_to_graphic[i].special;
451 int graphic = font_to_graphic[i].graphic;
452 int base_graphic = font2baseimg(font_nr);
454 if (IS_SPECIAL_GFX_ARG(special))
456 boolean base_redefined =
457 getImageListEntryFromImageID(base_graphic)->redefined;
458 boolean special_redefined =
459 getImageListEntryFromImageID(graphic)->redefined;
460 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
462 /* if the base font ("font.title_1", for example) has been redefined,
463 but not the special font ("font.title_1.LEVELS", for example), do not
464 use an existing (in this case considered obsolete) special font
465 anymore, but use the automatically determined default font */
466 /* special case: cloned special fonts must be explicitly redefined,
467 but are not automatically redefined by redefining base font */
468 if (base_redefined && !special_redefined && !special_cloned)
471 font_info[font_nr].special_graphic[special] = graphic;
472 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
477 // initialize special font/graphic mapping from dynamic configuration
478 for (i = 0; i < num_property_mappings; i++)
480 int font_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
481 int special = property_mapping[i].ext3_index;
482 int graphic = property_mapping[i].artwork_index;
484 if (font_nr < 0 || font_nr >= NUM_FONTS)
487 if (IS_SPECIAL_GFX_ARG(special))
489 font_info[font_nr].special_graphic[special] = graphic;
490 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
495 /* correct special font/graphic mapping for cloned fonts for downwards
496 compatibility of PREVIEW fonts -- this is only needed for implicit
497 redefinition of special font by redefined base font, and only if other
498 fonts are cloned from this special font (like in the "Zelda" level set) */
499 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
501 int font_nr = font_to_graphic[i].font_nr;
502 int special = font_to_graphic[i].special;
503 int graphic = font_to_graphic[i].graphic;
505 if (IS_SPECIAL_GFX_ARG(special))
507 boolean special_redefined =
508 getImageListEntryFromImageID(graphic)->redefined;
509 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
511 if (special_cloned && !special_redefined)
515 for (j = 0; font_to_graphic[j].font_nr > -1; j++)
517 int font_nr2 = font_to_graphic[j].font_nr;
518 int special2 = font_to_graphic[j].special;
519 int graphic2 = font_to_graphic[j].graphic;
521 if (IS_SPECIAL_GFX_ARG(special2) &&
522 graphic2 == graphic_info[graphic].clone_from)
524 font_info[font_nr].special_graphic[special] =
525 font_info[font_nr2].special_graphic[special2];
526 font_info[font_nr].special_bitmap_id[special] =
527 font_info[font_nr2].special_bitmap_id[special2];
534 // reset non-redefined ".active" font graphics if normal font is redefined
535 // (this different treatment is needed because normal and active fonts are
536 // independently defined ("active" is not a property of font definitions!)
537 for (i = 0; i < NUM_FONTS; i++)
539 int font_nr_base = i;
540 int font_nr_active = FONT_ACTIVE(font_nr_base);
542 // check only those fonts with exist as normal and ".active" variant
543 if (font_nr_base != font_nr_active)
545 int base_graphic = font_info[font_nr_base].graphic;
546 int active_graphic = font_info[font_nr_active].graphic;
547 boolean base_redefined =
548 getImageListEntryFromImageID(base_graphic)->redefined;
549 boolean active_redefined =
550 getImageListEntryFromImageID(active_graphic)->redefined;
552 /* if the base font ("font.menu_1", for example) has been redefined,
553 but not the active font ("font.menu_1.active", for example), do not
554 use an existing (in this case considered obsolete) active font
555 anymore, but use the automatically determined default font */
556 if (base_redefined && !active_redefined)
557 font_info[font_nr_active].graphic = base_graphic;
559 // now also check each "special" font (which may be the same as above)
560 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
562 int base_graphic = font_info[font_nr_base].special_graphic[j];
563 int active_graphic = font_info[font_nr_active].special_graphic[j];
564 boolean base_redefined =
565 getImageListEntryFromImageID(base_graphic)->redefined;
566 boolean active_redefined =
567 getImageListEntryFromImageID(active_graphic)->redefined;
569 // same as above, but check special graphic definitions, for example:
570 // redefined "font.menu_1.MAIN" invalidates "font.menu_1.active.MAIN"
571 if (base_redefined && !active_redefined)
573 font_info[font_nr_active].special_graphic[j] =
574 font_info[font_nr_base].special_graphic[j];
575 font_info[font_nr_active].special_bitmap_id[j] =
576 font_info[font_nr_base].special_bitmap_id[j];
582 // ---------- initialize font bitmap array ----------
584 if (font_bitmap_info != NULL)
585 FreeFontInfo(font_bitmap_info);
588 checked_calloc(num_font_bitmaps * sizeof(struct FontBitmapInfo));
590 // ---------- initialize font bitmap definitions ----------
592 for (i = 0; i < NUM_FONTS; i++)
594 if (i < NUM_INITIAL_FONTS)
596 font_bitmap_info[i] = font_initial[i];
600 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
602 int font_bitmap_id = font_info[i].special_bitmap_id[j];
603 int graphic = font_info[i].special_graphic[j];
605 // set 'graphic_info' for font entries, if uninitialized (guessed)
606 if (graphic_info[graphic].anim_frames < MIN_NUM_CHARS_PER_FONT)
608 graphic_info[graphic].anim_frames = DEFAULT_NUM_CHARS_PER_FONT;
609 graphic_info[graphic].anim_frames_per_line = DEFAULT_NUM_CHARS_PER_LINE;
612 // copy font relevant information from graphics information
613 font_bitmap_info[font_bitmap_id].bitmap = graphic_info[graphic].bitmap;
614 font_bitmap_info[font_bitmap_id].src_x = graphic_info[graphic].src_x;
615 font_bitmap_info[font_bitmap_id].src_y = graphic_info[graphic].src_y;
616 font_bitmap_info[font_bitmap_id].width = graphic_info[graphic].width;
617 font_bitmap_info[font_bitmap_id].height = graphic_info[graphic].height;
619 font_bitmap_info[font_bitmap_id].offset_x =
620 graphic_info[graphic].offset_x;
621 font_bitmap_info[font_bitmap_id].offset_y =
622 graphic_info[graphic].offset_y;
624 font_bitmap_info[font_bitmap_id].draw_xoffset =
625 graphic_info[graphic].draw_xoffset;
626 font_bitmap_info[font_bitmap_id].draw_yoffset =
627 graphic_info[graphic].draw_yoffset;
629 font_bitmap_info[font_bitmap_id].num_chars =
630 graphic_info[graphic].anim_frames;
631 font_bitmap_info[font_bitmap_id].num_chars_per_line =
632 graphic_info[graphic].anim_frames_per_line;
636 InitFontInfo(font_bitmap_info, num_font_bitmaps,
637 getFontBitmapID, getFontFromToken);
640 static void InitGlobalAnimGraphicInfo(void)
642 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
643 int num_property_mappings = getImageListPropertyMappingSize();
646 if (graphic_info == NULL) // still at startup phase
649 // always start with reliable default values (no global animations)
650 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
651 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
652 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
653 global_anim_info[i].graphic[j][k] = IMG_UNDEFINED;
655 // initialize global animation definitions from static configuration
656 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
658 int j = GLOBAL_ANIM_ID_PART_BASE;
659 int k = GFX_SPECIAL_ARG_DEFAULT;
661 global_anim_info[i].graphic[j][k] = IMG_GFX_GLOBAL_ANIM_1 + i;
664 // initialize global animation definitions from dynamic configuration
665 for (i = 0; i < num_property_mappings; i++)
667 int anim_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS - NUM_FONTS;
668 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
669 int special = property_mapping[i].ext3_index;
670 int graphic = property_mapping[i].artwork_index;
672 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
675 // set animation part to base part, if not specified
676 if (!IS_GLOBAL_ANIM_PART(part_nr))
677 part_nr = GLOBAL_ANIM_ID_PART_BASE;
679 // set animation screen to default, if not specified
680 if (!IS_SPECIAL_GFX_ARG(special))
681 special = GFX_SPECIAL_ARG_DEFAULT;
683 global_anim_info[anim_nr].graphic[part_nr][special] = graphic;
685 // fix default value for ".draw_masked" (for backward compatibility)
686 struct GraphicInfo *g = &graphic_info[graphic];
687 struct FileInfo *image = getImageListEntryFromImageID(graphic);
688 char **parameter_raw = image->parameter;
689 int p = GFX_ARG_DRAW_MASKED;
690 int draw_masked = get_graphic_parameter_value(parameter_raw[p],
691 image_config_suffix[p].token,
692 image_config_suffix[p].type);
694 // if ".draw_masked" parameter is undefined, use default value "TRUE"
695 if (draw_masked == ARG_UNDEFINED_VALUE)
696 g->draw_masked = TRUE;
700 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
701 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
702 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
703 if (global_anim_info[i].graphic[j][k] != IMG_UNDEFINED &&
704 graphic_info[global_anim_info[i].graphic[j][k]].bitmap != NULL)
705 Debug("init:InitGlobalAnimGraphicInfo",
706 "anim %d, part %d, mode %d => %d",
707 i, j, k, global_anim_info[i].graphic[j][k]);
711 static void InitGlobalAnimSoundInfo(void)
713 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
714 int num_property_mappings = getSoundListPropertyMappingSize();
717 // always start with reliable default values (no global animation sounds)
718 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
719 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
720 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
721 global_anim_info[i].sound[j][k] = SND_UNDEFINED;
723 // initialize global animation sound definitions from dynamic configuration
724 for (i = 0; i < num_property_mappings; i++)
726 int anim_nr = property_mapping[i].base_index - 2 * MAX_NUM_ELEMENTS;
727 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
728 int special = property_mapping[i].ext3_index;
729 int sound = property_mapping[i].artwork_index;
731 // sound uses control definition; map it to position of graphic (artwork)
732 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
734 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
737 // set animation part to base part, if not specified
738 if (!IS_GLOBAL_ANIM_PART(part_nr))
739 part_nr = GLOBAL_ANIM_ID_PART_BASE;
741 // set animation screen to default, if not specified
742 if (!IS_SPECIAL_GFX_ARG(special))
743 special = GFX_SPECIAL_ARG_DEFAULT;
745 global_anim_info[anim_nr].sound[part_nr][special] = sound;
749 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
750 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
751 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
752 if (global_anim_info[i].sound[j][k] != SND_UNDEFINED)
753 Debug("init:InitGlobalAnimSoundInfo",
754 "anim %d, part %d, mode %d => %d",
755 i, j, k, global_anim_info[i].sound[j][k]);
759 static void InitGlobalAnimMusicInfo(void)
761 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
762 int num_property_mappings = getMusicListPropertyMappingSize();
765 // always start with reliable default values (no global animation music)
766 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
767 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
768 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
769 global_anim_info[i].music[j][k] = MUS_UNDEFINED;
771 // initialize global animation music definitions from dynamic configuration
772 for (i = 0; i < num_property_mappings; i++)
774 int anim_nr = property_mapping[i].base_index - NUM_MUSIC_PREFIXES;
775 int part_nr = property_mapping[i].ext1_index - ACTION_PART_1;
776 int special = property_mapping[i].ext2_index;
777 int music = property_mapping[i].artwork_index;
779 // music uses control definition; map it to position of graphic (artwork)
780 anim_nr -= GLOBAL_ANIM_ID_CONTROL_FIRST;
782 if (anim_nr < 0 || anim_nr >= NUM_GLOBAL_ANIM_TOKENS)
785 // set animation part to base part, if not specified
786 if (!IS_GLOBAL_ANIM_PART(part_nr))
787 part_nr = GLOBAL_ANIM_ID_PART_BASE;
789 // set animation screen to default, if not specified
790 if (!IS_SPECIAL_GFX_ARG(special))
791 special = GFX_SPECIAL_ARG_DEFAULT;
793 global_anim_info[anim_nr].music[part_nr][special] = music;
797 for (i = 0; i < NUM_GLOBAL_ANIMS; i++)
798 for (j = 0; j < NUM_GLOBAL_ANIM_PARTS_ALL; j++)
799 for (k = 0; k < NUM_SPECIAL_GFX_ARGS; k++)
800 if (global_anim_info[i].music[j][k] != MUS_UNDEFINED)
801 Debug("init:InitGlobalAnimMusicInfo",
802 "anim %d, part %d, mode %d => %d",
803 i, j, k, global_anim_info[i].music[j][k]);
807 static void InitElementGraphicInfo(void)
809 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
810 int num_property_mappings = getImageListPropertyMappingSize();
813 if (graphic_info == NULL) // still at startup phase
816 // set values to -1 to identify later as "uninitialized" values
817 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
819 for (act = 0; act < NUM_ACTIONS; act++)
821 element_info[i].graphic[act] = -1;
822 element_info[i].crumbled[act] = -1;
824 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
826 element_info[i].direction_graphic[act][dir] = -1;
827 element_info[i].direction_crumbled[act][dir] = -1;
834 // initialize normal element/graphic mapping from static configuration
835 for (i = 0; element_to_graphic[i].element > -1; i++)
837 int element = element_to_graphic[i].element;
838 int action = element_to_graphic[i].action;
839 int direction = element_to_graphic[i].direction;
840 boolean crumbled = element_to_graphic[i].crumbled;
841 int graphic = element_to_graphic[i].graphic;
842 int base_graphic = el2baseimg(element);
844 if (graphic_info[graphic].bitmap == NULL)
847 if ((action > -1 || direction > -1 || crumbled == TRUE) &&
850 boolean base_redefined =
851 getImageListEntryFromImageID(base_graphic)->redefined;
852 boolean act_dir_redefined =
853 getImageListEntryFromImageID(graphic)->redefined;
855 /* if the base graphic ("emerald", for example) has been redefined,
856 but not the action graphic ("emerald.falling", for example), do not
857 use an existing (in this case considered obsolete) action graphic
858 anymore, but use the automatically determined default graphic */
859 if (base_redefined && !act_dir_redefined)
864 action = ACTION_DEFAULT;
869 element_info[element].direction_crumbled[action][direction] = graphic;
871 element_info[element].crumbled[action] = graphic;
876 element_info[element].direction_graphic[action][direction] = graphic;
878 element_info[element].graphic[action] = graphic;
882 // initialize normal element/graphic mapping from dynamic configuration
883 for (i = 0; i < num_property_mappings; i++)
885 int element = property_mapping[i].base_index;
886 int action = property_mapping[i].ext1_index;
887 int direction = property_mapping[i].ext2_index;
888 int special = property_mapping[i].ext3_index;
889 int graphic = property_mapping[i].artwork_index;
890 boolean crumbled = FALSE;
892 if (special == GFX_SPECIAL_ARG_CRUMBLED)
898 if (graphic_info[graphic].bitmap == NULL)
901 if (element >= MAX_NUM_ELEMENTS || special != -1)
905 action = ACTION_DEFAULT;
910 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
911 element_info[element].direction_crumbled[action][dir] = -1;
914 element_info[element].direction_crumbled[action][direction] = graphic;
916 element_info[element].crumbled[action] = graphic;
921 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
922 element_info[element].direction_graphic[action][dir] = -1;
925 element_info[element].direction_graphic[action][direction] = graphic;
927 element_info[element].graphic[action] = graphic;
931 // now copy all graphics that are defined to be cloned from other graphics
932 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
934 int graphic = element_info[i].graphic[ACTION_DEFAULT];
935 int crumbled_like, diggable_like;
940 crumbled_like = graphic_info[graphic].crumbled_like;
941 diggable_like = graphic_info[graphic].diggable_like;
943 if (crumbled_like != -1 && element_info[i].crumbled[ACTION_DEFAULT] == -1)
945 for (act = 0; act < NUM_ACTIONS; act++)
946 element_info[i].crumbled[act] =
947 element_info[crumbled_like].crumbled[act];
948 for (act = 0; act < NUM_ACTIONS; act++)
949 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
950 element_info[i].direction_crumbled[act][dir] =
951 element_info[crumbled_like].direction_crumbled[act][dir];
954 if (diggable_like != -1 && element_info[i].graphic[ACTION_DIGGING] == -1)
956 element_info[i].graphic[ACTION_DIGGING] =
957 element_info[diggable_like].graphic[ACTION_DIGGING];
958 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
959 element_info[i].direction_graphic[ACTION_DIGGING][dir] =
960 element_info[diggable_like].direction_graphic[ACTION_DIGGING][dir];
964 // set hardcoded definitions for some runtime elements without graphic
965 element_info[EL_AMOEBA_TO_DIAMOND].graphic[ACTION_DEFAULT] = IMG_AMOEBA_DEAD;
967 // set hardcoded definitions for some internal elements without graphic
968 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
970 if (IS_EDITOR_CASCADE_INACTIVE(i))
971 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST;
972 else if (IS_EDITOR_CASCADE_ACTIVE(i))
973 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST_ACTIVE;
976 // now set all undefined/invalid graphics to -1 to set to default after it
977 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
979 for (act = 0; act < NUM_ACTIONS; act++)
983 graphic = element_info[i].graphic[act];
984 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
985 element_info[i].graphic[act] = -1;
987 graphic = element_info[i].crumbled[act];
988 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
989 element_info[i].crumbled[act] = -1;
991 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
993 graphic = element_info[i].direction_graphic[act][dir];
994 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
995 element_info[i].direction_graphic[act][dir] = -1;
997 graphic = element_info[i].direction_crumbled[act][dir];
998 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
999 element_info[i].direction_crumbled[act][dir] = -1;
1004 UPDATE_BUSY_STATE();
1006 // adjust graphics with 2nd tile for movement according to direction
1007 // (do this before correcting '-1' values to minimize calculations)
1008 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1010 for (act = 0; act < NUM_ACTIONS; act++)
1012 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1014 int graphic = element_info[i].direction_graphic[act][dir];
1015 int move_dir = (act == ACTION_FALLING ? MV_BIT_DOWN : dir);
1017 if (act == ACTION_FALLING) // special case
1018 graphic = element_info[i].graphic[act];
1020 if (graphic != -1 &&
1021 graphic_info[graphic].double_movement &&
1022 graphic_info[graphic].swap_double_tiles != 0)
1024 struct GraphicInfo *g = &graphic_info[graphic];
1025 int src_x_front = g->src_x;
1026 int src_y_front = g->src_y;
1027 int src_x_back = g->src_x + g->offset2_x;
1028 int src_y_back = g->src_y + g->offset2_y;
1029 boolean frames_are_ordered_diagonally = (g->offset_x != 0 &&
1031 boolean front_is_left_or_upper = (src_x_front < src_x_back ||
1032 src_y_front < src_y_back);
1033 boolean swap_movement_tiles_always = (g->swap_double_tiles == 1);
1034 boolean swap_movement_tiles_autodetected =
1035 (!frames_are_ordered_diagonally &&
1036 ((move_dir == MV_BIT_LEFT && !front_is_left_or_upper) ||
1037 (move_dir == MV_BIT_UP && !front_is_left_or_upper) ||
1038 (move_dir == MV_BIT_RIGHT && front_is_left_or_upper) ||
1039 (move_dir == MV_BIT_DOWN && front_is_left_or_upper)));
1041 // swap frontside and backside graphic tile coordinates, if needed
1042 if (swap_movement_tiles_always || swap_movement_tiles_autodetected)
1044 // get current (wrong) backside tile coordinates
1045 getGraphicSourceXY(graphic, 0, &src_x_back, &src_y_back, TRUE);
1047 // set frontside tile coordinates to backside tile coordinates
1048 g->src_x = src_x_back;
1049 g->src_y = src_y_back;
1051 // invert tile offset to point to new backside tile coordinates
1055 // do not swap front and backside tiles again after correction
1056 g->swap_double_tiles = 0;
1063 UPDATE_BUSY_STATE();
1065 // now set all '-1' values to element specific default values
1066 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1068 int default_graphic = element_info[i].graphic[ACTION_DEFAULT];
1069 int default_crumbled = element_info[i].crumbled[ACTION_DEFAULT];
1070 int default_direction_graphic[NUM_DIRECTIONS_FULL];
1071 int default_direction_crumbled[NUM_DIRECTIONS_FULL];
1073 if (default_graphic == -1)
1074 default_graphic = IMG_UNKNOWN;
1076 if (default_crumbled == -1)
1077 default_crumbled = default_graphic;
1079 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1081 default_direction_graphic[dir] =
1082 element_info[i].direction_graphic[ACTION_DEFAULT][dir];
1083 default_direction_crumbled[dir] =
1084 element_info[i].direction_crumbled[ACTION_DEFAULT][dir];
1086 if (default_direction_graphic[dir] == -1)
1087 default_direction_graphic[dir] = default_graphic;
1089 if (default_direction_crumbled[dir] == -1)
1090 default_direction_crumbled[dir] = default_direction_graphic[dir];
1093 for (act = 0; act < NUM_ACTIONS; act++)
1095 boolean act_remove = ((IS_DIGGABLE(i) && act == ACTION_DIGGING) ||
1096 (IS_SNAPPABLE(i) && act == ACTION_SNAPPING) ||
1097 (IS_COLLECTIBLE(i) && act == ACTION_COLLECTING));
1098 boolean act_turning = (act == ACTION_TURNING_FROM_LEFT ||
1099 act == ACTION_TURNING_FROM_RIGHT ||
1100 act == ACTION_TURNING_FROM_UP ||
1101 act == ACTION_TURNING_FROM_DOWN);
1103 // generic default action graphic (defined by "[default]" directive)
1104 int default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1105 int default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1106 int default_remove_graphic = IMG_EMPTY;
1108 if (act_remove && default_action_graphic != -1)
1109 default_remove_graphic = default_action_graphic;
1111 // look for special default action graphic (classic game specific)
1112 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].graphic[act] != -1)
1113 default_action_graphic = element_info[EL_BD_DEFAULT].graphic[act];
1114 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].graphic[act] != -1)
1115 default_action_graphic = element_info[EL_SP_DEFAULT].graphic[act];
1116 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].graphic[act] != -1)
1117 default_action_graphic = element_info[EL_SB_DEFAULT].graphic[act];
1118 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].graphic[act] != -1)
1119 default_action_graphic = element_info[EL_MM_DEFAULT].graphic[act];
1121 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].crumbled[act] != -1)
1122 default_action_crumbled = element_info[EL_BD_DEFAULT].crumbled[act];
1123 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].crumbled[act] != -1)
1124 default_action_crumbled = element_info[EL_SP_DEFAULT].crumbled[act];
1125 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].crumbled[act] != -1)
1126 default_action_crumbled = element_info[EL_SB_DEFAULT].crumbled[act];
1127 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].crumbled[act] != -1)
1128 default_action_crumbled = element_info[EL_MM_DEFAULT].crumbled[act];
1130 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
1131 // !!! make this better !!!
1132 if (i == EL_EMPTY_SPACE)
1134 default_action_graphic = element_info[EL_DEFAULT].graphic[act];
1135 default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
1138 if (default_action_graphic == -1)
1139 default_action_graphic = default_graphic;
1141 if (default_action_crumbled == -1)
1142 default_action_crumbled = default_action_graphic;
1144 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
1146 // use action graphic as the default direction graphic, if undefined
1147 int default_action_direction_graphic = element_info[i].graphic[act];
1148 int default_action_direction_crumbled = element_info[i].crumbled[act];
1150 // no graphic for current action -- use default direction graphic
1151 if (default_action_direction_graphic == -1)
1152 default_action_direction_graphic =
1153 (act_remove ? default_remove_graphic :
1155 element_info[i].direction_graphic[ACTION_TURNING][dir] :
1156 default_action_graphic != default_graphic ?
1157 default_action_graphic :
1158 default_direction_graphic[dir]);
1160 if (element_info[i].direction_graphic[act][dir] == -1)
1161 element_info[i].direction_graphic[act][dir] =
1162 default_action_direction_graphic;
1164 if (default_action_direction_crumbled == -1)
1165 default_action_direction_crumbled =
1166 element_info[i].direction_graphic[act][dir];
1168 if (element_info[i].direction_crumbled[act][dir] == -1)
1169 element_info[i].direction_crumbled[act][dir] =
1170 default_action_direction_crumbled;
1173 // no graphic for this specific action -- use default action graphic
1174 if (element_info[i].graphic[act] == -1)
1175 element_info[i].graphic[act] =
1176 (act_remove ? default_remove_graphic :
1177 act_turning ? element_info[i].graphic[ACTION_TURNING] :
1178 default_action_graphic);
1180 if (element_info[i].crumbled[act] == -1)
1181 element_info[i].crumbled[act] = element_info[i].graphic[act];
1185 UPDATE_BUSY_STATE();
1188 static void InitElementSpecialGraphicInfo(void)
1190 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
1191 int num_property_mappings = getImageListPropertyMappingSize();
1194 // always start with reliable default values
1195 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1196 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1197 element_info[i].special_graphic[j] =
1198 element_info[i].graphic[ACTION_DEFAULT];
1200 // initialize special element/graphic mapping from static configuration
1201 for (i = 0; element_to_special_graphic[i].element > -1; i++)
1203 int element = element_to_special_graphic[i].element;
1204 int special = element_to_special_graphic[i].special;
1205 int graphic = element_to_special_graphic[i].graphic;
1206 int base_graphic = el2baseimg(element);
1207 boolean base_redefined =
1208 getImageListEntryFromImageID(base_graphic)->redefined;
1209 boolean special_redefined =
1210 getImageListEntryFromImageID(graphic)->redefined;
1212 /* if the base graphic ("emerald", for example) has been redefined,
1213 but not the special graphic ("emerald.EDITOR", for example), do not
1214 use an existing (in this case considered obsolete) special graphic
1215 anymore, but use the automatically created (down-scaled) graphic */
1216 if (base_redefined && !special_redefined)
1219 element_info[element].special_graphic[special] = graphic;
1222 // initialize special element/graphic mapping from dynamic configuration
1223 for (i = 0; i < num_property_mappings; i++)
1225 int element = property_mapping[i].base_index;
1226 int action = property_mapping[i].ext1_index;
1227 int direction = property_mapping[i].ext2_index;
1228 int special = property_mapping[i].ext3_index;
1229 int graphic = property_mapping[i].artwork_index;
1231 // for action ".active", replace element with active element, if exists
1232 if (action == ACTION_ACTIVE && element != ELEMENT_ACTIVE(element))
1234 element = ELEMENT_ACTIVE(element);
1238 if (element >= MAX_NUM_ELEMENTS)
1241 // do not change special graphic if action or direction was specified
1242 if (action != -1 || direction != -1)
1245 if (IS_SPECIAL_GFX_ARG(special))
1246 element_info[element].special_graphic[special] = graphic;
1249 // now set all undefined/invalid graphics to default
1250 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1251 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
1252 if (graphic_info[element_info[i].special_graphic[j]].bitmap == NULL)
1253 element_info[i].special_graphic[j] =
1254 element_info[i].graphic[ACTION_DEFAULT];
1257 static int get_graphic_parameter_value(char *value_raw, char *suffix, int type)
1259 if (type != TYPE_ELEMENT && type != TYPE_GRAPHIC)
1260 return get_parameter_value(value_raw, suffix, type);
1262 if (strEqual(value_raw, ARG_UNDEFINED))
1263 return ARG_UNDEFINED_VALUE;
1265 if (type == TYPE_ELEMENT)
1267 char *value = getHashEntry(element_token_hash, value_raw);
1272 Warn("error found in config file:");
1273 Warn("- config file: '%s'", getImageConfigFilename());
1274 Warn("error: invalid element token '%s'", value_raw);
1275 Warn("custom graphic rejected for this element/action");
1276 Warn("fallback done to undefined element for this graphic");
1280 return (value != NULL ? atoi(value) : EL_UNDEFINED);
1282 else if (type == TYPE_GRAPHIC)
1284 char *value = getHashEntry(graphic_token_hash, value_raw);
1285 int fallback_graphic = IMG_CHAR_EXCLAM;
1290 Warn("error found in config file:");
1291 Warn("- config file: '%s'", getImageConfigFilename());
1292 Warn("error: invalid graphic token '%s'", value_raw);
1293 Warn("custom graphic rejected for this element/action");
1294 Warn("fallback done to 'char_exclam' for this graphic");
1298 return (value != NULL ? atoi(value) : fallback_graphic);
1304 static int get_scaled_graphic_width(int graphic)
1306 int original_width = getOriginalImageWidthFromImageID(graphic);
1307 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1309 return original_width * scale_up_factor;
1312 static int get_scaled_graphic_height(int graphic)
1314 int original_height = getOriginalImageHeightFromImageID(graphic);
1315 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1317 return original_height * scale_up_factor;
1320 static void set_graphic_parameters_ext(int graphic, int *parameter,
1321 Bitmap **src_bitmaps)
1323 struct GraphicInfo *g = &graphic_info[graphic];
1324 Bitmap *src_bitmap = (src_bitmaps ? src_bitmaps[IMG_BITMAP_STANDARD] : NULL);
1325 int anim_frames_per_row = 1, anim_frames_per_col = 1;
1326 int anim_frames_per_line = 1;
1328 // always start with reliable default values
1329 g->src_image_width = 0;
1330 g->src_image_height = 0;
1333 g->width = TILEX; // default for element graphics
1334 g->height = TILEY; // default for element graphics
1335 g->offset_x = 0; // one or both of these values ...
1336 g->offset_y = 0; // ... will be corrected later
1337 g->offset2_x = 0; // one or both of these values ...
1338 g->offset2_y = 0; // ... will be corrected later
1339 g->swap_double_tiles = -1; // auto-detect tile swapping
1340 g->crumbled_like = -1; // do not use clone element
1341 g->diggable_like = -1; // do not use clone element
1342 g->border_size = TILEX / 8; // "CRUMBLED" border size
1343 g->scale_up_factor = 1; // default: no scaling up
1344 g->tile_size = TILESIZE; // default: standard tile size
1345 g->clone_from = -1; // do not use clone graphic
1346 g->init_delay_fixed = 0;
1347 g->init_delay_random = 0;
1348 g->init_delay_action = -1;
1349 g->anim_delay_fixed = 0;
1350 g->anim_delay_random = 0;
1351 g->anim_delay_action = -1;
1352 g->post_delay_fixed = 0;
1353 g->post_delay_random = 0;
1354 g->post_delay_action = -1;
1355 g->init_event = ANIM_EVENT_UNDEFINED;
1356 g->anim_event = ANIM_EVENT_UNDEFINED;
1357 g->init_event_action = -1;
1358 g->anim_event_action = -1;
1359 g->draw_masked = FALSE;
1361 g->fade_mode = FADE_MODE_DEFAULT;
1365 g->auto_delay_unit = AUTO_DELAY_UNIT_DEFAULT;
1366 g->align = ALIGN_CENTER; // default for title screens
1367 g->valign = VALIGN_MIDDLE; // default for title screens
1368 g->sort_priority = 0; // default for title screens
1370 g->style = STYLE_DEFAULT;
1372 g->bitmaps = src_bitmaps;
1373 g->bitmap = src_bitmap;
1375 // optional zoom factor for scaling up the image to a larger size
1376 if (parameter[GFX_ARG_SCALE_UP_FACTOR] != ARG_UNDEFINED_VALUE)
1377 g->scale_up_factor = parameter[GFX_ARG_SCALE_UP_FACTOR];
1378 if (g->scale_up_factor < 1)
1379 g->scale_up_factor = 1; // no scaling
1381 // optional tile size for using non-standard image size
1382 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1384 g->tile_size = parameter[GFX_ARG_TILE_SIZE];
1387 // CHECK: should tile sizes less than standard tile size be allowed?
1388 if (g->tile_size < TILESIZE)
1389 g->tile_size = TILESIZE; // standard tile size
1392 // when setting tile size, also set width and height accordingly
1393 g->width = g->tile_size;
1394 g->height = g->tile_size;
1397 if (g->use_image_size)
1399 // set new default bitmap size (with scaling, but without small images)
1400 g->width = get_scaled_graphic_width(graphic);
1401 g->height = get_scaled_graphic_height(graphic);
1404 // optional width and height of each animation frame
1405 if (parameter[GFX_ARG_WIDTH] != ARG_UNDEFINED_VALUE)
1406 g->width = parameter[GFX_ARG_WIDTH];
1407 if (parameter[GFX_ARG_HEIGHT] != ARG_UNDEFINED_VALUE)
1408 g->height = parameter[GFX_ARG_HEIGHT];
1410 // optional x and y tile position of animation frame sequence
1411 if (parameter[GFX_ARG_XPOS] != ARG_UNDEFINED_VALUE)
1412 g->src_x = parameter[GFX_ARG_XPOS] * g->width;
1413 if (parameter[GFX_ARG_YPOS] != ARG_UNDEFINED_VALUE)
1414 g->src_y = parameter[GFX_ARG_YPOS] * g->height;
1416 // optional x and y pixel position of animation frame sequence
1417 if (parameter[GFX_ARG_X] != ARG_UNDEFINED_VALUE)
1418 g->src_x = parameter[GFX_ARG_X];
1419 if (parameter[GFX_ARG_Y] != ARG_UNDEFINED_VALUE)
1420 g->src_y = parameter[GFX_ARG_Y];
1427 Warn("invalid value %d for '%s.width' (fallback done to %d)",
1428 g->width, getTokenFromImageID(graphic), TILEX);
1431 g->width = TILEX; // will be checked to be inside bitmap later
1437 Warn("invalid value %d for '%s.height' (fallback done to %d)",
1438 g->height, getTokenFromImageID(graphic), TILEY);
1441 g->height = TILEY; // will be checked to be inside bitmap later
1447 // get final bitmap size (with scaling, but without small images)
1448 int src_image_width = get_scaled_graphic_width(graphic);
1449 int src_image_height = get_scaled_graphic_height(graphic);
1451 if (src_image_width == 0 || src_image_height == 0)
1453 // only happens when loaded outside artwork system (like "global.busy")
1454 src_image_width = src_bitmap->width;
1455 src_image_height = src_bitmap->height;
1458 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1460 anim_frames_per_row = MAX(1, src_image_width / g->tile_size);
1461 anim_frames_per_col = MAX(1, src_image_height / g->tile_size);
1465 anim_frames_per_row = MAX(1, src_image_width / g->width);
1466 anim_frames_per_col = MAX(1, src_image_height / g->height);
1469 g->src_image_width = src_image_width;
1470 g->src_image_height = src_image_height;
1473 // correct x or y offset dependent of vertical or horizontal frame order
1474 if (parameter[GFX_ARG_VERTICAL]) // frames are ordered vertically
1476 g->offset_y = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1477 parameter[GFX_ARG_OFFSET] : g->height);
1478 anim_frames_per_line = anim_frames_per_col;
1480 else // frames are ordered horizontally
1482 g->offset_x = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1483 parameter[GFX_ARG_OFFSET] : g->width);
1484 anim_frames_per_line = anim_frames_per_row;
1487 // optionally, the x and y offset of frames can be specified directly
1488 if (parameter[GFX_ARG_XOFFSET] != ARG_UNDEFINED_VALUE)
1489 g->offset_x = parameter[GFX_ARG_XOFFSET];
1490 if (parameter[GFX_ARG_YOFFSET] != ARG_UNDEFINED_VALUE)
1491 g->offset_y = parameter[GFX_ARG_YOFFSET];
1493 // optionally, moving animations may have separate start and end graphics
1494 g->double_movement = parameter[GFX_ARG_2ND_MOVEMENT_TILE];
1496 if (parameter[GFX_ARG_2ND_VERTICAL] == ARG_UNDEFINED_VALUE)
1497 parameter[GFX_ARG_2ND_VERTICAL] = !parameter[GFX_ARG_VERTICAL];
1499 // correct x or y offset2 dependent of vertical or horizontal frame order
1500 if (parameter[GFX_ARG_2ND_VERTICAL]) // frames are ordered vertically
1501 g->offset2_y = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1502 parameter[GFX_ARG_2ND_OFFSET] : g->height);
1503 else // frames are ordered horizontally
1504 g->offset2_x = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1505 parameter[GFX_ARG_2ND_OFFSET] : g->width);
1507 // optionally, the x and y offset of 2nd graphic can be specified directly
1508 if (parameter[GFX_ARG_2ND_XOFFSET] != ARG_UNDEFINED_VALUE)
1509 g->offset2_x = parameter[GFX_ARG_2ND_XOFFSET];
1510 if (parameter[GFX_ARG_2ND_YOFFSET] != ARG_UNDEFINED_VALUE)
1511 g->offset2_y = parameter[GFX_ARG_2ND_YOFFSET];
1513 // optionally, the second movement tile can be specified as start tile
1514 if (parameter[GFX_ARG_2ND_SWAP_TILES] != ARG_UNDEFINED_VALUE)
1515 g->swap_double_tiles= parameter[GFX_ARG_2ND_SWAP_TILES];
1517 // automatically determine correct number of frames, if not defined
1518 if (parameter[GFX_ARG_FRAMES] != ARG_UNDEFINED_VALUE)
1519 g->anim_frames = parameter[GFX_ARG_FRAMES];
1520 else if (parameter[GFX_ARG_XPOS] == 0 && !parameter[GFX_ARG_VERTICAL])
1521 g->anim_frames = anim_frames_per_row;
1522 else if (parameter[GFX_ARG_YPOS] == 0 && parameter[GFX_ARG_VERTICAL])
1523 g->anim_frames = anim_frames_per_col;
1527 if (g->anim_frames < 1) // frames must be at least 1
1530 g->anim_frames_per_line =
1531 (parameter[GFX_ARG_FRAMES_PER_LINE] != ARG_UNDEFINED_VALUE ?
1532 parameter[GFX_ARG_FRAMES_PER_LINE] : anim_frames_per_line);
1534 g->anim_delay = parameter[GFX_ARG_DELAY];
1535 if (g->anim_delay < 1) // delay must be at least 1
1538 g->anim_mode = parameter[GFX_ARG_ANIM_MODE];
1540 // automatically determine correct start frame, if not defined
1541 if (parameter[GFX_ARG_START_FRAME] == ARG_UNDEFINED_VALUE)
1542 g->anim_start_frame = 0;
1543 else if (g->anim_mode & ANIM_REVERSE)
1544 g->anim_start_frame = g->anim_frames - parameter[GFX_ARG_START_FRAME] - 1;
1546 g->anim_start_frame = parameter[GFX_ARG_START_FRAME];
1548 // animation synchronized with global frame counter, not move position
1549 g->anim_global_sync = parameter[GFX_ARG_GLOBAL_SYNC];
1551 // optional element for cloning crumble graphics
1552 if (parameter[GFX_ARG_CRUMBLED_LIKE] != ARG_UNDEFINED_VALUE)
1553 g->crumbled_like = parameter[GFX_ARG_CRUMBLED_LIKE];
1555 // optional element for cloning digging graphics
1556 if (parameter[GFX_ARG_DIGGABLE_LIKE] != ARG_UNDEFINED_VALUE)
1557 g->diggable_like = parameter[GFX_ARG_DIGGABLE_LIKE];
1559 // optional border size for "crumbling" diggable graphics
1560 if (parameter[GFX_ARG_BORDER_SIZE] != ARG_UNDEFINED_VALUE)
1561 g->border_size = parameter[GFX_ARG_BORDER_SIZE];
1563 // used for global animations and player "boring" and "sleeping" actions
1564 if (parameter[GFX_ARG_INIT_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1565 g->init_delay_fixed = parameter[GFX_ARG_INIT_DELAY_FIXED];
1566 if (parameter[GFX_ARG_INIT_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1567 g->init_delay_random = parameter[GFX_ARG_INIT_DELAY_RANDOM];
1568 if (parameter[GFX_ARG_ANIM_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1569 g->anim_delay_fixed = parameter[GFX_ARG_ANIM_DELAY_FIXED];
1570 if (parameter[GFX_ARG_ANIM_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1571 g->anim_delay_random = parameter[GFX_ARG_ANIM_DELAY_RANDOM];
1572 if (parameter[GFX_ARG_POST_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1573 g->post_delay_fixed = parameter[GFX_ARG_POST_DELAY_FIXED];
1574 if (parameter[GFX_ARG_POST_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1575 g->post_delay_random = parameter[GFX_ARG_POST_DELAY_RANDOM];
1577 // used for global animations
1578 if (parameter[GFX_ARG_INIT_EVENT] != ARG_UNDEFINED_VALUE)
1579 g->init_event = parameter[GFX_ARG_INIT_EVENT];
1580 if (parameter[GFX_ARG_ANIM_EVENT] != ARG_UNDEFINED_VALUE)
1581 g->anim_event = parameter[GFX_ARG_ANIM_EVENT];
1582 if (parameter[GFX_ARG_INIT_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1583 g->init_event_action = parameter[GFX_ARG_INIT_EVENT_ACTION];
1584 if (parameter[GFX_ARG_ANIM_EVENT_ACTION] != ARG_UNDEFINED_VALUE)
1585 g->anim_event_action = parameter[GFX_ARG_ANIM_EVENT_ACTION];
1586 if (parameter[GFX_ARG_INIT_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1587 g->init_delay_action = parameter[GFX_ARG_INIT_DELAY_ACTION];
1588 if (parameter[GFX_ARG_ANIM_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1589 g->anim_delay_action = parameter[GFX_ARG_ANIM_DELAY_ACTION];
1590 if (parameter[GFX_ARG_POST_DELAY_ACTION] != ARG_UNDEFINED_VALUE)
1591 g->post_delay_action = parameter[GFX_ARG_POST_DELAY_ACTION];
1593 // used for toon animations and global animations
1594 g->step_offset = parameter[GFX_ARG_STEP_OFFSET];
1595 g->step_xoffset = parameter[GFX_ARG_STEP_XOFFSET];
1596 g->step_yoffset = parameter[GFX_ARG_STEP_YOFFSET];
1597 g->step_delay = parameter[GFX_ARG_STEP_DELAY];
1598 g->direction = parameter[GFX_ARG_DIRECTION];
1599 g->position = parameter[GFX_ARG_POSITION];
1600 g->x = parameter[GFX_ARG_X]; // (may be uninitialized,
1601 g->y = parameter[GFX_ARG_Y]; // unlike src_x and src_y)
1603 if (g->step_delay < 1) // delay must be at least 1
1606 // this is only used for drawing font characters
1607 g->draw_xoffset = parameter[GFX_ARG_DRAW_XOFFSET];
1608 g->draw_yoffset = parameter[GFX_ARG_DRAW_YOFFSET];
1610 // use a different default value for global animations and toons
1611 if ((graphic >= IMG_GFX_GLOBAL_ANIM_1 && graphic <= IMG_GFX_GLOBAL_ANIM_8) ||
1612 (graphic >= IMG_TOON_1 && graphic <= IMG_TOON_20))
1613 g->draw_masked = TRUE;
1615 // this is used for drawing envelopes, global animations and toons
1616 if (parameter[GFX_ARG_DRAW_MASKED] != ARG_UNDEFINED_VALUE)
1617 g->draw_masked = parameter[GFX_ARG_DRAW_MASKED];
1619 // used for toon animations and global animations
1620 if (parameter[GFX_ARG_DRAW_ORDER] != ARG_UNDEFINED_VALUE)
1621 g->draw_order = parameter[GFX_ARG_DRAW_ORDER];
1623 // optional graphic for cloning all graphics settings
1624 if (parameter[GFX_ARG_CLONE_FROM] != ARG_UNDEFINED_VALUE)
1625 g->clone_from = parameter[GFX_ARG_CLONE_FROM];
1627 // optional settings for drawing title screens and title messages
1628 if (parameter[GFX_ARG_FADE_MODE] != ARG_UNDEFINED_VALUE)
1629 g->fade_mode = parameter[GFX_ARG_FADE_MODE];
1630 if (parameter[GFX_ARG_FADE_DELAY] != ARG_UNDEFINED_VALUE)
1631 g->fade_delay = parameter[GFX_ARG_FADE_DELAY];
1632 if (parameter[GFX_ARG_POST_DELAY] != ARG_UNDEFINED_VALUE)
1633 g->post_delay = parameter[GFX_ARG_POST_DELAY];
1634 if (parameter[GFX_ARG_AUTO_DELAY] != ARG_UNDEFINED_VALUE)
1635 g->auto_delay = parameter[GFX_ARG_AUTO_DELAY];
1636 if (parameter[GFX_ARG_AUTO_DELAY_UNIT] != ARG_UNDEFINED_VALUE)
1637 g->auto_delay_unit = parameter[GFX_ARG_AUTO_DELAY_UNIT];
1638 if (parameter[GFX_ARG_ALIGN] != ARG_UNDEFINED_VALUE)
1639 g->align = parameter[GFX_ARG_ALIGN];
1640 if (parameter[GFX_ARG_VALIGN] != ARG_UNDEFINED_VALUE)
1641 g->valign = parameter[GFX_ARG_VALIGN];
1642 if (parameter[GFX_ARG_SORT_PRIORITY] != ARG_UNDEFINED_VALUE)
1643 g->sort_priority = parameter[GFX_ARG_SORT_PRIORITY];
1645 if (parameter[GFX_ARG_CLASS] != ARG_UNDEFINED_VALUE)
1646 g->class = parameter[GFX_ARG_CLASS];
1647 if (parameter[GFX_ARG_STYLE] != ARG_UNDEFINED_VALUE)
1648 g->style = parameter[GFX_ARG_STYLE];
1650 // this is only used for drawing menu buttons and text
1651 g->active_xoffset = parameter[GFX_ARG_ACTIVE_XOFFSET];
1652 g->active_yoffset = parameter[GFX_ARG_ACTIVE_YOFFSET];
1653 g->pressed_xoffset = parameter[GFX_ARG_PRESSED_XOFFSET];
1654 g->pressed_yoffset = parameter[GFX_ARG_PRESSED_YOFFSET];
1657 static void set_graphic_parameters(int graphic)
1659 struct FileInfo *image = getImageListEntryFromImageID(graphic);
1660 char **parameter_raw = image->parameter;
1661 Bitmap **src_bitmaps = getBitmapsFromImageID(graphic);
1662 int parameter[NUM_GFX_ARGS];
1665 // if fallback to default artwork is done, also use the default parameters
1666 if (image->fallback_to_default)
1667 parameter_raw = image->default_parameter;
1669 // get integer values from string parameters
1670 for (i = 0; i < NUM_GFX_ARGS; i++)
1671 parameter[i] = get_graphic_parameter_value(parameter_raw[i],
1672 image_config_suffix[i].token,
1673 image_config_suffix[i].type);
1675 set_graphic_parameters_ext(graphic, parameter, src_bitmaps);
1677 UPDATE_BUSY_STATE();
1680 static void set_cloned_graphic_parameters(int graphic)
1682 int fallback_graphic = IMG_CHAR_EXCLAM;
1683 int max_num_images = getImageListSize();
1684 int clone_graphic = graphic_info[graphic].clone_from;
1685 int num_references_followed = 1;
1687 while (graphic_info[clone_graphic].clone_from != -1 &&
1688 num_references_followed < max_num_images)
1690 clone_graphic = graphic_info[clone_graphic].clone_from;
1692 num_references_followed++;
1695 if (num_references_followed >= max_num_images)
1698 Warn("error found in config file:");
1699 Warn("- config file: '%s'", getImageConfigFilename());
1700 Warn("- config token: '%s'", getTokenFromImageID(graphic));
1701 Warn("error: loop discovered when resolving cloned graphics");
1702 Warn("custom graphic rejected for this element/action");
1704 if (graphic == fallback_graphic)
1705 Fail("no fallback graphic available");
1707 Warn("fallback done to 'char_exclam' for this graphic");
1710 graphic_info[graphic] = graphic_info[fallback_graphic];
1714 graphic_info[graphic] = graphic_info[clone_graphic];
1715 graphic_info[graphic].clone_from = clone_graphic;
1719 static void InitGraphicInfo(void)
1721 int fallback_graphic = IMG_CHAR_EXCLAM;
1722 int num_images = getImageListSize();
1725 // use image size as default values for width and height for these images
1726 static int full_size_graphics[] =
1729 IMG_GLOBAL_BORDER_MAIN,
1730 IMG_GLOBAL_BORDER_SCORES,
1731 IMG_GLOBAL_BORDER_EDITOR,
1732 IMG_GLOBAL_BORDER_PLAYING,
1735 IMG_BACKGROUND_ENVELOPE_1,
1736 IMG_BACKGROUND_ENVELOPE_2,
1737 IMG_BACKGROUND_ENVELOPE_3,
1738 IMG_BACKGROUND_ENVELOPE_4,
1739 IMG_BACKGROUND_REQUEST,
1742 IMG_BACKGROUND_LOADING_INITIAL,
1743 IMG_BACKGROUND_LOADING,
1744 IMG_BACKGROUND_TITLE_INITIAL,
1745 IMG_BACKGROUND_TITLE,
1746 IMG_BACKGROUND_MAIN,
1747 IMG_BACKGROUND_NAMES,
1748 IMG_BACKGROUND_LEVELS,
1749 IMG_BACKGROUND_LEVELNR,
1750 IMG_BACKGROUND_SCORES,
1751 IMG_BACKGROUND_SCOREINFO,
1752 IMG_BACKGROUND_EDITOR,
1753 IMG_BACKGROUND_INFO,
1754 IMG_BACKGROUND_INFO_ELEMENTS,
1755 IMG_BACKGROUND_INFO_MUSIC,
1756 IMG_BACKGROUND_INFO_CREDITS,
1757 IMG_BACKGROUND_INFO_PROGRAM,
1758 IMG_BACKGROUND_INFO_VERSION,
1759 IMG_BACKGROUND_INFO_LEVELSET,
1760 IMG_BACKGROUND_SETUP,
1761 IMG_BACKGROUND_PLAYING,
1762 IMG_BACKGROUND_DOOR,
1763 IMG_BACKGROUND_TAPE,
1764 IMG_BACKGROUND_PANEL,
1765 IMG_BACKGROUND_PALETTE,
1766 IMG_BACKGROUND_TOOLBOX,
1768 IMG_TITLESCREEN_INITIAL_1,
1769 IMG_TITLESCREEN_INITIAL_2,
1770 IMG_TITLESCREEN_INITIAL_3,
1771 IMG_TITLESCREEN_INITIAL_4,
1772 IMG_TITLESCREEN_INITIAL_5,
1779 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_1,
1780 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_2,
1781 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_3,
1782 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_4,
1783 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_5,
1784 IMG_BACKGROUND_TITLEMESSAGE_1,
1785 IMG_BACKGROUND_TITLEMESSAGE_2,
1786 IMG_BACKGROUND_TITLEMESSAGE_3,
1787 IMG_BACKGROUND_TITLEMESSAGE_4,
1788 IMG_BACKGROUND_TITLEMESSAGE_5,
1793 FreeGlobalAnimEventInfo();
1795 checked_free(graphic_info);
1797 graphic_info = checked_calloc(num_images * sizeof(struct GraphicInfo));
1799 // initialize "use_image_size" flag with default value
1800 for (i = 0; i < num_images; i++)
1801 graphic_info[i].use_image_size = FALSE;
1803 // initialize "use_image_size" flag from static configuration above
1804 for (i = 0; full_size_graphics[i] != -1; i++)
1805 graphic_info[full_size_graphics[i]].use_image_size = TRUE;
1807 // first set all graphic paramaters ...
1808 for (i = 0; i < num_images; i++)
1809 set_graphic_parameters(i);
1811 // ... then copy these parameters for cloned graphics
1812 for (i = 0; i < num_images; i++)
1813 if (graphic_info[i].clone_from != -1)
1814 set_cloned_graphic_parameters(i);
1816 for (i = 0; i < num_images; i++)
1818 Bitmap *src_bitmap = graphic_info[i].bitmap;
1822 int src_bitmap_width, src_bitmap_height;
1824 // now check if no animation frames are outside of the loaded image
1826 if (graphic_info[i].bitmap == NULL)
1827 continue; // skip check for optional images that are undefined
1829 // get image size (this can differ from the standard element tile size!)
1830 width = graphic_info[i].width;
1831 height = graphic_info[i].height;
1833 // get final bitmap size (with scaling, but without small images)
1834 src_bitmap_width = graphic_info[i].src_image_width;
1835 src_bitmap_height = graphic_info[i].src_image_height;
1837 // check if first animation frame is inside specified bitmap
1839 // do not use getGraphicSourceXY() here to get position of first frame;
1840 // this avoids calculating wrong start position for out-of-bounds frame
1841 src_x = graphic_info[i].src_x;
1842 src_y = graphic_info[i].src_y;
1844 if (program.headless)
1847 if (src_x < 0 || src_y < 0 ||
1848 src_x + width > src_bitmap_width ||
1849 src_y + height > src_bitmap_height)
1852 Warn("error found in config file:");
1853 Warn("- config file: '%s'", getImageConfigFilename());
1854 Warn("- config token: '%s'", getTokenFromImageID(i));
1855 Warn("- image file: '%s'", src_bitmap->source_filename);
1856 Warn("- frame size: %d, %d", width, height);
1857 Warn("error: first animation frame out of bounds (%d, %d) [%d, %d]",
1858 src_x, src_y, src_bitmap_width, src_bitmap_height);
1859 Warn("custom graphic rejected for this element/action");
1861 if (i == fallback_graphic)
1862 Fail("no fallback graphic available");
1864 Warn("fallback done to 'char_exclam' for this graphic");
1867 graphic_info[i] = graphic_info[fallback_graphic];
1869 // if first frame out of bounds, do not check last frame anymore
1873 // check if last animation frame is inside specified bitmap
1875 last_frame = graphic_info[i].anim_frames - 1;
1876 getGraphicSourceXY(i, last_frame, &src_x, &src_y, FALSE);
1878 if (src_x < 0 || src_y < 0 ||
1879 src_x + width > src_bitmap_width ||
1880 src_y + height > src_bitmap_height)
1883 Warn("error found in config file:");
1884 Warn("- config file: '%s'", getImageConfigFilename());
1885 Warn("- config token: '%s'", getTokenFromImageID(i));
1886 Warn("- image file: '%s'", src_bitmap->source_filename);
1887 Warn("- frame size: %d, %d", width, height);
1888 Warn("error: last animation frame (%d) out of bounds (%d, %d) [%d, %d]",
1889 last_frame, src_x, src_y, src_bitmap_width, src_bitmap_height);
1890 Warn("custom graphic rejected for this element/action");
1892 if (i == fallback_graphic)
1893 Fail("no fallback graphic available");
1895 Warn("fallback done to 'char_exclam' for this graphic");
1898 graphic_info[i] = graphic_info[fallback_graphic];
1903 static void InitGraphicCompatibilityInfo(void)
1905 struct FileInfo *fi_global_door =
1906 getImageListEntryFromImageID(IMG_GLOBAL_DOOR);
1907 int num_images = getImageListSize();
1910 /* the following compatibility handling is needed for the following case:
1911 versions up to 3.3.0.0 used one large bitmap "global.door" for various
1912 graphics mainly used for door and panel graphics, like editor, tape and
1913 in-game buttons with hard-coded bitmap positions and button sizes; as
1914 these graphics now have individual definitions, redefining "global.door"
1915 to change all these graphics at once like before does not work anymore
1916 (because all those individual definitions still have their default values);
1917 to solve this, remap all those individual definitions that are not
1918 redefined to the new bitmap of "global.door" if it was redefined */
1920 // special compatibility handling if image "global.door" was redefined
1921 if (fi_global_door->redefined)
1923 for (i = 0; i < num_images; i++)
1925 struct FileInfo *fi = getImageListEntryFromImageID(i);
1927 // process only those images that still use the default settings
1930 // process all images which default to same image as "global.door"
1931 if (strEqual(fi->default_filename, fi_global_door->default_filename))
1933 // skip all images that are cloned from images that default to same
1934 // image as "global.door", but that are redefined to something else
1935 if (graphic_info[i].clone_from != -1)
1937 int cloned_graphic = graphic_info[i].clone_from;
1939 if (getImageListEntryFromImageID(cloned_graphic)->redefined)
1944 Debug("init:InitGraphicCompatibilityInfo",
1945 "special treatment needed for token '%s'", fi->token);
1948 graphic_info[i].bitmaps = graphic_info[IMG_GLOBAL_DOOR].bitmaps;
1949 graphic_info[i].bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
1955 // special compatibility handling for "Snake Bite" graphics set
1956 if (strPrefix(leveldir_current->identifier, "snake_bite"))
1958 Bitmap *bitmap = graphic_info[IMG_BACKGROUND_SCORES].bitmap;
1960 BlitBitmap(bitmap, bitmap, 18, 66, 32, 480, 50, 66);
1961 BlitBitmap(bitmap, bitmap, 466, 66, 32, 480, 434, 66);
1963 ClearRectangle(bitmap, 2, 66, 32, 480);
1964 ClearRectangle(bitmap, 514, 66, 32, 480);
1967 InitGraphicCompatibilityInfo_Doors();
1970 static void InitElementSoundInfo(void)
1972 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
1973 int num_property_mappings = getSoundListPropertyMappingSize();
1976 // set values to -1 to identify later as "uninitialized" values
1977 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1978 for (act = 0; act < NUM_ACTIONS; act++)
1979 element_info[i].sound[act] = -1;
1981 // initialize element/sound mapping from static configuration
1982 for (i = 0; element_to_sound[i].element > -1; i++)
1984 int element = element_to_sound[i].element;
1985 int action = element_to_sound[i].action;
1986 int sound = element_to_sound[i].sound;
1987 boolean is_class = element_to_sound[i].is_class;
1990 action = ACTION_DEFAULT;
1993 element_info[element].sound[action] = sound;
1995 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
1996 if (strEqual(element_info[j].class_name,
1997 element_info[element].class_name))
1998 element_info[j].sound[action] = sound;
2001 // initialize element class/sound mapping from dynamic configuration
2002 for (i = 0; i < num_property_mappings; i++)
2004 int element_class = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
2005 int action = property_mapping[i].ext1_index;
2006 int sound = property_mapping[i].artwork_index;
2008 if (element_class < 0 || element_class >= MAX_NUM_ELEMENTS)
2012 action = ACTION_DEFAULT;
2014 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2015 if (strEqual(element_info[j].class_name,
2016 element_info[element_class].class_name))
2017 element_info[j].sound[action] = sound;
2020 // initialize element/sound mapping from dynamic configuration
2021 for (i = 0; i < num_property_mappings; i++)
2023 int element = property_mapping[i].base_index;
2024 int action = property_mapping[i].ext1_index;
2025 int sound = property_mapping[i].artwork_index;
2027 if (element >= MAX_NUM_ELEMENTS)
2031 action = ACTION_DEFAULT;
2033 element_info[element].sound[action] = sound;
2036 // now set all '-1' values to element specific default values
2037 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2039 for (act = 0; act < NUM_ACTIONS; act++)
2041 // generic default action sound (defined by "[default]" directive)
2042 int default_action_sound = element_info[EL_DEFAULT].sound[act];
2044 // look for special default action sound (classic game specific)
2045 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].sound[act] != -1)
2046 default_action_sound = element_info[EL_BD_DEFAULT].sound[act];
2047 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].sound[act] != -1)
2048 default_action_sound = element_info[EL_SP_DEFAULT].sound[act];
2049 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].sound[act] != -1)
2050 default_action_sound = element_info[EL_SB_DEFAULT].sound[act];
2051 if (IS_MM_ELEMENT(i) && element_info[EL_MM_DEFAULT].sound[act] != -1)
2052 default_action_sound = element_info[EL_MM_DEFAULT].sound[act];
2054 // !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!!
2055 // !!! make this better !!!
2056 if (i == EL_EMPTY_SPACE)
2057 default_action_sound = element_info[EL_DEFAULT].sound[act];
2059 // no sound for this specific action -- use default action sound
2060 if (element_info[i].sound[act] == -1)
2061 element_info[i].sound[act] = default_action_sound;
2065 // copy sound settings to some elements that are only stored in level file
2066 // in native R'n'D levels, but are used by game engine in native EM levels
2067 for (i = 0; copy_properties[i][0] != -1; i++)
2068 for (j = 1; j <= 4; j++)
2069 for (act = 0; act < NUM_ACTIONS; act++)
2070 element_info[copy_properties[i][j]].sound[act] =
2071 element_info[copy_properties[i][0]].sound[act];
2074 static void InitGameModeSoundInfo(void)
2078 // set values to -1 to identify later as "uninitialized" values
2079 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2082 // initialize gamemode/sound mapping from static configuration
2083 for (i = 0; gamemode_to_sound[i].sound > -1; i++)
2085 int gamemode = gamemode_to_sound[i].gamemode;
2086 int sound = gamemode_to_sound[i].sound;
2089 gamemode = GAME_MODE_DEFAULT;
2091 menu.sound[gamemode] = sound;
2094 // now set all '-1' values to levelset specific default values
2095 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2096 if (menu.sound[i] == -1)
2097 menu.sound[i] = menu.sound[GAME_MODE_DEFAULT];
2100 static void set_sound_parameters(int sound, char **parameter_raw)
2102 int parameter[NUM_SND_ARGS];
2105 // get integer values from string parameters
2106 for (i = 0; i < NUM_SND_ARGS; i++)
2108 get_parameter_value(parameter_raw[i],
2109 sound_config_suffix[i].token,
2110 sound_config_suffix[i].type);
2112 // explicit loop mode setting in configuration overrides default value
2113 if (parameter[SND_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2114 sound_info[sound].loop = parameter[SND_ARG_MODE_LOOP];
2116 // sound volume to change the original volume when loading the sound file
2117 sound_info[sound].volume = parameter[SND_ARG_VOLUME];
2119 // sound priority to give certain sounds a higher or lower priority
2120 sound_info[sound].priority = parameter[SND_ARG_PRIORITY];
2123 static void InitSoundInfo(void)
2125 int *sound_effect_properties;
2126 int num_sounds = getSoundListSize();
2129 checked_free(sound_info);
2131 sound_effect_properties = checked_calloc(num_sounds * sizeof(int));
2132 sound_info = checked_calloc(num_sounds * sizeof(struct SoundInfo));
2134 // initialize sound effect for all elements to "no sound"
2135 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2136 for (j = 0; j < NUM_ACTIONS; j++)
2137 element_info[i].sound[j] = SND_UNDEFINED;
2139 for (i = 0; i < num_sounds; i++)
2141 struct FileInfo *sound = getSoundListEntry(i);
2142 int len_effect_text = strlen(sound->token);
2144 sound_effect_properties[i] = ACTION_OTHER;
2145 sound_info[i].loop = FALSE; // default: play sound only once
2147 // determine all loop sounds and identify certain sound classes
2149 for (j = 0; element_action_info[j].suffix; j++)
2151 int len_action_text = strlen(element_action_info[j].suffix);
2153 if (len_action_text < len_effect_text &&
2154 strEqual(&sound->token[len_effect_text - len_action_text],
2155 element_action_info[j].suffix))
2157 sound_effect_properties[i] = element_action_info[j].value;
2158 sound_info[i].loop = element_action_info[j].is_loop_sound;
2164 // associate elements and some selected sound actions
2166 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2168 if (element_info[j].class_name)
2170 int len_class_text = strlen(element_info[j].class_name);
2172 if (len_class_text + 1 < len_effect_text &&
2173 strncmp(sound->token,
2174 element_info[j].class_name, len_class_text) == 0 &&
2175 sound->token[len_class_text] == '.')
2177 int sound_action_value = sound_effect_properties[i];
2179 element_info[j].sound[sound_action_value] = i;
2184 set_sound_parameters(i, sound->parameter);
2187 free(sound_effect_properties);
2190 static void InitGameModeMusicInfo(void)
2192 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
2193 int num_property_mappings = getMusicListPropertyMappingSize();
2194 int default_levelset_music = -1;
2197 // set values to -1 to identify later as "uninitialized" values
2198 for (i = 0; i < MAX_LEVELS; i++)
2199 levelset.music[i] = -1;
2200 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2203 // initialize gamemode/music mapping from static configuration
2204 for (i = 0; gamemode_to_music[i].music > -1; i++)
2206 int gamemode = gamemode_to_music[i].gamemode;
2207 int music = gamemode_to_music[i].music;
2210 gamemode = GAME_MODE_DEFAULT;
2212 menu.music[gamemode] = music;
2215 // initialize gamemode/music mapping from dynamic configuration
2216 for (i = 0; i < num_property_mappings; i++)
2218 int prefix = property_mapping[i].base_index;
2219 int gamemode = property_mapping[i].ext2_index;
2220 int level = property_mapping[i].ext3_index;
2221 int music = property_mapping[i].artwork_index;
2223 if (prefix < 0 || prefix >= NUM_MUSIC_PREFIXES)
2227 gamemode = GAME_MODE_DEFAULT;
2229 // level specific music only allowed for in-game music
2230 if (level != -1 && gamemode == GAME_MODE_DEFAULT)
2231 gamemode = GAME_MODE_PLAYING;
2236 default_levelset_music = music;
2239 if (gamemode == GAME_MODE_PLAYING || gamemode == GAME_MODE_DEFAULT)
2240 levelset.music[level] = music;
2241 if (gamemode != GAME_MODE_PLAYING)
2242 menu.music[gamemode] = music;
2245 // now set all '-1' values to menu specific default values
2246 // (undefined values of "levelset.music[]" might stay at "-1" to
2247 // allow dynamic selection of music files from music directory!)
2248 for (i = 0; i < MAX_LEVELS; i++)
2249 if (levelset.music[i] == -1)
2250 levelset.music[i] = default_levelset_music;
2251 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
2252 if (menu.music[i] == -1)
2253 menu.music[i] = menu.music[GAME_MODE_DEFAULT];
2256 static void set_music_parameters(int music, char **parameter_raw)
2258 int parameter[NUM_MUS_ARGS];
2261 // get integer values from string parameters
2262 for (i = 0; i < NUM_MUS_ARGS; i++)
2264 get_parameter_value(parameter_raw[i],
2265 music_config_suffix[i].token,
2266 music_config_suffix[i].type);
2268 // explicit loop mode setting in configuration overrides default value
2269 if (parameter[MUS_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
2270 music_info[music].loop = parameter[MUS_ARG_MODE_LOOP];
2273 static void InitMusicInfo(void)
2275 int num_music = getMusicListSize();
2278 checked_free(music_info);
2280 music_info = checked_calloc(num_music * sizeof(struct MusicInfo));
2282 for (i = 0; i < num_music; i++)
2284 struct FileInfo *music = getMusicListEntry(i);
2285 int len_music_text = strlen(music->token);
2287 music_info[i].loop = TRUE; // default: play music in loop mode
2289 // determine all loop music
2291 for (j = 0; music_prefix_info[j].prefix; j++)
2293 int len_prefix_text = strlen(music_prefix_info[j].prefix);
2295 if (len_prefix_text < len_music_text &&
2296 strncmp(music->token,
2297 music_prefix_info[j].prefix, len_prefix_text) == 0)
2299 music_info[i].loop = music_prefix_info[j].is_loop_music;
2305 set_music_parameters(i, music->parameter);
2310 static void InitGameInfoFromArtworkInfo(void)
2312 // special case: store initial value of custom artwork setting
2313 game.use_masked_elements_initial = game.use_masked_elements;
2316 static void ReinitializeGraphics(void)
2318 print_timestamp_init("ReinitializeGraphics");
2320 InitGfxTileSizeInfo(game.tile_size, TILESIZE);
2322 InitGraphicInfo(); // graphic properties mapping
2323 print_timestamp_time("InitGraphicInfo");
2324 InitElementGraphicInfo(); // element game graphic mapping
2325 print_timestamp_time("InitElementGraphicInfo");
2326 InitElementSpecialGraphicInfo(); // element special graphic mapping
2327 print_timestamp_time("InitElementSpecialGraphicInfo");
2329 InitElementSmallImages(); // scale elements to all needed sizes
2330 print_timestamp_time("InitElementSmallImages");
2331 InitScaledImages(); // scale all other images, if needed
2332 print_timestamp_time("InitScaledImages");
2333 InitBitmapPointers(); // set standard size bitmap pointers
2334 print_timestamp_time("InitBitmapPointers");
2335 InitFontGraphicInfo(); // initialize text drawing functions
2336 print_timestamp_time("InitFontGraphicInfo");
2337 InitGlobalAnimGraphicInfo(); // initialize global animation config
2338 print_timestamp_time("InitGlobalAnimGraphicInfo");
2340 InitImageTextures(); // create textures for certain images
2341 print_timestamp_time("InitImageTextures");
2343 InitGraphicInfo_EM(); // graphic mapping for EM engine
2344 print_timestamp_time("InitGraphicInfo_EM");
2346 InitGraphicCompatibilityInfo();
2347 print_timestamp_time("InitGraphicCompatibilityInfo");
2350 print_timestamp_time("InitGadgets");
2352 print_timestamp_time("InitDoors");
2354 InitGameInfoFromArtworkInfo();
2356 print_timestamp_done("ReinitializeGraphics");
2359 static void ReinitializeSounds(void)
2361 InitSoundInfo(); // sound properties mapping
2362 InitElementSoundInfo(); // element game sound mapping
2363 InitGameModeSoundInfo(); // game mode sound mapping
2364 InitGlobalAnimSoundInfo(); // global animation sound settings
2366 InitPlayLevelSound(); // internal game sound settings
2369 static void ReinitializeMusic(void)
2371 InitMusicInfo(); // music properties mapping
2372 InitGameModeMusicInfo(); // game mode music mapping
2373 InitGlobalAnimMusicInfo(); // global animation music settings
2376 static int get_special_property_bit(int element, int property_bit_nr)
2378 struct PropertyBitInfo
2384 static struct PropertyBitInfo pb_can_move_into_acid[] =
2386 // the player may be able fall into acid when gravity is activated
2391 { EL_SP_MURPHY, 0 },
2392 { EL_SOKOBAN_FIELD_PLAYER, 0 },
2394 // all elements that can move may be able to also move into acid
2397 { EL_BUG_RIGHT, 1 },
2400 { EL_SPACESHIP, 2 },
2401 { EL_SPACESHIP_LEFT, 2 },
2402 { EL_SPACESHIP_RIGHT, 2 },
2403 { EL_SPACESHIP_UP, 2 },
2404 { EL_SPACESHIP_DOWN, 2 },
2405 { EL_BD_BUTTERFLY, 3 },
2406 { EL_BD_BUTTERFLY_LEFT, 3 },
2407 { EL_BD_BUTTERFLY_RIGHT, 3 },
2408 { EL_BD_BUTTERFLY_UP, 3 },
2409 { EL_BD_BUTTERFLY_DOWN, 3 },
2410 { EL_BD_FIREFLY, 4 },
2411 { EL_BD_FIREFLY_LEFT, 4 },
2412 { EL_BD_FIREFLY_RIGHT, 4 },
2413 { EL_BD_FIREFLY_UP, 4 },
2414 { EL_BD_FIREFLY_DOWN, 4 },
2416 { EL_YAMYAM_LEFT, 5 },
2417 { EL_YAMYAM_RIGHT, 5 },
2418 { EL_YAMYAM_UP, 5 },
2419 { EL_YAMYAM_DOWN, 5 },
2420 { EL_DARK_YAMYAM, 6 },
2423 { EL_PACMAN_LEFT, 8 },
2424 { EL_PACMAN_RIGHT, 8 },
2425 { EL_PACMAN_UP, 8 },
2426 { EL_PACMAN_DOWN, 8 },
2428 { EL_MOLE_LEFT, 9 },
2429 { EL_MOLE_RIGHT, 9 },
2431 { EL_MOLE_DOWN, 9 },
2435 { EL_SATELLITE, 13 },
2436 { EL_SP_SNIKSNAK, 14 },
2437 { EL_SP_ELECTRON, 15 },
2440 { EL_SPRING_LEFT, 17 },
2441 { EL_SPRING_RIGHT, 17 },
2442 { EL_EMC_ANDROID, 18 },
2447 static struct PropertyBitInfo pb_dont_collide_with[] =
2449 { EL_SP_SNIKSNAK, 0 },
2450 { EL_SP_ELECTRON, 1 },
2458 struct PropertyBitInfo *pb_info;
2461 { EP_CAN_MOVE_INTO_ACID, pb_can_move_into_acid },
2462 { EP_DONT_COLLIDE_WITH, pb_dont_collide_with },
2467 struct PropertyBitInfo *pb_info = NULL;
2470 for (i = 0; pb_definition[i].bit_nr != -1; i++)
2471 if (pb_definition[i].bit_nr == property_bit_nr)
2472 pb_info = pb_definition[i].pb_info;
2474 if (pb_info == NULL)
2477 for (i = 0; pb_info[i].element != -1; i++)
2478 if (pb_info[i].element == element)
2479 return pb_info[i].bit_nr;
2484 void setBitfieldProperty(int *bitfield, int property_bit_nr, int element,
2485 boolean property_value)
2487 int bit_nr = get_special_property_bit(element, property_bit_nr);
2492 *bitfield |= (1 << bit_nr);
2494 *bitfield &= ~(1 << bit_nr);
2498 boolean getBitfieldProperty(int *bitfield, int property_bit_nr, int element)
2500 int bit_nr = get_special_property_bit(element, property_bit_nr);
2503 return ((*bitfield & (1 << bit_nr)) != 0);
2508 static void ResolveGroupElementExt(int group_element, int recursion_depth)
2510 static int group_nr;
2511 static struct ElementGroupInfo *group;
2512 struct ElementGroupInfo *actual_group = element_info[group_element].group;
2515 if (actual_group == NULL) // not yet initialized
2518 if (recursion_depth > NUM_GROUP_ELEMENTS) // recursion too deep
2520 Warn("recursion too deep when resolving group element %d",
2521 group_element - EL_GROUP_START + 1);
2523 // replace element which caused too deep recursion by question mark
2524 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
2529 if (recursion_depth == 0) // initialization
2531 group = actual_group;
2532 group_nr = GROUP_NR(group_element);
2534 group->num_elements_resolved = 0;
2535 group->choice_pos = 0;
2537 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2538 element_info[i].in_group[group_nr] = FALSE;
2541 for (i = 0; i < actual_group->num_elements; i++)
2543 int element = actual_group->element[i];
2545 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
2548 if (IS_GROUP_ELEMENT(element))
2549 ResolveGroupElementExt(element, recursion_depth + 1);
2552 group->element_resolved[group->num_elements_resolved++] = element;
2553 element_info[element].in_group[group_nr] = TRUE;
2558 void ResolveGroupElement(int group_element)
2560 ResolveGroupElementExt(group_element, 0);
2563 void InitElementPropertiesStatic(void)
2565 static boolean clipboard_elements_initialized = FALSE;
2567 static int ep_diggable[] =
2572 EL_SP_BUGGY_BASE_ACTIVATING,
2575 EL_INVISIBLE_SAND_ACTIVE,
2578 // !!! currently not diggable, but handled by 'ep_dont_run_into' !!!
2579 // (if amoeba can grow into anything diggable, maybe keep these out)
2584 EL_SP_BUGGY_BASE_ACTIVE,
2591 static int ep_collectible_only[] =
2613 EL_DYNABOMB_INCREASE_NUMBER,
2614 EL_DYNABOMB_INCREASE_SIZE,
2615 EL_DYNABOMB_INCREASE_POWER,
2633 // !!! handle separately !!!
2634 EL_DC_LANDMINE, // deadly when running into, but can be snapped
2640 static int ep_dont_run_into[] =
2642 // same elements as in 'ep_dont_touch'
2648 // same elements as in 'ep_dont_collide_with'
2660 // !!! maybe this should better be handled by 'ep_diggable' !!!
2665 EL_SP_BUGGY_BASE_ACTIVE,
2672 static int ep_dont_collide_with[] =
2674 // same elements as in 'ep_dont_touch'
2691 static int ep_dont_touch[] =
2701 static int ep_indestructible[] =
2705 EL_ACID_POOL_TOPLEFT,
2706 EL_ACID_POOL_TOPRIGHT,
2707 EL_ACID_POOL_BOTTOMLEFT,
2708 EL_ACID_POOL_BOTTOM,
2709 EL_ACID_POOL_BOTTOMRIGHT,
2710 EL_SP_HARDWARE_GRAY,
2711 EL_SP_HARDWARE_GREEN,
2712 EL_SP_HARDWARE_BLUE,
2714 EL_SP_HARDWARE_YELLOW,
2715 EL_SP_HARDWARE_BASE_1,
2716 EL_SP_HARDWARE_BASE_2,
2717 EL_SP_HARDWARE_BASE_3,
2718 EL_SP_HARDWARE_BASE_4,
2719 EL_SP_HARDWARE_BASE_5,
2720 EL_SP_HARDWARE_BASE_6,
2721 EL_INVISIBLE_STEELWALL,
2722 EL_INVISIBLE_STEELWALL_ACTIVE,
2723 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2724 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
2725 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
2726 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2727 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
2728 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
2729 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2730 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
2731 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
2732 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
2733 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
2734 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
2736 EL_LIGHT_SWITCH_ACTIVE,
2737 EL_SIGN_EXCLAMATION,
2738 EL_SIGN_RADIOACTIVITY,
2745 EL_SIGN_ENTRY_FORBIDDEN,
2746 EL_SIGN_EMERGENCY_EXIT,
2754 EL_STEEL_EXIT_CLOSED,
2756 EL_STEEL_EXIT_OPENING,
2757 EL_STEEL_EXIT_CLOSING,
2758 EL_EM_STEEL_EXIT_CLOSED,
2759 EL_EM_STEEL_EXIT_OPEN,
2760 EL_EM_STEEL_EXIT_OPENING,
2761 EL_EM_STEEL_EXIT_CLOSING,
2762 EL_DC_STEELWALL_1_LEFT,
2763 EL_DC_STEELWALL_1_RIGHT,
2764 EL_DC_STEELWALL_1_TOP,
2765 EL_DC_STEELWALL_1_BOTTOM,
2766 EL_DC_STEELWALL_1_HORIZONTAL,
2767 EL_DC_STEELWALL_1_VERTICAL,
2768 EL_DC_STEELWALL_1_TOPLEFT,
2769 EL_DC_STEELWALL_1_TOPRIGHT,
2770 EL_DC_STEELWALL_1_BOTTOMLEFT,
2771 EL_DC_STEELWALL_1_BOTTOMRIGHT,
2772 EL_DC_STEELWALL_1_TOPLEFT_2,
2773 EL_DC_STEELWALL_1_TOPRIGHT_2,
2774 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
2775 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
2776 EL_DC_STEELWALL_2_LEFT,
2777 EL_DC_STEELWALL_2_RIGHT,
2778 EL_DC_STEELWALL_2_TOP,
2779 EL_DC_STEELWALL_2_BOTTOM,
2780 EL_DC_STEELWALL_2_HORIZONTAL,
2781 EL_DC_STEELWALL_2_VERTICAL,
2782 EL_DC_STEELWALL_2_MIDDLE,
2783 EL_DC_STEELWALL_2_SINGLE,
2784 EL_STEELWALL_SLIPPERY,
2798 EL_GATE_1_GRAY_ACTIVE,
2799 EL_GATE_2_GRAY_ACTIVE,
2800 EL_GATE_3_GRAY_ACTIVE,
2801 EL_GATE_4_GRAY_ACTIVE,
2810 EL_EM_GATE_1_GRAY_ACTIVE,
2811 EL_EM_GATE_2_GRAY_ACTIVE,
2812 EL_EM_GATE_3_GRAY_ACTIVE,
2813 EL_EM_GATE_4_GRAY_ACTIVE,
2822 EL_EMC_GATE_5_GRAY_ACTIVE,
2823 EL_EMC_GATE_6_GRAY_ACTIVE,
2824 EL_EMC_GATE_7_GRAY_ACTIVE,
2825 EL_EMC_GATE_8_GRAY_ACTIVE,
2827 EL_DC_GATE_WHITE_GRAY,
2828 EL_DC_GATE_WHITE_GRAY_ACTIVE,
2829 EL_DC_GATE_FAKE_GRAY,
2831 EL_SWITCHGATE_OPENING,
2832 EL_SWITCHGATE_CLOSED,
2833 EL_SWITCHGATE_CLOSING,
2834 EL_DC_SWITCHGATE_SWITCH_UP,
2835 EL_DC_SWITCHGATE_SWITCH_DOWN,
2837 EL_TIMEGATE_OPENING,
2839 EL_TIMEGATE_CLOSING,
2840 EL_DC_TIMEGATE_SWITCH,
2841 EL_DC_TIMEGATE_SWITCH_ACTIVE,
2845 EL_TUBE_VERTICAL_LEFT,
2846 EL_TUBE_VERTICAL_RIGHT,
2847 EL_TUBE_HORIZONTAL_UP,
2848 EL_TUBE_HORIZONTAL_DOWN,
2853 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
2854 EL_EXPANDABLE_STEELWALL_VERTICAL,
2855 EL_EXPANDABLE_STEELWALL_ANY,
2860 static int ep_slippery[] =
2874 EL_ROBOT_WHEEL_ACTIVE,
2880 EL_ACID_POOL_TOPLEFT,
2881 EL_ACID_POOL_TOPRIGHT,
2891 EL_STEELWALL_SLIPPERY,
2894 EL_EMC_WALL_SLIPPERY_1,
2895 EL_EMC_WALL_SLIPPERY_2,
2896 EL_EMC_WALL_SLIPPERY_3,
2897 EL_EMC_WALL_SLIPPERY_4,
2899 EL_EMC_MAGIC_BALL_ACTIVE,
2904 static int ep_can_change[] =
2909 static int ep_can_move[] =
2911 // same elements as in 'pb_can_move_into_acid'
2934 static int ep_can_fall[] =
2949 EL_QUICKSAND_FAST_FULL,
2951 EL_BD_MAGIC_WALL_FULL,
2952 EL_DC_MAGIC_WALL_FULL,
2966 static int ep_can_smash_player[] =
2992 static int ep_can_smash_enemies[] =
3001 static int ep_can_smash_everything[] =
3010 static int ep_explodes_by_fire[] =
3012 // same elements as in 'ep_explodes_impact'
3017 // same elements as in 'ep_explodes_smashed'
3027 EL_EM_DYNAMITE_ACTIVE,
3028 EL_DYNABOMB_PLAYER_1_ACTIVE,
3029 EL_DYNABOMB_PLAYER_2_ACTIVE,
3030 EL_DYNABOMB_PLAYER_3_ACTIVE,
3031 EL_DYNABOMB_PLAYER_4_ACTIVE,
3032 EL_DYNABOMB_INCREASE_NUMBER,
3033 EL_DYNABOMB_INCREASE_SIZE,
3034 EL_DYNABOMB_INCREASE_POWER,
3035 EL_SP_DISK_RED_ACTIVE,
3049 static int ep_explodes_smashed[] =
3051 // same elements as in 'ep_explodes_impact'
3065 static int ep_explodes_impact[] =
3074 static int ep_walkable_over[] =
3094 EL_SOKOBAN_FIELD_EMPTY,
3101 EL_EM_STEEL_EXIT_OPEN,
3102 EL_EM_STEEL_EXIT_OPENING,
3111 EL_GATE_1_GRAY_ACTIVE,
3112 EL_GATE_2_GRAY_ACTIVE,
3113 EL_GATE_3_GRAY_ACTIVE,
3114 EL_GATE_4_GRAY_ACTIVE,
3122 static int ep_walkable_inside[] =
3127 EL_TUBE_VERTICAL_LEFT,
3128 EL_TUBE_VERTICAL_RIGHT,
3129 EL_TUBE_HORIZONTAL_UP,
3130 EL_TUBE_HORIZONTAL_DOWN,
3139 static int ep_walkable_under[] =
3144 static int ep_passable_over[] =
3154 EL_EM_GATE_1_GRAY_ACTIVE,
3155 EL_EM_GATE_2_GRAY_ACTIVE,
3156 EL_EM_GATE_3_GRAY_ACTIVE,
3157 EL_EM_GATE_4_GRAY_ACTIVE,
3166 EL_EMC_GATE_5_GRAY_ACTIVE,
3167 EL_EMC_GATE_6_GRAY_ACTIVE,
3168 EL_EMC_GATE_7_GRAY_ACTIVE,
3169 EL_EMC_GATE_8_GRAY_ACTIVE,
3171 EL_DC_GATE_WHITE_GRAY,
3172 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3179 static int ep_passable_inside[] =
3185 EL_SP_PORT_HORIZONTAL,
3186 EL_SP_PORT_VERTICAL,
3188 EL_SP_GRAVITY_PORT_LEFT,
3189 EL_SP_GRAVITY_PORT_RIGHT,
3190 EL_SP_GRAVITY_PORT_UP,
3191 EL_SP_GRAVITY_PORT_DOWN,
3192 EL_SP_GRAVITY_ON_PORT_LEFT,
3193 EL_SP_GRAVITY_ON_PORT_RIGHT,
3194 EL_SP_GRAVITY_ON_PORT_UP,
3195 EL_SP_GRAVITY_ON_PORT_DOWN,
3196 EL_SP_GRAVITY_OFF_PORT_LEFT,
3197 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3198 EL_SP_GRAVITY_OFF_PORT_UP,
3199 EL_SP_GRAVITY_OFF_PORT_DOWN,
3204 static int ep_passable_under[] =
3209 static int ep_droppable[] =
3214 static int ep_explodes_1x1_old[] =
3219 static int ep_pushable[] =
3231 EL_SOKOBAN_FIELD_FULL,
3240 static int ep_explodes_cross_old[] =
3245 static int ep_protected[] =
3247 // same elements as in 'ep_walkable_inside'
3251 EL_TUBE_VERTICAL_LEFT,
3252 EL_TUBE_VERTICAL_RIGHT,
3253 EL_TUBE_HORIZONTAL_UP,
3254 EL_TUBE_HORIZONTAL_DOWN,
3260 // same elements as in 'ep_passable_over'
3269 EL_EM_GATE_1_GRAY_ACTIVE,
3270 EL_EM_GATE_2_GRAY_ACTIVE,
3271 EL_EM_GATE_3_GRAY_ACTIVE,
3272 EL_EM_GATE_4_GRAY_ACTIVE,
3281 EL_EMC_GATE_5_GRAY_ACTIVE,
3282 EL_EMC_GATE_6_GRAY_ACTIVE,
3283 EL_EMC_GATE_7_GRAY_ACTIVE,
3284 EL_EMC_GATE_8_GRAY_ACTIVE,
3286 EL_DC_GATE_WHITE_GRAY,
3287 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3291 // same elements as in 'ep_passable_inside'
3296 EL_SP_PORT_HORIZONTAL,
3297 EL_SP_PORT_VERTICAL,
3299 EL_SP_GRAVITY_PORT_LEFT,
3300 EL_SP_GRAVITY_PORT_RIGHT,
3301 EL_SP_GRAVITY_PORT_UP,
3302 EL_SP_GRAVITY_PORT_DOWN,
3303 EL_SP_GRAVITY_ON_PORT_LEFT,
3304 EL_SP_GRAVITY_ON_PORT_RIGHT,
3305 EL_SP_GRAVITY_ON_PORT_UP,
3306 EL_SP_GRAVITY_ON_PORT_DOWN,
3307 EL_SP_GRAVITY_OFF_PORT_LEFT,
3308 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3309 EL_SP_GRAVITY_OFF_PORT_UP,
3310 EL_SP_GRAVITY_OFF_PORT_DOWN,
3315 static int ep_throwable[] =
3320 static int ep_can_explode[] =
3322 // same elements as in 'ep_explodes_impact'
3327 // same elements as in 'ep_explodes_smashed'
3333 // elements that can explode by explosion or by dragonfire
3337 EL_EM_DYNAMITE_ACTIVE,
3338 EL_DYNABOMB_PLAYER_1_ACTIVE,
3339 EL_DYNABOMB_PLAYER_2_ACTIVE,
3340 EL_DYNABOMB_PLAYER_3_ACTIVE,
3341 EL_DYNABOMB_PLAYER_4_ACTIVE,
3342 EL_DYNABOMB_INCREASE_NUMBER,
3343 EL_DYNABOMB_INCREASE_SIZE,
3344 EL_DYNABOMB_INCREASE_POWER,
3345 EL_SP_DISK_RED_ACTIVE,
3353 // elements that can explode only by explosion
3359 static int ep_gravity_reachable[] =
3365 EL_INVISIBLE_SAND_ACTIVE,
3370 EL_SP_PORT_HORIZONTAL,
3371 EL_SP_PORT_VERTICAL,
3373 EL_SP_GRAVITY_PORT_LEFT,
3374 EL_SP_GRAVITY_PORT_RIGHT,
3375 EL_SP_GRAVITY_PORT_UP,
3376 EL_SP_GRAVITY_PORT_DOWN,
3377 EL_SP_GRAVITY_ON_PORT_LEFT,
3378 EL_SP_GRAVITY_ON_PORT_RIGHT,
3379 EL_SP_GRAVITY_ON_PORT_UP,
3380 EL_SP_GRAVITY_ON_PORT_DOWN,
3381 EL_SP_GRAVITY_OFF_PORT_LEFT,
3382 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3383 EL_SP_GRAVITY_OFF_PORT_UP,
3384 EL_SP_GRAVITY_OFF_PORT_DOWN,
3390 static int ep_empty_space[] =
3413 static int ep_player[] =
3420 EL_SOKOBAN_FIELD_PLAYER,
3426 static int ep_can_pass_magic_wall[] =
3440 static int ep_can_pass_dc_magic_wall[] =
3456 static int ep_switchable[] =
3460 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3461 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3462 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3463 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3464 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3465 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3466 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3467 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3468 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3469 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3470 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3471 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3472 EL_SWITCHGATE_SWITCH_UP,
3473 EL_SWITCHGATE_SWITCH_DOWN,
3474 EL_DC_SWITCHGATE_SWITCH_UP,
3475 EL_DC_SWITCHGATE_SWITCH_DOWN,
3477 EL_LIGHT_SWITCH_ACTIVE,
3479 EL_DC_TIMEGATE_SWITCH,
3480 EL_BALLOON_SWITCH_LEFT,
3481 EL_BALLOON_SWITCH_RIGHT,
3482 EL_BALLOON_SWITCH_UP,
3483 EL_BALLOON_SWITCH_DOWN,
3484 EL_BALLOON_SWITCH_ANY,
3485 EL_BALLOON_SWITCH_NONE,
3488 EL_EMC_MAGIC_BALL_SWITCH,
3489 EL_EMC_MAGIC_BALL_SWITCH_ACTIVE,
3494 static int ep_bd_element[] =
3528 static int ep_sp_element[] =
3530 // should always be valid
3533 // standard classic Supaplex elements
3540 EL_SP_HARDWARE_GRAY,
3548 EL_SP_GRAVITY_PORT_RIGHT,
3549 EL_SP_GRAVITY_PORT_DOWN,
3550 EL_SP_GRAVITY_PORT_LEFT,
3551 EL_SP_GRAVITY_PORT_UP,
3556 EL_SP_PORT_VERTICAL,
3557 EL_SP_PORT_HORIZONTAL,
3563 EL_SP_HARDWARE_BASE_1,
3564 EL_SP_HARDWARE_GREEN,
3565 EL_SP_HARDWARE_BLUE,
3567 EL_SP_HARDWARE_YELLOW,
3568 EL_SP_HARDWARE_BASE_2,
3569 EL_SP_HARDWARE_BASE_3,
3570 EL_SP_HARDWARE_BASE_4,
3571 EL_SP_HARDWARE_BASE_5,
3572 EL_SP_HARDWARE_BASE_6,
3576 // additional elements that appeared in newer Supaplex levels
3579 // additional gravity port elements (not switching, but setting gravity)
3580 EL_SP_GRAVITY_ON_PORT_LEFT,
3581 EL_SP_GRAVITY_ON_PORT_RIGHT,
3582 EL_SP_GRAVITY_ON_PORT_UP,
3583 EL_SP_GRAVITY_ON_PORT_DOWN,
3584 EL_SP_GRAVITY_OFF_PORT_LEFT,
3585 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3586 EL_SP_GRAVITY_OFF_PORT_UP,
3587 EL_SP_GRAVITY_OFF_PORT_DOWN,
3589 // more than one Murphy in a level results in an inactive clone
3592 // runtime Supaplex elements
3593 EL_SP_DISK_RED_ACTIVE,
3594 EL_SP_TERMINAL_ACTIVE,
3595 EL_SP_BUGGY_BASE_ACTIVATING,
3596 EL_SP_BUGGY_BASE_ACTIVE,
3603 static int ep_sb_element[] =
3608 EL_SOKOBAN_FIELD_EMPTY,
3609 EL_SOKOBAN_FIELD_FULL,
3610 EL_SOKOBAN_FIELD_PLAYER,
3615 EL_INVISIBLE_STEELWALL,
3620 static int ep_gem[] =
3632 static int ep_food_dark_yamyam[] =
3660 static int ep_food_penguin[] =
3674 static int ep_food_pig[] =
3686 static int ep_historic_wall[] =
3697 EL_GATE_1_GRAY_ACTIVE,
3698 EL_GATE_2_GRAY_ACTIVE,
3699 EL_GATE_3_GRAY_ACTIVE,
3700 EL_GATE_4_GRAY_ACTIVE,
3709 EL_EM_GATE_1_GRAY_ACTIVE,
3710 EL_EM_GATE_2_GRAY_ACTIVE,
3711 EL_EM_GATE_3_GRAY_ACTIVE,
3712 EL_EM_GATE_4_GRAY_ACTIVE,
3719 EL_EXPANDABLE_WALL_HORIZONTAL,
3720 EL_EXPANDABLE_WALL_VERTICAL,
3721 EL_EXPANDABLE_WALL_ANY,
3722 EL_EXPANDABLE_WALL_GROWING,
3723 EL_BD_EXPANDABLE_WALL,
3730 EL_SP_HARDWARE_GRAY,
3731 EL_SP_HARDWARE_GREEN,
3732 EL_SP_HARDWARE_BLUE,
3734 EL_SP_HARDWARE_YELLOW,
3735 EL_SP_HARDWARE_BASE_1,
3736 EL_SP_HARDWARE_BASE_2,
3737 EL_SP_HARDWARE_BASE_3,
3738 EL_SP_HARDWARE_BASE_4,
3739 EL_SP_HARDWARE_BASE_5,
3740 EL_SP_HARDWARE_BASE_6,
3742 EL_SP_TERMINAL_ACTIVE,
3745 EL_INVISIBLE_STEELWALL,
3746 EL_INVISIBLE_STEELWALL_ACTIVE,
3748 EL_INVISIBLE_WALL_ACTIVE,
3749 EL_STEELWALL_SLIPPERY,
3766 static int ep_historic_solid[] =
3770 EL_EXPANDABLE_WALL_HORIZONTAL,
3771 EL_EXPANDABLE_WALL_VERTICAL,
3772 EL_EXPANDABLE_WALL_ANY,
3773 EL_BD_EXPANDABLE_WALL,
3786 EL_QUICKSAND_FILLING,
3787 EL_QUICKSAND_EMPTYING,
3789 EL_MAGIC_WALL_ACTIVE,
3790 EL_MAGIC_WALL_EMPTYING,
3791 EL_MAGIC_WALL_FILLING,
3795 EL_BD_MAGIC_WALL_ACTIVE,
3796 EL_BD_MAGIC_WALL_EMPTYING,
3797 EL_BD_MAGIC_WALL_FULL,
3798 EL_BD_MAGIC_WALL_FILLING,
3799 EL_BD_MAGIC_WALL_DEAD,
3808 EL_SP_TERMINAL_ACTIVE,
3812 EL_INVISIBLE_WALL_ACTIVE,
3813 EL_SWITCHGATE_SWITCH_UP,
3814 EL_SWITCHGATE_SWITCH_DOWN,
3816 EL_TIMEGATE_SWITCH_ACTIVE,
3828 // the following elements are a direct copy of "indestructible" elements,
3829 // except "EL_ACID", which is "indestructible", but not "solid"!
3834 EL_ACID_POOL_TOPLEFT,
3835 EL_ACID_POOL_TOPRIGHT,
3836 EL_ACID_POOL_BOTTOMLEFT,
3837 EL_ACID_POOL_BOTTOM,
3838 EL_ACID_POOL_BOTTOMRIGHT,
3839 EL_SP_HARDWARE_GRAY,
3840 EL_SP_HARDWARE_GREEN,
3841 EL_SP_HARDWARE_BLUE,
3843 EL_SP_HARDWARE_YELLOW,
3844 EL_SP_HARDWARE_BASE_1,
3845 EL_SP_HARDWARE_BASE_2,
3846 EL_SP_HARDWARE_BASE_3,
3847 EL_SP_HARDWARE_BASE_4,
3848 EL_SP_HARDWARE_BASE_5,
3849 EL_SP_HARDWARE_BASE_6,
3850 EL_INVISIBLE_STEELWALL,
3851 EL_INVISIBLE_STEELWALL_ACTIVE,
3852 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3853 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3854 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3855 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3856 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3857 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3858 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3859 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3860 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3861 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3862 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3863 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3865 EL_LIGHT_SWITCH_ACTIVE,
3866 EL_SIGN_EXCLAMATION,
3867 EL_SIGN_RADIOACTIVITY,
3874 EL_SIGN_ENTRY_FORBIDDEN,
3875 EL_SIGN_EMERGENCY_EXIT,
3883 EL_STEEL_EXIT_CLOSED,
3885 EL_STEEL_EXIT_OPENING,
3886 EL_STEEL_EXIT_CLOSING,
3887 EL_EM_STEEL_EXIT_CLOSED,
3888 EL_EM_STEEL_EXIT_OPEN,
3889 EL_EM_STEEL_EXIT_OPENING,
3890 EL_EM_STEEL_EXIT_CLOSING,
3891 EL_DC_STEELWALL_1_LEFT,
3892 EL_DC_STEELWALL_1_RIGHT,
3893 EL_DC_STEELWALL_1_TOP,
3894 EL_DC_STEELWALL_1_BOTTOM,
3895 EL_DC_STEELWALL_1_HORIZONTAL,
3896 EL_DC_STEELWALL_1_VERTICAL,
3897 EL_DC_STEELWALL_1_TOPLEFT,
3898 EL_DC_STEELWALL_1_TOPRIGHT,
3899 EL_DC_STEELWALL_1_BOTTOMLEFT,
3900 EL_DC_STEELWALL_1_BOTTOMRIGHT,
3901 EL_DC_STEELWALL_1_TOPLEFT_2,
3902 EL_DC_STEELWALL_1_TOPRIGHT_2,
3903 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
3904 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
3905 EL_DC_STEELWALL_2_LEFT,
3906 EL_DC_STEELWALL_2_RIGHT,
3907 EL_DC_STEELWALL_2_TOP,
3908 EL_DC_STEELWALL_2_BOTTOM,
3909 EL_DC_STEELWALL_2_HORIZONTAL,
3910 EL_DC_STEELWALL_2_VERTICAL,
3911 EL_DC_STEELWALL_2_MIDDLE,
3912 EL_DC_STEELWALL_2_SINGLE,
3913 EL_STEELWALL_SLIPPERY,
3927 EL_GATE_1_GRAY_ACTIVE,
3928 EL_GATE_2_GRAY_ACTIVE,
3929 EL_GATE_3_GRAY_ACTIVE,
3930 EL_GATE_4_GRAY_ACTIVE,
3939 EL_EM_GATE_1_GRAY_ACTIVE,
3940 EL_EM_GATE_2_GRAY_ACTIVE,
3941 EL_EM_GATE_3_GRAY_ACTIVE,
3942 EL_EM_GATE_4_GRAY_ACTIVE,
3951 EL_EMC_GATE_5_GRAY_ACTIVE,
3952 EL_EMC_GATE_6_GRAY_ACTIVE,
3953 EL_EMC_GATE_7_GRAY_ACTIVE,
3954 EL_EMC_GATE_8_GRAY_ACTIVE,
3956 EL_DC_GATE_WHITE_GRAY,
3957 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3958 EL_DC_GATE_FAKE_GRAY,
3960 EL_SWITCHGATE_OPENING,
3961 EL_SWITCHGATE_CLOSED,
3962 EL_SWITCHGATE_CLOSING,
3963 EL_DC_SWITCHGATE_SWITCH_UP,
3964 EL_DC_SWITCHGATE_SWITCH_DOWN,
3966 EL_TIMEGATE_OPENING,
3968 EL_TIMEGATE_CLOSING,
3969 EL_DC_TIMEGATE_SWITCH,
3970 EL_DC_TIMEGATE_SWITCH_ACTIVE,
3974 EL_TUBE_VERTICAL_LEFT,
3975 EL_TUBE_VERTICAL_RIGHT,
3976 EL_TUBE_HORIZONTAL_UP,
3977 EL_TUBE_HORIZONTAL_DOWN,
3982 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
3983 EL_EXPANDABLE_STEELWALL_VERTICAL,
3984 EL_EXPANDABLE_STEELWALL_ANY,
3989 static int ep_classic_enemy[] =
4006 static int ep_belt[] =
4008 EL_CONVEYOR_BELT_1_LEFT,
4009 EL_CONVEYOR_BELT_1_MIDDLE,
4010 EL_CONVEYOR_BELT_1_RIGHT,
4011 EL_CONVEYOR_BELT_2_LEFT,
4012 EL_CONVEYOR_BELT_2_MIDDLE,
4013 EL_CONVEYOR_BELT_2_RIGHT,
4014 EL_CONVEYOR_BELT_3_LEFT,
4015 EL_CONVEYOR_BELT_3_MIDDLE,
4016 EL_CONVEYOR_BELT_3_RIGHT,
4017 EL_CONVEYOR_BELT_4_LEFT,
4018 EL_CONVEYOR_BELT_4_MIDDLE,
4019 EL_CONVEYOR_BELT_4_RIGHT,
4024 static int ep_belt_active[] =
4026 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4027 EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE,
4028 EL_CONVEYOR_BELT_1_RIGHT_ACTIVE,
4029 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4030 EL_CONVEYOR_BELT_2_MIDDLE_ACTIVE,
4031 EL_CONVEYOR_BELT_2_RIGHT_ACTIVE,
4032 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4033 EL_CONVEYOR_BELT_3_MIDDLE_ACTIVE,
4034 EL_CONVEYOR_BELT_3_RIGHT_ACTIVE,
4035 EL_CONVEYOR_BELT_4_LEFT_ACTIVE,
4036 EL_CONVEYOR_BELT_4_MIDDLE_ACTIVE,
4037 EL_CONVEYOR_BELT_4_RIGHT_ACTIVE,
4042 static int ep_belt_switch[] =
4044 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4045 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4046 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4047 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4048 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4049 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4050 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4051 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4052 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4053 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4054 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4055 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4060 static int ep_tube[] =
4067 EL_TUBE_HORIZONTAL_UP,
4068 EL_TUBE_HORIZONTAL_DOWN,
4070 EL_TUBE_VERTICAL_LEFT,
4071 EL_TUBE_VERTICAL_RIGHT,
4077 static int ep_acid_pool[] =
4079 EL_ACID_POOL_TOPLEFT,
4080 EL_ACID_POOL_TOPRIGHT,
4081 EL_ACID_POOL_BOTTOMLEFT,
4082 EL_ACID_POOL_BOTTOM,
4083 EL_ACID_POOL_BOTTOMRIGHT,
4088 static int ep_keygate[] =
4098 EL_GATE_1_GRAY_ACTIVE,
4099 EL_GATE_2_GRAY_ACTIVE,
4100 EL_GATE_3_GRAY_ACTIVE,
4101 EL_GATE_4_GRAY_ACTIVE,
4110 EL_EM_GATE_1_GRAY_ACTIVE,
4111 EL_EM_GATE_2_GRAY_ACTIVE,
4112 EL_EM_GATE_3_GRAY_ACTIVE,
4113 EL_EM_GATE_4_GRAY_ACTIVE,
4122 EL_EMC_GATE_5_GRAY_ACTIVE,
4123 EL_EMC_GATE_6_GRAY_ACTIVE,
4124 EL_EMC_GATE_7_GRAY_ACTIVE,
4125 EL_EMC_GATE_8_GRAY_ACTIVE,
4127 EL_DC_GATE_WHITE_GRAY,
4128 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4133 static int ep_amoeboid[] =
4145 static int ep_amoebalive[] =
4156 static int ep_has_editor_content[] =
4162 EL_SOKOBAN_FIELD_PLAYER,
4179 static int ep_can_turn_each_move[] =
4181 // !!! do something with this one !!!
4185 static int ep_can_grow[] =
4199 static int ep_active_bomb[] =
4202 EL_EM_DYNAMITE_ACTIVE,
4203 EL_DYNABOMB_PLAYER_1_ACTIVE,
4204 EL_DYNABOMB_PLAYER_2_ACTIVE,
4205 EL_DYNABOMB_PLAYER_3_ACTIVE,
4206 EL_DYNABOMB_PLAYER_4_ACTIVE,
4207 EL_SP_DISK_RED_ACTIVE,
4212 static int ep_inactive[] =
4238 EL_QUICKSAND_FAST_EMPTY,
4261 EL_GATE_1_GRAY_ACTIVE,
4262 EL_GATE_2_GRAY_ACTIVE,
4263 EL_GATE_3_GRAY_ACTIVE,
4264 EL_GATE_4_GRAY_ACTIVE,
4273 EL_EM_GATE_1_GRAY_ACTIVE,
4274 EL_EM_GATE_2_GRAY_ACTIVE,
4275 EL_EM_GATE_3_GRAY_ACTIVE,
4276 EL_EM_GATE_4_GRAY_ACTIVE,
4285 EL_EMC_GATE_5_GRAY_ACTIVE,
4286 EL_EMC_GATE_6_GRAY_ACTIVE,
4287 EL_EMC_GATE_7_GRAY_ACTIVE,
4288 EL_EMC_GATE_8_GRAY_ACTIVE,
4290 EL_DC_GATE_WHITE_GRAY,
4291 EL_DC_GATE_WHITE_GRAY_ACTIVE,
4292 EL_DC_GATE_FAKE_GRAY,
4295 EL_INVISIBLE_STEELWALL,
4303 EL_WALL_EMERALD_YELLOW,
4304 EL_DYNABOMB_INCREASE_NUMBER,
4305 EL_DYNABOMB_INCREASE_SIZE,
4306 EL_DYNABOMB_INCREASE_POWER,
4310 EL_SOKOBAN_FIELD_EMPTY,
4311 EL_SOKOBAN_FIELD_FULL,
4312 EL_WALL_EMERALD_RED,
4313 EL_WALL_EMERALD_PURPLE,
4314 EL_ACID_POOL_TOPLEFT,
4315 EL_ACID_POOL_TOPRIGHT,
4316 EL_ACID_POOL_BOTTOMLEFT,
4317 EL_ACID_POOL_BOTTOM,
4318 EL_ACID_POOL_BOTTOMRIGHT,
4322 EL_BD_MAGIC_WALL_DEAD,
4324 EL_DC_MAGIC_WALL_DEAD,
4325 EL_AMOEBA_TO_DIAMOND,
4333 EL_SP_GRAVITY_PORT_RIGHT,
4334 EL_SP_GRAVITY_PORT_DOWN,
4335 EL_SP_GRAVITY_PORT_LEFT,
4336 EL_SP_GRAVITY_PORT_UP,
4337 EL_SP_PORT_HORIZONTAL,
4338 EL_SP_PORT_VERTICAL,
4349 EL_SP_HARDWARE_GRAY,
4350 EL_SP_HARDWARE_GREEN,
4351 EL_SP_HARDWARE_BLUE,
4353 EL_SP_HARDWARE_YELLOW,
4354 EL_SP_HARDWARE_BASE_1,
4355 EL_SP_HARDWARE_BASE_2,
4356 EL_SP_HARDWARE_BASE_3,
4357 EL_SP_HARDWARE_BASE_4,
4358 EL_SP_HARDWARE_BASE_5,
4359 EL_SP_HARDWARE_BASE_6,
4360 EL_SP_GRAVITY_ON_PORT_LEFT,
4361 EL_SP_GRAVITY_ON_PORT_RIGHT,
4362 EL_SP_GRAVITY_ON_PORT_UP,
4363 EL_SP_GRAVITY_ON_PORT_DOWN,
4364 EL_SP_GRAVITY_OFF_PORT_LEFT,
4365 EL_SP_GRAVITY_OFF_PORT_RIGHT,
4366 EL_SP_GRAVITY_OFF_PORT_UP,
4367 EL_SP_GRAVITY_OFF_PORT_DOWN,
4368 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4369 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
4370 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
4371 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4372 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
4373 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
4374 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4375 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
4376 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
4377 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
4378 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
4379 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
4380 EL_SIGN_EXCLAMATION,
4381 EL_SIGN_RADIOACTIVITY,
4388 EL_SIGN_ENTRY_FORBIDDEN,
4389 EL_SIGN_EMERGENCY_EXIT,
4397 EL_DC_STEELWALL_1_LEFT,
4398 EL_DC_STEELWALL_1_RIGHT,
4399 EL_DC_STEELWALL_1_TOP,
4400 EL_DC_STEELWALL_1_BOTTOM,
4401 EL_DC_STEELWALL_1_HORIZONTAL,
4402 EL_DC_STEELWALL_1_VERTICAL,
4403 EL_DC_STEELWALL_1_TOPLEFT,
4404 EL_DC_STEELWALL_1_TOPRIGHT,
4405 EL_DC_STEELWALL_1_BOTTOMLEFT,
4406 EL_DC_STEELWALL_1_BOTTOMRIGHT,
4407 EL_DC_STEELWALL_1_TOPLEFT_2,
4408 EL_DC_STEELWALL_1_TOPRIGHT_2,
4409 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
4410 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
4411 EL_DC_STEELWALL_2_LEFT,
4412 EL_DC_STEELWALL_2_RIGHT,
4413 EL_DC_STEELWALL_2_TOP,
4414 EL_DC_STEELWALL_2_BOTTOM,
4415 EL_DC_STEELWALL_2_HORIZONTAL,
4416 EL_DC_STEELWALL_2_VERTICAL,
4417 EL_DC_STEELWALL_2_MIDDLE,
4418 EL_DC_STEELWALL_2_SINGLE,
4419 EL_STEELWALL_SLIPPERY,
4424 EL_EMC_WALL_SLIPPERY_1,
4425 EL_EMC_WALL_SLIPPERY_2,
4426 EL_EMC_WALL_SLIPPERY_3,
4427 EL_EMC_WALL_SLIPPERY_4,
4448 static int ep_em_slippery_wall[] =
4453 static int ep_gfx_crumbled[] =
4464 static int ep_editor_cascade_active[] =
4466 EL_INTERNAL_CASCADE_BD_ACTIVE,
4467 EL_INTERNAL_CASCADE_EM_ACTIVE,
4468 EL_INTERNAL_CASCADE_EMC_ACTIVE,
4469 EL_INTERNAL_CASCADE_RND_ACTIVE,
4470 EL_INTERNAL_CASCADE_SB_ACTIVE,
4471 EL_INTERNAL_CASCADE_SP_ACTIVE,
4472 EL_INTERNAL_CASCADE_DC_ACTIVE,
4473 EL_INTERNAL_CASCADE_DX_ACTIVE,
4474 EL_INTERNAL_CASCADE_MM_ACTIVE,
4475 EL_INTERNAL_CASCADE_DF_ACTIVE,
4476 EL_INTERNAL_CASCADE_CHARS_ACTIVE,
4477 EL_INTERNAL_CASCADE_STEEL_CHARS_ACTIVE,
4478 EL_INTERNAL_CASCADE_CE_ACTIVE,
4479 EL_INTERNAL_CASCADE_GE_ACTIVE,
4480 EL_INTERNAL_CASCADE_ES_ACTIVE,
4481 EL_INTERNAL_CASCADE_REF_ACTIVE,
4482 EL_INTERNAL_CASCADE_USER_ACTIVE,
4483 EL_INTERNAL_CASCADE_DYNAMIC_ACTIVE,
4488 static int ep_editor_cascade_inactive[] =
4490 EL_INTERNAL_CASCADE_BD,
4491 EL_INTERNAL_CASCADE_EM,
4492 EL_INTERNAL_CASCADE_EMC,
4493 EL_INTERNAL_CASCADE_RND,
4494 EL_INTERNAL_CASCADE_SB,
4495 EL_INTERNAL_CASCADE_SP,
4496 EL_INTERNAL_CASCADE_DC,
4497 EL_INTERNAL_CASCADE_DX,
4498 EL_INTERNAL_CASCADE_MM,
4499 EL_INTERNAL_CASCADE_DF,
4500 EL_INTERNAL_CASCADE_CHARS,
4501 EL_INTERNAL_CASCADE_STEEL_CHARS,
4502 EL_INTERNAL_CASCADE_CE,
4503 EL_INTERNAL_CASCADE_GE,
4504 EL_INTERNAL_CASCADE_ES,
4505 EL_INTERNAL_CASCADE_REF,
4506 EL_INTERNAL_CASCADE_USER,
4507 EL_INTERNAL_CASCADE_DYNAMIC,
4512 static int ep_obsolete[] =
4516 EL_EM_KEY_1_FILE_OBSOLETE,
4517 EL_EM_KEY_2_FILE_OBSOLETE,
4518 EL_EM_KEY_3_FILE_OBSOLETE,
4519 EL_EM_KEY_4_FILE_OBSOLETE,
4520 EL_ENVELOPE_OBSOLETE,
4529 } element_properties[] =
4531 { ep_diggable, EP_DIGGABLE },
4532 { ep_collectible_only, EP_COLLECTIBLE_ONLY },
4533 { ep_dont_run_into, EP_DONT_RUN_INTO },
4534 { ep_dont_collide_with, EP_DONT_COLLIDE_WITH },
4535 { ep_dont_touch, EP_DONT_TOUCH },
4536 { ep_indestructible, EP_INDESTRUCTIBLE },
4537 { ep_slippery, EP_SLIPPERY },
4538 { ep_can_change, EP_CAN_CHANGE },
4539 { ep_can_move, EP_CAN_MOVE },
4540 { ep_can_fall, EP_CAN_FALL },
4541 { ep_can_smash_player, EP_CAN_SMASH_PLAYER },
4542 { ep_can_smash_enemies, EP_CAN_SMASH_ENEMIES },
4543 { ep_can_smash_everything, EP_CAN_SMASH_EVERYTHING },
4544 { ep_explodes_by_fire, EP_EXPLODES_BY_FIRE },
4545 { ep_explodes_smashed, EP_EXPLODES_SMASHED },
4546 { ep_explodes_impact, EP_EXPLODES_IMPACT },
4547 { ep_walkable_over, EP_WALKABLE_OVER },
4548 { ep_walkable_inside, EP_WALKABLE_INSIDE },
4549 { ep_walkable_under, EP_WALKABLE_UNDER },
4550 { ep_passable_over, EP_PASSABLE_OVER },
4551 { ep_passable_inside, EP_PASSABLE_INSIDE },
4552 { ep_passable_under, EP_PASSABLE_UNDER },
4553 { ep_droppable, EP_DROPPABLE },
4554 { ep_explodes_1x1_old, EP_EXPLODES_1X1_OLD },
4555 { ep_pushable, EP_PUSHABLE },
4556 { ep_explodes_cross_old, EP_EXPLODES_CROSS_OLD },
4557 { ep_protected, EP_PROTECTED },
4558 { ep_throwable, EP_THROWABLE },
4559 { ep_can_explode, EP_CAN_EXPLODE },
4560 { ep_gravity_reachable, EP_GRAVITY_REACHABLE },
4562 { ep_empty_space, EP_EMPTY_SPACE },
4563 { ep_player, EP_PLAYER },
4564 { ep_can_pass_magic_wall, EP_CAN_PASS_MAGIC_WALL },
4565 { ep_can_pass_dc_magic_wall, EP_CAN_PASS_DC_MAGIC_WALL },
4566 { ep_switchable, EP_SWITCHABLE },
4567 { ep_bd_element, EP_BD_ELEMENT },
4568 { ep_sp_element, EP_SP_ELEMENT },
4569 { ep_sb_element, EP_SB_ELEMENT },
4571 { ep_food_dark_yamyam, EP_FOOD_DARK_YAMYAM },
4572 { ep_food_penguin, EP_FOOD_PENGUIN },
4573 { ep_food_pig, EP_FOOD_PIG },
4574 { ep_historic_wall, EP_HISTORIC_WALL },
4575 { ep_historic_solid, EP_HISTORIC_SOLID },
4576 { ep_classic_enemy, EP_CLASSIC_ENEMY },
4577 { ep_belt, EP_BELT },
4578 { ep_belt_active, EP_BELT_ACTIVE },
4579 { ep_belt_switch, EP_BELT_SWITCH },
4580 { ep_tube, EP_TUBE },
4581 { ep_acid_pool, EP_ACID_POOL },
4582 { ep_keygate, EP_KEYGATE },
4583 { ep_amoeboid, EP_AMOEBOID },
4584 { ep_amoebalive, EP_AMOEBALIVE },
4585 { ep_has_editor_content, EP_HAS_EDITOR_CONTENT },
4586 { ep_can_turn_each_move, EP_CAN_TURN_EACH_MOVE },
4587 { ep_can_grow, EP_CAN_GROW },
4588 { ep_active_bomb, EP_ACTIVE_BOMB },
4589 { ep_inactive, EP_INACTIVE },
4591 { ep_em_slippery_wall, EP_EM_SLIPPERY_WALL },
4593 { ep_gfx_crumbled, EP_GFX_CRUMBLED },
4595 { ep_editor_cascade_active, EP_EDITOR_CASCADE_ACTIVE },
4596 { ep_editor_cascade_inactive, EP_EDITOR_CASCADE_INACTIVE },
4598 { ep_obsolete, EP_OBSOLETE },
4605 // always start with reliable default values (element has no properties)
4606 // (but never initialize clipboard elements after the very first time)
4607 // (to be able to use clipboard elements between several levels)
4608 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4609 if (!IS_CLIPBOARD_ELEMENT(i) || !clipboard_elements_initialized)
4610 for (j = 0; j < NUM_ELEMENT_PROPERTIES; j++)
4611 SET_PROPERTY(i, j, FALSE);
4613 // set all base element properties from above array definitions
4614 for (i = 0; element_properties[i].elements != NULL; i++)
4615 for (j = 0; (element_properties[i].elements)[j] != -1; j++)
4616 SET_PROPERTY((element_properties[i].elements)[j],
4617 element_properties[i].property, TRUE);
4619 // copy properties to some elements that are only stored in level file
4620 for (i = 0; i < NUM_ELEMENT_PROPERTIES; i++)
4621 for (j = 0; copy_properties[j][0] != -1; j++)
4622 if (HAS_PROPERTY(copy_properties[j][0], i))
4623 for (k = 1; k <= 4; k++)
4624 SET_PROPERTY(copy_properties[j][k], i, TRUE);
4626 // set static element properties that are not listed in array definitions
4627 for (i = EL_STEEL_CHAR_START; i <= EL_STEEL_CHAR_END; i++)
4628 SET_PROPERTY(i, EP_INDESTRUCTIBLE, TRUE);
4630 clipboard_elements_initialized = TRUE;
4633 void InitElementPropertiesEngine(int engine_version)
4635 static int no_wall_properties[] =
4638 EP_COLLECTIBLE_ONLY,
4640 EP_DONT_COLLIDE_WITH,
4643 EP_CAN_SMASH_PLAYER,
4644 EP_CAN_SMASH_ENEMIES,
4645 EP_CAN_SMASH_EVERYTHING,
4650 EP_FOOD_DARK_YAMYAM,
4666 /* important: after initialization in InitElementPropertiesStatic(), the
4667 elements are not again initialized to a default value; therefore all
4668 changes have to make sure that they leave the element with a defined
4669 property (which means that conditional property changes must be set to
4670 a reliable default value before) */
4672 // resolve group elements
4673 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
4674 ResolveGroupElement(EL_GROUP_START + i);
4676 // set all special, combined or engine dependent element properties
4677 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4679 // do not change (already initialized) clipboard elements here
4680 if (IS_CLIPBOARD_ELEMENT(i))
4683 // ---------- INACTIVE ----------------------------------------------------
4684 SET_PROPERTY(i, EP_INACTIVE, ((i >= EL_CHAR_START &&
4685 i <= EL_CHAR_END) ||
4686 (i >= EL_STEEL_CHAR_START &&
4687 i <= EL_STEEL_CHAR_END)));
4689 // ---------- WALKABLE, PASSABLE, ACCESSIBLE ------------------------------
4690 SET_PROPERTY(i, EP_WALKABLE, (IS_WALKABLE_OVER(i) ||
4691 IS_WALKABLE_INSIDE(i) ||
4692 IS_WALKABLE_UNDER(i)));
4694 SET_PROPERTY(i, EP_PASSABLE, (IS_PASSABLE_OVER(i) ||
4695 IS_PASSABLE_INSIDE(i) ||
4696 IS_PASSABLE_UNDER(i)));
4698 SET_PROPERTY(i, EP_ACCESSIBLE_OVER, (IS_WALKABLE_OVER(i) ||
4699 IS_PASSABLE_OVER(i)));
4701 SET_PROPERTY(i, EP_ACCESSIBLE_INSIDE, (IS_WALKABLE_INSIDE(i) ||
4702 IS_PASSABLE_INSIDE(i)));
4704 SET_PROPERTY(i, EP_ACCESSIBLE_UNDER, (IS_WALKABLE_UNDER(i) ||
4705 IS_PASSABLE_UNDER(i)));
4707 SET_PROPERTY(i, EP_ACCESSIBLE, (IS_WALKABLE(i) ||
4710 // ---------- COLLECTIBLE -------------------------------------------------
4711 SET_PROPERTY(i, EP_COLLECTIBLE, (IS_COLLECTIBLE_ONLY(i) ||
4715 // ---------- SNAPPABLE ---------------------------------------------------
4716 SET_PROPERTY(i, EP_SNAPPABLE, (IS_DIGGABLE(i) ||
4717 IS_COLLECTIBLE(i) ||
4721 // ---------- WALL --------------------------------------------------------
4722 SET_PROPERTY(i, EP_WALL, TRUE); // default: element is wall
4724 for (j = 0; no_wall_properties[j] != -1; j++)
4725 if (HAS_PROPERTY(i, no_wall_properties[j]) ||
4726 i >= EL_FIRST_RUNTIME_UNREAL)
4727 SET_PROPERTY(i, EP_WALL, FALSE);
4729 if (IS_HISTORIC_WALL(i))
4730 SET_PROPERTY(i, EP_WALL, TRUE);
4732 // ---------- SOLID_FOR_PUSHING -------------------------------------------
4733 if (engine_version < VERSION_IDENT(2,2,0,0))
4734 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, IS_HISTORIC_SOLID(i));
4736 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, (!IS_WALKABLE(i) &&
4738 !IS_COLLECTIBLE(i)));
4740 // ---------- DRAGONFIRE_PROOF --------------------------------------------
4741 if (IS_HISTORIC_SOLID(i) || i == EL_EXPLOSION)
4742 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, TRUE);
4744 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, (IS_INDESTRUCTIBLE(i) &&
4747 // ---------- EXPLOSION_PROOF ---------------------------------------------
4749 SET_PROPERTY(i, EP_EXPLOSION_PROOF, TRUE);
4750 else if (engine_version < VERSION_IDENT(2,2,0,0))
4751 SET_PROPERTY(i, EP_EXPLOSION_PROOF, IS_INDESTRUCTIBLE(i));
4753 SET_PROPERTY(i, EP_EXPLOSION_PROOF, (IS_INDESTRUCTIBLE(i) &&
4757 if (IS_CUSTOM_ELEMENT(i))
4759 // these are additional properties which are initially false when set
4761 // ---------- DONT_COLLIDE_WITH / DONT_RUN_INTO -------------------------
4763 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH, TRUE);
4764 if (DONT_COLLIDE_WITH(i))
4765 SET_PROPERTY(i, EP_DONT_RUN_INTO, TRUE);
4767 // ---------- CAN_SMASH_ENEMIES / CAN_SMASH_PLAYER ----------------------
4768 if (CAN_SMASH_EVERYTHING(i))
4769 SET_PROPERTY(i, EP_CAN_SMASH_ENEMIES, TRUE);
4770 if (CAN_SMASH_ENEMIES(i))
4771 SET_PROPERTY(i, EP_CAN_SMASH_PLAYER, TRUE);
4774 // ---------- CAN_SMASH ---------------------------------------------------
4775 SET_PROPERTY(i, EP_CAN_SMASH, (CAN_SMASH_PLAYER(i) ||
4776 CAN_SMASH_ENEMIES(i) ||
4777 CAN_SMASH_EVERYTHING(i)));
4779 // ---------- CAN_EXPLODE_BY_FIRE -----------------------------------------
4780 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_FIRE, (CAN_EXPLODE(i) &&
4781 EXPLODES_BY_FIRE(i)));
4783 // ---------- CAN_EXPLODE_SMASHED -----------------------------------------
4784 SET_PROPERTY(i, EP_CAN_EXPLODE_SMASHED, (CAN_EXPLODE(i) &&
4785 EXPLODES_SMASHED(i)));
4787 // ---------- CAN_EXPLODE_IMPACT ------------------------------------------
4788 SET_PROPERTY(i, EP_CAN_EXPLODE_IMPACT, (CAN_EXPLODE(i) &&
4789 EXPLODES_IMPACT(i)));
4791 // ---------- CAN_EXPLODE_BY_DRAGONFIRE -----------------------------------
4792 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_DRAGONFIRE, CAN_EXPLODE_BY_FIRE(i));
4794 // ---------- CAN_EXPLODE_BY_EXPLOSION ------------------------------------
4795 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_EXPLOSION, (CAN_EXPLODE_BY_FIRE(i) ||
4796 i == EL_BLACK_ORB));
4798 // ---------- COULD_MOVE_INTO_ACID ----------------------------------------
4799 SET_PROPERTY(i, EP_COULD_MOVE_INTO_ACID, (IS_PLAYER_ELEMENT(i) ||
4801 IS_CUSTOM_ELEMENT(i)));
4803 // ---------- MAYBE_DONT_COLLIDE_WITH -------------------------------------
4804 SET_PROPERTY(i, EP_MAYBE_DONT_COLLIDE_WITH, (i == EL_SP_SNIKSNAK ||
4805 i == EL_SP_ELECTRON));
4807 // ---------- CAN_MOVE_INTO_ACID ------------------------------------------
4808 if (COULD_MOVE_INTO_ACID(i) && !IS_CUSTOM_ELEMENT(i))
4809 SET_PROPERTY(i, EP_CAN_MOVE_INTO_ACID,
4810 getMoveIntoAcidProperty(&level, i));
4812 // ---------- DONT_COLLIDE_WITH -------------------------------------------
4813 if (MAYBE_DONT_COLLIDE_WITH(i))
4814 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH,
4815 getDontCollideWithProperty(&level, i));
4817 // ---------- SP_PORT -----------------------------------------------------
4818 SET_PROPERTY(i, EP_SP_PORT, (IS_SP_ELEMENT(i) &&
4819 IS_PASSABLE_INSIDE(i)));
4821 // ---------- CAN_BE_CLONED_BY_ANDROID ------------------------------------
4822 for (j = 0; j < level.num_android_clone_elements; j++)
4823 SET_PROPERTY(i, EP_CAN_BE_CLONED_BY_ANDROID,
4825 IS_EQUAL_OR_IN_GROUP(i, level.android_clone_element[j])));
4827 // ---------- CAN_CHANGE --------------------------------------------------
4828 SET_PROPERTY(i, EP_CAN_CHANGE, FALSE); // default: cannot change
4829 for (j = 0; j < element_info[i].num_change_pages; j++)
4830 if (element_info[i].change_page[j].can_change)
4831 SET_PROPERTY(i, EP_CAN_CHANGE, TRUE);
4833 // ---------- HAS_ACTION --------------------------------------------------
4834 SET_PROPERTY(i, EP_HAS_ACTION, FALSE); // default: has no action
4835 for (j = 0; j < element_info[i].num_change_pages; j++)
4836 if (element_info[i].change_page[j].has_action)
4837 SET_PROPERTY(i, EP_HAS_ACTION, TRUE);
4839 // ---------- CAN_CHANGE_OR_HAS_ACTION ------------------------------------
4840 SET_PROPERTY(i, EP_CAN_CHANGE_OR_HAS_ACTION, (CAN_CHANGE(i) ||
4843 // ---------- GFX_CRUMBLED ------------------------------------------------
4844 SET_PROPERTY(i, EP_GFX_CRUMBLED,
4845 element_info[i].crumbled[ACTION_DEFAULT] !=
4846 element_info[i].graphic[ACTION_DEFAULT]);
4848 // ---------- EDITOR_CASCADE ----------------------------------------------
4849 SET_PROPERTY(i, EP_EDITOR_CASCADE, (IS_EDITOR_CASCADE_ACTIVE(i) ||
4850 IS_EDITOR_CASCADE_INACTIVE(i)));
4853 // dynamically adjust element properties according to game engine version
4855 static int ep_em_slippery_wall[] =
4860 EL_EXPANDABLE_WALL_HORIZONTAL,
4861 EL_EXPANDABLE_WALL_VERTICAL,
4862 EL_EXPANDABLE_WALL_ANY,
4863 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
4864 EL_EXPANDABLE_STEELWALL_VERTICAL,
4865 EL_EXPANDABLE_STEELWALL_ANY,
4866 EL_EXPANDABLE_STEELWALL_GROWING,
4870 static int ep_em_explodes_by_fire[] =
4873 EL_EM_DYNAMITE_ACTIVE,
4878 // special EM style gems behaviour
4879 for (i = 0; ep_em_slippery_wall[i] != -1; i++)
4880 SET_PROPERTY(ep_em_slippery_wall[i], EP_EM_SLIPPERY_WALL,
4881 level.em_slippery_gems);
4883 // "EL_EXPANDABLE_WALL_GROWING" wasn't slippery for EM gems in 2.0.1
4884 SET_PROPERTY(EL_EXPANDABLE_WALL_GROWING, EP_EM_SLIPPERY_WALL,
4885 (level.em_slippery_gems &&
4886 engine_version > VERSION_IDENT(2,0,1,0)));
4888 // special EM style explosion behaviour regarding chain reactions
4889 for (i = 0; ep_em_explodes_by_fire[i] != -1; i++)
4890 SET_PROPERTY(ep_em_explodes_by_fire[i], EP_EXPLODES_BY_FIRE,
4891 level.em_explodes_by_fire);
4894 // this is needed because some graphics depend on element properties
4895 if (game_status == GAME_MODE_PLAYING)
4896 InitElementGraphicInfo();
4899 void InitElementPropertiesGfxElement(void)
4903 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4905 struct ElementInfo *ei = &element_info[i];
4907 ei->gfx_element = (ei->use_gfx_element ? ei->gfx_element_initial : i);
4911 static void InitGlobal(void)
4916 for (i = 0; i < MAX_NUM_ELEMENTS + 1; i++)
4918 // check if element_name_info entry defined for each element in "main.h"
4919 if (i < MAX_NUM_ELEMENTS && element_name_info[i].token_name == NULL)
4920 Fail("undefined 'element_name_info' entry for element %d", i);
4922 element_info[i].token_name = element_name_info[i].token_name;
4923 element_info[i].class_name = element_name_info[i].class_name;
4924 element_info[i].editor_description= element_name_info[i].editor_description;
4927 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS + 1; i++)
4929 // check if global_anim_name_info defined for each entry in "main.h"
4930 if (i < NUM_GLOBAL_ANIM_TOKENS &&
4931 global_anim_name_info[i].token_name == NULL)
4932 Fail("undefined 'global_anim_name_info' entry for anim %d", i);
4934 global_anim_info[i].token_name = global_anim_name_info[i].token_name;
4937 // create hash to store URLs for global animations
4938 anim_url_hash = newSetupFileHash();
4940 // create hash from image config list
4941 image_config_hash = newSetupFileHash();
4942 for (i = 0; image_config[i].token != NULL; i++)
4943 setHashEntry(image_config_hash,
4944 image_config[i].token,
4945 image_config[i].value);
4947 // create hash from element token list
4948 element_token_hash = newSetupFileHash();
4949 for (i = 0; element_name_info[i].token_name != NULL; i++)
4950 setHashEntry(element_token_hash,
4951 element_name_info[i].token_name,
4954 // create hash from graphic token list
4955 graphic_token_hash = newSetupFileHash();
4956 for (graphic = 0, i = 0; image_config[i].token != NULL; i++)
4957 if (strSuffix(image_config[i].value, ".png") ||
4958 strSuffix(image_config[i].value, ".pcx") ||
4959 strSuffix(image_config[i].value, ".wav") ||
4960 strEqual(image_config[i].value, UNDEFINED_FILENAME))
4961 setHashEntry(graphic_token_hash,
4962 image_config[i].token,
4963 int2str(graphic++, 0));
4965 // create hash from font token list
4966 font_token_hash = newSetupFileHash();
4967 for (i = 0; font_info[i].token_name != NULL; i++)
4968 setHashEntry(font_token_hash,
4969 font_info[i].token_name,
4972 // set default filenames for all cloned graphics in static configuration
4973 for (i = 0; image_config[i].token != NULL; i++)
4975 if (strEqual(image_config[i].value, UNDEFINED_FILENAME))
4977 char *token = image_config[i].token;
4978 char *token_clone_from = getStringCat2(token, ".clone_from");
4979 char *token_cloned = getHashEntry(image_config_hash, token_clone_from);
4981 if (token_cloned != NULL)
4983 char *value_cloned = getHashEntry(image_config_hash, token_cloned);
4985 if (value_cloned != NULL)
4987 // set default filename in static configuration
4988 image_config[i].value = value_cloned;
4990 // set default filename in image config hash
4991 setHashEntry(image_config_hash, token, value_cloned);
4995 free(token_clone_from);
4999 // always start with reliable default values (all elements)
5000 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5001 ActiveElement[i] = i;
5003 // now add all entries that have an active state (active elements)
5004 for (i = 0; element_with_active_state[i].element != -1; i++)
5006 int element = element_with_active_state[i].element;
5007 int element_active = element_with_active_state[i].element_active;
5009 ActiveElement[element] = element_active;
5012 // always start with reliable default values (all buttons)
5013 for (i = 0; i < NUM_IMAGE_FILES; i++)
5014 ActiveButton[i] = i;
5016 // now add all entries that have an active state (active buttons)
5017 for (i = 0; button_with_active_state[i].button != -1; i++)
5019 int button = button_with_active_state[i].button;
5020 int button_active = button_with_active_state[i].button_active;
5022 ActiveButton[button] = button_active;
5025 // always start with reliable default values (all fonts)
5026 for (i = 0; i < NUM_FONTS; i++)
5029 // now add all entries that have an active state (active fonts)
5030 for (i = 0; font_with_active_state[i].font_nr != -1; i++)
5032 int font = font_with_active_state[i].font_nr;
5033 int font_active = font_with_active_state[i].font_nr_active;
5035 ActiveFont[font] = font_active;
5038 global.autoplay_leveldir = NULL;
5039 global.patchtapes_leveldir = NULL;
5040 global.convert_leveldir = NULL;
5041 global.dumplevel_leveldir = NULL;
5042 global.dumptape_leveldir = NULL;
5043 global.create_sketch_images_dir = NULL;
5044 global.create_collect_images_dir = NULL;
5046 global.frames_per_second = 0;
5047 global.show_frames_per_second = FALSE;
5049 global.border_status = GAME_MODE_LOADING;
5050 global.anim_status = global.anim_status_next = GAME_MODE_LOADING;
5052 global.use_envelope_request = FALSE;
5054 global.user_names = NULL;
5057 static void Execute_Command(char *command)
5061 if (strEqual(command, "print graphicsinfo.conf"))
5063 Print("# You can configure additional/alternative image files here.\n");
5064 Print("# (The entries below are default and therefore commented out.)\n");
5066 Print("%s\n", getFormattedSetupEntry("name", "Classic Graphics"));
5068 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5071 for (i = 0; image_config[i].token != NULL; i++)
5072 Print("# %s\n", getFormattedSetupEntry(image_config[i].token,
5073 image_config[i].value));
5077 else if (strEqual(command, "print soundsinfo.conf"))
5079 Print("# You can configure additional/alternative sound files here.\n");
5080 Print("# (The entries below are default and therefore commented out.)\n");
5082 Print("%s\n", getFormattedSetupEntry("name", "Classic Sounds"));
5084 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5087 for (i = 0; sound_config[i].token != NULL; i++)
5088 Print("# %s\n", getFormattedSetupEntry(sound_config[i].token,
5089 sound_config[i].value));
5093 else if (strEqual(command, "print musicinfo.conf"))
5095 Print("# You can configure additional/alternative music files here.\n");
5096 Print("# (The entries below are default and therefore commented out.)\n");
5098 Print("%s\n", getFormattedSetupEntry("name", "Classic Music"));
5100 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
5103 for (i = 0; music_config[i].token != NULL; i++)
5104 Print("# %s\n", getFormattedSetupEntry(music_config[i].token,
5105 music_config[i].value));
5109 else if (strEqual(command, "print editorsetup.conf"))
5111 Print("# You can configure your personal editor element list here.\n");
5112 Print("# (The entries below are default and therefore commented out.)\n");
5115 // this is needed to be able to check element list for cascade elements
5116 InitElementPropertiesStatic();
5117 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
5119 PrintEditorElementList();
5123 else if (strEqual(command, "print helpanim.conf"))
5125 Print("# You can configure different element help animations here.\n");
5126 Print("# (The entries below are default and therefore commented out.)\n");
5129 for (i = 0; helpanim_config[i].token != NULL; i++)
5131 Print("# %s\n", getFormattedSetupEntry(helpanim_config[i].token,
5132 helpanim_config[i].value));
5134 if (strEqual(helpanim_config[i].token, "end"))
5140 else if (strEqual(command, "print helptext.conf"))
5142 Print("# You can configure different element help text here.\n");
5143 Print("# (The entries below are default and therefore commented out.)\n");
5146 for (i = 0; helptext_config[i].token != NULL; i++)
5147 Print("# %s\n", getFormattedSetupEntry(helptext_config[i].token,
5148 helptext_config[i].value));
5152 else if (strPrefix(command, "dump level "))
5154 char *filename = &command[11];
5156 if (fileExists(filename))
5158 LoadLevelFromFilename(&level, filename);
5164 char *leveldir = getStringCopy(filename); // read command parameters
5165 char *level_nr = strchr(leveldir, ' ');
5167 if (level_nr == NULL)
5168 Fail("cannot open file '%s'", filename);
5172 global.dumplevel_leveldir = leveldir;
5173 global.dumplevel_level_nr = atoi(level_nr);
5175 program.headless = TRUE;
5177 else if (strPrefix(command, "dump tape "))
5179 char *filename = &command[10];
5181 if (fileExists(filename))
5183 LoadTapeFromFilename(filename);
5189 char *leveldir = getStringCopy(filename); // read command parameters
5190 char *level_nr = strchr(leveldir, ' ');
5192 if (level_nr == NULL)
5193 Fail("cannot open file '%s'", filename);
5197 global.dumptape_leveldir = leveldir;
5198 global.dumptape_level_nr = atoi(level_nr);
5200 program.headless = TRUE;
5202 else if (strPrefix(command, "autoplay ") ||
5203 strPrefix(command, "autoffwd ") ||
5204 strPrefix(command, "autowarp ") ||
5205 strPrefix(command, "autotest ") ||
5206 strPrefix(command, "autosave ") ||
5207 strPrefix(command, "autoupload ") ||
5208 strPrefix(command, "autofix "))
5210 char *arg_ptr = strchr(command, ' ');
5211 char *str_ptr = getStringCopy(arg_ptr); // read command parameters
5213 global.autoplay_mode =
5214 (strPrefix(command, "autoplay") ? AUTOPLAY_MODE_PLAY :
5215 strPrefix(command, "autoffwd") ? AUTOPLAY_MODE_FFWD :
5216 strPrefix(command, "autowarp") ? AUTOPLAY_MODE_WARP :
5217 strPrefix(command, "autotest") ? AUTOPLAY_MODE_TEST :
5218 strPrefix(command, "autosave") ? AUTOPLAY_MODE_SAVE :
5219 strPrefix(command, "autoupload") ? AUTOPLAY_MODE_UPLOAD :
5220 strPrefix(command, "autofix") ? AUTOPLAY_MODE_FIX :
5221 AUTOPLAY_MODE_NONE);
5223 while (*str_ptr != '\0') // continue parsing string
5225 // cut leading whitespace from string, replace it by string terminator
5226 while (*str_ptr == ' ' || *str_ptr == '\t')
5229 if (*str_ptr == '\0') // end of string reached
5232 if (global.autoplay_leveldir == NULL) // read level set string
5234 global.autoplay_leveldir = str_ptr;
5235 global.autoplay_all = TRUE; // default: play all tapes
5237 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5238 global.autoplay_level[i] = FALSE;
5240 else // read level number string
5242 int level_nr = atoi(str_ptr); // get level_nr value
5244 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5245 global.autoplay_level[level_nr] = TRUE;
5247 global.autoplay_all = FALSE;
5250 // advance string pointer to the next whitespace (or end of string)
5251 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5255 if (global.autoplay_mode & AUTOPLAY_WARP_NO_DISPLAY)
5256 program.headless = TRUE;
5258 else if (strPrefix(command, "patch tapes "))
5260 char *str_ptr = getStringCopy(&command[12]); // read command parameters
5262 // skip leading whitespace
5263 while (*str_ptr == ' ' || *str_ptr == '\t')
5266 if (*str_ptr == '\0')
5267 Fail("cannot find MODE in command '%s'", command);
5269 global.patchtapes_mode = str_ptr; // store patch mode
5271 // advance to next whitespace (or end of string)
5272 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5275 while (*str_ptr != '\0') // continue parsing string
5277 // cut leading whitespace from string, replace it by string terminator
5278 while (*str_ptr == ' ' || *str_ptr == '\t')
5281 if (*str_ptr == '\0') // end of string reached
5284 if (global.patchtapes_leveldir == NULL) // read level set string
5286 global.patchtapes_leveldir = str_ptr;
5287 global.patchtapes_all = TRUE; // default: patch all tapes
5289 for (i = 0; i < MAX_TAPES_PER_SET; i++)
5290 global.patchtapes_level[i] = FALSE;
5292 else // read level number string
5294 int level_nr = atoi(str_ptr); // get level_nr value
5296 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
5297 global.patchtapes_level[level_nr] = TRUE;
5299 global.patchtapes_all = FALSE;
5302 // advance string pointer to the next whitespace (or end of string)
5303 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
5307 if (global.patchtapes_leveldir == NULL)
5309 if (strEqual(global.patchtapes_mode, "help"))
5310 global.patchtapes_leveldir = UNDEFINED_LEVELSET;
5312 Fail("cannot find LEVELDIR in command '%s'", command);
5315 program.headless = TRUE;
5317 else if (strPrefix(command, "convert "))
5319 char *str_copy = getStringCopy(strchr(command, ' ') + 1);
5320 char *str_ptr = strchr(str_copy, ' ');
5322 global.convert_leveldir = str_copy;
5323 global.convert_level_nr = -1;
5325 if (str_ptr != NULL) // level number follows
5327 *str_ptr++ = '\0'; // terminate leveldir string
5328 global.convert_level_nr = atoi(str_ptr); // get level_nr value
5331 program.headless = TRUE;
5333 else if (strPrefix(command, "create sketch images "))
5335 global.create_sketch_images_dir = getStringCopy(&command[21]);
5337 if (access(global.create_sketch_images_dir, W_OK) != 0)
5338 Fail("image target directory '%s' not found or not writable",
5339 global.create_sketch_images_dir);
5341 else if (strPrefix(command, "create collect image "))
5343 global.create_collect_images_dir = getStringCopy(&command[21]);
5345 if (access(global.create_collect_images_dir, W_OK) != 0)
5346 Fail("image target directory '%s' not found or not writable",
5347 global.create_collect_images_dir);
5349 else if (strPrefix(command, "create CE image "))
5351 CreateCustomElementImages(&command[16]);
5357 FailWithHelp("unrecognized command '%s'", command);
5360 // disable networking if any valid command was recognized
5361 options.network = setup.network_mode = FALSE;
5364 static void InitSetup(void)
5366 LoadUserNames(); // global user names
5367 LoadUserSetup(); // global user number
5369 LoadSetup(); // global setup info
5371 // set some options from setup file
5373 if (setup.options.verbose)
5374 options.verbose = TRUE;
5376 if (setup.debug.show_frames_per_second)
5377 global.show_frames_per_second = TRUE;
5380 static void InitGameInfo(void)
5382 game.restart_level = FALSE;
5383 game.restart_game_message = NULL;
5385 game.request_active = FALSE;
5386 game.request_active_or_moving = FALSE;
5388 game.use_masked_elements_initial = FALSE;
5391 static void InitPlayerInfo(void)
5395 // choose default local player
5396 local_player = &stored_player[0];
5398 for (i = 0; i < MAX_PLAYERS; i++)
5400 stored_player[i].connected_locally = FALSE;
5401 stored_player[i].connected_network = FALSE;
5404 local_player->connected_locally = TRUE;
5407 static void InitArtworkInfo(void)
5412 static char *get_string_in_brackets(char *string)
5414 char *string_in_brackets = checked_malloc(strlen(string) + 3);
5416 sprintf(string_in_brackets, "[%s]", string);
5418 return string_in_brackets;
5421 static char *get_level_id_suffix(int id_nr)
5423 char *id_suffix = checked_malloc(1 + 3 + 1);
5425 if (id_nr < 0 || id_nr > 999)
5428 sprintf(id_suffix, ".%03d", id_nr);
5433 static void InitArtworkConfig(void)
5435 static char *image_id_prefix[MAX_NUM_ELEMENTS +
5437 NUM_GLOBAL_ANIM_TOKENS + 1];
5438 static char *sound_id_prefix[2 * MAX_NUM_ELEMENTS +
5439 NUM_GLOBAL_ANIM_TOKENS + 1];
5440 static char *music_id_prefix[NUM_MUSIC_PREFIXES +
5441 NUM_GLOBAL_ANIM_TOKENS + 1];
5442 static char *action_id_suffix[NUM_ACTIONS + 1];
5443 static char *direction_id_suffix[NUM_DIRECTIONS_FULL + 1];
5444 static char *special_id_suffix[NUM_SPECIAL_GFX_ARGS + 1];
5445 static char *level_id_suffix[MAX_LEVELS + 1];
5446 static char *dummy[1] = { NULL };
5447 static char *ignore_generic_tokens[] =
5452 "program_copyright",
5457 static char **ignore_image_tokens;
5458 static char **ignore_sound_tokens;
5459 static char **ignore_music_tokens;
5460 int num_ignore_generic_tokens;
5461 int num_ignore_image_tokens;
5462 int num_ignore_sound_tokens;
5463 int num_ignore_music_tokens;
5466 // dynamically determine list of generic tokens to be ignored
5467 num_ignore_generic_tokens = 0;
5468 for (i = 0; ignore_generic_tokens[i] != NULL; i++)
5469 num_ignore_generic_tokens++;
5471 // dynamically determine list of image tokens to be ignored
5472 num_ignore_image_tokens = num_ignore_generic_tokens;
5473 for (i = 0; image_config_vars[i].token != NULL; i++)
5474 num_ignore_image_tokens++;
5475 ignore_image_tokens =
5476 checked_malloc((num_ignore_image_tokens + 1) * sizeof(char *));
5477 for (i = 0; i < num_ignore_generic_tokens; i++)
5478 ignore_image_tokens[i] = ignore_generic_tokens[i];
5479 for (i = 0; i < num_ignore_image_tokens - num_ignore_generic_tokens; i++)
5480 ignore_image_tokens[num_ignore_generic_tokens + i] =
5481 image_config_vars[i].token;
5482 ignore_image_tokens[num_ignore_image_tokens] = NULL;
5484 // dynamically determine list of sound tokens to be ignored
5485 num_ignore_sound_tokens = num_ignore_generic_tokens;
5486 ignore_sound_tokens =
5487 checked_malloc((num_ignore_sound_tokens + 1) * sizeof(char *));
5488 for (i = 0; i < num_ignore_generic_tokens; i++)
5489 ignore_sound_tokens[i] = ignore_generic_tokens[i];
5490 ignore_sound_tokens[num_ignore_sound_tokens] = NULL;
5492 // dynamically determine list of music tokens to be ignored
5493 num_ignore_music_tokens = num_ignore_generic_tokens;
5494 ignore_music_tokens =
5495 checked_malloc((num_ignore_music_tokens + 1) * sizeof(char *));
5496 for (i = 0; i < num_ignore_generic_tokens; i++)
5497 ignore_music_tokens[i] = ignore_generic_tokens[i];
5498 ignore_music_tokens[num_ignore_music_tokens] = NULL;
5500 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5501 image_id_prefix[i] = element_info[i].token_name;
5502 for (i = 0; i < NUM_FONTS; i++)
5503 image_id_prefix[MAX_NUM_ELEMENTS + i] = font_info[i].token_name;
5504 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5505 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + i] =
5506 global_anim_info[i].token_name;
5507 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5509 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5510 sound_id_prefix[i] = element_info[i].token_name;
5511 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
5512 sound_id_prefix[MAX_NUM_ELEMENTS + i] =
5513 get_string_in_brackets(element_info[i].class_name);
5514 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5515 sound_id_prefix[2 * MAX_NUM_ELEMENTS + i] =
5516 global_anim_info[i].token_name;
5517 sound_id_prefix[2 * MAX_NUM_ELEMENTS + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5519 for (i = 0; i < NUM_MUSIC_PREFIXES; i++)
5520 music_id_prefix[i] = music_prefix_info[i].prefix;
5521 for (i = 0; i < NUM_GLOBAL_ANIM_TOKENS; i++)
5522 music_id_prefix[NUM_MUSIC_PREFIXES + i] =
5523 global_anim_info[i].token_name;
5524 music_id_prefix[NUM_MUSIC_PREFIXES + NUM_GLOBAL_ANIM_TOKENS] = NULL;
5526 for (i = 0; i < NUM_ACTIONS; i++)
5527 action_id_suffix[i] = element_action_info[i].suffix;
5528 action_id_suffix[NUM_ACTIONS] = NULL;
5530 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
5531 direction_id_suffix[i] = element_direction_info[i].suffix;
5532 direction_id_suffix[NUM_DIRECTIONS_FULL] = NULL;
5534 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
5535 special_id_suffix[i] = special_suffix_info[i].suffix;
5536 special_id_suffix[NUM_SPECIAL_GFX_ARGS] = NULL;
5538 for (i = 0; i < MAX_LEVELS; i++)
5539 level_id_suffix[i] = get_level_id_suffix(i);
5540 level_id_suffix[MAX_LEVELS] = NULL;
5542 InitImageList(image_config, NUM_IMAGE_FILES, image_config_suffix,
5543 image_id_prefix, action_id_suffix, direction_id_suffix,
5544 special_id_suffix, ignore_image_tokens);
5545 InitSoundList(sound_config, NUM_SOUND_FILES, sound_config_suffix,
5546 sound_id_prefix, action_id_suffix, dummy,
5547 special_id_suffix, ignore_sound_tokens);
5548 InitMusicList(music_config, NUM_MUSIC_FILES, music_config_suffix,
5549 music_id_prefix, action_id_suffix, special_id_suffix,
5550 level_id_suffix, ignore_music_tokens);
5553 static void InitMixer(void)
5560 static void InitVideoOverlay(void)
5562 // if virtual buttons are not loaded from setup file, repeat initializing
5563 // virtual buttons grid with default values now that video is initialized
5564 if (!setup.touch.grid_initialized)
5567 InitTileCursorInfo();
5571 void InitGfxBuffers(void)
5573 static int win_xsize_last = -1;
5574 static int win_ysize_last = -1;
5576 // create additional image buffers for double-buffering and cross-fading
5578 if (WIN_XSIZE != win_xsize_last || WIN_YSIZE != win_ysize_last)
5580 // used to temporarily store the backbuffer -- only re-create if changed
5581 ReCreateBitmap(&bitmap_db_store_1, WIN_XSIZE, WIN_YSIZE);
5582 ReCreateBitmap(&bitmap_db_store_2, WIN_XSIZE, WIN_YSIZE);
5584 win_xsize_last = WIN_XSIZE;
5585 win_ysize_last = WIN_YSIZE;
5588 ReCreateBitmap(&bitmap_db_field, FXSIZE, FYSIZE);
5589 ReCreateBitmap(&bitmap_db_panel, DXSIZE, DYSIZE);
5590 ReCreateBitmap(&bitmap_db_door_1, 3 * DXSIZE, DYSIZE);
5591 ReCreateBitmap(&bitmap_db_door_2, 3 * VXSIZE, VYSIZE);
5593 // initialize screen properties
5594 InitGfxFieldInfo(SX, SY, SXSIZE, SYSIZE,
5595 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
5597 InitGfxDoor1Info(DX, DY, DXSIZE, DYSIZE);
5598 InitGfxDoor2Info(VX, VY, VXSIZE, VYSIZE);
5599 InitGfxDoor3Info(EX, EY, EXSIZE, EYSIZE);
5600 InitGfxWindowInfo(WIN_XSIZE, WIN_YSIZE);
5601 InitGfxScrollbufferInfo(FXSIZE, FYSIZE);
5602 InitGfxClipRegion(FALSE, -1, -1, -1, -1);
5604 // required if door size definitions have changed
5605 InitGraphicCompatibilityInfo_Doors();
5607 InitGfxBuffers_EM();
5608 InitGfxBuffers_SP();
5611 static void InitGfx(void)
5613 struct GraphicInfo *graphic_info_last = graphic_info;
5614 char *filename_font_initial = NULL;
5615 char *filename_image_initial[NUM_INITIAL_IMAGES] = { NULL };
5616 char *image_token[NUM_INITIAL_IMAGES] =
5618 CONFIG_TOKEN_GLOBAL_BUSY_INITIAL,
5619 CONFIG_TOKEN_GLOBAL_BUSY,
5620 CONFIG_TOKEN_GLOBAL_BUSY_PLAYFIELD,
5621 CONFIG_TOKEN_BACKGROUND_LOADING_INITIAL,
5622 CONFIG_TOKEN_BACKGROUND_LOADING
5624 struct MenuPosInfo *init_busy[NUM_INITIAL_IMAGES_BUSY] =
5628 &init.busy_playfield
5630 Bitmap *bitmap_font_initial = NULL;
5631 int parameter[NUM_INITIAL_IMAGES][NUM_GFX_ARGS];
5634 // determine settings for initial font (for displaying startup messages)
5635 for (i = 0; image_config[i].token != NULL; i++)
5637 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5639 char font_token[128];
5642 sprintf(font_token, "%s_%d", CONFIG_TOKEN_FONT_INITIAL, j + 1);
5643 len_font_token = strlen(font_token);
5645 if (strEqual(image_config[i].token, font_token))
5647 filename_font_initial = image_config[i].value;
5649 else if (strlen(image_config[i].token) > len_font_token &&
5650 strncmp(image_config[i].token, font_token, len_font_token) == 0)
5652 if (strEqual(&image_config[i].token[len_font_token], ".x"))
5653 font_initial[j].src_x = atoi(image_config[i].value);
5654 else if (strEqual(&image_config[i].token[len_font_token], ".y"))
5655 font_initial[j].src_y = atoi(image_config[i].value);
5656 else if (strEqual(&image_config[i].token[len_font_token], ".width"))
5657 font_initial[j].width = atoi(image_config[i].value);
5658 else if (strEqual(&image_config[i].token[len_font_token], ".height"))
5659 font_initial[j].height = atoi(image_config[i].value);
5664 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5666 font_initial[j].num_chars = DEFAULT_NUM_CHARS_PER_FONT;
5667 font_initial[j].num_chars_per_line = DEFAULT_NUM_CHARS_PER_LINE;
5670 if (filename_font_initial == NULL) // should not happen
5671 Fail("cannot get filename for '%s'", CONFIG_TOKEN_FONT_INITIAL);
5674 InitGfxCustomArtworkInfo();
5675 InitGfxOtherSettings();
5677 InitGfxTileSizeInfo(TILESIZE, TILESIZE);
5679 bitmap_font_initial = LoadCustomImage(filename_font_initial);
5681 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5682 font_initial[j].bitmap = bitmap_font_initial;
5684 InitFontGraphicInfo();
5688 DrawInitTextHead("Loading graphics");
5690 InitMenuDesignSettings_Static();
5692 // initialize settings for initial images with default values
5693 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5694 for (j = 0; j < NUM_GFX_ARGS; j++)
5696 get_graphic_parameter_value(image_config_suffix[j].value,
5697 image_config_suffix[j].token,
5698 image_config_suffix[j].type);
5700 // read settings for initial images from default custom artwork config
5701 char *gfx_config_filename = getPath3(options.graphics_directory,
5703 GRAPHICSINFO_FILENAME);
5705 if (fileExists(gfx_config_filename))
5707 SetupFileHash *setup_file_hash = loadSetupFileHash(gfx_config_filename);
5709 if (setup_file_hash)
5711 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5713 char *filename = getHashEntry(setup_file_hash, image_token[i]);
5717 filename_image_initial[i] = getStringCopy(filename);
5719 for (j = 0; image_config_suffix[j].token != NULL; j++)
5721 int type = image_config_suffix[j].type;
5722 char *suffix = image_config_suffix[j].token;
5723 char *token = getStringCat2(image_token[i], suffix);
5724 char *value = getHashEntry(setup_file_hash, token);
5726 checked_free(token);
5730 get_graphic_parameter_value(value, suffix, type);
5735 // read values from custom graphics config file
5736 InitMenuDesignSettings_FromHash(setup_file_hash, FALSE);
5738 freeSetupFileHash(setup_file_hash);
5742 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5744 if (filename_image_initial[i] == NULL)
5746 int len_token = strlen(image_token[i]);
5748 // read settings for initial images from static default artwork config
5749 for (j = 0; image_config[j].token != NULL; j++)
5751 if (strEqual(image_config[j].token, image_token[i]))
5753 filename_image_initial[i] = getStringCopy(image_config[j].value);
5755 else if (strlen(image_config[j].token) > len_token &&
5756 strncmp(image_config[j].token, image_token[i], len_token) == 0)
5758 for (k = 0; image_config_suffix[k].token != NULL; k++)
5760 if (strEqual(&image_config[j].token[len_token],
5761 image_config_suffix[k].token))
5763 get_graphic_parameter_value(image_config[j].value,
5764 image_config_suffix[k].token,
5765 image_config_suffix[k].type);
5772 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5774 if (filename_image_initial[i] == NULL) // should not happen
5775 Fail("cannot get filename for '%s'", image_token[i]);
5777 image_initial[i].bitmaps =
5778 checked_calloc(sizeof(Bitmap *) * NUM_IMG_BITMAP_POINTERS);
5780 if (!strEqual(filename_image_initial[i], UNDEFINED_FILENAME))
5781 image_initial[i].bitmaps[IMG_BITMAP_STANDARD] =
5782 LoadCustomImage(filename_image_initial[i]);
5784 checked_free(filename_image_initial[i]);
5787 graphic_info = image_initial; // graphic == 0 => image_initial
5789 for (i = 0; i < NUM_INITIAL_IMAGES; i++)
5790 set_graphic_parameters_ext(i, parameter[i], image_initial[i].bitmaps);
5792 graphic_info = graphic_info_last;
5794 for (i = 0; i < NUM_INITIAL_IMAGES_BUSY; i++)
5796 // set image size for busy animations
5797 init_busy[i]->width = image_initial[i].width;
5798 init_busy[i]->height = image_initial[i].height;
5801 SetLoadingBackgroundImage();
5803 ClearRectangleOnBackground(window, 0, 0, WIN_XSIZE, WIN_YSIZE);
5805 InitGfxDrawBusyAnimFunction(DrawInitAnim);
5806 InitGfxDrawGlobalAnimFunction(DrawGlobalAnimations);
5807 InitGfxDrawGlobalBorderFunction(DrawMaskedBorderToTarget);
5808 InitGfxDrawTileCursorFunction(DrawTileCursor);
5810 gfx.fade_border_source_status = global.border_status;
5811 gfx.fade_border_target_status = global.border_status;
5812 gfx.masked_border_bitmap_ptr = backbuffer;
5814 // use copy of busy animation to prevent change while reloading artwork
5818 static void InitGfxBackground(void)
5820 fieldbuffer = bitmap_db_field;
5821 SetDrawtoField(DRAW_TO_BACKBUFFER);
5823 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
5825 redraw_mask = REDRAW_ALL;
5828 static void InitLevelInfo(void)
5830 LoadLevelInfo(); // global level info
5831 LoadLevelSetup_LastSeries(); // last played series info
5832 LoadLevelSetup_SeriesInfo(); // last played level info
5834 if (global.autoplay_leveldir &&
5835 global.autoplay_mode != AUTOPLAY_MODE_TEST)
5837 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
5838 global.autoplay_leveldir);
5839 if (leveldir_current == NULL)
5840 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
5843 SetLevelSetInfo(leveldir_current->identifier, level_nr);
5846 static void InitLevelArtworkInfo(void)
5848 LoadLevelArtworkInfo();
5851 static void InitImages(void)
5853 print_timestamp_init("InitImages");
5856 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
5857 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5858 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
5859 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5860 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
5861 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5862 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
5863 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5866 setLevelArtworkDir(artwork.gfx_first);
5869 Debug("init:InitImages", "leveldir_current->identifier == '%s'",
5870 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5871 Debug("init:InitImages", "leveldir_current->graphics_path == '%s'",
5872 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5873 Debug("init:InitImages", "leveldir_current->graphics_set == '%s'",
5874 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5875 Debug("init:InitImages", "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
5876 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5880 Debug("init:InitImages", "InitImages for '%s' ['%s', '%s'] ['%s', '%s']",
5881 leveldir_current->identifier,
5882 artwork.gfx_current_identifier,
5883 artwork.gfx_current->identifier,
5884 leveldir_current->graphics_set,
5885 leveldir_current->graphics_path);
5888 UPDATE_BUSY_STATE();
5890 ReloadCustomImages();
5891 print_timestamp_time("ReloadCustomImages");
5893 UPDATE_BUSY_STATE();
5895 LoadCustomElementDescriptions();
5896 print_timestamp_time("LoadCustomElementDescriptions");
5898 UPDATE_BUSY_STATE();
5900 LoadMenuDesignSettings();
5901 print_timestamp_time("LoadMenuDesignSettings");
5903 UPDATE_BUSY_STATE();
5905 ReinitializeGraphics();
5906 print_timestamp_time("ReinitializeGraphics");
5908 LoadMenuDesignSettings_AfterGraphics();
5909 print_timestamp_time("LoadMenuDesignSettings_AfterGraphics");
5911 UPDATE_BUSY_STATE();
5913 print_timestamp_done("InitImages");
5916 static void InitSound(char *identifier)
5918 print_timestamp_init("InitSound");
5920 if (identifier == NULL)
5921 identifier = artwork.snd_current->identifier;
5923 // set artwork path to send it to the sound server process
5924 setLevelArtworkDir(artwork.snd_first);
5926 InitReloadCustomSounds(identifier);
5927 print_timestamp_time("InitReloadCustomSounds");
5929 ReinitializeSounds();
5930 print_timestamp_time("ReinitializeSounds");
5932 print_timestamp_done("InitSound");
5935 static void InitMusic(char *identifier)
5937 print_timestamp_init("InitMusic");
5939 if (identifier == NULL)
5940 identifier = artwork.mus_current->identifier;
5942 // set artwork path to send it to the sound server process
5943 setLevelArtworkDir(artwork.mus_first);
5945 InitReloadCustomMusic(identifier);
5946 print_timestamp_time("InitReloadCustomMusic");
5948 ReinitializeMusic();
5949 print_timestamp_time("ReinitializeMusic");
5951 print_timestamp_done("InitMusic");
5954 static void InitArtworkDone(void)
5956 if (program.headless)
5959 InitGlobalAnimations();
5962 static void InitNetworkSettings(void)
5964 boolean network_enabled = (options.network || setup.network_mode);
5965 char *network_server = (options.server_host != NULL ? options.server_host :
5966 setup.network_server_hostname);
5968 if (strEqual(network_server, STR_NETWORK_AUTO_DETECT))
5969 network_server = NULL;
5971 InitNetworkInfo(network_enabled,
5975 options.server_port);
5978 void InitNetworkServer(void)
5980 if (!network.enabled || network.connected)
5983 LimitScreenUpdates(FALSE);
5985 if (game_status == GAME_MODE_LOADING)
5988 if (!ConnectToServer(network.server_host, network.server_port))
5990 network.enabled = FALSE;
5992 setup.network_mode = FALSE;
5996 SendToServer_ProtocolVersion();
5997 SendToServer_PlayerName(setup.player_name);
5998 SendToServer_NrWanted(setup.network_player_nr + 1);
6000 network.connected = TRUE;
6003 // short time to recognize result of network initialization
6004 if (game_status == GAME_MODE_LOADING)
6005 Delay_WithScreenUpdates(1000);
6008 static boolean CheckArtworkConfigForCustomElements(char *filename)
6010 SetupFileHash *setup_file_hash;
6011 boolean redefined_ce_found = FALSE;
6013 // !!! CACHE THIS BY USING HASH 'filename' => 'true/false' !!!
6015 if ((setup_file_hash = loadSetupFileHash(filename)) != NULL)
6017 BEGIN_HASH_ITERATION(setup_file_hash, itr)
6019 char *token = HASH_ITERATION_TOKEN(itr);
6021 if (strPrefix(token, "custom_"))
6023 redefined_ce_found = TRUE;
6028 END_HASH_ITERATION(setup_file_hash, itr)
6030 freeSetupFileHash(setup_file_hash);
6033 return redefined_ce_found;
6036 static boolean CheckArtworkTypeForRedefinedCustomElements(int type)
6038 char *filename_base, *filename_local;
6039 boolean redefined_ce_found = FALSE;
6041 setLevelArtworkDir(ARTWORK_FIRST_NODE(artwork, type));
6044 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6045 "leveldir_current->identifier == '%s'",
6046 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
6047 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6048 "leveldir_current->graphics_path == '%s'",
6049 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
6050 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6051 "leveldir_current->graphics_set == '%s'",
6052 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
6053 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6054 "getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'",
6055 leveldir_current == NULL ? "[NULL]" :
6056 LEVELDIR_ARTWORK_SET(leveldir_current, type));
6059 // first look for special artwork configured in level series config
6060 filename_base = getCustomArtworkLevelConfigFilename(type);
6063 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6064 "filename_base == '%s'", filename_base);
6067 if (fileExists(filename_base))
6068 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_base);
6070 filename_local = getCustomArtworkConfigFilename(type);
6073 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6074 "filename_local == '%s'", filename_local);
6077 if (filename_local != NULL && !strEqual(filename_base, filename_local))
6078 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_local);
6081 Debug("init:CheckArtworkTypeForRedefinedCustomElements",
6082 "redefined_ce_found == %d", redefined_ce_found);
6085 return redefined_ce_found;
6088 static void InitOverrideArtwork(void)
6090 boolean redefined_ce_found = FALSE;
6092 // to check if this level set redefines any CEs, do not use overriding
6093 gfx.override_level_graphics = FALSE;
6094 gfx.override_level_sounds = FALSE;
6095 gfx.override_level_music = FALSE;
6097 // now check if this level set has definitions for custom elements
6098 if (setup.override_level_graphics == AUTO ||
6099 setup.override_level_sounds == AUTO ||
6100 setup.override_level_music == AUTO)
6101 redefined_ce_found =
6102 (CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_GRAPHICS) |
6103 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_SOUNDS) |
6104 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_MUSIC));
6107 Debug("init:InitOverrideArtwork", "redefined_ce_found == %d",
6108 redefined_ce_found);
6111 if (redefined_ce_found)
6113 // this level set has CE definitions: change "AUTO" to "FALSE"
6114 gfx.override_level_graphics = (setup.override_level_graphics == TRUE);
6115 gfx.override_level_sounds = (setup.override_level_sounds == TRUE);
6116 gfx.override_level_music = (setup.override_level_music == TRUE);
6120 // this level set has no CE definitions: change "AUTO" to "TRUE"
6121 gfx.override_level_graphics = (setup.override_level_graphics != FALSE);
6122 gfx.override_level_sounds = (setup.override_level_sounds != FALSE);
6123 gfx.override_level_music = (setup.override_level_music != FALSE);
6127 Debug("init:InitOverrideArtwork", "%d, %d, %d",
6128 gfx.override_level_graphics,
6129 gfx.override_level_sounds,
6130 gfx.override_level_music);
6134 static char *getNewArtworkIdentifier(int type)
6136 static char *last_leveldir_identifier[3] = { NULL, NULL, NULL };
6137 static char *last_artwork_identifier[3] = { NULL, NULL, NULL };
6138 static boolean last_override_level_artwork[3] = { FALSE, FALSE, FALSE };
6139 static boolean last_has_custom_artwork_set[3] = { FALSE, FALSE, FALSE };
6140 static boolean initialized[3] = { FALSE, FALSE, FALSE };
6141 TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type);
6142 boolean setup_override_artwork = GFX_OVERRIDE_ARTWORK(type);
6143 char *setup_artwork_set = SETUP_ARTWORK_SET(setup, type);
6144 char *leveldir_identifier = leveldir_current->identifier;
6145 // !!! setLevelArtworkDir() should be moved to an earlier stage !!!
6146 char *leveldir_artwork_set = setLevelArtworkDir(artwork_first_node);
6147 boolean has_level_artwork_set = (leveldir_artwork_set != NULL);
6148 TreeInfo *custom_artwork_set =
6149 getTreeInfoFromIdentifier(artwork_first_node, leveldir_identifier);
6150 boolean has_custom_artwork_set = (custom_artwork_set != NULL);
6151 char *artwork_current_identifier;
6152 char *artwork_new_identifier = NULL; // default: nothing has changed
6154 // leveldir_current may be invalid (level group, parent link)
6155 if (!validLevelSeries(leveldir_current))
6158 /* 1st step: determine artwork set to be activated in descending order:
6159 --------------------------------------------------------------------
6160 1. setup artwork (when configured to override everything else)
6161 2. artwork set configured in "levelinfo.conf" of current level set
6162 (artwork in level directory will have priority when loading later)
6163 3. artwork in level directory (stored in artwork sub-directory)
6164 4. setup artwork (currently configured in setup menu) */
6166 if (setup_override_artwork)
6167 artwork_current_identifier = setup_artwork_set;
6168 else if (has_level_artwork_set)
6169 artwork_current_identifier = leveldir_artwork_set;
6170 else if (has_custom_artwork_set)
6171 artwork_current_identifier = leveldir_identifier;
6173 artwork_current_identifier = setup_artwork_set;
6175 /* 2nd step: check if it is really needed to reload artwork set
6176 ------------------------------------------------------------ */
6178 // ---------- reload if level set and also artwork set has changed ----------
6179 if (last_leveldir_identifier[type] != leveldir_identifier &&
6180 (last_has_custom_artwork_set[type] || has_custom_artwork_set))
6181 artwork_new_identifier = artwork_current_identifier;
6183 last_leveldir_identifier[type] = leveldir_identifier;
6184 last_has_custom_artwork_set[type] = has_custom_artwork_set;
6186 // ---------- reload if "override artwork" setting has changed --------------
6187 if (last_override_level_artwork[type] != setup_override_artwork)
6188 artwork_new_identifier = artwork_current_identifier;
6190 last_override_level_artwork[type] = setup_override_artwork;
6192 // ---------- reload if current artwork identifier has changed --------------
6193 if (!strEqual(last_artwork_identifier[type], artwork_current_identifier))
6194 artwork_new_identifier = artwork_current_identifier;
6196 // (we cannot compare string pointers here, so copy string content itself)
6197 setString(&last_artwork_identifier[type], artwork_current_identifier);
6199 *(ARTWORK_CURRENT_IDENTIFIER_PTR(artwork, type)) = artwork_current_identifier;
6201 // ---------- do not reload directly after starting -------------------------
6202 if (!initialized[type])
6203 artwork_new_identifier = NULL;
6205 initialized[type] = TRUE;
6207 return artwork_new_identifier;
6210 void ReloadCustomArtwork(int force_reload)
6212 int last_game_status = game_status; // save current game status
6213 char *gfx_new_identifier;
6214 char *snd_new_identifier;
6215 char *mus_new_identifier;
6216 boolean force_reload_gfx = (force_reload & (1 << ARTWORK_TYPE_GRAPHICS));
6217 boolean force_reload_snd = (force_reload & (1 << ARTWORK_TYPE_SOUNDS));
6218 boolean force_reload_mus = (force_reload & (1 << ARTWORK_TYPE_MUSIC));
6219 boolean reload_needed;
6221 InitOverrideArtwork();
6223 AdjustGraphicsForEMC();
6224 AdjustSoundsForEMC();
6226 gfx_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
6227 snd_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
6228 mus_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
6230 reload_needed = (gfx_new_identifier != NULL || force_reload_gfx ||
6231 snd_new_identifier != NULL || force_reload_snd ||
6232 mus_new_identifier != NULL || force_reload_mus);
6237 print_timestamp_init("ReloadCustomArtwork");
6239 SetGameStatus(GAME_MODE_LOADING);
6241 FadeOut(REDRAW_ALL);
6243 SetLoadingBackgroundImage();
6245 ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6246 print_timestamp_time("ClearRectangleOnBackground");
6250 UPDATE_BUSY_STATE();
6252 if (gfx_new_identifier != NULL || force_reload_gfx)
6255 Debug("init:ReloadCustomArtwork",
6256 "RELOADING GRAPHICS '%s' -> '%s' ['%s', '%s']",
6257 artwork.gfx_current_identifier,
6259 artwork.gfx_current->identifier,
6260 leveldir_current->graphics_set);
6264 print_timestamp_time("InitImages");
6267 if (snd_new_identifier != NULL || force_reload_snd)
6269 InitSound(snd_new_identifier);
6270 print_timestamp_time("InitSound");
6273 if (mus_new_identifier != NULL || force_reload_mus)
6275 InitMusic(mus_new_identifier);
6276 print_timestamp_time("InitMusic");
6281 SetGameStatus(last_game_status); // restore current game status
6283 FadeOut(REDRAW_ALL);
6285 RedrawGlobalBorder();
6287 // force redraw of (open or closed) door graphics
6288 SetDoorState(DOOR_OPEN_ALL);
6289 CloseDoor(DOOR_CLOSE_ALL | DOOR_NO_DELAY);
6291 FadeSetEnterScreen();
6292 FadeSkipNextFadeOut();
6294 print_timestamp_done("ReloadCustomArtwork");
6296 LimitScreenUpdates(FALSE);
6299 void KeyboardAutoRepeatOffUnlessAutoplay(void)
6301 if (global.autoplay_leveldir == NULL)
6302 KeyboardAutoRepeatOff();
6305 void DisplayExitMessage(char *format, va_list ap)
6307 // also check for initialized video (headless flag may be temporarily unset)
6308 if (program.headless || !video.initialized)
6311 // check if draw buffer and fonts for exit message are already available
6312 if (drawto == NULL || font_initial[NUM_INITIAL_FONTS - 1].bitmap == NULL)
6315 int font_1 = FC_RED;
6316 int font_2 = FC_YELLOW;
6317 int font_3 = FC_BLUE;
6318 int font_width = getFontWidth(font_2);
6319 int font_height = getFontHeight(font_2);
6322 int sxsize = WIN_XSIZE - 2 * sx;
6323 int sysize = WIN_YSIZE - 2 * sy;
6324 int line_length = sxsize / font_width;
6325 int max_lines = sysize / font_height;
6326 int num_lines_printed;
6330 gfx.sxsize = sxsize;
6331 gfx.sysize = sysize;
6335 ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
6337 DrawTextSCentered(sy, font_1, "Fatal error:");
6338 sy += 3 * font_height;;
6341 DrawTextBufferVA(sx, sy, format, ap, font_2,
6342 line_length, line_length, max_lines,
6343 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6344 sy += (num_lines_printed + 3) * font_height;
6346 DrawTextSCentered(sy, font_1, "For details, see the following error file:");
6347 sy += 3 * font_height;
6350 DrawTextBuffer(sx, sy, program.log_filename[LOG_ERR_ID], font_2,
6351 line_length, line_length, max_lines,
6352 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
6354 DrawTextSCentered(SYSIZE - 20, font_3, "Press any key or button to exit");
6356 redraw_mask = REDRAW_ALL;
6358 // force drawing exit message even if screen updates are currently limited
6359 LimitScreenUpdates(FALSE);
6363 // deactivate toons on error message screen
6364 setup.toons = FALSE;
6366 WaitForEventToContinue();
6370 // ============================================================================
6372 // ============================================================================
6376 print_timestamp_init("OpenAll");
6378 SetGameStatus(GAME_MODE_LOADING);
6382 InitGlobal(); // initialize some global variables
6384 InitRND(NEW_RANDOMIZE);
6385 InitSimpleRandom(NEW_RANDOMIZE);
6386 InitBetterRandom(NEW_RANDOMIZE);
6388 print_timestamp_time("[init global stuff]");
6392 print_timestamp_time("[init setup/config stuff (1)]");
6394 if (options.execute_command)
6395 Execute_Command(options.execute_command);
6397 InitNetworkSettings();
6401 if (network.serveronly)
6403 #if defined(PLATFORM_UNIX)
6404 NetworkServer(network.server_port, TRUE);
6406 Warn("networking only supported in Unix version");
6409 exit(0); // never reached, server loops forever
6413 print_timestamp_time("[init setup/config stuff (2)]");
6415 print_timestamp_time("[init setup/config stuff (3)]");
6416 InitArtworkInfo(); // needed before loading gfx, sound & music
6417 print_timestamp_time("[init setup/config stuff (4)]");
6418 InitArtworkConfig(); // needed before forking sound child process
6419 print_timestamp_time("[init setup/config stuff (5)]");
6421 print_timestamp_time("[init setup/config stuff (6)]");
6425 print_timestamp_time("[init setup/config stuff]");
6427 InitVideoDefaults();
6429 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
6432 InitEventFilter(FilterMouseMotionEvents);
6434 print_timestamp_time("[init video stuff]");
6436 InitElementPropertiesStatic();
6437 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
6438 InitElementPropertiesGfxElement();
6440 print_timestamp_time("[init element properties stuff]");
6444 print_timestamp_time("InitGfx");
6447 print_timestamp_time("InitLevelInfo");
6449 InitLevelArtworkInfo();
6450 print_timestamp_time("InitLevelArtworkInfo");
6452 InitOverrideArtwork(); // needs to know current level directory
6453 print_timestamp_time("InitOverrideArtwork");
6455 InitImages(); // needs to know current level directory
6456 print_timestamp_time("InitImages");
6458 InitSound(NULL); // needs to know current level directory
6459 print_timestamp_time("InitSound");
6461 InitMusic(NULL); // needs to know current level directory
6462 print_timestamp_time("InitMusic");
6466 InitGfxBackground();
6472 if (global.autoplay_leveldir)
6477 else if (global.patchtapes_leveldir)
6482 else if (global.convert_leveldir)
6487 else if (global.dumplevel_leveldir)
6492 else if (global.dumptape_leveldir)
6497 else if (global.create_sketch_images_dir)
6499 CreateLevelSketchImages();
6502 else if (global.create_collect_images_dir)
6504 CreateCollectElementImages();
6508 InitNetworkServer();
6510 SetGameStatus(GAME_MODE_MAIN);
6512 FadeSetEnterScreen();
6513 if (!(fading.fade_mode & FADE_TYPE_TRANSFORM))
6514 FadeSkipNextFadeOut();
6516 print_timestamp_time("[post-artwork]");
6518 print_timestamp_done("OpenAll");
6520 if (setup.ask_for_remaining_tapes)
6521 setup.ask_for_uploading_tapes = TRUE;
6526 Debug("internal:path", "SDL_GetBasePath() == '%s'",
6528 Debug("internal:path", "SDL_GetPrefPath() == '%s'",
6529 SDL_GetPrefPath("artsoft", "rocksndiamonds"));
6530 #if defined(PLATFORM_ANDROID)
6531 Debug("internal:path", "SDL_AndroidGetInternalStoragePath() == '%s'",
6532 SDL_AndroidGetInternalStoragePath());
6533 Debug("internal:path", "SDL_AndroidGetExternalStoragePath() == '%s'",
6534 SDL_AndroidGetExternalStoragePath());
6535 Debug("internal:path", "SDL_AndroidGetExternalStorageState() == '%s'",
6536 (SDL_AndroidGetExternalStorageState() &
6537 SDL_ANDROID_EXTERNAL_STORAGE_WRITE ? "writable" :
6538 SDL_AndroidGetExternalStorageState() &
6539 SDL_ANDROID_EXTERNAL_STORAGE_READ ? "readable" : "not available"));
6544 static boolean WaitForApiThreads(void)
6546 unsigned int thread_delay = 0;
6547 unsigned int thread_delay_value = 10000;
6549 if (program.api_thread_count == 0)
6552 // deactivate global animations (not accessible in game state "loading")
6553 setup.toons = FALSE;
6555 // set game state to "loading" to be able to show busy animation
6556 SetGameStatus(GAME_MODE_LOADING);
6558 ResetDelayCounter(&thread_delay);
6560 // wait for threads to finish (and fail on timeout)
6561 while (program.api_thread_count > 0)
6563 if (DelayReached(&thread_delay, thread_delay_value))
6565 Error("failed waiting for threads - TIMEOUT");
6570 UPDATE_BUSY_STATE();
6578 void CloseAllAndExit(int exit_value)
6580 WaitForApiThreads();
6585 CloseAudio(); // called after freeing sounds (needed for SDL)
6593 // set a flag to tell the network server thread to quit and wait for it
6594 // using SDL_WaitThread()
6596 // Code used with SDL 1.2:
6597 // if (network.server_thread) // terminate network server
6598 // SDL_KillThread(network.server_thread);
6600 CloseVideoDisplay();
6601 ClosePlatformDependentStuff();
6603 if (exit_value != 0 && !options.execute_command)
6605 // fall back to default level set (current set may have caused an error)
6606 SaveLevelSetup_LastSeries_Deactivate();
6608 // tell user where to find error log file which may contain more details
6609 // (error notification now directly displayed on screen inside R'n'D
6610 // NotifyUserAboutErrorFile(); // currently only works for Windows