rnd-20040807-1-src
authorHolger Schemel <info@artsoft.org>
Sat, 7 Aug 2004 13:47:55 +0000 (15:47 +0200)
committerHolger Schemel <info@artsoft.org>
Sat, 30 Aug 2014 08:47:35 +0000 (10:47 +0200)
* fixed bug in gadget code which caused reset of CEs in level editor
  (example: pressing 'b' [grab brush] on CE config page erased values)
  (solution: check if gadgets in ClickOnGadget() are really mapped)
* fixed maze runner style CEs to use the configured move delay value
* added Aaron Davidson's tutorial level set to the "Tutorials" section

ChangeLog
src/conftime.h
src/editor.c
src/files.c
src/game.c
src/libgame/gadgets.c
src/libgame/gadgets.h
src/main.h

index 6888cd7ba3769bc8d40d450c9bcee0cdd7f1d1d1..b56015cabd5d5233c494bc9fc4a13fd82160a5fa 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2004-08-07
+       * fixed bug in gadget code which caused reset of CEs in level editor
+         (example: pressing 'b' [grab brush] on CE config page erased values)
+         (solution: check if gadgets in ClickOnGadget() are really mapped)
+
+2004-07-29
+       * fixed maze runner style CEs to use the configured move delay value
+
+2004-06-27
+       * added Aaron Davidson's tutorial level set to the "Tutorials" section
+
 2004-06-20
        * fixed engine change that broke 3.0.8 levels like "Walpurgis Gardens"
        * fixed the above fix because it broke level set "machine" (*sigh*)
index bf22a8e74763445e67a24755db80b30f9ffa146b..0515c7cc599b4d9be877c2146d5c8ba3005a3bfb 100644 (file)
@@ -1 +1 @@
-#define COMPILE_DATE_STRING "[2004-06-21 01:39]"
+#define COMPILE_DATE_STRING "[2004-08-07 15:46]"
index 5dc75613970142144db6e289a234b90550988c35..de334bb5241a7d9966b7deb138afdd1f8a24c4a5 100644 (file)
@@ -5509,6 +5509,8 @@ static void ResetUndoBuffer()
   undo_buffer_position = -1;
   undo_buffer_steps = -1;
   CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
+
+  level.changed = FALSE;
 }
 
 static void DrawEditModeWindow()
@@ -5526,15 +5528,18 @@ static void DrawEditModeWindow()
 
 static boolean LevelChanged()
 {
-  boolean level_changed = FALSE;
+  boolean field_changed = FALSE;
   int x, y;
 
+  if (leveldir_current->readonly)
+    return FALSE;
+
   for (y = 0; y < lev_fieldy; y++) 
     for (x = 0; x < lev_fieldx; x++)
       if (Feld[x][y] != level.field[x][y])
-       level_changed = TRUE;
+       field_changed = TRUE;
 
-  return level_changed;
+  return (level.changed || field_changed);
 }
 
 static boolean LevelContainsPlayer()
@@ -5542,6 +5547,8 @@ static boolean LevelContainsPlayer()
   boolean player_found = FALSE;
   int x, y;
 
