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 x = ALIGNED_TEXT_XPOS(&init_last.busy);
106 y = ALIGNED_TEXT_YPOS(&init_last.busy);
108 graphic_info = &anim_initial; /* graphic == 0 => anim_initial */
110 if (sync_frame % anim_initial.anim_delay == 0)
114 int width = graphic_info[graphic].width;
115 int height = graphic_info[graphic].height;
116 int frame = getGraphicAnimationFrame(graphic, sync_frame);
118 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
119 BlitBitmap(src_bitmap, window, src_x, src_y, width, height, x, y);
122 graphic_info = graphic_info_last;
129 FreeLevelEditorGadgets();
138 static boolean gadgets_initialized = FALSE;
140 if (gadgets_initialized)
143 CreateLevelEditorGadgets();
147 CreateScreenGadgets();
149 InitGadgetsSoundCallback(PlaySoundActivating, PlaySoundSelecting);
151 gadgets_initialized = TRUE;
154 inline void InitElementSmallImagesScaledUp(int graphic)
156 CreateImageWithSmallImages(graphic, graphic_info[graphic].scale_up_factor,
157 graphic_info[graphic].tile_size);
160 void InitElementSmallImages()
162 print_timestamp_init("InitElementSmallImages");
164 static int special_graphics[] =
166 IMG_EDITOR_ELEMENT_BORDER,
167 IMG_EDITOR_ELEMENT_BORDER_INPUT,
168 IMG_EDITOR_CASCADE_LIST,
169 IMG_EDITOR_CASCADE_LIST_ACTIVE,
172 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
173 int num_property_mappings = getImageListPropertyMappingSize();
176 print_timestamp_time("getImageListPropertyMapping/Size");
178 print_timestamp_init("InitElementSmallImagesScaledUp (1)");
179 /* initialize normal images from static configuration */
180 for (i = 0; element_to_graphic[i].element > -1; i++)
181 InitElementSmallImagesScaledUp(element_to_graphic[i].graphic);
182 print_timestamp_done("InitElementSmallImagesScaledUp (1)");
184 /* initialize special images from static configuration */
185 for (i = 0; element_to_special_graphic[i].element > -1; i++)
186 InitElementSmallImagesScaledUp(element_to_special_graphic[i].graphic);
187 print_timestamp_time("InitElementSmallImagesScaledUp (2)");
189 /* initialize images from dynamic configuration (may be elements or other) */
190 for (i = 0; i < num_property_mappings; i++)
191 InitElementSmallImagesScaledUp(property_mapping[i].artwork_index);
192 print_timestamp_time("InitElementSmallImagesScaledUp (3)");
194 /* initialize special images from above list (non-element images) */
195 for (i = 0; special_graphics[i] > -1; i++)
196 InitElementSmallImagesScaledUp(special_graphics[i]);
197 print_timestamp_time("InitElementSmallImagesScaledUp (4)");
199 print_timestamp_done("InitElementSmallImages");
202 void InitScaledImages()
206 /* scale normal images from static configuration, if not already scaled */
207 for (i = 0; i < NUM_IMAGE_FILES; i++)
208 ScaleImage(i, graphic_info[i].scale_up_factor);
212 /* !!! FIX THIS (CHANGE TO USING NORMAL ELEMENT GRAPHIC DEFINITIONS) !!! */
213 void SetBitmaps_EM(Bitmap **em_bitmap)
215 em_bitmap[0] = graphic_info[IMG_EMC_OBJECT].bitmap;
216 em_bitmap[1] = graphic_info[IMG_EMC_SPRITE].bitmap;
221 /* !!! FIX THIS (CHANGE TO USING NORMAL ELEMENT GRAPHIC DEFINITIONS) !!! */
222 void SetBitmaps_SP(Bitmap **sp_bitmap)
224 *sp_bitmap = graphic_info[IMG_SP_OBJECTS].bitmap;
228 static int getFontBitmapID(int font_nr)
232 /* (special case: do not use special font for GAME_MODE_LOADING) */
233 if (game_status >= GAME_MODE_TITLE_INITIAL &&
234 game_status <= GAME_MODE_PSEUDO_PREVIEW)
235 special = game_status;
236 else if (game_status == GAME_MODE_PSEUDO_TYPENAME)
237 special = GFX_SPECIAL_ARG_MAIN;
240 return font_info[font_nr].special_bitmap_id[special];
245 static int getFontFromToken(char *token)
247 char *value = getHashEntry(font_token_hash, token);
252 /* if font not found, use reliable default value */
253 return FONT_INITIAL_1;
256 void InitFontGraphicInfo()
258 static struct FontBitmapInfo *font_bitmap_info = NULL;
259 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
260 int num_property_mappings = getImageListPropertyMappingSize();
261 int num_font_bitmaps = NUM_FONTS;
264 if (graphic_info == NULL) /* still at startup phase */
266 InitFontInfo(font_initial, NUM_INITIAL_FONTS,
267 getFontBitmapID, getFontFromToken);
272 /* ---------- initialize font graphic definitions ---------- */
274 /* always start with reliable default values (normal font graphics) */
275 for (i = 0; i < NUM_FONTS; i++)
276 font_info[i].graphic = IMG_FONT_INITIAL_1;
278 /* initialize normal font/graphic mapping from static configuration */
279 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
281 int font_nr = font_to_graphic[i].font_nr;
282 int special = font_to_graphic[i].special;
283 int graphic = font_to_graphic[i].graphic;
288 font_info[font_nr].graphic = graphic;
291 /* always start with reliable default values (special font graphics) */
292 for (i = 0; i < NUM_FONTS; i++)
294 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
296 font_info[i].special_graphic[j] = font_info[i].graphic;
297 font_info[i].special_bitmap_id[j] = i;
301 /* initialize special font/graphic mapping from static configuration */
302 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
304 int font_nr = font_to_graphic[i].font_nr;
305 int special = font_to_graphic[i].special;
306 int graphic = font_to_graphic[i].graphic;
307 int base_graphic = font2baseimg(font_nr);
309 if (IS_SPECIAL_GFX_ARG(special))
311 boolean base_redefined =
312 getImageListEntryFromImageID(base_graphic)->redefined;
313 boolean special_redefined =
314 getImageListEntryFromImageID(graphic)->redefined;
315 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
317 /* if the base font ("font.title_1", for example) has been redefined,
318 but not the special font ("font.title_1.LEVELS", for example), do not
319 use an existing (in this case considered obsolete) special font
320 anymore, but use the automatically determined default font */
321 /* special case: cloned special fonts must be explicitly redefined,
322 but are not automatically redefined by redefining base font */
323 if (base_redefined && !special_redefined && !special_cloned)
326 font_info[font_nr].special_graphic[special] = graphic;
327 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
332 /* initialize special font/graphic mapping from dynamic configuration */
333 for (i = 0; i < num_property_mappings; i++)
335 int font_nr = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
336 int special = property_mapping[i].ext3_index;
337 int graphic = property_mapping[i].artwork_index;
342 if (IS_SPECIAL_GFX_ARG(special))
344 font_info[font_nr].special_graphic[special] = graphic;
345 font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
350 /* correct special font/graphic mapping for cloned fonts for downwards
351 compatibility of PREVIEW fonts -- this is only needed for implicit
352 redefinition of special font by redefined base font, and only if other
353 fonts are cloned from this special font (like in the "Zelda" level set) */
354 for (i = 0; font_to_graphic[i].font_nr > -1; i++)
356 int font_nr = font_to_graphic[i].font_nr;
357 int special = font_to_graphic[i].special;
358 int graphic = font_to_graphic[i].graphic;
360 if (IS_SPECIAL_GFX_ARG(special))
362 boolean special_redefined =
363 getImageListEntryFromImageID(graphic)->redefined;
364 boolean special_cloned = (graphic_info[graphic].clone_from != -1);
366 if (special_cloned && !special_redefined)
370 for (j = 0; font_to_graphic[j].font_nr > -1; j++)
372 int font_nr2 = font_to_graphic[j].font_nr;
373 int special2 = font_to_graphic[j].special;
374 int graphic2 = font_to_graphic[j].graphic;
376 if (IS_SPECIAL_GFX_ARG(special2) &&
377 graphic2 == graphic_info[graphic].clone_from)
379 font_info[font_nr].special_graphic[special] =
380 font_info[font_nr2].special_graphic[special2];
381 font_info[font_nr].special_bitmap_id[special] =
382 font_info[font_nr2].special_bitmap_id[special2];
389 /* reset non-redefined ".active" font graphics if normal font is redefined */
390 /* (this different treatment is needed because normal and active fonts are
391 independently defined ("active" is not a property of font definitions!) */
392 for (i = 0; i < NUM_FONTS; i++)
394 int font_nr_base = i;
395 int font_nr_active = FONT_ACTIVE(font_nr_base);
397 /* check only those fonts with exist as normal and ".active" variant */
398 if (font_nr_base != font_nr_active)
400 int base_graphic = font_info[font_nr_base].graphic;
401 int active_graphic = font_info[font_nr_active].graphic;
402 boolean base_redefined =
403 getImageListEntryFromImageID(base_graphic)->redefined;
404 boolean active_redefined =
405 getImageListEntryFromImageID(active_graphic)->redefined;
407 /* if the base font ("font.menu_1", for example) has been redefined,
408 but not the active font ("font.menu_1.active", for example), do not
409 use an existing (in this case considered obsolete) active font
410 anymore, but use the automatically determined default font */
411 if (base_redefined && !active_redefined)
412 font_info[font_nr_active].graphic = base_graphic;
414 /* now also check each "special" font (which may be the same as above) */
415 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
417 int base_graphic = font_info[font_nr_base].special_graphic[j];
418 int active_graphic = font_info[font_nr_active].special_graphic[j];
419 boolean base_redefined =
420 getImageListEntryFromImageID(base_graphic)->redefined;
421 boolean active_redefined =
422 getImageListEntryFromImageID(active_graphic)->redefined;
424 /* same as above, but check special graphic definitions, for example:
425 redefined "font.menu_1.MAIN" invalidates "font.menu_1.active.MAIN" */
426 if (base_redefined && !active_redefined)
428 font_info[font_nr_active].special_graphic[j] =
429 font_info[font_nr_base].special_graphic[j];
430 font_info[font_nr_active].special_bitmap_id[j] =
431 font_info[font_nr_base].special_bitmap_id[j];
437 /* ---------- initialize font bitmap array ---------- */
439 if (font_bitmap_info != NULL)
440 FreeFontInfo(font_bitmap_info);
443 checked_calloc(num_font_bitmaps * sizeof(struct FontBitmapInfo));
445 /* ---------- initialize font bitmap definitions ---------- */
447 for (i = 0; i < NUM_FONTS; i++)
449 if (i < NUM_INITIAL_FONTS)
451 font_bitmap_info[i] = font_initial[i];
455 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
457 int font_bitmap_id = font_info[i].special_bitmap_id[j];
458 int graphic = font_info[i].special_graphic[j];
460 /* set 'graphic_info' for font entries, if uninitialized (guessed) */
461 if (graphic_info[graphic].anim_frames < MIN_NUM_CHARS_PER_FONT)
463 graphic_info[graphic].anim_frames = DEFAULT_NUM_CHARS_PER_FONT;
464 graphic_info[graphic].anim_frames_per_line = DEFAULT_NUM_CHARS_PER_LINE;
467 /* copy font relevant information from graphics information */
468 font_bitmap_info[font_bitmap_id].bitmap = graphic_info[graphic].bitmap;
469 font_bitmap_info[font_bitmap_id].src_x = graphic_info[graphic].src_x;
470 font_bitmap_info[font_bitmap_id].src_y = graphic_info[graphic].src_y;
471 font_bitmap_info[font_bitmap_id].width = graphic_info[graphic].width;
472 font_bitmap_info[font_bitmap_id].height = graphic_info[graphic].height;
474 font_bitmap_info[font_bitmap_id].draw_xoffset =
475 graphic_info[graphic].draw_xoffset;
476 font_bitmap_info[font_bitmap_id].draw_yoffset =
477 graphic_info[graphic].draw_yoffset;
479 font_bitmap_info[font_bitmap_id].num_chars =
480 graphic_info[graphic].anim_frames;
481 font_bitmap_info[font_bitmap_id].num_chars_per_line =
482 graphic_info[graphic].anim_frames_per_line;
486 InitFontInfo(font_bitmap_info, num_font_bitmaps,
487 getFontBitmapID, getFontFromToken);
490 void InitElementGraphicInfo()
492 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
493 int num_property_mappings = getImageListPropertyMappingSize();
496 if (graphic_info == NULL) /* still at startup phase */
499 /* set values to -1 to identify later as "uninitialized" values */
500 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
502 for (act = 0; act < NUM_ACTIONS; act++)
504 element_info[i].graphic[act] = -1;
505 element_info[i].crumbled[act] = -1;
507 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
509 element_info[i].direction_graphic[act][dir] = -1;
510 element_info[i].direction_crumbled[act][dir] = -1;
517 /* initialize normal element/graphic mapping from static configuration */
518 for (i = 0; element_to_graphic[i].element > -1; i++)
520 int element = element_to_graphic[i].element;
521 int action = element_to_graphic[i].action;
522 int direction = element_to_graphic[i].direction;
523 boolean crumbled = element_to_graphic[i].crumbled;
524 int graphic = element_to_graphic[i].graphic;
525 int base_graphic = el2baseimg(element);
527 if (graphic_info[graphic].bitmap == NULL)
530 if ((action > -1 || direction > -1 || crumbled == TRUE) &&
533 boolean base_redefined =
534 getImageListEntryFromImageID(base_graphic)->redefined;
535 boolean act_dir_redefined =
536 getImageListEntryFromImageID(graphic)->redefined;
538 /* if the base graphic ("emerald", for example) has been redefined,
539 but not the action graphic ("emerald.falling", for example), do not
540 use an existing (in this case considered obsolete) action graphic
541 anymore, but use the automatically determined default graphic */
542 if (base_redefined && !act_dir_redefined)
547 action = ACTION_DEFAULT;
552 element_info[element].direction_crumbled[action][direction] = graphic;
554 element_info[element].crumbled[action] = graphic;
559 element_info[element].direction_graphic[action][direction] = graphic;
561 element_info[element].graphic[action] = graphic;
565 /* initialize normal element/graphic mapping from dynamic configuration */
566 for (i = 0; i < num_property_mappings; i++)
568 int element = property_mapping[i].base_index;
569 int action = property_mapping[i].ext1_index;
570 int direction = property_mapping[i].ext2_index;
571 int special = property_mapping[i].ext3_index;
572 int graphic = property_mapping[i].artwork_index;
573 boolean crumbled = FALSE;
575 if (special == GFX_SPECIAL_ARG_CRUMBLED)
581 if (graphic_info[graphic].bitmap == NULL)
584 if (element >= MAX_NUM_ELEMENTS || special != -1)
588 action = ACTION_DEFAULT;
593 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
594 element_info[element].direction_crumbled[action][dir] = -1;
597 element_info[element].direction_crumbled[action][direction] = graphic;
599 element_info[element].crumbled[action] = graphic;
604 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
605 element_info[element].direction_graphic[action][dir] = -1;
608 element_info[element].direction_graphic[action][direction] = graphic;
610 element_info[element].graphic[action] = graphic;
614 /* now copy all graphics that are defined to be cloned from other graphics */
615 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
617 int graphic = element_info[i].graphic[ACTION_DEFAULT];
618 int crumbled_like, diggable_like;
623 crumbled_like = graphic_info[graphic].crumbled_like;
624 diggable_like = graphic_info[graphic].diggable_like;
626 if (crumbled_like != -1 && element_info[i].crumbled[ACTION_DEFAULT] == -1)
628 for (act = 0; act < NUM_ACTIONS; act++)
629 element_info[i].crumbled[act] =
630 element_info[crumbled_like].crumbled[act];
631 for (act = 0; act < NUM_ACTIONS; act++)
632 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
633 element_info[i].direction_crumbled[act][dir] =
634 element_info[crumbled_like].direction_crumbled[act][dir];
637 if (diggable_like != -1 && element_info[i].graphic[ACTION_DIGGING] == -1)
639 element_info[i].graphic[ACTION_DIGGING] =
640 element_info[diggable_like].graphic[ACTION_DIGGING];
641 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
642 element_info[i].direction_graphic[ACTION_DIGGING][dir] =
643 element_info[diggable_like].direction_graphic[ACTION_DIGGING][dir];
647 /* set hardcoded definitions for some runtime elements without graphic */
648 element_info[EL_AMOEBA_TO_DIAMOND].graphic[ACTION_DEFAULT] = IMG_AMOEBA_DEAD;
650 /* set hardcoded definitions for some internal elements without graphic */
651 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
653 if (IS_EDITOR_CASCADE_INACTIVE(i))
654 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST;
655 else if (IS_EDITOR_CASCADE_ACTIVE(i))
656 element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST_ACTIVE;
659 /* now set all undefined/invalid graphics to -1 to set to default after it */
660 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
662 for (act = 0; act < NUM_ACTIONS; act++)
666 graphic = element_info[i].graphic[act];
667 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
668 element_info[i].graphic[act] = -1;
670 graphic = element_info[i].crumbled[act];
671 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
672 element_info[i].crumbled[act] = -1;
674 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
676 graphic = element_info[i].direction_graphic[act][dir];
677 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
678 element_info[i].direction_graphic[act][dir] = -1;
680 graphic = element_info[i].direction_crumbled[act][dir];
681 if (graphic > 0 && graphic_info[graphic].bitmap == NULL)
682 element_info[i].direction_crumbled[act][dir] = -1;
689 /* adjust graphics with 2nd tile for movement according to direction
690 (do this before correcting '-1' values to minimize calculations) */
691 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
693 for (act = 0; act < NUM_ACTIONS; act++)
695 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
697 int graphic = element_info[i].direction_graphic[act][dir];
698 int move_dir = (act == ACTION_FALLING ? MV_BIT_DOWN : dir);
700 if (act == ACTION_FALLING) /* special case */
701 graphic = element_info[i].graphic[act];
704 graphic_info[graphic].double_movement &&
705 graphic_info[graphic].swap_double_tiles != 0)
707 struct GraphicInfo *g = &graphic_info[graphic];
708 int src_x_front = g->src_x;
709 int src_y_front = g->src_y;
710 int src_x_back = g->src_x + g->offset2_x;
711 int src_y_back = g->src_y + g->offset2_y;
712 boolean frames_are_ordered_diagonally = (g->offset_x != 0 &&
714 boolean front_is_left_or_upper = (src_x_front < src_x_back ||
715 src_y_front < src_y_back);
716 boolean swap_movement_tiles_always = (g->swap_double_tiles == 1);
717 boolean swap_movement_tiles_autodetected =
718 (!frames_are_ordered_diagonally &&
719 ((move_dir == MV_BIT_LEFT && !front_is_left_or_upper) ||
720 (move_dir == MV_BIT_UP && !front_is_left_or_upper) ||
721 (move_dir == MV_BIT_RIGHT && front_is_left_or_upper) ||
722 (move_dir == MV_BIT_DOWN && front_is_left_or_upper)));
725 /* swap frontside and backside graphic tile coordinates, if needed */
726 if (swap_movement_tiles_always || swap_movement_tiles_autodetected)
728 /* get current (wrong) backside tile coordinates */
729 getFixedGraphicSourceExt(graphic, 0, &dummy,
730 &src_x_back, &src_y_back, TRUE);
732 /* set frontside tile coordinates to backside tile coordinates */
733 g->src_x = src_x_back;
734 g->src_y = src_y_back;
736 /* invert tile offset to point to new backside tile coordinates */
740 /* do not swap front and backside tiles again after correction */
741 g->swap_double_tiles = 0;
750 /* now set all '-1' values to element specific default values */
751 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
753 int default_graphic = element_info[i].graphic[ACTION_DEFAULT];
754 int default_crumbled = element_info[i].crumbled[ACTION_DEFAULT];
755 int default_direction_graphic[NUM_DIRECTIONS_FULL];
756 int default_direction_crumbled[NUM_DIRECTIONS_FULL];
758 if (default_graphic == -1)
759 default_graphic = IMG_UNKNOWN;
761 if (default_crumbled == -1)
762 default_crumbled = default_graphic;
764 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
766 default_direction_graphic[dir] =
767 element_info[i].direction_graphic[ACTION_DEFAULT][dir];
768 default_direction_crumbled[dir] =
769 element_info[i].direction_crumbled[ACTION_DEFAULT][dir];
771 if (default_direction_graphic[dir] == -1)
772 default_direction_graphic[dir] = default_graphic;
774 if (default_direction_crumbled[dir] == -1)
775 default_direction_crumbled[dir] = default_direction_graphic[dir];
778 for (act = 0; act < NUM_ACTIONS; act++)
780 boolean act_remove = ((IS_DIGGABLE(i) && act == ACTION_DIGGING) ||
781 (IS_SNAPPABLE(i) && act == ACTION_SNAPPING) ||
782 (IS_COLLECTIBLE(i) && act == ACTION_COLLECTING));
783 boolean act_turning = (act == ACTION_TURNING_FROM_LEFT ||
784 act == ACTION_TURNING_FROM_RIGHT ||
785 act == ACTION_TURNING_FROM_UP ||
786 act == ACTION_TURNING_FROM_DOWN);
788 /* generic default action graphic (defined by "[default]" directive) */
789 int default_action_graphic = element_info[EL_DEFAULT].graphic[act];
790 int default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
791 int default_remove_graphic = IMG_EMPTY;
793 if (act_remove && default_action_graphic != -1)
794 default_remove_graphic = default_action_graphic;
796 /* look for special default action graphic (classic game specific) */
797 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].graphic[act] != -1)
798 default_action_graphic = element_info[EL_BD_DEFAULT].graphic[act];
799 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].graphic[act] != -1)
800 default_action_graphic = element_info[EL_SP_DEFAULT].graphic[act];
801 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].graphic[act] != -1)
802 default_action_graphic = element_info[EL_SB_DEFAULT].graphic[act];
804 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].crumbled[act] != -1)
805 default_action_crumbled = element_info[EL_BD_DEFAULT].crumbled[act];
806 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].crumbled[act] != -1)
807 default_action_crumbled = element_info[EL_SP_DEFAULT].crumbled[act];
808 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].crumbled[act] != -1)
809 default_action_crumbled = element_info[EL_SB_DEFAULT].crumbled[act];
811 /* !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!! */
812 /* !!! make this better !!! */
813 if (i == EL_EMPTY_SPACE)
815 default_action_graphic = element_info[EL_DEFAULT].graphic[act];
816 default_action_crumbled = element_info[EL_DEFAULT].crumbled[act];
819 if (default_action_graphic == -1)
820 default_action_graphic = default_graphic;
822 if (default_action_crumbled == -1)
823 default_action_crumbled = default_action_graphic;
825 for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
827 /* use action graphic as the default direction graphic, if undefined */
828 int default_action_direction_graphic = element_info[i].graphic[act];
829 int default_action_direction_crumbled = element_info[i].crumbled[act];
831 /* no graphic for current action -- use default direction graphic */
832 if (default_action_direction_graphic == -1)
833 default_action_direction_graphic =
834 (act_remove ? default_remove_graphic :
836 element_info[i].direction_graphic[ACTION_TURNING][dir] :
837 default_action_graphic != default_graphic ?
838 default_action_graphic :
839 default_direction_graphic[dir]);
841 if (element_info[i].direction_graphic[act][dir] == -1)
842 element_info[i].direction_graphic[act][dir] =
843 default_action_direction_graphic;
845 if (default_action_direction_crumbled == -1)
846 default_action_direction_crumbled =
847 element_info[i].direction_graphic[act][dir];
849 if (element_info[i].direction_crumbled[act][dir] == -1)
850 element_info[i].direction_crumbled[act][dir] =
851 default_action_direction_crumbled;
854 /* no graphic for this specific action -- use default action graphic */
855 if (element_info[i].graphic[act] == -1)
856 element_info[i].graphic[act] =
857 (act_remove ? default_remove_graphic :
858 act_turning ? element_info[i].graphic[ACTION_TURNING] :
859 default_action_graphic);
861 if (element_info[i].crumbled[act] == -1)
862 element_info[i].crumbled[act] = element_info[i].graphic[act];
869 void InitElementSpecialGraphicInfo()
871 struct PropertyMapping *property_mapping = getImageListPropertyMapping();
872 int num_property_mappings = getImageListPropertyMappingSize();
875 /* always start with reliable default values */
876 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
877 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
878 element_info[i].special_graphic[j] =
879 element_info[i].graphic[ACTION_DEFAULT];
881 /* initialize special element/graphic mapping from static configuration */
882 for (i = 0; element_to_special_graphic[i].element > -1; i++)
884 int element = element_to_special_graphic[i].element;
885 int special = element_to_special_graphic[i].special;
886 int graphic = element_to_special_graphic[i].graphic;
887 int base_graphic = el2baseimg(element);
888 boolean base_redefined =
889 getImageListEntryFromImageID(base_graphic)->redefined;
890 boolean special_redefined =
891 getImageListEntryFromImageID(graphic)->redefined;
893 /* if the base graphic ("emerald", for example) has been redefined,
894 but not the special graphic ("emerald.EDITOR", for example), do not
895 use an existing (in this case considered obsolete) special graphic
896 anymore, but use the automatically created (down-scaled) graphic */
897 if (base_redefined && !special_redefined)
900 element_info[element].special_graphic[special] = graphic;
903 /* initialize special element/graphic mapping from dynamic configuration */
904 for (i = 0; i < num_property_mappings; i++)
906 int element = property_mapping[i].base_index;
907 int action = property_mapping[i].ext1_index;
908 int direction = property_mapping[i].ext2_index;
909 int special = property_mapping[i].ext3_index;
910 int graphic = property_mapping[i].artwork_index;
912 /* for action ".active", replace element with active element, if exists */
913 if (action == ACTION_ACTIVE && element != ELEMENT_ACTIVE(element))
915 element = ELEMENT_ACTIVE(element);
919 if (element >= MAX_NUM_ELEMENTS)
922 /* do not change special graphic if action or direction was specified */
923 if (action != -1 || direction != -1)
926 if (IS_SPECIAL_GFX_ARG(special))
927 element_info[element].special_graphic[special] = graphic;
930 /* now set all undefined/invalid graphics to default */
931 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
932 for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
933 if (graphic_info[element_info[i].special_graphic[j]].bitmap == NULL)
934 element_info[i].special_graphic[j] =
935 element_info[i].graphic[ACTION_DEFAULT];
938 static int get_graphic_parameter_value(char *value_raw, char *suffix, int type)
940 if (type != TYPE_ELEMENT && type != TYPE_GRAPHIC)
941 return get_parameter_value(value_raw, suffix, type);
943 if (strEqual(value_raw, ARG_UNDEFINED))
944 return ARG_UNDEFINED_VALUE;
946 if (type == TYPE_ELEMENT)
948 char *value = getHashEntry(element_token_hash, value_raw);
950 return (value != NULL ? atoi(value) : EL_UNDEFINED);
952 else if (type == TYPE_GRAPHIC)
954 char *value = getHashEntry(graphic_token_hash, value_raw);
956 return (value != NULL ? atoi(value) : IMG_UNDEFINED);
962 static int get_scaled_graphic_width(int graphic)
964 int original_width = getOriginalImageWidthFromImageID(graphic);
965 int scale_up_factor = graphic_info[graphic].scale_up_factor;
967 return original_width * scale_up_factor;
970 static int get_scaled_graphic_height(int graphic)
972 int original_height = getOriginalImageHeightFromImageID(graphic);
973 int scale_up_factor = graphic_info[graphic].scale_up_factor;
975 return original_height * scale_up_factor;
978 static void set_graphic_parameters_ext(int graphic, int *parameter,
981 struct GraphicInfo *g = &graphic_info[graphic];
982 int anim_frames_per_row = 1, anim_frames_per_col = 1;
983 int anim_frames_per_line = 1;
985 /* always start with reliable default values */
986 g->src_image_width = 0;
987 g->src_image_height = 0;
990 g->width = TILEX; /* default for element graphics */
991 g->height = TILEY; /* default for element graphics */
992 g->offset_x = 0; /* one or both of these values ... */
993 g->offset_y = 0; /* ... will be corrected later */
994 g->offset2_x = 0; /* one or both of these values ... */
995 g->offset2_y = 0; /* ... will be corrected later */
996 g->swap_double_tiles = -1; /* auto-detect tile swapping */
997 g->crumbled_like = -1; /* do not use clone element */
998 g->diggable_like = -1; /* do not use clone element */
999 g->border_size = TILEX / 8; /* "CRUMBLED" border size */
1000 g->scale_up_factor = 1; /* default: no scaling up */
1001 g->tile_size = TILESIZE; /* default: standard tile size */
1002 g->clone_from = -1; /* do not use clone graphic */
1003 g->anim_delay_fixed = 0;
1004 g->anim_delay_random = 0;
1005 g->post_delay_fixed = 0;
1006 g->post_delay_random = 0;
1007 g->fade_mode = FADE_MODE_DEFAULT;
1011 g->align = ALIGN_CENTER; /* default for title screens */
1012 g->valign = VALIGN_MIDDLE; /* default for title screens */
1013 g->sort_priority = 0; /* default for title screens */
1015 g->style = STYLE_DEFAULT;
1017 g->bitmap = src_bitmap;
1019 /* optional zoom factor for scaling up the image to a larger size */
1020 if (parameter[GFX_ARG_SCALE_UP_FACTOR] != ARG_UNDEFINED_VALUE)
1021 g->scale_up_factor = parameter[GFX_ARG_SCALE_UP_FACTOR];
1022 if (g->scale_up_factor < 1)
1023 g->scale_up_factor = 1; /* no scaling */
1025 /* optional tile size for using non-standard image size */
1026 if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
1027 g->tile_size = parameter[GFX_ARG_TILE_SIZE];
1028 if (g->tile_size < TILESIZE)
1029 g->tile_size = TILESIZE; /* standard tile size */
1031 if (g->use_image_size)
1033 /* set new default bitmap size (with scaling, but without small images) */
1034 g->width = get_scaled_graphic_width(graphic);
1035 g->height = get_scaled_graphic_height(graphic);
1038 /* optional x and y tile position of animation frame sequence */
1039 if (parameter[GFX_ARG_XPOS] != ARG_UNDEFINED_VALUE)
1040 g->src_x = parameter[GFX_ARG_XPOS] * TILEX;
1041 if (parameter[GFX_ARG_YPOS] != ARG_UNDEFINED_VALUE)
1042 g->src_y = parameter[GFX_ARG_YPOS] * TILEY;
1044 /* optional x and y pixel position of animation frame sequence */
1045 if (parameter[GFX_ARG_X] != ARG_UNDEFINED_VALUE)
1046 g->src_x = parameter[GFX_ARG_X];
1047 if (parameter[GFX_ARG_Y] != ARG_UNDEFINED_VALUE)
1048 g->src_y = parameter[GFX_ARG_Y];
1050 /* optional width and height of each animation frame */
1051 if (parameter[GFX_ARG_WIDTH] != ARG_UNDEFINED_VALUE)
1052 g->width = parameter[GFX_ARG_WIDTH];
1053 if (parameter[GFX_ARG_HEIGHT] != ARG_UNDEFINED_VALUE)
1054 g->height = parameter[GFX_ARG_HEIGHT];
1060 Error(ERR_INFO_LINE, "-");
1061 Error(ERR_WARN, "invalid value %d for '%s.width' (fallback done to %d)",
1062 g->width, getTokenFromImageID(graphic), TILEX);
1063 Error(ERR_INFO_LINE, "-");
1065 g->width = TILEX; /* will be checked to be inside bitmap later */
1070 Error(ERR_INFO_LINE, "-");
1071 Error(ERR_WARN, "invalid value %d for '%s.height' (fallback done to %d)",
1072 g->height, getTokenFromImageID(graphic), TILEY);
1073 Error(ERR_INFO_LINE, "-");
1075 g->height = TILEY; /* will be checked to be inside bitmap later */
1081 /* get final bitmap size (with scaling, but without small images) */
1082 int src_image_width = get_scaled_graphic_width(graphic);
1083 int src_image_height = get_scaled_graphic_height(graphic);
1085 if (src_image_width == 0 || src_image_height == 0)
1087 /* only happens when loaded outside artwork system (like "global.busy") */
1088 src_image_width = src_bitmap->width;
1089 src_image_height = src_bitmap->height;
1092 anim_frames_per_row = src_image_width / g->width;
1093 anim_frames_per_col = src_image_height / g->height;
1095 g->src_image_width = src_image_width;
1096 g->src_image_height = src_image_height;
1099 /* correct x or y offset dependent of vertical or horizontal frame order */
1100 if (parameter[GFX_ARG_VERTICAL]) /* frames are ordered vertically */
1102 g->offset_y = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1103 parameter[GFX_ARG_OFFSET] : g->height);
1104 anim_frames_per_line = anim_frames_per_col;
1106 else /* frames are ordered horizontally */
1108 g->offset_x = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
1109 parameter[GFX_ARG_OFFSET] : g->width);
1110 anim_frames_per_line = anim_frames_per_row;
1113 /* optionally, the x and y offset of frames can be specified directly */
1114 if (parameter[GFX_ARG_XOFFSET] != ARG_UNDEFINED_VALUE)
1115 g->offset_x = parameter[GFX_ARG_XOFFSET];
1116 if (parameter[GFX_ARG_YOFFSET] != ARG_UNDEFINED_VALUE)
1117 g->offset_y = parameter[GFX_ARG_YOFFSET];
1119 /* optionally, moving animations may have separate start and end graphics */
1120 g->double_movement = parameter[GFX_ARG_2ND_MOVEMENT_TILE];
1122 if (parameter[GFX_ARG_2ND_VERTICAL] == ARG_UNDEFINED_VALUE)
1123 parameter[GFX_ARG_2ND_VERTICAL] = !parameter[GFX_ARG_VERTICAL];
1125 /* correct x or y offset2 dependent of vertical or horizontal frame order */
1126 if (parameter[GFX_ARG_2ND_VERTICAL]) /* frames are ordered vertically */
1127 g->offset2_y = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1128 parameter[GFX_ARG_2ND_OFFSET] : g->height);
1129 else /* frames are ordered horizontally */
1130 g->offset2_x = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
1131 parameter[GFX_ARG_2ND_OFFSET] : g->width);
1133 /* optionally, the x and y offset of 2nd graphic can be specified directly */
1134 if (parameter[GFX_ARG_2ND_XOFFSET] != ARG_UNDEFINED_VALUE)
1135 g->offset2_x = parameter[GFX_ARG_2ND_XOFFSET];
1136 if (parameter[GFX_ARG_2ND_YOFFSET] != ARG_UNDEFINED_VALUE)
1137 g->offset2_y = parameter[GFX_ARG_2ND_YOFFSET];
1139 /* optionally, the second movement tile can be specified as start tile */
1140 if (parameter[GFX_ARG_2ND_SWAP_TILES] != ARG_UNDEFINED_VALUE)
1141 g->swap_double_tiles= parameter[GFX_ARG_2ND_SWAP_TILES];
1143 /* automatically determine correct number of frames, if not defined */
1144 if (parameter[GFX_ARG_FRAMES] != ARG_UNDEFINED_VALUE)
1145 g->anim_frames = parameter[GFX_ARG_FRAMES];
1146 else if (parameter[GFX_ARG_XPOS] == 0 && !parameter[GFX_ARG_VERTICAL])
1147 g->anim_frames = anim_frames_per_row;
1148 else if (parameter[GFX_ARG_YPOS] == 0 && parameter[GFX_ARG_VERTICAL])
1149 g->anim_frames = anim_frames_per_col;
1153 if (g->anim_frames == 0) /* frames must be at least 1 */
1156 g->anim_frames_per_line =
1157 (parameter[GFX_ARG_FRAMES_PER_LINE] != ARG_UNDEFINED_VALUE ?
1158 parameter[GFX_ARG_FRAMES_PER_LINE] : anim_frames_per_line);
1160 g->anim_delay = parameter[GFX_ARG_DELAY];
1161 if (g->anim_delay == 0) /* delay must be at least 1 */
1164 g->anim_mode = parameter[GFX_ARG_ANIM_MODE];
1166 /* automatically determine correct start frame, if not defined */
1167 if (parameter[GFX_ARG_START_FRAME] == ARG_UNDEFINED_VALUE)
1168 g->anim_start_frame = 0;
1169 else if (g->anim_mode & ANIM_REVERSE)
1170 g->anim_start_frame = g->anim_frames - parameter[GFX_ARG_START_FRAME] - 1;
1172 g->anim_start_frame = parameter[GFX_ARG_START_FRAME];
1174 /* animation synchronized with global frame counter, not move position */
1175 g->anim_global_sync = parameter[GFX_ARG_GLOBAL_SYNC];
1177 /* optional element for cloning crumble graphics */
1178 if (parameter[GFX_ARG_CRUMBLED_LIKE] != ARG_UNDEFINED_VALUE)
1179 g->crumbled_like = parameter[GFX_ARG_CRUMBLED_LIKE];
1181 /* optional element for cloning digging graphics */
1182 if (parameter[GFX_ARG_DIGGABLE_LIKE] != ARG_UNDEFINED_VALUE)
1183 g->diggable_like = parameter[GFX_ARG_DIGGABLE_LIKE];
1185 /* optional border size for "crumbling" diggable graphics */
1186 if (parameter[GFX_ARG_BORDER_SIZE] != ARG_UNDEFINED_VALUE)
1187 g->border_size = parameter[GFX_ARG_BORDER_SIZE];
1189 /* this is only used for player "boring" and "sleeping" actions */
1190 if (parameter[GFX_ARG_ANIM_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1191 g->anim_delay_fixed = parameter[GFX_ARG_ANIM_DELAY_FIXED];
1192 if (parameter[GFX_ARG_ANIM_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1193 g->anim_delay_random = parameter[GFX_ARG_ANIM_DELAY_RANDOM];
1194 if (parameter[GFX_ARG_POST_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
1195 g->post_delay_fixed = parameter[GFX_ARG_POST_DELAY_FIXED];
1196 if (parameter[GFX_ARG_POST_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
1197 g->post_delay_random = parameter[GFX_ARG_POST_DELAY_RANDOM];
1199 /* this is only used for toon animations */
1200 g->step_offset = parameter[GFX_ARG_STEP_OFFSET];
1201 g->step_delay = parameter[GFX_ARG_STEP_DELAY];
1203 /* this is only used for drawing font characters */
1204 g->draw_xoffset = parameter[GFX_ARG_DRAW_XOFFSET];
1205 g->draw_yoffset = parameter[GFX_ARG_DRAW_YOFFSET];
1207 /* this is only used for drawing envelope graphics */
1208 g->draw_masked = parameter[GFX_ARG_DRAW_MASKED];
1210 /* optional graphic for cloning all graphics settings */
1211 if (parameter[GFX_ARG_CLONE_FROM] != ARG_UNDEFINED_VALUE)
1212 g->clone_from = parameter[GFX_ARG_CLONE_FROM];
1214 /* optional settings for drawing title screens and title messages */
1215 if (parameter[GFX_ARG_FADE_MODE] != ARG_UNDEFINED_VALUE)
1216 g->fade_mode = parameter[GFX_ARG_FADE_MODE];
1217 if (parameter[GFX_ARG_FADE_DELAY] != ARG_UNDEFINED_VALUE)
1218 g->fade_delay = parameter[GFX_ARG_FADE_DELAY];
1219 if (parameter[GFX_ARG_POST_DELAY] != ARG_UNDEFINED_VALUE)
1220 g->post_delay = parameter[GFX_ARG_POST_DELAY];
1221 if (parameter[GFX_ARG_AUTO_DELAY] != ARG_UNDEFINED_VALUE)
1222 g->auto_delay = parameter[GFX_ARG_AUTO_DELAY];
1223 if (parameter[GFX_ARG_ALIGN] != ARG_UNDEFINED_VALUE)
1224 g->align = parameter[GFX_ARG_ALIGN];
1225 if (parameter[GFX_ARG_VALIGN] != ARG_UNDEFINED_VALUE)
1226 g->valign = parameter[GFX_ARG_VALIGN];
1227 if (parameter[GFX_ARG_SORT_PRIORITY] != ARG_UNDEFINED_VALUE)
1228 g->sort_priority = parameter[GFX_ARG_SORT_PRIORITY];
1230 if (parameter[GFX_ARG_CLASS] != ARG_UNDEFINED_VALUE)
1231 g->class = parameter[GFX_ARG_CLASS];
1232 if (parameter[GFX_ARG_STYLE] != ARG_UNDEFINED_VALUE)
1233 g->style = parameter[GFX_ARG_STYLE];
1235 /* this is only used for drawing menu buttons and text */
1236 g->active_xoffset = parameter[GFX_ARG_ACTIVE_XOFFSET];
1237 g->active_yoffset = parameter[GFX_ARG_ACTIVE_YOFFSET];
1238 g->pressed_xoffset = parameter[GFX_ARG_PRESSED_XOFFSET];
1239 g->pressed_yoffset = parameter[GFX_ARG_PRESSED_YOFFSET];
1242 static void set_graphic_parameters(int graphic)
1244 struct FileInfo *image = getImageListEntryFromImageID(graphic);
1245 char **parameter_raw = image->parameter;
1246 Bitmap *src_bitmap = getBitmapFromImageID(graphic);
1247 int parameter[NUM_GFX_ARGS];
1250 /* if fallback to default artwork is done, also use the default parameters */
1251 if (image->fallback_to_default)
1252 parameter_raw = image->default_parameter;
1254 /* get integer values from string parameters */
1255 for (i = 0; i < NUM_GFX_ARGS; i++)
1256 parameter[i] = get_graphic_parameter_value(parameter_raw[i],
1257 image_config_suffix[i].token,
1258 image_config_suffix[i].type);
1260 set_graphic_parameters_ext(graphic, parameter, src_bitmap);
1262 UPDATE_BUSY_STATE();
1265 static void set_cloned_graphic_parameters(int graphic)
1267 int fallback_graphic = IMG_CHAR_EXCLAM;
1268 int max_num_images = getImageListSize();
1269 int clone_graphic = graphic_info[graphic].clone_from;
1270 int num_references_followed = 1;
1272 while (graphic_info[clone_graphic].clone_from != -1 &&
1273 num_references_followed < max_num_images)
1275 clone_graphic = graphic_info[clone_graphic].clone_from;
1277 num_references_followed++;
1280 if (num_references_followed >= max_num_images)
1282 Error(ERR_INFO_LINE, "-");
1283 Error(ERR_INFO, "warning: error found in config file:");
1284 Error(ERR_INFO, "- config file: '%s'", getImageConfigFilename());
1285 Error(ERR_INFO, "- config token: '%s'", getTokenFromImageID(graphic));
1286 Error(ERR_INFO, "error: loop discovered when resolving cloned graphics");
1287 Error(ERR_INFO, "custom graphic rejected for this element/action");
1289 if (graphic == fallback_graphic)
1290 Error(ERR_EXIT, "fatal error: no fallback graphic available");
1292 Error(ERR_INFO, "fallback done to 'char_exclam' for this graphic");
1293 Error(ERR_INFO_LINE, "-");
1295 graphic_info[graphic] = graphic_info[fallback_graphic];
1299 graphic_info[graphic] = graphic_info[clone_graphic];
1300 graphic_info[graphic].clone_from = clone_graphic;
1304 static void InitGraphicInfo()
1306 int fallback_graphic = IMG_CHAR_EXCLAM;
1307 int num_images = getImageListSize();
1310 /* use image size as default values for width and height for these images */
1311 static int full_size_graphics[] =
1316 IMG_BACKGROUND_ENVELOPE_1,
1317 IMG_BACKGROUND_ENVELOPE_2,
1318 IMG_BACKGROUND_ENVELOPE_3,
1319 IMG_BACKGROUND_ENVELOPE_4,
1320 IMG_BACKGROUND_REQUEST,
1323 IMG_BACKGROUND_TITLE_INITIAL,
1324 IMG_BACKGROUND_TITLE,
1325 IMG_BACKGROUND_MAIN,
1326 IMG_BACKGROUND_LEVELS,
1327 IMG_BACKGROUND_LEVELNR,
1328 IMG_BACKGROUND_SCORES,
1329 IMG_BACKGROUND_EDITOR,
1330 IMG_BACKGROUND_INFO,
1331 IMG_BACKGROUND_INFO_ELEMENTS,
1332 IMG_BACKGROUND_INFO_MUSIC,
1333 IMG_BACKGROUND_INFO_CREDITS,
1334 IMG_BACKGROUND_INFO_PROGRAM,
1335 IMG_BACKGROUND_INFO_VERSION,
1336 IMG_BACKGROUND_INFO_LEVELSET,
1337 IMG_BACKGROUND_SETUP,
1338 IMG_BACKGROUND_PLAYING,
1339 IMG_BACKGROUND_DOOR,
1340 IMG_BACKGROUND_TAPE,
1341 IMG_BACKGROUND_PANEL,
1342 IMG_BACKGROUND_PALETTE,
1343 IMG_BACKGROUND_TOOLBOX,
1345 IMG_TITLESCREEN_INITIAL_1,
1346 IMG_TITLESCREEN_INITIAL_2,
1347 IMG_TITLESCREEN_INITIAL_3,
1348 IMG_TITLESCREEN_INITIAL_4,
1349 IMG_TITLESCREEN_INITIAL_5,
1356 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_1,
1357 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_2,
1358 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_3,
1359 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_4,
1360 IMG_BACKGROUND_TITLEMESSAGE_INITIAL_5,
1361 IMG_BACKGROUND_TITLEMESSAGE_1,
1362 IMG_BACKGROUND_TITLEMESSAGE_2,
1363 IMG_BACKGROUND_TITLEMESSAGE_3,
1364 IMG_BACKGROUND_TITLEMESSAGE_4,
1365 IMG_BACKGROUND_TITLEMESSAGE_5,
1370 checked_free(graphic_info);
1372 graphic_info = checked_calloc(num_images * sizeof(struct GraphicInfo));
1374 /* initialize "use_image_size" flag with default value */
1375 for (i = 0; i < num_images; i++)
1376 graphic_info[i].use_image_size = FALSE;
1378 /* initialize "use_image_size" flag from static configuration above */
1379 for (i = 0; full_size_graphics[i] != -1; i++)
1380 graphic_info[full_size_graphics[i]].use_image_size = TRUE;
1382 /* first set all graphic paramaters ... */
1383 for (i = 0; i < num_images; i++)
1384 set_graphic_parameters(i);
1386 /* ... then copy these parameters for cloned graphics */
1387 for (i = 0; i < num_images; i++)
1388 if (graphic_info[i].clone_from != -1)
1389 set_cloned_graphic_parameters(i);
1391 for (i = 0; i < num_images; i++)
1396 int first_frame, last_frame;
1397 int src_bitmap_width, src_bitmap_height;
1399 /* now check if no animation frames are outside of the loaded image */
1401 if (graphic_info[i].bitmap == NULL)
1402 continue; /* skip check for optional images that are undefined */
1404 /* get image size (this can differ from the standard element tile size!) */
1405 width = graphic_info[i].width;
1406 height = graphic_info[i].height;
1408 /* get final bitmap size (with scaling, but without small images) */
1409 src_bitmap_width = graphic_info[i].src_image_width;
1410 src_bitmap_height = graphic_info[i].src_image_height;
1412 /* check if first animation frame is inside specified bitmap */
1415 getFixedGraphicSource(i, first_frame, &src_bitmap, &src_x, &src_y);
1417 /* this avoids calculating wrong start position for out-of-bounds frame */
1418 src_x = graphic_info[i].src_x;
1419 src_y = graphic_info[i].src_y;
1421 if (src_x < 0 || src_y < 0 ||
1422 src_x + width > src_bitmap_width ||
1423 src_y + height > src_bitmap_height)
1425 Error(ERR_INFO_LINE, "-");
1426 Error(ERR_INFO, "warning: error found in config file:");
1427 Error(ERR_INFO, "- config file: '%s'", getImageConfigFilename());
1428 Error(ERR_INFO, "- config token: '%s'", getTokenFromImageID(i));
1429 Error(ERR_INFO, "- image file: '%s'", src_bitmap->source_filename);
1431 "error: first animation frame out of bounds (%d, %d) [%d, %d]",
1432 src_x, src_y, src_bitmap_width, src_bitmap_height);
1433 Error(ERR_INFO, "custom graphic rejected for this element/action");
1435 if (i == fallback_graphic)
1436 Error(ERR_EXIT, "fatal error: no fallback graphic available");
1438 Error(ERR_INFO, "fallback done to 'char_exclam' for this graphic");
1439 Error(ERR_INFO_LINE, "-");
1441 graphic_info[i] = graphic_info[fallback_graphic];
1444 /* check if last animation frame is inside specified bitmap */
1446 last_frame = graphic_info[i].anim_frames - 1;
1447 getFixedGraphicSource(i, last_frame, &src_bitmap, &src_x, &src_y);
1449 if (src_x < 0 || src_y < 0 ||
1450 src_x + width > src_bitmap_width ||
1451 src_y + height > src_bitmap_height)
1453 Error(ERR_INFO_LINE, "-");
1454 Error(ERR_INFO, "warning: error found in config file:");
1455 Error(ERR_INFO, "- config file: '%s'", getImageConfigFilename());
1456 Error(ERR_INFO, "- config token: '%s'", getTokenFromImageID(i));
1457 Error(ERR_INFO, "- image file: '%s'", src_bitmap->source_filename);
1459 "error: last animation frame (%d) out of bounds (%d, %d) [%d, %d]",
1460 last_frame, src_x, src_y, src_bitmap_width, src_bitmap_height);
1461 Error(ERR_INFO, "::: %d, %d", width, height);
1462 Error(ERR_INFO, "custom graphic rejected for this element/action");
1464 if (i == fallback_graphic)
1465 Error(ERR_EXIT, "fatal error: no fallback graphic available");
1467 Error(ERR_INFO, "fallback done to 'char_exclam' for this graphic");
1468 Error(ERR_INFO_LINE, "-");
1470 graphic_info[i] = graphic_info[fallback_graphic];
1475 static void InitGraphicCompatibilityInfo()
1477 struct FileInfo *fi_global_door =
1478 getImageListEntryFromImageID(IMG_GLOBAL_DOOR);
1479 int num_images = getImageListSize();
1482 /* the following compatibility handling is needed for the following case:
1483 versions up to 3.3.0.0 used one large bitmap "global.door" for various
1484 graphics mainly used for door and panel graphics, like editor, tape and
1485 in-game buttons with hard-coded bitmap positions and button sizes; as
1486 these graphics now have individual definitions, redefining "global.door"
1487 to change all these graphics at once like before does not work anymore
1488 (because all those individual definitions still have their default values);
1489 to solve this, remap all those individual definitions that are not
1490 redefined to the new bitmap of "global.door" if it was redefined */
1492 /* special compatibility handling if image "global.door" was redefined */
1493 if (fi_global_door->redefined)
1495 for (i = 0; i < num_images; i++)
1497 struct FileInfo *fi = getImageListEntryFromImageID(i);
1499 /* process only those images that still use the default settings */
1502 /* process all images which default to same image as "global.door" */
1503 if (strEqual(fi->default_filename, fi_global_door->default_filename))
1505 // printf("::: special treatment needed for token '%s'\n", fi->token);
1507 graphic_info[i].bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
1513 InitGraphicCompatibilityInfo_Doors();
1516 static void InitElementSoundInfo()
1518 struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
1519 int num_property_mappings = getSoundListPropertyMappingSize();
1522 /* set values to -1 to identify later as "uninitialized" values */
1523 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1524 for (act = 0; act < NUM_ACTIONS; act++)
1525 element_info[i].sound[act] = -1;
1527 /* initialize element/sound mapping from static configuration */
1528 for (i = 0; element_to_sound[i].element > -1; i++)
1530 int element = element_to_sound[i].element;
1531 int action = element_to_sound[i].action;
1532 int sound = element_to_sound[i].sound;
1533 boolean is_class = element_to_sound[i].is_class;
1536 action = ACTION_DEFAULT;
1539 element_info[element].sound[action] = sound;
1541 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
1542 if (strEqual(element_info[j].class_name,
1543 element_info[element].class_name))
1544 element_info[j].sound[action] = sound;
1547 /* initialize element class/sound mapping from dynamic configuration */
1548 for (i = 0; i < num_property_mappings; i++)
1550 int element_class = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
1551 int action = property_mapping[i].ext1_index;
1552 int sound = property_mapping[i].artwork_index;
1554 if (element_class < 0 || element_class >= MAX_NUM_ELEMENTS)
1558 action = ACTION_DEFAULT;
1560 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
1561 if (strEqual(element_info[j].class_name,
1562 element_info[element_class].class_name))
1563 element_info[j].sound[action] = sound;
1566 /* initialize element/sound mapping from dynamic configuration */
1567 for (i = 0; i < num_property_mappings; i++)
1569 int element = property_mapping[i].base_index;
1570 int action = property_mapping[i].ext1_index;
1571 int sound = property_mapping[i].artwork_index;
1573 if (element >= MAX_NUM_ELEMENTS)
1577 action = ACTION_DEFAULT;
1579 element_info[element].sound[action] = sound;
1582 /* now set all '-1' values to element specific default values */
1583 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1585 for (act = 0; act < NUM_ACTIONS; act++)
1587 /* generic default action sound (defined by "[default]" directive) */
1588 int default_action_sound = element_info[EL_DEFAULT].sound[act];
1590 /* look for special default action sound (classic game specific) */
1591 if (IS_BD_ELEMENT(i) && element_info[EL_BD_DEFAULT].sound[act] != -1)
1592 default_action_sound = element_info[EL_BD_DEFAULT].sound[act];
1593 if (IS_SP_ELEMENT(i) && element_info[EL_SP_DEFAULT].sound[act] != -1)
1594 default_action_sound = element_info[EL_SP_DEFAULT].sound[act];
1595 if (IS_SB_ELEMENT(i) && element_info[EL_SB_DEFAULT].sound[act] != -1)
1596 default_action_sound = element_info[EL_SB_DEFAULT].sound[act];
1598 /* !!! needed because EL_EMPTY_SPACE treated as IS_SP_ELEMENT !!! */
1599 /* !!! make this better !!! */
1600 if (i == EL_EMPTY_SPACE)
1601 default_action_sound = element_info[EL_DEFAULT].sound[act];
1603 /* no sound for this specific action -- use default action sound */
1604 if (element_info[i].sound[act] == -1)
1605 element_info[i].sound[act] = default_action_sound;
1609 /* copy sound settings to some elements that are only stored in level file
1610 in native R'n'D levels, but are used by game engine in native EM levels */
1611 for (i = 0; copy_properties[i][0] != -1; i++)
1612 for (j = 1; j <= 4; j++)
1613 for (act = 0; act < NUM_ACTIONS; act++)
1614 element_info[copy_properties[i][j]].sound[act] =
1615 element_info[copy_properties[i][0]].sound[act];
1618 static void InitGameModeSoundInfo()
1622 /* set values to -1 to identify later as "uninitialized" values */
1623 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
1626 /* initialize gamemode/sound mapping from static configuration */
1627 for (i = 0; gamemode_to_sound[i].sound > -1; i++)
1629 int gamemode = gamemode_to_sound[i].gamemode;
1630 int sound = gamemode_to_sound[i].sound;
1633 gamemode = GAME_MODE_DEFAULT;
1635 menu.sound[gamemode] = sound;
1638 /* now set all '-1' values to levelset specific default values */
1639 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
1640 if (menu.sound[i] == -1)
1641 menu.sound[i] = menu.sound[GAME_MODE_DEFAULT];
1644 static void set_sound_parameters(int sound, char **parameter_raw)
1646 int parameter[NUM_SND_ARGS];
1649 /* get integer values from string parameters */
1650 for (i = 0; i < NUM_SND_ARGS; i++)
1652 get_parameter_value(parameter_raw[i],
1653 sound_config_suffix[i].token,
1654 sound_config_suffix[i].type);
1656 /* explicit loop mode setting in configuration overrides default value */
1657 if (parameter[SND_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
1658 sound_info[sound].loop = parameter[SND_ARG_MODE_LOOP];
1660 /* sound volume to change the original volume when loading the sound file */
1661 sound_info[sound].volume = parameter[SND_ARG_VOLUME];
1663 /* sound priority to give certain sounds a higher or lower priority */
1664 sound_info[sound].priority = parameter[SND_ARG_PRIORITY];
1667 static void InitSoundInfo()
1669 int *sound_effect_properties;
1670 int num_sounds = getSoundListSize();
1673 checked_free(sound_info);
1675 sound_effect_properties = checked_calloc(num_sounds * sizeof(int));
1676 sound_info = checked_calloc(num_sounds * sizeof(struct SoundInfo));
1678 /* initialize sound effect for all elements to "no sound" */
1679 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1680 for (j = 0; j < NUM_ACTIONS; j++)
1681 element_info[i].sound[j] = SND_UNDEFINED;
1683 for (i = 0; i < num_sounds; i++)
1685 struct FileInfo *sound = getSoundListEntry(i);
1686 int len_effect_text = strlen(sound->token);
1688 sound_effect_properties[i] = ACTION_OTHER;
1689 sound_info[i].loop = FALSE; /* default: play sound only once */
1691 /* determine all loop sounds and identify certain sound classes */
1693 for (j = 0; element_action_info[j].suffix; j++)
1695 int len_action_text = strlen(element_action_info[j].suffix);
1697 if (len_action_text < len_effect_text &&
1698 strEqual(&sound->token[len_effect_text - len_action_text],
1699 element_action_info[j].suffix))
1701 sound_effect_properties[i] = element_action_info[j].value;
1702 sound_info[i].loop = element_action_info[j].is_loop_sound;
1708 /* associate elements and some selected sound actions */
1710 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
1712 if (element_info[j].class_name)
1714 int len_class_text = strlen(element_info[j].class_name);
1716 if (len_class_text + 1 < len_effect_text &&
1717 strncmp(sound->token,
1718 element_info[j].class_name, len_class_text) == 0 &&
1719 sound->token[len_class_text] == '.')
1721 int sound_action_value = sound_effect_properties[i];
1723 element_info[j].sound[sound_action_value] = i;
1728 set_sound_parameters(i, sound->parameter);
1731 free(sound_effect_properties);
1734 static void InitGameModeMusicInfo()
1736 struct PropertyMapping *property_mapping = getMusicListPropertyMapping();
1737 int num_property_mappings = getMusicListPropertyMappingSize();
1738 int default_levelset_music = -1;
1741 /* set values to -1 to identify later as "uninitialized" values */
1742 for (i = 0; i < MAX_LEVELS; i++)
1743 levelset.music[i] = -1;
1744 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
1747 /* initialize gamemode/music mapping from static configuration */
1748 for (i = 0; gamemode_to_music[i].music > -1; i++)
1750 int gamemode = gamemode_to_music[i].gamemode;
1751 int music = gamemode_to_music[i].music;
1754 gamemode = GAME_MODE_DEFAULT;
1756 menu.music[gamemode] = music;
1759 /* initialize gamemode/music mapping from dynamic configuration */
1760 for (i = 0; i < num_property_mappings; i++)
1762 int prefix = property_mapping[i].base_index;
1763 int gamemode = property_mapping[i].ext1_index;
1764 int level = property_mapping[i].ext2_index;
1765 int music = property_mapping[i].artwork_index;
1767 if (prefix < 0 || prefix >= NUM_MUSIC_PREFIXES)
1771 gamemode = GAME_MODE_DEFAULT;
1773 /* level specific music only allowed for in-game music */
1774 if (level != -1 && gamemode == GAME_MODE_DEFAULT)
1775 gamemode = GAME_MODE_PLAYING;
1780 default_levelset_music = music;
1783 if (gamemode == GAME_MODE_PLAYING || gamemode == GAME_MODE_DEFAULT)
1784 levelset.music[level] = music;
1785 if (gamemode != GAME_MODE_PLAYING)
1786 menu.music[gamemode] = music;
1789 /* now set all '-1' values to menu specific default values */
1790 /* (undefined values of "levelset.music[]" might stay at "-1" to
1791 allow dynamic selection of music files from music directory!) */
1792 for (i = 0; i < MAX_LEVELS; i++)
1793 if (levelset.music[i] == -1)
1794 levelset.music[i] = default_levelset_music;
1795 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
1796 if (menu.music[i] == -1)
1797 menu.music[i] = menu.music[GAME_MODE_DEFAULT];
1800 static void set_music_parameters(int music, char **parameter_raw)
1802 int parameter[NUM_MUS_ARGS];
1805 /* get integer values from string parameters */
1806 for (i = 0; i < NUM_MUS_ARGS; i++)
1808 get_parameter_value(parameter_raw[i],
1809 music_config_suffix[i].token,
1810 music_config_suffix[i].type);
1812 /* explicit loop mode setting in configuration overrides default value */
1813 if (parameter[MUS_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
1814 music_info[music].loop = parameter[MUS_ARG_MODE_LOOP];
1817 static void InitMusicInfo()
1819 int num_music = getMusicListSize();
1822 checked_free(music_info);
1824 music_info = checked_calloc(num_music * sizeof(struct MusicInfo));
1826 for (i = 0; i < num_music; i++)
1828 struct FileInfo *music = getMusicListEntry(i);
1829 int len_music_text = strlen(music->token);
1831 music_info[i].loop = TRUE; /* default: play music in loop mode */
1833 /* determine all loop music */
1835 for (j = 0; music_prefix_info[j].prefix; j++)
1837 int len_prefix_text = strlen(music_prefix_info[j].prefix);
1839 if (len_prefix_text < len_music_text &&
1840 strncmp(music->token,
1841 music_prefix_info[j].prefix, len_prefix_text) == 0)
1843 music_info[i].loop = music_prefix_info[j].is_loop_music;
1849 set_music_parameters(i, music->parameter);
1853 static void ReinitializeGraphics()
1855 print_timestamp_init("ReinitializeGraphics");
1857 InitGfxTileSizeInfo(game.tile_size, TILESIZE);
1859 InitGraphicInfo(); /* graphic properties mapping */
1860 print_timestamp_time("InitGraphicInfo");
1861 InitElementGraphicInfo(); /* element game graphic mapping */
1862 print_timestamp_time("InitElementGraphicInfo");
1863 InitElementSpecialGraphicInfo(); /* element special graphic mapping */
1864 print_timestamp_time("InitElementSpecialGraphicInfo");
1866 InitElementSmallImages(); /* scale elements to all needed sizes */
1867 print_timestamp_time("InitElementSmallImages");
1868 InitScaledImages(); /* scale all other images, if needed */
1869 print_timestamp_time("InitScaledImages");
1870 InitFontGraphicInfo(); /* initialize text drawing functions */
1871 print_timestamp_time("InitFontGraphicInfo");
1873 InitGraphicInfo_EM(); /* graphic mapping for EM engine */
1874 print_timestamp_time("InitGraphicInfo_EM");
1876 InitGraphicCompatibilityInfo();
1877 print_timestamp_time("InitGraphicCompatibilityInfo");
1879 SetMainBackgroundImage(IMG_BACKGROUND);
1880 print_timestamp_time("SetMainBackgroundImage");
1881 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
1882 print_timestamp_time("SetDoorBackgroundImage");
1885 print_timestamp_time("InitGadgets");
1887 print_timestamp_time("InitToons");
1889 print_timestamp_time("InitDoors");
1891 print_timestamp_done("ReinitializeGraphics");
1894 static void ReinitializeSounds()
1896 InitSoundInfo(); /* sound properties mapping */
1897 InitElementSoundInfo(); /* element game sound mapping */
1898 InitGameModeSoundInfo(); /* game mode sound mapping */
1900 InitPlayLevelSound(); /* internal game sound settings */
1903 static void ReinitializeMusic()
1905 InitMusicInfo(); /* music properties mapping */
1906 InitGameModeMusicInfo(); /* game mode music mapping */
1909 static int get_special_property_bit(int element, int property_bit_nr)
1911 struct PropertyBitInfo
1917 static struct PropertyBitInfo pb_can_move_into_acid[] =
1919 /* the player may be able fall into acid when gravity is activated */
1924 { EL_SP_MURPHY, 0 },
1925 { EL_SOKOBAN_FIELD_PLAYER, 0 },
1927 /* all elements that can move may be able to also move into acid */
1930 { EL_BUG_RIGHT, 1 },
1933 { EL_SPACESHIP, 2 },
1934 { EL_SPACESHIP_LEFT, 2 },
1935 { EL_SPACESHIP_RIGHT, 2 },
1936 { EL_SPACESHIP_UP, 2 },
1937 { EL_SPACESHIP_DOWN, 2 },
1938 { EL_BD_BUTTERFLY, 3 },
1939 { EL_BD_BUTTERFLY_LEFT, 3 },
1940 { EL_BD_BUTTERFLY_RIGHT, 3 },
1941 { EL_BD_BUTTERFLY_UP, 3 },
1942 { EL_BD_BUTTERFLY_DOWN, 3 },
1943 { EL_BD_FIREFLY, 4 },
1944 { EL_BD_FIREFLY_LEFT, 4 },
1945 { EL_BD_FIREFLY_RIGHT, 4 },
1946 { EL_BD_FIREFLY_UP, 4 },
1947 { EL_BD_FIREFLY_DOWN, 4 },
1949 { EL_YAMYAM_LEFT, 5 },
1950 { EL_YAMYAM_RIGHT, 5 },
1951 { EL_YAMYAM_UP, 5 },
1952 { EL_YAMYAM_DOWN, 5 },
1953 { EL_DARK_YAMYAM, 6 },
1956 { EL_PACMAN_LEFT, 8 },
1957 { EL_PACMAN_RIGHT, 8 },
1958 { EL_PACMAN_UP, 8 },
1959 { EL_PACMAN_DOWN, 8 },
1961 { EL_MOLE_LEFT, 9 },
1962 { EL_MOLE_RIGHT, 9 },
1964 { EL_MOLE_DOWN, 9 },
1968 { EL_SATELLITE, 13 },
1969 { EL_SP_SNIKSNAK, 14 },
1970 { EL_SP_ELECTRON, 15 },
1973 { EL_EMC_ANDROID, 18 },
1978 static struct PropertyBitInfo pb_dont_collide_with[] =
1980 { EL_SP_SNIKSNAK, 0 },
1981 { EL_SP_ELECTRON, 1 },
1989 struct PropertyBitInfo *pb_info;
1992 { EP_CAN_MOVE_INTO_ACID, pb_can_move_into_acid },
1993 { EP_DONT_COLLIDE_WITH, pb_dont_collide_with },
1998 struct PropertyBitInfo *pb_info = NULL;
2001 for (i = 0; pb_definition[i].bit_nr != -1; i++)
2002 if (pb_definition[i].bit_nr == property_bit_nr)
2003 pb_info = pb_definition[i].pb_info;
2005 if (pb_info == NULL)
2008 for (i = 0; pb_info[i].element != -1; i++)
2009 if (pb_info[i].element == element)
2010 return pb_info[i].bit_nr;
2015 void setBitfieldProperty(int *bitfield, int property_bit_nr, int element,
2016 boolean property_value)
2018 int bit_nr = get_special_property_bit(element, property_bit_nr);
2023 *bitfield |= (1 << bit_nr);
2025 *bitfield &= ~(1 << bit_nr);
2029 boolean getBitfieldProperty(int *bitfield, int property_bit_nr, int element)
2031 int bit_nr = get_special_property_bit(element, property_bit_nr);
2034 return ((*bitfield & (1 << bit_nr)) != 0);
2039 static void ResolveGroupElementExt(int group_element, int recursion_depth)
2041 static int group_nr;
2042 static struct ElementGroupInfo *group;
2043 struct ElementGroupInfo *actual_group = element_info[group_element].group;
2046 if (actual_group == NULL) /* not yet initialized */
2049 if (recursion_depth > NUM_GROUP_ELEMENTS) /* recursion too deep */
2051 Error(ERR_WARN, "recursion too deep when resolving group element %d",
2052 group_element - EL_GROUP_START + 1);
2054 /* replace element which caused too deep recursion by question mark */
2055 group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
2060 if (recursion_depth == 0) /* initialization */
2062 group = actual_group;
2063 group_nr = GROUP_NR(group_element);
2065 group->num_elements_resolved = 0;
2066 group->choice_pos = 0;
2068 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2069 element_info[i].in_group[group_nr] = FALSE;
2072 for (i = 0; i < actual_group->num_elements; i++)
2074 int element = actual_group->element[i];
2076 if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
2079 if (IS_GROUP_ELEMENT(element))
2080 ResolveGroupElementExt(element, recursion_depth + 1);
2083 group->element_resolved[group->num_elements_resolved++] = element;
2084 element_info[element].in_group[group_nr] = TRUE;
2089 void ResolveGroupElement(int group_element)
2091 ResolveGroupElementExt(group_element, 0);
2094 void InitElementPropertiesStatic()
2096 static boolean clipboard_elements_initialized = FALSE;
2098 static int ep_diggable[] =
2103 EL_SP_BUGGY_BASE_ACTIVATING,
2106 EL_INVISIBLE_SAND_ACTIVE,
2109 /* !!! currently not diggable, but handled by 'ep_dont_run_into' !!! */
2110 /* (if amoeba can grow into anything diggable, maybe keep these out) */
2115 EL_SP_BUGGY_BASE_ACTIVE,
2122 static int ep_collectible_only[] =
2144 EL_DYNABOMB_INCREASE_NUMBER,
2145 EL_DYNABOMB_INCREASE_SIZE,
2146 EL_DYNABOMB_INCREASE_POWER,
2164 /* !!! handle separately !!! */
2165 EL_DC_LANDMINE, /* deadly when running into, but can be snapped */
2171 static int ep_dont_run_into[] =
2173 /* same elements as in 'ep_dont_touch' */
2179 /* same elements as in 'ep_dont_collide_with' */
2191 /* !!! maybe this should better be handled by 'ep_diggable' !!! */
2196 EL_SP_BUGGY_BASE_ACTIVE,
2203 static int ep_dont_collide_with[] =
2205 /* same elements as in 'ep_dont_touch' */
2222 static int ep_dont_touch[] =
2232 static int ep_indestructible[] =
2236 EL_ACID_POOL_TOPLEFT,
2237 EL_ACID_POOL_TOPRIGHT,
2238 EL_ACID_POOL_BOTTOMLEFT,
2239 EL_ACID_POOL_BOTTOM,
2240 EL_ACID_POOL_BOTTOMRIGHT,
2241 EL_SP_HARDWARE_GRAY,
2242 EL_SP_HARDWARE_GREEN,
2243 EL_SP_HARDWARE_BLUE,
2245 EL_SP_HARDWARE_YELLOW,
2246 EL_SP_HARDWARE_BASE_1,
2247 EL_SP_HARDWARE_BASE_2,
2248 EL_SP_HARDWARE_BASE_3,
2249 EL_SP_HARDWARE_BASE_4,
2250 EL_SP_HARDWARE_BASE_5,
2251 EL_SP_HARDWARE_BASE_6,
2252 EL_INVISIBLE_STEELWALL,
2253 EL_INVISIBLE_STEELWALL_ACTIVE,
2254 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2255 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
2256 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
2257 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2258 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
2259 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
2260 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2261 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
2262 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
2263 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
2264 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
2265 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
2267 EL_LIGHT_SWITCH_ACTIVE,
2268 EL_SIGN_EXCLAMATION,
2269 EL_SIGN_RADIOACTIVITY,
2276 EL_SIGN_ENTRY_FORBIDDEN,
2277 EL_SIGN_EMERGENCY_EXIT,
2285 EL_STEEL_EXIT_CLOSED,
2287 EL_STEEL_EXIT_OPENING,
2288 EL_STEEL_EXIT_CLOSING,
2289 EL_EM_STEEL_EXIT_CLOSED,
2290 EL_EM_STEEL_EXIT_OPEN,
2291 EL_EM_STEEL_EXIT_OPENING,
2292 EL_EM_STEEL_EXIT_CLOSING,
2293 EL_DC_STEELWALL_1_LEFT,
2294 EL_DC_STEELWALL_1_RIGHT,
2295 EL_DC_STEELWALL_1_TOP,
2296 EL_DC_STEELWALL_1_BOTTOM,
2297 EL_DC_STEELWALL_1_HORIZONTAL,
2298 EL_DC_STEELWALL_1_VERTICAL,
2299 EL_DC_STEELWALL_1_TOPLEFT,
2300 EL_DC_STEELWALL_1_TOPRIGHT,
2301 EL_DC_STEELWALL_1_BOTTOMLEFT,
2302 EL_DC_STEELWALL_1_BOTTOMRIGHT,
2303 EL_DC_STEELWALL_1_TOPLEFT_2,
2304 EL_DC_STEELWALL_1_TOPRIGHT_2,
2305 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
2306 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
2307 EL_DC_STEELWALL_2_LEFT,
2308 EL_DC_STEELWALL_2_RIGHT,
2309 EL_DC_STEELWALL_2_TOP,
2310 EL_DC_STEELWALL_2_BOTTOM,
2311 EL_DC_STEELWALL_2_HORIZONTAL,
2312 EL_DC_STEELWALL_2_VERTICAL,
2313 EL_DC_STEELWALL_2_MIDDLE,
2314 EL_DC_STEELWALL_2_SINGLE,
2315 EL_STEELWALL_SLIPPERY,
2329 EL_GATE_1_GRAY_ACTIVE,
2330 EL_GATE_2_GRAY_ACTIVE,
2331 EL_GATE_3_GRAY_ACTIVE,
2332 EL_GATE_4_GRAY_ACTIVE,
2341 EL_EM_GATE_1_GRAY_ACTIVE,
2342 EL_EM_GATE_2_GRAY_ACTIVE,
2343 EL_EM_GATE_3_GRAY_ACTIVE,
2344 EL_EM_GATE_4_GRAY_ACTIVE,
2353 EL_EMC_GATE_5_GRAY_ACTIVE,
2354 EL_EMC_GATE_6_GRAY_ACTIVE,
2355 EL_EMC_GATE_7_GRAY_ACTIVE,
2356 EL_EMC_GATE_8_GRAY_ACTIVE,
2358 EL_DC_GATE_WHITE_GRAY,
2359 EL_DC_GATE_WHITE_GRAY_ACTIVE,
2360 EL_DC_GATE_FAKE_GRAY,
2362 EL_SWITCHGATE_OPENING,
2363 EL_SWITCHGATE_CLOSED,
2364 EL_SWITCHGATE_CLOSING,
2365 EL_DC_SWITCHGATE_SWITCH_UP,
2366 EL_DC_SWITCHGATE_SWITCH_DOWN,
2368 EL_TIMEGATE_OPENING,
2370 EL_TIMEGATE_CLOSING,
2371 EL_DC_TIMEGATE_SWITCH,
2372 EL_DC_TIMEGATE_SWITCH_ACTIVE,
2376 EL_TUBE_VERTICAL_LEFT,
2377 EL_TUBE_VERTICAL_RIGHT,
2378 EL_TUBE_HORIZONTAL_UP,
2379 EL_TUBE_HORIZONTAL_DOWN,
2384 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
2385 EL_EXPANDABLE_STEELWALL_VERTICAL,
2386 EL_EXPANDABLE_STEELWALL_ANY,
2391 static int ep_slippery[] =
2405 EL_ROBOT_WHEEL_ACTIVE,
2411 EL_ACID_POOL_TOPLEFT,
2412 EL_ACID_POOL_TOPRIGHT,
2422 EL_STEELWALL_SLIPPERY,
2425 EL_EMC_WALL_SLIPPERY_1,
2426 EL_EMC_WALL_SLIPPERY_2,
2427 EL_EMC_WALL_SLIPPERY_3,
2428 EL_EMC_WALL_SLIPPERY_4,
2430 EL_EMC_MAGIC_BALL_ACTIVE,
2435 static int ep_can_change[] =
2440 static int ep_can_move[] =
2442 /* same elements as in 'pb_can_move_into_acid' */
2465 static int ep_can_fall[] =
2479 EL_QUICKSAND_FAST_FULL,
2481 EL_BD_MAGIC_WALL_FULL,
2482 EL_DC_MAGIC_WALL_FULL,
2496 static int ep_can_smash_player[] =
2522 static int ep_can_smash_enemies[] =
2531 static int ep_can_smash_everything[] =
2540 static int ep_explodes_by_fire[] =
2542 /* same elements as in 'ep_explodes_impact' */
2547 /* same elements as in 'ep_explodes_smashed' */
2557 EL_EM_DYNAMITE_ACTIVE,
2558 EL_DYNABOMB_PLAYER_1_ACTIVE,
2559 EL_DYNABOMB_PLAYER_2_ACTIVE,
2560 EL_DYNABOMB_PLAYER_3_ACTIVE,
2561 EL_DYNABOMB_PLAYER_4_ACTIVE,
2562 EL_DYNABOMB_INCREASE_NUMBER,
2563 EL_DYNABOMB_INCREASE_SIZE,
2564 EL_DYNABOMB_INCREASE_POWER,
2565 EL_SP_DISK_RED_ACTIVE,
2579 static int ep_explodes_smashed[] =
2581 /* same elements as in 'ep_explodes_impact' */
2595 static int ep_explodes_impact[] =
2604 static int ep_walkable_over[] =
2608 EL_SOKOBAN_FIELD_EMPTY,
2615 EL_EM_STEEL_EXIT_OPEN,
2616 EL_EM_STEEL_EXIT_OPENING,
2625 EL_GATE_1_GRAY_ACTIVE,
2626 EL_GATE_2_GRAY_ACTIVE,
2627 EL_GATE_3_GRAY_ACTIVE,
2628 EL_GATE_4_GRAY_ACTIVE,
2636 static int ep_walkable_inside[] =
2641 EL_TUBE_VERTICAL_LEFT,
2642 EL_TUBE_VERTICAL_RIGHT,
2643 EL_TUBE_HORIZONTAL_UP,
2644 EL_TUBE_HORIZONTAL_DOWN,
2653 static int ep_walkable_under[] =
2658 static int ep_passable_over[] =
2668 EL_EM_GATE_1_GRAY_ACTIVE,
2669 EL_EM_GATE_2_GRAY_ACTIVE,
2670 EL_EM_GATE_3_GRAY_ACTIVE,
2671 EL_EM_GATE_4_GRAY_ACTIVE,
2680 EL_EMC_GATE_5_GRAY_ACTIVE,
2681 EL_EMC_GATE_6_GRAY_ACTIVE,
2682 EL_EMC_GATE_7_GRAY_ACTIVE,
2683 EL_EMC_GATE_8_GRAY_ACTIVE,
2685 EL_DC_GATE_WHITE_GRAY,
2686 EL_DC_GATE_WHITE_GRAY_ACTIVE,
2693 static int ep_passable_inside[] =
2699 EL_SP_PORT_HORIZONTAL,
2700 EL_SP_PORT_VERTICAL,
2702 EL_SP_GRAVITY_PORT_LEFT,
2703 EL_SP_GRAVITY_PORT_RIGHT,
2704 EL_SP_GRAVITY_PORT_UP,
2705 EL_SP_GRAVITY_PORT_DOWN,
2706 EL_SP_GRAVITY_ON_PORT_LEFT,
2707 EL_SP_GRAVITY_ON_PORT_RIGHT,
2708 EL_SP_GRAVITY_ON_PORT_UP,
2709 EL_SP_GRAVITY_ON_PORT_DOWN,
2710 EL_SP_GRAVITY_OFF_PORT_LEFT,
2711 EL_SP_GRAVITY_OFF_PORT_RIGHT,
2712 EL_SP_GRAVITY_OFF_PORT_UP,
2713 EL_SP_GRAVITY_OFF_PORT_DOWN,
2718 static int ep_passable_under[] =
2723 static int ep_droppable[] =
2728 static int ep_explodes_1x1_old[] =
2733 static int ep_pushable[] =
2745 EL_SOKOBAN_FIELD_FULL,
2754 static int ep_explodes_cross_old[] =
2759 static int ep_protected[] =
2761 /* same elements as in 'ep_walkable_inside' */
2765 EL_TUBE_VERTICAL_LEFT,
2766 EL_TUBE_VERTICAL_RIGHT,
2767 EL_TUBE_HORIZONTAL_UP,
2768 EL_TUBE_HORIZONTAL_DOWN,
2774 /* same elements as in 'ep_passable_over' */
2783 EL_EM_GATE_1_GRAY_ACTIVE,
2784 EL_EM_GATE_2_GRAY_ACTIVE,
2785 EL_EM_GATE_3_GRAY_ACTIVE,
2786 EL_EM_GATE_4_GRAY_ACTIVE,
2795 EL_EMC_GATE_5_GRAY_ACTIVE,
2796 EL_EMC_GATE_6_GRAY_ACTIVE,
2797 EL_EMC_GATE_7_GRAY_ACTIVE,
2798 EL_EMC_GATE_8_GRAY_ACTIVE,
2800 EL_DC_GATE_WHITE_GRAY,
2801 EL_DC_GATE_WHITE_GRAY_ACTIVE,
2805 /* same elements as in 'ep_passable_inside' */
2810 EL_SP_PORT_HORIZONTAL,
2811 EL_SP_PORT_VERTICAL,
2813 EL_SP_GRAVITY_PORT_LEFT,
2814 EL_SP_GRAVITY_PORT_RIGHT,
2815 EL_SP_GRAVITY_PORT_UP,
2816 EL_SP_GRAVITY_PORT_DOWN,
2817 EL_SP_GRAVITY_ON_PORT_LEFT,
2818 EL_SP_GRAVITY_ON_PORT_RIGHT,
2819 EL_SP_GRAVITY_ON_PORT_UP,
2820 EL_SP_GRAVITY_ON_PORT_DOWN,
2821 EL_SP_GRAVITY_OFF_PORT_LEFT,
2822 EL_SP_GRAVITY_OFF_PORT_RIGHT,
2823 EL_SP_GRAVITY_OFF_PORT_UP,
2824 EL_SP_GRAVITY_OFF_PORT_DOWN,
2829 static int ep_throwable[] =
2834 static int ep_can_explode[] =
2836 /* same elements as in 'ep_explodes_impact' */
2841 /* same elements as in 'ep_explodes_smashed' */
2847 /* elements that can explode by explosion or by dragonfire */
2851 EL_EM_DYNAMITE_ACTIVE,
2852 EL_DYNABOMB_PLAYER_1_ACTIVE,
2853 EL_DYNABOMB_PLAYER_2_ACTIVE,
2854 EL_DYNABOMB_PLAYER_3_ACTIVE,
2855 EL_DYNABOMB_PLAYER_4_ACTIVE,
2856 EL_DYNABOMB_INCREASE_NUMBER,
2857 EL_DYNABOMB_INCREASE_SIZE,
2858 EL_DYNABOMB_INCREASE_POWER,
2859 EL_SP_DISK_RED_ACTIVE,
2867 /* elements that can explode only by explosion */
2873 static int ep_gravity_reachable[] =
2879 EL_INVISIBLE_SAND_ACTIVE,
2884 EL_SP_PORT_HORIZONTAL,
2885 EL_SP_PORT_VERTICAL,
2887 EL_SP_GRAVITY_PORT_LEFT,
2888 EL_SP_GRAVITY_PORT_RIGHT,
2889 EL_SP_GRAVITY_PORT_UP,
2890 EL_SP_GRAVITY_PORT_DOWN,
2891 EL_SP_GRAVITY_ON_PORT_LEFT,
2892 EL_SP_GRAVITY_ON_PORT_RIGHT,
2893 EL_SP_GRAVITY_ON_PORT_UP,
2894 EL_SP_GRAVITY_ON_PORT_DOWN,
2895 EL_SP_GRAVITY_OFF_PORT_LEFT,
2896 EL_SP_GRAVITY_OFF_PORT_RIGHT,
2897 EL_SP_GRAVITY_OFF_PORT_UP,
2898 EL_SP_GRAVITY_OFF_PORT_DOWN,
2904 static int ep_player[] =
2911 EL_SOKOBAN_FIELD_PLAYER,
2917 static int ep_can_pass_magic_wall[] =
2931 static int ep_can_pass_dc_magic_wall[] =
2947 static int ep_switchable[] =
2951 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
2952 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
2953 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
2954 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
2955 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
2956 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
2957 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
2958 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
2959 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
2960 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
2961 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
2962 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
2963 EL_SWITCHGATE_SWITCH_UP,
2964 EL_SWITCHGATE_SWITCH_DOWN,
2965 EL_DC_SWITCHGATE_SWITCH_UP,
2966 EL_DC_SWITCHGATE_SWITCH_DOWN,
2968 EL_LIGHT_SWITCH_ACTIVE,
2970 EL_DC_TIMEGATE_SWITCH,
2971 EL_BALLOON_SWITCH_LEFT,
2972 EL_BALLOON_SWITCH_RIGHT,
2973 EL_BALLOON_SWITCH_UP,
2974 EL_BALLOON_SWITCH_DOWN,
2975 EL_BALLOON_SWITCH_ANY,
2976 EL_BALLOON_SWITCH_NONE,
2979 EL_EMC_MAGIC_BALL_SWITCH,
2980 EL_EMC_MAGIC_BALL_SWITCH_ACTIVE,
2985 static int ep_bd_element[] =
3019 static int ep_sp_element[] =
3021 /* should always be valid */
3024 /* standard classic Supaplex elements */
3031 EL_SP_HARDWARE_GRAY,
3039 EL_SP_GRAVITY_PORT_RIGHT,
3040 EL_SP_GRAVITY_PORT_DOWN,
3041 EL_SP_GRAVITY_PORT_LEFT,
3042 EL_SP_GRAVITY_PORT_UP,
3047 EL_SP_PORT_VERTICAL,
3048 EL_SP_PORT_HORIZONTAL,
3054 EL_SP_HARDWARE_BASE_1,
3055 EL_SP_HARDWARE_GREEN,
3056 EL_SP_HARDWARE_BLUE,
3058 EL_SP_HARDWARE_YELLOW,
3059 EL_SP_HARDWARE_BASE_2,
3060 EL_SP_HARDWARE_BASE_3,
3061 EL_SP_HARDWARE_BASE_4,
3062 EL_SP_HARDWARE_BASE_5,
3063 EL_SP_HARDWARE_BASE_6,
3067 /* additional elements that appeared in newer Supaplex levels */
3070 /* additional gravity port elements (not switching, but setting gravity) */
3071 EL_SP_GRAVITY_ON_PORT_LEFT,
3072 EL_SP_GRAVITY_ON_PORT_RIGHT,
3073 EL_SP_GRAVITY_ON_PORT_UP,
3074 EL_SP_GRAVITY_ON_PORT_DOWN,
3075 EL_SP_GRAVITY_OFF_PORT_LEFT,
3076 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3077 EL_SP_GRAVITY_OFF_PORT_UP,
3078 EL_SP_GRAVITY_OFF_PORT_DOWN,
3080 /* more than one Murphy in a level results in an inactive clone */
3083 /* runtime Supaplex elements */
3084 EL_SP_DISK_RED_ACTIVE,
3085 EL_SP_TERMINAL_ACTIVE,
3086 EL_SP_BUGGY_BASE_ACTIVATING,
3087 EL_SP_BUGGY_BASE_ACTIVE,
3094 static int ep_sb_element[] =
3099 EL_SOKOBAN_FIELD_EMPTY,
3100 EL_SOKOBAN_FIELD_FULL,
3101 EL_SOKOBAN_FIELD_PLAYER,
3106 EL_INVISIBLE_STEELWALL,
3111 static int ep_gem[] =
3123 static int ep_food_dark_yamyam[] =
3151 static int ep_food_penguin[] =
3165 static int ep_food_pig[] =
3177 static int ep_historic_wall[] =
3188 EL_GATE_1_GRAY_ACTIVE,
3189 EL_GATE_2_GRAY_ACTIVE,
3190 EL_GATE_3_GRAY_ACTIVE,
3191 EL_GATE_4_GRAY_ACTIVE,
3200 EL_EM_GATE_1_GRAY_ACTIVE,
3201 EL_EM_GATE_2_GRAY_ACTIVE,
3202 EL_EM_GATE_3_GRAY_ACTIVE,
3203 EL_EM_GATE_4_GRAY_ACTIVE,
3210 EL_EXPANDABLE_WALL_HORIZONTAL,
3211 EL_EXPANDABLE_WALL_VERTICAL,
3212 EL_EXPANDABLE_WALL_ANY,
3213 EL_EXPANDABLE_WALL_GROWING,
3214 EL_BD_EXPANDABLE_WALL,
3221 EL_SP_HARDWARE_GRAY,
3222 EL_SP_HARDWARE_GREEN,
3223 EL_SP_HARDWARE_BLUE,
3225 EL_SP_HARDWARE_YELLOW,
3226 EL_SP_HARDWARE_BASE_1,
3227 EL_SP_HARDWARE_BASE_2,
3228 EL_SP_HARDWARE_BASE_3,
3229 EL_SP_HARDWARE_BASE_4,
3230 EL_SP_HARDWARE_BASE_5,
3231 EL_SP_HARDWARE_BASE_6,
3233 EL_SP_TERMINAL_ACTIVE,
3236 EL_INVISIBLE_STEELWALL,
3237 EL_INVISIBLE_STEELWALL_ACTIVE,
3239 EL_INVISIBLE_WALL_ACTIVE,
3240 EL_STEELWALL_SLIPPERY,
3257 static int ep_historic_solid[] =
3261 EL_EXPANDABLE_WALL_HORIZONTAL,
3262 EL_EXPANDABLE_WALL_VERTICAL,
3263 EL_EXPANDABLE_WALL_ANY,
3264 EL_BD_EXPANDABLE_WALL,
3277 EL_QUICKSAND_FILLING,
3278 EL_QUICKSAND_EMPTYING,
3280 EL_MAGIC_WALL_ACTIVE,
3281 EL_MAGIC_WALL_EMPTYING,
3282 EL_MAGIC_WALL_FILLING,
3286 EL_BD_MAGIC_WALL_ACTIVE,
3287 EL_BD_MAGIC_WALL_EMPTYING,
3288 EL_BD_MAGIC_WALL_FULL,
3289 EL_BD_MAGIC_WALL_FILLING,
3290 EL_BD_MAGIC_WALL_DEAD,
3299 EL_SP_TERMINAL_ACTIVE,
3303 EL_INVISIBLE_WALL_ACTIVE,
3304 EL_SWITCHGATE_SWITCH_UP,
3305 EL_SWITCHGATE_SWITCH_DOWN,
3306 EL_DC_SWITCHGATE_SWITCH_UP,
3307 EL_DC_SWITCHGATE_SWITCH_DOWN,
3309 EL_TIMEGATE_SWITCH_ACTIVE,
3310 EL_DC_TIMEGATE_SWITCH,
3311 EL_DC_TIMEGATE_SWITCH_ACTIVE,
3323 /* the following elements are a direct copy of "indestructible" elements,
3324 except "EL_ACID", which is "indestructible", but not "solid"! */
3329 EL_ACID_POOL_TOPLEFT,
3330 EL_ACID_POOL_TOPRIGHT,
3331 EL_ACID_POOL_BOTTOMLEFT,
3332 EL_ACID_POOL_BOTTOM,
3333 EL_ACID_POOL_BOTTOMRIGHT,
3334 EL_SP_HARDWARE_GRAY,
3335 EL_SP_HARDWARE_GREEN,
3336 EL_SP_HARDWARE_BLUE,
3338 EL_SP_HARDWARE_YELLOW,
3339 EL_SP_HARDWARE_BASE_1,
3340 EL_SP_HARDWARE_BASE_2,
3341 EL_SP_HARDWARE_BASE_3,
3342 EL_SP_HARDWARE_BASE_4,
3343 EL_SP_HARDWARE_BASE_5,
3344 EL_SP_HARDWARE_BASE_6,
3345 EL_INVISIBLE_STEELWALL,
3346 EL_INVISIBLE_STEELWALL_ACTIVE,
3347 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3348 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3349 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3350 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3351 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3352 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3353 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3354 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3355 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3356 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3357 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3358 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3360 EL_LIGHT_SWITCH_ACTIVE,
3361 EL_SIGN_EXCLAMATION,
3362 EL_SIGN_RADIOACTIVITY,
3369 EL_SIGN_ENTRY_FORBIDDEN,
3370 EL_SIGN_EMERGENCY_EXIT,
3378 EL_STEEL_EXIT_CLOSED,
3380 EL_DC_STEELWALL_1_LEFT,
3381 EL_DC_STEELWALL_1_RIGHT,
3382 EL_DC_STEELWALL_1_TOP,
3383 EL_DC_STEELWALL_1_BOTTOM,
3384 EL_DC_STEELWALL_1_HORIZONTAL,
3385 EL_DC_STEELWALL_1_VERTICAL,
3386 EL_DC_STEELWALL_1_TOPLEFT,
3387 EL_DC_STEELWALL_1_TOPRIGHT,
3388 EL_DC_STEELWALL_1_BOTTOMLEFT,
3389 EL_DC_STEELWALL_1_BOTTOMRIGHT,
3390 EL_DC_STEELWALL_1_TOPLEFT_2,
3391 EL_DC_STEELWALL_1_TOPRIGHT_2,
3392 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
3393 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
3394 EL_DC_STEELWALL_2_LEFT,
3395 EL_DC_STEELWALL_2_RIGHT,
3396 EL_DC_STEELWALL_2_TOP,
3397 EL_DC_STEELWALL_2_BOTTOM,
3398 EL_DC_STEELWALL_2_HORIZONTAL,
3399 EL_DC_STEELWALL_2_VERTICAL,
3400 EL_DC_STEELWALL_2_MIDDLE,
3401 EL_DC_STEELWALL_2_SINGLE,
3402 EL_STEELWALL_SLIPPERY,
3416 EL_GATE_1_GRAY_ACTIVE,
3417 EL_GATE_2_GRAY_ACTIVE,
3418 EL_GATE_3_GRAY_ACTIVE,
3419 EL_GATE_4_GRAY_ACTIVE,
3428 EL_EM_GATE_1_GRAY_ACTIVE,
3429 EL_EM_GATE_2_GRAY_ACTIVE,
3430 EL_EM_GATE_3_GRAY_ACTIVE,
3431 EL_EM_GATE_4_GRAY_ACTIVE,
3433 EL_SWITCHGATE_OPENING,
3434 EL_SWITCHGATE_CLOSED,
3435 EL_SWITCHGATE_CLOSING,
3437 EL_TIMEGATE_OPENING,
3439 EL_TIMEGATE_CLOSING,
3443 EL_TUBE_VERTICAL_LEFT,
3444 EL_TUBE_VERTICAL_RIGHT,
3445 EL_TUBE_HORIZONTAL_UP,
3446 EL_TUBE_HORIZONTAL_DOWN,
3455 static int ep_classic_enemy[] =
3472 static int ep_belt[] =
3474 EL_CONVEYOR_BELT_1_LEFT,
3475 EL_CONVEYOR_BELT_1_MIDDLE,
3476 EL_CONVEYOR_BELT_1_RIGHT,
3477 EL_CONVEYOR_BELT_2_LEFT,
3478 EL_CONVEYOR_BELT_2_MIDDLE,
3479 EL_CONVEYOR_BELT_2_RIGHT,
3480 EL_CONVEYOR_BELT_3_LEFT,
3481 EL_CONVEYOR_BELT_3_MIDDLE,
3482 EL_CONVEYOR_BELT_3_RIGHT,
3483 EL_CONVEYOR_BELT_4_LEFT,
3484 EL_CONVEYOR_BELT_4_MIDDLE,
3485 EL_CONVEYOR_BELT_4_RIGHT,
3490 static int ep_belt_active[] =
3492 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3493 EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE,
3494 EL_CONVEYOR_BELT_1_RIGHT_ACTIVE,
3495 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3496 EL_CONVEYOR_BELT_2_MIDDLE_ACTIVE,
3497 EL_CONVEYOR_BELT_2_RIGHT_ACTIVE,
3498 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3499 EL_CONVEYOR_BELT_3_MIDDLE_ACTIVE,
3500 EL_CONVEYOR_BELT_3_RIGHT_ACTIVE,
3501 EL_CONVEYOR_BELT_4_LEFT_ACTIVE,
3502 EL_CONVEYOR_BELT_4_MIDDLE_ACTIVE,
3503 EL_CONVEYOR_BELT_4_RIGHT_ACTIVE,
3508 static int ep_belt_switch[] =
3510 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3511 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3512 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3513 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3514 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3515 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3516 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3517 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3518 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3519 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3520 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3521 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3526 static int ep_tube[] =
3533 EL_TUBE_HORIZONTAL_UP,
3534 EL_TUBE_HORIZONTAL_DOWN,
3536 EL_TUBE_VERTICAL_LEFT,
3537 EL_TUBE_VERTICAL_RIGHT,
3543 static int ep_acid_pool[] =
3545 EL_ACID_POOL_TOPLEFT,
3546 EL_ACID_POOL_TOPRIGHT,
3547 EL_ACID_POOL_BOTTOMLEFT,
3548 EL_ACID_POOL_BOTTOM,
3549 EL_ACID_POOL_BOTTOMRIGHT,
3554 static int ep_keygate[] =
3564 EL_GATE_1_GRAY_ACTIVE,
3565 EL_GATE_2_GRAY_ACTIVE,
3566 EL_GATE_3_GRAY_ACTIVE,
3567 EL_GATE_4_GRAY_ACTIVE,
3576 EL_EM_GATE_1_GRAY_ACTIVE,
3577 EL_EM_GATE_2_GRAY_ACTIVE,
3578 EL_EM_GATE_3_GRAY_ACTIVE,
3579 EL_EM_GATE_4_GRAY_ACTIVE,
3588 EL_EMC_GATE_5_GRAY_ACTIVE,
3589 EL_EMC_GATE_6_GRAY_ACTIVE,
3590 EL_EMC_GATE_7_GRAY_ACTIVE,
3591 EL_EMC_GATE_8_GRAY_ACTIVE,
3593 EL_DC_GATE_WHITE_GRAY,
3594 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3599 static int ep_amoeboid[] =
3611 static int ep_amoebalive[] =
3622 static int ep_has_editor_content[] =
3628 EL_SOKOBAN_FIELD_PLAYER,
3645 static int ep_can_turn_each_move[] =
3647 /* !!! do something with this one !!! */
3651 static int ep_can_grow[] =
3665 static int ep_active_bomb[] =
3668 EL_EM_DYNAMITE_ACTIVE,
3669 EL_DYNABOMB_PLAYER_1_ACTIVE,
3670 EL_DYNABOMB_PLAYER_2_ACTIVE,
3671 EL_DYNABOMB_PLAYER_3_ACTIVE,
3672 EL_DYNABOMB_PLAYER_4_ACTIVE,
3673 EL_SP_DISK_RED_ACTIVE,
3678 static int ep_inactive[] =
3688 EL_QUICKSAND_FAST_EMPTY,
3711 EL_GATE_1_GRAY_ACTIVE,
3712 EL_GATE_2_GRAY_ACTIVE,
3713 EL_GATE_3_GRAY_ACTIVE,
3714 EL_GATE_4_GRAY_ACTIVE,
3723 EL_EM_GATE_1_GRAY_ACTIVE,
3724 EL_EM_GATE_2_GRAY_ACTIVE,
3725 EL_EM_GATE_3_GRAY_ACTIVE,
3726 EL_EM_GATE_4_GRAY_ACTIVE,
3735 EL_EMC_GATE_5_GRAY_ACTIVE,
3736 EL_EMC_GATE_6_GRAY_ACTIVE,
3737 EL_EMC_GATE_7_GRAY_ACTIVE,
3738 EL_EMC_GATE_8_GRAY_ACTIVE,
3740 EL_DC_GATE_WHITE_GRAY,
3741 EL_DC_GATE_WHITE_GRAY_ACTIVE,
3742 EL_DC_GATE_FAKE_GRAY,
3745 EL_INVISIBLE_STEELWALL,
3753 EL_WALL_EMERALD_YELLOW,
3754 EL_DYNABOMB_INCREASE_NUMBER,
3755 EL_DYNABOMB_INCREASE_SIZE,
3756 EL_DYNABOMB_INCREASE_POWER,
3760 EL_SOKOBAN_FIELD_EMPTY,
3761 EL_SOKOBAN_FIELD_FULL,
3762 EL_WALL_EMERALD_RED,
3763 EL_WALL_EMERALD_PURPLE,
3764 EL_ACID_POOL_TOPLEFT,
3765 EL_ACID_POOL_TOPRIGHT,
3766 EL_ACID_POOL_BOTTOMLEFT,
3767 EL_ACID_POOL_BOTTOM,
3768 EL_ACID_POOL_BOTTOMRIGHT,
3772 EL_BD_MAGIC_WALL_DEAD,
3774 EL_DC_MAGIC_WALL_DEAD,
3775 EL_AMOEBA_TO_DIAMOND,
3783 EL_SP_GRAVITY_PORT_RIGHT,
3784 EL_SP_GRAVITY_PORT_DOWN,
3785 EL_SP_GRAVITY_PORT_LEFT,
3786 EL_SP_GRAVITY_PORT_UP,
3787 EL_SP_PORT_HORIZONTAL,
3788 EL_SP_PORT_VERTICAL,
3799 EL_SP_HARDWARE_GRAY,
3800 EL_SP_HARDWARE_GREEN,
3801 EL_SP_HARDWARE_BLUE,
3803 EL_SP_HARDWARE_YELLOW,
3804 EL_SP_HARDWARE_BASE_1,
3805 EL_SP_HARDWARE_BASE_2,
3806 EL_SP_HARDWARE_BASE_3,
3807 EL_SP_HARDWARE_BASE_4,
3808 EL_SP_HARDWARE_BASE_5,
3809 EL_SP_HARDWARE_BASE_6,
3810 EL_SP_GRAVITY_ON_PORT_LEFT,
3811 EL_SP_GRAVITY_ON_PORT_RIGHT,
3812 EL_SP_GRAVITY_ON_PORT_UP,
3813 EL_SP_GRAVITY_ON_PORT_DOWN,
3814 EL_SP_GRAVITY_OFF_PORT_LEFT,
3815 EL_SP_GRAVITY_OFF_PORT_RIGHT,
3816 EL_SP_GRAVITY_OFF_PORT_UP,
3817 EL_SP_GRAVITY_OFF_PORT_DOWN,
3818 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3819 EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
3820 EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
3821 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3822 EL_CONVEYOR_BELT_2_SWITCH_MIDDLE,
3823 EL_CONVEYOR_BELT_2_SWITCH_RIGHT,
3824 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3825 EL_CONVEYOR_BELT_3_SWITCH_MIDDLE,
3826 EL_CONVEYOR_BELT_3_SWITCH_RIGHT,
3827 EL_CONVEYOR_BELT_4_SWITCH_LEFT,
3828 EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
3829 EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
3830 EL_SIGN_EXCLAMATION,
3831 EL_SIGN_RADIOACTIVITY,
3838 EL_SIGN_ENTRY_FORBIDDEN,
3839 EL_SIGN_EMERGENCY_EXIT,
3847 EL_DC_STEELWALL_1_LEFT,
3848 EL_DC_STEELWALL_1_RIGHT,
3849 EL_DC_STEELWALL_1_TOP,
3850 EL_DC_STEELWALL_1_BOTTOM,
3851 EL_DC_STEELWALL_1_HORIZONTAL,
3852 EL_DC_STEELWALL_1_VERTICAL,
3853 EL_DC_STEELWALL_1_TOPLEFT,
3854 EL_DC_STEELWALL_1_TOPRIGHT,
3855 EL_DC_STEELWALL_1_BOTTOMLEFT,
3856 EL_DC_STEELWALL_1_BOTTOMRIGHT,
3857 EL_DC_STEELWALL_1_TOPLEFT_2,
3858 EL_DC_STEELWALL_1_TOPRIGHT_2,
3859 EL_DC_STEELWALL_1_BOTTOMLEFT_2,
3860 EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
3861 EL_DC_STEELWALL_2_LEFT,
3862 EL_DC_STEELWALL_2_RIGHT,
3863 EL_DC_STEELWALL_2_TOP,
3864 EL_DC_STEELWALL_2_BOTTOM,
3865 EL_DC_STEELWALL_2_HORIZONTAL,
3866 EL_DC_STEELWALL_2_VERTICAL,
3867 EL_DC_STEELWALL_2_MIDDLE,
3868 EL_DC_STEELWALL_2_SINGLE,
3869 EL_STEELWALL_SLIPPERY,
3874 EL_EMC_WALL_SLIPPERY_1,
3875 EL_EMC_WALL_SLIPPERY_2,
3876 EL_EMC_WALL_SLIPPERY_3,
3877 EL_EMC_WALL_SLIPPERY_4,
3898 static int ep_em_slippery_wall[] =
3903 static int ep_gfx_crumbled[] =
3914 static int ep_editor_cascade_active[] =
3916 EL_INTERNAL_CASCADE_BD_ACTIVE,
3917 EL_INTERNAL_CASCADE_EM_ACTIVE,
3918 EL_INTERNAL_CASCADE_EMC_ACTIVE,
3919 EL_INTERNAL_CASCADE_RND_ACTIVE,
3920 EL_INTERNAL_CASCADE_SB_ACTIVE,
3921 EL_INTERNAL_CASCADE_SP_ACTIVE,
3922 EL_INTERNAL_CASCADE_DC_ACTIVE,
3923 EL_INTERNAL_CASCADE_DX_ACTIVE,
3924 EL_INTERNAL_CASCADE_CHARS_ACTIVE,
3925 EL_INTERNAL_CASCADE_STEEL_CHARS_ACTIVE,
3926 EL_INTERNAL_CASCADE_CE_ACTIVE,
3927 EL_INTERNAL_CASCADE_GE_ACTIVE,
3928 EL_INTERNAL_CASCADE_REF_ACTIVE,
3929 EL_INTERNAL_CASCADE_USER_ACTIVE,
3930 EL_INTERNAL_CASCADE_DYNAMIC_ACTIVE,
3935 static int ep_editor_cascade_inactive[] =
3937 EL_INTERNAL_CASCADE_BD,
3938 EL_INTERNAL_CASCADE_EM,
3939 EL_INTERNAL_CASCADE_EMC,
3940 EL_INTERNAL_CASCADE_RND,
3941 EL_INTERNAL_CASCADE_SB,
3942 EL_INTERNAL_CASCADE_SP,
3943 EL_INTERNAL_CASCADE_DC,
3944 EL_INTERNAL_CASCADE_DX,
3945 EL_INTERNAL_CASCADE_CHARS,
3946 EL_INTERNAL_CASCADE_STEEL_CHARS,
3947 EL_INTERNAL_CASCADE_CE,
3948 EL_INTERNAL_CASCADE_GE,
3949 EL_INTERNAL_CASCADE_REF,
3950 EL_INTERNAL_CASCADE_USER,
3951 EL_INTERNAL_CASCADE_DYNAMIC,
3956 static int ep_obsolete[] =
3960 EL_EM_KEY_1_FILE_OBSOLETE,
3961 EL_EM_KEY_2_FILE_OBSOLETE,
3962 EL_EM_KEY_3_FILE_OBSOLETE,
3963 EL_EM_KEY_4_FILE_OBSOLETE,
3964 EL_ENVELOPE_OBSOLETE,
3973 } element_properties[] =
3975 { ep_diggable, EP_DIGGABLE },
3976 { ep_collectible_only, EP_COLLECTIBLE_ONLY },
3977 { ep_dont_run_into, EP_DONT_RUN_INTO },
3978 { ep_dont_collide_with, EP_DONT_COLLIDE_WITH },
3979 { ep_dont_touch, EP_DONT_TOUCH },
3980 { ep_indestructible, EP_INDESTRUCTIBLE },
3981 { ep_slippery, EP_SLIPPERY },
3982 { ep_can_change, EP_CAN_CHANGE },
3983 { ep_can_move, EP_CAN_MOVE },
3984 { ep_can_fall, EP_CAN_FALL },
3985 { ep_can_smash_player, EP_CAN_SMASH_PLAYER },
3986 { ep_can_smash_enemies, EP_CAN_SMASH_ENEMIES },
3987 { ep_can_smash_everything, EP_CAN_SMASH_EVERYTHING },
3988 { ep_explodes_by_fire, EP_EXPLODES_BY_FIRE },
3989 { ep_explodes_smashed, EP_EXPLODES_SMASHED },
3990 { ep_explodes_impact, EP_EXPLODES_IMPACT },
3991 { ep_walkable_over, EP_WALKABLE_OVER },
3992 { ep_walkable_inside, EP_WALKABLE_INSIDE },
3993 { ep_walkable_under, EP_WALKABLE_UNDER },
3994 { ep_passable_over, EP_PASSABLE_OVER },
3995 { ep_passable_inside, EP_PASSABLE_INSIDE },
3996 { ep_passable_under, EP_PASSABLE_UNDER },
3997 { ep_droppable, EP_DROPPABLE },
3998 { ep_explodes_1x1_old, EP_EXPLODES_1X1_OLD },
3999 { ep_pushable, EP_PUSHABLE },
4000 { ep_explodes_cross_old, EP_EXPLODES_CROSS_OLD },
4001 { ep_protected, EP_PROTECTED },
4002 { ep_throwable, EP_THROWABLE },
4003 { ep_can_explode, EP_CAN_EXPLODE },
4004 { ep_gravity_reachable, EP_GRAVITY_REACHABLE },
4006 { ep_player, EP_PLAYER },
4007 { ep_can_pass_magic_wall, EP_CAN_PASS_MAGIC_WALL },
4008 { ep_can_pass_dc_magic_wall, EP_CAN_PASS_DC_MAGIC_WALL },
4009 { ep_switchable, EP_SWITCHABLE },
4010 { ep_bd_element, EP_BD_ELEMENT },
4011 { ep_sp_element, EP_SP_ELEMENT },
4012 { ep_sb_element, EP_SB_ELEMENT },
4014 { ep_food_dark_yamyam, EP_FOOD_DARK_YAMYAM },
4015 { ep_food_penguin, EP_FOOD_PENGUIN },
4016 { ep_food_pig, EP_FOOD_PIG },
4017 { ep_historic_wall, EP_HISTORIC_WALL },
4018 { ep_historic_solid, EP_HISTORIC_SOLID },
4019 { ep_classic_enemy, EP_CLASSIC_ENEMY },
4020 { ep_belt, EP_BELT },
4021 { ep_belt_active, EP_BELT_ACTIVE },
4022 { ep_belt_switch, EP_BELT_SWITCH },
4023 { ep_tube, EP_TUBE },
4024 { ep_acid_pool, EP_ACID_POOL },
4025 { ep_keygate, EP_KEYGATE },
4026 { ep_amoeboid, EP_AMOEBOID },
4027 { ep_amoebalive, EP_AMOEBALIVE },
4028 { ep_has_editor_content, EP_HAS_EDITOR_CONTENT },
4029 { ep_can_turn_each_move, EP_CAN_TURN_EACH_MOVE },
4030 { ep_can_grow, EP_CAN_GROW },
4031 { ep_active_bomb, EP_ACTIVE_BOMB },
4032 { ep_inactive, EP_INACTIVE },
4034 { ep_em_slippery_wall, EP_EM_SLIPPERY_WALL },
4036 { ep_gfx_crumbled, EP_GFX_CRUMBLED },
4038 { ep_editor_cascade_active, EP_EDITOR_CASCADE_ACTIVE },
4039 { ep_editor_cascade_inactive, EP_EDITOR_CASCADE_INACTIVE },
4041 { ep_obsolete, EP_OBSOLETE },
4048 /* always start with reliable default values (element has no properties) */
4049 /* (but never initialize clipboard elements after the very first time) */
4050 /* (to be able to use clipboard elements between several levels) */
4051 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4052 if (!IS_CLIPBOARD_ELEMENT(i) || !clipboard_elements_initialized)
4053 for (j = 0; j < NUM_ELEMENT_PROPERTIES; j++)
4054 SET_PROPERTY(i, j, FALSE);
4056 /* set all base element properties from above array definitions */
4057 for (i = 0; element_properties[i].elements != NULL; i++)
4058 for (j = 0; (element_properties[i].elements)[j] != -1; j++)
4059 SET_PROPERTY((element_properties[i].elements)[j],
4060 element_properties[i].property, TRUE);
4062 /* copy properties to some elements that are only stored in level file */
4063 for (i = 0; i < NUM_ELEMENT_PROPERTIES; i++)
4064 for (j = 0; copy_properties[j][0] != -1; j++)
4065 if (HAS_PROPERTY(copy_properties[j][0], i))
4066 for (k = 1; k <= 4; k++)
4067 SET_PROPERTY(copy_properties[j][k], i, TRUE);
4069 /* set static element properties that are not listed in array definitions */
4070 for (i = EL_STEEL_CHAR_START; i <= EL_STEEL_CHAR_END; i++)
4071 SET_PROPERTY(i, EP_INDESTRUCTIBLE, TRUE);
4073 clipboard_elements_initialized = TRUE;
4076 void InitElementPropertiesEngine(int engine_version)
4078 static int no_wall_properties[] =
4081 EP_COLLECTIBLE_ONLY,
4083 EP_DONT_COLLIDE_WITH,
4086 EP_CAN_SMASH_PLAYER,
4087 EP_CAN_SMASH_ENEMIES,
4088 EP_CAN_SMASH_EVERYTHING,
4093 EP_FOOD_DARK_YAMYAM,
4109 /* important: after initialization in InitElementPropertiesStatic(), the
4110 elements are not again initialized to a default value; therefore all
4111 changes have to make sure that they leave the element with a defined
4112 property (which means that conditional property changes must be set to
4113 a reliable default value before) */
4115 /* resolve group elements */
4116 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
4117 ResolveGroupElement(EL_GROUP_START + i);
4119 /* set all special, combined or engine dependent element properties */
4120 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4122 /* do not change (already initialized) clipboard elements here */
4123 if (IS_CLIPBOARD_ELEMENT(i))
4126 /* ---------- INACTIVE ------------------------------------------------- */
4127 SET_PROPERTY(i, EP_INACTIVE, ((i >= EL_CHAR_START &&
4128 i <= EL_CHAR_END) ||
4129 (i >= EL_STEEL_CHAR_START &&
4130 i <= EL_STEEL_CHAR_END)));
4132 /* ---------- WALKABLE, PASSABLE, ACCESSIBLE --------------------------- */
4133 SET_PROPERTY(i, EP_WALKABLE, (IS_WALKABLE_OVER(i) ||
4134 IS_WALKABLE_INSIDE(i) ||
4135 IS_WALKABLE_UNDER(i)));
4137 SET_PROPERTY(i, EP_PASSABLE, (IS_PASSABLE_OVER(i) ||
4138 IS_PASSABLE_INSIDE(i) ||
4139 IS_PASSABLE_UNDER(i)));
4141 SET_PROPERTY(i, EP_ACCESSIBLE_OVER, (IS_WALKABLE_OVER(i) ||
4142 IS_PASSABLE_OVER(i)));
4144 SET_PROPERTY(i, EP_ACCESSIBLE_INSIDE, (IS_WALKABLE_INSIDE(i) ||
4145 IS_PASSABLE_INSIDE(i)));
4147 SET_PROPERTY(i, EP_ACCESSIBLE_UNDER, (IS_WALKABLE_UNDER(i) ||
4148 IS_PASSABLE_UNDER(i)));
4150 SET_PROPERTY(i, EP_ACCESSIBLE, (IS_WALKABLE(i) ||
4153 /* ---------- COLLECTIBLE ---------------------------------------------- */
4154 SET_PROPERTY(i, EP_COLLECTIBLE, (IS_COLLECTIBLE_ONLY(i) ||
4158 /* ---------- SNAPPABLE ------------------------------------------------ */
4159 SET_PROPERTY(i, EP_SNAPPABLE, (IS_DIGGABLE(i) ||
4160 IS_COLLECTIBLE(i) ||
4164 /* ---------- WALL ----------------------------------------------------- */
4165 SET_PROPERTY(i, EP_WALL, TRUE); /* default: element is wall */
4167 for (j = 0; no_wall_properties[j] != -1; j++)
4168 if (HAS_PROPERTY(i, no_wall_properties[j]) ||
4169 i >= EL_FIRST_RUNTIME_UNREAL)
4170 SET_PROPERTY(i, EP_WALL, FALSE);
4172 if (IS_HISTORIC_WALL(i))
4173 SET_PROPERTY(i, EP_WALL, TRUE);
4175 /* ---------- SOLID_FOR_PUSHING ---------------------------------------- */
4176 if (engine_version < VERSION_IDENT(2,2,0,0))
4177 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, IS_HISTORIC_SOLID(i));
4179 SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, (!IS_WALKABLE(i) &&
4181 !IS_COLLECTIBLE(i)));
4183 /* ---------- DRAGONFIRE_PROOF ----------------------------------------- */
4184 if (IS_HISTORIC_SOLID(i) || i == EL_EXPLOSION)
4185 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, TRUE);
4187 SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, (IS_CUSTOM_ELEMENT(i) &&
4188 IS_INDESTRUCTIBLE(i)));
4190 /* ---------- EXPLOSION_PROOF ------------------------------------------ */
4192 SET_PROPERTY(i, EP_EXPLOSION_PROOF, TRUE);
4193 else if (engine_version < VERSION_IDENT(2,2,0,0))
4194 SET_PROPERTY(i, EP_EXPLOSION_PROOF, IS_INDESTRUCTIBLE(i));
4196 SET_PROPERTY(i, EP_EXPLOSION_PROOF, (IS_INDESTRUCTIBLE(i) &&
4200 if (IS_CUSTOM_ELEMENT(i))
4202 /* these are additional properties which are initially false when set */
4204 /* ---------- DONT_COLLIDE_WITH / DONT_RUN_INTO ---------------------- */
4206 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH, TRUE);
4207 if (DONT_COLLIDE_WITH(i))
4208 SET_PROPERTY(i, EP_DONT_RUN_INTO, TRUE);
4210 /* ---------- CAN_SMASH_ENEMIES / CAN_SMASH_PLAYER ------------------- */
4211 if (CAN_SMASH_EVERYTHING(i))
4212 SET_PROPERTY(i, EP_CAN_SMASH_ENEMIES, TRUE);
4213 if (CAN_SMASH_ENEMIES(i))
4214 SET_PROPERTY(i, EP_CAN_SMASH_PLAYER, TRUE);
4217 /* ---------- CAN_SMASH ------------------------------------------------ */
4218 SET_PROPERTY(i, EP_CAN_SMASH, (CAN_SMASH_PLAYER(i) ||
4219 CAN_SMASH_ENEMIES(i) ||
4220 CAN_SMASH_EVERYTHING(i)));
4222 /* ---------- CAN_EXPLODE_BY_FIRE -------------------------------------- */
4223 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_FIRE, (CAN_EXPLODE(i) &&
4224 EXPLODES_BY_FIRE(i)));
4226 /* ---------- CAN_EXPLODE_SMASHED -------------------------------------- */
4227 SET_PROPERTY(i, EP_CAN_EXPLODE_SMASHED, (CAN_EXPLODE(i) &&
4228 EXPLODES_SMASHED(i)));
4230 /* ---------- CAN_EXPLODE_IMPACT --------------------------------------- */
4231 SET_PROPERTY(i, EP_CAN_EXPLODE_IMPACT, (CAN_EXPLODE(i) &&
4232 EXPLODES_IMPACT(i)));
4234 /* ---------- CAN_EXPLODE_BY_DRAGONFIRE -------------------------------- */
4235 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_DRAGONFIRE, CAN_EXPLODE_BY_FIRE(i));
4237 /* ---------- CAN_EXPLODE_BY_EXPLOSION --------------------------------- */
4238 SET_PROPERTY(i, EP_CAN_EXPLODE_BY_EXPLOSION, (CAN_EXPLODE_BY_FIRE(i) ||
4239 i == EL_BLACK_ORB));
4241 /* ---------- COULD_MOVE_INTO_ACID ------------------------------------- */
4242 SET_PROPERTY(i, EP_COULD_MOVE_INTO_ACID, (ELEM_IS_PLAYER(i) ||
4244 IS_CUSTOM_ELEMENT(i)));
4246 /* ---------- MAYBE_DONT_COLLIDE_WITH ---------------------------------- */
4247 SET_PROPERTY(i, EP_MAYBE_DONT_COLLIDE_WITH, (i == EL_SP_SNIKSNAK ||
4248 i == EL_SP_ELECTRON));
4250 /* ---------- CAN_MOVE_INTO_ACID --------------------------------------- */
4251 if (COULD_MOVE_INTO_ACID(i) && !IS_CUSTOM_ELEMENT(i))
4252 SET_PROPERTY(i, EP_CAN_MOVE_INTO_ACID,
4253 getMoveIntoAcidProperty(&level, i));
4255 /* ---------- DONT_COLLIDE_WITH ---------------------------------------- */
4256 if (MAYBE_DONT_COLLIDE_WITH(i))
4257 SET_PROPERTY(i, EP_DONT_COLLIDE_WITH,
4258 getDontCollideWithProperty(&level, i));
4260 /* ---------- SP_PORT -------------------------------------------------- */
4261 SET_PROPERTY(i, EP_SP_PORT, (IS_SP_ELEMENT(i) &&
4262 IS_PASSABLE_INSIDE(i)));
4264 /* ---------- CAN_BE_CLONED_BY_ANDROID --------------------------------- */
4265 for (j = 0; j < level.num_android_clone_elements; j++)
4266 SET_PROPERTY(i, EP_CAN_BE_CLONED_BY_ANDROID,
4268 IS_EQUAL_OR_IN_GROUP(i, level.android_clone_element[j])));
4270 /* ---------- CAN_CHANGE ----------------------------------------------- */
4271 SET_PROPERTY(i, EP_CAN_CHANGE, FALSE); /* default: cannot change */
4272 for (j = 0; j < element_info[i].num_change_pages; j++)
4273 if (element_info[i].change_page[j].can_change)
4274 SET_PROPERTY(i, EP_CAN_CHANGE, TRUE);
4276 /* ---------- HAS_ACTION ----------------------------------------------- */
4277 SET_PROPERTY(i, EP_HAS_ACTION, FALSE); /* default: has no action */
4278 for (j = 0; j < element_info[i].num_change_pages; j++)
4279 if (element_info[i].change_page[j].has_action)
4280 SET_PROPERTY(i, EP_HAS_ACTION, TRUE);
4282 /* ---------- CAN_CHANGE_OR_HAS_ACTION --------------------------------- */
4283 SET_PROPERTY(i, EP_CAN_CHANGE_OR_HAS_ACTION, (CAN_CHANGE(i) ||
4286 /* ---------- GFX_CRUMBLED --------------------------------------------- */
4287 SET_PROPERTY(i, EP_GFX_CRUMBLED,
4288 element_info[i].crumbled[ACTION_DEFAULT] !=
4289 element_info[i].graphic[ACTION_DEFAULT]);
4291 /* ---------- EDITOR_CASCADE ------------------------------------------- */
4292 SET_PROPERTY(i, EP_EDITOR_CASCADE, (IS_EDITOR_CASCADE_ACTIVE(i) ||
4293 IS_EDITOR_CASCADE_INACTIVE(i)));
4296 /* dynamically adjust element properties according to game engine version */
4298 static int ep_em_slippery_wall[] =
4303 EL_EXPANDABLE_WALL_HORIZONTAL,
4304 EL_EXPANDABLE_WALL_VERTICAL,
4305 EL_EXPANDABLE_WALL_ANY,
4306 EL_EXPANDABLE_STEELWALL_HORIZONTAL,
4307 EL_EXPANDABLE_STEELWALL_VERTICAL,
4308 EL_EXPANDABLE_STEELWALL_ANY,
4309 EL_EXPANDABLE_STEELWALL_GROWING,
4313 static int ep_em_explodes_by_fire[] =
4316 EL_EM_DYNAMITE_ACTIVE,
4321 /* special EM style gems behaviour */
4322 for (i = 0; ep_em_slippery_wall[i] != -1; i++)
4323 SET_PROPERTY(ep_em_slippery_wall[i], EP_EM_SLIPPERY_WALL,
4324 level.em_slippery_gems);
4326 /* "EL_EXPANDABLE_WALL_GROWING" wasn't slippery for EM gems in 2.0.1 */
4327 SET_PROPERTY(EL_EXPANDABLE_WALL_GROWING, EP_EM_SLIPPERY_WALL,
4328 (level.em_slippery_gems &&
4329 engine_version > VERSION_IDENT(2,0,1,0)));
4331 /* special EM style explosion behaviour regarding chain reactions */
4332 for (i = 0; ep_em_explodes_by_fire[i] != -1; i++)
4333 SET_PROPERTY(ep_em_explodes_by_fire[i], EP_EXPLODES_BY_FIRE,
4334 level.em_explodes_by_fire);
4337 /* this is needed because some graphics depend on element properties */
4338 if (game_status == GAME_MODE_PLAYING)
4339 InitElementGraphicInfo();
4342 void InitElementPropertiesAfterLoading(int engine_version)
4346 /* set some other uninitialized values of custom elements in older levels */
4347 if (engine_version < VERSION_IDENT(3,1,0,0))
4349 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
4351 int element = EL_CUSTOM_START + i;
4353 element_info[element].access_direction = MV_ALL_DIRECTIONS;
4355 element_info[element].explosion_delay = 17;
4356 element_info[element].ignition_delay = 8;
4361 void InitElementPropertiesGfxElement()
4365 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4367 struct ElementInfo *ei = &element_info[i];
4369 ei->gfx_element = (ei->use_gfx_element ? ei->gfx_element_initial : i);
4373 static void InitGlobal()
4378 for (i = 0; i < MAX_NUM_ELEMENTS + 1; i++)
4380 /* check if element_name_info entry defined for each element in "main.h" */
4381 if (i < MAX_NUM_ELEMENTS && element_name_info[i].token_name == NULL)
4382 Error(ERR_EXIT, "undefined 'element_name_info' entry for element %d", i);
4384 element_info[i].token_name = element_name_info[i].token_name;
4385 element_info[i].class_name = element_name_info[i].class_name;
4386 element_info[i].editor_description= element_name_info[i].editor_description;
4389 /* create hash from image config list */
4390 image_config_hash = newSetupFileHash();
4391 for (i = 0; image_config[i].token != NULL; i++)
4392 setHashEntry(image_config_hash,
4393 image_config[i].token,
4394 image_config[i].value);
4396 /* create hash from element token list */
4397 element_token_hash = newSetupFileHash();
4398 for (i = 0; element_name_info[i].token_name != NULL; i++)
4399 setHashEntry(element_token_hash,
4400 element_name_info[i].token_name,
4403 /* create hash from graphic token list */
4404 graphic_token_hash = newSetupFileHash();
4405 for (graphic = 0, i = 0; image_config[i].token != NULL; i++)
4406 if (strSuffix(image_config[i].value, ".png") ||
4407 strSuffix(image_config[i].value, ".pcx") ||
4408 strSuffix(image_config[i].value, ".wav") ||
4409 strEqual(image_config[i].value, UNDEFINED_FILENAME))
4410 setHashEntry(graphic_token_hash,
4411 image_config[i].token,
4412 int2str(graphic++, 0));
4414 /* create hash from font token list */
4415 font_token_hash = newSetupFileHash();
4416 for (i = 0; font_info[i].token_name != NULL; i++)
4417 setHashEntry(font_token_hash,
4418 font_info[i].token_name,
4421 /* always start with reliable default values (all elements) */
4422 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4423 ActiveElement[i] = i;
4425 /* now add all entries that have an active state (active elements) */
4426 for (i = 0; element_with_active_state[i].element != -1; i++)
4428 int element = element_with_active_state[i].element;
4429 int element_active = element_with_active_state[i].element_active;
4431 ActiveElement[element] = element_active;
4434 /* always start with reliable default values (all buttons) */
4435 for (i = 0; i < NUM_IMAGE_FILES; i++)
4436 ActiveButton[i] = i;
4438 /* now add all entries that have an active state (active buttons) */
4439 for (i = 0; button_with_active_state[i].button != -1; i++)
4441 int button = button_with_active_state[i].button;
4442 int button_active = button_with_active_state[i].button_active;
4444 ActiveButton[button] = button_active;
4447 /* always start with reliable default values (all fonts) */
4448 for (i = 0; i < NUM_FONTS; i++)
4451 /* now add all entries that have an active state (active fonts) */
4452 for (i = 0; font_with_active_state[i].font_nr != -1; i++)
4454 int font = font_with_active_state[i].font_nr;
4455 int font_active = font_with_active_state[i].font_nr_active;
4457 ActiveFont[font] = font_active;
4460 global.autoplay_leveldir = NULL;
4461 global.convert_leveldir = NULL;
4462 global.create_images_dir = NULL;
4464 global.frames_per_second = 0;
4465 global.fps_slowdown = FALSE;
4466 global.fps_slowdown_factor = 1;
4468 global.border_status = GAME_MODE_MAIN;
4470 global.use_envelope_request = FALSE;
4473 void Execute_Command(char *command)
4477 if (strEqual(command, "print graphicsinfo.conf"))
4479 printf("# You can configure additional/alternative image files here.\n");
4480 printf("# (The entries below are default and therefore commented out.)\n");
4482 printf("%s\n", getFormattedSetupEntry("name", "Classic Graphics"));
4484 printf("%s\n", getFormattedSetupEntry("sort_priority", "100"));
4487 for (i = 0; image_config[i].token != NULL; i++)
4488 printf("# %s\n", getFormattedSetupEntry(image_config[i].token,
4489 image_config[i].value));
4493 else if (strEqual(command, "print soundsinfo.conf"))
4495 printf("# You can configure additional/alternative sound files here.\n");
4496 printf("# (The entries below are default and therefore commented out.)\n");
4498 printf("%s\n", getFormattedSetupEntry("name", "Classic Sounds"));
4500 printf("%s\n", getFormattedSetupEntry("sort_priority", "100"));
4503 for (i = 0; sound_config[i].token != NULL; i++)
4504 printf("# %s\n", getFormattedSetupEntry(sound_config[i].token,
4505 sound_config[i].value));
4509 else if (strEqual(command, "print musicinfo.conf"))
4511 printf("# You can configure additional/alternative music files here.\n");
4512 printf("# (The entries below are default and therefore commented out.)\n");
4514 printf("%s\n", getFormattedSetupEntry("name", "Classic Music"));
4516 printf("%s\n", getFormattedSetupEntry("sort_priority", "100"));
4519 for (i = 0; music_config[i].token != NULL; i++)
4520 printf("# %s\n", getFormattedSetupEntry(music_config[i].token,
4521 music_config[i].value));
4525 else if (strEqual(command, "print editorsetup.conf"))
4527 printf("# You can configure your personal editor element list here.\n");
4528 printf("# (The entries below are default and therefore commented out.)\n");
4531 /* this is needed to be able to check element list for cascade elements */
4532 InitElementPropertiesStatic();
4533 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
4535 PrintEditorElementList();
4539 else if (strEqual(command, "print helpanim.conf"))
4541 printf("# You can configure different element help animations here.\n");
4542 printf("# (The entries below are default and therefore commented out.)\n");
4545 for (i = 0; helpanim_config[i].token != NULL; i++)
4547 printf("# %s\n", getFormattedSetupEntry(helpanim_config[i].token,
4548 helpanim_config[i].value));
4550 if (strEqual(helpanim_config[i].token, "end"))
4556 else if (strEqual(command, "print helptext.conf"))
4558 printf("# You can configure different element help text here.\n");
4559 printf("# (The entries below are default and therefore commented out.)\n");
4562 for (i = 0; helptext_config[i].token != NULL; i++)
4563 printf("# %s\n", getFormattedSetupEntry(helptext_config[i].token,
4564 helptext_config[i].value));
4568 else if (strPrefix(command, "dump level "))
4570 char *filename = &command[11];
4572 if (!fileExists(filename))
4573 Error(ERR_EXIT, "cannot open file '%s'", filename);
4575 LoadLevelFromFilename(&level, filename);
4580 else if (strPrefix(command, "dump tape "))
4582 char *filename = &command[10];
4584 if (!fileExists(filename))
4585 Error(ERR_EXIT, "cannot open file '%s'", filename);
4587 LoadTapeFromFilename(filename);
4592 else if (strPrefix(command, "autoplay "))
4594 char *str_ptr = getStringCopy(&command[9]); /* read command parameters */
4596 while (*str_ptr != '\0') /* continue parsing string */
4598 /* cut leading whitespace from string, replace it by string terminator */
4599 while (*str_ptr == ' ' || *str_ptr == '\t')
4602 if (*str_ptr == '\0') /* end of string reached */
4605 if (global.autoplay_leveldir == NULL) /* read level set string */
4607 global.autoplay_leveldir = str_ptr;
4608 global.autoplay_all = TRUE; /* default: play all tapes */
4610 for (i = 0; i < MAX_TAPES_PER_SET; i++)
4611 global.autoplay_level[i] = FALSE;
4613 else /* read level number string */
4615 int level_nr = atoi(str_ptr); /* get level_nr value */
4617 if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
4618 global.autoplay_level[level_nr] = TRUE;
4620 global.autoplay_all = FALSE;
4623 /* advance string pointer to the next whitespace (or end of string) */
4624 while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
4628 else if (strPrefix(command, "convert "))
4630 char *str_copy = getStringCopy(strchr(command, ' ') + 1);
4631 char *str_ptr = strchr(str_copy, ' ');
4633 global.convert_leveldir = str_copy;
4634 global.convert_level_nr = -1;
4636 if (str_ptr != NULL) /* level number follows */
4638 *str_ptr++ = '\0'; /* terminate leveldir string */
4639 global.convert_level_nr = atoi(str_ptr); /* get level_nr value */
4642 else if (strPrefix(command, "create images "))
4644 global.create_images_dir = getStringCopy(&command[14]);
4646 if (access(global.create_images_dir, W_OK) != 0)
4647 Error(ERR_EXIT, "image target directory '%s' not found or not writable",
4648 global.create_images_dir);
4652 #if defined(TARGET_SDL2)
4653 else if (strEqual(command, "SDL_ListModes"))
4655 SDL_Init(SDL_INIT_VIDEO);
4657 int num_displays = SDL_GetNumVideoDisplays();
4659 // check if there are any displays available
4660 if (num_displays < 0)
4662 printf("No displays available: %s\n", SDL_GetError());
4667 for (i = 0; i < num_displays; i++)
4669 int num_modes = SDL_GetNumDisplayModes(i);
4672 printf("Available display modes for display %d:\n", i);
4674 // check if there are any display modes available for this display
4677 printf("No display modes available for display %d: %s\n",
4683 for (j = 0; j < num_modes; j++)
4685 SDL_DisplayMode mode;
4687 if (SDL_GetDisplayMode(i, j, &mode) < 0)
4689 printf("Cannot get display mode %d for display %d: %s\n",
4690 j, i, SDL_GetError());
4695 printf("- %d x %d\n", mode.w, mode.h);
4701 #elif defined(TARGET_SDL)
4702 else if (strEqual(command, "SDL_ListModes"))
4707 SDL_Init(SDL_INIT_VIDEO);
4709 /* get available fullscreen/hardware modes */
4710 modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE);
4712 /* check if there are any modes available */
4715 printf("No modes available!\n");
4720 /* check if our resolution is restricted */
4721 if (modes == (SDL_Rect **)-1)
4723 printf("All resolutions available.\n");
4727 printf("Available display modes:\n");
4729 for (i = 0; modes[i]; i++)
4730 printf("- %d x %d\n", modes[i]->w, modes[i]->h);
4740 Error(ERR_EXIT_HELP, "unrecognized command '%s'", command);
4744 static void InitSetup()
4746 LoadSetup(); /* global setup info */
4748 /* set some options from setup file */
4750 if (setup.options.verbose)
4751 options.verbose = TRUE;
4754 static void InitGameInfo()
4756 game.restart_level = FALSE;
4759 static void InitPlayerInfo()
4763 /* choose default local player */
4764 local_player = &stored_player[0];
4766 for (i = 0; i < MAX_PLAYERS; i++)
4767 stored_player[i].connected = FALSE;
4769 local_player->connected = TRUE;
4772 static void InitArtworkInfo()
4777 static char *get_string_in_brackets(char *string)
4779 char *string_in_brackets = checked_malloc(strlen(string) + 3);
4781 sprintf(string_in_brackets, "[%s]", string);
4783 return string_in_brackets;
4786 static char *get_level_id_suffix(int id_nr)
4788 char *id_suffix = checked_malloc(1 + 3 + 1);
4790 if (id_nr < 0 || id_nr > 999)
4793 sprintf(id_suffix, ".%03d", id_nr);
4798 static void InitArtworkConfig()
4800 static char *image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + 1];
4801 static char *sound_id_prefix[2 * MAX_NUM_ELEMENTS + 1];
4802 static char *music_id_prefix[NUM_MUSIC_PREFIXES + 1];
4803 static char *action_id_suffix[NUM_ACTIONS + 1];
4804 static char *direction_id_suffix[NUM_DIRECTIONS_FULL + 1];
4805 static char *special_id_suffix[NUM_SPECIAL_GFX_ARGS + 1];
4806 static char *level_id_suffix[MAX_LEVELS + 1];
4807 static char *dummy[1] = { NULL };
4808 static char *ignore_generic_tokens[] =
4814 static char **ignore_image_tokens;
4815 static char **ignore_sound_tokens;
4816 static char **ignore_music_tokens;
4817 int num_ignore_generic_tokens;
4818 int num_ignore_image_tokens;
4819 int num_ignore_sound_tokens;
4820 int num_ignore_music_tokens;
4823 /* dynamically determine list of generic tokens to be ignored */
4824 num_ignore_generic_tokens = 0;
4825 for (i = 0; ignore_generic_tokens[i] != NULL; i++)
4826 num_ignore_generic_tokens++;
4828 /* dynamically determine list of image tokens to be ignored */
4829 num_ignore_image_tokens = num_ignore_generic_tokens;
4830 for (i = 0; image_config_vars[i].token != NULL; i++)
4831 num_ignore_image_tokens++;
4832 ignore_image_tokens =
4833 checked_malloc((num_ignore_image_tokens + 1) * sizeof(char *));
4834 for (i = 0; i < num_ignore_generic_tokens; i++)
4835 ignore_image_tokens[i] = ignore_generic_tokens[i];
4836 for (i = 0; i < num_ignore_image_tokens - num_ignore_generic_tokens; i++)
4837 ignore_image_tokens[num_ignore_generic_tokens + i] =
4838 image_config_vars[i].token;
4839 ignore_image_tokens[num_ignore_image_tokens] = NULL;
4841 /* dynamically determine list of sound tokens to be ignored */
4842 num_ignore_sound_tokens = num_ignore_generic_tokens;
4843 ignore_sound_tokens =
4844 checked_malloc((num_ignore_sound_tokens + 1) * sizeof(char *));
4845 for (i = 0; i < num_ignore_generic_tokens; i++)
4846 ignore_sound_tokens[i] = ignore_generic_tokens[i];
4847 ignore_sound_tokens[num_ignore_sound_tokens] = NULL;
4849 /* dynamically determine list of music tokens to be ignored */
4850 num_ignore_music_tokens = num_ignore_generic_tokens;
4851 ignore_music_tokens =
4852 checked_malloc((num_ignore_music_tokens + 1) * sizeof(char *));
4853 for (i = 0; i < num_ignore_generic_tokens; i++)
4854 ignore_music_tokens[i] = ignore_generic_tokens[i];
4855 ignore_music_tokens[num_ignore_music_tokens] = NULL;
4857 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4858 image_id_prefix[i] = element_info[i].token_name;
4859 for (i = 0; i < NUM_FONTS; i++)
4860 image_id_prefix[MAX_NUM_ELEMENTS + i] = font_info[i].token_name;
4861 image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS] = NULL;
4863 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4864 sound_id_prefix[i] = element_info[i].token_name;
4865 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4866 sound_id_prefix[MAX_NUM_ELEMENTS + i] =
4867 get_string_in_brackets(element_info[i].class_name);
4868 sound_id_prefix[2 * MAX_NUM_ELEMENTS] = NULL;
4870 for (i = 0; i < NUM_MUSIC_PREFIXES; i++)
4871 music_id_prefix[i] = music_prefix_info[i].prefix;
4872 music_id_prefix[NUM_MUSIC_PREFIXES] = NULL;
4874 for (i = 0; i < NUM_ACTIONS; i++)
4875 action_id_suffix[i] = element_action_info[i].suffix;
4876 action_id_suffix[NUM_ACTIONS] = NULL;
4878 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
4879 direction_id_suffix[i] = element_direction_info[i].suffix;
4880 direction_id_suffix[NUM_DIRECTIONS_FULL] = NULL;
4882 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
4883 special_id_suffix[i] = special_suffix_info[i].suffix;
4884 special_id_suffix[NUM_SPECIAL_GFX_ARGS] = NULL;
4886 for (i = 0; i < MAX_LEVELS; i++)
4887 level_id_suffix[i] = get_level_id_suffix(i);
4888 level_id_suffix[MAX_LEVELS] = NULL;
4890 InitImageList(image_config, NUM_IMAGE_FILES, image_config_suffix,
4891 image_id_prefix, action_id_suffix, direction_id_suffix,
4892 special_id_suffix, ignore_image_tokens);
4893 InitSoundList(sound_config, NUM_SOUND_FILES, sound_config_suffix,
4894 sound_id_prefix, action_id_suffix, dummy,
4895 special_id_suffix, ignore_sound_tokens);
4896 InitMusicList(music_config, NUM_MUSIC_FILES, music_config_suffix,
4897 music_id_prefix, special_id_suffix, level_id_suffix,
4898 dummy, ignore_music_tokens);
4901 static void InitMixer()
4908 void InitGfxBuffers()
4910 /* create additional image buffers for double-buffering and cross-fading */
4911 ReCreateBitmap(&bitmap_db_store, WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
4912 ReCreateBitmap(&bitmap_db_cross, WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
4913 ReCreateBitmap(&bitmap_db_field, FXSIZE, FYSIZE, DEFAULT_DEPTH);
4914 ReCreateBitmap(&bitmap_db_panel, DXSIZE, DYSIZE, DEFAULT_DEPTH);
4915 ReCreateBitmap(&bitmap_db_door_1, 3 * DXSIZE, DYSIZE, DEFAULT_DEPTH);
4916 ReCreateBitmap(&bitmap_db_door_2, 3 * VXSIZE, VYSIZE, DEFAULT_DEPTH);
4917 ReCreateBitmap(&bitmap_db_toons, FULL_SXSIZE, FULL_SYSIZE, DEFAULT_DEPTH);
4919 /* initialize screen properties */
4920 InitGfxFieldInfo(SX, SY, SXSIZE, SYSIZE,
4921 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
4923 InitGfxDoor1Info(DX, DY, DXSIZE, DYSIZE);
4924 InitGfxDoor2Info(VX, VY, VXSIZE, VYSIZE);
4925 InitGfxDoor3Info(EX, EY, EXSIZE, EYSIZE);
4926 InitGfxWindowInfo(WIN_XSIZE, WIN_YSIZE);
4927 InitGfxScrollbufferInfo(FXSIZE, FYSIZE);
4928 InitGfxClipRegion(FALSE, -1, -1, -1, -1);
4930 InitGfxBuffers_EM();
4931 InitGfxBuffers_SP();
4936 struct GraphicInfo *graphic_info_last = graphic_info;
4937 char *filename_font_initial = NULL;
4938 char *filename_anim_initial = NULL;
4939 Bitmap *bitmap_font_initial = NULL;
4943 /* determine settings for initial font (for displaying startup messages) */
4944 for (i = 0; image_config[i].token != NULL; i++)
4946 for (j = 0; j < NUM_INITIAL_FONTS; j++)
4948 char font_token[128];
4951 sprintf(font_token, "%s_%d", CONFIG_TOKEN_FONT_INITIAL, j + 1);
4952 len_font_token = strlen(font_token);
4954 if (strEqual(image_config[i].token, font_token))
4955 filename_font_initial = image_config[i].value;
4956 else if (strlen(image_config[i].token) > len_font_token &&
4957 strncmp(image_config[i].token, font_token, len_font_token) == 0)
4959 if (strEqual(&image_config[i].token[len_font_token], ".x"))
4960 font_initial[j].src_x = atoi(image_config[i].value);
4961 else if (strEqual(&image_config[i].token[len_font_token], ".y"))
4962 font_initial[j].src_y = atoi(image_config[i].value);
4963 else if (strEqual(&image_config[i].token[len_font_token], ".width"))
4964 font_initial[j].width = atoi(image_config[i].value);
4965 else if (strEqual(&image_config[i].token[len_font_token], ".height"))
4966 font_initial[j].height = atoi(image_config[i].value);
4971 for (j = 0; j < NUM_INITIAL_FONTS; j++)
4973 font_initial[j].num_chars = DEFAULT_NUM_CHARS_PER_FONT;
4974 font_initial[j].num_chars_per_line = DEFAULT_NUM_CHARS_PER_LINE;
4977 if (filename_font_initial == NULL) /* should not happen */
4978 Error(ERR_EXIT, "cannot get filename for '%s'", CONFIG_TOKEN_FONT_INITIAL);
4981 InitGfxCustomArtworkInfo();
4983 bitmap_font_initial = LoadCustomImage(filename_font_initial);
4985 for (j = 0; j < NUM_INITIAL_FONTS; j++)
4986 font_initial[j].bitmap = bitmap_font_initial;
4988 InitFontGraphicInfo();
4990 font_height = getFontHeight(FC_RED);
4992 DrawInitTextAlways(getProgramInitString(), 20, FC_YELLOW);
4993 DrawInitTextAlways(PROGRAM_COPYRIGHT_STRING, 50, FC_RED);
4994 DrawInitTextAlways(PROGRAM_WEBSITE_STRING, WIN_YSIZE - 20 - font_height,
4997 DrawInitTextAlways("Loading graphics", 120, FC_GREEN);
4999 /* initialize busy animation with default values */
5000 int parameter[NUM_GFX_ARGS];
5001 for (i = 0; i < NUM_GFX_ARGS; i++)
5002 parameter[i] = get_graphic_parameter_value(image_config_suffix[i].value,
5003 image_config_suffix[i].token,
5004 image_config_suffix[i].type);
5006 /* determine settings for busy animation (when displaying startup messages) */
5007 for (i = 0; image_config[i].token != NULL; i++)
5009 char *anim_token = CONFIG_TOKEN_GLOBAL_BUSY;
5010 int len_anim_token = strlen(anim_token);
5012 if (strEqual(image_config[i].token, anim_token))
5013 filename_anim_initial = image_config[i].value;
5014 else if (strlen(image_config[i].token) > len_anim_token &&
5015 strncmp(image_config[i].token, anim_token, len_anim_token) == 0)
5017 for (j = 0; image_config_suffix[j].token != NULL; j++)
5019 if (strEqual(&image_config[i].token[len_anim_token],
5020 image_config_suffix[j].token))
5022 get_graphic_parameter_value(image_config[i].value,
5023 image_config_suffix[j].token,
5024 image_config_suffix[j].type);
5029 #if defined(CREATE_SPECIAL_EDITION_RND_JUE)
5030 filename_anim_initial = "loading.pcx";
5032 parameter[GFX_ARG_X] = 0;
5033 parameter[GFX_ARG_Y] = 0;
5034 parameter[GFX_ARG_WIDTH] = 128;
5035 parameter[GFX_ARG_HEIGHT] = 40;
5036 parameter[GFX_ARG_FRAMES] = 32;
5037 parameter[GFX_ARG_DELAY] = 4;
5038 parameter[GFX_ARG_FRAMES_PER_LINE] = ARG_UNDEFINED_VALUE;
5041 if (filename_anim_initial == NULL) /* should not happen */
5042 Error(ERR_EXIT, "cannot get filename for '%s'", CONFIG_TOKEN_GLOBAL_BUSY);
5044 anim_initial.bitmap = LoadCustomImage(filename_anim_initial);
5046 graphic_info = &anim_initial; /* graphic == 0 => anim_initial */
5048 set_graphic_parameters_ext(0, parameter, anim_initial.bitmap);
5050 graphic_info = graphic_info_last;
5052 init.busy.width = anim_initial.width;
5053 init.busy.height = anim_initial.height;
5055 InitMenuDesignSettings_Static();
5056 InitGfxDrawBusyAnimFunction(DrawInitAnim);
5058 /* use copy of busy animation to prevent change while reloading artwork */
5062 void RedrawBackground()
5064 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, backbuffer,
5065 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
5067 redraw_mask = REDRAW_ALL;
5070 void InitGfxBackground()
5074 fieldbuffer = bitmap_db_field;
5075 SetDrawtoField(DRAW_BACKBUFFER);
5077 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
5079 for (x = 0; x < MAX_BUF_XSIZE; x++)
5080 for (y = 0; y < MAX_BUF_YSIZE; y++)
5083 redraw_mask = REDRAW_ALL;
5086 static void InitLevelInfo()
5088 LoadLevelInfo(); /* global level info */
5089 LoadLevelSetup_LastSeries(); /* last played series info */
5090 LoadLevelSetup_SeriesInfo(); /* last played level info */
5093 static void InitLevelArtworkInfo()
5095 LoadLevelArtworkInfo();
5098 static void InitImages()
5100 print_timestamp_init("InitImages");
5103 printf("::: leveldir_current->identifier == '%s'\n",
5104 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5105 printf("::: leveldir_current->graphics_path == '%s'\n",
5106 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5107 printf("::: leveldir_current->graphics_set == '%s'\n",
5108 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5109 printf("::: getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'\n",
5110 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5113 setLevelArtworkDir(artwork.gfx_first);
5116 printf("::: leveldir_current->identifier == '%s'\n",
5117 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5118 printf("::: leveldir_current->graphics_path == '%s'\n",
5119 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5120 printf("::: leveldir_current->graphics_set == '%s'\n",
5121 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5122 printf("::: getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'\n",
5123 leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
5127 printf("::: InitImages for '%s' ['%s', '%s'] ['%s', '%s']\n",
5128 leveldir_current->identifier,
5129 artwork.gfx_current_identifier,
5130 artwork.gfx_current->identifier,
5131 leveldir_current->graphics_set,
5132 leveldir_current->graphics_path);
5135 UPDATE_BUSY_STATE();
5137 ReloadCustomImages();
5138 print_timestamp_time("ReloadCustomImages");
5140 UPDATE_BUSY_STATE();
5142 LoadCustomElementDescriptions();
5143 print_timestamp_time("LoadCustomElementDescriptions");
5145 UPDATE_BUSY_STATE();
5147 LoadMenuDesignSettings();
5148 print_timestamp_time("LoadMenuDesignSettings");
5150 UPDATE_BUSY_STATE();
5152 ReinitializeGraphics();
5153 print_timestamp_time("ReinitializeGraphics");
5155 UPDATE_BUSY_STATE();
5157 print_timestamp_done("InitImages");
5160 static void InitSound(char *identifier)
5162 print_timestamp_init("InitSound");
5164 if (identifier == NULL)
5165 identifier = artwork.snd_current->identifier;
5167 /* set artwork path to send it to the sound server process */
5168 setLevelArtworkDir(artwork.snd_first);
5170 InitReloadCustomSounds(identifier);
5171 print_timestamp_time("InitReloadCustomSounds");
5173 ReinitializeSounds();
5174 print_timestamp_time("ReinitializeSounds");
5176 print_timestamp_done("InitSound");
5179 static void InitMusic(char *identifier)
5181 print_timestamp_init("InitMusic");
5183 if (identifier == NULL)
5184 identifier = artwork.mus_current->identifier;
5186 /* set artwork path to send it to the sound server process */
5187 setLevelArtworkDir(artwork.mus_first);
5189 InitReloadCustomMusic(identifier);
5190 print_timestamp_time("InitReloadCustomMusic");
5192 ReinitializeMusic();
5193 print_timestamp_time("ReinitializeMusic");
5195 print_timestamp_done("InitMusic");
5198 void InitNetworkServer()
5200 #if defined(NETWORK_AVALIABLE)
5204 if (!options.network)
5207 #if defined(NETWORK_AVALIABLE)
5208 nr_wanted = Request("Choose player", REQ_PLAYER | REQ_STAY_CLOSED);
5210 if (!ConnectToServer(options.server_host, options.server_port))
5211 Error(ERR_EXIT, "cannot connect to network game server");
5213 SendToServer_PlayerName(setup.player_name);
5214 SendToServer_ProtocolVersion();
5217 SendToServer_NrWanted(nr_wanted);
5221 static boolean CheckArtworkConfigForCustomElements(char *filename)
5223 SetupFileHash *setup_file_hash;
5224 boolean redefined_ce_found = FALSE;
5226 /* !!! CACHE THIS BY USING HASH 'filename' => 'true/false' !!! */
5228 if ((setup_file_hash = loadSetupFileHash(filename)) != NULL)
5230 BEGIN_HASH_ITERATION(setup_file_hash, itr)
5232 char *token = HASH_ITERATION_TOKEN(itr);
5234 if (strPrefix(token, "custom_"))
5236 redefined_ce_found = TRUE;
5241 END_HASH_ITERATION(setup_file_hash, itr)
5243 freeSetupFileHash(setup_file_hash);
5246 return redefined_ce_found;
5249 static boolean CheckArtworkTypeForRedefinedCustomElements(int type)
5251 char *filename_base, *filename_local;
5252 boolean redefined_ce_found = FALSE;
5254 setLevelArtworkDir(ARTWORK_FIRST_NODE(artwork, type));
5257 printf("::: leveldir_current->identifier == '%s'\n",
5258 leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
5259 printf("::: leveldir_current->graphics_path == '%s'\n",
5260 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
5261 printf("::: leveldir_current->graphics_set == '%s'\n",
5262 leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
5263 printf("::: getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'\n",
5264 leveldir_current == NULL ? "[NULL]" :
5265 LEVELDIR_ARTWORK_SET(leveldir_current, type));
5268 /* first look for special artwork configured in level series config */
5269 filename_base = getCustomArtworkLevelConfigFilename(type);
5272 printf("::: filename_base == '%s'\n", filename_base);
5275 if (fileExists(filename_base))
5276 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_base);
5278 filename_local = getCustomArtworkConfigFilename(type);
5281 printf("::: filename_local == '%s'\n", filename_local);
5284 if (filename_local != NULL && !strEqual(filename_base, filename_local))
5285 redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_local);
5288 printf("::: redefined_ce_found == %d\n", redefined_ce_found);
5291 return redefined_ce_found;
5294 static void InitOverrideArtwork()
5296 boolean redefined_ce_found = FALSE;
5298 /* to check if this level set redefines any CEs, do not use overriding */
5299 gfx.override_level_graphics = FALSE;
5300 gfx.override_level_sounds = FALSE;
5301 gfx.override_level_music = FALSE;
5303 /* now check if this level set has definitions for custom elements */
5304 if (setup.override_level_graphics == AUTO ||
5305 setup.override_level_sounds == AUTO ||
5306 setup.override_level_music == AUTO)
5307 redefined_ce_found =
5308 (CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_GRAPHICS) |
5309 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_SOUNDS) |
5310 CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_MUSIC));
5313 printf("::: redefined_ce_found == %d\n", redefined_ce_found);
5316 if (redefined_ce_found)
5318 /* this level set has CE definitions: change "AUTO" to "FALSE" */
5319 gfx.override_level_graphics = (setup.override_level_graphics == TRUE);
5320 gfx.override_level_sounds = (setup.override_level_sounds == TRUE);
5321 gfx.override_level_music = (setup.override_level_music == TRUE);
5325 /* this level set has no CE definitions: change "AUTO" to "TRUE" */
5326 gfx.override_level_graphics = (setup.override_level_graphics != FALSE);
5327 gfx.override_level_sounds = (setup.override_level_sounds != FALSE);
5328 gfx.override_level_music = (setup.override_level_music != FALSE);
5332 printf("::: => %d, %d, %d\n",
5333 gfx.override_level_graphics,
5334 gfx.override_level_sounds,
5335 gfx.override_level_music);
5339 static char *getNewArtworkIdentifier(int type)
5341 static char *leveldir_current_identifier[3] = { NULL, NULL, NULL };
5342 static boolean last_override_level_artwork[3] = { FALSE, FALSE, FALSE };
5343 static boolean last_has_level_artwork_set[3] = { FALSE, FALSE, FALSE };
5344 static boolean initialized[3] = { FALSE, FALSE, FALSE };
5345 TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type);
5346 boolean setup_override_artwork = GFX_OVERRIDE_ARTWORK(type);
5347 char *setup_artwork_set = SETUP_ARTWORK_SET(setup, type);
5348 char *leveldir_identifier = leveldir_current->identifier;
5349 /* !!! setLevelArtworkDir() should be moved to an earlier stage !!! */
5350 char *leveldir_artwork_set = setLevelArtworkDir(artwork_first_node);
5351 boolean has_level_artwork_set = (leveldir_artwork_set != NULL);
5352 char *artwork_current_identifier;
5353 char *artwork_new_identifier = NULL; /* default: nothing has changed */
5355 /* leveldir_current may be invalid (level group, parent link) */
5356 if (!validLevelSeries(leveldir_current))
5359 /* 1st step: determine artwork set to be activated in descending order:
5360 --------------------------------------------------------------------
5361 1. setup artwork (when configured to override everything else)
5362 2. artwork set configured in "levelinfo.conf" of current level set
5363 (artwork in level directory will have priority when loading later)
5364 3. artwork in level directory (stored in artwork sub-directory)
5365 4. setup artwork (currently configured in setup menu) */
5367 if (setup_override_artwork)
5368 artwork_current_identifier = setup_artwork_set;
5369 else if (leveldir_artwork_set != NULL)
5370 artwork_current_identifier = leveldir_artwork_set;
5371 else if (getTreeInfoFromIdentifier(artwork_first_node, leveldir_identifier))
5372 artwork_current_identifier = leveldir_identifier;
5374 artwork_current_identifier = setup_artwork_set;
5377 /* 2nd step: check if it is really needed to reload artwork set
5378 ------------------------------------------------------------ */
5380 /* ---------- reload if level set and also artwork set has changed ------- */
5381 if (leveldir_current_identifier[type] != leveldir_identifier &&
5382 (last_has_level_artwork_set[type] || has_level_artwork_set))
5383 artwork_new_identifier = artwork_current_identifier;
5385 leveldir_current_identifier[type] = leveldir_identifier;
5386 last_has_level_artwork_set[type] = has_level_artwork_set;
5388 /* ---------- reload if "override artwork" setting has changed ----------- */
5389 if (last_override_level_artwork[type] != setup_override_artwork)
5390 artwork_new_identifier = artwork_current_identifier;
5392 last_override_level_artwork[type] = setup_override_artwork;
5394 /* ---------- reload if current artwork identifier has changed ----------- */
5395 if (!strEqual(ARTWORK_CURRENT_IDENTIFIER(artwork, type),
5396 artwork_current_identifier))
5397 artwork_new_identifier = artwork_current_identifier;
5399 *(ARTWORK_CURRENT_IDENTIFIER_PTR(artwork, type))= artwork_current_identifier;
5401 /* ---------- do not reload directly after starting ---------------------- */
5402 if (!initialized[type])
5403 artwork_new_identifier = NULL;
5405 initialized[type] = TRUE;
5407 return artwork_new_identifier;
5410 void ReloadCustomArtwork(int force_reload)
5412 int last_game_status = game_status; /* save current game status */
5413 char *gfx_new_identifier;
5414 char *snd_new_identifier;
5415 char *mus_new_identifier;
5416 boolean force_reload_gfx = (force_reload & (1 << ARTWORK_TYPE_GRAPHICS));
5417 boolean force_reload_snd = (force_reload & (1 << ARTWORK_TYPE_SOUNDS));
5418 boolean force_reload_mus = (force_reload & (1 << ARTWORK_TYPE_MUSIC));
5419 boolean reload_needed;
5421 InitOverrideArtwork();
5423 force_reload_gfx |= AdjustGraphicsForEMC();
5425 gfx_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
5426 snd_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
5427 mus_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
5429 reload_needed = (gfx_new_identifier != NULL || force_reload_gfx ||
5430 snd_new_identifier != NULL || force_reload_snd ||
5431 mus_new_identifier != NULL || force_reload_mus);
5436 print_timestamp_init("ReloadCustomArtwork");
5438 game_status = GAME_MODE_LOADING;
5440 FadeOut(REDRAW_ALL);
5442 ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
5443 print_timestamp_time("ClearRectangle");
5447 if (gfx_new_identifier != NULL || force_reload_gfx)
5450 printf("RELOADING GRAPHICS '%s' -> '%s' ['%s', '%s']\n",
5451 artwork.gfx_current_identifier,
5453 artwork.gfx_current->identifier,
5454 leveldir_current->graphics_set);
5458 print_timestamp_time("InitImages");
5461 if (snd_new_identifier != NULL || force_reload_snd)
5463 InitSound(snd_new_identifier);
5464 print_timestamp_time("InitSound");
5467 if (mus_new_identifier != NULL || force_reload_mus)
5469 InitMusic(mus_new_identifier);
5470 print_timestamp_time("InitMusic");
5473 game_status = last_game_status; /* restore current game status */
5475 init_last = init; /* switch to new busy animation */
5477 FadeOut(REDRAW_ALL);
5481 /* force redraw of (open or closed) door graphics */
5482 SetDoorState(DOOR_OPEN_ALL);
5483 CloseDoor(DOOR_CLOSE_ALL | DOOR_NO_DELAY);
5485 FadeSetEnterScreen();
5486 FadeSkipNextFadeOut();
5488 print_timestamp_done("ReloadCustomArtwork");
5490 LimitScreenUpdates(FALSE);
5493 void KeyboardAutoRepeatOffUnlessAutoplay()
5495 if (global.autoplay_leveldir == NULL)
5496 KeyboardAutoRepeatOff();
5499 void DisplayExitMessage(char *format, va_list ap)
5501 // check if draw buffer and fonts for exit message are already available
5502 if (drawto == NULL || font_initial[NUM_INITIAL_FONTS - 1].bitmap == NULL)
5505 int font_1 = FC_RED;
5506 int font_2 = FC_YELLOW;
5507 int font_3 = FC_BLUE;
5508 int font_width = getFontWidth(font_2);
5509 int font_height = getFontHeight(font_2);
5512 int sxsize = WIN_XSIZE - 2 * sx;
5513 int sysize = WIN_YSIZE - 2 * sy;
5514 int line_length = sxsize / font_width;
5515 int max_lines = sysize / font_height;
5516 int num_lines_printed;
5520 gfx.sxsize = sxsize;
5521 gfx.sysize = sysize;
5525 ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
5527 DrawTextSCentered(sy, font_1, "Fatal error:");
5528 sy += 3 * font_height;;
5531 DrawTextBufferVA(sx, sy, format, ap, font_2,
5532 line_length, line_length, max_lines,
5533 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
5534 sy += (num_lines_printed + 3) * font_height;
5536 DrawTextSCentered(sy, font_1, "For details, see the following error file:");
5537 sy += 3 * font_height;
5540 DrawTextBuffer(sx, sy, program.error_filename, font_2,
5541 line_length, line_length, max_lines,
5542 0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
5544 DrawTextSCentered(SYSIZE - 20, font_3, "Press any key or button to exit");
5546 redraw_mask = REDRAW_ALL;
5550 /* deactivate toons on error message screen */
5551 setup.toons = FALSE;
5553 WaitForEventToContinue();
5557 /* ========================================================================= */
5559 /* ========================================================================= */
5563 print_timestamp_init("OpenAll");
5565 game_status = GAME_MODE_LOADING;
5569 InitGlobal(); /* initialize some global variables */
5571 print_timestamp_time("[init global stuff]");
5573 if (options.execute_command)
5574 Execute_Command(options.execute_command);
5576 if (options.serveronly)
5578 #if defined(PLATFORM_UNIX)
5579 NetworkServer(options.server_port, options.serveronly);
5581 Error(ERR_WARN, "networking only supported in Unix version");
5584 exit(0); /* never reached, server loops forever */
5589 print_timestamp_time("[init setup/config stuff (1)]");
5592 print_timestamp_time("[init setup/config stuff (2)]");
5594 print_timestamp_time("[init setup/config stuff (3)]");
5595 InitArtworkInfo(); /* needed before loading gfx, sound & music */
5596 print_timestamp_time("[init setup/config stuff (4)]");
5597 InitArtworkConfig(); /* needed before forking sound child process */
5598 print_timestamp_time("[init setup/config stuff (5)]");
5600 print_timestamp_time("[init setup/config stuff (6)]");
5602 InitRND(NEW_RANDOMIZE);
5603 InitSimpleRandom(NEW_RANDOMIZE);
5607 print_timestamp_time("[init setup/config stuff]");
5610 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
5612 InitEventFilter(FilterEvents);
5614 print_timestamp_time("[init video stuff]");
5616 InitElementPropertiesStatic();
5617 InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
5618 InitElementPropertiesGfxElement();
5620 print_timestamp_time("[init element properties stuff]");
5624 print_timestamp_time("InitGfx");
5627 print_timestamp_time("InitLevelInfo");
5629 InitLevelArtworkInfo();
5630 print_timestamp_time("InitLevelArtworkInfo");
5632 InitOverrideArtwork(); /* needs to know current level directory */
5633 print_timestamp_time("InitOverrideArtwork");
5635 InitImages(); /* needs to know current level directory */
5636 print_timestamp_time("InitImages");
5638 InitSound(NULL); /* needs to know current level directory */
5639 print_timestamp_time("InitSound");
5641 InitMusic(NULL); /* needs to know current level directory */
5642 print_timestamp_time("InitMusic");
5644 InitGfxBackground();
5649 if (global.autoplay_leveldir)
5654 else if (global.convert_leveldir)
5659 else if (global.create_images_dir)
5661 CreateLevelSketchImages();
5665 game_status = GAME_MODE_MAIN;
5667 FadeSetEnterScreen();
5668 if (!(fading.fade_mode & FADE_TYPE_TRANSFORM))
5669 FadeSkipNextFadeOut();
5671 print_timestamp_time("[post-artwork]");
5673 print_timestamp_done("OpenAll");
5677 InitNetworkServer();
5680 Error(ERR_DEBUG, "::: SDL_GetBasePath() == '%s'",
5682 Error(ERR_DEBUG, "::: SDL_GetPrefPath() == '%s'",
5683 SDL_GetPrefPath("artsoft", "rocksndiamonds"));
5684 #if defined(PLATFORM_ANDROID)
5685 Error(ERR_DEBUG, "::: SDL_AndroidGetInternalStoragePath() == '%s'",
5686 SDL_AndroidGetInternalStoragePath());
5687 Error(ERR_DEBUG, "::: SDL_AndroidGetExternalStoragePath() == '%s'",
5688 SDL_AndroidGetExternalStoragePath());
5689 Error(ERR_DEBUG, "::: SDL_AndroidGetExternalStorageState() == '%s'",
5690 (SDL_AndroidGetExternalStorageState() ==
5691 SDL_ANDROID_EXTERNAL_STORAGE_READ ? "read" :
5692 SDL_AndroidGetExternalStorageState() ==
5693 SDL_ANDROID_EXTERNAL_STORAGE_WRITE ? "write" : "not available"));
5698 void CloseAllAndExit(int exit_value)
5703 CloseAudio(); /* called after freeing sounds (needed for SDL) */
5710 #if defined(TARGET_SDL)
5711 #if defined(TARGET_SDL2)
5713 // set a flag to tell the network server thread to quit and wait for it
5714 // using SDL_WaitThread()
5716 if (network_server) /* terminate network server */
5717 SDL_KillThread(server_thread);
5721 CloseVideoDisplay();
5722 ClosePlatformDependentStuff();
5724 if (exit_value != 0)
5726 /* fall back to default level set (current set may have caused an error) */
5727 SaveLevelSetup_LastSeries_Deactivate();
5729 /* tell user where to find error log file which may contain more details */
5730 // (error notification now directly displayed on screen inside R'n'D
5731 // NotifyUserAboutErrorFile(); /* currently only works for Windows */