diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt
index 86e6c61a4a..e735b6e846 100644
--- a/client/CMakeLists.txt
+++ b/client/CMakeLists.txt
@@ -97,6 +97,7 @@ target_sources(
   themes.cpp
   themes_common.cpp
   tileset_debugger.cpp
+  tileset_options.cpp
   tooltips.cpp
   top_bar.cpp
   tradecalculation.cpp
diff --git a/client/menu.cpp b/client/menu.cpp
index e19daf50f0..dd8e85ec81 100644
--- a/client/menu.cpp
+++ b/client/menu.cpp
@@ -15,9 +15,11 @@
 #include <QApplication>
 #include <QFileDialog>
 #include <QMainWindow>
+#include <QMenuBar>
 #include <QMessageBox>
 #include <QStandardPaths>
 #include <QVBoxLayout>
+
 // utility
 #include "fcintl.h"
 // common
@@ -29,6 +31,7 @@
 #include "map.h"
 #include "multipliers.h"
 #include "road.h"
+#include "tileset_options.h"
 #include "unit.h"
 // client
 #include "audio/audio.h"
@@ -577,6 +580,10 @@ void mr_menu::setup_menus()
   connect(act, &QAction::triggered, this, &mr_menu::tileset_custom_load);
   act = menu->addAction(_("Add Modpacks"));
   connect(act, &QAction::triggered, this, &mr_menu::add_modpacks);
+  tileset_options = menu->addAction(_("Tileset Options"));
+  connect(tileset_options, &QAction::triggered, this,
+          &mr_menu::show_tileset_options);
+  tileset_options->setEnabled(tileset_has_options(tileset));
   act = menu->addAction(_("Tileset Debugger"));
   connect(act, &QAction::triggered, queen()->mapview_wdg,
           &map_view::show_debugger);
@@ -2662,6 +2669,15 @@ void mr_menu::tileset_custom_load()
   dialog->show();
 }
 
+/**
+ * Slot for loading modpack installer
+ */
+void mr_menu::show_tileset_options()
+{
+  auto dialog = new freeciv::tileset_options_dialog(tileset, this);
+  dialog->show();
+}
+
 /**
  * Slot for loading modpack installer
  */
@@ -2735,6 +2751,17 @@ void mr_menu::slot_build_base(int id)
   }
 }
 
+/**
+ * Reimplemented virtual function.
+ */
+bool mr_menu::event(QEvent *event)
+{
+  if (event->type() == TilesetChanged) {
+    tileset_options->setEnabled(tileset_has_options(tileset));
+  }
+  return QMenuBar::event(event);
+}
+
 /**
    Invoke dialog with interface (local) options
  */
diff --git a/client/menu.h b/client/menu.h
index 69a6b51375..e99d0d6567 100644
--- a/client/menu.h
+++ b/client/menu.h
@@ -181,6 +181,7 @@ class mr_menu : public QMenuBar {
   void update_bases_menu();
   void set_tile_for_order(struct tile *ptile);
   bool shortcut_exists(const fc_shortcut &fcs, QString &where);
+  QAction *tileset_options = nullptr;
   QAction *minimap_status = nullptr;
   QAction *scale_fonts_status = nullptr;
   QAction *lock_status = nullptr;
@@ -191,6 +192,10 @@ class mr_menu : public QMenuBar {
   bool delayed_order = false;
   bool quick_airlifting = false;
   Unit_type_id airlift_type_id = 0;
+
+protected:
+  bool event(QEvent *event) override;
+
 private slots:
   // game menu
   void local_options();
@@ -204,6 +209,7 @@ private slots:
   void tileset_custom_load();
   void load_new_tileset();
   void add_modpacks();
+  void show_tileset_options();
   void back_to_menu();
   void quit_game();
 
diff --git a/client/options.cpp b/client/options.cpp
index 6708d96f86..9f31e42cee 100644
--- a/client/options.cpp
+++ b/client/options.cpp
@@ -14,6 +14,7 @@
 #include <fc_config.h>
 
 #include <cstring>
+#include <qglobal.h>
 #include <sys/stat.h>
 
 // Qt
@@ -65,6 +66,8 @@
 #include "views/view_map_common.h"
 #include "views/view_nations_data.h"
 
+const char *const TILESET_OPTIONS_PREFIX = "tileset_";
+
 typedef QHash<QString, QString> optionsHash;
 typedef QHash<QString, intptr_t> dialOptionsHash;
 
@@ -3870,6 +3873,91 @@ static const char *get_last_option_file_name(bool *allow_digital_boolean)
 
 Q_GLOBAL_STATIC(optionsHash, settable_options)
 
+/**
+ * Migrate players using cimpletoon/toonhex to amplio2/hexemplio with the
+ * cimpletoon option enabled.
+ *
+ * \since 3.1
+ */
+static void
+tileset_options_migrate_cimpletoon(struct client_options *options)
+{
+  for (auto &name : {options->default_tileset_iso_name,
+                     options->default_tileset_isohex_name,
+                     options->default_tileset_square_name}) {
+    if (name == QStringLiteral("cimpletoon")) {
+      fc_strlcpy(name, "amplio2", sizeof(name));
+      options->tileset_options[QStringLiteral("amplio2")]
+                              [QStringLiteral("cimpletoon")] = true;
+    } else if (name == QStringLiteral("toonhex")) {
+      fc_strlcpy(name, "hexemplio", sizeof(name));
+      options->tileset_options[QStringLiteral("hexemplio")]
+                              [QStringLiteral("cimpletoon")] = true;
+    }
+  }
+}
+
+/**
+ * Load tileset options.
+ *
+ * Every tileset has its own section called tileset_xxx. The options are
+ * saved as name=value pairs.
+ * \see tileset_options_save
+ */
+static void tileset_options_load(struct section_file *sf,
+                                 struct client_options *options)
+{
+  // Gather all tileset_xxx sections
+  auto sections =
+      secfile_sections_by_name_prefix(sf, TILESET_OPTIONS_PREFIX);
+  if (!sections) {
+    return;
+  }
+
+  section_list_iterate(sections, psection)
+  {
+    // Extract the tileset name from the name of the section.
+    auto tileset_name =
+        section_name(psection) + strlen(TILESET_OPTIONS_PREFIX);
+
+    // Get all values from the section and fill a map with them.
+    auto entries = section_entries(psection);
+    auto settings = std::map<QString, bool>();
+    entry_list_iterate(entries, pentry)
+    {
+      bool value = true;
+      if (entry_bool_get(pentry, &value)) {
+        settings[entry_name(pentry)] = value;
+      } else {
+        // Ignore options we can't convert to a bool, but warn the user.
+        qWarning("Could not load option %s for tileset %s",
+                 entry_name(pentry), tileset_name);
+      }
+    }
+    entry_list_iterate_end;
+
+    // Store the loaded options for later use.
+    options->tileset_options[tileset_name] = settings;
+  }
+  section_list_iterate_end;
+}
+
+/**
+ * Save tileset options.
+ *
+ * \see tileset_options_load
+ */
+static void tileset_options_save(struct section_file *sf,
+                                 const struct client_options *options)
+{
+  for (const auto &[tileset, settings] : options->tileset_options) {
+    for (const auto &[name, value] : settings) {
+      secfile_insert_bool(sf, value, "%s%s.%s", TILESET_OPTIONS_PREFIX,
+                          qUtf8Printable(tileset), qUtf8Printable(name));
+    }
+  }
+}
+
 /**
    Load the server options.
  */
@@ -4381,6 +4469,8 @@ void options_load()
     create_default_cma_presets();
   }
 
+  tileset_options_load(sf, gui_options);
+  tileset_options_migrate_cimpletoon(gui_options);
   settable_options_load(sf);
   global_worklists_load(sf);
 
@@ -4435,6 +4525,9 @@ void options_save(option_save_log_callback log_cb)
   message_options_save(sf, "client");
   options_dialogs_save(sf);
 
+  // Tileset options
+  tileset_options_save(sf, gui_options);
+
   // server settings
   save_cma_presets(sf);
   settable_options_save(sf);
diff --git a/client/options.h b/client/options.h
index ac79380398..ba2c8339e2 100644
--- a/client/options.h
+++ b/client/options.h
@@ -177,6 +177,10 @@ struct client_options {
   bool gui_qt_show_titlebar = true;
 
   struct overview overview = {};
+
+  /// Saved tileset options. The first index is the tileset name, the second
+  /// is the option name.
+  std::map<QString, std::map<QString, bool>> tileset_options;
 };
 
 extern client_options *gui_options;
diff --git a/client/tileset/tilespec.cpp b/client/tileset/tilespec.cpp
index bac1989d07..0c1021e10e 100644
--- a/client/tileset/tilespec.cpp
+++ b/client/tileset/tilespec.cpp
@@ -35,6 +35,7 @@
 #include "deprecations.h"
 #include "fcintl.h"
 #include "log.h"
+#include "name_translation.h"
 #include "rand.h"
 #include "registry.h"
 #include "registry_ini.h"
@@ -79,7 +80,7 @@
 #include "layer_special.h"
 #include "layer_terrain.h"
 #include "layer_units.h"
-#include "options.h" // for fill_xxx
+#include "options.h" // for fill_xxx, tileset options
 #include "page_game.h"
 #include "tilespec.h"
 #include "utils/colorizer.h"
@@ -88,7 +89,7 @@
 #define TILESPEC_CAPSTR                                                     \
   "+Freeciv-tilespec-Devel-2019-Jul-03 duplicates_ok precise-hp-bars "      \
   "unlimited-unit-select-frames unlimited-upkeep-sprites hex_corner "       \
-  "terrain-specific-extras"
+  "terrain-specific-extras options"
 /*
  * Tilespec capabilities acceptable to this program:
  *
@@ -112,19 +113,26 @@
  *      (upkeep.unhappy*, upkeep.output*)
  * hex_corner
  *    - The sprite type "hex_corner" is supported
+ * options
+ *    - Signals that tileset options are supported
  */
 
-#define SPEC_CAPSTR "+Freeciv-spec-Devel-2019-Jul-03"
+#define SPEC_CAPSTR "+Freeciv-spec-Devel-2019-Jul-03 options"
 /*
  * Individual spec file capabilities acceptable to this program:
  *
  * +Freeciv-3.1-spec
  *    - basic format for Freeciv versions 3.1.x; required
+ * options
+ *    - Signals that tileset options are supported
  */
 
 #define TILESPEC_SUFFIX ".tilespec"
 #define TILE_SECTION_PREFIX "tile_"
 
+/// The prefix for option sections in the tilespec file.
+const static char *const OPTION_SECTION_PREFIX = "option_";
+
 #define NUM_TILES_DIGITS 10
 
 #define FULL_TILE_X_OFFSET ((t->normal_tile_width - t->full_tile_width) / 2)
@@ -269,7 +277,7 @@ struct specfile {
   char *file_name;
 };
 
-/*
+/**
  * Information about an individual sprite. All fields except 'sprite' are
  * filled at the time of the scan of the specfile. 'Sprite' is
  * set/cleared on demand in load_sprite/unload_sprite.
@@ -301,6 +309,8 @@ struct tileset {
 
   std::vector<tileset_log_entry> log;
 
+  std::map<QString, tileset_option> options;
+
   std::vector<std::unique_ptr<freeciv::layer>> layers;
   struct {
     freeciv::layer_special *background, *middleground, *foreground;
@@ -367,6 +377,9 @@ static bool tileset_update = false;
 static struct tileset *tileset_read_toplevel(const QString &tileset_name,
                                              bool verbose, int topology_id);
 
+static bool tileset_setup_options(struct tileset *t,
+                                  const section_file *file);
+
 static void tileset_setup_base(struct tileset *t,
                                const struct extra_type *pextra,
                                const char *tag);
@@ -988,8 +1001,7 @@ bool tilespec_try_read(const QString &tileset_name, bool verbose,
    Unlike the initial reading code, which reads pieces one at a time,
    this gets rid of the old data and reads in the new all at once.  If the
    new tileset fails to load the old tileset may be reloaded; otherwise the
-   client will exit.  If a nullptr name is given the current tileset will be
-   reread.
+   client will exit.
 
    It will also call the necessary functions to redraw the graphics.
 
@@ -1331,11 +1343,28 @@ static void scan_specfile(struct tileset *t, struct specfile *sf,
                     secfile_error());
           continue;
         }
+
+        // Cursor pointing coordinates
         hot_x = secfile_lookup_int_default(file, 0, "%s.tiles%d.hot_x",
                                            sec_name, j);
         hot_y = secfile_lookup_int_default(file, 0, "%s.tiles%d.hot_y",
                                            sec_name, j);
 
+        // User-configured options
+        auto option = QString::fromUtf8(secfile_lookup_str_default(
+            file, "", "%s.tiles%d.option", sec_name, j));
+        if (!option.isEmpty()) {
+          if (!tileset_has_option(t, option)) {
+            // Ignore unknown options
+            tileset_error(
+                t, QtWarningMsg, "%s: unknown option %s for sprite %s",
+                tileset_basename(t), qUtf8Printable(option), tags[0]);
+          } else if (!tileset_option_is_enabled(t, option)) {
+            // Skip sprites that correspond to disabled options
+            continue;
+          }
+        }
+
         // there must be at least 1 because of the while():
         fc_assert_action(num_tags > 0, continue);
 
@@ -1358,7 +1387,9 @@ static void scan_specfile(struct tileset *t, struct specfile *sf,
 
         if (!duplicates_ok) {
           for (k = 0; k < num_tags; k++) {
-            if (t->sprite_hash->contains(tags[k])) {
+            if (t->sprite_hash->contains(tags[k]) && !option.isEmpty()) {
+              // Warn about duplicated sprites, except if it was enabled by
+              // a user option (to override the default).
               qCritical("warning: %s: already have a sprite for \"%s\".",
                         t->name, tags[k]);
             }
@@ -1395,9 +1426,25 @@ static void scan_specfile(struct tileset *t, struct specfile *sf,
                 secfile_error());
       continue;
     }
+
+    // Cursor pointing coordinates
     hot_x = secfile_lookup_int_default(file, 0, "extra.sprites%d.hot_x", i);
     hot_y = secfile_lookup_int_default(file, 0, "extra.sprites%d.hot_y", i);
 
+    // User-configured options
+    auto option = QString::fromUtf8(
+        secfile_lookup_str_default(file, "", "extras.sprites%d.option", i));
+    if (!option.isEmpty()) {
+      if (!tileset_has_option(t, option)) {
+        // Ignore unknown options
+        tileset_error(t, QtWarningMsg, "%s: unknown option %s for sprite %s",
+                      tileset_basename(t), qUtf8Printable(option), tags[0]);
+      } else if (!tileset_option_is_enabled(t, option)) {
+        // Skip sprites that correspond to disabled options
+        continue;
+      }
+    }
+
     ss = new small_sprite;
     ss->ref_count = 0;
     ss->file = fc_strdup(filename);
@@ -2048,6 +2095,10 @@ static struct tileset *tileset_read_toplevel(const QString &tileset_name,
   section_list_destroy(sections);
   sections = nullptr;
 
+  if (!tileset_setup_options(t, file)) {
+    return nullptr;
+  }
+
   t->estyle_hash = new QHash<QString, int>;
 
   for (i = 0; i < ESTYLE_COUNT; i++) {
@@ -2133,6 +2184,77 @@ static struct tileset *tileset_read_toplevel(const QString &tileset_name,
   return t;
 }
 
+/**
+ * Loads tileset options.
+ *
+ * This function loads options from the a '.tilespec' file and sets up all
+ * structures in the tileset.
+ */
+static bool tileset_setup_options(struct tileset *t,
+                                  const section_file *file)
+{
+  // First load options from the tilespec file.
+  auto sections =
+      secfile_sections_by_name_prefix(file, OPTION_SECTION_PREFIX);
+  if (!sections) {
+    return true;
+  }
+
+  std::set<QString> all_names;
+
+  section_list_iterate(sections, psection)
+  {
+    // Mandatory fields: name, description. Optional: enabled_by_default.
+    const auto sec_name = section_name(psection);
+
+    tileset_option option;
+
+    auto name = secfile_lookup_str_default(file, "", "%s.name", sec_name);
+    if (qstrlen(name) == 0) {
+      tileset_error(t, QtCriticalMsg, "Option \"%s\" has no name", sec_name);
+      continue; // Skip instead of erroring out: options are optional
+    }
+
+    // Check for duplicates
+    if (all_names.count(name)) {
+      tileset_error(t, QtCriticalMsg, "Duplicated option name \"%s\"", name);
+      continue; // Skip instead of erroring out: options are optional
+    }
+    all_names.insert(name);
+
+    auto description =
+        secfile_lookup_str_default(file, "", "%s.description", sec_name);
+    if (qstrlen(description) == 0) {
+      tileset_error(t, QtCriticalMsg, "Option \"%s\" has no description",
+                    name);
+      continue; // Skip instead of erroring out: options are optional
+    }
+    option.description = QString::fromUtf8(description);
+
+    option.enabled_by_default =
+        secfile_lookup_bool_default(file, false, "%s.default", sec_name);
+    option.enabled = option.enabled_by_default;
+
+    t->options[name] = std::move(option);
+  }
+  section_list_iterate_end;
+
+  // Then apply client options. They override any default value we may have
+  // set.
+  const auto tileset_name = tileset_basename(t);
+  if (gui_options->tileset_options.count(tileset_name)) {
+    for (const auto &[name, value] :
+         gui_options->tileset_options[tileset_name]) {
+      if (tileset_has_option(t, name)) {
+        t->options[name].enabled = value;
+      }
+      // Silently ignore options that do not exist.
+    }
+  }
+
+  return true;
+}
+
 /**
    Returns a text name for the citizen, as used in the tileset.
  */
@@ -5690,35 +5812,97 @@ bool tileset_has_error(const struct tileset *t)
                      [](auto &entry) { return entry.level == LOG_ERROR; });
 }
 