+  return TRUE;         /* !!! CURRENTLY DEACTIVATED !!! */
+
   for (y = 0; y < lev_fieldy; y++) 
     for (x = 0; x < lev_fieldx; x++)
       if (Feld[x][y] == EL_PLAYER_1 ||
@@ -5741,6 +5748,8 @@ static boolean CopyCustomElement(int element_old, int element_new,
     element_old = (IS_CUSTOM_ELEMENT(element_new) ?
                   EL_INTERNAL_CLIPBOARD_CUSTOM : EL_INTERNAL_CLIPBOARD_GROUP);
     copy_mode = GADGET_ID_CUSTOM_COPY_TO;
+
+    level.changed = TRUE;
   }
   else if (IS_CUSTOM_ELEMENT(element_old) && !IS_CUSTOM_ELEMENT(element_new))
   {
@@ -5754,6 +5763,10 @@ static boolean CopyCustomElement(int element_old, int element_new,
 
     return FALSE;
   }
+  else
+  {
+    level.changed = TRUE;
+  }
 
   if (copy_mode == GADGET_ID_CUSTOM_COPY_FROM)
   {
@@ -5999,6 +6012,7 @@ static void CopyCustomElementPropertiesToGame(int element)
 
   /* mark that this custom element has been modified */
   custom_element.modified_settings = TRUE;
+  level.changed = TRUE;
 
   if (level.use_custom_template)
   {
@@ -6156,6 +6170,7 @@ static void CopyGroupElementPropertiesToGame(int element)
 
   /* mark that this group element has been modified */
   element_info[element].modified_settings = TRUE;
+  level.changed = TRUE;
 }
 
 static void CopyClassicElementPropertiesToGame(int element)
@@ -7903,6 +7918,8 @@ static int DrawLevelText(int sx, int sy, char letter, int mode)
          DrawLevelText(start_sx, sy + 1, 0, TEXT_SETCURSOR);
        else
          DrawLevelText(0, 0, 0, TEXT_END);
+
+       level.changed = TRUE;
       }
       break;
 
@@ -7991,6 +8008,8 @@ static void CopyLevelToUndoBuffer(int mode)
   SetBorderElement();
   if (BorderElement != last_border_element)
     DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
+
+  level.changed = TRUE;
 }
 
 static void RandomPlacement(int new_element)
@@ -8024,24 +8043,21 @@ static void RandomPlacement(int new_element)
       for (y = 0; y < lev_fieldy; y++)
        if (free_position[x][y])
          Feld[x][y] = new_element;
-
-    DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
-    CopyLevelToUndoBuffer(UNDO_IMMEDIATE);
-
-    return;
   }
-
-  while (num_elements > 0)
+  else
   {
-    x = RND(lev_fieldx);
-    y = RND(lev_fieldy);
-
-    /* don't place element at the same position twice */
-    if (free_position[x][y])
+    while (num_elements > 0)
     {
-      free_position[x][y] = FALSE;
-      Feld[x][y] = new_element;
-      num_elements--;
+      x = RND(lev_fieldx);
+      y = RND(lev_fieldy);
+
+      /* don't place element at the same position twice */
+      if (free_position[x][y])
+      {
+       free_position[x][y] = FALSE;
+       Feld[x][y] = new_element;
+       num_elements--;
+      }
     }
   }
 
@@ -8414,6 +8430,7 @@ static void HandleCounterButtons(struct GadgetInfo *gi)
     {
       if (gadget_id == counterbutton_info[counter_id].gadget_id_text)
        ModifyEditorCounter(counter_id, *counter_value);
+
       return;
     }
   }
@@ -8423,6 +8440,16 @@ static void HandleCounterButtons(struct GadgetInfo *gi)
   else
     ModifyEditorCounter(counter_id, *counter_value + step);
 
+  if (counter_id == ED_COUNTER_ID_SELECT_LEVEL)
+  {
+      LoadLevel(level_nr);
+      TapeErase();
+      ResetUndoBuffer();
+      DrawEditModeWindow();
+
+      return;
+  }
+
   switch (counter_id)
   {
     case ED_COUNTER_ID_ELEMENT_CONTENT:
@@ -8445,13 +8472,6 @@ static void HandleCounterButtons(struct GadgetInfo *gi)
       lev_fieldy = level.fieldy;
       break;
 
-    case ED_COUNTER_ID_SELECT_LEVEL:
-      LoadLevel(level_nr);
-      TapeErase();
-      ResetUndoBuffer();
-      DrawEditModeWindow();
-      break;
-
     default:
       break;
   }
@@ -8461,6 +8481,8 @@ static void HandleCounterButtons(struct GadgetInfo *gi)
       (counter_id >= ED_COUNTER_ID_CHANGE_FIRST &&
        counter_id <= ED_COUNTER_ID_CHANGE_LAST))
     CopyElementPropertiesToGame(properties_element);
+
+  level.changed = TRUE;
 }
 
 static void HandleTextInputGadgets(struct GadgetInfo *gi)
