+ struct PropertyMapping *property_mapping = getImageListPropertyMapping();
+ int num_property_mappings = getImageListPropertyMappingSize();
+ int i, j;
+
+ /* always start with reliable default values */
+ for (i=0; i < MAX_NUM_ELEMENTS; i++)
+ for (j=0; j < NUM_SPECIAL_GFX_ARGS; j++)
+ element_info[i].special_graphic[j] =
+ element_info[i].graphic[ACTION_DEFAULT];
+
+ /* initialize special element/graphic mapping from static configuration */
+ for (i=0; element_to_special_graphic[i].element > -1; i++)
+ {
+ int element = element_to_special_graphic[i].element;
+ int special = element_to_special_graphic[i].special;
+ int graphic = element_to_special_graphic[i].graphic;
+ boolean base_redefined = getImageListEntry(el2img(element))->redefined;
+ boolean special_redefined = getImageListEntry(graphic)->redefined;
+
+ if (base_redefined && !special_redefined)
+ continue;
+
+ element_info[element].special_graphic[special] = graphic;
+ }
+
+ /* initialize special element/graphic mapping from dynamic configuration */
+ for (i=0; i < num_property_mappings; i++)
+ {
+ int element = property_mapping[i].base_index;
+ int special = property_mapping[i].ext3_index;
+ int graphic = property_mapping[i].artwork_index;
+
+ if (element >= MAX_NUM_ELEMENTS)
+ continue;
+
+ if (special >= 0 && special < NUM_SPECIAL_GFX_ARGS)
+ element_info[element].special_graphic[special] = graphic;
+ }
+}
+
+static void set_graphic_parameters(int graphic, char **parameter_raw)
+{
+ Bitmap *src_bitmap = getBitmapFromImageID(graphic);
+ int num_xtiles = (src_bitmap ? src_bitmap->width : TILEX) / TILEX;
+ int num_ytiles = (src_bitmap ? src_bitmap->height * 2 / 3 : TILEY) / TILEY;
+ int parameter[NUM_GFX_ARGS];
+ int i;
+
+ /* get integer values from string parameters */
+ for (i=0; i < NUM_GFX_ARGS; i++)
+ parameter[i] =
+ get_parameter_value(image_config_suffix[i].token, parameter_raw[i],
+ image_config_suffix[i].type);
+
+ graphic_info[graphic].bitmap = src_bitmap;
+
+ /* start with reliable default values */
+ graphic_info[graphic].src_x = 0;
+ graphic_info[graphic].src_y = 0;
+ graphic_info[graphic].width = TILEX;
+ graphic_info[graphic].height = TILEY;
+ graphic_info[graphic].offset_x = 0; /* one or both of these values ... */
+ graphic_info[graphic].offset_y = 0; /* ... will be corrected later */
+
+ /* optional x and y tile position of animation frame sequence */
+ if (parameter[GFX_ARG_XPOS] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].src_x = parameter[GFX_ARG_XPOS] * TILEX;
+ if (parameter[GFX_ARG_YPOS] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].src_y = parameter[GFX_ARG_YPOS] * TILEY;
+
+ /* optional x and y pixel position of animation frame sequence */
+ if (parameter[GFX_ARG_X] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].src_x = parameter[GFX_ARG_X];
+ if (parameter[GFX_ARG_Y] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].src_y = parameter[GFX_ARG_Y];
+
+ /* optional width and height of each animation frame */
+ if (parameter[GFX_ARG_WIDTH] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].width = parameter[GFX_ARG_WIDTH];
+ if (parameter[GFX_ARG_HEIGHT] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].height = parameter[GFX_ARG_HEIGHT];
+
+ /* correct x or y offset dependant of vertical or horizontal frame order */
+ if (parameter[GFX_ARG_VERTICAL]) /* frames are ordered vertically */
+ {
+ if (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].offset_y = parameter[GFX_ARG_OFFSET];
+ else
+ graphic_info[graphic].offset_y = graphic_info[graphic].height;
+ }
+ else /* frames are ordered horizontally */
+ {
+ if (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].offset_x = parameter[GFX_ARG_OFFSET];
+ else
+ graphic_info[graphic].offset_x = graphic_info[graphic].width;
+ }
+
+ /* optionally, the x and y offset of frames can be specified directly */
+ if (parameter[GFX_ARG_XOFFSET] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].offset_x = parameter[GFX_ARG_XOFFSET];
+ if (parameter[GFX_ARG_YOFFSET] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].offset_y = parameter[GFX_ARG_YOFFSET];
+
+ /* automatically determine correct number of frames, if not defined */
+ if (parameter[GFX_ARG_FRAMES] != ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].anim_frames = parameter[GFX_ARG_FRAMES];
+ else if (parameter[GFX_ARG_XPOS] == 0 && !parameter[GFX_ARG_VERTICAL])
+ graphic_info[graphic].anim_frames = num_xtiles;
+ else if (parameter[GFX_ARG_YPOS] == 0 && parameter[GFX_ARG_VERTICAL])
+ graphic_info[graphic].anim_frames = num_ytiles;
+ else
+ graphic_info[graphic].anim_frames = 1;
+
+ graphic_info[graphic].anim_delay = parameter[GFX_ARG_DELAY];
+ if (graphic_info[graphic].anim_delay == 0) /* delay must be at least 1 */
+ graphic_info[graphic].anim_delay = 1;
+
+ if (parameter[GFX_ARG_ANIM_MODE] != ANIM_NONE)
+ graphic_info[graphic].anim_mode = parameter[GFX_ARG_ANIM_MODE];
+ else if (graphic_info[graphic].anim_frames > 1)
+ graphic_info[graphic].anim_mode = ANIM_LOOP;
+
+ /* automatically determine correct start frame, if not defined */
+ if (parameter[GFX_ARG_START_FRAME] == ARG_UNDEFINED_VALUE)
+ graphic_info[graphic].anim_start_frame = 0;
+ else if (graphic_info[graphic].anim_mode & ANIM_REVERSE)
+ graphic_info[graphic].anim_start_frame =
+ graphic_info[graphic].anim_frames - parameter[GFX_ARG_START_FRAME] - 1;
+ else
+ graphic_info[graphic].anim_start_frame = parameter[GFX_ARG_START_FRAME];
+
+ /* animation synchronized with global frame counter, not move position */
+ graphic_info[graphic].anim_global_sync = parameter[GFX_ARG_GLOBAL_SYNC];
+
+ /* this is only used for toon animations */
+ graphic_info[graphic].step_offset = parameter[GFX_ARG_STEP_OFFSET];
+ graphic_info[graphic].step_delay = parameter[GFX_ARG_STEP_DELAY];
+
+ /* this is only used for drawing font characters */
+ graphic_info[graphic].draw_x = parameter[GFX_ARG_DRAW_XOFFSET];
+ graphic_info[graphic].draw_y = parameter[GFX_ARG_DRAW_YOFFSET];
+}
+
+static void InitGraphicInfo()
+{
+ int fallback_graphic = IMG_CHAR_EXCLAM;
+ struct FileInfo *fallback_image = getImageListEntry(fallback_graphic);
+ Bitmap *fallback_bitmap = getBitmapFromImageID(fallback_graphic);
+ int num_images = getImageListSize();
+ int i;
+
+#if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
+ static boolean clipmasks_initialized = FALSE;
+ Pixmap src_pixmap;
+ XGCValues clip_gc_values;
+ unsigned long clip_gc_valuemask;
+ GC copy_clipmask_gc = None;
+#endif
+
+ if (graphic_info != NULL)
+ free(graphic_info);
+
+ graphic_info = checked_calloc(num_images * sizeof(struct GraphicInfo));
+
+#if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
+ if (clipmasks_initialized)
+ {
+ for (i=0; i<num_images; i++)
+ {
+ if (graphic_info[i].clip_mask)
+ XFreePixmap(display, graphic_info[i].clip_mask);
+ if (graphic_info[i].clip_gc)
+ XFreeGC(display, graphic_info[i].clip_gc);
+
+ graphic_info[i].clip_mask = None;
+ graphic_info[i].clip_gc = None;
+ }
+ }
+#endif
+
+ for (i=0; i<num_images; i++)
+ {
+ struct FileInfo *image = getImageListEntry(i);
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+ int first_frame, last_frame;
+
+ set_graphic_parameters(i, image->parameter);
+
+ /* now check if no animation frames are outside of the loaded image */
+
+ if (graphic_info[i].bitmap == NULL)
+ continue; /* skip check for optional images that are undefined */
+
+ first_frame = 0;
+ getGraphicSource(i, first_frame, &src_bitmap, &src_x, &src_y);
+ if (src_x < 0 || src_y < 0 ||
+ src_x + TILEX > src_bitmap->width ||
+ src_y + TILEY > src_bitmap->height)
+ {
+ Error(ERR_RETURN_LINE, "-");
+ Error(ERR_RETURN, "warning: error found in config file:");
+ Error(ERR_RETURN, "- config file: '%s'",
+ getImageConfigFilename());
+ Error(ERR_RETURN, "- config token: '%s'",
+ getTokenFromImageID(i));
+ Error(ERR_RETURN, "- image file: '%s'",
+ src_bitmap->source_filename);
+ Error(ERR_RETURN,
+ "error: first animation frame out of bounds (%d, %d)",
+ src_x, src_y);
+ Error(ERR_RETURN, "custom graphic rejected for this element/action");
+
+ if (i == fallback_graphic)
+ Error(ERR_EXIT, "fatal error: no fallback graphic available");
+
+ Error(ERR_RETURN, "fallback done to 'char_exclam' for this graphic");
+ Error(ERR_RETURN_LINE, "-");
+
+ set_graphic_parameters(i, fallback_image->default_parameter);
+ graphic_info[i].bitmap = fallback_bitmap;
+ }
+
+ last_frame = graphic_info[i].anim_frames - 1;
+ getGraphicSource(i, last_frame, &src_bitmap, &src_x, &src_y);
+ if (src_x < 0 || src_y < 0 ||
+ src_x + TILEX > src_bitmap->width ||
+ src_y + TILEY > src_bitmap->height)
+ {
+ Error(ERR_RETURN_LINE, "-");
+ Error(ERR_RETURN, "warning: error found in config file:");
+ Error(ERR_RETURN, "- config file: '%s'",
+ getImageConfigFilename());
+ Error(ERR_RETURN, "- config token: '%s'",
+ getTokenFromImageID(i));
+ Error(ERR_RETURN, "- image file: '%s'",
+ src_bitmap->source_filename);
+ Error(ERR_RETURN,
+ "error: last animation frame (%d) out of bounds (%d, %d)",
+ last_frame, src_x, src_y);
+ Error(ERR_RETURN, "custom graphic rejected for this element/action");
+
+ if (i == fallback_graphic)
+ Error(ERR_EXIT, "fatal error: no fallback graphic available");
+
+ Error(ERR_RETURN, "fallback done to 'char_exclam' for this graphic");
+ Error(ERR_RETURN_LINE, "-");
+
+ set_graphic_parameters(i, fallback_image->default_parameter);
+ graphic_info[i].bitmap = fallback_bitmap;
+ }
+
+#if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
+ /* currently we need only a tile clip mask from the first frame */
+ getGraphicSource(i, first_frame, &src_bitmap, &src_x, &src_y);
+
+ if (copy_clipmask_gc == None)
+ {
+ clip_gc_values.graphics_exposures = False;
+ clip_gc_valuemask = GCGraphicsExposures;
+ copy_clipmask_gc = XCreateGC(display, src_bitmap->clip_mask,
+ clip_gc_valuemask, &clip_gc_values);
+ }
+
+ graphic_info[i].clip_mask =
+ XCreatePixmap(display, window->drawable, TILEX, TILEY, 1);
+
+ src_pixmap = src_bitmap->clip_mask;
+ XCopyArea(display, src_pixmap, graphic_info[i].clip_mask,
+ copy_clipmask_gc, src_x, src_y, TILEX, TILEY, 0, 0);
+
+ clip_gc_values.graphics_exposures = False;
+ clip_gc_values.clip_mask = graphic_info[i].clip_mask;
+ clip_gc_valuemask = GCGraphicsExposures | GCClipMask;
+
+ graphic_info[i].clip_gc =
+ XCreateGC(display, window->drawable, clip_gc_valuemask, &clip_gc_values);
+#endif
+ }
+
+#if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
+ if (copy_clipmask_gc)
+ XFreeGC(display, copy_clipmask_gc);
+
+ clipmasks_initialized = TRUE;
+#endif
+}
+
+static void InitElementSoundInfo()
+{
+ struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
+ int num_property_mappings = getSoundListPropertyMappingSize();
+ int i, j, act;
+
+ /* set values to -1 to identify later as "uninitialized" values */
+ for (i=0; i < MAX_NUM_ELEMENTS; i++)
+ for (act=0; act < NUM_ACTIONS; act++)
+ element_info[i].sound[act] = -1;
+
+ /* initialize element/sound mapping from static configuration */
+ for (i=0; element_to_sound[i].element > -1; i++)
+ {
+ int element = element_to_sound[i].element;
+ int action = element_to_sound[i].action;
+ int sound = element_to_sound[i].sound;
+ boolean is_class = element_to_sound[i].is_class;
+
+ if (action < 0)
+ action = ACTION_DEFAULT;
+
+ if (!is_class)
+ element_info[element].sound[action] = sound;
+ else
+ for (j=0; j < MAX_NUM_ELEMENTS; j++)
+ if (strcmp(element_info[j].class_name,
+ element_info[element].class_name) == 0)
+ element_info[j].sound[action] = sound;
+ }
+
+ /* initialize element/sound mapping from dynamic configuration */
+ for (i=0; i < num_property_mappings; i++)
+ {
+ int element = property_mapping[i].base_index;
+ int action = property_mapping[i].ext1_index;
+ int sound = property_mapping[i].artwork_index;
+
+ if (element >= MAX_NUM_ELEMENTS)
+ continue;
+
+ if (action < 0)
+ action = ACTION_DEFAULT;
+
+ element_info[element].sound[action] = sound;
+ }
+
+ /* initialize element class/sound mapping from dynamic configuration */
+ for (i=0; i < num_property_mappings; i++)
+ {
+ int element_class = property_mapping[i].base_index - MAX_NUM_ELEMENTS;
+ int action = property_mapping[i].ext1_index;
+ int sound = property_mapping[i].artwork_index;
+
+ if (element_class < 0 || element_class >= MAX_NUM_ELEMENTS)
+ continue;
+
+ if (action < 0)
+ action = ACTION_DEFAULT;
+
+ for (j=0; j < MAX_NUM_ELEMENTS; j++)
+ if (strcmp(element_info[j].class_name,
+ element_info[element_class].class_name) == 0)
+ element_info[j].sound[action] = sound;
+ }
+
+ /* now set all '-1' values to element specific default values */
+ for (i=0; i<MAX_NUM_ELEMENTS; i++)
+ {
+ int default_action_sound = element_info[i].sound[ACTION_DEFAULT];
+
+ for (act=0; act < NUM_ACTIONS; act++)
+ {
+ /* no sound for this specific action -- use default action sound */
+ if (element_info[i].sound[act] == -1)
+ element_info[i].sound[act] = default_action_sound;
+ }
+ }
+}
+
+static void set_sound_parameters(int sound, char **parameter_raw)
+{
+ int parameter[NUM_SND_ARGS];
+ int i;
+
+ /* get integer values from string parameters */
+ for (i=0; i < NUM_SND_ARGS; i++)
+ parameter[i] =
+ get_parameter_value(sound_config_suffix[i].token, parameter_raw[i],
+ sound_config_suffix[i].type);
+
+ /* explicit loop mode setting in configuration overrides default value */
+ if (parameter[SND_ARG_MODE_LOOP] != ARG_UNDEFINED_VALUE)
+ sound_info[sound].loop = parameter[SND_ARG_MODE_LOOP];
+}
+
+static void InitSoundInfo()
+{
+ struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
+ int num_property_mappings = getSoundListPropertyMappingSize();
+ int *sound_effect_properties;
+ int num_sounds = getSoundListSize();
+ int i, j;
+
+ if (sound_info != NULL)
+ free(sound_info);
+
+ sound_effect_properties = checked_calloc(num_sounds * sizeof(int));
+ sound_info = checked_calloc(num_sounds * sizeof(struct SoundInfo));
+
+ /* initialize sound effect for all elements to "no sound" */
+ for (i=0; i<MAX_NUM_ELEMENTS; i++)
+ for (j=0; j<NUM_ACTIONS; j++)
+ element_info[i].sound[j] = SND_UNDEFINED;
+
+ for (i=0; i<num_sounds; i++)
+ {
+ struct FileInfo *sound = getSoundListEntry(i);
+ int len_effect_text = strlen(sound->token);
+
+ sound_effect_properties[i] = ACTION_OTHER;
+ sound_info[i].loop = FALSE;
+
+ /* determine all loop sounds and identify certain sound classes */
+
+ for (j=0; element_action_info[j].suffix; j++)
+ {
+ int len_action_text = strlen(element_action_info[j].suffix);
+
+ if (len_action_text < len_effect_text &&
+ strcmp(&sound->token[len_effect_text - len_action_text],
+ element_action_info[j].suffix) == 0)
+ {
+ sound_effect_properties[i] = element_action_info[j].value;
+
+ if (element_action_info[j].is_loop_sound)
+ sound_info[i].loop = TRUE;
+ }
+ }
+
+ /* associate elements and some selected sound actions */
+
+ for (j=0; j<MAX_NUM_ELEMENTS; j++)
+ {
+ if (element_info[j].class_name)
+ {
+ int len_class_text = strlen(element_info[j].class_name);
+
+ if (len_class_text + 1 < len_effect_text &&
+ strncmp(sound->token,
+ element_info[j].class_name, len_class_text) == 0 &&
+ sound->token[len_class_text] == '.')
+ {
+ int sound_action_value = sound_effect_properties[i];
+
+ element_info[j].sound[sound_action_value] = i;
+ }
+ }
+ }
+
+ set_sound_parameters(i, sound->parameter);
+ }
+
+ free(sound_effect_properties);
+
+ /* initialize element/sound mapping from dynamic configuration */
+ for (i=0; i < num_property_mappings; i++)
+ {
+ int element = property_mapping[i].base_index;
+ int action = property_mapping[i].ext1_index;
+ int sound = property_mapping[i].artwork_index;
+
+ if (action < 0)
+ action = ACTION_DEFAULT;
+
+ element_info[element].sound[action] = sound;
+ }
+
+#if 0
+ /* TEST ONLY */
+ {
+ int element = EL_CUSTOM_11;
+ int j = 0;
+
+ while (element_action_info[j].suffix)
+ {
+ printf("element %d, sound action '%s' == %d\n",
+ element, element_action_info[j].suffix,
+ element_info[element].sound[j]);
+ j++;
+ }
+ }
+
+ PlaySoundLevelElementAction(0,0, EL_CUSTOM_11, ACTION_PUSHING);
+#endif
+
+#if 0
+ /* TEST ONLY */
+ {
+ int element = EL_SAND;
+ int sound_action = ACTION_DIGGING;
+ int j = 0;
+
+ while (element_action_info[j].suffix)
+ {
+ if (element_action_info[j].value == sound_action)
+ printf("element %d, sound action '%s' == %d\n",
+ element, element_action_info[j].suffix,
+ element_info[element].sound[sound_action]);
+ j++;
+ }
+ }
+#endif
+}
+
+static void ReinitializeGraphics()
+{
+ InitElementGraphicInfo(); /* element game graphic mapping */
+ InitElementSpecialGraphicInfo(); /* element special graphic mapping */
+ InitGraphicInfo(); /* graphic properties mapping */
+
+ InitElementSmallImages(); /* create editor and preview images */
+ InitFontGraphicInfo(); /* initialize text drawing functions */
+
+ SetMainBackgroundImage(IMG_BACKGROUND);
+ SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
+
+ InitGadgets();
+ InitToons();
+}
+
+static void ReinitializeSounds()
+{
+ InitElementSoundInfo(); /* element game sound mapping */
+ InitSoundInfo(); /* sound properties mapping */
+
+#if 1
+ InitElementSoundInfo(); /* element game sound mapping */
+#endif
+
+ InitPlaySoundLevel(); /* internal game sound settings */
+}
+
+static void ReinitializeMusic()
+{
+ /* currently nothing to do */