added support for dynamic window and playfield size
authorHolger Schemel <info@artsoft.org>
Sat, 1 Dec 2018 11:30:29 +0000 (12:30 +0100)
committerHolger Schemel <info@artsoft.org>
Sat, 1 Dec 2018 11:30:29 +0000 (12:30 +0100)
This change added a few new and extended directives for the graphics
configuration done in "graphicsinfo.conf" files in artwork and level
sets.

For the window and playfield definitions, there are new properties
".min_width" / ".min_height" and also ".max_width" / ".max_height"
to define minimum and maximum window and playfield sizes. This is
useful to dynamically support different screen sizes which have a
different screen aspect ratio (like tablets with 4:3 or 16:9 sized
screens), always using the full screen size without black borders
that cannot be used for the game.

To be able to place the "doors" (game panel and tape recorder) also
dynamically aligned with the right or bottom screen border, margins
can be defined for the playfield to not use these areas, which can
then be used for the doors being aligned to the corresponding window
edges. That's what the new ".margin_left", ".margin_right",
".margin_top" and ".margin_bottom" directives are for, together with
".align" and ".valign" properties to align the playfield or the doors
horizontally ("left", "center" or "right") or vertically ("top",
"middle", or "bottom").

To make sure that the game playfield always uses complete tiles (or
half tiles, or any other size), there's also a new directive called
".align_size" to specify the alignment tile size for the playfield
(which is set to half a tile by default).