@@ -8475,6 +8497,8 @@ static void HandleTextInputGadgets(struct GadgetInfo *gi)
 
     ModifyEditorElementList(); /* update changed button info text */
   }
+
+  level.changed = TRUE;
 }
 
 static void HandleTextAreaGadgets(struct GadgetInfo *gi)
@@ -8482,6 +8506,8 @@ static void HandleTextAreaGadgets(struct GadgetInfo *gi)
   int type_id = gi->custom_type_id;
 
   strcpy(textarea_info[type_id].value, gi->textarea.value);
+
+  level.changed = TRUE;
 }
 
 static void HandleSelectboxGadgets(struct GadgetInfo *gi)
@@ -8502,7 +8528,11 @@ static void HandleSelectboxGadgets(struct GadgetInfo *gi)
           (type_id >= ED_SELECTBOX_ID_CHANGE_FIRST &&
            type_id <= ED_SELECTBOX_ID_CHANGE_LAST) ||
           (type_id == ED_SELECTBOX_ID_GROUP_CHOICE_MODE))
+  {
     CopyElementPropertiesToGame(properties_element);
+
+    level.changed = TRUE;
+  }
 }
 
 static void HandleTextbuttonGadgets(struct GadgetInfo *gi)
@@ -8543,6 +8573,8 @@ static void HandleTextbuttonGadgets(struct GadgetInfo *gi)
     setElementChangeInfoToDefaults(ei->change);
 
     DrawPropertiesWindow();
+
+    level.changed = TRUE;
   }
   else if (type_id == ED_TEXTBUTTON_ID_DEL_CHANGE_PAGE &&
           custom_element.num_change_pages > MIN_CHANGE_PAGES)
@@ -8556,6 +8588,8 @@ static void HandleTextbuttonGadgets(struct GadgetInfo *gi)
     setElementChangePages(ei, ei->num_change_pages - 1);
 
     DrawPropertiesWindow();
+
+    level.changed = TRUE;
   }
 }
 
@@ -8589,9 +8623,13 @@ static void HandleGraphicbuttonGadgets(struct GadgetInfo *gi)
       element_info[EL_INTERNAL_CLIPBOARD_CHANGE].change_page[0] =
        ei->change_page[current_change_page];
     else if (type_id == ED_GRAPHICBUTTON_ID_PASTE_CHANGE_PAGE)
+    {
       ei->change_page[current_change_page] =
        element_info[EL_INTERNAL_CLIPBOARD_CHANGE].change_page[0];
 
+      level.changed = TRUE;
+    }
+
     DrawPropertiesWindow();
   }
 }
@@ -8600,6 +8638,8 @@ static void HandleRadiobuttons(struct GadgetInfo *gi)
 {
   *radiobutton_info[gi->custom_type_id].value =
     radiobutton_info[gi->custom_type_id].checked_value;
+
+  level.changed = TRUE;
 }
 
 static void HandleCheckbuttons(struct GadgetInfo *gi)
@@ -8642,6 +8682,8 @@ static void HandleCheckbuttons(struct GadgetInfo *gi)
 
     DrawEditModeWindow();
   }
+
+  level.changed = TRUE;
 }
 
 static void HandleControlButtons(struct GadgetInfo *gi)
@@ -8907,6 +8949,7 @@ static void HandleControlButtons(struct GadgetInfo *gi)
       for (x = 0; x < MAX_LEV_FIELDX; x++) 
        for (y = 0; y < MAX_LEV_FIELDY; y++) 
          Feld[x][y] = (button == 1 ? EL_EMPTY : new_element);
+
       CopyLevelToUndoBuffer(GADGET_ID_CLEAR);
 
       DrawMiniLevel(ed_fieldx, ed_fieldy, level_xpos, level_ypos);
@@ -8919,7 +8962,7 @@ static void HandleControlButtons(struct GadgetInfo *gi)
        break;
       }
 