+/**
+ * Checks if the tileset has any user-settable options.
+ */
+bool tileset_has_options(const struct tileset *t)
+{
+  return !t->options.empty();
+}
+
+/**
+ * Checks if the tileset has supports the given user-settable option.
+ */
+bool tileset_has_option(const struct tileset *t, const QString &option)
+{
+  return t->options.count(option);
+}
+
+/**
+ * Gets the user-settable options of the tileset.
+ */
+std::map<QString, tileset_option>
+tileset_get_options(const struct tileset *t)
+{
+  return t->options;
+}
+
+/**
+ * Checks if an user-settable tileset option is enabled.
+ *
+ * The option must exist in the tileset.
+ */
+bool tileset_option_is_enabled(const struct tileset *t, const QString &name)
+{
+  return t->options.at(name).enabled;
+}
+
+/**
+ * Enable or disable a user-settable tileset option.
+ *
+ * The tileset may be reloaded as a result, invalidating \c t.
+ *
+ * Returns false if the option does not exist. The game must have been
+ * initialized before calling this.
+ */
+bool tileset_set_option(struct tileset *t, const QString &name, bool enabled)
+{
+  auto it = t->options.find(name);
+  fc_assert_ret_val(it != t->options.end(), false);
+
+  if (it->second.enabled != enabled) {
+    // Change the value in the client settings
+    gui_options->tileset_options[tileset_basename(t)][name] = enabled;
+    tilespec_reread_frozen_refresh(t->name);
+  }
+  return true;
+}
+
 /**
    Return tileset name
  */
-const char *tileset_name_get(struct tileset *t) { return t->given_name; }
+const char *tileset_name_get(const struct tileset *t)
+{
+  return t->given_name;
+}
 
 /**
    Return tileset version
  */
-const char *tileset_version(struct tileset *t) { return t->version; }
+const char *tileset_version(const struct tileset *t) { return t->version; }
 
 /**
    Return tileset description summary
  */
-const char *tileset_summary(struct tileset *t) { return t->summary; }
+const char *tileset_summary(const struct tileset *t) { return t->summary; }
 
 /**
    Return tileset description body
  */
-const char *tileset_description(struct tileset *t) { return t->description; }
+const char *tileset_description(const struct tileset *t)
+{
+  return t->description;
+}
 
 /**
    Return tileset topology index
  */
-int tileset_topo_index(struct tileset *t) { return t->ts_topo_idx; }
+int tileset_topo_index(const struct tileset *t) { return t->ts_topo_idx; }
 
 /**
  * Creates the help item for the given tileset
  */