The last enhancement extends playfield border sizes to be defined
for each border individually, so instead of a unique ".border_size"
for all playfield borders, different border sizes can be specified
by using ".border_left", ".border_right", ".border_top" and
".border_bottom". If they are not set, they inherit their value from
".border_size" just like before. Using the value "-1" causes borders
to be calculated dynamically -- this only makes sense when using a
variable window size (by using ".min_width" and/or ".min_height") with
a fixed playfield size, so borders can also have variable size. This
is mainly useful for using a fixed (or limited, by ".max_width/height)
playfield size while still being able to display global animations on
the whole surrounding playfield border area.

src/conf_gfx.c
src/files.c
src/libgame/system.h
src/tools.c

index cc4e3fc..1fa9a01 100644 (file)
@@ -9446,6 +9446,10 @@ struct ConfigInfo image_config[] =
 
   { "viewport.window.width",                   "672"                   },
   { "viewport.window.height",                  "560"                   },
+  { "viewport.window.min_width",               "-1"                    },
+  { "viewport.window.min_height",              "-1"                    },
+  { "viewport.window.max_width",               "-1"                    },
+  { "viewport.window.max_height",              "-1"                    },
   { "viewport.window.TITLE.width",             ARG_DEFAULT             },
   { "viewport.window.TITLE.height",            ARG_DEFAULT             },
 
@@ -9453,79 +9457,174 @@ struct ConfigInfo image_config[] =
   { "viewport.playfield.y",                    "6"                     },
   { "viewport.playfield.width",                        "548"                   },
   { "viewport.playfield.height",               "548"                   },
+  { "viewport.playfield.min_width",            "-1"                    },
+  { "viewport.playfield.min_height",           "-1"                    },
+  { "viewport.playfield.max_width",            "-1"                    },
+  { "viewport.playfield.max_height",           "-1"                    },
+  { "viewport.playfield.margin_left",          "0"                     },
+  { "viewport.playfield.margin_right",         "0"                     },
+  { "viewport.playfield.margin_top",           "0"                     },
+  { "viewport.playfield.margin_bottom",                "0"                     },
+  { "viewport.playfield.border_left",          "-1"                    },
+  { "viewport.playfield.border_right",         "-1"                    },
+  { "viewport.playfield.border_top",           "-1"                    },
+  { "viewport.playfield.border_bottom",                "-1"                    },
   { "viewport.playfield.border_size",          "2"                     },
+  { "viewport.playfield.align_size",           "16"                    },
+  { "viewport.playfield.align",                        "left"                  },
+  { "viewport.playfield.valign",               "top"                   },
   { "viewport.playfield.MAIN.x",               ARG_DEFAULT             },
   { "viewport.playfield.MAIN.y",               ARG_DEFAULT             },
   { "viewport.playfield.MAIN.width",           ARG_DEFAULT             },
   { "viewport.playfield.MAIN.height",          ARG_DEFAULT             },
+  { "viewport.playfield.MAIN.min_width",       ARG_DEFAULT             },
+  { "viewport.playfield.MAIN.min_height",      ARG_DEFAULT             },
+  { "viewport.playfield.MAIN.max_width",       ARG_DEFAULT             },
+  { "viewport.playfield.MAIN.max_height",      ARG_DEFAULT             },
+  { "viewport.playfield.MAIN.margin_left",     ARG_DEFAULT             },
+  { "viewport.playfield.MAIN.margin_right",    ARG_DEFAULT             },
+  { "viewport.playfield.MAIN.margin_top",      ARG_DEFAULT             },
+  { "viewport.playfield.MAIN.margin_bottom",   ARG_DEFAULT             },
+  { "viewport.playfield.MAIN.border_left",     ARG_DEFAULT             },
+  { "viewport.playfield.MAIN.border_right",    ARG_DEFAULT             },
+  { "viewport.playfield.MAIN.border_top",      ARG_DEFAULT             },
+  { "viewport.playfield.MAIN.border_bottom",   ARG_DEFAULT             },
   { "viewport.playfield.MAIN.border_size",     ARG_DEFAULT             },
+  { "viewport.playfield.MAIN.align_size",      ARG_DEFAULT             },
+  { "viewport.playfield.MAIN.align",           ARG_DEFAULT             },
+  { "viewport.playfield.MAIN.valign",          ARG_DEFAULT             },
   { "viewport.playfield.SCORES.x",             ARG_DEFAULT             },
   { "viewport.playfield.SCORES.y",             ARG_DEFAULT             },
   { "viewport.playfield.SCORES.width",         ARG_DEFAULT             },
   { "viewport.playfield.SCORES.height",                ARG_DEFAULT             },
+  { "viewport.playfield.SCORES.min_width",     ARG_DEFAULT             },
+  { "viewport.playfield.SCORES.min_height",    ARG_DEFAULT             },
+  { "viewport.playfield.SCORES.max_width",     ARG_DEFAULT             },
+  { "viewport.playfield.SCORES.max_height",    ARG_DEFAULT             },
+  { "viewport.playfield.SCORES.margin_left",   ARG_DEFAULT             },
+  { "viewport.playfield.SCORES.margin_right",  ARG_DEFAULT             },
+  { "viewport.playfield.SCORES.margin_top",    ARG_DEFAULT             },
+  { "viewport.playfield.SCORES.margin_bottom", ARG_DEFAULT             },
+  { "viewport.playfield.SCORES.border_left",   ARG_DEFAULT             },
+  { "viewport.playfield.SCORES.border_right",  ARG_DEFAULT             },
+  { "viewport.playfield.SCORES.border_top",    ARG_DEFAULT             },
+  { "viewport.playfield.SCORES.border_bottom", ARG_DEFAULT             },
   { "viewport.playfield.SCORES.border_size",   ARG_DEFAULT             },
+  { "viewport.playfield.SCORES.align_size",    ARG_DEFAULT             },
+  { "viewport.playfield.SCORES.align",         ARG_DEFAULT             },
+  { "viewport.playfield.SCORES.valign",                ARG_DEFAULT             },
   { "viewport.playfield.EDITOR.x",             ARG_DEFAULT             },
   { "viewport.playfield.EDITOR.y",             ARG_DEFAULT             },
   { "viewport.playfield.EDITOR.width",         ARG_DEFAULT             },
   { "viewport.playfield.EDITOR.height",                ARG_DEFAULT             },
+  { "viewport.playfield.EDITOR.min_width",     ARG_DEFAULT             },
+  { "viewport.playfield.EDITOR.min_height",    ARG_DEFAULT             },
+  { "viewport.playfield.EDITOR.max_width",     ARG_DEFAULT             },
+  { "viewport.playfield.EDITOR.max_height",    ARG_DEFAULT             },
+  { "viewport.playfield.EDITOR.margin_left",   ARG_DEFAULT             },
+  { "viewport.playfield.EDITOR.margin_right",  ARG_DEFAULT             },
+  { "viewport.playfield.EDITOR.margin_top",    ARG_DEFAULT             },
+  { "viewport.playfield.EDITOR.margin_bottom", ARG_DEFAULT             },
+  { "viewport.playfield.EDITOR.border_left",   ARG_DEFAULT             },
+  { "viewport.playfield.EDITOR.border_right",  ARG_DEFAULT             },
+  { "viewport.playfield.EDITOR.border_top",    ARG_DEFAULT             },
+  { "viewport.playfield.EDITOR.border_bottom", ARG_DEFAULT             },
   { "viewport.playfield.EDITOR.border_size",   ARG_DEFAULT             },
+  { "viewport.playfield.EDITOR.align_size",    ARG_DEFAULT             },
+  { "viewport.playfield.EDITOR.align",         ARG_DEFAULT             },
+  { "viewport.playfield.EDITOR.valign",                ARG_DEFAULT             },
   { "viewport.playfield.PLAYING.x",            ARG_DEFAULT             },
   { "viewport.playfield.PLAYING.y",            ARG_DEFAULT             },
   { "viewport.playfield.PLAYING.width",                ARG_DEFAULT             },
   { "viewport.playfield.PLAYING.height",       ARG_DEFAULT             },
+  { "viewport.playfield.PLAYING.min_width",    ARG_DEFAULT             },
+  { "viewport.playfield.PLAYING.min_height",   ARG_DEFAULT             },
+  { "viewport.playfield.PLAYING.max_width",    ARG_DEFAULT             },
+  { "viewport.playfield.PLAYING.max_height",   ARG_DEFAULT             },
+  { "viewport.playfield.PLAYING.margin_left",  ARG_DEFAULT             },
+  { "viewport.playfield.PLAYING.margin_right", ARG_DEFAULT             },
+  { "viewport.playfield.PLAYING.margin_top",   ARG_DEFAULT             },
+  { "viewport.playfield.PLAYING.margin_bottom",        ARG_DEFAULT             },
+  { "viewport.playfield.PLAYING.border_left",  ARG_DEFAULT             },
+  { "viewport.playfield.PLAYING.border_right", ARG_DEFAULT             },
+  { "viewport.playfield.PLAYING.border_top",   ARG_DEFAULT             },
+  { "viewport.playfield.PLAYING.border_bottom",        ARG_DEFAULT             },
   { "viewport.playfield.PLAYING.border_size",  ARG_DEFAULT             },
+  { "viewport.playfield.PLAYING.align_size",   ARG_DEFAULT             },
+  { "viewport.playfield.PLAYING.align",                ARG_DEFAULT             },
+  { "viewport.playfield.PLAYING.valign",       ARG_DEFAULT             },
 
   { "viewport.door_1.x",                       "566"                   },
   { "viewport.door_1.y",                       "60"                    },
   { "viewport.door_1.width",                   "100"                   },
   { "viewport.door_1.height",                  "280"                   },
   { "viewport.door_1.border_size",             "4"                     },
+  { "viewport.door_1.align",                   "left"                  },
+  { "viewport.door_1.valign",                  "top"                   },
   { "viewport.door_1.MAIN.x",                  ARG_DEFAULT             },
   { "viewport.door_1.MAIN.y",                  ARG_DEFAULT             },
   { "viewport.door_1.MAIN.width",              ARG_DEFAULT             },
   { "viewport.door_1.MAIN.height",             ARG_DEFAULT             },
   { "viewport.door_1.MAIN.border_size",                ARG_DEFAULT             },
+  { "viewport.door_1.MAIN.align",              ARG_DEFAULT             },
+  { "viewport.door_1.MAIN.valign",             ARG_DEFAULT             },
   { "viewport.door_1.SCORES.x",                        ARG_DEFAULT             },
   { "viewport.door_1.SCORES.y",                        ARG_DEFAULT             },
   { "viewport.door_1.SCORES.width",            ARG_DEFAULT             },
   { "viewport.door_1.SCORES.height",           ARG_DEFAULT             },
   { "viewport.door_1.SCORES.border_size",      ARG_DEFAULT             },
+  { "viewport.door_1.SCORES.align",            ARG_DEFAULT             },
+  { "viewport.door_1.SCORES.valign",           ARG_DEFAULT             },
   { "viewport.door_1.EDITOR.x",                        ARG_DEFAULT             },
   { "viewport.door_1.EDITOR.y",                        ARG_DEFAULT             },
   { "viewport.door_1.EDITOR.width",            ARG_DEFAULT             },
   { "viewport.door_1.EDITOR.height",           ARG_DEFAULT             },
   { "viewport.door_1.EDITOR.border_size",      ARG_DEFAULT             },
+  { "viewport.door_1.EDITOR.align",            ARG_DEFAULT             },
+  { "viewport.door_1.EDITOR.valign",           ARG_DEFAULT             },
   { "viewport.door_1.PLAYING.x",               ARG_DEFAULT             },
   { "viewport.door_1.PLAYING.y",               ARG_DEFAULT             },
   { "viewport.door_1.PLAYING.width",           ARG_DEFAULT             },
   { "viewport.door_1.PLAYING.height",          ARG_DEFAULT             },
   { "viewport.door_1.PLAYING.border_size",     ARG_DEFAULT             },
+  { "viewport.door_1.PLAYING.align",           ARG_DEFAULT             },
+  { "viewport.door_1.PLAYING.valign",          ARG_DEFAULT             },
 
   { "viewport.door_2.x",                       "566"                   },
   { "viewport.door_2.y",                       "400"                   },
   { "viewport.door_2.width",                   "100"                   },
   { "viewport.door_2.height",                  "100"                   },
   { "viewport.door_2.border_size",             "4"                     },
+  { "viewport.door_2.align",                   "left"                  },
+  { "viewport.door_2.valign",                  "top"                   },
   { "viewport.door_2.MAIN.x",                  ARG_DEFAULT             },
   { "viewport.door_2.MAIN.y",                  ARG_DEFAULT             },
   { "viewport.door_2.MAIN.width",              ARG_DEFAULT             },
   { "viewport.door_2.MAIN.height",             ARG_DEFAULT             },
   { "viewport.door_2.MAIN.border_size",                ARG_DEFAULT             },
+  { "viewport.door_2.MAIN.align",              ARG_DEFAULT             },
+  { "viewport.door_2.MAIN.valign",             ARG_DEFAULT             },
   { "viewport.door_2.SCORES.x",                        ARG_DEFAULT             },
   { "viewport.door_2.SCORES.y",                        ARG_DEFAULT             },
   { "viewport.door_2.SCORES.width",            ARG_DEFAULT             },
   { "viewport.door_2.SCORES.height",           ARG_DEFAULT             },
   { "viewport.door_2.SCORES.border_size",      ARG_DEFAULT             },
+  { "viewport.door_2.SCORES.align",            ARG_DEFAULT             },
+  { "viewport.door_2.SCORES.valign",           ARG_DEFAULT             },
   { "viewport.door_2.EDITOR.x",                        "566"                   },
   { "viewport.door_2.EDITOR.y",                        "356"                   },
   { "viewport.door_2.EDITOR.width",            "100"                   },
   { "viewport.door_2.EDITOR.height",           "144"                   },
   { "viewport.door_2.EDITOR.border_size",      "4"                     },
+  { "viewport.door_2.EDITOR.align",            "left"                  },
+  { "viewport.door_2.EDITOR.valign",           "top"                   },
   { "viewport.door_2.PLAYING.x",               ARG_DEFAULT             },
   { "viewport.door_2.PLAYING.y",               ARG_DEFAULT             },
   { "viewport.door_2.PLAYING.width",           ARG_DEFAULT             },
   { "viewport.door_2.PLAYING.height",          ARG_DEFAULT             },
   { "viewport.door_2.PLAYING.border_size",     ARG_DEFAULT             },
+  { "viewport.door_2.PLAYING.align",           ARG_DEFAULT             },
+  { "viewport.door_2.PLAYING.valign",          ARG_DEFAULT             },
 
   { NULL,                                      NULL                    }
 };