-      if (!LevelContainsPlayer)
+      if (!LevelContainsPlayer())
        Request("No Level without Gregor Mc Duffin please !", REQ_CONFIRM);
       else
       {
@@ -8936,11 +8979,13 @@ static void HandleControlButtons(struct GadgetInfo *gi)
 
        if (new_level)
          Request("Level saved !", REQ_CONFIRM);
+
+       level.changed = FALSE;
       }
       break;
 
     case GADGET_ID_TEST:
-      if (!LevelContainsPlayer)
+      if (!LevelContainsPlayer())
        Request("No Level without Gregor Mc Duffin please !", REQ_CONFIRM);
       else
       {
index b6a462e6fd637e8a34196a7a3f735d7cfc697277..e9d2cb5968a1677854bb94e2bc38ab6a6de243b6 100644 (file)
@@ -313,6 +313,8 @@ static void setLevelInfoToDefaults(struct LevelInfo *level)
 
   level->no_valid_file = FALSE;
 
+  level->changed = FALSE;
+
   if (leveldir_current == NULL)                /* only when dumping level */
     return;
 
@@ -2374,6 +2376,7 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
   }
 
   /* initialize "can_explode" field for old levels which did not store this */
+  /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
   if (level->game_version <= VERSION_IDENT(3,1,0,0))
   {
     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
@@ -2389,6 +2392,22 @@ static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
     }
   }
 
+  /* correct previously hard-coded move delay values for maze runner style */
+  if (level->game_version < VERSION_IDENT(3,1,1,0))
+  {
+    for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+    {
+      int element = EL_CUSTOM_START + i;
+
+      if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
+      {
+       /* previously hard-coded and therefore ignored */
+       element_info[element].move_delay_fixed = 9;
+       element_info[element].move_delay_random = 0;
+      }
+    }
+  }
+
 #if 0
   /* set default push delay values (corrected since version 3.0.7-1) */
   if (level->game_version < VERSION_IDENT(3,0,7,1))
index bf9b3892aa42200833396a0e620a41f2b41b3425..eaf9ee12e8bb92dae1e76654a6dbbc120132c40b 100644 (file)
@@ -37,7 +37,7 @@
 #define USE_NEW_SP_SLIPPERY    TRUE    * USE_NEW_STUFF         * 1
 #define USE_NEW_RANDOMIZE      TRUE    * USE_NEW_STUFF         * 1
 
-#define USE_PUSH_BUGFIX                TRUE    * 1
+#define USE_PUSH_BUGFIX                TRUE    * USE_NEW_STUFF         * 1
 
 /* for DigField() */
 #define DF_NO_PUSH             0
@@ -5145,7 +5145,13 @@ inline static void TurnRoundExt(int x, int y)
 
     MovDir[x][y] = new_move_dir;
     if (old_move_dir != new_move_dir)
+    {
+#if 1
+      MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
+#else
       MovDelay[x][y] = 9;
+#endif
+    }
   }
 }
 
index 8601256cbf3ed1b2da04be5a9422d40f62bf6efd..3431719caeb799898974860d5982cced01e4df71 100644 (file)
@@ -776,6 +776,7 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
       case GDI_NUMBER_VALUE:
        gi->textinput.number_value = va_arg(ap, long);
        sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
+       strcpy(gi->textinput.last_value, gi->textinput.value);
        gi->textinput.cursor_position = strlen(gi->textinput.value);
        break;
 
@@ -785,6 +786,7 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
        {
          gi->textinput.number_value = gi->textinput.number_min;
          sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
+         strcpy(gi->textinput.last_value, gi->textinput.value);
        }
        break;
 
@@ -794,6 +796,7 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
        {
          gi->textinput.number_value = gi->textinput.number_max;
          sprintf(gi->textinput.value, "%d", gi->textinput.number_value);
+         strcpy(gi->textinput.last_value, gi->textinput.value);
        }
        break;
 
@@ -805,12 +808,15 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
            max_textsize = MIN(gi->textinput.size, MAX_GADGET_TEXTSIZE - 1);
 
          strncpy(gi->textinput.value, va_arg(ap, char *), max_textsize);