-help_item *tileset_help(struct tileset *t)
+help_item *tileset_help(const struct tileset *t)
 {
   if (t == nullptr) {
     return nullptr;
diff --git a/client/tileset/tilespec.h b/client/tileset/tilespec.h
index 55ee70e94d..b3b0814743 100644
--- a/client/tileset/tilespec.h
+++ b/client/tileset/tilespec.h
@@ -23,6 +23,8 @@
 #include <QColor>
 #include <QEvent>
 
+#include <map>
+
 struct base_type;
 struct help_item;
 struct resource_type;
@@ -97,6 +99,16 @@ struct tileset_log_entry {
   QString message;
 };
 
+/**
+ * Tileset options allow altering the behavior of a tileset.
+ */
+struct tileset_option {
+  QString
+      description; ///< One-line description for use in the UI (translated)
+  bool enabled;    /// < Current status
+  bool enabled_by_default; ///< Default status
+};
+
 struct tileset;
 
 extern struct tileset *tileset;
@@ -119,6 +131,14 @@ bool tileset_is_fully_loaded();
 std::vector<tileset_log_entry> tileset_log(const struct tileset *t);
 bool tileset_has_error(const struct tileset *t);
 
+bool tileset_has_options(const struct tileset *t);
+bool tileset_has_option(const struct tileset *t, const QString &name);
+std::map<QString, tileset_option>
+tileset_get_options(const struct tileset *t);
+bool tileset_option_is_enabled(const struct tileset *t, const QString &name);
+bool tileset_set_option(struct tileset *t, const QString &name,
+                        bool enabled);
+
 void finish_loading_sprites(struct tileset *t);
 
 bool tilespec_try_read(const QString &name, bool verbose, int topo_id);
@@ -321,9 +341,9 @@ QString cardinal_index_str(const struct tileset *t, int idx);
 #define TS_TOPO_HEX 1
 #define TS_TOPO_ISOHEX 2
 
-const char *tileset_name_get(struct tileset *t);
-const char *tileset_version(struct tileset *t);
-const char *tileset_summary(struct tileset *t);
-const char *tileset_description(struct tileset *t);
-int tileset_topo_index(struct tileset *t);
-help_item *tileset_help(struct tileset *t);
+const char *tileset_name_get(const struct tileset *t);
+const char *tileset_version(const struct tileset *t);
+const char *tileset_summary(const struct tileset *t);
+const char *tileset_description(const struct tileset *t);
+int tileset_topo_index(const struct tileset *t);
+help_item *tileset_help(const struct tileset *t);
diff --git a/client/tileset_options.cpp b/client/tileset_options.cpp
new file mode 100644
index 0000000000..296608a131
--- /dev/null
+++ b/client/tileset_options.cpp
@@ -0,0 +1,71 @@
+// SPDX-FileCopyrightText: Louis Moureaux
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "tileset_options.h"
+
+#include "name_translation.h"
+#include "tileset/tilespec.h"
+
+#include <QCheckBox>
+#include <QDialogButtonBox>
+#include <QPushButton>
+#include <QVBoxLayout>
+
+namespace freeciv {
+
+/**
+ * Sets up the tileset options dialog.
+ *
+ * The dialog contains a series of check boxes, one for each options. They
+ * take effect immediately. There is also a close button and a reset button.
+ */
+tileset_options_dialog::tileset_options_dialog(struct tileset *t,
+                                               QWidget *parent)
+    : QDialog(parent)
+{
+  setModal(true);
+  setMinimumWidth(300); // For the title to be fully visible
+  setWindowTitle(_("Tileset Options"));
+
+  auto layout = new QVBoxLayout;
+  setLayout(layout);
+
+  for (const auto &[name_, option] : tileset_get_options(t)) {
+    // https://stackoverflow.com/q/46114214 (TODO C++20)
+    auto name = name_;
+
+    auto check = new QCheckBox(option.description);
+
+    // Sync check box state with the tileset
+    check->setChecked(tileset_option_is_enabled(t, name));
+    connect(check, &QCheckBox::toggled, [name](bool checked) {
+      tileset_set_option(tileset, name, checked);
+    });
+
+    m_checks[name] = check;
+    layout->addWidget(check);
+  }
+
+  layout->addSpacing(6);
+
+  auto buttons = new QDialogButtonBox(QDialogButtonBox::Close
+                                      | QDialogButtonBox::Reset);
+  connect(buttons->button(QDialogButtonBox::Close), &QPushButton::clicked,
+          this, &QDialog::accept);
+  connect(buttons->button(QDialogButtonBox::Reset), &QPushButton::clicked,
+          this, &tileset_options_dialog::reset);
+  layout->addWidget(buttons);
+}
+
+/**
+ * Resets all options to the tileset defaults.
+ */
+void tileset_options_dialog::reset()
+{
+  for (const auto &[name, option] : tileset_get_options(tileset)) {
+    // Changes are propagated though the clicked() signal.
+    m_checks[name]->setChecked(option.enabled_by_default);
+  }
+}
+
+} // namespace freeciv
diff --git a/client/tileset_options.h b/client/tileset_options.h
new file mode 100644
index 0000000000..8cc4f20fb0
--- /dev/null
+++ b/client/tileset_options.h
@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: 2024 Louis Moureaux
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <QDialog>
+#include <QString>
+
+#include <map>
+
+class QCheckBox;
+struct tileset;
+
+namespace freeciv {
+
+/**
+ * Lets the user toggle tileset options.
+ */
+class tileset_options_dialog : public QDialog {
+  Q_OBJECT
+
+  std::map<QString, QCheckBox *> m_checks;
+
+public:
+  explicit tileset_options_dialog(struct tileset *t, QWidget *parent = 0);
+
+private slots:
+  void reset();
+};
+
+} // namespace freeciv
diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt
index 773f7e0cee..87209653bd 100644
--- a/data/CMakeLists.txt
+++ b/data/CMakeLists.txt
@@ -131,13 +131,19 @@ endif()
 if (FREECIV_ENABLE_CLIENT)
   # Tilesets
   add_tileset(NAME amplio2)
-  add_tileset(NAME cimpletoon)
   add_tileset(NAME hex2t)
   add_tileset(NAME hexemplio)
   add_tileset(NAME isophex)
   add_tileset(NAME isotrident)
   add_tileset(NAME trident)
 
+  # Cimpletoon option. Sprites shared by amplio2 and hexemplio
+  install(
+    DIRECTORY cimpletoon
+    DESTINATION "${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}"
+    COMPONENT freeciv21
+    FILES_MATCHING PATTERN *.png PATTERN *.spec PATTERN README.*)
+
   add_subdirectory(tilesets)
 
   # Misc
diff --git a/data/amplio2.tilespec b/data/amplio2.tilespec
index f5c586c85a..6419b662da 100644
--- a/data/amplio2.tilespec
+++ b/data/amplio2.tilespec
@@ -149,6 +149,7 @@ files =
   "misc/governments.spec",
   "misc/specialists.spec",
   "misc/units.spec",
+  "cimpletoon/orient_units.spec",
   "amplio2/veterancy.spec",
   "misc/flags-large.spec",
   "misc/buildings-large.spec",
@@ -340,3 +341,8 @@ styles =
       "ts.horses",     "Single1"
       "ts.seals",      "Single1"
     }
+
+[option_cimpletoon]
+name = "cimpletoon"
+description = _("Use 3D Cimpletoon units")
+default = FALSE
diff --git a/data/cimpletoon.tilespec b/data/cimpletoon.tilespec
deleted file mode 100644
index c2c6545dce..0000000000
--- a/data/cimpletoon.tilespec
+++ /dev/null
@@ -1,345 +0,0 @@
-
-[tilespec]
-
-; Format and options of this tilespec file:
-options = "+Freeciv-tilespec-Devel-2019-Jul-03"
-
-; A simple name for the tileset specified by this file:
-name = "Cimpletoon"
-priority = 25
-
-; There`s no separate versioning in tilesets part of main freeciv distribution
-;version = ""
-
-; Summary and full description of the tileset.
-summary = _("\
-Variant of Amplio2 isometric tileset with unit sprites showing the \
-direction the unit is facing.\
-")
-;description = ""
-
-; TODO: add more overall information fields on tiles,
-; eg, authors, colors, etc.
-
-; Basic tile sizes:
-normal_tile_width  = 96
-normal_tile_height = 48
-small_tile_width   = 15
-small_tile_height  = 20
-
-; Basic tile style.
-type = "isometric"
-is_hex = FALSE
-
-; Blended fog
-fog_style      = "Darkness"
-darkness_style = "Corner"
-
-; offset the flags by this amount when drawing units
-unit_flag_offset_x = 25
-unit_flag_offset_y = 16
-city_flag_offset_x = 2
-city_flag_offset_y = 9
-
-; offset the city occupied sprite by this amount
-occupied_offset_x = 0
-occupied_offset_y = 0
-
-; offset the units by this amount
-unit_offset_x = 19
-unit_offset_y = 18
-
-; colors with this (HSL) hue will be replaced by the player color in city and
-; unit sprites (-1 to disable)
-replaced_hue = -1
-
-; offset of the normal activity icons
-activity_offset_x = 0
-activity_offset_y = 0
-
-; offset of the selected unit sprites
-select_offset_x = 0
-select_offset_y = 0
-; delay between two consecutive images
-;select_step_ms = 100
-
-; offset the cities by this amount
-city_offset_x = 0
-city_offset_y = 0
-
-; offset the city size number by this amount
-; This is relative to full sprite origin.
-city_size_offset_x = 0
-city_size_offset_y = 0
-
-; offset the city bar text by this amount (from the city tile origin)
-citybar_offset_y = 30
-
-; offset the tile label text by this amount
-tilelabel_offset_y = 15
-
-; offset the upkeep icons by this amount from the top of the unit itself.
-; The default is the normal tile height, which means that the upkeep icons
-; appear below the unit icon if the unit icons are equal to tile height
-; (typical in overhead tileset), or overlay lower part of the unit icon,
-; if unit icon is higher than tiles (typical in iso tilesets)
-;unit_upkeep_offset_y = 0
-
-; Like unit_upkeep_offset_y, but to be used in case there`s only small
-; space for the overall icon produced. Defaults to unit_upkeep_offset_y -
-; not having alternative layout.
-;unit_upkeep_small_offset_y = 0
-
-; For tilesets with oriented units, the directional sprite to use to
-; represent a unit type rather than a specific unit from the map
-; (e.g., in worklists, editor, and online help). Does not have to be a
-; valid direction for the tileset.
-unit_default_orientation = "s"
-
-; The map is rendered in "layers", just like any decent image editor
-; supports. The setting below allows to change the layer drawing order. The
-; first layer in the list will be drawn below the others; the second on top
-; of it, and so on. No layer can be omitted from the list, nor can new ones
-; be added.
-;layer_order =
-;  "Background", ; Background color (if enabled, the player color where there
-;                ; are units or cities). You probably want to leave this
-;                ; first.
-;  "Terrain1",   ; The three terrain layers. See sections [layerN] below.
-;  "Darkness",   ; Darkness (unseen tiles)
-;  "Terrain2",
-;  "Terrain3",
-;  "Water",      ; All extras with "River" style.
-;  "Roads",      ; All extras with style "RoadAllSeparate",
-;                ; "RoadParityCombined" or "RoadAllCombined".
-;  "Special1",   ; 1st layer for extras with style "3Layers" or "Single1".
-;  "Grid1",      ; Grid layer for isometric tilesets.
-;  "City1",      ; City and walls.
-;  "Special2",   ; 2nd layer for extras with "3Layers" and "Single2" styles.
-;  "Fog",        ; Fog of war (on tiles one knows but doesn`t see).
-;  "Unit",       ; Units except the selected one(s).
-;  "Special3",   ; 3rd layer for extras with "3Layers" style.
-;  "BaseFlags",  ; Base flags.
-;  "City2",      ; City size when the city bar is disabled.
-;  "Grid2",      ; Second grid layer (overhead tilesets only).
-;  "Overlays",   ; Tile output sprites.
-;  "TileLabel",  ; Tile labels ("Scorched spot").
-;  "CityBar",    ; The city bar with name, production, food, ...
-;  "FocusUnit",  ; The focused unit(s).
-;  "Goto",       ; Goto turn count and intermediate points, *not* goto lines.
-;  "WorkerTask", ; The unit task indicators ("G", "S", ...).
-;  "Editor",     ; Editor stuff (selected tile and start points).
-;  "InfraWork"   ; Icons for the extras being placed.
-
-; Below, the graphics spec files; must be somewhere (anywhere) in
-; the data path. Order may be important for color allocation on
-; low-color systems, and if there are any duplicate tags (lattermost
-; tag is used).
-files =
-  "amplio2/terrain1.spec",
-  "amplio2/maglev.spec",
-  "amplio2/terrain2.spec",
-  "amplio2/hills.spec",
-  "amplio2/mountains.spec",
-  "amplio2/ocean.spec",
-  "amplio2/water.spec",
-  "amplio2/tiles.spec",
-  "amplio2/upkeep.spec",
-  "amplio2/activities.spec",
-  "amplio2/fog.spec",
-  "misc/small.spec",
-  "misc/events.spec",
-  "misc/governments.spec",
-  "misc/specialists.spec",
-  "amplio2/veterancy.spec",
-  "cimpletoon/orient_units.spec",
-  "misc/flags-large.spec",
-  "misc/buildings-large.spec",
-  "misc/wonders-large.spec",
-  "misc/space.spec",
-  "misc/techs.spec",
-  "misc/treaty.spec",
-  "amplio2/nuke.spec",
-  "amplio2/explosions.spec",
-  "amplio2/cities.spec",
-  "amplio2/bases.spec",
-  "amplio2/select.spec",
-  "amplio2/grid.spec",
-  "misc/cursors.spec",
-  "misc/overlays.spec",
-  "misc/citybar.spec",
-  "misc/shields-large.spec",
-  "misc/editor.spec"
-
-
-; Include color definitions
-*include "misc/colors.tilespec"
-
-; Terrain info - see README.graphics
-
-[layer0]
-match_types = "shallow", "deep", "land"
-
-[layer1]
-match_types = "forest", "hills", "mountains", "water", "ice"
-
-[layer2]
-match_types = "water", "ice"
-
-; Water graphics referenced by terrain.ruleset
-;
-[tile_lake]
-tag = "lake"
-blend_layer = 1
-num_layers = 1
-layer0_match_type = "shallow"
-layer0_match_with = "land"
-layer0_sprite_type = "corner"
-
-[tile_coast]
-tag = "coast"
-blend_layer = 1
-num_layers = 2
-layer0_match_type = "shallow"
-layer0_match_with = "deep", "land"
-layer0_sprite_type = "corner"
-layer1_match_type = "water"
-layer1_match_with = "ice"
-layer1_sprite_type = "corner"
-
-[tile_floor]
-tag = "floor"
-blend_layer = 0
-num_layers = 2
-layer0_match_type = "deep"
-layer0_match_with = "shallow", "land"
-layer0_sprite_type = "corner"
-layer1_match_type = "water"
-layer1_match_with = "ice"
-layer1_sprite_type = "corner"
-
-; Land graphics referenced by terrain.ruleset
-;
-[tile_arctic]
-tag = "arctic"
-; treated as water for ice cliffs
-blend_layer = 0
-num_layers = 3
-layer0_match_type = "shallow"
-layer1_match_type = "ice"
-layer2_match_type = "ice"
-
-[tile_desert]
-tag = "desert"
-blend_layer = 1
-num_layers = 1
-layer0_match_type = "land"
-
-[tile_forest]
-tag = "forest"
-blend_layer = 1
-num_layers = 2
-layer0_match_type = "land"
-layer1_match_type = "forest"
-layer1_match_with = "forest"
-
-[tile_grassland]
-tag = "grassland"
-blend_layer = 1
-num_layers = 1
-layer0_match_type = "land"
-
-[tile_hills]
-tag = "hills"
-blend_layer = 1
-num_layers = 2
-layer0_match_type = "land"
-layer1_match_type = "hills"
-layer1_match_with = "hills"
-
-[tile_jungle]
-tag = "jungle"
-blend_layer = 1
-num_layers = 2
-layer0_match_type = "land"
-layer1_match_type = "forest"
-layer1_match_with = "forest"
-
-[tile_mountains]
-tag = "mountains"
-blend_layer = 1
-num_layers = 2
-layer0_match_type = "land"
-layer1_match_type = "hills"
-layer1_match_with = "hills"
-layer1_is_tall = TRUE
-layer1_offset_y = 6
-
-[tile_plains]
-tag = "plains"
-blend_layer = 1
-num_layers = 1
-layer0_match_type = "land"
-
-[tile_swamp]
-tag = "swamp"
-blend_layer = 1
-num_layers = 1
-layer0_match_type = "land"
-
-[tile_tundra]
-tag = "tundra"
-blend_layer = 1
-num_layers = 1
-layer0_match_type = "land"
-
-[tile_inaccessible]
-tag = "inaccessible"
-blend_layer = 0
-num_layers = 1
-layer0_match_type = "land"
-
-[extras]
-styles =
-    { "name",          "style"
-      "road.road",     "RoadAllSeparate"
-      "road.rail",     "RoadAllSeparate"
-      "road.maglev",   "RoadAllSeparate"
-      "road.river",    "River"
-      "tx.irrigation", "Cardinals"
-      "tx.farmland",   "Cardinals"
-      "tx.mine",       "Single1"
-      "tx.oil_mine",   "Single1"
-      "tx.oil_rig",    "Single1"
-      "tx.pollution",  "Single2"
-      "tx.fallout",    "Single2"
-      "tx.village",    "Single1"
-      "base.outpost",  "3Layer"
-      "base.fortress", "3Layer"
-      "base.airstrip", "3Layer"
-      "base.airbase",  "3Layer"
-      "base.buoy",     "3Layer"
-      "extra.ruins",   "3Layer"
-      "ts.gold",       "Single1"
-      "ts.iron",       "Single1"
-      "ts.tundra_game", "Single1"
-      "ts.furs",       "Single1"
-      "ts.coal",       "Single1"
-      "ts.fish",       "Single1"
-      "ts.fruit",      "Single1"
-      "ts.gems",       "Single1"
-      "ts.buffalo",    "Single1"
-      "ts.wheat",      "Single1"
-      "ts.oasis",      "Single1"
-      "ts.peat",       "Single1"
-      "ts.pheasant",   "Single1"
-      "ts.grassland_resources", "Single1"
-      "ts.arctic_ivory", "Single1"
-      "ts.silk",       "Single1"
-      "ts.spice",      "Single1"
-      "ts.whales",     "Single1"
-      "ts.wine",       "Single1"
-      "ts.oil",        "Single1"
-      "ts.horses",     "Single1"
-      "ts.seals",      "Single1"
-    }
diff --git a/data/cimpletoon/orient_units.spec b/data/cimpletoon/orient_units.spec
index fc5e389c95..e1130b2719 100644
--- a/data/cimpletoon/orient_units.spec
+++ b/data/cimpletoon/orient_units.spec
@@ -2,7 +2,7 @@
 [spec]
 
 ; Format and options of this spec file:
-options = "+Freeciv-spec-Devel-2019-Jul-03"
+options = "+Freeciv-spec-Devel-2019-Jul-03 options"
 
 [info]
 
@@ -23,509 +23,509 @@ dx = 64
 dy = 48
 pixel_border = 1
 
-tiles = { "row", "column", "tag"
-  0,  0, "u.settlers_sw"
-  0,  1, "u.settlers_w"
-  0,  2, "u.settlers_nw"
-  0,  3, "u.settlers_n"
-  0,  4, "u.settlers_ne"
-  0,  5, "u.settlers_e"
-  0,  6, "u.settlers_se"
-  0,  7, "u.settlers_s"
-
-  1,  0, "u.warriors_sw"
-  1,  1, "u.warriors_w"
-  1,  2, "u.warriors_nw"
-  1,  3, "u.warriors_n"
-  1,  4, "u.warriors_ne"
-  1,  5, "u.warriors_e"
-  1,  6, "u.warriors_se"
-  1,  7, "u.warriors_s"
-
-  2,  0, "u.explorer_sw"
-  2,  1, "u.explorer_w"
-  2,  2, "u.explorer_nw"
-  2,  3, "u.explorer_n"
-  2,  4, "u.explorer_ne"
-  2,  5, "u.explorer_e"
-  2,  6, "u.explorer_se"
-  2,  7, "u.explorer_s"
-
-  3,  0, "u.worker_sw"
-  3,  1, "u.worker_w"
-  3,  2, "u.worker_nw"
-  3,  3, "u.worker_n"
-  3,  4, "u.worker_ne"
-  3,  5, "u.worker_e"
-  3,  6, "u.worker_se"
-  3,  7, "u.worker_s"
-
-  4,  0, "u.horsemen_sw"
-  4,  1, "u.horsemen_w"
-  4,  2, "u.horsemen_nw"
-  4,  3, "u.horsemen_n"
-  4,  4, "u.horsemen_ne"
-  4,  5, "u.horsemen_e"
-  4,  6, "u.horsemen_se"
-  4,  7, "u.horsemen_s"
-
-  5,  0, "u.archers_sw"
-  5,  1, "u.archers_w"
-  5,  2, "u.archers_nw"
-  5,  3, "u.archers_n"
-  5,  4, "u.archers_ne"
-  5,  5, "u.archers_e"
-  5,  6, "u.archers_se"
-  5,  7, "u.archers_s"
-
-  6,  0, "u.phalanx_sw"
-  6,  1, "u.phalanx_w"
-  6,  2, "u.phalanx_nw"
-  6,  3, "u.phalanx_n"
-  6,  4, "u.phalanx_ne"
-  6,  5, "u.phalanx_e"
-  6,  6, "u.phalanx_se"
-  6,  7, "u.phalanx_s"
-
-  7,  0, "u.trireme_sw"
-  7,  1, "u.trireme_w"
-  7,  2, "u.trireme_nw"
-  7,  3, "u.trireme_n"
-  7,  4, "u.trireme_ne"
-  7,  5, "u.trireme_e"
-  7,  6, "u.trireme_se"
-  7,  7, "u.trireme_s"
-
-  8,  0, "u.chariot_sw"
-  8,  1, "u.chariot_w"
-  8,  2, "u.chariot_nw"
-  8,  3, "u.chariot_n"
-  8,  4, "u.chariot_ne"
-  8,  5, "u.chariot_e"
-  8,  6, "u.chariot_se"
-  8,  7, "u.chariot_s"
-
-  9,  0, "u.catapult_sw"
-  9,  1, "u.catapult_w"
-  9,  2, "u.catapult_nw"
-  9,  3, "u.catapult_n"
-  9,  4, "u.catapult_ne"
-  9,  5, "u.catapult_e"
-  9,  6, "u.catapult_se"
-  9,  7, "u.catapult_s"
-
-  10,  0, "u.legion_sw"
-  10,  1, "u.legion_w"
-  10,  2, "u.legion_nw"
-  10,  3, "u.legion_n"
-  10,  4, "u.legion_ne"
-  10,  5, "u.legion_e"
-  10,  6, "u.legion_se"
-  10,  7, "u.legion_s"
-
-  11,  0, "u.diplomat_sw"
-  11,  1, "u.diplomat_w"
-  11,  2, "u.diplomat_nw"
-  11,  3, "u.diplomat_n"
-  11,  4, "u.diplomat_ne"
-  11,  5, "u.diplomat_e"
-  11,  6, "u.diplomat_se"
-  11,  7, "u.diplomat_s"
-
-  12,  0, "u.caravan_sw"
-  12,  1, "u.caravan_w"
-  12,  2, "u.caravan_nw"
-  12,  3, "u.caravan_n"
-  12,  4, "u.caravan_ne"
-  12,  5, "u.caravan_e"
-  12,  6, "u.caravan_se"
-  12,  7, "u.caravan_s"
-
-  13,  0, "u.pikemen_sw"
-  13,  1, "u.pikemen_w"
-  13,  2, "u.pikemen_nw"
-  13,  3, "u.pikemen_n"
-  13,  4, "u.pikemen_ne"
-  13,  5, "u.pikemen_e"
-  13,  6, "u.pikemen_se"
-  13,  7, "u.pikemen_s"
-
-  14,  0, "u.knights_sw"
-  14,  1, "u.knights_w"
-  14,  2, "u.knights_nw"
-  14,  3, "u.knights_n"
-  14,  4, "u.knights_ne"
-  14,  5, "u.knights_e"
-  14,  6, "u.knights_se"
-  14,  7, "u.knights_s"
-
-  15,  0, "u.caravel_sw"
-  15,  1, "u.caravel_w"
-  15,  2, "u.caravel_nw"
-  15,  3, "u.caravel_n"
-  15,  4, "u.caravel_ne"
-  15,  5, "u.caravel_e"
-  15,  6, "u.caravel_se"
-  15,  7, "u.caravel_s"
-
-  16,  0, "u.galleon_sw"
-  16,  1, "u.galleon_w"
-  16,  2, "u.galleon_nw"
-  16,  3, "u.galleon_n"
-  16,  4, "u.galleon_ne"
-  16,  5, "u.galleon_e"
-  16,  6, "u.galleon_se"
-  16,  7, "u.galleon_s"
-
-  17,  0, "u.frigate_sw"
-  17,  1, "u.frigate_w"
-  17,  2, "u.frigate_nw"
-  17,  3, "u.frigate_n"
-  17,  4, "u.frigate_ne"
-  17,  5, "u.frigate_e"
-  17,  6, "u.frigate_se"
-  17,  7, "u.frigate_s"
-
-  18,  0, "u.ironclad_sw"
-  18,  1, "u.ironclad_w"
-  18,  2, "u.ironclad_nw"
-  18,  3, "u.ironclad_n"
-  18,  4, "u.ironclad_ne"
-  18,  5, "u.ironclad_e"
-  18,  6, "u.ironclad_se"
-  18,  7, "u.ironclad_s"
-
-  19,  0, "u.musketeers_sw"
-  19,  1, "u.musketeers_w"
-  19,  2, "u.musketeers_nw"
-  19,  3, "u.musketeers_n"
-  19,  4, "u.musketeers_ne"
-  19,  5, "u.musketeers_e"
-  19,  6, "u.musketeers_se"
-  19,  7, "u.musketeers_s"
-
-  20,  0, "u.dragoons_sw"
-  20,  1, "u.dragoons_w"
-  20,  2, "u.dragoons_nw"
-  20,  3, "u.dragoons_n"
-  20,  4, "u.dragoons_ne"
-  20,  5, "u.dragoons_e"
-  20,  6, "u.dragoons_se"
-  20,  7, "u.dragoons_s"
-
-  21,  0, "u.cannon_sw"
-  21,  1, "u.cannon_w"
-  21,  2, "u.cannon_nw"
-  21,  3, "u.cannon_n"
-  21,  4, "u.cannon_ne"
-  21,  5, "u.cannon_e"
-  21,  6, "u.cannon_se"
-  21,  7, "u.cannon_s"
-
-  22,  0, "u.engineers_sw"
-  22,  1, "u.engineers_w"
-  22,  2, "u.engineers_nw"
-  22,  3, "u.engineers_n"
-  22,  4, "u.engineers_ne"
-  22,  5, "u.engineers_e"
-  22,  6, "u.engineers_se"
-  22,  7, "u.engineers_s"
-
-  23,  0, "u.transport_sw"
-  23,  1, "u.transport_w"
-  23,  2, "u.transport_nw"
-  23,  3, "u.transport_n"
-  23,  4, "u.transport_ne"
-  23,  5, "u.transport_e"
-  23,  6, "u.transport_se"
-  23,  7, "u.transport_s"
-
-  24,  0, "u.destroyer_sw"
-  24,  1, "u.destroyer_w"
-  24,  2, "u.destroyer_nw"
-  24,  3, "u.destroyer_n"
-  24,  4, "u.destroyer_ne"
-  24,  5, "u.destroyer_e"
-  24,  6, "u.destroyer_se"
-  24,  7, "u.destroyer_s"
-
-  25,  0, "u.riflemen_sw"
-  25,  1, "u.riflemen_w"
-  25,  2, "u.riflemen_nw"
-  25,  3, "u.riflemen_n"
-  25,  4, "u.riflemen_ne"
-  25,  5, "u.riflemen_e"
-  25,  6, "u.riflemen_se"
-  25,  7, "u.riflemen_s"
-
-  26,  0, "u.cavalry_sw"
-  26,  1, "u.cavalry_w"
-  26,  2, "u.cavalry_nw"
-  26,  3, "u.cavalry_n"
-  26,  4, "u.cavalry_ne"
-  26,  5, "u.cavalry_e"
-  26,  6, "u.cavalry_se"
-  26,  7, "u.cavalry_s"
-
-  27,  0, "u.alpine_troops_sw"
-  27,  1, "u.alpine_troops_w"
-  27,  2, "u.alpine_troops_nw"
-  27,  3, "u.alpine_troops_n"
-  27,  4, "u.alpine_troops_ne"
-  27,  5, "u.alpine_troops_e"
-  27,  6, "u.alpine_troops_se"
-  27,  7, "u.alpine_troops_s"
-
-  28,  0, "u.freight_sw"
-  28,  1, "u.freight_w"
-  28,  2, "u.freight_nw"
-  28,  3, "u.freight_n"
-  28,  4, "u.freight_ne"
-  28,  5, "u.freight_e"
-  28,  6, "u.freight_se"
-  28,  7, "u.freight_s"
-
-  29,  0, "u.spy_sw"
-  29,  1, "u.spy_w"
-  29,  2, "u.spy_nw"
-  29,  3, "u.spy_n"
-  29,  4, "u.spy_ne"
-  29,  5, "u.spy_e"
-  29,  6, "u.spy_se"
-  29,  7, "u.spy_s"
-
-  30,  0, "u.cruiser_sw"
-  30,  1, "u.cruiser_w"
-  30,  2, "u.cruiser_nw"
-  30,  3, "u.cruiser_n"
-  30,  4, "u.cruiser_ne"
-  30,  5, "u.cruiser_e"
-  30,  6, "u.cruiser_se"
-  30,  7, "u.cruiser_s"
-
-  31,  0, "u.battleship_sw"
-  31,  1, "u.battleship_w"
-  31,  2, "u.battleship_nw"
-  31,  3, "u.battleship_n"
-  31,  4, "u.battleship_ne"
-  31,  5, "u.battleship_e"
-  31,  6, "u.battleship_se"
-  31,  7, "u.battleship_s"
-
-  32,  0, "u.submarine_sw"
-  32,  1, "u.submarine_w"
-  32,  2, "u.submarine_nw"
-  32,  3, "u.submarine_n"
-  32,  4, "u.submarine_ne"
-  32,  5, "u.submarine_e"
-  32,  6, "u.submarine_se"
-  32,  7, "u.submarine_s"
-
-  33,  0, "u.marines_sw"
-  33,  1, "u.marines_w"
-  33,  2, "u.marines_nw"
-  33,  3, "u.marines_n"
-  33,  4, "u.marines_ne"
-  33,  5, "u.marines_e"
-  33,  6, "u.marines_se"
-  33,  7, "u.marines_s"
-
-  34,  0, "u.partisan_sw"
-  34,  1, "u.partisan_w"
-  34,  2, "u.partisan_nw"
-  34,  3, "u.partisan_n"
-  34,  4, "u.partisan_ne"
-  34,  5, "u.partisan_e"
-  34,  6, "u.partisan_se"
-  34,  7, "u.partisan_s"
-
-  35,  0, "u.artillery_sw"
-  35,  1, "u.artillery_w"
-  35,  2, "u.artillery_nw"
-  35,  3, "u.artillery_n"
-  35,  4, "u.artillery_ne"
-  35,  5, "u.artillery_e"
-  35,  6, "u.artillery_se"
-  35,  7, "u.artillery_s"
-
-  36,  0, "u.fighter_sw"
-  36,  1, "u.fighter_w"
-  36,  2, "u.fighter_nw"
-  36,  3, "u.fighter_n"
-  36,  4, "u.fighter_ne"
-  36,  5, "u.fighter_e"
-  36,  6, "u.fighter_se"
-  36,  7, "u.fighter_s"
-
-  37,  0, "u.aegis_cruiser_sw"
-  37,  1, "u.aegis_cruiser_w"
-  37,  2, "u.aegis_cruiser_nw"
-  37,  3, "u.aegis_cruiser_n"
-  37,  4, "u.aegis_cruiser_ne"
-  37,  5, "u.aegis_cruiser_e"
-  37,  6, "u.aegis_cruiser_se"
-  37,  7, "u.aegis_cruiser_s"
-
-  38,  0, "u.carrier_sw"
-  38,  1, "u.carrier_w"
-  38,  2, "u.carrier_nw"
-  38,  3, "u.carrier_n"
-  38,  4, "u.carrier_ne"
-  38,  5, "u.carrier_e"
-  38,  6, "u.carrier_se"
-  38,  7, "u.carrier_s"
-
-  39,  0, "u.armor_sw"
-  39,  1, "u.armor_w"
-  39,  2, "u.armor_nw"
-  39,  3, "u.armor_n"
-  39,  4, "u.armor_ne"
-  39,  5, "u.armor_e"
-  39,  6, "u.armor_se"
-  39,  7, "u.armor_s"
-
-  40,  0, "u.mech_inf_sw"
-  40,  1, "u.mech_inf_w"
-  40,  2, "u.mech_inf_nw"
-  40,  3, "u.mech_inf_n"
-  40,  4, "u.mech_inf_ne"
-  40,  5, "u.mech_inf_e"
-  40,  6, "u.mech_inf_se"
-  40,  7, "u.mech_inf_s"
-
-  41,  0, "u.howitzer_sw"
-  41,  1, "u.howitzer_w"
-  41,  2, "u.howitzer_nw"
-  41,  3, "u.howitzer_n"
-  41,  4, "u.howitzer_ne"
-  41,  5, "u.howitzer_e"
-  41,  6, "u.howitzer_se"
-  41,  7, "u.howitzer_s"
-
-  42,  0, "u.paratroopers_sw"
-  42,  1, "u.paratroopers_w"
-  42,  2, "u.paratroopers_nw"
-  42,  3, "u.paratroopers_n"
-  42,  4, "u.paratroopers_ne"
-  42,  5, "u.paratroopers_e"
-  42,  6, "u.paratroopers_se"
-  42,  7, "u.paratroopers_s"
-
-  43,  0, "u.helicopter_sw"
-  43,  1, "u.helicopter_w"
-  43,  2, "u.helicopter_nw"
-  43,  3, "u.helicopter_n"
-  43,  4, "u.helicopter_ne"
-  43,  5, "u.helicopter_e"
-  43,  6, "u.helicopter_se"
-  43,  7, "u.helicopter_s"
-
-  44,  0, "u.bomber_sw"
-  44,  1, "u.bomber_w"
-  44,  2, "u.bomber_nw"
-  44,  3, "u.bomber_n"
-  44,  4, "u.bomber_ne"
-  44,  5, "u.bomber_e"
-  44,  6, "u.bomber_se"
-  44,  7, "u.bomber_s"
-
-  45,  0, "u.awacs_sw"
-  45,  1, "u.awacs_w"
-  45,  2, "u.awacs_nw"
-  45,  3, "u.awacs_n"
-  45,  4, "u.awacs_ne"
-  45,  5, "u.awacs_e"
-  45,  6, "u.awacs_se"
-  45,  7, "u.awacs_s"
-
-  46,  0, "u.nuclear_sw"
-  46,  1, "u.nuclear_w"
-  46,  2, "u.nuclear_nw"
-  46,  3, "u.nuclear_n"
-  46,  4, "u.nuclear_ne"
-  46,  5, "u.nuclear_e"
-  46,  6, "u.nuclear_se"
-  46,  7, "u.nuclear_s"
-
-  47,  0, "u.cruise_missile_sw"
-  47,  1, "u.cruise_missile_w"
-  47,  2, "u.cruise_missile_nw"
-  47,  3, "u.cruise_missile_n"
-  47,  4, "u.cruise_missile_ne"
-  47,  5, "u.cruise_missile_e"
-  47,  6, "u.cruise_missile_se"
-  47,  7, "u.cruise_missile_s"
-
-  48,  0, "u.stealth_bomber_sw"
-  48,  1, "u.stealth_bomber_w"
-  48,  2, "u.stealth_bomber_nw"
-  48,  3, "u.stealth_bomber_n"
-  48,  4, "u.stealth_bomber_ne"
-  48,  5, "u.stealth_bomber_e"
-  48,  6, "u.stealth_bomber_se"
-  48,  7, "u.stealth_bomber_s"
-
-  49,  0, "u.stealth_fighter_sw"
-  49,  1, "u.stealth_fighter_w"
-  49,  2, "u.stealth_fighter_nw"
-  49,  3, "u.stealth_fighter_n"
-  49,  4, "u.stealth_fighter_ne"
-  49,  5, "u.stealth_fighter_e"
-  49,  6, "u.stealth_fighter_se"
-  49,  7, "u.stealth_fighter_s"
-
-  50,  0, "u.leader_sw"
-  50,  1, "u.leader_w"
-  50,  2, "u.leader_nw"
-  50,  3, "u.leader_n"
-  50,  4, "u.leader_ne"
-  50,  5, "u.leader_e"
-  50,  6, "u.leader_se"
-  50,  7, "u.leader_s"
-
-  51,  0, "u.barbarian_leader_sw"
-  51,  1, "u.barbarian_leader_w"
-  51,  2, "u.barbarian_leader_nw"
-  51,  3, "u.barbarian_leader_n"
-  51,  4, "u.barbarian_leader_ne"
-  51,  5, "u.barbarian_leader_e"
-  51,  6, "u.barbarian_leader_se"
-  51,  7, "u.barbarian_leader_s"
-
-  52,  0, "u.fanatics_sw"
-  52,  1, "u.fanatics_w"
-  52,  2, "u.fanatics_nw"
-  52,  3, "u.fanatics_n"
-  52,  4, "u.fanatics_ne"
-  52,  5, "u.fanatics_e"
-  52,  6, "u.fanatics_se"
-  52,  7, "u.fanatics_s"
-
-  53,  0, "u.crusaders_sw"
-  53,  1, "u.crusaders_w"
-  53,  2, "u.crusaders_nw"
-  53,  3, "u.crusaders_n"
-  53,  4, "u.crusaders_ne"
-  53,  5, "u.crusaders_e"
-  53,  6, "u.crusaders_se"
-  53,  7, "u.crusaders_s"
-
-  54,  0, "u.elephants_sw"
-  54,  1, "u.elephants_w"
-  54,  2, "u.elephants_nw"
-  54,  3, "u.elephants_n"
-  54,  4, "u.elephants_ne"
-  54,  5, "u.elephants_e"
-  54,  6, "u.elephants_se"
-  54,  7, "u.elephants_s"
-
-  55,  0, "u.migrants_sw"
-  55,  1, "u.migrants_w"
-  55,  2, "u.migrants_nw"
-  55,  3, "u.migrants_n"
-  55,  4, "u.migrants_ne"
-  55,  5, "u.migrants_e"
-  55,  6, "u.migrants_se"
-  55,  7, "u.migrants_s"
+tiles = { "row", "column", "option", "tag"
+  0,  0, "cimpletoon", "u.settlers_sw"
+  0,  1, "cimpletoon", "u.settlers_w"
+  0,  2, "cimpletoon", "u.settlers_nw"
+  0,  3, "cimpletoon", "u.settlers_n"
+  0,  4, "cimpletoon", "u.settlers_ne"
+  0,  5, "cimpletoon", "u.settlers_e"
+  0,  6, "cimpletoon", "u.settlers_se"
+  0,  7, "cimpletoon", "u.settlers_s"
+
+  1,  0, "cimpletoon", "u.warriors_sw"
+  1,  1, "cimpletoon", "u.warriors_w"
+  1,  2, "cimpletoon", "u.warriors_nw"
+  1,  3, "cimpletoon", "u.warriors_n"
+  1,  4, "cimpletoon", "u.warriors_ne"
+  1,  5, "cimpletoon", "u.warriors_e"
+  1,  6, "cimpletoon", "u.warriors_se"
+  1,  7, "cimpletoon", "u.warriors_s"
+
+  2,  0, "cimpletoon", "u.explorer_sw"
+  2,  1, "cimpletoon", "u.explorer_w"
+  2,  2, "cimpletoon", "u.explorer_nw"
+  2,  3, "cimpletoon", "u.explorer_n"
+  2,  4, "cimpletoon", "u.explorer_ne"
+  2,  5, "cimpletoon", "u.explorer_e"
+  2,  6, "cimpletoon", "u.explorer_se"
+  2,  7, "cimpletoon", "u.explorer_s"
+
+  3,  0, "cimpletoon", "u.worker_sw"
+  3,  1, "cimpletoon", "u.worker_w"
+  3,  2, "cimpletoon", "u.worker_nw"
+  3,  3, "cimpletoon", "u.worker_n"
+  3,  4, "cimpletoon", "u.worker_ne"
+  3,  5, "cimpletoon", "u.worker_e"
+  3,  6, "cimpletoon", "u.worker_se"
+  3,  7, "cimpletoon", "u.worker_s"
+
+  4,  0, "cimpletoon", "u.horsemen_sw"
+  4,  1, "cimpletoon", "u.horsemen_w"
+  4,  2, "cimpletoon", "u.horsemen_nw"
+  4,  3, "cimpletoon", "u.horsemen_n"
+  4,  4, "cimpletoon", "u.horsemen_ne"
+  4,  5, "cimpletoon", "u.horsemen_e"
+  4,  6, "cimpletoon", "u.horsemen_se"
+  4,  7, "cimpletoon", "u.horsemen_s"
+
+  5,  0, "cimpletoon", "u.archers_sw"
+  5,  1, "cimpletoon", "u.archers_w"
+  5,  2, "cimpletoon", "u.archers_nw"
+  5,  3, "cimpletoon", "u.archers_n"
+  5,  4, "cimpletoon", "u.archers_ne"
+  5,  5, "cimpletoon", "u.archers_e"
+  5,  6, "cimpletoon", "u.archers_se"
+  5,  7, "cimpletoon", "u.archers_s"
+
+  6,  0, "cimpletoon", "u.phalanx_sw"
+  6,  1, "cimpletoon", "u.phalanx_w"
+  6,  2, "cimpletoon", "u.phalanx_nw"
+  6,  3, "cimpletoon", "u.phalanx_n"
+  6,  4, "cimpletoon", "u.phalanx_ne"
+  6,  5, "cimpletoon", "u.phalanx_e"
+  6,  6, "cimpletoon", "u.phalanx_se"
+  6,  7, "cimpletoon", "u.phalanx_s"
+
+  7,  0, "cimpletoon", "u.trireme_sw"
+  7,  1, "cimpletoon", "u.trireme_w"
+  7,  2, "cimpletoon", "u.trireme_nw"
+  7,  3, "cimpletoon", "u.trireme_n"
+  7,  4, "cimpletoon", "u.trireme_ne"
+  7,  5, "cimpletoon", "u.trireme_e"
+  7,  6, "cimpletoon", "u.trireme_se"
+  7,  7, "cimpletoon", "u.trireme_s"
+
+  8,  0, "cimpletoon", "u.chariot_sw"
+  8,  1, "cimpletoon", "u.chariot_w"
+  8,  2, "cimpletoon", "u.chariot_nw"
+  8,  3, "cimpletoon", "u.chariot_n"
+  8,  4, "cimpletoon", "u.chariot_ne"
+  8,  5, "cimpletoon", "u.chariot_e"
+  8,  6, "cimpletoon", "u.chariot_se"
+  8,  7, "cimpletoon", "u.chariot_s"
+
+  9,  0, "cimpletoon", "u.catapult_sw"
+  9,  1, "cimpletoon", "u.catapult_w"
+  9,  2, "cimpletoon", "u.catapult_nw"
+  9,  3, "cimpletoon", "u.catapult_n"
+  9,  4, "cimpletoon", "u.catapult_ne"
+  9,  5, "cimpletoon", "u.catapult_e"
+  9,  6, "cimpletoon", "u.catapult_se"
+  9,  7, "cimpletoon", "u.catapult_s"
+
+  10,  0, "cimpletoon", "u.legion_sw"
+  10,  1, "cimpletoon", "u.legion_w"
+  10,  2, "cimpletoon", "u.legion_nw"
+  10,  3, "cimpletoon", "u.legion_n"
+  10,  4, "cimpletoon", "u.legion_ne"
+  10,  5, "cimpletoon", "u.legion_e"
+  10,  6, "cimpletoon", "u.legion_se"
+  10,  7, "cimpletoon", "u.legion_s"
+
+  11,  0, "cimpletoon", "u.diplomat_sw"
+  11,  1, "cimpletoon", "u.diplomat_w"
+  11,  2, "cimpletoon", "u.diplomat_nw"
+  11,  3, "cimpletoon", "u.diplomat_n"
+  11,  4, "cimpletoon", "u.diplomat_ne"
+  11,  5, "cimpletoon", "u.diplomat_e"
+  11,  6, "cimpletoon", "u.diplomat_se"
+  11,  7, "cimpletoon", "u.diplomat_s"
+
+  12,  0, "cimpletoon", "u.caravan_sw"
+  12,  1, "cimpletoon", "u.caravan_w"
+  12,  2, "cimpletoon", "u.caravan_nw"
+  12,  3, "cimpletoon", "u.caravan_n"
+  12,  4, "cimpletoon", "u.caravan_ne"
+  12,  5, "cimpletoon", "u.caravan_e"
+  12,  6, "cimpletoon", "u.caravan_se"
+  12,  7, "cimpletoon", "u.caravan_s"
+
+  13,  0, "cimpletoon", "u.pikemen_sw"
+  13,  1, "cimpletoon", "u.pikemen_w"
+  13,  2, "cimpletoon", "u.pikemen_nw"
+  13,  3, "cimpletoon", "u.pikemen_n"
+  13,  4, "cimpletoon", "u.pikemen_ne"
+  13,  5, "cimpletoon", "u.pikemen_e"
+  13,  6, "cimpletoon", "u.pikemen_se"
+  13,  7, "cimpletoon", "u.pikemen_s"
+
+  14,  0, "cimpletoon", "u.knights_sw"
+  14,  1, "cimpletoon", "u.knights_w"
+  14,  2, "cimpletoon", "u.knights_nw"
+  14,  3, "cimpletoon", "u.knights_n"
+  14,  4, "cimpletoon", "u.knights_ne"
+  14,  5, "cimpletoon", "u.knights_e"
+  14,  6, "cimpletoon", "u.knights_se"
+  14,  7, "cimpletoon", "u.knights_s"
+
+  15,  0, "cimpletoon", "u.caravel_sw"
+  15,  1, "cimpletoon", "u.caravel_w"
+  15,  2, "cimpletoon", "u.caravel_nw"
+  15,  3, "cimpletoon", "u.caravel_n"
+  15,  4, "cimpletoon", "u.caravel_ne"
+  15,  5, "cimpletoon", "u.caravel_e"
+  15,  6, "cimpletoon", "u.caravel_se"
+  15,  7, "cimpletoon", "u.caravel_s"
+
+  16,  0, "cimpletoon", "u.galleon_sw"
+  16,  1, "cimpletoon", "u.galleon_w"
+  16,  2, "cimpletoon", "u.galleon_nw"
+  16,  3, "cimpletoon", "u.galleon_n"
+  16,  4, "cimpletoon", "u.galleon_ne"
+  16,  5, "cimpletoon", "u.galleon_e"
+  16,  6, "cimpletoon", "u.galleon_se"
+  16,  7, "cimpletoon", "u.galleon_s"
+
+  17,  0, "cimpletoon", "u.frigate_sw"
+  17,  1, "cimpletoon", "u.frigate_w"
+  17,  2, "cimpletoon", "u.frigate_nw"
+  17,  3, "cimpletoon", "u.frigate_n"
+  17,  4, "cimpletoon", "u.frigate_ne"
+  17,  5, "cimpletoon", "u.frigate_e"
+  17,  6, "cimpletoon", "u.frigate_se"
+  17,  7, "cimpletoon", "u.frigate_s"
+
+  18,  0, "cimpletoon", "u.ironclad_sw"
+  18,  1, "cimpletoon", "u.ironclad_w"
+  18,  2, "cimpletoon", "u.ironclad_nw"
+  18,  3, "cimpletoon", "u.ironclad_n"
+  18,  4, "cimpletoon", "u.ironclad_ne"
+  18,  5, "cimpletoon", "u.ironclad_e"
+  18,  6, "cimpletoon", "u.ironclad_se"
+  18,  7, "cimpletoon", "u.ironclad_s"
+
+  19,  0, "cimpletoon", "u.musketeers_sw"
+  19,  1, "cimpletoon", "u.musketeers_w"
+  19,  2, "cimpletoon", "u.musketeers_nw"
+  19,  3, "cimpletoon", "u.musketeers_n"
+  19,  4, "cimpletoon", "u.musketeers_ne"
+  19,  5, "cimpletoon", "u.musketeers_e"
+  19,  6, "cimpletoon", "u.musketeers_se"
+  19,  7, "cimpletoon", "u.musketeers_s"
+
+  20,  0, "cimpletoon", "u.dragoons_sw"
+  20,  1, "cimpletoon", "u.dragoons_w"
+  20,  2, "cimpletoon", "u.dragoons_nw"
+  20,  3, "cimpletoon", "u.dragoons_n"
+  20,  4, "cimpletoon", "u.dragoons_ne"
+  20,  5, "cimpletoon", "u.dragoons_e"
+  20,  6, "cimpletoon", "u.dragoons_se"
+  20,  7, "cimpletoon", "u.dragoons_s"
+
+  21,  0, "cimpletoon", "u.cannon_sw"
+  21,  1, "cimpletoon", "u.cannon_w"
+  21,  2, "cimpletoon", "u.cannon_nw"
+  21,  3, "cimpletoon", "u.cannon_n"
+  21,  4, "cimpletoon", "u.cannon_ne"
+  21,  5, "cimpletoon", "u.cannon_e"
+  21,  6, "cimpletoon", "u.cannon_se"
+  21,  7, "cimpletoon", "u.cannon_s"
+
+  22,  0, "cimpletoon", "u.engineers_sw"
+  22,  1, "cimpletoon", "u.engineers_w"
+  22,  2, "cimpletoon", "u.engineers_nw"
+  22,  3, "cimpletoon", "u.engineers_n"
+  22,  4, "cimpletoon", "u.engineers_ne"
+  22,  5, "cimpletoon", "u.engineers_e"
+  22,  6, "cimpletoon", "u.engineers_se"
+  22,  7, "cimpletoon", "u.engineers_s"
+
+  23,  0, "cimpletoon", "u.transport_sw"
+  23,  1, "cimpletoon", "u.transport_w"
+  23,  2, "cimpletoon", "u.transport_nw"
+  23,  3, "cimpletoon", "u.transport_n"
+  23,  4, "cimpletoon", "u.transport_ne"
+  23,  5, "cimpletoon", "u.transport_e"
+  23,  6, "cimpletoon", "u.transport_se"
+  23,  7, "cimpletoon", "u.transport_s"
+
+  24,  0, "cimpletoon", "u.destroyer_sw"
+  24,  1, "cimpletoon", "u.destroyer_w"
+  24,  2, "cimpletoon", "u.destroyer_nw"
+  24,  3, "cimpletoon", "u.destroyer_n"
+  24,  4, "cimpletoon", "u.destroyer_ne"
+  24,  5, "cimpletoon", "u.destroyer_e"
+  24,  6, "cimpletoon", "u.destroyer_se"
+  24,  7, "cimpletoon", "u.destroyer_s"
+
+  25,  0, "cimpletoon", "u.riflemen_sw"
+  25,  1, "cimpletoon", "u.riflemen_w"
+  25,  2, "cimpletoon", "u.riflemen_nw"
+  25,  3, "cimpletoon", "u.riflemen_n"
+  25,  4, "cimpletoon", "u.riflemen_ne"
+  25,  5, "cimpletoon", "u.riflemen_e"
+  25,  6, "cimpletoon", "u.riflemen_se"
+  25,  7, "cimpletoon", "u.riflemen_s"
+
+  26,  0, "cimpletoon", "u.cavalry_sw"
+  26,  1, "cimpletoon", "u.cavalry_w"
+  26,  2, "cimpletoon", "u.cavalry_nw"
+  26,  3, "cimpletoon", "u.cavalry_n"
+  26,  4, "cimpletoon", "u.cavalry_ne"
+  26,  5, "cimpletoon", "u.cavalry_e"
+  26,  6, "cimpletoon", "u.cavalry_se"
+  26,  7, "cimpletoon", "u.cavalry_s"
+
+  27,  0, "cimpletoon", "u.alpine_troops_sw"
+  27,  1, "cimpletoon", "u.alpine_troops_w"
+  27,  2, "cimpletoon", "u.alpine_troops_nw"
+  27,  3, "cimpletoon", "u.alpine_troops_n"
+  27,  4, "cimpletoon", "u.alpine_troops_ne"
+  27,  5, "cimpletoon", "u.alpine_troops_e"
+  27,  6, "cimpletoon", "u.alpine_troops_se"
+  27,  7, "cimpletoon", "u.alpine_troops_s"
+
+  28,  0, "cimpletoon", "u.freight_sw"
+  28,  1, "cimpletoon", "u.freight_w"
+  28,  2, "cimpletoon", "u.freight_nw"
+  28,  3, "cimpletoon", "u.freight_n"
+  28,  4, "cimpletoon", "u.freight_ne"
+  28,  5, "cimpletoon", "u.freight_e"
+  28,  6, "cimpletoon", "u.freight_se"
+  28,  7, "cimpletoon", "u.freight_s"
+
+  29,  0, "cimpletoon", "u.spy_sw"
+  29,  1, "cimpletoon", "u.spy_w"
+  29,  2, "cimpletoon", "u.spy_nw"
+  29,  3, "cimpletoon", "u.spy_n"
+  29,  4, "cimpletoon", "u.spy_ne"
+  29,  5, "cimpletoon", "u.spy_e"
+  29,  6, "cimpletoon", "u.spy_se"
+  29,  7, "cimpletoon", "u.spy_s"
+
+  30,  0, "cimpletoon", "u.cruiser_sw"
+  30,  1, "cimpletoon", "u.cruiser_w"
+  30,  2, "cimpletoon", "u.cruiser_nw"
+  30,  3, "cimpletoon", "u.cruiser_n"
+  30,  4, "cimpletoon", "u.cruiser_ne"
+  30,  5, "cimpletoon", "u.cruiser_e"
+  30,  6, "cimpletoon", "u.cruiser_se"
+  30,  7, "cimpletoon", "u.cruiser_s"
+
+  31,  0, "cimpletoon", "u.battleship_sw"
+  31,  1, "cimpletoon", "u.battleship_w"
+  31,  2, "cimpletoon", "u.battleship_nw"
+  31,  3, "cimpletoon", "u.battleship_n"
+  31,  4, "cimpletoon", "u.battleship_ne"
+  31,  5, "cimpletoon", "u.battleship_e"
+  31,  6, "cimpletoon", "u.battleship_se"
+  31,  7, "cimpletoon", "u.battleship_s"
+
+  32,  0, "cimpletoon", "u.submarine_sw"
+  32,  1, "cimpletoon", "u.submarine_w"
+  32,  2, "cimpletoon", "u.submarine_nw"
+  32,  3, "cimpletoon", "u.submarine_n"
+  32,  4, "cimpletoon", "u.submarine_ne"
+  32,  5, "cimpletoon", "u.submarine_e"
+  32,  6, "cimpletoon", "u.submarine_se"
+  32,  7, "cimpletoon", "u.submarine_s"
+
+  33,  0, "cimpletoon", "u.marines_sw"
+  33,  1, "cimpletoon", "u.marines_w"
+  33,  2, "cimpletoon", "u.marines_nw"
+  33,  3, "cimpletoon", "u.marines_n"
+  33,  4, "cimpletoon", "u.marines_ne"
+  33,  5, "cimpletoon", "u.marines_e"
+  33,  6, "cimpletoon", "u.marines_se"
+  33,  7, "cimpletoon", "u.marines_s"
+
+  34,  0, "cimpletoon", "u.partisan_sw"
+  34,  1, "cimpletoon", "u.partisan_w"
+  34,  2, "cimpletoon", "u.partisan_nw"
+  34,  3, "cimpletoon", "u.partisan_n"
+  34,  4, "cimpletoon", "u.partisan_ne"
+  34,  5, "cimpletoon", "u.partisan_e"
+  34,  6, "cimpletoon", "u.partisan_se"
+  34,  7, "cimpletoon", "u.partisan_s"
+
+  35,  0, "cimpletoon", "u.artillery_sw"
+  35,  1, "cimpletoon", "u.artillery_w"
+  35,  2, "cimpletoon", "u.artillery_nw"
+  35,  3, "cimpletoon", "u.artillery_n"
+  35,  4, "cimpletoon", "u.artillery_ne"
+  35,  5, "cimpletoon", "u.artillery_e"
+  35,  6, "cimpletoon", "u.artillery_se"
+  35,  7, "cimpletoon", "u.artillery_s"
+
+  36,  0, "cimpletoon", "u.fighter_sw"
+  36,  1, "cimpletoon", "u.fighter_w"
+  36,  2, "cimpletoon", "u.fighter_nw"
+  36,  3, "cimpletoon", "u.fighter_n"
+  36,  4, "cimpletoon", "u.fighter_ne"
+  36,  5, "cimpletoon", "u.fighter_e"
+  36,  6, "cimpletoon", "u.fighter_se"
+  36,  7, "cimpletoon", "u.fighter_s"
+
+  37,  0, "cimpletoon", "u.aegis_cruiser_sw"
+  37,  1, "cimpletoon", "u.aegis_cruiser_w"
+  37,  2, "cimpletoon", "u.aegis_cruiser_nw"
+  37,  3, "cimpletoon", "u.aegis_cruiser_n"
+  37,  4, "cimpletoon", "u.aegis_cruiser_ne"
+  37,  5, "cimpletoon", "u.aegis_cruiser_e"
+  37,  6, "cimpletoon", "u.aegis_cruiser_se"
+  37,  7, "cimpletoon", "u.aegis_cruiser_s"
+
+  38,  0, "cimpletoon", "u.carrier_sw"
+  38,  1, "cimpletoon", "u.carrier_w"
+  38,  2, "cimpletoon", "u.carrier_nw"
+  38,  3, "cimpletoon", "u.carrier_n"
+  38,  4, "cimpletoon", "u.carrier_ne"
+  38,  5, "cimpletoon", "u.carrier_e"
+  38,  6, "cimpletoon", "u.carrier_se"
+  38,  7, "cimpletoon", "u.carrier_s"
+
+  39,  0, "cimpletoon", "u.armor_sw"
+  39,  1, "cimpletoon", "u.armor_w"
+  39,  2, "cimpletoon", "u.armor_nw"
+  39,  3, "cimpletoon", "u.armor_n"
+  39,  4, "cimpletoon", "u.armor_ne"
+  39,  5, "cimpletoon", "u.armor_e"
+  39,  6, "cimpletoon", "u.armor_se"
+  39,  7, "cimpletoon", "u.armor_s"
+
+  40,  0, "cimpletoon", "u.mech_inf_sw"
+  40,  1, "cimpletoon", "u.mech_inf_w"
+  40,  2, "cimpletoon", "u.mech_inf_nw"
+  40,  3, "cimpletoon", "u.mech_inf_n"
+  40,  4, "cimpletoon", "u.mech_inf_ne"
+  40,  5, "cimpletoon", "u.mech_inf_e"
+  40,  6, "cimpletoon", "u.mech_inf_se"
+  40,  7, "cimpletoon", "u.mech_inf_s"
+
+  41,  0, "cimpletoon", "u.howitzer_sw"
+  41,  1, "cimpletoon", "u.howitzer_w"
+  41,  2, "cimpletoon", "u.howitzer_nw"
+  41,  3, "cimpletoon", "u.howitzer_n"
+  41,  4, "cimpletoon", "u.howitzer_ne"
+  41,  5, "cimpletoon", "u.howitzer_e"
+  41,  6, "cimpletoon", "u.howitzer_se"
+  41,  7, "cimpletoon", "u.howitzer_s"
+
+  42,  0, "cimpletoon", "u.paratroopers_sw"
+  42,  1, "cimpletoon", "u.paratroopers_w"
+  42,  2, "cimpletoon", "u.paratroopers_nw"
+  42,  3, "cimpletoon", "u.paratroopers_n"
+  42,  4, "cimpletoon", "u.paratroopers_ne"
+  42,  5, "cimpletoon", "u.paratroopers_e"
+  42,  6, "cimpletoon", "u.paratroopers_se"
+  42,  7, "cimpletoon", "u.paratroopers_s"
+
+  43,  0, "cimpletoon", "u.helicopter_sw"
+  43,  1, "cimpletoon", "u.helicopter_w"
+  43,  2, "cimpletoon", "u.helicopter_nw"
+  43,  3, "cimpletoon", "u.helicopter_n"
+  43,  4, "cimpletoon", "u.helicopter_ne"
+  43,  5, "cimpletoon", "u.helicopter_e"
+  43,  6, "cimpletoon", "u.helicopter_se"
+  43,  7, "cimpletoon", "u.helicopter_s"
+
+  44,  0, "cimpletoon", "u.bomber_sw"
+  44,  1, "cimpletoon", "u.bomber_w"
+  44,  2, "cimpletoon", "u.bomber_nw"
+  44,  3, "cimpletoon", "u.bomber_n"
+  44,  4, "cimpletoon", "u.bomber_ne"
+  44,  5, "cimpletoon", "u.bomber_e"
+  44,  6, "cimpletoon", "u.bomber_se"
+  44,  7, "cimpletoon", "u.bomber_s"
+
+  45,  0, "cimpletoon", "u.awacs_sw"
+  45,  1, "cimpletoon", "u.awacs_w"
+  45,  2, "cimpletoon", "u.awacs_nw"
+  45,  3, "cimpletoon", "u.awacs_n"
+  45,  4, "cimpletoon", "u.awacs_ne"
+  45,  5, "cimpletoon", "u.awacs_e"
+  45,  6, "cimpletoon", "u.awacs_se"
+  45,  7, "cimpletoon", "u.awacs_s"
+
+  46,  0, "cimpletoon", "u.nuclear_sw"
+  46,  1, "cimpletoon", "u.nuclear_w"
+  46,  2, "cimpletoon", "u.nuclear_nw"
+  46,  3, "cimpletoon", "u.nuclear_n"
+  46,  4, "cimpletoon", "u.nuclear_ne"
+  46,  5, "cimpletoon", "u.nuclear_e"
+  46,  6, "cimpletoon", "u.nuclear_se"
+  46,  7, "cimpletoon", "u.nuclear_s"
+
+  47,  0, "cimpletoon", "u.cruise_missile_sw"
+  47,  1, "cimpletoon", "u.cruise_missile_w"
+  47,  2, "cimpletoon", "u.cruise_missile_nw"
+  47,  3, "cimpletoon", "u.cruise_missile_n"
+  47,  4, "cimpletoon", "u.cruise_missile_ne"
+  47,  5, "cimpletoon", "u.cruise_missile_e"
+  47,  6, "cimpletoon", "u.cruise_missile_se"
+  47,  7, "cimpletoon", "u.cruise_missile_s"
+
+  48,  0, "cimpletoon", "u.stealth_bomber_sw"
+  48,  1, "cimpletoon", "u.stealth_bomber_w"
+  48,  2, "cimpletoon", "u.stealth_bomber_nw"
+  48,  3, "cimpletoon", "u.stealth_bomber_n"
+  48,  4, "cimpletoon", "u.stealth_bomber_ne"
+  48,  5, "cimpletoon", "u.stealth_bomber_e"
+  48,  6, "cimpletoon", "u.stealth_bomber_se"
+  48,  7, "cimpletoon", "u.stealth_bomber_s"
+
+  49,  0, "cimpletoon", "u.stealth_fighter_sw"
+  49,  1, "cimpletoon", "u.stealth_fighter_w"
+  49,  2, "cimpletoon", "u.stealth_fighter_nw"
+  49,  3, "cimpletoon", "u.stealth_fighter_n"
+  49,  4, "cimpletoon", "u.stealth_fighter_ne"
+  49,  5, "cimpletoon", "u.stealth_fighter_e"
+  49,  6, "cimpletoon", "u.stealth_fighter_se"
+  49,  7, "cimpletoon", "u.stealth_fighter_s"
+
+  50,  0, "cimpletoon", "u.leader_sw"
+  50,  1, "cimpletoon", "u.leader_w"
+  50,  2, "cimpletoon", "u.leader_nw"
+  50,  3, "cimpletoon", "u.leader_n"
+  50,  4, "cimpletoon", "u.leader_ne"
+  50,  5, "cimpletoon", "u.leader_e"
+  50,  6, "cimpletoon", "u.leader_se"
+  50,  7, "cimpletoon", "u.leader_s"
+
+  51,  0, "cimpletoon", "u.barbarian_leader_sw"
+  51,  1, "cimpletoon", "u.barbarian_leader_w"
+  51,  2, "cimpletoon", "u.barbarian_leader_nw"
+  51,  3, "cimpletoon", "u.barbarian_leader_n"
+  51,  4, "cimpletoon", "u.barbarian_leader_ne"
+  51,  5, "cimpletoon", "u.barbarian_leader_e"
+  51,  6, "cimpletoon", "u.barbarian_leader_se"
+  51,  7, "cimpletoon", "u.barbarian_leader_s"
+
+  52,  0, "cimpletoon", "u.fanatics_sw"
+  52,  1, "cimpletoon", "u.fanatics_w"
+  52,  2, "cimpletoon", "u.fanatics_nw"
+  52,  3, "cimpletoon", "u.fanatics_n"
+  52,  4, "cimpletoon", "u.fanatics_ne"
+  52,  5, "cimpletoon", "u.fanatics_e"
+  52,  6, "cimpletoon", "u.fanatics_se"
+  52,  7, "cimpletoon", "u.fanatics_s"
+
+  53,  0, "cimpletoon", "u.crusaders_sw"
+  53,  1, "cimpletoon", "u.crusaders_w"
+  53,  2, "cimpletoon", "u.crusaders_nw"
+  53,  3, "cimpletoon", "u.crusaders_n"
+  53,  4, "cimpletoon", "u.crusaders_ne"
+  53,  5, "cimpletoon", "u.crusaders_e"
+  53,  6, "cimpletoon", "u.crusaders_se"
+  53,  7, "cimpletoon", "u.crusaders_s"
+
+  54,  0, "cimpletoon", "u.elephants_sw"
+  54,  1, "cimpletoon", "u.elephants_w"
+  54,  2, "cimpletoon", "u.elephants_nw"
+  54,  3, "cimpletoon", "u.elephants_n"
+  54,  4, "cimpletoon", "u.elephants_ne"
+  54,  5, "cimpletoon", "u.elephants_e"
+  54,  6, "cimpletoon", "u.elephants_se"
+  54,  7, "cimpletoon", "u.elephants_s"
+
+  55,  0, "cimpletoon", "u.migrants_sw"
+  55,  1, "cimpletoon", "u.migrants_w"
+  55,  2, "cimpletoon", "u.migrants_nw"
+  55,  3, "cimpletoon", "u.migrants_n"
+  55,  4, "cimpletoon", "u.migrants_ne"
+  55,  5, "cimpletoon", "u.migrants_e"
+  55,  6, "cimpletoon", "u.migrants_se"
+  55,  7, "cimpletoon", "u.migrants_s"
 
 }
diff --git a/data/hexemplio.tilespec b/data/hexemplio.tilespec
index 15318b8f6f..8055a3cacd 100644
--- a/data/hexemplio.tilespec
+++ b/data/hexemplio.tilespec
@@ -155,6 +155,7 @@ files =
   "misc/treaty.spec",
   "misc/editor.spec",
   "misc/units.spec",
+  "cimpletoon/orient_units.spec",
   "amplio2/nuke.spec",
   "amplio2/explosions.spec",
   "hexemplio/activities.spec",
@@ -359,3 +360,8 @@ styles =
       "ts.horses",     "Single1"
       "ts.seals",      "Single1"
     }
+
+[option_cimpletoon]
+name = "cimpletoon"
+description = _("Use 3D Cimpletoon units")
+default = FALSE
diff --git a/data/toonhex.tilespec b/data/toonhex.tilespec
deleted file mode 100644
index 2ad1840456..0000000000
--- a/data/toonhex.tilespec
+++ /dev/null
@@ -1,364 +0,0 @@
-
-[tilespec]
-
-; Format and options of this tilespec file:
-options = "+Freeciv-tilespec-Devel-2019-Jul-03"
-
-; A simple name for the tileset specified by this file:
-name = "Toonhex"
-; Based on hexemplio, which in turn is based on
-; amplio2hexgbig by GriffonSpade
-
-priority = 3
-
-; There`s no separate versioning in tilesets part of main freeciv distribution
-;version = ""
-
-; Summary and full description of the tileset.
-summary = _("Large iso-hex tileset combining Hexemplio's terrains \
-and Cimpletoon's units with orientation.")
-;description = ""
-
-; TODO: add more overall information fields on tiles,
-; eg, authors, colors, etc.
-
-; Basic tile sizes:
-normal_tile_width  = 126
-normal_tile_height = 64
-small_tile_width   = 15
-small_tile_height  = 20
-
-; Basic tile style.
-hex_side = 16
-is_hex = TRUE
-type = "isometric"
-
-; Use old iso style
-fog_style      = "Auto"
-
-; Was darkness style "IsoRect" (single-sprite)
-darkness_style = "CardinalSingle"
-
-; offset the flags by this amount when drawing units
-unit_flag_offset_x = 45
-unit_flag_offset_y = 39
-city_flag_offset_x = 41
-city_flag_offset_y = 10
-
-; offset the city occupied sprite by this amount
-occupied_offset_x = 0
-occupied_offset_y = 0
-
-; offset the units by this amount
-unit_offset_x = 34
-unit_offset_y = 38
-
-; colors with this (HSL) hue will be replaced by the player color in city and
-; unit sprites (-1 to disable)
-replaced_hue = -1
-
-; offset of the normal activity icons
-activity_offset_x = 74
-activity_offset_y = 28
-
-; offset of the selected unit sprites
-select_offset_x = 0
-select_offset_y = 21
-; delay between two consecutive images
-;select_step_ms = 100
-
-; offset the cities by this amount
-city_offset_x = 17
-city_offset_y = 21
-
-; offset the city size number by this amount
-; This is relative to full sprite origin.
-city_size_offset_x = 0
-city_size_offset_y = 0
-
-; offset the city bar text by this amount (from the city tile origin)
-citybar_offset_y = 40
-
-; offset the tile label text by this amount
-tilelabel_offset_y = 20
-
-; offset the upkeep icons by this amount from the top of the unit itself.
-; The default is the normal tile height, which means that the upkeep icons
-; appear below the unit icon if the unit icons are equal to tile height
-; (typical in overhead tileset), or overlay lower part of the unit icon,
-; if unit icon is higher than tiles (typical in iso tilesets)
-;unit_upkeep_offset_y = 0
-
-; Like unit_upkeep_offset_y, but to be used in case there`s only small
-; space for the overall icon produced. Defaults to unit_upkeep_offset_y -
-; not having alternative layout.
-;unit_upkeep_small_offset_y = 0
-
-; For tilesets with oriented units, the directional sprite to use to
-; represent a unit type rather than a specific unit from the map
-; (e.g., in worklists, editor, and online help). Does not have to be a
-; valid direction for the tileset.
-unit_default_orientation = "s"
-
-; The map is rendered in "layers", just like any decent image editor
-; supports. The setting below allows to change the layer drawing order. The
-; first layer in the list will be drawn below the others; the second on top
-; of it, and so on. No layer can be omitted from the list, nor can new ones
-; be added.
-layer_order =
-  "Background", ; Background color (if enabled, the player color where there
-                ; are units or cities). You probably want to leave this
-                ; first.
-  "Terrain1",   ; The three terrain layers. See sections [layerN] below.
-  "Terrain2",
-  "Darkness",   ; Darkness (unseen tiles)
-  "Terrain3",
-  "Water",      ; All extras with "River" style.
-  "Roads",      ; All extras with style "RoadAllSeparate",
-                ; "RoadParityCombined" or "RoadAllCombined".
-  "Special1",   ; 1st layer for extras with style "3Layers" or "Single1".
-  "Grid1",      ; Grid layer for isometric tilesets.
-  "City1",      ; City and walls.
-  "Special2",   ; 2nd layer for extras with "3Layers" and "Single2" styles.
-  "Fog",        ; Fog of war (on tiles one knows but doesn`t see).
-  "Unit",       ; Units except the selected one(s).
-  "Special3",   ; 3rd layer for extras with "3Layers" style.
-  "BaseFlags",  ; Base flags.
-  "City2",      ; City size when the city bar is disabled.
-  "Grid2",      ; Second grid layer (overhead tilesets only).
-  "Overlays",   ; Tile output sprites.
-  "TileLabel",  ; Tile labels ("Scorched spot").
-  "CityBar",    ; The city bar with name, production, food, ...
-  "FocusUnit",  ; The focused unit(s).
-  "Goto",       ; Goto turn count and intermediate points, *not* goto lines.
-  "WorkerTask", ; The unit task indicators ("G", "S", ...).
-  "Editor",     ; Editor stuff (selected tile and start points).
-  "InfraWork"   ; Icons for the extras being placed.
-
-; Below, the graphics spec files; must be somewhere (anywhere) in
-; the data path. Order may be important for color allocation on
-; low-color systems, and if there are any duplicate tags (lattermost
-; tag is used).
-
-files =
-  "misc/overlays.spec",
-  "misc/citybar.spec",
-  "misc/small.spec",
-  "misc/governments.spec",
-  "misc/specialists.spec",
-  "misc/events.spec",
-  "misc/buildings-large.spec",
-  "misc/wonders-large.spec",
-  "misc/flags-large.spec",
-  "misc/shields-large.spec",
-  "misc/cursors.spec",
-  "misc/space.spec",
-  "misc/techs.spec",
-  "misc/treaty.spec",
-  "misc/editor.spec",
-  "cimpletoon/orient_units.spec",
-  "amplio2/nuke.spec",
-  "amplio2/explosions.spec",
-  "hexemplio/activities.spec",
-  "hexemplio/bases.spec",
-  "hexemplio/cities.spec",
-  "hexemplio/terrain.spec",
-  "hexemplio/embellishments.spec",
-  "hexemplio/forests.spec",
-  "hexemplio/hills.spec",
-  "hexemplio/mountains.spec",
-  "hexemplio/tiles.spec",
-  "hexemplio/grid.spec",
-  "hexemplio/rivers.spec",
-  "hexemplio/roads.spec",
-  "hexemplio/roads-rails.spec",
-  "hexemplio/roads-maglevs.spec",
-  "hexemplio/unitextras.spec",
-  "hexemplio/unitcost.spec",
-  "hexemplio/select.spec",
-  "hexemplio/water1.spec",
-  "hexemplio/water2.spec",
-  "hexemplio/water3.spec"
-
-
-; Include color definitions
-*include "misc/colors.tilespec"
-
-; Terrain info - see README.graphics
-
-[layer0]
-match_types = "floor", "coast", "lake"
-
-[layer1]
-match_types = "water", "land"
-
-[layer2]
-match_types = "hills", "foliage"
-
-; Water graphics referenced by terrain.ruleset
-;
-
-[tile_lake]
-tag = "lake"
-blend_layer = 2
-num_layers = 2
-layer0_match_type = "lake"
-layer0_match_with = "lake"
-layer0_sprite_type = "corner"
-layer1_match_type = "water"
-layer1_match_with = "water"
-layer1_sprite_type = "corner"
-
-[tile_coast]
-tag = "coast"
-blend_layer = 2
-num_layers = 2
-layer0_match_type = "coast"
-layer1_match_type = "water"
-layer1_match_with = "water"
-layer1_sprite_type = "corner"
-
-[tile_floor]
-tag = "floor"
-blend_layer = 2
-num_layers = 2
-layer0_match_type = "floor"
-layer0_match_with = "floor"
-layer0_sprite_type = "corner"
-layer1_match_type = "water"
-layer1_match_with = "water"
-layer1_sprite_type = "corner"
-
-[tile_inaccessible]
-tag = "inaccessible"
-blend_layer = 0
-num_layers = 2
-layer0_match_type = "floor"
-layer1_match_type = "land"
-
-; Land graphics referenced by terrain.ruleset
-;
-
-[tile_arctic]
-tag = "arctic"
-blend_layer = 0
-num_layers = 2
-layer0_match_type = "lake"
-layer1_match_type = "water"
-
-[tile_desert]
-tag = "desert"
-blend_layer = 1
-num_layers = 2
-layer0_match_type = "lake"
-layer1_match_type = "land"
-
-[tile_forest]
-tag = "forest"
-blend_layer = 1
-num_layers = 3
-layer0_match_type = "lake"
-layer1_match_type = "land"
-layer2_match_type = "foliage"
-layer2_match_with = "foliage"
-
-[tile_grassland]
-tag = "grassland"
-blend_layer = 1
-num_layers = 2
-layer0_match_type = "lake"
-layer1_match_type = "land"
-
-[tile_hills]
-tag = "hills"
-blend_layer = 1
-num_layers = 3
-layer0_match_type = "lake"
-layer1_match_type = "land"
-layer2_match_type = "hills"
-layer2_match_with = "hills"
-
-[tile_jungle]
-tag = "jungle"
-blend_layer = 1
-num_layers = 3
-layer0_match_type = "lake"
-layer1_match_type = "land"
-layer2_match_type = "foliage"
-layer2_match_with = "foliage"
-
-[tile_mountains]
-tag = "mountains"
-blend_layer = 1
-num_layers = 3
-layer0_match_type = "lake"
-layer1_match_type = "land"
-layer2_match_type = "hills"
-layer2_match_with = "hills"
-
-[tile_plains]
-tag = "plains"
-blend_layer = 1
-num_layers = 2
-layer0_match_type = "lake"
-layer1_match_type = "land"
-
-[tile_swamp]
-tag = "swamp"
-blend_layer = 1
-num_layers = 2
-layer0_match_type = "lake"
-layer1_match_type = "land"
-
-[tile_tundra]
-tag = "tundra"
-blend_layer = 1
-num_layers = 2
-layer0_match_type = "lake"
-layer1_match_type = "land"
-
-[extras]
-styles =
-    { "name",          "style"
-      "road.road",     "RoadAllSeparate"
-      "road.rail",     "RoadAllCombined"
-      "road.maglev",   "RoadAllCombined"
-      "road.pave",     "RoadAllSeparate"
-      "road.river",    "River"
-      "tx.irrigation", "River"
-      "tx.farmland",   "River"
-      "tx.mine",       "Single1"
-      "tx.oil_mine",   "Single1"
-      "tx.oil_rig",    "Single1"
-      "tx.pollution",  "Single2"
-      "tx.fallout",    "Single2"
-      "tx.village",    "Single1"
-      "base.outpost",  "3Layer"
-      "base.fortress", "3Layer"
-      "base.airstrip", "3Layer"
-      "base.airbase",  "3layer"
-      "base.buoy",     "3layer"
-      "extra.ruins",   "3layer"
-      "ts.gold",       "Single1"
-      "ts.iron",       "Single1"
-      "ts.tundra_game", "Single1"
-      "ts.furs",       "Single1"
-      "ts.coal",       "Single1"
-      "ts.fish",       "Single1"
-      "ts.fruit",      "Single1"
-      "ts.gems",       "Single1"
-      "ts.buffalo",    "Single1"
-      "ts.wheat",      "Single1"
-      "ts.oasis",      "Single1"
-      "ts.peat",       "Single1"
-      "ts.pheasant",   "Single1"
-      "ts.grassland_resources", "Single1"
-      "ts.arctic_ivory", "Single1"
-      "ts.silk",       "Single1"
-      "ts.spice",      "Single1"
-      "ts.whales",     "Single1"
-      "ts.wine",       "Single1"
-      "ts.oil",        "Single1"
-      "ts.horses",     "Single1"
-      "ts.seals",      "Single1"
-    }
diff --git a/docs/Manuals/Game/menu.rst b/docs/Manuals/Game/menu.rst
index 5cf0f430a3..3770aaa3ff 100644
--- a/docs/Manuals/Game/menu.rst
+++ b/docs/Manuals/Game/menu.rst
@@ -114,6 +114,9 @@ Add Modpacks
     Launches the modpack installer. This utility allows the Longturn community to create third-party content
     and offer it for enhanced gameplay. For more information refer to :doc:`/Manuals/modpack-installer`.
 
+Tileset Options
+    For tilesets supporting it, opens a dialog that lets you change the appearance of the map.
+
 Tileset Debugger
     Opens the :guilabel:`Tileset Debugger` dialog. This option is documented in
     :doc:`/Modding/Tilesets/debugger`
diff --git a/docs/Modding/Tilesets/compatibility.rst b/docs/Modding/Tilesets/compatibility.rst
index 583a01fee3..1e30c88377 100644
--- a/docs/Modding/Tilesets/compatibility.rst
+++ b/docs/Modding/Tilesets/compatibility.rst
@@ -1,7 +1,7 @@
 .. SPDX-License-Identifier:  GPL-3.0-or-later
 .. SPDX-FileCopyrightText: Louis Moureaux <m_louis30@yahoo.com>
 
-Tileset compatibility
+Tileset Compatibility
 *********************
 
 The tileset format evolves when new Freeciv21 releases are published. As a rule of thumb, we try
@@ -50,3 +50,6 @@ terrain-specific-extras
     bases is expected to roll out soon. To use terrain-specific sprites, use the same nomenclature as before,
     but with the terrain time included after the type of extra. 
     E.g. ``road.road_isolated`` becomes ``road.road_mountain_isolated``.
+
+options
+    Support for this option signals that :doc:`options` are available.
diff --git a/docs/Modding/Tilesets/options.rst b/docs/Modding/Tilesets/options.rst
new file mode 100644
index 0000000000..4cbe72af95
--- /dev/null
+++ b/docs/Modding/Tilesets/options.rst
@@ -0,0 +1,75 @@
+.. SPDX-License-Identifier: GPL-3.0-or-later
+.. SPDX-FileCopyrightText: Louis Moureaux <m_louis30@yahoo.com>
+
+.. role:: unit
+
+Tileset Options
+***************
+
+It is often possible to introduce slight variations in a tileset. One may
+have, for instance, multiple versions of unit sprites. In such cases, it may
+make sense to let the user choose between the two options. This is enabled
+by tileset options, which provide a list of parameters that the user can
+control under :menuselection:`Game --> Tileset Options` in the
+:doc:`main menu </Manuals/Game/menu>`.
+
+Options are always very simple yes/no questions. Tilesets can configure which
+questions are asked and, most importantly, load different sprites depending
+on the answer.
+
+:since: 3.1
+
+Adding Options
+==============
+
+Each option is added as a section in the main ``tilespec`` file of the
+tileset. The section title must start with ``option_`` as this is how the
+engine recognizes that it defines an option. Each option must be given a name
+(which will be used when selecting sprites, see below), a one-line
+description for the user, and a default value. The following example can be
+used as a starting point:
+
+.. code-block:: ini
+
+  [option_cimpletoon]
+  name = "cimpletoon"
+  description = _("Use 3D Cimpletoon units")
+  default = FALSE
+
+This defines an option called ``cimpletoon`` that will be shown to the user
+as "Use 3D Cimpletoon units" and will be disabled when the tileset is used
+for the first time.
+
+Sprite Selection
+================
+
+When specifying sprites for use in the tileset, option support is enabled by
+an additional attribute called ``option``. If the attribute is not empty, it
+specifies the name of an option used to control the loading of the sprite,
+and the sprite is only loaded when the option is enabled. A simple example
+could be as follows:
+
+.. code-block:: py
+
+  tiles = { "row", "column", "option",     "tag"
+            0,     0,        "",           "u.settlers"
+            0,     1,        "cimpletoon", "u.settlers"
+  }
+
+Let us decompose how this works. Regardless of whether the ``cimpletoon``
+option is enabled, the sprite at row 0 and column 0 is always specified for
+:unit:`Settlers` units. However, when the ``cimpletoon`` is enabled, the
+second line overrides the first and the alternative sprite in column 1 is
+used. The end result is that the user sees a different sprite depending on
+the state of the option.
+
+In the example above, the "main" and "optional" sprites are located in the
+same grid. This is not required and they can very well belong to different
+files. Remember, however, that the alternative must come *after* the main
+sprite in order to override it when the option is enabled.
+
+Tileset options are very flexible and the changes they control can range from
+small tweaks involving a couple of sprites to complete overhauls that change
+the appearance of the map completely. The main limitation is that map
+geometry cannot change, as tiles have the same dimensions regardless of which
+options are enabled.
diff --git a/docs/Modding/index.rst b/docs/Modding/index.rst
index a74d7d3395..687302f1c6 100644
--- a/docs/Modding/index.rst
+++ b/docs/Modding/index.rst
@@ -89,6 +89,7 @@ guides document specific aspects of tileset creation:
   Tilesets/tutorial.rst
   Tilesets/graphics.rst
   Tilesets/terrain.rst
+  Tilesets/options.rst
   Tilesets/debugger.rst
   Tilesets/compatibility.rst
   Tilesets/ts-breaking-changes.rst
diff --git a/translations/core/POTFILES.in b/translations/core/POTFILES.in
index ecd421a553..192ff95451 100644
--- a/translations/core/POTFILES.in
+++ b/translations/core/POTFILES.in
@@ -206,7 +206,6 @@ common/vision.cpp
 common/workertask.cpp
 common/worklist.cpp
 data/amplio2.tilespec
-data/cimpletoon.tilespec
 data/civ1/buildings.ruleset
 data/civ1/cities.ruleset
 data/civ1/effects.ruleset
@@ -348,7 +347,6 @@ data/sandbox/styles.ruleset
 data/sandbox/techs.ruleset
 data/sandbox/terrain.ruleset
 data/sandbox/units.ruleset
-data/toonhex.tilespec
 data/trident.tilespec
 server/actiontools.cpp
 server/advisors/advbuilding.cpp