index 18759ae..e9b56ea 100644 (file)
@@ -9842,7 +9842,7 @@ static void InitMenuDesignSettings_SpecialPostProcessing(void)
 
     { NULL,                    NULL                    }
   };
-  int i;
+  int i, j;
 
   // special case: initialize later added SETUP list size from LEVELS value
   if (menu.list_size[GAME_MODE_SETUP] == -1)
@@ -9853,6 +9853,237 @@ static void InitMenuDesignSettings_SpecialPostProcessing(void)
     if ((*game_buttons_xy[i].dst).x == -1 &&
        (*game_buttons_xy[i].dst).y == -1)
       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
+
+  // --------------------------------------------------------------------------
+  // dynamic viewports (including playfield margins, borders and alignments)
+  // --------------------------------------------------------------------------
+
+  // dynamic viewports currently only supported for landscape mode
+  int display_width  = MAX(video.display_width, video.display_height);
+  int display_height = MIN(video.display_width, video.display_height);
+
+  for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
+  {
+    struct RectWithBorder *vp_window    = &viewport.window[i];
+    struct RectWithBorder *vp_playfield = &viewport.playfield[i];
+    struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
+    struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
+    boolean dynamic_window_width     = (vp_window->min_width     != -1);
+    boolean dynamic_window_height    = (vp_window->min_height    != -1);
+    boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
+    boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
+
+    // adjust window size if min/max width/height is specified
+
+    if (vp_window->min_width != -1)
+    {
+      int window_width = display_width;
+
+      // when using static window height, use aspect ratio of display
+      if (vp_window->min_height == -1)
+       window_width = vp_window->height * display_width / display_height;
+
+      vp_window->width = MAX(vp_window->min_width, window_width);
+    }
+
+    if (vp_window->min_height != -1)
+    {
+      int window_height = display_height;
+
+      // when using static window width, use aspect ratio of display
+      if (vp_window->min_width == -1)
+       window_height = vp_window->width * display_height / display_width;
+
+      vp_window->height = MAX(vp_window->min_height, window_height);
+    }
+
+    if (vp_window->max_width != -1)
+      vp_window->width = MIN(vp_window->width, vp_window->max_width);
+
+    if (vp_window->max_height != -1)
+      vp_window->height = MIN(vp_window->height, vp_window->max_height);
+
+    int playfield_width  = vp_window->width;
+    int playfield_height = vp_window->height;
+
+    // adjust playfield size and position according to specified margins
+
+    playfield_width  -= vp_playfield->margin_left;
+    playfield_width  -= vp_playfield->margin_right;
+
+    playfield_height -= vp_playfield->margin_top;
+    playfield_height -= vp_playfield->margin_bottom;
+
+    // adjust playfield size if min/max width/height is specified
+
+    if (vp_playfield->min_width != -1)
+      vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
+
+    if (vp_playfield->min_height != -1)
+      vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
+
+    if (vp_playfield->max_width != -1)
+      vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
+
+    if (vp_playfield->max_height != -1)
+      vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
+
+    // adjust playfield position according to specified alignment
+
+    if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
+      vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
+    else if (vp_playfield->align == ALIGN_CENTER)
+      vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
+    else if (vp_playfield->align == ALIGN_RIGHT)
+      vp_playfield->x += playfield_width - vp_playfield->width;
+
+    if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
+      vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
+    else if (vp_playfield->valign == VALIGN_MIDDLE)
+      vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
+    else if (vp_playfield->valign == VALIGN_BOTTOM)
+      vp_playfield->y += playfield_height - vp_playfield->height;
+
+    vp_playfield->x += vp_playfield->margin_left;
+    vp_playfield->y += vp_playfield->margin_top;
+
+    // adjust individual playfield borders if only default border is specified
+
+    if (vp_playfield->border_left == -1)
+      vp_playfield->border_left = vp_playfield->border_size;
+    if (vp_playfield->border_right == -1)
+      vp_playfield->border_right = vp_playfield->border_size;
+    if (vp_playfield->border_top == -1)
+      vp_playfield->border_top = vp_playfield->border_size;
+    if (vp_playfield->border_bottom == -1)
+      vp_playfield->border_bottom = vp_playfield->border_size;
+
+    // set dynamic playfield borders if borders are specified as undefined
+    // (but only if window size was dynamic and playfield size was static)
+
+    if (dynamic_window_width && !dynamic_playfield_width)
+    {
+      if (vp_playfield->border_left == -1)
+      {
+       vp_playfield->border_left = (vp_playfield->x -
+                                    vp_playfield->margin_left);
+       vp_playfield->x     -= vp_playfield->border_left;
+       vp_playfield->width += vp_playfield->border_left;
+      }
+
+      if (vp_playfield->border_right == -1)
+      {
+       vp_playfield->border_right = (vp_window->width -
+                                     vp_playfield->x -
+                                     vp_playfield->width -
+                                     vp_playfield->margin_right);
+       vp_playfield->width += vp_playfield->border_right;
+      }
+    }
+
+    if (dynamic_window_height && !dynamic_playfield_height)
+    {
+      if (vp_playfield->border_top == -1)
+      {
+       vp_playfield->border_top = (vp_playfield->y -
+                                   vp_playfield->margin_top);
+       vp_playfield->y      -= vp_playfield->border_top;
+       vp_playfield->height += vp_playfield->border_top;
+      }
+
+      if (vp_playfield->border_bottom == -1)
+      {
+       vp_playfield->border_bottom = (vp_window->height -
+                                      vp_playfield->y -
+                                      vp_playfield->height -
+                                      vp_playfield->margin_bottom);
+       vp_playfield->height += vp_playfield->border_bottom;
+      }
+    }
+
+    // adjust playfield size to be a multiple of a defined alignment tile size
+
+    int align_size = vp_playfield->align_size;
+    int playfield_xtiles = vp_playfield->width  / align_size;
+    int playfield_ytiles = vp_playfield->height / align_size;
+    int playfield_width_corrected  = playfield_xtiles * align_size;
+    int playfield_height_corrected = playfield_ytiles * align_size;
+    boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
+                                i == GFX_SPECIAL_ARG_EDITOR);
+
+    if (is_playfield_mode &&
+       dynamic_playfield_width &&
+       vp_playfield->width != playfield_width_corrected)
+    {
+      int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
+
+      vp_playfield->width = playfield_width_corrected;
+
+      if (vp_playfield->align == ALIGN_LEFT)
+      {
+       vp_playfield->border_left += playfield_xdiff;
+      }
+      else if (vp_playfield->align == ALIGN_RIGHT)
+      {
+       vp_playfield->border_right += playfield_xdiff;
+      }
+      else if (vp_playfield->align == ALIGN_CENTER)
+      {
+       int border_left_diff  = playfield_xdiff / 2;
+       int border_right_diff = playfield_xdiff - border_left_diff;
+
+       vp_playfield->border_left  += border_left_diff;
+       vp_playfield->border_right += border_right_diff;
+      }
+    }
+
+    if (is_playfield_mode &&
+       dynamic_playfield_height &&
+       vp_playfield->height != playfield_height_corrected)
+    {
+      int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
+
+      vp_playfield->height = playfield_height_corrected;
+
+      if (vp_playfield->valign == VALIGN_TOP)
+      {
+       vp_playfield->border_top += playfield_ydiff;
+      }
+      else if (vp_playfield->align == VALIGN_BOTTOM)
+      {
+       vp_playfield->border_right += playfield_ydiff;
+      }
+      else if (vp_playfield->align == VALIGN_MIDDLE)
+      {
+       int border_top_diff    = playfield_ydiff / 2;
+       int border_bottom_diff = playfield_ydiff - border_top_diff;
+
+       vp_playfield->border_top    += border_top_diff;
+       vp_playfield->border_bottom += border_bottom_diff;
+      }
+    }
+
+    // adjust door positions according to specified alignment
+
+    for (j = 0; j < 2; j++)
+    {
+      struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
+
+      if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
+       vp_door->x = ALIGNED_VP_XPOS(vp_door);
+      else if (vp_door->align == ALIGN_CENTER)
+       vp_door->x = vp_window->width / 2 - vp_door->width / 2;
+      else if (vp_door->align == ALIGN_RIGHT)
+       vp_door->x += vp_window->width - vp_door->width;
+
+      if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
+       vp_door->y = ALIGNED_VP_YPOS(vp_door);
+      else if (vp_door->valign == VALIGN_MIDDLE)
+       vp_door->y = vp_window->height / 2 - vp_door->height / 2;
+      else if (vp_door->valign == VALIGN_BOTTOM)
+       vp_door->y += vp_window->height - vp_door->height;
+    }
+  }
 }
 
 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
