+
+ gadgets_initialized = TRUE;
+}
+
+void InitElementImages()
+{
+ struct PropertyMapping *property_mapping = getImageListPropertyMapping();
+ int num_property_mappings = getImageListPropertyMappingSize();
+ int i;
+
+ /* initialize images from static configuration */
+ for (i=0; element_to_graphic[i].element > -1; i++)
+ CreateImageWithSmallImages(element_to_graphic[i].graphic);
+
+ /* initialize images from dynamic configuration */
+ for (i=0; i < num_property_mappings; i++)
+ CreateImageWithSmallImages(property_mapping[i].artwork_index);
+}
+
+void InitElementGraphicInfo()
+{
+ struct PropertyMapping *property_mapping = getImageListPropertyMapping();
+ int num_property_mappings = getImageListPropertyMappingSize();
+ int i, act, dir;
+
+ /* 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].graphic[act] = -1;
+
+ for (dir=0; dir<NUM_DIRECTIONS; dir++)
+ element_info[i].direction_graphic[act][dir] = -1;
+ }
+ }
+
+ for (i=EL_CHAR_START; i<=EL_CHAR_END; i++)
+ element_info[i].graphic[ACTION_DEFAULT] =
+ IMG_CHAR_START + (i - EL_CHAR_START);
+
+ for (i=EL_CUSTOM_START; i<=EL_CUSTOM_END; i++)
+ element_info[i].graphic[ACTION_DEFAULT] =
+ IMG_CUSTOM_START + (i - EL_CUSTOM_START);
+
+ /* initialize element/graphic mapping from static configuration */
+ for (i=0; element_to_graphic[i].element > -1; i++)
+ {
+ int element = element_to_graphic[i].element;
+ int action = element_to_graphic[i].action;
+ int direction = element_to_graphic[i].direction;
+ int graphic = element_to_graphic[i].graphic;
+
+ if (action < 0)
+ action = ACTION_DEFAULT;
+
+ if (direction > -1)
+ element_info[element].direction_graphic[action][direction] = graphic;
+ else
+ element_info[element].graphic[action] = graphic;
+ }
+
+ /* initialize element/graphic 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 direction = property_mapping[i].ext2_index;
+ int special = property_mapping[i].ext3_index;
+ int graphic = property_mapping[i].artwork_index;
+
+ if (special != -1)
+ continue;
+
+ if (action < 0)
+ action = ACTION_DEFAULT;
+
+ if (direction > -1)
+ element_info[element].direction_graphic[action][direction] = graphic;
+ else
+ element_info[element].graphic[action] = graphic;
+ }
+
+ /* now set all '-1' values to element specific default values */
+ for (i=0; i<MAX_NUM_ELEMENTS; i++)
+ {
+ int default_action_graphic = element_info[i].graphic[ACTION_DEFAULT];
+ int default_action_direction_graphic[NUM_DIRECTIONS];
+
+ if (default_action_graphic == -1)
+ default_action_graphic = IMG_CHAR_QUESTION;
+
+ for (dir=0; dir<NUM_DIRECTIONS; dir++)
+ {
+ default_action_direction_graphic[dir] =
+ element_info[i].direction_graphic[ACTION_DEFAULT][dir];
+
+ if (default_action_direction_graphic[dir] == -1)
+ default_action_direction_graphic[dir] = default_action_graphic;
+ }
+
+ for (act=0; act<NUM_ACTIONS; act++)
+ {
+ for (dir=0; dir<NUM_DIRECTIONS; dir++)
+ {
+ int default_direction_graphic = element_info[i].graphic[act];
+
+ /* no graphic for current action -- use default direction graphic */
+ if (default_direction_graphic == -1)
+ default_direction_graphic = default_action_direction_graphic[dir];
+
+ if (element_info[i].direction_graphic[act][dir] == -1)
+ element_info[i].direction_graphic[act][dir] =
+ default_direction_graphic;
+ }
+
+ /* no graphic for this specific action -- use default action graphic */
+ if (element_info[i].graphic[act] == -1)
+ element_info[i].graphic[act] = default_action_graphic;
+ }
+ }
+}
+
+void InitElementEditorGraphicInfo()
+{
+ struct PropertyMapping *property_mapping = getImageListPropertyMapping();
+ int num_property_mappings = getImageListPropertyMappingSize();
+ int i;
+
+ /* always start with reliable default values */
+ for (i=0; i<MAX_NUM_ELEMENTS; i++)
+ element_info[i].editor_graphic = element_info[i].graphic[ACTION_DEFAULT];
+
+#if 0
+ /* initialize element/graphic mapping from static configuration */
+ for (i=0; ... ; i++)
+ {
+ int element = ... element;
+ int special = ... special;
+ int graphic = ... graphic;
+
+ if (special != GFX_SPECIAL_ARG_EDITOR)
+ continue;
+
+ element_info[element].editor_graphic = graphic;
+ }
+#endif
+
+ /* initialize 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 (special != GFX_SPECIAL_ARG_EDITOR)
+ continue;
+
+ element_info[element].editor_graphic = graphic;
+ }
+}
+
+static void InitElementSoundInfo()
+{
+ /* soon to come */
+}
+
+static void set_graphic_parameters(int graphic, int *parameter)
+{
+ 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;
+
+ graphic_info[graphic].bitmap = src_bitmap;
+
+ graphic_info[graphic].src_x = parameter[GFX_ARG_XPOS] * TILEX;
+ graphic_info[graphic].src_y = parameter[GFX_ARG_YPOS] * TILEY;
+ graphic_info[graphic].offset_x = parameter[GFX_ARG_OFFSET];
+ graphic_info[graphic].offset_y = 0;
+
+ /* animation frames are ordered vertically instead of horizontally */
+ if (parameter[GFX_ARG_VERTICAL])
+ {
+ graphic_info[graphic].offset_x = 0;
+ graphic_info[graphic].offset_y = parameter[GFX_ARG_OFFSET];
+ }
+
+ /* 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;
+
+ /* set mode for animation frame order */
+ if (parameter[GFX_ARG_MODE_LOOP])
+ graphic_info[graphic].anim_mode = ANIM_LOOP;
+ else if (parameter[GFX_ARG_MODE_LINEAR])
+ graphic_info[graphic].anim_mode = ANIM_LINEAR;
+ else if (parameter[GFX_ARG_MODE_PINGPONG])
+ graphic_info[graphic].anim_mode = ANIM_PINGPONG;
+ else if (parameter[GFX_ARG_MODE_PINGPONG2])
+ graphic_info[graphic].anim_mode = ANIM_PINGPONG2;
+ else if (parameter[GFX_ARG_MODE_RANDOM])
+ graphic_info[graphic].anim_mode = ANIM_RANDOM;
+ else if (graphic_info[graphic].anim_frames > 1)
+ graphic_info[graphic].anim_mode = ANIM_LOOP;
+ else
+ graphic_info[graphic].anim_mode = ANIM_NONE;
+
+ /* set additional flag to play animation frames in reverse order */
+ if (parameter[GFX_ARG_MODE_REVERSE])
+ graphic_info[graphic].anim_mode |= ANIM_REVERSE;
+
+ /* 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];
+}
+
+static void InitGraphicInfo()
+{
+ static boolean clipmasks_initialized = FALSE;
+ int num_images = getImageListSize();
+ int i;
+#if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
+ 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");
+ Error(ERR_RETURN_LINE, "-");
+
+ set_graphic_parameters(i, image->default_parameter);
+ }
+
+ 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");
+ Error(ERR_RETURN_LINE, "-");
+
+ set_graphic_parameters(i, image->default_parameter);
+ }
+
+#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);
+#endif
+
+ clipmasks_initialized = TRUE;
+}
+
+static void set_sound_parameters(int sound, int *parameter)
+{
+ /* 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()
+{
+ 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].sound_class_name)
+ {
+ int len_class_text = strlen(element_info[j].sound_class_name);
+
+ if (len_class_text + 1 < len_effect_text &&
+ strncmp(sound->token,
+ element_info[j].sound_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);
+
+#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