+         strcpy(gi->textinput.last_value, gi->textinput.value);
+
          gi->textinput.value[max_textsize] = '\0';
          gi->textinput.cursor_position = strlen(gi->textinput.value);
 
          /* same tag also used for other gadget definitions */
          strcpy(gi->textbutton.value, gi->textinput.value);
          strcpy(gi->textarea.value, gi->textinput.value);
+         strcpy(gi->textarea.last_value, gi->textinput.value);
        }
        break;
 
@@ -821,10 +827,17 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
 
          gi->textinput.size = max_textsize;
          gi->textinput.value[max_textsize] = '\0';
+         strcpy(gi->textinput.last_value, gi->textinput.value);
 
          /* same tag also used for other gadget definitions */
-         strcpy(gi->textbutton.value, gi->textinput.value);
-         gi->textbutton.size = gi->textinput.size;
+
+         gi->textarea.size = max_textsize;
+         gi->textarea.value[max_textsize] = '\0';
+         strcpy(gi->textarea.last_value, gi->textinput.value);
+
+         gi->textbutton.size = max_textsize;
+         gi->textbutton.value[max_textsize] = '\0';
+
          gi->selectbox.size = gi->textinput.size;
        }
        break;
@@ -1336,6 +1349,9 @@ boolean anyTextGadgetActive()
 
 void ClickOnGadget(struct GadgetInfo *gi, int button)
 {
+  if (!gi->mapped)
+    return;
+
   /* simulate releasing mouse button over last gadget, if still pressed */
   if (button_status)
     HandleGadgets(-1, -1, 0);
@@ -1429,14 +1445,35 @@ boolean HandleGadgets(int mx, int my, int button)
       button != 0 && !motion_status && new_gi != last_gi)
 #endif
   {
-    CheckRangeOfNumericInputGadget(last_gi);   /* in case of numeric gadget */
+    struct GadgetInfo *gi = last_gi;
+    boolean gadget_changed = (gi->event_mask & GD_EVENT_TEXT_LEAVING);
+
+    /* check if text gadget has changed its value */
+    if (gi->type & GD_TYPE_TEXT_INPUT)
+    {
+      CheckRangeOfNumericInputGadget(gi);
 
-    DrawGadget(last_gi, DG_UNPRESSED, last_gi->direct_draw);
+      if (strcmp(gi->textinput.value, gi->textinput.last_value) != 0)
+       strcpy(gi->textinput.last_value, gi->textinput.value);
+      else
+       gadget_changed = FALSE;
+    }
+
+    /* selectbox does not change its value when closed by clicking outside */
+    if (gi->type & GD_TYPE_SELECTBOX)
+      gadget_changed = FALSE;
+
+    DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
 
-    last_gi->event.type = GD_EVENT_TEXT_LEAVING;
+    gi->event.type = GD_EVENT_TEXT_LEAVING;
 
-    if (last_gi->event_mask & GD_EVENT_TEXT_LEAVING)
-      last_gi->callback_action(last_gi);
+#if 1
+    if (gadget_changed && !(gi->type & GD_TYPE_SELECTBOX))
+      gi->callback_action(gi);
+#else
+    if (gi->event_mask & GD_EVENT_TEXT_LEAVING)
+      gi->callback_action(gi);
+#endif
 
     last_gi = NULL;
 
@@ -1781,20 +1818,29 @@ boolean HandleGadgets(int mx, int my, int button)
   if (gadget_released_inside)
   {
     boolean deactivate_gadget = TRUE;
+    boolean gadget_changed = TRUE;
 
     if (gi->type & GD_TYPE_SELECTBOX)
     {
 #if 1
       if (mouse_released_where_pressed ||
          !gadget_released_inside_select_area)       /* selectbox stays open */
+      {
        deactivate_gadget = FALSE;
+       gadget_changed = FALSE;
+      }
 #else
       if (gadget_released_inside_select_line ||
          gadget_released_off_borders)               /* selectbox stays open */
+      {
        deactivate_gadget = FALSE;
+       gadget_changed = FALSE;
+      }
 #endif
-      else
+      else if (gi->selectbox.index != gi->selectbox.current_index)
        gi->selectbox.index = gi->selectbox.current_index;
+      else
+       gadget_changed = FALSE;
     }
 
     if (deactivate_gadget &&
@@ -1805,8 +1851,15 @@ boolean HandleGadgets(int mx, int my, int button)
     gi->state = GD_BUTTON_UNPRESSED;
     gi->event.type = GD_EVENT_RELEASED;
 
+#if 1
+    if ((gi->event_mask & GD_EVENT_RELEASED) && gadget_changed)
+    {
+      gi->callback_action(gi);
+    }
+#else
     if ((gi->event_mask & GD_EVENT_RELEASED) && deactivate_gadget)
       gi->callback_action(gi);
+#endif
   }
 
   if (gadget_released_off_borders)
@@ -1856,10 +1909,24 @@ boolean HandleGadgetsKeyInput(Key key)
 
   if (key == KSYM_Return)      /* valid for both text input and selectbox */
   {
+    boolean gadget_changed = (gi->event_mask & GD_EVENT_TEXT_RETURN);
+
     if (gi->type & GD_TYPE_TEXT_INPUT)
+    {
       CheckRangeOfNumericInputGadget(gi);
+
+      if (strcmp(gi->textinput.value, gi->textinput.last_value) != 0)
+       strcpy(gi->textinput.last_value, gi->textinput.value);
+      else
+       gadget_changed = FALSE;
+    }
     else if (gi->type & GD_TYPE_SELECTBOX)
-      gi->selectbox.index = gi->selectbox.current_index;
+    {
+      if (gi->selectbox.index != gi->selectbox.current_index)
+       gi->selectbox.index = gi->selectbox.current_index;
+      else
+       gadget_changed = FALSE;
+    }
 
     if (gi->type & GD_TYPE_TEXT_AREA)
     {
@@ -1876,8 +1943,13 @@ boolean HandleGadgetsKeyInput(Key key)
       last_gi = NULL;
     }
 
+#if 1
+    if (gadget_changed)
+      gi->callback_action(gi);
+#else
     if (gi->event_mask & GD_EVENT_TEXT_RETURN)
       gi->callback_action(gi);
+#endif
   }
   else if (gi->type & GD_TYPE_TEXT_INPUT)      /* only valid for text input */
   {
index 4e1dd0f789d5e70beaf1107c1f615440e1ab7d40..5c71680466463186c3c910691ad22b1200e5be9e 100644 (file)
@@ -152,6 +152,7 @@ struct GadgetTextButton
 struct GadgetTextInput
 {
   char value[MAX_GADGET_TEXTSIZE];     /* text string in input field */
+  char last_value[MAX_GADGET_TEXTSIZE];        /* last text string in input field */
   int cursor_position;                 /* actual text cursor position */
   int number_value;                    /* integer value, if numeric */
   int number_min;                      /* minimal allowed numeric value */
@@ -162,6 +163,7 @@ struct GadgetTextInput
 struct GadgetTextArea
 {
   char value[MAX_GADGET_TEXTSIZE];     /* text string in input field */
+  char last_value[MAX_GADGET_TEXTSIZE];        /* last text string in input field */
   int cursor_position;                 /* actual text cursor position */
   int cursor_x;                                /* actual x cursor position */
   int cursor_y;                                /* actual y cursor position */
@@ -247,6 +249,7 @@ void UnmapAllGadgets();
 void RemapAllGadgets();
 
 boolean anyTextGadgetActive();
+
 void ClickOnGadget(struct GadgetInfo *, int);
 
 boolean HandleGadgets(int, int, int);
index 7b6b6f5d0cdc167da9ba03021c8fb38f6c631d6a..0ee2ca27a570731d1acd6e503409b5a9505e8c44 100644 (file)
@@ -1529,6 +1529,8 @@ struct LevelInfo
   boolean use_custom_template; /* use custom properties from template file */
 
   boolean no_valid_file;       /* set when level file missing or invalid */
+
+  boolean changed;             /* set when level was changed in the editor */
 };
 
 struct TapeInfo