@@ -10174,7 +10405,22 @@ static void LoadMenuDesignSettingsFromFilename(char *filename)
         { ".y",                        &vp_struct[j].struct_ptr->y             },
         { ".width",            &vp_struct[j].struct_ptr->width         },
         { ".height",           &vp_struct[j].struct_ptr->height        },
-        { ".border_size",      &vp_struct[j].struct_ptr->border_size   }
+        { ".min_width",                &vp_struct[j].struct_ptr->min_width     },
+        { ".min_height",       &vp_struct[j].struct_ptr->min_height    },
+        { ".max_width",                &vp_struct[j].struct_ptr->max_width     },
+        { ".max_height",       &vp_struct[j].struct_ptr->max_height    },
+        { ".margin_left",      &vp_struct[j].struct_ptr->margin_left   },
+        { ".margin_right",     &vp_struct[j].struct_ptr->margin_right  },
+        { ".margin_top",       &vp_struct[j].struct_ptr->margin_top    },
+        { ".margin_bottom",    &vp_struct[j].struct_ptr->margin_bottom },
+        { ".border_left",      &vp_struct[j].struct_ptr->border_left   },
+        { ".border_right",     &vp_struct[j].struct_ptr->border_right  },
+        { ".border_top",       &vp_struct[j].struct_ptr->border_top    },
+        { ".border_bottom",    &vp_struct[j].struct_ptr->border_bottom },
+        { ".border_size",      &vp_struct[j].struct_ptr->border_size   },
+        { ".align_size",       &vp_struct[j].struct_ptr->align_size    },
+        { ".align",            &vp_struct[j].struct_ptr->align         },
+        { ".valign",           &vp_struct[j].struct_ptr->valign        }
       };
 
       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
