1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
12 #include "libgame/libgame.h"
27 #include "conf_e2g.c" /* include auto-generated data structure definitions */
28 #include "conf_esg.c" /* include auto-generated data structure definitions */
29 #include "conf_e2s.c" /* include auto-generated data structure definitions */
30 #include "conf_fnt.c" /* include auto-generated data structure definitions */
31 #include "conf_g2s.c" /* include auto-generated data structure definitions */
32 #include "conf_g2m.c" /* include auto-generated data structure definitions */
33 #include "conf_act.c" /* include auto-generated data structure definitions */
36 #define CONFIG_TOKEN_FONT_INITIAL "font.initial"
37 #define CONFIG_TOKEN_GLOBAL_BUSY "global.busy"
40 static struct FontBitmapInfo font_initial[NUM_INITIAL_FONTS];
41 static struct GraphicInfo anim_initial;
43 static int copy_properties[][5] =
47 EL_BUG_LEFT, EL_BUG_RIGHT,
48 EL_BUG_UP, EL_BUG_DOWN
52 EL_SPACESHIP_LEFT, EL_SPACESHIP_RIGHT,
53 EL_SPACESHIP_UP, EL_SPACESHIP_DOWN
57 EL_BD_BUTTERFLY_LEFT, EL_BD_BUTTERFLY_RIGHT,
58 EL_BD_BUTTERFLY_UP, EL_BD_BUTTERFLY_DOWN
62 EL_BD_FIREFLY_LEFT, EL_BD_FIREFLY_RIGHT,
63 EL_BD_FIREFLY_UP, EL_BD_FIREFLY_DOWN
67 EL_PACMAN_LEFT, EL_PACMAN_RIGHT,
68 EL_PACMAN_UP, EL_PACMAN_DOWN
72 EL_YAMYAM_LEFT, EL_YAMYAM_RIGHT,
73 EL_YAMYAM_UP, EL_YAMYAM_DOWN
77 EL_MOLE_LEFT, EL_MOLE_RIGHT,
78 EL_MOLE_UP, EL_MOLE_DOWN
89 struct GraphicInfo *graphic_info_last = graphic_info;
91 static unsigned int action_delay = 0;
92 unsigned int action_delay_value = GameFrameDelay;
93 int sync_frame = FrameCounter;
96 if (game_status != GAME_MODE_LOADING)
99 if (anim_initial.bitmap == NULL || window == NULL)
102 if (!DelayReached(&action_delay, action_delay_value))
105 if (init_last.busy.x == -1)
106 init_last.busy.x = WIN_XSIZE / 2;
107 if (init_last.busy.y == -1)
108 init_last.busy.y = WIN_YSIZE / 2;
110 x = ALIGNED_TEXT_XPOS(&init_last.busy);
111 y = ALIGNED_TEXT_YPOS(&init_last.busy);
113 graphic_info = &anim_initial; /* graphic == 0 => anim_initial */
115 if (sync_frame % anim_initial.anim_delay == 0)
119 int width = graphic_info[graphic].width;
120 int height = graphic_info[graphic].height;
121 int frame = getGraphicAnimationFrame(graphic, sync_frame);
123 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
124 BlitBitmap(src_bitmap, window, src_x, src_y, width, height, x, y);
127 graphic_info = graphic_info_last;
134 FreeLevelEditorGadgets();
143 static boolean gadgets_initialized = FALSE;
145 if (gadgets_initialized)
148 CreateLevelEditorGadgets();
152 CreateScreenGadgets();
154 InitGadgetsSoundCallback(PlaySoundActivating, PlaySoundSelecting);
156 gadgets_initialized = TRUE;
159 inline static void InitElementSmallImagesScaledUp(int graphic)
161 struct GraphicInfo *g = &graphic_info[graphic];
163 // create small and game tile sized bitmaps (and scale up, if needed)
164 CreateImageWithSmallImages(graphic, g->scale_up_factor, g->tile_size);
167 void InitElementSmallImages()
169 print_timestamp_init("InitElementSmallImages");
171 static int special_graphics[] =
173 IMG_EDITOR_ELEMENT_BORDER,
174 IMG_EDITOR_ELEMENT_BORDER_INPUT,
175 IMG_EDITOR_CASCADE_LIST,
176 IMG_EDITOR_CASCADE_LIST_ACTIVE,
179 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
180 int num_property_mappings = getImageListPropertyMappingSize();
183 print_timestamp_time("getImageListPropertyMapping/Size");
185 print_timestamp_init("InitElementSmallImagesScaledUp (1)");
186 /* initialize normal images from static configuration */
187 for (i = 0; element_to_graphic[i].element > -1; i++)
188 InitElementSmallImagesScaledUp(element_to_graphic[i].graphic);
189 print_timestamp_done("InitElementSmallImagesScaledUp (1)");
191 /* initialize special images from static configuration */
192 for (i = 0; element_to_special_graphic[i].element > -1; i++)
193 InitElementSmallImagesScaledUp(element_to_special_graphic[i].graphic);
194 print_timestamp_time("InitElementSmallImagesScaledUp (2)");
196 /* initialize images from dynamic configuration (may be elements or other) */
197 for (i = 0; i < num_property_mappings; i++)
198 InitElementSmallImagesScaledUp(property_mapping[i].artwork_index);
199 print_timestamp_time("InitElementSmallImagesScaledUp (3)");
201 /* initialize special images from above list (non-element images) */
202 for (i = 0; special_graphics[i] > -1; i++)
203 InitElementSmallImagesScaledUp(special_graphics[i]);
204 print_timestamp_time("InitElementSmallImagesScaledUp (4)");
206 print_timestamp_done("InitElementSmallImages");
209 void InitScaledImages()
213 /* scale normal images from static configuration, if not already scaled */
214 for (i = 0; i < NUM_IMAGE_FILES; i++)
215 ScaleImage(i, graphic_info[i].scale_up_factor);
218 void InitBitmapPointers()
220 int num_images = getImageListSize();
223 // standard size bitmap may have changed -- update default bitmap pointer
224 for (i = 0; i < num_images; i++)
225 if (graphic_info[i].bitmaps)
226 graphic_info[i].bitmap = graphic_info[i].bitmaps[IMG_BITMAP_STANDARD];
230 /* !!! FIX THIS (CHANGE TO USING NORMAL ELEMENT GRAPHIC DEFINITIONS) !!! */
231 void SetBitmaps_EM(Bitmap **em_bitmap)
233 em_bitmap[0] = graphic_info[IMG_EMC_OBJECT].bitmap;
234 em_bitmap[1] = graphic_info[IMG_EMC_SPRITE].bitmap;
239 /* !!! FIX THIS (CHANGE TO USING NORMAL ELEMENT GRAPHIC DEFINITIONS) !!! */
240 void SetBitmaps_SP(Bitmap **sp_bitmap)
242 *sp_bitmap = graphic_info[IMG_SP_OBJECTS].bitmap;
246 static int getFontBitmapID(int font_nr)
250 /* (special case: do not use special font for GAME_MODE_LOADING) */
251 if (game_status >= GAME_MODE_TITLE_INITIAL &&
252 game_status <= GAME_MODE_PSEUDO_PREVIEW)
253 special = game_status;
254 else if (game_status == GAME_MODE_PSEUDO_TYPENAME)
255 special = GFX_SPECIAL_ARG_MAIN;
258 return font_info[font_nr].special_bitmap_id[special];
263 static int getFontFromToken(char *token)
265 char *value = getHashEntry(font_token_hash, token);
270 /* if font not found, use reliable default value */
271 return FONT_INITIAL_1;
274 void InitFontGraphicInfo()
276 static struct FontBitmapInfo *font_bitmap_info = NULL;
277 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
278 int num_property_mappings = getImageListPropertyMappingSize();
279 int num_font_bitmaps = NUM_FONTS;
282 if (graphic_info == NULL) /* still at startup phase */
284 InitFontInfo(font_initial, NUM_INITIAL_FONTS,
285 getFontBitmapID, getFontFromToken);
290 /* ---------- initialize font graphic definitions ---------- */
292 /* always start with reliable default values (normal font graphics) */
293 for (i = 0; i < NUM_FONTS; i++)
294 font_info[i].graphic = IMG_FONT_INITIAL_1;
296 /* initialize normal font/graphic mapping from static configuration */
297 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
299 int font_nr = font_to_graphic[i].font_nr;
300 int special = font_to_graphic[i].special;
301 int graphic = font_to_graphic[i].graphic;
306 font_info[font_nr].graphic = graphic;
309 /* always start with reliable default values (special font graphics) */
310 for (i = 0; i < NUM_FONTS; i++)
312 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
314 font_info[i].special_graphic[j] = font_info[i].graphic;
315 font_info[i].special_bitmap_id[j] = i;
319 /* initialize special font/graphic mapping from static configuration */
320 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
322 int font_nr = font_to_graphic[i].font_nr;
323 int special = font_to_graphic[i].special;
324 int graphic = font_to_graphic[i].graphic;
325 int base_graphic = font2baseimg(font_nr);
327 if (IS_SPECIAL_GFX_ARG(special))
329 boolean base_redefined =
330 getImageListEntryFromImageID(base_graphic)->redefined;
331 boolean special_redefined =
332 getImageListEntryFromImageID(graphic)->redefined;
333 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
335 /* if the base font ("font.title_1", for example) has been redefined,
336 but not the special font ("font.title_1.LEVELS", for example), do not
337 use an existing (in this case considered obsolete) special font
338 anymore, but use the automatically determined default font */
339 /* special case: cloned special fonts must be explicitly redefined,
340 but are not automatically redefined by redefining base font */
341 if (base_redefined && !special_redefined && !special_cloned)
344 font_info[font_nr].special_graphic[special] = graphic;
345 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
350 /* initialize special font/graphic mapping from dynamic configuration */
351 for (i = 0; i < num_property_mappings; i++)
353 int font_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
354 int special = property_mapping[i].ext3_index;
355 int graphic = property_mapping[i].artwork_index;
360 if (IS_SPECIAL_GFX_ARG(special))
362 font_info[font_nr].special_graphic[special] = graphic;
363 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
368 /* correct special font/graphic mapping for cloned fonts for downwards
369 compatibility of PREVIEW fonts -- this is only needed for implicit
370 redefinition of special font by redefined base font, and only if other
371 fonts are cloned from this special font (like in the "Zelda" level set) */
372 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
374 int font_nr = font_to_graphic[i].font_nr;
375 int special = font_to_graphic[i].special;
376 int graphic = font_to_graphic[i].graphic;
378 if (IS_SPECIAL_GFX_ARG(special))
380 boolean special_redefined =
381 getImageListEntryFromImageID(graphic)->redefined;
382 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
384 if (special_cloned && !special_redefined)
388 for (j = 0; font_to_graphic[j].font_nr > -1; j++)
390 int font_nr2 = font_to_graphic[j].font_nr;
391 int special2 = font_to_graphic[j].special;
392 int graphic2 = font_to_graphic[j].graphic;
394 if (IS_SPECIAL_GFX_ARG(special2) &&
395 graphic2 == graphic_info[graphic].clone_from)
397 font_info[font_nr].special_graphic[special] =
398 font_info[font_nr2].special_graphic[special2];
399 font_info[font_nr].special_bitmap_id[special] =
400 font_info[font_nr2].special_bitmap_id[special2];
407 /* reset non-redefined ".active" font graphics if normal font is redefined */
408 /* (this different treatment is needed because normal and active fonts are
409 independently defined ("active" is not a property of font definitions!) */
410 for (i = 0; i < NUM_FONTS; i++)
412 int font_nr_base = i;
413 int font_nr_active = FONT_ACTIVE(font_nr_base);
415 /* check only those fonts with exist as normal and ".active" variant */
416 if (font_nr_base != font_nr_active)
418 int base_graphic = font_info[font_nr_base].graphic;
419 int active_graphic = font_info[font_nr_active].graphic;
420 boolean base_redefined =
421 getImageListEntryFromImageID(base_graphic)->redefined;
422 boolean active_redefined =
423 getImageListEntryFromImageID(active_graphic)->redefined;
425 /* if the base font ("font.menu_1", for example) has been redefined,
426 but not the active font ("font.menu_1.active", for example), do not
427 use an existing (in this case considered obsolete) active font
428 anymore, but use the automatically determined default font */
429 if (base_redefined && !active_redefined)
430 font_info[font_nr_active].graphic = base_graphic;
432 /* now also check each "special" font (which may be the same as above) */
433 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
435 int base_graphic = font_info[font_nr_base].special_graphic[j];
436 int active_graphic = font_info[font_nr_active].special_graphic[j];
437 boolean base_redefined =
438 getImageListEntryFromImageID(base_graphic)->redefined;
439 boolean active_redefined =
440 getImageListEntryFromImageID(active_graphic)->redefined;
442 /* same as above, but check special graphic definitions, for example:
443 redefined "font.menu_1.MAIN" invalidates "font.menu_1.active.MAIN" */
444 if (base_redefined && !active_redefined)
446 font_info[font_nr_active].special_graphic[j] =
447 font_info[font_nr_base].special_graphic[j];
448 font_info[font_nr_active].special_bitmap_id[j] =
449 font_info[font_nr_base].special_bitmap_id[j];
455 /* ---------- initialize font bitmap array ---------- */
457 if (font_bitmap_info != NULL)
458 FreeFontInfo(font_bitmap_info);
461 checked_calloc(num_font_bitmaps * sizeof(struct FontBitmapInfo));
463 /* ---------- initialize font bitmap definitions ---------- */
465 for (i = 0; i < NUM_FONTS; i++)
467 if (i < NUM_INITIAL_FONTS)
469 font_bitmap_info[i] = font_initial[i];
473 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
475 int font_bitmap_id = font_info[i].special_bitmap_id[j];
476 int graphic = font_info[i].special_graphic[j];
478 /* set 'graphic_info' for font entries, if uninitialized (guessed) */
479 if (graphic_info[graphic].anim_frames < MIN_NUM_CHARS_PER_FONT)
481 graphic_info[graphic].anim_frames = DEFAULT_NUM_CHARS_PER_FONT;
482 graphic_info[graphic].anim_frames_per_line = DEFAULT_NUM_CHARS_PER_LINE;
485 /* copy font relevant information from graphics information */
486 font_bitmap_info[font_bitmap_id].bitmap = graphic_info[graphic].bitmap;
487 font_bitmap_info[font_bitmap_id].src_x = graphic_info[graphic].src_x;
488 font_bitmap_info[font_bitmap_id].src_y = graphic_info[graphic].src_y;
489 font_bitmap_info[font_bitmap_id].width = graphic_info[graphic].width;
490 font_bitmap_info[font_bitmap_id].height = graphic_info[graphic].height;
492 font_bitmap_info[font_bitmap_id].draw_xoffset =
493 graphic_info[graphic].draw_xoffset;
494 font_bitmap_info[font_bitmap_id].draw_yoffset =
495 graphic_info[graphic].draw_yoffset;
497 font_bitmap_info[font_bitmap_id].num_chars =
498 graphic_info[graphic].anim_frames;
499 font_bitmap_info[font_bitmap_id].num_chars_per_line =
500 graphic_info[graphic].anim_frames_per_line;
504 InitFontInfo(font_bitmap_info, num_font_bitmaps,
505 getFontBitmapID, getFontFromToken);
508 void InitElementGraphicInfo()
510 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
511 int num_property_mappings = getImageListPropertyMappingSize();
514 if (graphic_info == NULL) /* still at startup phase */
517 /* set values to -1 to identify later as "uninitialized" values */
518 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
520 for (act = 0; act < NUM_ACTIONS; act++)
522 element_info[i].graphic[act] = -1;
523 element_info[i].crumbled[act] = -1;
525 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
527 element_info[i].direction_graphic[act][dir] = -1;
528 element_info[i].direction_crumbled[act][dir] = -1;
535 /* initialize normal element/graphic mapping from static configuration */
536 for (i = 0; element_to_graphic[i].element > -1; i++)
538 int element = element_to_graphic[i].element;
539 int action = element_to_graphic[i].action;
540 int direction = element_to_graphic[i].direction;
541 boolean crumbled = element_to_graphic[i].crumbled;
542 int graphic = element_to_graphic[i].graphic;
543 int base_graphic = el2baseimg(element);
545 if (graphic_info[graphic].bitmap == NULL)
548 if ((action > -1 || direction > -1 || crumbled == TRUE) &&
551 boolean base_redefined =
552 getImageListEntryFromImageID(base_graphic)->redefined;
553 boolean act_dir_redefined =
554 getImageListEntryFromImageID(graphic)->redefined;
556 /* if the base graphic ("emerald", for example) has been redefined,
557 but not the action graphic ("emerald.falling", for example), do not
558 use an existing (in this case considered obsolete) action graphic
559 anymore, but use the automatically determined default graphic */
560 if (base_redefined && !act_dir_redefined)
565 action = ACTION_DEFAULT;
570 element_info[element].direction_crumbled[action][direction] = graphic;
572 element_info[element].crumbled[action] = graphic;
577 element_info[element].direction_graphic[action][direction] = graphic;
579 element_info[element].graphic[action] = graphic;
583 /* initialize normal element/graphic mapping from dynamic configuration */
584 for (i = 0; i < num_property_mappings; i++)
586 int element = property_mapping[i].base_index;
587 int action = property_mapping[i].ext1_index;
588 int direction = property_mapping[i].ext2_index;
589 int special = property_mapping[i].ext3_index;
590 int graphic = property_mapping[i].artwork_index;
591 boolean crumbled = FALSE;
593 if (special == GFX_SPECIAL_ARG_CRUMBLED)
599 if (graphic_info[graphic].bitmap == NULL)
602 if (element >= MAX_NUM_ELEMENTS || special != -1)
606 action = ACTION_DEFAULT;
611 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
612 element_info[element].direction_crumbled[action][dir] = -1;
615 element_info[element].direction_crumbled[action][direction] = graphic;
617 element_info[element].crumbled[action] = graphic;
622 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
623 element_info[element].direction_graphic[action][dir] = -1;
626 element_info[element].direction_graphic[action][direction] = graphic;
628 element_info[element].graphic[action] = graphic;
632 /* now copy all graphics that are defined to be cloned from other graphics */
633 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
635 int graphic = element_info[i].graphic[ACTION_DEFAULT];
636 int crumbled_like, diggable_like;
641 crumbled_like = graphic_info[graphic].crumbled_like;
642 diggable_like = graphic_info[graphic].diggable_like;
644 if (crumbled_like != -1 && element_info[i].crumbled[ACTION_DEFAULT] == -1)
646 for (act = 0; act < NUM_ACTIONS; act++)
647 element_info[i].crumbled[act] =
648 element_info[crumbled_like].crumbled[act];
649 for (act = 0; act < NUM_ACTIONS; act++)
650 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
651 element_info[i].direction_crumbled[act][dir] =
652 element_info[crumbled_like].direction_crumbled[act][dir];
655 if (diggable_like != -1 && element_info[i].graphic[ACTION_DIGGING] == -1)
657 element_info[i].graphic[ACTION_DIGGING] =
658 element_info[diggable_like].graphic[ACTION_DIGGING];
659 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
660 element_info[i].direction_graphic[ACTION_DIGGING][dir] =
661 element_info[diggable_like].direction_graphic[ACTION_DIGGING][dir];
665 /* set hardcoded definitions for some runtime elements without graphic */
666 element_info[EL_AMOEBA_TO_DIAMOND].graphic[ACTION_DEFAULT] = IMG_AMOEBA_DEAD;
668 /* set hardcoded definitions for some internal elements without graphic */
669 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
671 if (IS_EDITOR_CASCADE_INACTIVE(i))
672 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST;
673 else if (IS_EDITOR_CASCADE_ACTIVE(i))
674 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST_ACTIVE;
677 /* now set all undefined/invalid graphics to -1 to set to default after it */
678 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
680 for (act = 0; act < NUM_ACTIONS; act++)
684 graphic = element_info[i].graphic[act];
685 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
686 element_info[i].graphic[act] = -1;
688 graphic = element_info[i].crumbled[act];
689 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
690 element_info[i].crumbled[act] = -1;
692 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
694 graphic = element_info[i].direction_graphic[act][dir];
695 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
696 element_info[i].direction_graphic[act][dir] = -1;
698 graphic = element_info[i].direction_crumbled[act][dir];
699 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
700 element_info[i].direction_crumbled[act][dir] = -1;
707 /* adjust graphics with 2nd tile for movement according to direction
708 (do this before correcting '-1' values to minimize calculations) */
709 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
711 for (act = 0; act < NUM_ACTIONS; act++)
713 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
715 int graphic = element_info[i].direction_graphic[act][dir];
716 int move_dir = (act == ACTION_FALLING ? MV_BIT_DOWN : dir);
718 if (act == ACTION_FALLING) /* special case */
719 graphic = element_info[i].graphic[act];
722 graphic_info[graphic].double_movement &&
723 graphic_info[graphic].swap_double_tiles != 0)
725 struct GraphicInfo *g = &graphic_info[graphic];
726 int src_x_front = g->src_x;
727 int src_y_front = g->src_y;
728 int src_x_back = g->src_x + g->offset2_x;
729 int src_y_back = g->src_y + g->offset2_y;
730 boolean frames_are_ordered_diagonally = (g->offset_x != 0 &&
732 boolean front_is_left_or_upper = (src_x_front < src_x_back ||
733 src_y_front < src_y_back);
734 boolean swap_movement_tiles_always = (g->swap_double_tiles == 1);
735 boolean swap_movement_tiles_autodetected =
736 (!frames_are_ordered_diagonally &&
737 ((move_dir == MV_BIT_LEFT && !front_is_left_or_upper) ||
738 (move_dir == MV_BIT_UP && !front_is_left_or_upper) ||
739 (move_dir == MV_BIT_RIGHT && front_is_left_or_upper) ||
740 (move_dir == MV_BIT_DOWN && front_is_left_or_upper)));
743 /* swap frontside and backside graphic tile coordinates, if needed */
744 if (swap_movement_tiles_always || swap_movement_tiles_autodetected)
746 /* get current (wrong) backside tile coordinates */
747 getFixedGraphicSourceExt(graphic, 0, &dummy,
748 &src_x_back, &src_y_back, TRUE);
750 /* set frontside tile coordinates to backside tile coordinates */
751 g->src_x = src_x_back;
752 g->src_y = src_y_back;
754 /* invert tile offset to point to new backside tile coordinates */
758 /* do not swap front and backside tiles again after correction */
759 g->swap_double_tiles = 0;
768 /* now set all '-1' values to element specific default values */
769 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
771 int default_graphic = element_info[i].graphic[ACTION_DEFAULT];
772 int default_crumbled = element_info[i].crumbled[ACTION_DEFAULT];
773 int default_direction_graphic[NUM_DIRECTIONS_FULL];
774 int default_direction_crumbled[NUM_DIRECTIONS_FULL];
776 if (default_graphic == -1)
777 default_graphic = IMG_UNKNOWN;
779 if (default_crumbled == -1)
780 default_crumbled = default_graphic;
782 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
784 default_direction_graphic[dir] =
785 element_info[i].direction_graphic[ACTION_DEFAULT][dir];
786 default_direction_crumbled[dir] =
787 element_info[i].direction_crumbled[ACTION_DEFAULT][dir];
789 if (default_direction_graphic[dir] == -1)
790 default_direction_graphic[dir] = default_graphic;
792 if (default_direction_crumbled[dir] == -1)
793 default_direction_crumbled[dir] = default_direction_graphic[dir];
796 for (act = 0; act < NUM_ACTIONS; act++)
798 boolean act_remove = ((IS_DIGGABLE(i) && act == ACTION_DIGGING) ||
799 (IS_SNAPPABLE(i) && act == ACTION_SNAPPING) ||
800 (IS_COLLECTIBLE(i) && act == ACTION_COLLECTING));
801 boolean act_turning = (act == ACTION_TURNING_FROM_LEFT ||
802 act == ACTION_TURNING_FROM_RIGHT ||
803 act == ACTION_TURNING_FROM_UP ||
804 act == ACTION_TURNING_FROM_DOWN);
806 /* generic default action graphic (defined by "[default]" directive) */
807 int default_action_graphic = element_info[EL_DEFAULT].graphic[act];
808 int default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
809 int default_remove_graphic = IMG_EMPTY;
811 if (act_remove && default_action_graphic != -1)
812 default_remove_graphic = default_action_graphic;
814 /* look for special default action graphic (classic game specific) */
815 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].graphic[act] != -1)
816 default_action_graphic = element_info[EL_BD_DEFAULT].graphic[act];
817 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].graphic[act] != -1)
818 default_action_graphic = element_info[EL_SP_DEFAULT].graphic[act];
819 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].graphic[act] != -1)
820 default_action_graphic = element_info[EL_SB_DEFAULT].graphic[act];
822 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].crumbled[act] != -1)
823 default_action_crumbled = element_info[EL_BD_DEFAULT].crumbled[act];
824 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].crumbled[act] != -1)
825 default_action_crumbled = element_info[EL_SP_DEFAULT].crumbled[act];
826 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].crumbled[act] != -1)
827 default_action_crumbled = element_info[EL_SB_DEFAULT].crumbled[act];
829 /* !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!! */
830 /* !!! make this better !!! */
831 if (i == EL_EMPTY_SPACE)
833 default_action_graphic = element_info[EL_DEFAULT].graphic[act];
834 default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
837 if (default_action_graphic == -1)
838 default_action_graphic = default_graphic;
840 if (default_action_crumbled == -1)
841 default_action_crumbled = default_action_graphic;
843 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
845 /* use action graphic as the default direction graphic, if undefined */
846 int default_action_direction_graphic = element_info[i].graphic[act];
847 int default_action_direction_crumbled = element_info[i].crumbled[act];
849 /* no graphic for current action -- use default direction graphic */
850 if (default_action_direction_graphic == -1)
851 default_action_direction_graphic =
852 (act_remove ? default_remove_graphic :
854 element_info[i].direction_graphic[ACTION_TURNING][dir] :
855 default_action_graphic != default_graphic ?
856 default_action_graphic :
857 default_direction_graphic[dir]);
859 if (element_info[i].direction_graphic[act][dir] == -1)
860 element_info[i].direction_graphic[act][dir] =
861 default_action_direction_graphic;
863 if (default_action_direction_crumbled == -1)
864 default_action_direction_crumbled =
865 element_info[i].direction_graphic[act][dir];
867 if (element_info[i].direction_crumbled[act][dir] == -1)
868 element_info[i].direction_crumbled[act][dir] =
869 default_action_direction_crumbled;
872 /* no graphic for this specific action -- use default action graphic */
873 if (element_info[i].graphic[act] == -1)
874 element_info[i].graphic[act] =
875 (act_remove ? default_remove_graphic :
876 act_turning ? element_info[i].graphic[ACTION_TURNING] :
877 default_action_graphic);
879 if (element_info[i].crumbled[act] == -1)
880 element_info[i].crumbled[act] = element_info[i].graphic[act];
887 void InitElementSpecialGraphicInfo()
889 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
890 int num_property_mappings = getImageListPropertyMappingSize();
893 /* always start with reliable default values */
894 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
895 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
896 element_info[i].special_graphic[j] =
897 element_info[i].graphic[ACTION_DEFAULT];
899 /* initialize special element/graphic mapping from static configuration */
900 for (i = 0; element_to_special_graphic[i].element > -1; i++)
902 int element = element_to_special_graphic[i].element;
903 int special = element_to_special_graphic[i].special;
904 int graphic = element_to_special_graphic[i].graphic;
905 int base_graphic = el2baseimg(element);
906 boolean base_redefined =
907 getImageListEntryFromImageID(base_graphic)->redefined;
908 boolean special_redefined =
909 getImageListEntryFromImageID(graphic)->redefined;
911 /* if the base graphic ("emerald", for example) has been redefined,
912 but not the special graphic ("emerald.EDITOR", for example), do not
913 use an existing (in this case considered obsolete) special graphic
914 anymore, but use the automatically created (down-scaled) graphic */
915 if (base_redefined && !special_redefined)
918 element_info[element].special_graphic[special] = graphic;
921 /* initialize special element/graphic mapping from dynamic configuration */
922 for (i = 0; i < num_property_mappings; i++)
924 int element = property_mapping[i].base_index;
925 int action = property_mapping[i].ext1_index;
926 int direction = property_mapping[i].ext2_index;
927 int special = property_mapping[i].ext3_index;
928 int graphic = property_mapping[i].artwork_index;
930 /* for action ".active", replace element with active element, if exists */
931 if (action == ACTION_ACTIVE && element != ELEMENT_ACTIVE(element))
933 element = ELEMENT_ACTIVE(element);
937 if (element >= MAX_NUM_ELEMENTS)
940 /* do not change special graphic if action or direction was specified */
941 if (action != -1 || direction != -1)
944 if (IS_SPECIAL_GFX_ARG(special))
945 element_info[element].special_graphic[special] = graphic;
948 /* now set all undefined/invalid graphics to default */
949 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
950 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
951 if (graphic_info[element_info[i].special_graphic[j]].bitmap == NULL)
952 element_info[i].special_graphic[j] =
953 element_info[i].graphic[ACTION_DEFAULT];
956 static int get_graphic_parameter_value(char *value_raw, char *suffix, int type)
958 if (type != TYPE_ELEMENT && type != TYPE_GRAPHIC)
959 return get_parameter_value(value_raw, suffix, type);
961 if (strEqual(value_raw, ARG_UNDEFINED))
962 return ARG_UNDEFINED_VALUE;
964 if (type == TYPE_ELEMENT)
966 char *value = getHashEntry(element_token_hash, value_raw);
970 Error(ERR_INFO_LINE, "-");
971 Error(ERR_INFO, "warning: error found in config file:");
972 Error(ERR_INFO, "- config file: '%s'", getImageConfigFilename());
973 Error(ERR_INFO, "error: invalid element token '%s'", value_raw);
974 Error(ERR_INFO, "custom graphic rejected for this element/action");
975 Error(ERR_INFO, "fallback done to undefined element for this graphic");
976 Error(ERR_INFO_LINE, "-");
979 return (value != NULL ? atoi(value) : EL_UNDEFINED);
981 else if (type == TYPE_GRAPHIC)
983 char *value = getHashEntry(graphic_token_hash, value_raw);
984 int fallback_graphic = IMG_CHAR_EXCLAM;
988 Error(ERR_INFO_LINE, "-");
989 Error(ERR_INFO, "warning: error found in config file:");
990 Error(ERR_INFO, "- config file: '%s'", getImageConfigFilename());
991 Error(ERR_INFO, "error: invalid graphic token '%s'", value_raw);
992 Error(ERR_INFO, "custom graphic rejected for this element/action");
993 Error(ERR_INFO, "fallback done to 'char_exclam' for this graphic");
994 Error(ERR_INFO_LINE, "-");
997 return (value != NULL ? atoi(value) : fallback_graphic);
1003 static int get_scaled_graphic_width(int graphic)
1005 int original_width = getOriginalImageWidthFromImageID(graphic);
1006 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1008 return original_width * scale_up_factor;
1011 static int get_scaled_graphic_height(int graphic)
1013 int original_height = getOriginalImageHeightFromImageID(graphic);
1014 int scale_up_factor = graphic_info[graphic].scale_up_factor;
1016 return original_height * scale_up_factor;
1019 static void set_graphic_parameters_ext(int graphic, int *parameter,
1020 Bitmap **src_bitmaps)
1022 struct GraphicInfo *g = &graphic_info[graphic];
1023 Bitmap *src_bitmap = (src_bitmaps ? src_bitmaps[IMG_BITMAP_STANDARD] : NULL);
1024 int anim_frames_per_row = 1, anim_frames_per_col = 1;
1025 int anim_frames_per_line = 1;
1027 /* always start with reliable default values */
1028 g->src_image_width = 0;
1029 g->src_image_height = 0;
1032 g->width = TILEX; /* default for element graphics */
1033 g->height = TILEY; /* default for element graphics */
1034 g->offset_x = 0; /* one or both of these values ... */
1035 g->offset_y = 0; /* ... will be corrected later */
1036 g->offset2_x = 0; /* one or both of these values ... */
1037 g->offset2_y = 0; /* ... will be corrected later */
1038 g->swap_double_tiles = -1; /* auto-detect tile swapping */
1039 g->crumbled_like = -1; /* do not use clone element */
1040 g->diggable_like = -1; /* do not use clone element */
1041 g->border_size = TILEX / 8; /* "CRUMBLED" border size */
1042 g->scale_up_factor = 1; /* default: no scaling up */
1043 g->tile_size = TILESIZE; /* default: standard tile size */
1044 g->clone_from = -1; /* do not use clone graphic */
1045 g->anim_delay_fixed = 0;
1046 g->anim_delay_random = 0;
1047 g->post_delay_fixed = 0;
1048 g->post_delay_random = 0;
1049 g->fade_mode = FADE_MODE_DEFAULT;
1053 g->align = ALIGN_CENTER; /* default for title screens */
1054 g->valign = VALIGN_MIDDLE; /* default for title screens */
1055 g->sort_priority = 0; /* default for title screens */
1057 g->style = STYLE_DEFAULT;
1059 g->bitmaps = src_bitmaps;
1060 g->bitmap = src_bitmap;
1062 /* optional zoom factor for scaling up the image to a larger size */
1063 if (parameter[GFX_ARG_SCALE_UP_FACTOR] != ARG_UNDEFINED_VALUE)
1064 g->scale_up_factor = parameter[GFX_ARG_SCALE_UP_FACTOR];
1065 if (g->scale_up_factor < 1)
1066 g->scale_up_factor = 1; /* no scaling */
1068 /* optional tile size for using non-standard image size */
1069 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1071 g->tile_size = parameter[GFX_ARG_TILE_SIZE];
1074 // CHECK: should tile sizes less than standard tile size be allowed?
1075 if (g->tile_size < TILESIZE)
1076 g->tile_size = TILESIZE; /* standard tile size */
1080 // CHECK: when setting tile size, should this set width and height?
1081 g->width = g->tile_size;
1082 g->height = g->tile_size;
1086 if (g->use_image_size)
1088 /* set new default bitmap size (with scaling, but without small images) */
1089 g->width = get_scaled_graphic_width(graphic);
1090 g->height = get_scaled_graphic_height(graphic);
1093 /* optional width and height of each animation frame */
1094 if (parameter[GFX_ARG_WIDTH] != ARG_UNDEFINED_VALUE)
1095 g->width = parameter[GFX_ARG_WIDTH];
1096 if (parameter[GFX_ARG_HEIGHT] != ARG_UNDEFINED_VALUE)
1097 g->height = parameter[GFX_ARG_HEIGHT];
1099 /* optional x and y tile position of animation frame sequence */
1100 if (parameter[GFX_ARG_XPOS] != ARG_UNDEFINED_VALUE)
1101 g->src_x = parameter[GFX_ARG_XPOS] * g->width;
1102 if (parameter[GFX_ARG_YPOS] != ARG_UNDEFINED_VALUE)
1103 g->src_y = parameter[GFX_ARG_YPOS] * g->height;
1105 /* optional x and y pixel position of animation frame sequence */
1106 if (parameter[GFX_ARG_X] != ARG_UNDEFINED_VALUE)
1107 g->src_x = parameter[GFX_ARG_X];
1108 if (parameter[GFX_ARG_Y] != ARG_UNDEFINED_VALUE)
1109 g->src_y = parameter[GFX_ARG_Y];
1115 Error(ERR_INFO_LINE, "-");
1116 Error(ERR_WARN, "invalid value %d for '%s.width' (fallback done to %d)",
1117 g->width, getTokenFromImageID(graphic), TILEX);
1118 Error(ERR_INFO_LINE, "-");
1120 g->width = TILEX; /* will be checked to be inside bitmap later */
1125 Error(ERR_INFO_LINE, "-");
1126 Error(ERR_WARN, "invalid value %d for '%s.height' (fallback done to %d)",
1127 g->height, getTokenFromImageID(graphic), TILEY);
1128 Error(ERR_INFO_LINE, "-");
1130 g->height = TILEY; /* will be checked to be inside bitmap later */
1136 /* get final bitmap size (with scaling, but without small images) */
1137 int src_image_width = get_scaled_graphic_width(graphic);
1138 int src_image_height = get_scaled_graphic_height(graphic);
1140 if (src_image_width == 0 || src_image_height == 0)
1142 /* only happens when loaded outside artwork system (like "global.busy") */
1143 src_image_width = src_bitmap->width;
1144 src_image_height = src_bitmap->height;
1147 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1149 anim_frames_per_row = src_image_width / g->tile_size;
1150 anim_frames_per_col = src_image_height / g->tile_size;
1154 anim_frames_per_row = src_image_width / g->width;
1155 anim_frames_per_col = src_image_height / g->height;
1158 g->src_image_width = src_image_width;
1159 g->src_image_height = src_image_height;
1162 /* correct x or y offset dependent of vertical or horizontal frame order */
1163 if (parameter[GFX_ARG_VERTICAL]) /* frames are ordered vertically */
1165 g->offset_y = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1166 parameter[GFX_ARG_OFFSET] : g->height);
1167 anim_frames_per_line = anim_frames_per_col;
1169 else /* frames are ordered horizontally */
1171 g->offset_x = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1172 parameter[GFX_ARG_OFFSET] : g->width);
1173 anim_frames_per_line = anim_frames_per_row;
1176 /* optionally, the x and y offset of frames can be specified directly */
1177 if (parameter[GFX_ARG_XOFFSET] != ARG_UNDEFINED_VALUE)
1178 g->offset_x = parameter[GFX_ARG_XOFFSET];
1179 if (parameter[GFX_ARG_YOFFSET] != ARG_UNDEFINED_VALUE)
1180 g->offset_y = parameter[GFX_ARG_YOFFSET];
1182 /* optionally, moving animations may have separate start and end graphics */
1183 g->double_movement = parameter[GFX_ARG_2ND_MOVEMENT_TILE];
1185 if (parameter[GFX_ARG_2ND_VERTICAL] == ARG_UNDEFINED_VALUE)
1186 parameter[GFX_ARG_2ND_VERTICAL] = !parameter[GFX_ARG_VERTICAL];
1188 /* correct x or y offset2 dependent of vertical or horizontal frame order */
1189 if (parameter[GFX_ARG_2ND_VERTICAL]) /* frames are ordered vertically */
1190 g->offset2_y = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1191 parameter[GFX_ARG_2ND_OFFSET] : g->height);
1192 else /* frames are ordered horizontally */
1193 g->offset2_x = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1194 parameter[GFX_ARG_2ND_OFFSET] : g->width);
1196 /* optionally, the x and y offset of 2nd graphic can be specified directly */
1197 if (parameter[GFX_ARG_2ND_XOFFSET] != ARG_UNDEFINED_VALUE)
1198 g->offset2_x = parameter[GFX_ARG_2ND_XOFFSET];
1199 if (parameter[GFX_ARG_2ND_YOFFSET] != ARG_UNDEFINED_VALUE)
1200 g->offset2_y = parameter[GFX_ARG_2ND_YOFFSET];
1202 /* optionally, the second movement tile can be specified as start tile */
1203 if (parameter[GFX_ARG_2ND_SWAP_TILES] != ARG_UNDEFINED_VALUE)
1204 g->swap_double_tiles= parameter[GFX_ARG_2ND_SWAP_TILES];
1206 /* automatically determine correct number of frames, if not defined */
1207 if (parameter[GFX_ARG_FRAMES] != ARG_UNDEFINED_VALUE)
1208 g->anim_frames = parameter[GFX_ARG_FRAMES];
1209 else if (parameter[GFX_ARG_XPOS] == 0 && !parameter[GFX_ARG_VERTICAL])
1210 g->anim_frames = anim_frames_per_row;
1211 else if (parameter[GFX_ARG_YPOS] == 0 && parameter[GFX_ARG_VERTICAL])
1212 g->anim_frames = anim_frames_per_col;
1216 if (g->anim_frames == 0) /* frames must be at least 1 */
1219 g->anim_frames_per_line =
1220 (parameter[GFX_ARG_FRAMES_PER_LINE] != ARG_UNDEFINED_VALUE ?
1221 parameter[GFX_ARG_FRAMES_PER_LINE] : anim_frames_per_line);
1223 g->anim_delay = parameter[GFX_ARG_DELAY];
1224 if (g->anim_delay == 0) /* delay must be at least 1 */
1227 g->anim_mode = parameter[GFX_ARG_ANIM_MODE];
1229 /* automatically determine correct start frame, if not defined */
1230 if (parameter[GFX_ARG_START_FRAME] == ARG_UNDEFINED_VALUE)
1231 g->anim_start_frame = 0;
1232 else if (g->anim_mode & ANIM_REVERSE)
1233 g->anim_start_frame = g->anim_frames - parameter[GFX_ARG_START_FRAME] - 1;
1235 g->anim_start_frame = parameter[GFX_ARG_START_FRAME];
1237 /* animation synchronized with global frame counter, not move position */
1238 g->anim_global_sync = parameter[GFX_ARG_GLOBAL_SYNC];
1240 /* optional element for cloning crumble graphics */
1241 if (parameter[GFX_ARG_CRUMBLED_LIKE] != ARG_UNDEFINED_VALUE)
1242 g->crumbled_like = parameter[GFX_ARG_CRUMBLED_LIKE];
1244 /* optional element for cloning digging graphics */
1245 if (parameter[GFX_ARG_DIGGABLE_LIKE] != ARG_UNDEFINED_VALUE)
1246 g->diggable_like = parameter[GFX_ARG_DIGGABLE_LIKE];
1248 /* optional border size for "crumbling" diggable graphics */
1249 if (parameter[GFX_ARG_BORDER_SIZE] != ARG_UNDEFINED_VALUE)
1250 g->border_size = parameter[GFX_ARG_BORDER_SIZE];
1252 /* this is only used for player "boring" and "sleeping" actions */
1253 if (parameter[GFX_ARG_ANIM_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1254 g->anim_delay_fixed = parameter[GFX_ARG_ANIM_DELAY_FIXED];
1255 if (parameter[GFX_ARG_ANIM_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1256 g->anim_delay_random = parameter[GFX_ARG_ANIM_DELAY_RANDOM];
1257 if (parameter[GFX_ARG_POST_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1258 g->post_delay_fixed = parameter[GFX_ARG_POST_DELAY_FIXED];
1259 if (parameter[GFX_ARG_POST_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1260 g->post_delay_random = parameter[GFX_ARG_POST_DELAY_RANDOM];
1262 /* this is only used for toon animations */
1263 g->step_offset = parameter[GFX_ARG_STEP_OFFSET];
1264 g->step_delay = parameter[GFX_ARG_STEP_DELAY];
1266 /* this is only used for drawing font characters */
1267 g->draw_xoffset = parameter[GFX_ARG_DRAW_XOFFSET];
1268 g->draw_yoffset = parameter[GFX_ARG_DRAW_YOFFSET];
1270 /* this is only used for drawing envelope graphics */
1271 g->draw_masked = parameter[GFX_ARG_DRAW_MASKED];
1273 /* optional graphic for cloning all graphics settings */
1274 if (parameter[GFX_ARG_CLONE_FROM] != ARG_UNDEFINED_VALUE)
1275 g->clone_from = parameter[GFX_ARG_CLONE_FROM];
1277 /* optional settings for drawing title screens and title messages */
1278 if (parameter[GFX_ARG_FADE_MODE] != ARG_UNDEFINED_VALUE)
1279 g->fade_mode = parameter[GFX_ARG_FADE_MODE];
1280 if (parameter[GFX_ARG_FADE_DELAY] != ARG_UNDEFINED_VALUE)
1281 g->fade_delay = parameter[GFX_ARG_FADE_DELAY];
1282 if (parameter[GFX_ARG_POST_DELAY] != ARG_UNDEFINED_VALUE)
1283 g->post_delay = parameter[GFX_ARG_POST_DELAY];
1284 if (parameter[GFX_ARG_AUTO_DELAY] != ARG_UNDEFINED_VALUE)
1285 g->auto_delay = parameter[GFX_ARG_AUTO_DELAY];
1286 if (parameter[GFX_ARG_ALIGN] != ARG_UNDEFINED_VALUE)
1287 g->align = parameter[GFX_ARG_ALIGN];
1288 if (parameter[GFX_ARG_VALIGN] != ARG_UNDEFINED_VALUE)
1289 g->valign = parameter[GFX_ARG_VALIGN];
1290 if (parameter[GFX_ARG_SORT_PRIORITY] != ARG_UNDEFINED_VALUE)
1291 g->sort_priority = parameter[GFX_ARG_SORT_PRIORITY];
1293 if (parameter[GFX_ARG_CLASS] != ARG_UNDEFINED_VALUE)
1294 g->class = parameter[GFX_ARG_CLASS];
1295 if (parameter[GFX_ARG_STYLE] != ARG_UNDEFINED_VALUE)
1296 g->style = parameter[GFX_ARG_STYLE];
1298 /* this is only used for drawing menu buttons and text */
1299 g->active_xoffset = parameter[GFX_ARG_ACTIVE_XOFFSET];
1300 g->active_yoffset = parameter[GFX_ARG_ACTIVE_YOFFSET];
1301 g->pressed_xoffset = parameter[GFX_ARG_PRESSED_XOFFSET];
1302 g->pressed_yoffset = parameter[GFX_ARG_PRESSED_YOFFSET];
1305 static void set_graphic_parameters(int graphic)
1307 struct FileInfo *image = getImageListEntryFromImageID(graphic);
1308 char **parameter_raw = image->parameter;
1309 Bitmap **src_bitmaps = getBitmapsFromImageID(graphic);
1310 int parameter[NUM_GFX_ARGS];
1313 /* if fallback to default artwork is done, also use the default parameters */
1314 if (image->fallback_to_default)
1315 parameter_raw = image->default_parameter;
1317 /* get integer values from string parameters */
1318 for (i = 0; i < NUM_GFX_ARGS; i++)
1319 parameter[i] = get_graphic_parameter_value(parameter_raw[i],
1320 image_config_suffix[i].token,
1321 image_config_suffix[i].type);
1323 set_graphic_parameters_ext(graphic, parameter, src_bitmaps);
1325 UPDATE_BUSY_STATE();
1328 static void set_cloned_graphic_parameters(int graphic)
1330 int fallback_graphic = IMG_CHAR_EXCLAM;
1331 int max_num_images = getImageListSize();
1332 int clone_graphic = graphic_info[graphic].clone_from;
1333 int num_references_followed = 1;
1335 while (graphic_info[clone_graphic].clone_from != -1 &&
1336 num_references_followed < max_num_images)
1338 clone_graphic = graphic_info[clone_graphic].clone_from;
1340 num_references_followed++;
1343 if (num_references_followed >= max_num_images)
1345 Error(ERR_INFO_LINE, "-");
1346 Error(ERR_INFO, "warning: error found in config file:");
1347 Error(ERR_INFO, "- config file: '%s'", getImageConfigFilename());
1348 Error(ERR_INFO, "- config token: '%s'", getTokenFromImageID(graphic));
1349 Error(ERR_INFO, "error: loop discovered when resolving cloned graphics");
1350 Error(ERR_INFO, "custom graphic rejected for this element/action");
1352 if (graphic == fallback_graphic)
1353 Error(ERR_EXIT, "no fallback graphic available");
1355 Error(ERR_INFO, "fallback done to 'char_exclam' for this graphic");
1356 Error(ERR_INFO_LINE, "-");
1358 graphic_info[graphic] = graphic_info[fallback_graphic];
1362 graphic_info[graphic] = graphic_info[clone_graphic];
1363 graphic_info[graphic].clone_from = clone_graphic;
1367 static void InitGraphicInfo()
1369 int fallback_graphic = IMG_CHAR_EXCLAM;
1370 int num_images = getImageListSize();
1373 /* use image size as default values for width and height for these images */
1374 static int full_size_graphics[] =
1377 IMG_GLOBAL_BORDER_MAIN,
1378 IMG_GLOBAL_BORDER_SCORES,
1379 IMG_GLOBAL_BORDER_EDITOR,
1380 IMG_GLOBAL_BORDER_PLAYING,
1383 IMG_BACKGROUND_ENVELOPE_1,
1384 IMG_BACKGROUND_ENVELOPE_2,
1385 IMG_BACKGROUND_ENVELOPE_3,
1386 IMG_BACKGROUND_ENVELOPE_4,
1387 IMG_BACKGROUND_REQUEST,
1390 IMG_BACKGROUND_TITLE_INITIAL,
1391 IMG_BACKGROUND_TITLE,
1392 IMG_BACKGROUND_MAIN,
1393 IMG_BACKGROUND_LEVELS,
1394 IMG_BACKGROUND_LEVELNR,
1395 IMG_BACKGROUND_SCORES,
1396 IMG_BACKGROUND_EDITOR,
1397 IMG_BACKGROUND_INFO,
1398 IMG_BACKGROUND_INFO_ELEMENTS,
1399 IMG_BACKGROUND_INFO_MUSIC,
1400 IMG_BACKGROUND_INFO_CREDITS,
1401 IMG_BACKGROUND_INFO_PROGRAM,
1402 IMG_BACKGROUND_INFO_VERSION,
1403 IMG_BACKGROUND_INFO_LEVELSET,
1404 IMG_BACKGROUND_SETUP,
1405 IMG_BACKGROUND_PLAYING,
1406 IMG_BACKGROUND_DOOR,
1407 IMG_BACKGROUND_TAPE,
1408 IMG_BACKGROUND_PANEL,
1409 IMG_BACKGROUND_PALETTE,
1410 IMG_BACKGROUND_TOOLBOX,
1412 IMG_TITLESCREEN_INITIAL_1,
1413 IMG_TITLESCREEN_INITIAL_2,
1414 IMG_TITLESCREEN_INITIAL_3,
1415 IMG_TITLESCREEN_INITIAL_4,
1416 IMG_TITLESCREEN_INITIAL_5,
1423 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_1,
1424 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_2,
1425 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_3,
1426 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_4,
1427 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_5,
1428 IMG_BACKGROUND_TITLEMESSAGE_1,
1429 IMG_BACKGROUND_TITLEMESSAGE_2,
1430 IMG_BACKGROUND_TITLEMESSAGE_3,
1431 IMG_BACKGROUND_TITLEMESSAGE_4,
1432 IMG_BACKGROUND_TITLEMESSAGE_5,
1437 checked_free(graphic_info);
1439 graphic_info = checked_calloc(num_images * sizeof(struct GraphicInfo));
1441 /* initialize "use_image_size" flag with default value */
1442 for (i = 0; i < num_images; i++)
1443 graphic_info[i].use_image_size = FALSE;
1445 /* initialize "use_image_size" flag from static configuration above */
1446 for (i = 0; full_size_graphics[i] != -1; i++)
1447 graphic_info[full_size_graphics[i]].use_image_size = TRUE;
1449 /* first set all graphic paramaters ... */
1450 for (i = 0; i < num_images; i++)
1451 set_graphic_parameters(i);
1453 /* ... then copy these parameters for cloned graphics */
1454 for (i = 0; i < num_images; i++)
1455 if (graphic_info[i].clone_from != -1)
1456 set_cloned_graphic_parameters(i);
1458 for (i = 0; i < num_images; i++)
1463 int first_frame, last_frame;
1464 int src_bitmap_width, src_bitmap_height;
1466 /* now check if no animation frames are outside of the loaded image */
1468 if (graphic_info[i].bitmap == NULL)
1469 continue; /* skip check for optional images that are undefined */
1471 /* get image size (this can differ from the standard element tile size!) */
1472 width = graphic_info[i].width;
1473 height = graphic_info[i].height;
1475 /* get final bitmap size (with scaling, but without small images) */
1476 src_bitmap_width = graphic_info[i].src_image_width;
1477 src_bitmap_height = graphic_info[i].src_image_height;
1479 /* check if first animation frame is inside specified bitmap */
1482 getFixedGraphicSource(i, first_frame, &src_bitmap, &src_x, &src_y);
1484 /* this avoids calculating wrong start position for out-of-bounds frame */
1485 src_x = graphic_info[i].src_x;
1486 src_y = graphic_info[i].src_y;
1488 if (src_x < 0 || src_y < 0 ||
1489 src_x + width > src_bitmap_width ||
1490 src_y + height > src_bitmap_height)
1492 Error(ERR_INFO_LINE, "-");
1493 Error(ERR_INFO, "warning: error found in config file:");
1494 Error(ERR_INFO, "- config file: '%s'", getImageConfigFilename());
1495 Error(ERR_INFO, "- config token: '%s'", getTokenFromImageID(i));
1496 Error(ERR_INFO, "- image file: '%s'", src_bitmap->source_filename);
1498 "error: first animation frame out of bounds (%d, %d) [%d, %d]",
1499 src_x, src_y, src_bitmap_width, src_bitmap_height);
1500 Error(ERR_INFO, "custom graphic rejected for this element/action");
1502 if (i == fallback_graphic)
1503 Error(ERR_EXIT, "no fallback graphic available");
1505 Error(ERR_INFO, "fallback done to 'char_exclam' for this graphic");
1506 Error(ERR_INFO_LINE, "-");
1508 graphic_info[i] = graphic_info[fallback_graphic];
1511 /* check if last animation frame is inside specified bitmap */
1513 last_frame = graphic_info[i].anim_frames - 1;
1514 getFixedGraphicSource(i, last_frame, &src_bitmap, &src_x, &src_y);
1516 if (src_x < 0 || src_y < 0 ||
1517 src_x + width > src_bitmap_width ||
1518 src_y + height > src_bitmap_height)
1520 Error(ERR_INFO_LINE, "-");
1521 Error(ERR_INFO, "warning: error found in config file:");
1522 Error(ERR_INFO, "- config file: '%s'", getImageConfigFilename());
1523 Error(ERR_INFO, "- config token: '%s'", getTokenFromImageID(i));
1524 Error(ERR_INFO, "- image file: '%s'", src_bitmap->source_filename);
1526 "error: last animation frame (%d) out of bounds (%d, %d) [%d, %d]",
1527 last_frame, src_x, src_y, src_bitmap_width, src_bitmap_height);
1528 Error(ERR_INFO, "::: %d, %d", width, height);
1529 Error(ERR_INFO, "custom graphic rejected for this element/action");
1531 if (i == fallback_graphic)
1532 Error(ERR_EXIT, "no fallback graphic available");
1534 Error(ERR_INFO, "fallback done to 'char_exclam' for this graphic");
1535 Error(ERR_INFO_LINE, "-");
1537 graphic_info[i] = graphic_info[fallback_graphic];
1542 static void InitGraphicCompatibilityInfo()
1544 struct FileInfo *fi_global_door =
1545 getImageListEntryFromImageID(IMG_GLOBAL_DOOR);
1546 int num_images = getImageListSize();
1549 /* the following compatibility handling is needed for the following case:
1550 versions up to 3.3.0.0 used one large bitmap "global.door" for various
1551 graphics mainly used for door and panel graphics, like editor, tape and
1552 in-game buttons with hard-coded bitmap positions and button sizes; as
1553 these graphics now have individual definitions, redefining "global.door"
1554 to change all these graphics at once like before does not work anymore
1555 (because all those individual definitions still have their default values);
1556 to solve this, remap all those individual definitions that are not
1557 redefined to the new bitmap of "global.door" if it was redefined */
1559 /* special compatibility handling if image "global.door" was redefined */
1560 if (fi_global_door->redefined)
1562 for (i = 0; i < num_images; i++)
1564 struct FileInfo *fi = getImageListEntryFromImageID(i);
1566 /* process only those images that still use the default settings */
1569 /* process all images which default to same image as "global.door" */
1570 if (strEqual(fi->default_filename, fi_global_door->default_filename))
1572 // printf("::: special treatment needed for token '%s'\n", fi->token);
1574 graphic_info[i].bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
1580 InitGraphicCompatibilityInfo_Doors();
1583 static void InitElementSoundInfo()
1585 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
1586 int num_property_mappings = getSoundListPropertyMappingSize();
1589 /* set values to -1 to identify later as "uninitialized" values */
1590 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1591 for (act = 0; act < NUM_ACTIONS; act++)
1592 element_info[i].sound[act] = -1;
1594 /* initialize element/sound mapping from static configuration */
1595 for (i = 0; element_to_sound[i].element > -1; i++)
1597 int element = element_to_sound[i].element;
1598 int action = element_to_sound[i].action;
1599 int sound = element_to_sound[i].sound;
1600 boolean is_class = element_to_sound[i].is_class;
1603 action = ACTION_DEFAULT;
1606 element_info[element].sound[action] = sound;
1608 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
1609 if (strEqual(element_info[j].class_name,
1610 element_info[element].class_name))
1611 element_info[j].sound[action] = sound;
1614 /* initialize element class/sound mapping from dynamic configuration */
1615 for (i = 0; i < num_property_mappings; i++)
1617 int element_class = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
1618 int action = property_mapping[i].ext1_index;
1619 int sound = property_mapping[i].artwork_index;
1621 if (element_class < 0 || element_class >= MAX_NUM_ELEMENTS)
1625 action = ACTION_DEFAULT;
1627 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
1628 if (strEqual(element_info[j].class_name,
1629 element_info[element_class].class_name))
1630 element_info[j].sound[action] = sound;
1633 /* initialize element/sound mapping from dynamic configuration */
1634 for (i = 0; i < num_property_mappings; i++)
1636 int element = property_mapping[i].base_index;
1637 int action = property_mapping[i].ext1_index;
1638 int sound = property_mapping[i].artwork_index;
1640 if (element >= MAX_NUM_ELEMENTS)
1644 action = ACTION_DEFAULT;
1646 element_info[element].sound[action] = sound;
1649 /* now set all '-1' values to element specific default values */
1650 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1652 for (act = 0; act < NUM_ACTIONS; act++)
1654 /* generic default action sound (defined by "[default]" directive) */
1655 int default_action_sound = element_info[EL_DEFAULT].sound[act];
1657 /* look for special default action sound (classic game specific) */
1658 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].sound[act] != -1)
1659 default_action_sound = element_info[EL_BD_DEFAULT].sound[act];
1660 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].sound[act] != -1)
1661 default_action_sound = element_info[EL_SP_DEFAULT].sound[act];
1662 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].sound[act] != -1)
1663 default_action_sound = element_info[EL_SB_DEFAULT].sound[act];
1665 /* !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!! */
1666 /* !!! make this better !!! */
1667 if (i == EL_EMPTY_SPACE)
1668 default_action_sound = element_info[EL_DEFAULT].sound[act];
1670 /* no sound for this specific action -- use default action sound */
1671 if (element_info[i].sound[act] == -1)
1672 element_info[i].sound[act] = default_action_sound;
1676 /* copy sound settings to some elements that are only stored in level file
1677 in native R'n'D levels, but are used by game engine in native EM levels */
1678 for (i = 0; copy_properties[i][0] != -1; i++)
1679 for (j = 1; j <= 4; j++)
1680 for (act = 0; act < NUM_ACTIONS; act++)
1681 element_info[copy_properties[i][j]].sound[act] =
1682 element_info[copy_properties[i][0]].sound[act];
1685 static void InitGameModeSoundInfo()
1689 /* set values to -1 to identify later as "uninitialized" values */
1690 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
1693 /* initialize gamemode/sound mapping from static configuration */
1694 for (i = 0; gamemode_to_sound[i].sound > -1; i++)
1696 int gamemode = gamemode_to_sound[i].gamemode;
1697 int sound = gamemode_to_sound[i].sound;
1700 gamemode = GAME_MODE_DEFAULT;
1702 menu.sound[gamemode] = sound;
1705 /* now set all '-1' values to levelset specific default values */
1706 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
1707 if (menu.sound[i] == -1)
1708 menu.sound[i] = menu.sound[GAME_MODE_DEFAULT];
1711 static void set_sound_parameters(int sound, char **parameter_raw)
1713 int parameter[NUM_SND_ARGS];
1716 /* get integer values from string parameters */
1717 for (i = 0; i < NUM_SND_ARGS; i++)
1719 get_parameter_value(parameter_raw[i],
1720 sound_config_suffix[i].token,
1721 sound_config_suffix[i].type);
1723 /* explicit loop mode setting in configuration overrides default value */
1724 if (parameter[SND_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
1725 sound_info[sound].loop = parameter[SND_ARG_MODE_LOOP];
1727 /* sound volume to change the original volume when loading the sound file */
1728 sound_info[sound].volume = parameter[SND_ARG_VOLUME];
1730 /* sound priority to give certain sounds a higher or lower priority */
1731 sound_info[sound].priority = parameter[SND_ARG_PRIORITY];
1734 static void InitSoundInfo()
1736 int *sound_effect_properties;
1737 int num_sounds = getSoundListSize();
1740 checked_free(sound_info);
1742 sound_effect_properties = checked_calloc(num_sounds * sizeof(int));
1743 sound_info = checked_calloc(num_sounds * sizeof(struct SoundInfo));
1745 /* initialize sound effect for all elements to "no sound" */
1746 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1747 for (j = 0; j < NUM_ACTIONS; j++)
1748 element_info[i].sound[j] = SND_UNDEFINED;
1750 for (i = 0; i < num_sounds; i++)
1752 struct FileInfo *sound = getSoundListEntry(i);
1753 int len_effect_text = strlen(sound->token);
1755 sound_effect_properties[i] = ACTION_OTHER;
1756 sound_info[i].loop = FALSE; /* default: play sound only once */
1758 /* determine all loop sounds and identify certain sound classes */
1760 for (j = 0; element_action_info[j].suffix; j++)
1762 int len_action_text = strlen(element_action_info[j].suffix);
1764 if (len_action_text < len_effect_text &&
1765 strEqual(&sound->token[len_effect_text - len_action_text],
1766 element_action_info[j].suffix))
1768 sound_effect_properties[i] = element_action_info[j].value;
1769 sound_info[i].loop = element_action_info[j].is_loop_sound;
1775 /* associate elements and some selected sound actions */
1777 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
1779 if (element_info[j].class_name)
1781 int len_class_text = strlen(element_info[j].class_name);
1783 if (len_class_text + 1 < len_effect_text &&
1784 strncmp(sound->token,
1785 element_info[j].class_name, len_class_text) == 0 &&
1786 sound->token[len_class_text] == '.')
1788 int sound_action_value = sound_effect_properties[i];
1790 element_info[j].sound[sound_action_value] = i;
1795 set_sound_parameters(i, sound->parameter);
1798 free(sound_effect_properties);
1801 static void InitGameModeMusicInfo()
1803 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
1804 int num_property_mappings = getMusicListPropertyMappingSize();
1805 int default_levelset_music = -1;
1808 /* set values to -1 to identify later as "uninitialized" values */
1809 for (i = 0; i < MAX_LEVELS; i++)
1810 levelset.music[i] = -1;
1811 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
1814 /* initialize gamemode/music mapping from static configuration */
1815 for (i = 0; gamemode_to_music[i].music > -1; i++)
1817 int gamemode = gamemode_to_music[i].gamemode;
1818 int music = gamemode_to_music[i].music;
1821 gamemode = GAME_MODE_DEFAULT;
1823 menu.music[gamemode] = music;
1826 /* initialize gamemode/music mapping from dynamic configuration */
1827 for (i = 0; i < num_property_mappings; i++)
1829 int prefix = property_mapping[i].base_index;
1830 int gamemode = property_mapping[i].ext1_index;
1831 int level = property_mapping[i].ext2_index;
1832 int music = property_mapping[i].artwork_index;
1834 if (prefix < 0 || prefix >= NUM_MUSIC_PREFIXES)
1838 gamemode = GAME_MODE_DEFAULT;
1840 /* level specific music only allowed for in-game music */
1841 if (level != -1 && gamemode == GAME_MODE_DEFAULT)
1842 gamemode = GAME_MODE_PLAYING;
1847 default_levelset_music = music;
1850 if (gamemode == GAME_MODE_PLAYING || gamemode == GAME_MODE_DEFAULT)
1851 levelset.music[level] = music;
1852 if (gamemode != GAME_MODE_PLAYING)
1853 menu.music[gamemode] = music;
1856 /* now set all '-1' values to menu specific default values */
1857 /* (undefined values of "levelset.music[]" might stay at "-1" to
1858 allow dynamic selection of music files from music directory!) */
1859 for (i = 0; i < MAX_LEVELS; i++)
1860 if (levelset.music[i] == -1)
1861 levelset.music[i] = default_levelset_music;
1862 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
1863 if (menu.music[i] == -1)
1864 menu.music[i] = menu.music[GAME_MODE_DEFAULT];
1867 static void set_music_parameters(int music, char **parameter_raw)
1869 int parameter[NUM_MUS_ARGS];
1872 /* get integer values from string parameters */
1873 for (i = 0; i < NUM_MUS_ARGS; i++)
1875 get_parameter_value(parameter_raw[i],
1876 music_config_suffix[i].token,
1877 music_config_suffix[i].type);
1879 /* explicit loop mode setting in configuration overrides default value */
1880 if (parameter[MUS_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
1881 music_info[music].loop = parameter[MUS_ARG_MODE_LOOP];
1884 static void InitMusicInfo()
1886 int num_music = getMusicListSize();
1889 checked_free(music_info);
1891 music_info = checked_calloc(num_music * sizeof(struct MusicInfo));
1893 for (i = 0; i < num_music; i++)
1895 struct FileInfo *music = getMusicListEntry(i);
1896 int len_music_text = strlen(music->token);
1898 music_info[i].loop = TRUE; /* default: play music in loop mode */
1900 /* determine all loop music */
1902 for (j = 0; music_prefix_info[j].prefix; j++)
1904 int len_prefix_text = strlen(music_prefix_info[j].prefix);
1906 if (len_prefix_text < len_music_text &&
1907 strncmp(music->token,
1908 music_prefix_info[j].prefix, len_prefix_text) == 0)
1910 music_info[i].loop = music_prefix_info[j].is_loop_music;
1916 set_music_parameters(i, music->parameter);
1920 static void ReinitializeGraphics()
1922 print_timestamp_init("ReinitializeGraphics");
1924 InitGfxTileSizeInfo(game.tile_size, TILESIZE);
1926 InitGraphicInfo(); /* graphic properties mapping */
1927 print_timestamp_time("InitGraphicInfo");
1928 InitElementGraphicInfo(); /* element game graphic mapping */
1929 print_timestamp_time("InitElementGraphicInfo");
1930 InitElementSpecialGraphicInfo(); /* element special graphic mapping */
1931 print_timestamp_time("InitElementSpecialGraphicInfo");
1933 InitElementSmallImages(); /* scale elements to all needed sizes */
1934 print_timestamp_time("InitElementSmallImages");
1935 InitScaledImages(); /* scale all other images, if needed */
1936 print_timestamp_time("InitScaledImages");
1937 InitBitmapPointers(); /* set standard size bitmap pointers */
1938 print_timestamp_time("InitBitmapPointers");
1939 InitFontGraphicInfo(); /* initialize text drawing functions */
1940 print_timestamp_time("InitFontGraphicInfo");
1942 InitGraphicInfo_EM(); /* graphic mapping for EM engine */
1943 print_timestamp_time("InitGraphicInfo_EM");
1945 InitGraphicCompatibilityInfo();
1946 print_timestamp_time("InitGraphicCompatibilityInfo");
1948 SetMainBackgroundImage(IMG_BACKGROUND);
1949 print_timestamp_time("SetMainBackgroundImage");
1950 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
1951 print_timestamp_time("SetDoorBackgroundImage");
1954 print_timestamp_time("InitGadgets");
1956 print_timestamp_time("InitToons");
1958 print_timestamp_time("InitDoors");
1960 print_timestamp_done("ReinitializeGraphics");
1963 static void ReinitializeSounds()
1965 InitSoundInfo(); /* sound properties mapping */
1966 InitElementSoundInfo(); /* element game sound mapping */
1967 InitGameModeSoundInfo(); /* game mode sound mapping */
1969 InitPlayLevelSound(); /* internal game sound settings */
1972 static void ReinitializeMusic()
1974 InitMusicInfo(); /* music properties mapping */
1975 InitGameModeMusicInfo(); /* game mode music mapping */
1978 static int get_special_property_bit(int element, int property_bit_nr)
1980 struct PropertyBitInfo
1986 static struct PropertyBitInfo pb_can_move_into_acid[] =
1988 /* the player may be able fall into acid when gravity is activated */
1993 { EL_SP_MURPHY, 0 },
1994 { EL_SOKOBAN_FIELD_PLAYER, 0 },
1996 /* all elements that can move may be able to also move into acid */
1999 { EL_BUG_RIGHT, 1 },
2002 { EL_SPACESHIP, 2 },
2003 { EL_SPACESHIP_LEFT, 2 },
2004 { EL_SPACESHIP_RIGHT, 2 },
2005 { EL_SPACESHIP_UP, 2 },
2006 { EL_SPACESHIP_DOWN, 2 },
2007 { EL_BD_BUTTERFLY, 3 },
2008 { EL_BD_BUTTERFLY_LEFT, 3 },
2009 { EL_BD_BUTTERFLY_RIGHT, 3 },
2010 { EL_BD_BUTTERFLY_UP, 3 },
2011 { EL_BD_BUTTERFLY_DOWN, 3 },
2012 { EL_BD_FIREFLY, 4 },
2013 { EL_BD_FIREFLY_LEFT, 4 },
2014 { EL_BD_FIREFLY_RIGHT, 4 },
2015 { EL_BD_FIREFLY_UP, 4 },
2016 { EL_BD_FIREFLY_DOWN, 4 },
2018 { EL_YAMYAM_LEFT, 5 },
2019 { EL_YAMYAM_RIGHT, 5 },
2020 { EL_YAMYAM_UP, 5 },
2021 { EL_YAMYAM_DOWN, 5 },
2022 { EL_DARK_YAMYAM, 6 },
2025 { EL_PACMAN_LEFT, 8 },
2026 { EL_PACMAN_RIGHT, 8 },
2027 { EL_PACMAN_UP, 8 },
2028 { EL_PACMAN_DOWN, 8 },
2030 { EL_MOLE_LEFT, 9 },
2031 { EL_MOLE_RIGHT, 9 },
2033 { EL_MOLE_DOWN, 9 },
2037 { EL_SATELLITE, 13 },
2038 { EL_SP_SNIKSNAK, 14 },
2039 { EL_SP_ELECTRON, 15 },
2042 { EL_EMC_ANDROID, 18 },
2047 static struct PropertyBitInfo pb_dont_collide_with[] =
2049 { EL_SP_SNIKSNAK, 0 },
2050 { EL_SP_ELECTRON, 1 },
2058 struct PropertyBitInfo *pb_info;
2061 { EP_CAN_MOVE_INTO_ACID, pb_can_move_into_acid },
2062 { EP_DONT_COLLIDE_WITH, pb_dont_collide_with },
2067 struct PropertyBitInfo *pb_info = NULL;
2070 for (i = 0; pb_definition[i].bit_nr != -1; i++)
2071 if (pb_definition[i].bit_nr == property_bit_nr)
2072 pb_info = pb_definition[i].pb_info;
2074 if (pb_info == NULL)
2077 for (i = 0; pb_info[i].element != -1; i++)
2078 if (pb_info[i].element == element)
2079 return pb_info[i].bit_nr;
2084 void setBitfieldProperty(int *bitfield, int property_bit_nr, int element,
2085 boolean property_value)
2087 int bit_nr = get_special_property_bit(element, property_bit_nr);
2092 *bitfield |= (1 << bit_nr);
2094 *bitfield &= ~(1 << bit_nr);
2098 boolean getBitfieldProperty(int *bitfield, int property_bit_nr, int element)
2100 int bit_nr = get_special_property_bit(element, property_bit_nr);
2103 return ((*bitfield & (1 << bit_nr)) != 0);
2108 static void ResolveGroupElementExt(int group_element, int recursion_depth)
2110 static int group_nr;
2111 static struct ElementGroupInfo *group;
2112 struct ElementGroupInfo *actual_group = element_info[group_element].group;
2115 if (actual_group == NULL) /* not yet initialized */
2118 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
2120 Error(ERR_WARN, "recursion too deep when resolving group element %d",
2121 group_element - EL_GROUP_START + 1);
2123 /* replace element which caused too deep recursion by question mark */
2124 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
2129 if (recursion_depth == 0) /* initialization */
2131 group = actual_group;
2132 group_nr = GROUP_NR(group_element);
2134 group->num_elements_resolved = 0;
2135 group->choice_pos = 0;
2137 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2138 element_info[i].in_group[group_nr] = FALSE;
2141 for (i = 0; i < actual_group->num_elements; i++)
2143 int element = actual_group->element[i];
2145 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
2148 if (IS_GROUP_ELEMENT(element))
2149 ResolveGroupElementExt(element, recursion_depth + 1);
2152 group->element_resolved[group->num_elements_resolved++] = element;
2153 element_info[element].in_group[group_nr] = TRUE;
2158 void ResolveGroupElement(int group_element)
2160 ResolveGroupElementExt(group_element, 0);
2163 void InitElementPropertiesStatic()
2165 static boolean clipboard_elements_initialized = FALSE;
2167 static int ep_diggable[] =
2172 EL_SP_BUGGY_BASE_ACTIVATING,
2175 EL_INVISIBLE_SAND_ACTIVE,
2178 /* !!! currently not diggable, but handled by 'ep_dont_run_into' !!! */
2179 /* (if amoeba can grow into anything diggable, maybe keep these out) */
2184 EL_SP_BUGGY_BASE_ACTIVE,
2191 static int ep_collectible_only[] =
2213 EL_DYNABOMB_INCREASE_NUMBER,
2214 EL_DYNABOMB_INCREASE_SIZE,
2215 EL_DYNABOMB_INCREASE_POWER,
2233 /* !!! handle separately !!! */
2234 EL_DC_LANDMINE, /* deadly when running into, but can be snapped */
2240 static int ep_dont_run_into[] =
2242 /* same elements as in 'ep_dont_touch' */
2248 /* same elements as in 'ep_dont_collide_with' */
2260 /* !!! maybe this should better be handled by 'ep_diggable' !!! */
2265 EL_SP_BUGGY_BASE_ACTIVE,
2272 static int ep_dont_collide_with[] =
2274 /* same elements as in 'ep_dont_touch' */
2291 static int ep_dont_touch[] =
2301 static int ep_indestructible[] =
2305 EL_ACID_POOL_TOPLEFT,
2306 EL_ACID_POOL_TOPRIGHT,
2307 EL_ACID_POOL_BOTTOMLEFT,
2308 EL_ACID_POOL_BOTTOM,
2309 EL_ACID_POOL_BOTTOMRIGHT,
2310 EL_SP_HARDWARE_GRAY,
2311 EL_SP_HARDWARE_GREEN,
2312 EL_SP_HARDWARE_BLUE,
2314 EL_SP_HARDWARE_YELLOW,
2315 EL_SP_HARDWARE_BASE_1,
2316 EL_SP_HARDWARE_BASE_2,
2317 EL_SP_HARDWARE_BASE_3,
2318 EL_SP_HARDWARE_BASE_4,
2319 EL_SP_HARDWARE_BASE_5,
2320 EL_SP_HARDWARE_BASE_6,
2321 EL_INVISIBLE_STEELWALL,
2322 EL_INVISIBLE_STEELWALL_ACTIVE,
2323 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2324 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
2325 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
2326 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2327 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
2328 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
2329 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2330 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
2331 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
2332 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
2333 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
2334 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
2336 EL_LIGHT_SWITCH_ACTIVE,
2337 EL_SIGN_EXCLAMATION,
2338 EL_SIGN_RADIOACTIVITY,
2345 EL_SIGN_ENTRY_FORBIDDEN,
2346 EL_SIGN_EMERGENCY_EXIT,
2354 EL_STEEL_EXIT_CLOSED,
2356 EL_STEEL_EXIT_OPENING,
2357 EL_STEEL_EXIT_CLOSING,
2358 EL_EM_STEEL_EXIT_CLOSED,
2359 EL_EM_STEEL_EXIT_OPEN,
2360 EL_EM_STEEL_EXIT_OPENING,
2361 EL_EM_STEEL_EXIT_CLOSING,
2362 EL_DC_STEELWALL_1_LEFT,
2363 EL_DC_STEELWALL_1_RIGHT,
2364 EL_DC_STEELWALL_1_TOP,
2365 EL_DC_STEELWALL_1_BOTTOM,
2366 EL_DC_STEELWALL_1_HORIZONTAL,
2367 EL_DC_STEELWALL_1_VERTICAL,
2368 EL_DC_STEELWALL_1_TOPLEFT,
2369 EL_DC_STEELWALL_1_TOPRIGHT,
2370 EL_DC_STEELWALL_1_BOTTOMLEFT,
2371 EL_DC_STEELWALL_1_BOTTOMRIGHT,
2372 EL_DC_STEELWALL_1_TOPLEFT_2,
2373 EL_DC_STEELWALL_1_TOPRIGHT_2,
2374 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
2375 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
2376 EL_DC_STEELWALL_2_LEFT,
2377 EL_DC_STEELWALL_2_RIGHT,
2378 EL_DC_STEELWALL_2_TOP,
2379 EL_DC_STEELWALL_2_BOTTOM,
2380 EL_DC_STEELWALL_2_HORIZONTAL,
2381 EL_DC_STEELWALL_2_VERTICAL,
2382 EL_DC_STEELWALL_2_MIDDLE,
2383 EL_DC_STEELWALL_2_SINGLE,
2384 EL_STEELWALL_SLIPPERY,
2398 EL_GATE_1_GRAY_ACTIVE,
2399 EL_GATE_2_GRAY_ACTIVE,
2400 EL_GATE_3_GRAY_ACTIVE,
2401 EL_GATE_4_GRAY_ACTIVE,
2410 EL_EM_GATE_1_GRAY_ACTIVE,
2411 EL_EM_GATE_2_GRAY_ACTIVE,
2412 EL_EM_GATE_3_GRAY_ACTIVE,
2413 EL_EM_GATE_4_GRAY_ACTIVE,
2422 EL_EMC_GATE_5_GRAY_ACTIVE,
2423 EL_EMC_GATE_6_GRAY_ACTIVE,
2424 EL_EMC_GATE_7_GRAY_ACTIVE,
2425 EL_EMC_GATE_8_GRAY_ACTIVE,
2427 EL_DC_GATE_WHITE_GRAY,
2428 EL_DC_GATE_WHITE_GRAY_ACTIVE,
2429 EL_DC_GATE_FAKE_GRAY,
2431 EL_SWITCHGATE_OPENING,
2432 EL_SWITCHGATE_CLOSED,
2433 EL_SWITCHGATE_CLOSING,
2434 EL_DC_SWITCHGATE_SWITCH_UP,
2435 EL_DC_SWITCHGATE_SWITCH_DOWN,
2437 EL_TIMEGATE_OPENING,
2439 EL_TIMEGATE_CLOSING,
2440 EL_DC_TIMEGATE_SWITCH,
2441 EL_DC_TIMEGATE_SWITCH_ACTIVE,
2445 EL_TUBE_VERTICAL_LEFT,
2446 EL_TUBE_VERTICAL_RIGHT,
2447 EL_TUBE_HORIZONTAL_UP,
2448 EL_TUBE_HORIZONTAL_DOWN,
2453 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
2454 EL_EXPANDABLE_STEELWALL_VERTICAL,
2455 EL_EXPANDABLE_STEELWALL_ANY,
2460 static int ep_slippery[] =
2474 EL_ROBOT_WHEEL_ACTIVE,
2480 EL_ACID_POOL_TOPLEFT,
2481 EL_ACID_POOL_TOPRIGHT,
2491 EL_STEELWALL_SLIPPERY,
2494 EL_EMC_WALL_SLIPPERY_1,
2495 EL_EMC_WALL_SLIPPERY_2,
2496 EL_EMC_WALL_SLIPPERY_3,
2497 EL_EMC_WALL_SLIPPERY_4,
2499 EL_EMC_MAGIC_BALL_ACTIVE,
2504 static int ep_can_change[] =
2509 static int ep_can_move[] =
2511 /* same elements as in 'pb_can_move_into_acid' */
2534 static int ep_can_fall[] =
2548 EL_QUICKSAND_FAST_FULL,
2550 EL_BD_MAGIC_WALL_FULL,
2551 EL_DC_MAGIC_WALL_FULL,
2565 static int ep_can_smash_player[] =
2591 static int ep_can_smash_enemies[] =
2600 static int ep_can_smash_everything[] =
2609 static int ep_explodes_by_fire[] =
2611 /* same elements as in 'ep_explodes_impact' */
2616 /* same elements as in 'ep_explodes_smashed' */
2626 EL_EM_DYNAMITE_ACTIVE,
2627 EL_DYNABOMB_PLAYER_1_ACTIVE,
2628 EL_DYNABOMB_PLAYER_2_ACTIVE,
2629 EL_DYNABOMB_PLAYER_3_ACTIVE,
2630 EL_DYNABOMB_PLAYER_4_ACTIVE,
2631 EL_DYNABOMB_INCREASE_NUMBER,
2632 EL_DYNABOMB_INCREASE_SIZE,
2633 EL_DYNABOMB_INCREASE_POWER,
2634 EL_SP_DISK_RED_ACTIVE,
2648 static int ep_explodes_smashed[] =
2650 /* same elements as in 'ep_explodes_impact' */
2664 static int ep_explodes_impact[] =
2673 static int ep_walkable_over[] =
2677 EL_SOKOBAN_FIELD_EMPTY,
2684 EL_EM_STEEL_EXIT_OPEN,
2685 EL_EM_STEEL_EXIT_OPENING,
2694 EL_GATE_1_GRAY_ACTIVE,
2695 EL_GATE_2_GRAY_ACTIVE,
2696 EL_GATE_3_GRAY_ACTIVE,
2697 EL_GATE_4_GRAY_ACTIVE,
2705 static int ep_walkable_inside[] =
2710 EL_TUBE_VERTICAL_LEFT,
2711 EL_TUBE_VERTICAL_RIGHT,
2712 EL_TUBE_HORIZONTAL_UP,
2713 EL_TUBE_HORIZONTAL_DOWN,
2722 static int ep_walkable_under[] =
2727 static int ep_passable_over[] =
2737 EL_EM_GATE_1_GRAY_ACTIVE,
2738 EL_EM_GATE_2_GRAY_ACTIVE,
2739 EL_EM_GATE_3_GRAY_ACTIVE,
2740 EL_EM_GATE_4_GRAY_ACTIVE,
2749 EL_EMC_GATE_5_GRAY_ACTIVE,
2750 EL_EMC_GATE_6_GRAY_ACTIVE,
2751 EL_EMC_GATE_7_GRAY_ACTIVE,
2752 EL_EMC_GATE_8_GRAY_ACTIVE,
2754 EL_DC_GATE_WHITE_GRAY,
2755 EL_DC_GATE_WHITE_GRAY_ACTIVE,
2762 static int ep_passable_inside[] =
2768 EL_SP_PORT_HORIZONTAL,
2769 EL_SP_PORT_VERTICAL,
2771 EL_SP_GRAVITY_PORT_LEFT,
2772 EL_SP_GRAVITY_PORT_RIGHT,
2773 EL_SP_GRAVITY_PORT_UP,
2774 EL_SP_GRAVITY_PORT_DOWN,
2775 EL_SP_GRAVITY_ON_PORT_LEFT,
2776 EL_SP_GRAVITY_ON_PORT_RIGHT,
2777 EL_SP_GRAVITY_ON_PORT_UP,
2778 EL_SP_GRAVITY_ON_PORT_DOWN,
2779 EL_SP_GRAVITY_OFF_PORT_LEFT,
2780 EL_SP_GRAVITY_OFF_PORT_RIGHT,
2781 EL_SP_GRAVITY_OFF_PORT_UP,
2782 EL_SP_GRAVITY_OFF_PORT_DOWN,
2787 static int ep_passable_under[] =
2792 static int ep_droppable[] =
2797 static int ep_explodes_1x1_old[] =
2802 static int ep_pushable[] =
2814 EL_SOKOBAN_FIELD_FULL,
2823 static int ep_explodes_cross_old[] =
2828 static int ep_protected[] =
2830 /* same elements as in 'ep_walkable_inside' */
2834 EL_TUBE_VERTICAL_LEFT,
2835 EL_TUBE_VERTICAL_RIGHT,
2836 EL_TUBE_HORIZONTAL_UP,
2837 EL_TUBE_HORIZONTAL_DOWN,
2843 /* same elements as in 'ep_passable_over' */
2852 EL_EM_GATE_1_GRAY_ACTIVE,
2853 EL_EM_GATE_2_GRAY_ACTIVE,
2854 EL_EM_GATE_3_GRAY_ACTIVE,
2855 EL_EM_GATE_4_GRAY_ACTIVE,
2864 EL_EMC_GATE_5_GRAY_ACTIVE,
2865 EL_EMC_GATE_6_GRAY_ACTIVE,
2866 EL_EMC_GATE_7_GRAY_ACTIVE,
2867 EL_EMC_GATE_8_GRAY_ACTIVE,
2869 EL_DC_GATE_WHITE_GRAY,
2870 EL_DC_GATE_WHITE_GRAY_ACTIVE,
2874 /* same elements as in 'ep_passable_inside' */
2879 EL_SP_PORT_HORIZONTAL,
2880 EL_SP_PORT_VERTICAL,
2882 EL_SP_GRAVITY_PORT_LEFT,
2883 EL_SP_GRAVITY_PORT_RIGHT,
2884 EL_SP_GRAVITY_PORT_UP,
2885 EL_SP_GRAVITY_PORT_DOWN,
2886 EL_SP_GRAVITY_ON_PORT_LEFT,
2887 EL_SP_GRAVITY_ON_PORT_RIGHT,
2888 EL_SP_GRAVITY_ON_PORT_UP,
2889 EL_SP_GRAVITY_ON_PORT_DOWN,
2890 EL_SP_GRAVITY_OFF_PORT_LEFT,
2891 EL_SP_GRAVITY_OFF_PORT_RIGHT,
2892 EL_SP_GRAVITY_OFF_PORT_UP,
2893 EL_SP_GRAVITY_OFF_PORT_DOWN,
2898 static int ep_throwable[] =
2903 static int ep_can_explode[] =
2905 /* same elements as in 'ep_explodes_impact' */
2910 /* same elements as in 'ep_explodes_smashed' */
2916 /* elements that can explode by explosion or by dragonfire */
2920 EL_EM_DYNAMITE_ACTIVE,
2921 EL_DYNABOMB_PLAYER_1_ACTIVE,
2922 EL_DYNABOMB_PLAYER_2_ACTIVE,
2923 EL_DYNABOMB_PLAYER_3_ACTIVE,
2924 EL_DYNABOMB_PLAYER_4_ACTIVE,
2925 EL_DYNABOMB_INCREASE_NUMBER,
2926 EL_DYNABOMB_INCREASE_SIZE,
2927 EL_DYNABOMB_INCREASE_POWER,
2928 EL_SP_DISK_RED_ACTIVE,
2936 /* elements that can explode only by explosion */
2942 static int ep_gravity_reachable[] =
2948 EL_INVISIBLE_SAND_ACTIVE,
2953 EL_SP_PORT_HORIZONTAL,
2954 EL_SP_PORT_VERTICAL,
2956 EL_SP_GRAVITY_PORT_LEFT,
2957 EL_SP_GRAVITY_PORT_RIGHT,
2958 EL_SP_GRAVITY_PORT_UP,
2959 EL_SP_GRAVITY_PORT_DOWN,
2960 EL_SP_GRAVITY_ON_PORT_LEFT,
2961 EL_SP_GRAVITY_ON_PORT_RIGHT,
2962 EL_SP_GRAVITY_ON_PORT_UP,
2963 EL_SP_GRAVITY_ON_PORT_DOWN,
2964 EL_SP_GRAVITY_OFF_PORT_LEFT,
2965 EL_SP_GRAVITY_OFF_PORT_RIGHT,
2966 EL_SP_GRAVITY_OFF_PORT_UP,
2967 EL_SP_GRAVITY_OFF_PORT_DOWN,
2973 static int ep_player[] =
2980 EL_SOKOBAN_FIELD_PLAYER,
2986 static int ep_can_pass_magic_wall[] =
3000 static int ep_can_pass_dc_magic_wall[] =
3016 static int ep_switchable[] =
3020 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3021 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3022 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3023 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3024 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3025 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3026 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3027 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3028 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3029 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3030 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3031 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3032 EL_SWITCHGATE_SWITCH_UP,
3033 EL_SWITCHGATE_SWITCH_DOWN,
3034 EL_DC_SWITCHGATE_SWITCH_UP,
3035 EL_DC_SWITCHGATE_SWITCH_DOWN,
3037 EL_LIGHT_SWITCH_ACTIVE,
3039 EL_DC_TIMEGATE_SWITCH,
3040 EL_BALLOON_SWITCH_LEFT,
3041 EL_BALLOON_SWITCH_RIGHT,
3042 EL_BALLOON_SWITCH_UP,
3043 EL_BALLOON_SWITCH_DOWN,
3044 EL_BALLOON_SWITCH_ANY,
3045 EL_BALLOON_SWITCH_NONE,
3048 EL_EMC_MAGIC_BALL_SWITCH,
3049 EL_EMC_MAGIC_BALL_SWITCH_ACTIVE,
3054 static int ep_bd_element[] =
3088 static int ep_sp_element[] =
3090 /* should always be valid */
3093 /* standard classic Supaplex elements */
3100 EL_SP_HARDWARE_GRAY,
3108 EL_SP_GRAVITY_PORT_RIGHT,
3109 EL_SP_GRAVITY_PORT_DOWN,
3110 EL_SP_GRAVITY_PORT_LEFT,
3111 EL_SP_GRAVITY_PORT_UP,
3116 EL_SP_PORT_VERTICAL,
3117 EL_SP_PORT_HORIZONTAL,
3123 EL_SP_HARDWARE_BASE_1,
3124 EL_SP_HARDWARE_GREEN,
3125 EL_SP_HARDWARE_BLUE,
3127 EL_SP_HARDWARE_YELLOW,
3128 EL_SP_HARDWARE_BASE_2,
3129 EL_SP_HARDWARE_BASE_3,
3130 EL_SP_HARDWARE_BASE_4,
3131 EL_SP_HARDWARE_BASE_5,
3132 EL_SP_HARDWARE_BASE_6,
3136 /* additional elements that appeared in newer Supaplex levels */
3139 /* additional gravity port elements (not switching, but setting gravity) */
3140 EL_SP_GRAVITY_ON_PORT_LEFT,
3141 EL_SP_GRAVITY_ON_PORT_RIGHT,
3142 EL_SP_GRAVITY_ON_PORT_UP,
3143 EL_SP_GRAVITY_ON_PORT_DOWN,
3144 EL_SP_GRAVITY_OFF_PORT_LEFT,
3145 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3146 EL_SP_GRAVITY_OFF_PORT_UP,
3147 EL_SP_GRAVITY_OFF_PORT_DOWN,
3149 /* more than one Murphy in a level results in an inactive clone */
3152 /* runtime Supaplex elements */
3153 EL_SP_DISK_RED_ACTIVE,
3154 EL_SP_TERMINAL_ACTIVE,
3155 EL_SP_BUGGY_BASE_ACTIVATING,
3156 EL_SP_BUGGY_BASE_ACTIVE,
3163 static int ep_sb_element[] =
3168 EL_SOKOBAN_FIELD_EMPTY,
3169 EL_SOKOBAN_FIELD_FULL,
3170 EL_SOKOBAN_FIELD_PLAYER,
3175 EL_INVISIBLE_STEELWALL,
3180 static int ep_gem[] =
3192 static int ep_food_dark_yamyam[] =
3220 static int ep_food_penguin[] =
3234 static int ep_food_pig[] =
3246 static int ep_historic_wall[] =
3257 EL_GATE_1_GRAY_ACTIVE,
3258 EL_GATE_2_GRAY_ACTIVE,
3259 EL_GATE_3_GRAY_ACTIVE,
3260 EL_GATE_4_GRAY_ACTIVE,
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,
3279 EL_EXPANDABLE_WALL_HORIZONTAL,
3280 EL_EXPANDABLE_WALL_VERTICAL,
3281 EL_EXPANDABLE_WALL_ANY,
3282 EL_EXPANDABLE_WALL_GROWING,
3283 EL_BD_EXPANDABLE_WALL,
3290 EL_SP_HARDWARE_GRAY,
3291 EL_SP_HARDWARE_GREEN,
3292 EL_SP_HARDWARE_BLUE,
3294 EL_SP_HARDWARE_YELLOW,
3295 EL_SP_HARDWARE_BASE_1,
3296 EL_SP_HARDWARE_BASE_2,
3297 EL_SP_HARDWARE_BASE_3,
3298 EL_SP_HARDWARE_BASE_4,
3299 EL_SP_HARDWARE_BASE_5,
3300 EL_SP_HARDWARE_BASE_6,
3302 EL_SP_TERMINAL_ACTIVE,
3305 EL_INVISIBLE_STEELWALL,
3306 EL_INVISIBLE_STEELWALL_ACTIVE,
3308 EL_INVISIBLE_WALL_ACTIVE,
3309 EL_STEELWALL_SLIPPERY,
3326 static int ep_historic_solid[] =
3330 EL_EXPANDABLE_WALL_HORIZONTAL,
3331 EL_EXPANDABLE_WALL_VERTICAL,
3332 EL_EXPANDABLE_WALL_ANY,
3333 EL_BD_EXPANDABLE_WALL,
3346 EL_QUICKSAND_FILLING,
3347 EL_QUICKSAND_EMPTYING,
3349 EL_MAGIC_WALL_ACTIVE,
3350 EL_MAGIC_WALL_EMPTYING,
3351 EL_MAGIC_WALL_FILLING,
3355 EL_BD_MAGIC_WALL_ACTIVE,
3356 EL_BD_MAGIC_WALL_EMPTYING,
3357 EL_BD_MAGIC_WALL_FULL,
3358 EL_BD_MAGIC_WALL_FILLING,
3359 EL_BD_MAGIC_WALL_DEAD,
3368 EL_SP_TERMINAL_ACTIVE,
3372 EL_INVISIBLE_WALL_ACTIVE,
3373 EL_SWITCHGATE_SWITCH_UP,
3374 EL_SWITCHGATE_SWITCH_DOWN,
3375 EL_DC_SWITCHGATE_SWITCH_UP,
3376 EL_DC_SWITCHGATE_SWITCH_DOWN,
3378 EL_TIMEGATE_SWITCH_ACTIVE,
3379 EL_DC_TIMEGATE_SWITCH,
3380 EL_DC_TIMEGATE_SWITCH_ACTIVE,
3392 /* the following elements are a direct copy of "indestructible" elements,
3393 except "EL_ACID", which is "indestructible", but not "solid"! */
3398 EL_ACID_POOL_TOPLEFT,
3399 EL_ACID_POOL_TOPRIGHT,
3400 EL_ACID_POOL_BOTTOMLEFT,
3401 EL_ACID_POOL_BOTTOM,
3402 EL_ACID_POOL_BOTTOMRIGHT,
3403 EL_SP_HARDWARE_GRAY,
3404 EL_SP_HARDWARE_GREEN,
3405 EL_SP_HARDWARE_BLUE,
3407 EL_SP_HARDWARE_YELLOW,
3408 EL_SP_HARDWARE_BASE_1,
3409 EL_SP_HARDWARE_BASE_2,
3410 EL_SP_HARDWARE_BASE_3,
3411 EL_SP_HARDWARE_BASE_4,
3412 EL_SP_HARDWARE_BASE_5,
3413 EL_SP_HARDWARE_BASE_6,
3414 EL_INVISIBLE_STEELWALL,
3415 EL_INVISIBLE_STEELWALL_ACTIVE,
3416 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3417 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3418 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3419 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3420 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3421 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3422 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3423 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3424 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3425 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3426 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3427 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3429 EL_LIGHT_SWITCH_ACTIVE,
3430 EL_SIGN_EXCLAMATION,
3431 EL_SIGN_RADIOACTIVITY,
3438 EL_SIGN_ENTRY_FORBIDDEN,
3439 EL_SIGN_EMERGENCY_EXIT,
3447 EL_STEEL_EXIT_CLOSED,
3449 EL_DC_STEELWALL_1_LEFT,
3450 EL_DC_STEELWALL_1_RIGHT,
3451 EL_DC_STEELWALL_1_TOP,
3452 EL_DC_STEELWALL_1_BOTTOM,
3453 EL_DC_STEELWALL_1_HORIZONTAL,
3454 EL_DC_STEELWALL_1_VERTICAL,
3455 EL_DC_STEELWALL_1_TOPLEFT,
3456 EL_DC_STEELWALL_1_TOPRIGHT,
3457 EL_DC_STEELWALL_1_BOTTOMLEFT,
3458 EL_DC_STEELWALL_1_BOTTOMRIGHT,
3459 EL_DC_STEELWALL_1_TOPLEFT_2,
3460 EL_DC_STEELWALL_1_TOPRIGHT_2,
3461 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
3462 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
3463 EL_DC_STEELWALL_2_LEFT,
3464 EL_DC_STEELWALL_2_RIGHT,
3465 EL_DC_STEELWALL_2_TOP,
3466 EL_DC_STEELWALL_2_BOTTOM,
3467 EL_DC_STEELWALL_2_HORIZONTAL,
3468 EL_DC_STEELWALL_2_VERTICAL,
3469 EL_DC_STEELWALL_2_MIDDLE,
3470 EL_DC_STEELWALL_2_SINGLE,
3471 EL_STEELWALL_SLIPPERY,
3485 EL_GATE_1_GRAY_ACTIVE,
3486 EL_GATE_2_GRAY_ACTIVE,
3487 EL_GATE_3_GRAY_ACTIVE,
3488 EL_GATE_4_GRAY_ACTIVE,
3497 EL_EM_GATE_1_GRAY_ACTIVE,
3498 EL_EM_GATE_2_GRAY_ACTIVE,
3499 EL_EM_GATE_3_GRAY_ACTIVE,
3500 EL_EM_GATE_4_GRAY_ACTIVE,
3502 EL_SWITCHGATE_OPENING,
3503 EL_SWITCHGATE_CLOSED,
3504 EL_SWITCHGATE_CLOSING,
3506 EL_TIMEGATE_OPENING,
3508 EL_TIMEGATE_CLOSING,
3512 EL_TUBE_VERTICAL_LEFT,
3513 EL_TUBE_VERTICAL_RIGHT,
3514 EL_TUBE_HORIZONTAL_UP,
3515 EL_TUBE_HORIZONTAL_DOWN,
3524 static int ep_classic_enemy[] =
3541 static int ep_belt[] =
3543 EL_CONVEYOR_BELT_1_LEFT,
3544 EL_CONVEYOR_BELT_1_MIDDLE,
3545 EL_CONVEYOR_BELT_1_RIGHT,
3546 EL_CONVEYOR_BELT_2_LEFT,
3547 EL_CONVEYOR_BELT_2_MIDDLE,
3548 EL_CONVEYOR_BELT_2_RIGHT,
3549 EL_CONVEYOR_BELT_3_LEFT,
3550 EL_CONVEYOR_BELT_3_MIDDLE,
3551 EL_CONVEYOR_BELT_3_RIGHT,
3552 EL_CONVEYOR_BELT_4_LEFT,
3553 EL_CONVEYOR_BELT_4_MIDDLE,
3554 EL_CONVEYOR_BELT_4_RIGHT,
3559 static int ep_belt_active[] =
3561 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3562 EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE,
3563 EL_CONVEYOR_BELT_1_RIGHT_ACTIVE,
3564 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3565 EL_CONVEYOR_BELT_2_MIDDLE_ACTIVE,
3566 EL_CONVEYOR_BELT_2_RIGHT_ACTIVE,
3567 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3568 EL_CONVEYOR_BELT_3_MIDDLE_ACTIVE,
3569 EL_CONVEYOR_BELT_3_RIGHT_ACTIVE,
3570 EL_CONVEYOR_BELT_4_LEFT_ACTIVE,
3571 EL_CONVEYOR_BELT_4_MIDDLE_ACTIVE,
3572 EL_CONVEYOR_BELT_4_RIGHT_ACTIVE,
3577 static int ep_belt_switch[] =
3579 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3580 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3581 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3582 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3583 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3584 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3585 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3586 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3587 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3588 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3589 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3590 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3595 static int ep_tube[] =
3602 EL_TUBE_HORIZONTAL_UP,
3603 EL_TUBE_HORIZONTAL_DOWN,
3605 EL_TUBE_VERTICAL_LEFT,
3606 EL_TUBE_VERTICAL_RIGHT,
3612 static int ep_acid_pool[] =
3614 EL_ACID_POOL_TOPLEFT,
3615 EL_ACID_POOL_TOPRIGHT,
3616 EL_ACID_POOL_BOTTOMLEFT,
3617 EL_ACID_POOL_BOTTOM,
3618 EL_ACID_POOL_BOTTOMRIGHT,
3623 static int ep_keygate[] =
3633 EL_GATE_1_GRAY_ACTIVE,
3634 EL_GATE_2_GRAY_ACTIVE,
3635 EL_GATE_3_GRAY_ACTIVE,
3636 EL_GATE_4_GRAY_ACTIVE,
3645 EL_EM_GATE_1_GRAY_ACTIVE,
3646 EL_EM_GATE_2_GRAY_ACTIVE,
3647 EL_EM_GATE_3_GRAY_ACTIVE,
3648 EL_EM_GATE_4_GRAY_ACTIVE,
3657 EL_EMC_GATE_5_GRAY_ACTIVE,
3658 EL_EMC_GATE_6_GRAY_ACTIVE,
3659 EL_EMC_GATE_7_GRAY_ACTIVE,
3660 EL_EMC_GATE_8_GRAY_ACTIVE,
3662 EL_DC_GATE_WHITE_GRAY,
3663 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3668 static int ep_amoeboid[] =
3680 static int ep_amoebalive[] =
3691 static int ep_has_editor_content[] =
3697 EL_SOKOBAN_FIELD_PLAYER,
3714 static int ep_can_turn_each_move[] =
3716 /* !!! do something with this one !!! */
3720 static int ep_can_grow[] =
3734 static int ep_active_bomb[] =
3737 EL_EM_DYNAMITE_ACTIVE,
3738 EL_DYNABOMB_PLAYER_1_ACTIVE,
3739 EL_DYNABOMB_PLAYER_2_ACTIVE,
3740 EL_DYNABOMB_PLAYER_3_ACTIVE,
3741 EL_DYNABOMB_PLAYER_4_ACTIVE,
3742 EL_SP_DISK_RED_ACTIVE,
3747 static int ep_inactive[] =
3757 EL_QUICKSAND_FAST_EMPTY,
3780 EL_GATE_1_GRAY_ACTIVE,
3781 EL_GATE_2_GRAY_ACTIVE,
3782 EL_GATE_3_GRAY_ACTIVE,
3783 EL_GATE_4_GRAY_ACTIVE,
3792 EL_EM_GATE_1_GRAY_ACTIVE,
3793 EL_EM_GATE_2_GRAY_ACTIVE,
3794 EL_EM_GATE_3_GRAY_ACTIVE,
3795 EL_EM_GATE_4_GRAY_ACTIVE,
3804 EL_EMC_GATE_5_GRAY_ACTIVE,
3805 EL_EMC_GATE_6_GRAY_ACTIVE,
3806 EL_EMC_GATE_7_GRAY_ACTIVE,
3807 EL_EMC_GATE_8_GRAY_ACTIVE,
3809 EL_DC_GATE_WHITE_GRAY,
3810 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3811 EL_DC_GATE_FAKE_GRAY,
3814 EL_INVISIBLE_STEELWALL,
3822 EL_WALL_EMERALD_YELLOW,
3823 EL_DYNABOMB_INCREASE_NUMBER,
3824 EL_DYNABOMB_INCREASE_SIZE,
3825 EL_DYNABOMB_INCREASE_POWER,
3829 EL_SOKOBAN_FIELD_EMPTY,
3830 EL_SOKOBAN_FIELD_FULL,
3831 EL_WALL_EMERALD_RED,
3832 EL_WALL_EMERALD_PURPLE,
3833 EL_ACID_POOL_TOPLEFT,
3834 EL_ACID_POOL_TOPRIGHT,
3835 EL_ACID_POOL_BOTTOMLEFT,
3836 EL_ACID_POOL_BOTTOM,
3837 EL_ACID_POOL_BOTTOMRIGHT,
3841 EL_BD_MAGIC_WALL_DEAD,
3843 EL_DC_MAGIC_WALL_DEAD,
3844 EL_AMOEBA_TO_DIAMOND,
3852 EL_SP_GRAVITY_PORT_RIGHT,
3853 EL_SP_GRAVITY_PORT_DOWN,
3854 EL_SP_GRAVITY_PORT_LEFT,
3855 EL_SP_GRAVITY_PORT_UP,
3856 EL_SP_PORT_HORIZONTAL,
3857 EL_SP_PORT_VERTICAL,
3868 EL_SP_HARDWARE_GRAY,
3869 EL_SP_HARDWARE_GREEN,
3870 EL_SP_HARDWARE_BLUE,
3872 EL_SP_HARDWARE_YELLOW,
3873 EL_SP_HARDWARE_BASE_1,
3874 EL_SP_HARDWARE_BASE_2,
3875 EL_SP_HARDWARE_BASE_3,
3876 EL_SP_HARDWARE_BASE_4,
3877 EL_SP_HARDWARE_BASE_5,
3878 EL_SP_HARDWARE_BASE_6,
3879 EL_SP_GRAVITY_ON_PORT_LEFT,
3880 EL_SP_GRAVITY_ON_PORT_RIGHT,
3881 EL_SP_GRAVITY_ON_PORT_UP,
3882 EL_SP_GRAVITY_ON_PORT_DOWN,
3883 EL_SP_GRAVITY_OFF_PORT_LEFT,
3884 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3885 EL_SP_GRAVITY_OFF_PORT_UP,
3886 EL_SP_GRAVITY_OFF_PORT_DOWN,
3887 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3888 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3889 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3890 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3891 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3892 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3893 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3894 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3895 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3896 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3897 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3898 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3899 EL_SIGN_EXCLAMATION,
3900 EL_SIGN_RADIOACTIVITY,
3907 EL_SIGN_ENTRY_FORBIDDEN,
3908 EL_SIGN_EMERGENCY_EXIT,
3916 EL_DC_STEELWALL_1_LEFT,
3917 EL_DC_STEELWALL_1_RIGHT,
3918 EL_DC_STEELWALL_1_TOP,
3919 EL_DC_STEELWALL_1_BOTTOM,
3920 EL_DC_STEELWALL_1_HORIZONTAL,
3921 EL_DC_STEELWALL_1_VERTICAL,
3922 EL_DC_STEELWALL_1_TOPLEFT,
3923 EL_DC_STEELWALL_1_TOPRIGHT,
3924 EL_DC_STEELWALL_1_BOTTOMLEFT,
3925 EL_DC_STEELWALL_1_BOTTOMRIGHT,
3926 EL_DC_STEELWALL_1_TOPLEFT_2,
3927 EL_DC_STEELWALL_1_TOPRIGHT_2,
3928 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
3929 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
3930 EL_DC_STEELWALL_2_LEFT,
3931 EL_DC_STEELWALL_2_RIGHT,
3932 EL_DC_STEELWALL_2_TOP,
3933 EL_DC_STEELWALL_2_BOTTOM,
3934 EL_DC_STEELWALL_2_HORIZONTAL,
3935 EL_DC_STEELWALL_2_VERTICAL,
3936 EL_DC_STEELWALL_2_MIDDLE,
3937 EL_DC_STEELWALL_2_SINGLE,
3938 EL_STEELWALL_SLIPPERY,
3943 EL_EMC_WALL_SLIPPERY_1,
3944 EL_EMC_WALL_SLIPPERY_2,
3945 EL_EMC_WALL_SLIPPERY_3,
3946 EL_EMC_WALL_SLIPPERY_4,
3967 static int ep_em_slippery_wall[] =
3972 static int ep_gfx_crumbled[] =
3983 static int ep_editor_cascade_active[] =
3985 EL_INTERNAL_CASCADE_BD_ACTIVE,
3986 EL_INTERNAL_CASCADE_EM_ACTIVE,
3987 EL_INTERNAL_CASCADE_EMC_ACTIVE,
3988 EL_INTERNAL_CASCADE_RND_ACTIVE,
3989 EL_INTERNAL_CASCADE_SB_ACTIVE,
3990 EL_INTERNAL_CASCADE_SP_ACTIVE,
3991 EL_INTERNAL_CASCADE_DC_ACTIVE,
3992 EL_INTERNAL_CASCADE_DX_ACTIVE,
3993 EL_INTERNAL_CASCADE_CHARS_ACTIVE,
3994 EL_INTERNAL_CASCADE_STEEL_CHARS_ACTIVE,
3995 EL_INTERNAL_CASCADE_CE_ACTIVE,
3996 EL_INTERNAL_CASCADE_GE_ACTIVE,
3997 EL_INTERNAL_CASCADE_REF_ACTIVE,
3998 EL_INTERNAL_CASCADE_USER_ACTIVE,
3999 EL_INTERNAL_CASCADE_DYNAMIC_ACTIVE,
4004 static int ep_editor_cascade_inactive[] =
4006 EL_INTERNAL_CASCADE_BD,
4007 EL_INTERNAL_CASCADE_EM,
4008 EL_INTERNAL_CASCADE_EMC,
4009 EL_INTERNAL_CASCADE_RND,
4010 EL_INTERNAL_CASCADE_SB,
4011 EL_INTERNAL_CASCADE_SP,
4012 EL_INTERNAL_CASCADE_DC,
4013 EL_INTERNAL_CASCADE_DX,
4014 EL_INTERNAL_CASCADE_CHARS,
4015 EL_INTERNAL_CASCADE_STEEL_CHARS,
4016 EL_INTERNAL_CASCADE_CE,
4017 EL_INTERNAL_CASCADE_GE,
4018 EL_INTERNAL_CASCADE_REF,
4019 EL_INTERNAL_CASCADE_USER,
4020 EL_INTERNAL_CASCADE_DYNAMIC,
4025 static int ep_obsolete[] =
4029 EL_EM_KEY_1_FILE_OBSOLETE,
4030 EL_EM_KEY_2_FILE_OBSOLETE,
4031 EL_EM_KEY_3_FILE_OBSOLETE,
4032 EL_EM_KEY_4_FILE_OBSOLETE,
4033 EL_ENVELOPE_OBSOLETE,
4042 } element_properties[] =
4044 { ep_diggable, EP_DIGGABLE },
4045 { ep_collectible_only, EP_COLLECTIBLE_ONLY },
4046 { ep_dont_run_into, EP_DONT_RUN_INTO },
4047 { ep_dont_collide_with, EP_DONT_COLLIDE_WITH },
4048 { ep_dont_touch, EP_DONT_TOUCH },
4049 { ep_indestructible, EP_INDESTRUCTIBLE },
4050 { ep_slippery, EP_SLIPPERY },
4051 { ep_can_change, EP_CAN_CHANGE },
4052 { ep_can_move, EP_CAN_MOVE },
4053 { ep_can_fall, EP_CAN_FALL },
4054 { ep_can_smash_player, EP_CAN_SMASH_PLAYER },
4055 { ep_can_smash_enemies, EP_CAN_SMASH_ENEMIES },
4056 { ep_can_smash_everything, EP_CAN_SMASH_EVERYTHING },
4057 { ep_explodes_by_fire, EP_EXPLODES_BY_FIRE },
4058 { ep_explodes_smashed, EP_EXPLODES_SMASHED },
4059 { ep_explodes_impact, EP_EXPLODES_IMPACT },
4060 { ep_walkable_over, EP_WALKABLE_OVER },
4061 { ep_walkable_inside, EP_WALKABLE_INSIDE },
4062 { ep_walkable_under, EP_WALKABLE_UNDER },
4063 { ep_passable_over, EP_PASSABLE_OVER },
4064 { ep_passable_inside, EP_PASSABLE_INSIDE },
4065 { ep_passable_under, EP_PASSABLE_UNDER },
4066 { ep_droppable, EP_DROPPABLE },
4067 { ep_explodes_1x1_old, EP_EXPLODES_1X1_OLD },
4068 { ep_pushable, EP_PUSHABLE },
4069 { ep_explodes_cross_old, EP_EXPLODES_CROSS_OLD },
4070 { ep_protected, EP_PROTECTED },
4071 { ep_throwable, EP_THROWABLE },
4072 { ep_can_explode, EP_CAN_EXPLODE },
4073 { ep_gravity_reachable, EP_GRAVITY_REACHABLE },
4075 { ep_player, EP_PLAYER },
4076 { ep_can_pass_magic_wall, EP_CAN_PASS_MAGIC_WALL },
4077 { ep_can_pass_dc_magic_wall, EP_CAN_PASS_DC_MAGIC_WALL },
4078 { ep_switchable, EP_SWITCHABLE },
4079 { ep_bd_element, EP_BD_ELEMENT },
4080 { ep_sp_element, EP_SP_ELEMENT },
4081 { ep_sb_element, EP_SB_ELEMENT },
4083 { ep_food_dark_yamyam, EP_FOOD_DARK_YAMYAM },
4084 { ep_food_penguin, EP_FOOD_PENGUIN },
4085 { ep_food_pig, EP_FOOD_PIG },
4086 { ep_historic_wall, EP_HISTORIC_WALL },
4087 { ep_historic_solid, EP_HISTORIC_SOLID },
4088 { ep_classic_enemy, EP_CLASSIC_ENEMY },
4089 { ep_belt, EP_BELT },
4090 { ep_belt_active, EP_BELT_ACTIVE },
4091 { ep_belt_switch, EP_BELT_SWITCH },
4092 { ep_tube, EP_TUBE },
4093 { ep_acid_pool, EP_ACID_POOL },
4094 { ep_keygate, EP_KEYGATE },
4095 { ep_amoeboid, EP_AMOEBOID },
4096 { ep_amoebalive, EP_AMOEBALIVE },
4097 { ep_has_editor_content, EP_HAS_EDITOR_CONTENT },
4098 { ep_can_turn_each_move, EP_CAN_TURN_EACH_MOVE },
4099 { ep_can_grow, EP_CAN_GROW },
4100 { ep_active_bomb, EP_ACTIVE_BOMB },
4101 { ep_inactive, EP_INACTIVE },
4103 { ep_em_slippery_wall, EP_EM_SLIPPERY_WALL },
4105 { ep_gfx_crumbled, EP_GFX_CRUMBLED },
4107 { ep_editor_cascade_active, EP_EDITOR_CASCADE_ACTIVE },
4108 { ep_editor_cascade_inactive, EP_EDITOR_CASCADE_INACTIVE },
4110 { ep_obsolete, EP_OBSOLETE },
4117 /* always start with reliable default values (element has no properties) */
4118 /* (but never initialize clipboard elements after the very first time) */
4119 /* (to be able to use clipboard elements between several levels) */
4120 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4121 if (!IS_CLIPBOARD_ELEMENT(i) || !clipboard_elements_initialized)
4122 for (j = 0; j < NUM_ELEMENT_PROPERTIES; j++)
4123 SET_PROPERTY(i, j, FALSE);
4125 /* set all base element properties from above array definitions */
4126 for (i = 0; element_properties[i].elements != NULL; i++)
4127 for (j = 0; (element_properties[i].elements)[j] != -1; j++)
4128 SET_PROPERTY((element_properties[i].elements)[j],
4129 element_properties[i].property, TRUE);
4131 /* copy properties to some elements that are only stored in level file */
4132 for (i = 0; i < NUM_ELEMENT_PROPERTIES; i++)
4133 for (j = 0; copy_properties[j][0] != -1; j++)
4134 if (HAS_PROPERTY(copy_properties[j][0], i))
4135 for (k = 1; k <= 4; k++)
4136 SET_PROPERTY(copy_properties[j][k], i, TRUE);
4138 /* set static element properties that are not listed in array definitions */
4139 for (i = EL_STEEL_CHAR_START; i <= EL_STEEL_CHAR_END; i++)
4140 SET_PROPERTY(i, EP_INDESTRUCTIBLE, TRUE);
4142 clipboard_elements_initialized = TRUE;
4145 void InitElementPropertiesEngine(int engine_version)
4147 static int no_wall_properties[] =
4150 EP_COLLECTIBLE_ONLY,
4152 EP_DONT_COLLIDE_WITH,
4155 EP_CAN_SMASH_PLAYER,
4156 EP_CAN_SMASH_ENEMIES,
4157 EP_CAN_SMASH_EVERYTHING,
4162 EP_FOOD_DARK_YAMYAM,
4178 /* important: after initialization in InitElementPropertiesStatic(), the
4179 elements are not again initialized to a default value; therefore all
4180 changes have to make sure that they leave the element with a defined
4181 property (which means that conditional property changes must be set to
4182 a reliable default value before) */
4184 /* resolve group elements */
4185 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
4186 ResolveGroupElement(EL_GROUP_START + i);
4188 /* set all special, combined or engine dependent element properties */
4189 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4191 /* do not change (already initialized) clipboard elements here */
4192 if (IS_CLIPBOARD_ELEMENT(i))
4195 /* ---------- INACTIVE ------------------------------------------------- */
4196 SET_PROPERTY(i, EP_INACTIVE, ((i >= EL_CHAR_START &&
4197 i <= EL_CHAR_END) ||
4198 (i >= EL_STEEL_CHAR_START &&
4199 i <= EL_STEEL_CHAR_END)));
4201 /* ---------- WALKABLE, PASSABLE, ACCESSIBLE --------------------------- */
4202 SET_PROPERTY(i, EP_WALKABLE, (IS_WALKABLE_OVER(i) ||
4203 IS_WALKABLE_INSIDE(i) ||
4204 IS_WALKABLE_UNDER(i)));
4206 SET_PROPERTY(i, EP_PASSABLE, (IS_PASSABLE_OVER(i) ||
4207 IS_PASSABLE_INSIDE(i) ||
4208 IS_PASSABLE_UNDER(i)));
4210 SET_PROPERTY(i, EP_ACCESSIBLE_OVER, (IS_WALKABLE_OVER(i) ||
4211 IS_PASSABLE_OVER(i)));
4213 SET_PROPERTY(i, EP_ACCESSIBLE_INSIDE, (IS_WALKABLE_INSIDE(i) ||
4214 IS_PASSABLE_INSIDE(i)));
4216 SET_PROPERTY(i, EP_ACCESSIBLE_UNDER, (IS_WALKABLE_UNDER(i) ||
4217 IS_PASSABLE_UNDER(i)));
4219 SET_PROPERTY(i, EP_ACCESSIBLE, (IS_WALKABLE(i) ||
4222 /* ---------- COLLECTIBLE ---------------------------------------------- */
4223 SET_PROPERTY(i, EP_COLLECTIBLE, (IS_COLLECTIBLE_ONLY(i) ||
4227 /* ---------- SNAPPABLE ------------------------------------------------ */
4228 SET_PROPERTY(i, EP_SNAPPABLE, (IS_DIGGABLE(i) ||
4229 IS_COLLECTIBLE(i) ||
4233 /* ---------- WALL ----------------------------------------------------- */
4234 SET_PROPERTY(i, EP_WALL, TRUE); /* default: element is wall */
4236 for (j = 0; no_wall_properties[j] != -1; j++)
4237 if (HAS_PROPERTY(i, no_wall_properties[j]) ||
4238 i >= EL_FIRST_RUNTIME_UNREAL)
4239 SET_PROPERTY(i, EP_WALL, FALSE);
4241 if (IS_HISTORIC_WALL(i))
4242 SET_PROPERTY(i, EP_WALL, TRUE);
4244 /* ---------- SOLID_FOR_PUSHING ---------------------------------------- */
4245 if (engine_version < VERSION_IDENT(2,2,0,0))
4246 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, IS_HISTORIC_SOLID(i));
4248 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, (!IS_WALKABLE(i) &&
4250 !IS_COLLECTIBLE(i)));
4252 /* ---------- DRAGONFIRE_PROOF ----------------------------------------- */
4253 if (IS_HISTORIC_SOLID(i) || i == EL_EXPLOSION)
4254 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, TRUE);
4256 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, (IS_CUSTOM_ELEMENT(i) &&
4257 IS_INDESTRUCTIBLE(i)));
4259 /* ---------- EXPLOSION_PROOF ------------------------------------------ */
4261 SET_PROPERTY(i, EP_EXPLOSION_PROOF, TRUE);
4262 else if (engine_version < VERSION_IDENT(2,2,0,0))
4263 SET_PROPERTY(i, EP_EXPLOSION_PROOF, IS_INDESTRUCTIBLE(i));
4265 SET_PROPERTY(i, EP_EXPLOSION_PROOF, (IS_INDESTRUCTIBLE(i) &&
4269 if (IS_CUSTOM_ELEMENT(i))
4271 /* these are additional properties which are initially false when set */
4273 /* ---------- DONT_COLLIDE_WITH / DONT_RUN_INTO ---------------------- */
4275 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH, TRUE);
4276 if (DONT_COLLIDE_WITH(i))
4277 SET_PROPERTY(i, EP_DONT_RUN_INTO, TRUE);
4279 /* ---------- CAN_SMASH_ENEMIES / CAN_SMASH_PLAYER ------------------- */
4280 if (CAN_SMASH_EVERYTHING(i))
4281 SET_PROPERTY(i, EP_CAN_SMASH_ENEMIES, TRUE);
4282 if (CAN_SMASH_ENEMIES(i))
4283 SET_PROPERTY(i, EP_CAN_SMASH_PLAYER, TRUE);
4286 /* ---------- CAN_SMASH ------------------------------------------------ */
4287 SET_PROPERTY(i, EP_CAN_SMASH, (CAN_SMASH_PLAYER(i) ||
4288 CAN_SMASH_ENEMIES(i) ||
4289 CAN_SMASH_EVERYTHING(i)));
4291 /* ---------- CAN_EXPLODE_BY_FIRE -------------------------------------- */
4292 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_FIRE, (CAN_EXPLODE(i) &&
4293 EXPLODES_BY_FIRE(i)));
4295 /* ---------- CAN_EXPLODE_SMASHED -------------------------------------- */
4296 SET_PROPERTY(i, EP_CAN_EXPLODE_SMASHED, (CAN_EXPLODE(i) &&
4297 EXPLODES_SMASHED(i)));
4299 /* ---------- CAN_EXPLODE_IMPACT --------------------------------------- */
4300 SET_PROPERTY(i, EP_CAN_EXPLODE_IMPACT, (CAN_EXPLODE(i) &&
4301 EXPLODES_IMPACT(i)));
4303 /* ---------- CAN_EXPLODE_BY_DRAGONFIRE -------------------------------- */
4304 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_DRAGONFIRE, CAN_EXPLODE_BY_FIRE(i));
4306 /* ---------- CAN_EXPLODE_BY_EXPLOSION --------------------------------- */
4307 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_EXPLOSION, (CAN_EXPLODE_BY_FIRE(i) ||
4308 i == EL_BLACK_ORB));
4310 /* ---------- COULD_MOVE_INTO_ACID ------------------------------------- */
4311 SET_PROPERTY(i, EP_COULD_MOVE_INTO_ACID, (ELEM_IS_PLAYER(i) ||
4313 IS_CUSTOM_ELEMENT(i)));
4315 /* ---------- MAYBE_DONT_COLLIDE_WITH ---------------------------------- */
4316 SET_PROPERTY(i, EP_MAYBE_DONT_COLLIDE_WITH, (i == EL_SP_SNIKSNAK ||
4317 i == EL_SP_ELECTRON));
4319 /* ---------- CAN_MOVE_INTO_ACID --------------------------------------- */
4320 if (COULD_MOVE_INTO_ACID(i) && !IS_CUSTOM_ELEMENT(i))
4321 SET_PROPERTY(i, EP_CAN_MOVE_INTO_ACID,
4322 getMoveIntoAcidProperty(&level, i));
4324 /* ---------- DONT_COLLIDE_WITH ---------------------------------------- */
4325 if (MAYBE_DONT_COLLIDE_WITH(i))
4326 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH,
4327 getDontCollideWithProperty(&level, i));
4329 /* ---------- SP_PORT -------------------------------------------------- */
4330 SET_PROPERTY(i, EP_SP_PORT, (IS_SP_ELEMENT(i) &&
4331 IS_PASSABLE_INSIDE(i)));
4333 /* ---------- CAN_BE_CLONED_BY_ANDROID --------------------------------- */
4334 for (j = 0; j < level.num_android_clone_elements; j++)
4335 SET_PROPERTY(i, EP_CAN_BE_CLONED_BY_ANDROID,
4337 IS_EQUAL_OR_IN_GROUP(i, level.android_clone_element[j])));
4339 /* ---------- CAN_CHANGE ----------------------------------------------- */
4340 SET_PROPERTY(i, EP_CAN_CHANGE, FALSE); /* default: cannot change */
4341 for (j = 0; j < element_info[i].num_change_pages; j++)
4342 if (element_info[i].change_page[j].can_change)
4343 SET_PROPERTY(i, EP_CAN_CHANGE, TRUE);
4345 /* ---------- HAS_ACTION ----------------------------------------------- */
4346 SET_PROPERTY(i, EP_HAS_ACTION, FALSE); /* default: has no action */
4347 for (j = 0; j < element_info[i].num_change_pages; j++)
4348 if (element_info[i].change_page[j].has_action)
4349 SET_PROPERTY(i, EP_HAS_ACTION, TRUE);
4351 /* ---------- CAN_CHANGE_OR_HAS_ACTION --------------------------------- */
4352 SET_PROPERTY(i, EP_CAN_CHANGE_OR_HAS_ACTION, (CAN_CHANGE(i) ||
4355 /* ---------- GFX_CRUMBLED --------------------------------------------- */
4356 SET_PROPERTY(i, EP_GFX_CRUMBLED,
4357 element_info[i].crumbled[ACTION_DEFAULT] !=
4358 element_info[i].graphic[ACTION_DEFAULT]);
4360 /* ---------- EDITOR_CASCADE ------------------------------------------- */
4361 SET_PROPERTY(i, EP_EDITOR_CASCADE, (IS_EDITOR_CASCADE_ACTIVE(i) ||
4362 IS_EDITOR_CASCADE_INACTIVE(i)));
4365 /* dynamically adjust element properties according to game engine version */
4367 static int ep_em_slippery_wall[] =
4372 EL_EXPANDABLE_WALL_HORIZONTAL,
4373 EL_EXPANDABLE_WALL_VERTICAL,
4374 EL_EXPANDABLE_WALL_ANY,
4375 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
4376 EL_EXPANDABLE_STEELWALL_VERTICAL,
4377 EL_EXPANDABLE_STEELWALL_ANY,
4378 EL_EXPANDABLE_STEELWALL_GROWING,
4382 static int ep_em_explodes_by_fire[] =
4385 EL_EM_DYNAMITE_ACTIVE,
4390 /* special EM style gems behaviour */
4391 for (i = 0; ep_em_slippery_wall[i] != -1; i++)
4392 SET_PROPERTY(ep_em_slippery_wall[i], EP_EM_SLIPPERY_WALL,
4393 level.em_slippery_gems);
4395 /* "EL_EXPANDABLE_WALL_GROWING" wasn't slippery for EM gems in 2.0.1 */
4396 SET_PROPERTY(EL_EXPANDABLE_WALL_GROWING, EP_EM_SLIPPERY_WALL,
4397 (level.em_slippery_gems &&
4398 engine_version > VERSION_IDENT(2,0,1,0)));
4400 /* special EM style explosion behaviour regarding chain reactions */
4401 for (i = 0; ep_em_explodes_by_fire[i] != -1; i++)
4402 SET_PROPERTY(ep_em_explodes_by_fire[i], EP_EXPLODES_BY_FIRE,
4403 level.em_explodes_by_fire);
4406 /* this is needed because some graphics depend on element properties */
4407 if (game_status == GAME_MODE_PLAYING)
4408 InitElementGraphicInfo();
4411 void InitElementPropertiesAfterLoading(int engine_version)
4415 /* set some other uninitialized values of custom elements in older levels */
4416 if (engine_version < VERSION_IDENT(3,1,0,0))
4418 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4420 int element = EL_CUSTOM_START + i;
4422 element_info[element].access_direction = MV_ALL_DIRECTIONS;
4424 element_info[element].explosion_delay = 17;
4425 element_info[element].ignition_delay = 8;
4430 void InitElementPropertiesGfxElement()
4434 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4436 struct ElementInfo *ei = &element_info[i];
4438 ei->gfx_element = (ei->use_gfx_element ? ei->gfx_element_initial : i);
4442 static void InitGlobal()
4447 for (i = 0; i < MAX_NUM_ELEMENTS + 1; i++)
4449 /* check if element_name_info entry defined for each element in "main.h" */
4450 if (i < MAX_NUM_ELEMENTS && element_name_info[i].token_name == NULL)
4451 Error(ERR_EXIT, "undefined 'element_name_info' entry for element %d", i);
4453 element_info[i].token_name = element_name_info[i].token_name;
4454 element_info[i].class_name = element_name_info[i].class_name;
4455 element_info[i].editor_description= element_name_info[i].editor_description;
4458 /* create hash from image config list */
4459 image_config_hash = newSetupFileHash();
4460 for (i = 0; image_config[i].token != NULL; i++)
4461 setHashEntry(image_config_hash,
4462 image_config[i].token,
4463 image_config[i].value);
4465 /* create hash from element token list */
4466 element_token_hash = newSetupFileHash();
4467 for (i = 0; element_name_info[i].token_name != NULL; i++)
4468 setHashEntry(element_token_hash,
4469 element_name_info[i].token_name,
4472 /* create hash from graphic token list */
4473 graphic_token_hash = newSetupFileHash();
4474 for (graphic = 0, i = 0; image_config[i].token != NULL; i++)
4475 if (strSuffix(image_config[i].value, ".png") ||
4476 strSuffix(image_config[i].value, ".pcx") ||
4477 strSuffix(image_config[i].value, ".wav") ||
4478 strEqual(image_config[i].value, UNDEFINED_FILENAME))
4479 setHashEntry(graphic_token_hash,
4480 image_config[i].token,
4481 int2str(graphic++, 0));
4483 /* create hash from font token list */
4484 font_token_hash = newSetupFileHash();
4485 for (i = 0; font_info[i].token_name != NULL; i++)
4486 setHashEntry(font_token_hash,
4487 font_info[i].token_name,
4490 /* set default filenames for all cloned graphics in static configuration */
4491 for (i = 0; image_config[i].token != NULL; i++)
4493 if (strEqual(image_config[i].value, UNDEFINED_FILENAME))
4495 char *token = image_config[i].token;
4496 char *token_clone_from = getStringCat2(token, ".clone_from");
4497 char *token_cloned = getHashEntry(image_config_hash, token_clone_from);
4499 if (token_cloned != NULL)
4501 char *value_cloned = getHashEntry(image_config_hash, token_cloned);
4503 if (value_cloned != NULL)
4505 /* set default filename in static configuration */
4506 image_config[i].value = value_cloned;
4508 /* set default filename in image config hash */
4509 setHashEntry(image_config_hash, token, value_cloned);
4513 free(token_clone_from);
4517 /* always start with reliable default values (all elements) */
4518 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4519 ActiveElement[i] = i;
4521 /* now add all entries that have an active state (active elements) */
4522 for (i = 0; element_with_active_state[i].element != -1; i++)
4524 int element = element_with_active_state[i].element;
4525 int element_active = element_with_active_state[i].element_active;
4527 ActiveElement[element] = element_active;
4530 /* always start with reliable default values (all buttons) */
4531 for (i = 0; i < NUM_IMAGE_FILES; i++)
4532 ActiveButton[i] = i;
4534 /* now add all entries that have an active state (active buttons) */
4535 for (i = 0; button_with_active_state[i].button != -1; i++)
4537 int button = button_with_active_state[i].button;
4538 int button_active = button_with_active_state[i].button_active;
4540 ActiveButton[button] = button_active;
4543 /* always start with reliable default values (all fonts) */
4544 for (i = 0; i < NUM_FONTS; i++)
4547 /* now add all entries that have an active state (active fonts) */
4548 for (i = 0; font_with_active_state[i].font_nr != -1; i++)
4550 int font = font_with_active_state[i].font_nr;
4551 int font_active = font_with_active_state[i].font_nr_active;
4553 ActiveFont[font] = font_active;
4556 global.autoplay_leveldir = NULL;
4557 global.convert_leveldir = NULL;
4558 global.create_images_dir = NULL;
4560 global.frames_per_second = 0;
4562 global.border_status = GAME_MODE_MAIN;
4564 global.use_envelope_request = FALSE;
4567 void Execute_Command(char *command)
4571 if (strEqual(command, "print graphicsinfo.conf"))
4573 Print("# You can configure additional/alternative image files here.\n");
4574 Print("# (The entries below are default and therefore commented out.)\n");
4576 Print("%s\n", getFormattedSetupEntry("name", "Classic Graphics"));
4578 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
4581 for (i = 0; image_config[i].token != NULL; i++)
4582 Print("# %s\n", getFormattedSetupEntry(image_config[i].token,
4583 image_config[i].value));
4587 else if (strEqual(command, "print soundsinfo.conf"))
4589 Print("# You can configure additional/alternative sound files here.\n");
4590 Print("# (The entries below are default and therefore commented out.)\n");
4592 Print("%s\n", getFormattedSetupEntry("name", "Classic Sounds"));
4594 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
4597 for (i = 0; sound_config[i].token != NULL; i++)
4598 Print("# %s\n", getFormattedSetupEntry(sound_config[i].token,
4599 sound_config[i].value));
4603 else if (strEqual(command, "print musicinfo.conf"))
4605 Print("# You can configure additional/alternative music files here.\n");
4606 Print("# (The entries below are default and therefore commented out.)\n");
4608 Print("%s\n", getFormattedSetupEntry("name", "Classic Music"));
4610 Print("%s\n", getFormattedSetupEntry("sort_priority", "100"));
4613 for (i = 0; music_config[i].token != NULL; i++)
4614 Print("# %s\n", getFormattedSetupEntry(music_config[i].token,
4615 music_config[i].value));
4619 else if (strEqual(command, "print editorsetup.conf"))
4621 Print("# You can configure your personal editor element list here.\n");
4622 Print("# (The entries below are default and therefore commented out.)\n");
4625 /* this is needed to be able to check element list for cascade elements */
4626 InitElementPropertiesStatic();
4627 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
4629 PrintEditorElementList();
4633 else if (strEqual(command, "print helpanim.conf"))
4635 Print("# You can configure different element help animations here.\n");
4636 Print("# (The entries below are default and therefore commented out.)\n");
4639 for (i = 0; helpanim_config[i].token != NULL; i++)
4641 Print("# %s\n", getFormattedSetupEntry(helpanim_config[i].token,
4642 helpanim_config[i].value));
4644 if (strEqual(helpanim_config[i].token, "end"))
4650 else if (strEqual(command, "print helptext.conf"))
4652 Print("# You can configure different element help text here.\n");
4653 Print("# (The entries below are default and therefore commented out.)\n");
4656 for (i = 0; helptext_config[i].token != NULL; i++)
4657 Print("# %s\n", getFormattedSetupEntry(helptext_config[i].token,
4658 helptext_config[i].value));
4662 else if (strPrefix(command, "dump level "))
4664 char *filename = &command[11];
4666 if (!fileExists(filename))
4667 Error(ERR_EXIT, "cannot open file '%s'", filename);
4669 LoadLevelFromFilename(&level, filename);
4674 else if (strPrefix(command, "dump tape "))
4676 char *filename = &command[10];
4678 if (!fileExists(filename))
4679 Error(ERR_EXIT, "cannot open file '%s'", filename);
4681 LoadTapeFromFilename(filename);
4686 else if (strPrefix(command, "autotest ") ||
4687 strPrefix(command, "autoplay ") ||
4688 strPrefix(command, "autoffwd "))
4690 char *str_ptr = getStringCopy(&command[9]); /* read command parameters */
4692 global.autoplay_mode = (strPrefix(command, "autotest") ? AUTOPLAY_TEST :
4693 strPrefix(command, "autoplay") ? AUTOPLAY_PLAY :
4694 strPrefix(command, "autoffwd") ? AUTOPLAY_FFWD : 0);
4696 while (*str_ptr != '\0') /* continue parsing string */
4698 /* cut leading whitespace from string, replace it by string terminator */
4699 while (*str_ptr == ' ' || *str_ptr == '\t')
4702 if (*str_ptr == '\0') /* end of string reached */
4705 if (global.autoplay_leveldir == NULL) /* read level set string */
4707 global.autoplay_leveldir = str_ptr;
4708 global.autoplay_all = TRUE; /* default: play all tapes */
4710 for (i = 0; i < MAX_TAPES_PER_SET; i++)
4711 global.autoplay_level[i] = FALSE;
4713 else /* read level number string */
4715 int level_nr = atoi(str_ptr); /* get level_nr value */
4717 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
4718 global.autoplay_level[level_nr] = TRUE;
4720 global.autoplay_all = FALSE;
4723 /* advance string pointer to the next whitespace (or end of string) */
4724 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
4728 else if (strPrefix(command, "convert "))
4730 char *str_copy = getStringCopy(strchr(command, ' ') + 1);
4731 char *str_ptr = strchr(str_copy, ' ');
4733 global.convert_leveldir = str_copy;
4734 global.convert_level_nr = -1;
4736 if (str_ptr != NULL) /* level number follows */
4738 *str_ptr++ = '\0'; /* terminate leveldir string */
4739 global.convert_level_nr = atoi(str_ptr); /* get level_nr value */
4742 else if (strPrefix(command, "create images "))
4744 global.create_images_dir = getStringCopy(&command[14]);
4746 if (access(global.create_images_dir, W_OK) != 0)
4747 Error(ERR_EXIT, "image target directory '%s' not found or not writable",
4748 global.create_images_dir);
4750 else if (strPrefix(command, "create CE image "))
4752 CreateCustomElementImages(&command[16]);
4758 #if defined(TARGET_SDL2)
4759 else if (strEqual(command, "SDL_ListModes"))
4761 SDL_Init(SDL_INIT_VIDEO);
4763 int num_displays = SDL_GetNumVideoDisplays();
4765 // check if there are any displays available
4766 if (num_displays < 0)
4768 Print("No displays available: %s\n", SDL_GetError());
4773 for (i = 0; i < num_displays; i++)
4775 int num_modes = SDL_GetNumDisplayModes(i);
4778 Print("Available display modes for display %d:\n", i);
4780 // check if there are any display modes available for this display
4783 Print("No display modes available for display %d: %s\n",
4789 for (j = 0; j < num_modes; j++)
4791 SDL_DisplayMode mode;
4793 if (SDL_GetDisplayMode(i, j, &mode) < 0)
4795 Print("Cannot get display mode %d for display %d: %s\n",
4796 j, i, SDL_GetError());
4801 Print("- %d x %d\n", mode.w, mode.h);
4807 #elif defined(TARGET_SDL)
4808 else if (strEqual(command, "SDL_ListModes"))
4813 SDL_Init(SDL_INIT_VIDEO);
4815 /* get available fullscreen/hardware modes */
4816 modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE);
4818 /* check if there are any modes available */
4821 Print("No modes available!\n");
4826 /* check if our resolution is restricted */
4827 if (modes == (SDL_Rect **)-1)
4829 Print("All resolutions available.\n");
4833 Print("Available display modes:\n");
4835 for (i = 0; modes[i]; i++)
4836 Print("- %d x %d\n", modes[i]->w, modes[i]->h);
4846 Error(ERR_EXIT_HELP, "unrecognized command '%s'", command);
4850 static void InitSetup()
4852 LoadSetup(); /* global setup info */
4854 /* set some options from setup file */
4856 if (setup.options.verbose)
4857 options.verbose = TRUE;
4860 static void InitGameInfo()
4862 game.restart_level = FALSE;
4865 static void InitPlayerInfo()
4869 /* choose default local player */
4870 local_player = &stored_player[0];
4872 for (i = 0; i < MAX_PLAYERS; i++)
4873 stored_player[i].connected = FALSE;
4875 local_player->connected = TRUE;
4878 static void InitArtworkInfo()
4883 static char *get_string_in_brackets(char *string)
4885 char *string_in_brackets = checked_malloc(strlen(string) + 3);
4887 sprintf(string_in_brackets, "[%s]", string);
4889 return string_in_brackets;
4892 static char *get_level_id_suffix(int id_nr)
4894 char *id_suffix = checked_malloc(1 + 3 + 1);
4896 if (id_nr < 0 || id_nr > 999)
4899 sprintf(id_suffix, ".%03d", id_nr);
4904 static void InitArtworkConfig()
4906 static char *image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + 1];
4907 static char *sound_id_prefix[2 * MAX_NUM_ELEMENTS + 1];
4908 static char *music_id_prefix[NUM_MUSIC_PREFIXES + 1];
4909 static char *action_id_suffix[NUM_ACTIONS + 1];
4910 static char *direction_id_suffix[NUM_DIRECTIONS_FULL + 1];
4911 static char *special_id_suffix[NUM_SPECIAL_GFX_ARGS + 1];
4912 static char *level_id_suffix[MAX_LEVELS + 1];
4913 static char *dummy[1] = { NULL };
4914 static char *ignore_generic_tokens[] =
4920 static char **ignore_image_tokens;
4921 static char **ignore_sound_tokens;
4922 static char **ignore_music_tokens;
4923 int num_ignore_generic_tokens;
4924 int num_ignore_image_tokens;
4925 int num_ignore_sound_tokens;
4926 int num_ignore_music_tokens;
4929 /* dynamically determine list of generic tokens to be ignored */
4930 num_ignore_generic_tokens = 0;
4931 for (i = 0; ignore_generic_tokens[i] != NULL; i++)
4932 num_ignore_generic_tokens++;
4934 /* dynamically determine list of image tokens to be ignored */
4935 num_ignore_image_tokens = num_ignore_generic_tokens;
4936 for (i = 0; image_config_vars[i].token != NULL; i++)
4937 num_ignore_image_tokens++;
4938 ignore_image_tokens =
4939 checked_malloc((num_ignore_image_tokens + 1) * sizeof(char *));
4940 for (i = 0; i < num_ignore_generic_tokens; i++)
4941 ignore_image_tokens[i] = ignore_generic_tokens[i];
4942 for (i = 0; i < num_ignore_image_tokens - num_ignore_generic_tokens; i++)
4943 ignore_image_tokens[num_ignore_generic_tokens + i] =
4944 image_config_vars[i].token;
4945 ignore_image_tokens[num_ignore_image_tokens] = NULL;
4947 /* dynamically determine list of sound tokens to be ignored */
4948 num_ignore_sound_tokens = num_ignore_generic_tokens;
4949 ignore_sound_tokens =
4950 checked_malloc((num_ignore_sound_tokens + 1) * sizeof(char *));
4951 for (i = 0; i < num_ignore_generic_tokens; i++)
4952 ignore_sound_tokens[i] = ignore_generic_tokens[i];
4953 ignore_sound_tokens[num_ignore_sound_tokens] = NULL;
4955 /* dynamically determine list of music tokens to be ignored */
4956 num_ignore_music_tokens = num_ignore_generic_tokens;
4957 ignore_music_tokens =
4958 checked_malloc((num_ignore_music_tokens + 1) * sizeof(char *));
4959 for (i = 0; i < num_ignore_generic_tokens; i++)
4960 ignore_music_tokens[i] = ignore_generic_tokens[i];
4961 ignore_music_tokens[num_ignore_music_tokens] = NULL;
4963 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4964 image_id_prefix[i] = element_info[i].token_name;
4965 for (i = 0; i < NUM_FONTS; i++)
4966 image_id_prefix[MAX_NUM_ELEMENTS + i] = font_info[i].token_name;
4967 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS] = NULL;
4969 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4970 sound_id_prefix[i] = element_info[i].token_name;
4971 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4972 sound_id_prefix[MAX_NUM_ELEMENTS + i] =
4973 get_string_in_brackets(element_info[i].class_name);
4974 sound_id_prefix[2 * MAX_NUM_ELEMENTS] = NULL;
4976 for (i = 0; i < NUM_MUSIC_PREFIXES; i++)
4977 music_id_prefix[i] = music_prefix_info[i].prefix;
4978 music_id_prefix[NUM_MUSIC_PREFIXES] = NULL;
4980 for (i = 0; i < NUM_ACTIONS; i++)
4981 action_id_suffix[i] = element_action_info[i].suffix;
4982 action_id_suffix[NUM_ACTIONS] = NULL;
4984 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
4985 direction_id_suffix[i] = element_direction_info[i].suffix;
4986 direction_id_suffix[NUM_DIRECTIONS_FULL] = NULL;
4988 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
4989 special_id_suffix[i] = special_suffix_info[i].suffix;
4990 special_id_suffix[NUM_SPECIAL_GFX_ARGS] = NULL;
4992 for (i = 0; i < MAX_LEVELS; i++)
4993 level_id_suffix[i] = get_level_id_suffix(i);
4994 level_id_suffix[MAX_LEVELS] = NULL;
4996 InitImageList(image_config, NUM_IMAGE_FILES, image_config_suffix,
4997 image_id_prefix, action_id_suffix, direction_id_suffix,
4998 special_id_suffix, ignore_image_tokens);
4999 InitSoundList(sound_config, NUM_SOUND_FILES, sound_config_suffix,
5000 sound_id_prefix, action_id_suffix, dummy,
5001 special_id_suffix, ignore_sound_tokens);
5002 InitMusicList(music_config, NUM_MUSIC_FILES, music_config_suffix,
5003 music_id_prefix, special_id_suffix, level_id_suffix,
5004 dummy, ignore_music_tokens);
5007 static void InitMixer()
5014 void InitGfxBuffers()
5016 static int win_xsize_last = -1;
5017 static int win_ysize_last = -1;
5019 /* create additional image buffers for double-buffering and cross-fading */
5021 if (WIN_XSIZE != win_xsize_last || WIN_YSIZE != win_ysize_last)
5023 /* may contain content for cross-fading -- only re-create if changed */
5024 ReCreateBitmap(&bitmap_db_store, WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
5025 ReCreateBitmap(&bitmap_db_cross, WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
5027 win_xsize_last = WIN_XSIZE;
5028 win_ysize_last = WIN_YSIZE;
5031 ReCreateBitmap(&bitmap_db_field, FXSIZE, FYSIZE, DEFAULT_DEPTH);
5032 ReCreateBitmap(&bitmap_db_panel, DXSIZE, DYSIZE, DEFAULT_DEPTH);
5033 ReCreateBitmap(&bitmap_db_door_1, 3 * DXSIZE, DYSIZE, DEFAULT_DEPTH);
5034 ReCreateBitmap(&bitmap_db_door_2, 3 * VXSIZE, VYSIZE, DEFAULT_DEPTH);
5035 ReCreateBitmap(&bitmap_db_toons, FULL_SXSIZE, FULL_SYSIZE, DEFAULT_DEPTH);
5037 /* initialize screen properties */
5038 InitGfxFieldInfo(SX, SY, SXSIZE, SYSIZE,
5039 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
5041 InitGfxDoor1Info(DX, DY, DXSIZE, DYSIZE);
5042 InitGfxDoor2Info(VX, VY, VXSIZE, VYSIZE);
5043 InitGfxDoor3Info(EX, EY, EXSIZE, EYSIZE);
5044 InitGfxWindowInfo(WIN_XSIZE, WIN_YSIZE);
5045 InitGfxScrollbufferInfo(FXSIZE, FYSIZE);
5046 InitGfxClipRegion(FALSE, -1, -1, -1, -1);
5048 /* required if door size definitions have changed */
5049 InitGraphicCompatibilityInfo_Doors();
5051 InitGfxBuffers_EM();
5052 InitGfxBuffers_SP();
5057 struct GraphicInfo *graphic_info_last = graphic_info;
5058 char *filename_font_initial = NULL;
5059 char *filename_anim_initial = NULL;
5060 Bitmap *bitmap_font_initial = NULL;
5064 /* determine settings for initial font (for displaying startup messages) */
5065 for (i = 0; image_config[i].token != NULL; i++)
5067 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5069 char font_token[128];
5072 sprintf(font_token, "%s_%d", CONFIG_TOKEN_FONT_INITIAL, j + 1);
5073 len_font_token = strlen(font_token);
5075 if (strEqual(image_config[i].token, font_token))
5076 filename_font_initial = image_config[i].value;
5077 else if (strlen(image_config[i].token) > len_font_token &&
5078 strncmp(image_config[i].token, font_token, len_font_token) == 0)
5080 if (strEqual(&image_config[i].token[len_font_token], ".x"))
5081 font_initial[j].src_x = atoi(image_config[i].value);
5082 else if (strEqual(&image_config[i].token[len_font_token], ".y"))
5083 font_initial[j].src_y = atoi(image_config[i].value);
5084 else if (strEqual(&image_config[i].token[len_font_token], ".width"))
5085 font_initial[j].width = atoi(image_config[i].value);
5086 else if (strEqual(&image_config[i].token[len_font_token], ".height"))
5087 font_initial[j].height = atoi(image_config[i].value);
5092 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5094 font_initial[j].num_chars = DEFAULT_NUM_CHARS_PER_FONT;
5095 font_initial[j].num_chars_per_line = DEFAULT_NUM_CHARS_PER_LINE;
5098 if (filename_font_initial == NULL) /* should not happen */
5099 Error(ERR_EXIT, "cannot get filename for '%s'", CONFIG_TOKEN_FONT_INITIAL);
5102 InitGfxCustomArtworkInfo();
5103 InitGfxOtherSettings();
5105 bitmap_font_initial = LoadCustomImage(filename_font_initial);
5107 for (j = 0; j < NUM_INITIAL_FONTS; j++)
5108 font_initial[j].bitmap = bitmap_font_initial;
5110 InitFontGraphicInfo();
5112 font_height = getFontHeight(FC_RED);
5114 DrawInitText(getProgramInitString(), 20, FC_YELLOW);
5115 DrawInitText(setup.internal.program_copyright, 50, FC_RED);
5116 DrawInitText(setup.internal.program_website, WIN_YSIZE - 20 - font_height,
5119 DrawInitText("Loading graphics", 120, FC_GREEN);
5121 /* initialize settings for busy animation with default values */
5122 int parameter[NUM_GFX_ARGS];
5123 for (i = 0; i < NUM_GFX_ARGS; i++)
5124 parameter[i] = get_graphic_parameter_value(image_config_suffix[i].value,
5125 image_config_suffix[i].token,
5126 image_config_suffix[i].type);
5128 char *anim_token = CONFIG_TOKEN_GLOBAL_BUSY;
5129 int len_anim_token = strlen(anim_token);
5131 /* read settings for busy animation from default custom artwork config */
5132 char *gfx_config_filename = getPath3(options.graphics_directory,
5134 GRAPHICSINFO_FILENAME);
5136 if (fileExists(gfx_config_filename))
5138 SetupFileHash *setup_file_hash = loadSetupFileHash(gfx_config_filename);
5140 if (setup_file_hash)
5142 char *filename = getHashEntry(setup_file_hash, anim_token);
5146 filename_anim_initial = getStringCopy(filename);
5148 for (j = 0; image_config_suffix[j].token != NULL; j++)
5150 int type = image_config_suffix[j].type;
5151 char *suffix = image_config_suffix[j].token;
5152 char *token = getStringCat2(anim_token, suffix);
5153 char *value = getHashEntry(setup_file_hash, token);
5155 checked_free(token);
5158 parameter[j] = get_graphic_parameter_value(value, suffix, type);
5162 freeSetupFileHash(setup_file_hash);
5166 if (filename_anim_initial == NULL)
5168 /* read settings for busy animation from static default artwork config */
5169 for (i = 0; image_config[i].token != NULL; i++)
5171 if (strEqual(image_config[i].token, anim_token))
5172 filename_anim_initial = getStringCopy(image_config[i].value);
5173 else if (strlen(image_config[i].token) > len_anim_token &&
5174 strncmp(image_config[i].token, anim_token, len_anim_token) == 0)
5176 for (j = 0; image_config_suffix[j].token != NULL; j++)
5178 if (strEqual(&image_config[i].token[len_anim_token],
5179 image_config_suffix[j].token))
5181 get_graphic_parameter_value(image_config[i].value,
5182 image_config_suffix[j].token,
5183 image_config_suffix[j].type);
5189 if (filename_anim_initial == NULL) /* should not happen */
5190 Error(ERR_EXIT, "cannot get filename for '%s'", CONFIG_TOKEN_GLOBAL_BUSY);
5192 anim_initial.bitmaps =
5193 checked_calloc(sizeof(Bitmap *) * NUM_IMG_BITMAP_POINTERS);
5195 anim_initial.bitmaps[IMG_BITMAP_STANDARD] =
5196 LoadCustomImage(filename_anim_initial);
5198 checked_free(filename_anim_initial);
5200 graphic_info = &anim_initial; /* graphic == 0 => anim_initial */
5202 set_graphic_parameters_ext(0, parameter, anim_initial.bitmaps);
5204 graphic_info = graphic_info_last;
5206 init.busy.width = anim_initial.width;
5207 init.busy.height = anim_initial.height;
5209 InitMenuDesignSettings_Static();
5210 InitGfxDrawBusyAnimFunction(DrawInitAnim);
5212 /* use copy of busy animation to prevent change while reloading artwork */
5216 void InitGfxBackground()
5218 fieldbuffer = bitmap_db_field;
5219 SetDrawtoField(DRAW_BACKBUFFER);
5221 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
5223 redraw_mask = REDRAW_ALL;
5226 static void InitLevelInfo()
5228 LoadLevelInfo(); /* global level info */
5229 LoadLevelSetup_LastSeries(); /* last played series info */
5230 LoadLevelSetup_SeriesInfo(); /* last played level info */
5232 if (global.autoplay_leveldir &&
5233 global.autoplay_mode != AUTOPLAY_TEST)
5235 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
5236 global.autoplay_leveldir);
5237 if (leveldir_current == NULL)
5238 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
5242 static void InitLevelArtworkInfo()
5244 LoadLevelArtworkInfo();
5247 static void InitImages()
5249 print_timestamp_init("InitImages");
5252 printf("::: leveldir_current->identifier == '%s'\n",
5253 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5254 printf("::: leveldir_current->graphics_path == '%s'\n",
5255 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5256 printf("::: leveldir_current->graphics_set == '%s'\n",
5257 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5258 printf("::: getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'\n",
5259 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5262 setLevelArtworkDir(artwork.gfx_first);
5265 printf("::: leveldir_current->identifier == '%s'\n",
5266 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5267 printf("::: leveldir_current->graphics_path == '%s'\n",
5268 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5269 printf("::: leveldir_current->graphics_set == '%s'\n",
5270 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5271 printf("::: getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'\n",
5272 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5276 printf("::: InitImages for '%s' ['%s', '%s'] ['%s', '%s']\n",
5277 leveldir_current->identifier,
5278 artwork.gfx_current_identifier,
5279 artwork.gfx_current->identifier,
5280 leveldir_current->graphics_set,
5281 leveldir_current->graphics_path);
5284 UPDATE_BUSY_STATE();
5286 ReloadCustomImages();
5287 print_timestamp_time("ReloadCustomImages");
5289 UPDATE_BUSY_STATE();
5291 LoadCustomElementDescriptions();
5292 print_timestamp_time("LoadCustomElementDescriptions");
5294 UPDATE_BUSY_STATE();
5296 LoadMenuDesignSettings();
5297 print_timestamp_time("LoadMenuDesignSettings");
5299 UPDATE_BUSY_STATE();
5301 ReinitializeGraphics();
5302 print_timestamp_time("ReinitializeGraphics");
5304 UPDATE_BUSY_STATE();
5306 print_timestamp_done("InitImages");
5309 static void InitSound(char *identifier)
5311 print_timestamp_init("InitSound");
5313 if (identifier == NULL)
5314 identifier = artwork.snd_current->identifier;
5316 /* set artwork path to send it to the sound server process */
5317 setLevelArtworkDir(artwork.snd_first);
5319 InitReloadCustomSounds(identifier);
5320 print_timestamp_time("InitReloadCustomSounds");
5322 ReinitializeSounds();
5323 print_timestamp_time("ReinitializeSounds");
5325 print_timestamp_done("InitSound");
5328 static void InitMusic(char *identifier)
5330 print_timestamp_init("InitMusic");
5332 if (identifier == NULL)
5333 identifier = artwork.mus_current->identifier;
5335 /* set artwork path to send it to the sound server process */
5336 setLevelArtworkDir(artwork.mus_first);
5338 InitReloadCustomMusic(identifier);
5339 print_timestamp_time("InitReloadCustomMusic");
5341 ReinitializeMusic();
5342 print_timestamp_time("ReinitializeMusic");
5344 print_timestamp_done("InitMusic");
5347 void InitNetworkServer()
5349 #if defined(NETWORK_AVALIABLE)
5353 if (!options.network)
5356 #if defined(NETWORK_AVALIABLE)
5357 nr_wanted = Request("Choose player", REQ_PLAYER | REQ_STAY_CLOSED);
5359 if (!ConnectToServer(options.server_host, options.server_port))
5360 Error(ERR_EXIT, "cannot connect to network game server");
5362 SendToServer_PlayerName(setup.player_name);
5363 SendToServer_ProtocolVersion();
5366 SendToServer_NrWanted(nr_wanted);
5370 static boolean CheckArtworkConfigForCustomElements(char *filename)
5372 SetupFileHash *setup_file_hash;
5373 boolean redefined_ce_found = FALSE;
5375 /* !!! CACHE THIS BY USING HASH 'filename' => 'true/false' !!! */
5377 if ((setup_file_hash = loadSetupFileHash(filename)) != NULL)
5379 BEGIN_HASH_ITERATION(setup_file_hash, itr)
5381 char *token = HASH_ITERATION_TOKEN(itr);
5383 if (strPrefix(token, "custom_"))
5385 redefined_ce_found = TRUE;
5390 END_HASH_ITERATION(setup_file_hash, itr)
5392 freeSetupFileHash(setup_file_hash);
5395 return redefined_ce_found;
5398 static boolean CheckArtworkTypeForRedefinedCustomElements(int type)
5400 char *filename_base, *filename_local;
5401 boolean redefined_ce_found = FALSE;
5403 setLevelArtworkDir(ARTWORK_FIRST_NODE(artwork, type));
5406 printf("::: leveldir_current->identifier == '%s'\n",
5407 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5408 printf("::: leveldir_current->graphics_path == '%s'\n",
5409 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5410 printf("::: leveldir_current->graphics_set == '%s'\n",
5411 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5412 printf("::: getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'\n",
5413 leveldir_current == NULL ? "[NULL]" :
5414 LEVELDIR_ARTWORK_SET(leveldir_current, type));
5417 /* first look for special artwork configured in level series config */
5418 filename_base = getCustomArtworkLevelConfigFilename(type);
5421 printf("::: filename_base == '%s'\n", filename_base);
5424 if (fileExists(filename_base))
5425 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_base);
5427 filename_local = getCustomArtworkConfigFilename(type);
5430 printf("::: filename_local == '%s'\n", filename_local);
5433 if (filename_local != NULL && !strEqual(filename_base, filename_local))
5434 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_local);
5437 printf("::: redefined_ce_found == %d\n", redefined_ce_found);
5440 return redefined_ce_found;
5443 static void InitOverrideArtwork()
5445 boolean redefined_ce_found = FALSE;
5447 /* to check if this level set redefines any CEs, do not use overriding */
5448 gfx.override_level_graphics = FALSE;
5449 gfx.override_level_sounds = FALSE;
5450 gfx.override_level_music = FALSE;
5452 /* now check if this level set has definitions for custom elements */
5453 if (setup.override_level_graphics == AUTO ||
5454 setup.override_level_sounds == AUTO ||
5455 setup.override_level_music == AUTO)
5456 redefined_ce_found =
5457 (CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_GRAPHICS) |
5458 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_SOUNDS) |
5459 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_MUSIC));
5462 printf("::: redefined_ce_found == %d\n", redefined_ce_found);
5465 if (redefined_ce_found)
5467 /* this level set has CE definitions: change "AUTO" to "FALSE" */
5468 gfx.override_level_graphics = (setup.override_level_graphics == TRUE);
5469 gfx.override_level_sounds = (setup.override_level_sounds == TRUE);
5470 gfx.override_level_music = (setup.override_level_music == TRUE);
5474 /* this level set has no CE definitions: change "AUTO" to "TRUE" */
5475 gfx.override_level_graphics = (setup.override_level_graphics != FALSE);
5476 gfx.override_level_sounds = (setup.override_level_sounds != FALSE);
5477 gfx.override_level_music = (setup.override_level_music != FALSE);
5481 printf("::: => %d, %d, %d\n",
5482 gfx.override_level_graphics,
5483 gfx.override_level_sounds,
5484 gfx.override_level_music);
5488 static char *getNewArtworkIdentifier(int type)
5490 static char *leveldir_current_identifier[3] = { NULL, NULL, NULL };
5491 static boolean last_override_level_artwork[3] = { FALSE, FALSE, FALSE };
5492 static boolean last_has_level_artwork_set[3] = { FALSE, FALSE, FALSE };
5493 static boolean initialized[3] = { FALSE, FALSE, FALSE };
5494 TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type);
5495 boolean setup_override_artwork = GFX_OVERRIDE_ARTWORK(type);
5496 char *setup_artwork_set = SETUP_ARTWORK_SET(setup, type);
5497 char *leveldir_identifier = leveldir_current->identifier;
5498 /* !!! setLevelArtworkDir() should be moved to an earlier stage !!! */
5499 char *leveldir_artwork_set = setLevelArtworkDir(artwork_first_node);
5500 boolean has_level_artwork_set = (leveldir_artwork_set != NULL);
5501 char *artwork_current_identifier;
5502 char *artwork_new_identifier = NULL; /* default: nothing has changed */
5504 /* leveldir_current may be invalid (level group, parent link) */
5505 if (!validLevelSeries(leveldir_current))
5508 /* 1st step: determine artwork set to be activated in descending order:
5509 --------------------------------------------------------------------
5510 1. setup artwork (when configured to override everything else)
5511 2. artwork set configured in "levelinfo.conf" of current level set
5512 (artwork in level directory will have priority when loading later)
5513 3. artwork in level directory (stored in artwork sub-directory)
5514 4. setup artwork (currently configured in setup menu) */
5516 if (setup_override_artwork)
5517 artwork_current_identifier = setup_artwork_set;
5518 else if (leveldir_artwork_set != NULL)
5519 artwork_current_identifier = leveldir_artwork_set;
5520 else if (getTreeInfoFromIdentifier(artwork_first_node, leveldir_identifier))
5521 artwork_current_identifier = leveldir_identifier;
5523 artwork_current_identifier = setup_artwork_set;
5526 /* 2nd step: check if it is really needed to reload artwork set
5527 ------------------------------------------------------------ */
5529 /* ---------- reload if level set and also artwork set has changed ------- */
5530 if (leveldir_current_identifier[type] != leveldir_identifier &&
5531 (last_has_level_artwork_set[type] || has_level_artwork_set))
5532 artwork_new_identifier = artwork_current_identifier;
5534 leveldir_current_identifier[type] = leveldir_identifier;
5535 last_has_level_artwork_set[type] = has_level_artwork_set;
5537 /* ---------- reload if "override artwork" setting has changed ----------- */
5538 if (last_override_level_artwork[type] != setup_override_artwork)
5539 artwork_new_identifier = artwork_current_identifier;
5541 last_override_level_artwork[type] = setup_override_artwork;
5543 /* ---------- reload if current artwork identifier has changed ----------- */
5544 if (!strEqual(ARTWORK_CURRENT_IDENTIFIER(artwork, type),
5545 artwork_current_identifier))
5546 artwork_new_identifier = artwork_current_identifier;
5548 *(ARTWORK_CURRENT_IDENTIFIER_PTR(artwork, type))= artwork_current_identifier;
5550 /* ---------- do not reload directly after starting ---------------------- */
5551 if (!initialized[type])
5552 artwork_new_identifier = NULL;
5554 initialized[type] = TRUE;
5556 return artwork_new_identifier;
5559 void ReloadCustomArtwork(int force_reload)
5561 int last_game_status = game_status; /* save current game status */
5562 char *gfx_new_identifier;
5563 char *snd_new_identifier;
5564 char *mus_new_identifier;
5565 boolean force_reload_gfx = (force_reload & (1 << ARTWORK_TYPE_GRAPHICS));
5566 boolean force_reload_snd = (force_reload & (1 << ARTWORK_TYPE_SOUNDS));
5567 boolean force_reload_mus = (force_reload & (1 << ARTWORK_TYPE_MUSIC));
5568 boolean reload_needed;
5570 InitOverrideArtwork();
5572 force_reload_gfx |= AdjustGraphicsForEMC();
5574 gfx_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
5575 snd_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
5576 mus_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
5578 reload_needed = (gfx_new_identifier != NULL || force_reload_gfx ||
5579 snd_new_identifier != NULL || force_reload_snd ||
5580 mus_new_identifier != NULL || force_reload_mus);
5585 print_timestamp_init("ReloadCustomArtwork");
5587 game_status = GAME_MODE_LOADING;
5589 FadeOut(REDRAW_ALL);
5591 ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
5592 print_timestamp_time("ClearRectangle");
5596 if (gfx_new_identifier != NULL || force_reload_gfx)
5599 printf("RELOADING GRAPHICS '%s' -> '%s' ['%s', '%s']\n",
5600 artwork.gfx_current_identifier,
5602 artwork.gfx_current->identifier,
5603 leveldir_current->graphics_set);
5607 print_timestamp_time("InitImages");
5610 if (snd_new_identifier != NULL || force_reload_snd)
5612 InitSound(snd_new_identifier);
5613 print_timestamp_time("InitSound");
5616 if (mus_new_identifier != NULL || force_reload_mus)
5618 InitMusic(mus_new_identifier);
5619 print_timestamp_time("InitMusic");
5622 game_status = last_game_status; /* restore current game status */
5624 init_last = init; /* switch to new busy animation */
5626 FadeOut(REDRAW_ALL);
5628 RedrawGlobalBorder();
5630 /* force redraw of (open or closed) door graphics */
5631 SetDoorState(DOOR_OPEN_ALL);
5632 CloseDoor(DOOR_CLOSE_ALL | DOOR_NO_DELAY);
5634 FadeSetEnterScreen();
5635 FadeSkipNextFadeOut();
5637 print_timestamp_done("ReloadCustomArtwork");
5639 LimitScreenUpdates(FALSE);
5642 void KeyboardAutoRepeatOffUnlessAutoplay()
5644 if (global.autoplay_leveldir == NULL)
5645 KeyboardAutoRepeatOff();
5648 void DisplayExitMessage(char *format, va_list ap)
5650 // check if draw buffer and fonts for exit message are already available
5651 if (drawto == NULL || font_initial[NUM_INITIAL_FONTS - 1].bitmap == NULL)
5654 int font_1 = FC_RED;
5655 int font_2 = FC_YELLOW;
5656 int font_3 = FC_BLUE;
5657 int font_width = getFontWidth(font_2);
5658 int font_height = getFontHeight(font_2);
5661 int sxsize = WIN_XSIZE - 2 * sx;
5662 int sysize = WIN_YSIZE - 2 * sy;
5663 int line_length = sxsize / font_width;
5664 int max_lines = sysize / font_height;
5665 int num_lines_printed;
5669 gfx.sxsize = sxsize;
5670 gfx.sysize = sysize;
5674 ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
5676 DrawTextSCentered(sy, font_1, "Fatal error:");
5677 sy += 3 * font_height;;
5680 DrawTextBufferVA(sx, sy, format, ap, font_2,
5681 line_length, line_length, max_lines,
5682 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
5683 sy += (num_lines_printed + 3) * font_height;
5685 DrawTextSCentered(sy, font_1, "For details, see the following error file:");
5686 sy += 3 * font_height;
5689 DrawTextBuffer(sx, sy, program.log_filename[LOG_ERR_ID], font_2,
5690 line_length, line_length, max_lines,
5691 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
5693 DrawTextSCentered(SYSIZE - 20, font_3, "Press any key or button to exit");
5695 redraw_mask = REDRAW_ALL;
5697 /* force drawing exit message even if screen updates are currently limited */
5698 LimitScreenUpdates(FALSE);
5702 /* deactivate toons on error message screen */
5703 setup.toons = FALSE;
5705 WaitForEventToContinue();
5709 /* ========================================================================= */
5711 /* ========================================================================= */
5715 print_timestamp_init("OpenAll");
5717 game_status = GAME_MODE_LOADING;
5721 InitGlobal(); /* initialize some global variables */
5723 print_timestamp_time("[init global stuff]");
5727 print_timestamp_time("[init setup/config stuff (1)]");
5729 if (options.execute_command)
5730 Execute_Command(options.execute_command);
5732 if (options.serveronly)
5734 #if defined(PLATFORM_UNIX)
5735 NetworkServer(options.server_port, options.serveronly);
5737 Error(ERR_WARN, "networking only supported in Unix version");
5740 exit(0); /* never reached, server loops forever */
5744 print_timestamp_time("[init setup/config stuff (2)]");
5746 print_timestamp_time("[init setup/config stuff (3)]");
5747 InitArtworkInfo(); /* needed before loading gfx, sound & music */
5748 print_timestamp_time("[init setup/config stuff (4)]");
5749 InitArtworkConfig(); /* needed before forking sound child process */
5750 print_timestamp_time("[init setup/config stuff (5)]");
5752 print_timestamp_time("[init setup/config stuff (6)]");
5754 InitRND(NEW_RANDOMIZE);
5755 InitSimpleRandom(NEW_RANDOMIZE);
5759 print_timestamp_time("[init setup/config stuff]");
5762 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
5764 InitEventFilter(FilterEvents);
5766 print_timestamp_time("[init video stuff]");
5768 InitElementPropertiesStatic();
5769 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
5770 InitElementPropertiesGfxElement();
5772 print_timestamp_time("[init element properties stuff]");
5776 print_timestamp_time("InitGfx");
5779 print_timestamp_time("InitLevelInfo");
5781 InitLevelArtworkInfo();
5782 print_timestamp_time("InitLevelArtworkInfo");
5784 InitOverrideArtwork(); /* needs to know current level directory */
5785 print_timestamp_time("InitOverrideArtwork");
5787 InitImages(); /* needs to know current level directory */
5788 print_timestamp_time("InitImages");
5790 InitSound(NULL); /* needs to know current level directory */
5791 print_timestamp_time("InitSound");
5793 InitMusic(NULL); /* needs to know current level directory */
5794 print_timestamp_time("InitMusic");
5796 InitGfxBackground();
5801 if (global.autoplay_leveldir)
5806 else if (global.convert_leveldir)
5811 else if (global.create_images_dir)
5813 CreateLevelSketchImages();
5817 game_status = GAME_MODE_MAIN;
5819 FadeSetEnterScreen();
5820 if (!(fading.fade_mode & FADE_TYPE_TRANSFORM))
5821 FadeSkipNextFadeOut();
5823 print_timestamp_time("[post-artwork]");
5825 print_timestamp_done("OpenAll");
5829 InitNetworkServer();
5832 Error(ERR_DEBUG, "::: SDL_GetBasePath() == '%s'",
5834 Error(ERR_DEBUG, "::: SDL_GetPrefPath() == '%s'",
5835 SDL_GetPrefPath("artsoft", "rocksndiamonds"));
5836 #if defined(PLATFORM_ANDROID)
5837 Error(ERR_DEBUG, "::: SDL_AndroidGetInternalStoragePath() == '%s'",
5838 SDL_AndroidGetInternalStoragePath());
5839 Error(ERR_DEBUG, "::: SDL_AndroidGetExternalStoragePath() == '%s'",
5840 SDL_AndroidGetExternalStoragePath());
5841 Error(ERR_DEBUG, "::: SDL_AndroidGetExternalStorageState() == '%s'",
5842 (SDL_AndroidGetExternalStorageState() ==
5843 SDL_ANDROID_EXTERNAL_STORAGE_READ ? "read" :
5844 SDL_AndroidGetExternalStorageState() ==
5845 SDL_ANDROID_EXTERNAL_STORAGE_WRITE ? "write" : "not available"));
5850 void CloseAllAndExit(int exit_value)
5855 CloseAudio(); /* called after freeing sounds (needed for SDL) */
5862 #if defined(TARGET_SDL)
5863 #if defined(TARGET_SDL2)
5865 // set a flag to tell the network server thread to quit and wait for it
5866 // using SDL_WaitThread()
5868 if (network_server) /* terminate network server */
5869 SDL_KillThread(server_thread);
5873 CloseVideoDisplay();
5874 ClosePlatformDependentStuff();
5876 if (exit_value != 0)
5878 /* fall back to default level set (current set may have caused an error) */
5879 SaveLevelSetup_LastSeries_Deactivate();
5881 /* tell user where to find error log file which may contain more details */
5882 // (error notification now directly displayed on screen inside R'n'D
5883 // NotifyUserAboutErrorFile(); /* currently only works for Windows */