index b82b7e8..2f7c2bf 100644 (file)
                                 (v) == VALIGN_BOTTOM ? (y) - (h) : (y))
 #define ALIGNED_TEXT_XPOS(p)   ALIGNED_XPOS((p)->x, (p)->width,  (p)->align)
 #define ALIGNED_TEXT_YPOS(p)   ALIGNED_YPOS((p)->y, (p)->height, (p)->valign)
+#define ALIGNED_VP_XPOS(p)     ALIGNED_TEXT_XPOS(p)
+#define ALIGNED_VP_YPOS(p)     ALIGNED_TEXT_YPOS(p)
 
 // values for redraw_mask
 #define REDRAW_NONE            (0)
@@ -1533,7 +1535,19 @@ struct RectWithBorder
 {
   int x, y;
   int width, height;
+  int min_width, min_height;
+  int max_width, max_height;
+  int margin_left;
+  int margin_right;
+  int margin_top;
+  int margin_bottom;
+  int border_left;
+  int border_right;
+  int border_top;
+  int border_bottom;
   int border_size;
+  int align_size;
+  int align, valign;
 };
 
 struct MenuPosInfo
index 451ef01..6066522 100644 (file)
@@ -9442,11 +9442,14 @@ void ChangeViewportPropertiesIfNeeded(void)
   struct RectWithBorder *vp_door_3    = &viewport.door_2[GAME_MODE_EDITOR];
   int new_win_xsize    = vp_window->width;
   int new_win_ysize    = vp_window->height;
-  int border_size      = vp_playfield->border_size;
-  int new_sx           = vp_playfield->x + border_size;
-  int new_sy           = vp_playfield->y + border_size;
-  int new_sxsize       = vp_playfield->width  - 2 * border_size;
-  int new_sysize       = vp_playfield->height - 2 * border_size;
+  int border_left      = vp_playfield->border_left;
+  int border_right     = vp_playfield->border_right;
+  int border_top       = vp_playfield->border_top;
+  int border_bottom    = vp_playfield->border_bottom;
+  int new_sx           = vp_playfield->x      + border_left;
+  int new_sy           = vp_playfield->y      + border_top;
+  int new_sxsize       = vp_playfield->width  - border_left - border_right;
+  int new_sysize       = vp_playfield->height - border_top  - border_bottom;
   int new_real_sx      = vp_playfield->x;
   int new_real_sy      = vp_playfield->y;
   int new_full_sxsize  = vp_playfield->width;