From 01fa7069b33cedd4992f7b0f21b29916b949de1c Mon Sep 17 00:00:00 2001 From: Cubitect Date: Sun, 7 Jan 2024 11:54:31 +0100 Subject: [PATCH] Some changes for v4.0.0 (locations finder + biome sampling + area input change) * added biome samples filter to check biome proportions (#173, #266) * added outline display for the area of selected conditions (WIP) * added "from-visible" to conditions editor (#271) * added Locations to Search tab, to look for positions in the current seed * moved Tiggers to Search tab * changed area/position input fields to use block coordinates (#265) * changed spiral iterator to use an arbitrary, user defined step size * fixed octave options and added generated display texts (#253) --- buildguide.md | 2 +- cubiomes | 2 +- cubiomes-viewer.pro | 11 +- ...thub.cubitect.cubiomes-viewer.metainfo.xml | 4 +- rc/dark.qss | 2 +- src/aboutdialog.h | 6 +- src/conditiondialog.cpp | 355 +++++++----- src/conditiondialog.h | 14 +- src/conditiondialog.ui | 261 +++++++-- src/configdialog.cpp | 4 +- src/formconditions.cpp | 10 +- src/formconditions.h | 1 + src/formsearchcontrol.cpp | 24 +- src/formsearchcontrol.h | 1 + src/formsearchcontrol.ui | 2 +- src/gotodialog.cpp | 2 +- src/layerdialog.cpp | 221 ++++--- src/layerdialog.h | 11 +- src/mainwindow.cpp | 84 ++- src/mainwindow.h | 5 +- src/mainwindow.ui | 153 ++--- src/mapview.cpp | 51 +- src/mapview.h | 4 + src/rangeslider.cpp | 2 +- src/scripts.cpp | 44 +- src/scripts.h | 5 +- src/search.cpp | 546 +++++++++++------- src/search.h | 215 ++++--- src/searchthread.cpp | 9 +- src/tabbiomes.cpp | 17 +- src/tablocations.cpp | 437 ++++++++++++++ src/tablocations.h | 78 +++ src/tablocations.ui | 175 ++++++ src/tabstructures.cpp | 17 +- src/tabtriggers.cpp | 16 +- src/tabtriggers.ui | 2 +- src/util.cpp | 30 +- src/util.h | 6 + src/world.cpp | 26 +- src/world.h | 11 + 40 files changed, 1981 insertions(+), 885 deletions(-) create mode 100644 src/tablocations.cpp create mode 100644 src/tablocations.h create mode 100644 src/tablocations.ui diff --git a/buildguide.md b/buildguide.md index dd1d451..9a39393 100644 --- a/buildguide.md +++ b/buildguide.md @@ -1,7 +1,7 @@ # Cubiomes Viewer Build Instructions Cubiomes Viewer is a Qt5 application and requires: -* Qt5.9 or newer (Qt6 is not supported) and a +* Qt5.9 or newer (Qt6 might work, but isn't currently supported) and a * GNU C++ compiler (GCC or Clang). The cubiomes library is included as a submodule to this repository. diff --git a/cubiomes b/cubiomes index 6b36603..c3ff21a 160000 --- a/cubiomes +++ b/cubiomes @@ -1 +1 @@ -Subproject commit 6b36603d2dc31f6ef8a3d4746c9b64ad3ace0a00 +Subproject commit c3ff21a1a8b6bf362221db3f4dee71a0dc7dba9b diff --git a/cubiomes-viewer.pro b/cubiomes-viewer.pro index c7664ce..d0e284c 100644 --- a/cubiomes-viewer.pro +++ b/cubiomes-viewer.pro @@ -32,6 +32,10 @@ win32: { static_gnu: { LIBS += -static -static-libgcc -static-libstdc++ } +sanitizer: { + QMAKE_CFLAGS += -fsanitize=undefined + LIBS += -lubsan -ldl +} gcc { greaterThan(QMAKE_GCC_MAJOR_VERSION, 9): QMAKE_CXXFLAGS += -Wno-deprecated-copy @@ -39,10 +43,6 @@ gcc { CONFIG(debug, debug|release): { CUTARGET = debug - !win32 { - QMAKE_CFLAGS += -fsanitize=undefined - LIBS += -lubsan -ldl - } } else { CUTARGET = release } @@ -115,6 +115,7 @@ SOURCES += \ src/search.cpp \ src/searchthread.cpp \ src/tabbiomes.cpp \ + src/tablocations.cpp \ src/tabstructures.cpp \ src/tabtriggers.cpp \ src/mainwindow.cpp \ @@ -182,6 +183,7 @@ HEADERS += \ src/searchthread.h \ src/seedtables.h \ src/tabbiomes.h \ + src/tablocations.h \ src/tabstructures.h \ src/tabtriggers.h \ src/mainwindow.h \ @@ -206,6 +208,7 @@ FORMS += \ src/mainwindow.ui \ src/rangedialog.ui \ src/tabbiomes.ui \ + src/tablocations.ui \ src/tabstructures.ui \ src/tabtriggers.ui diff --git a/etc/com.github.cubitect.cubiomes-viewer.metainfo.xml b/etc/com.github.cubitect.cubiomes-viewer.metainfo.xml index 6fa02b5..920fc30 100644 --- a/etc/com.github.cubitect.cubiomes-viewer.metainfo.xml +++ b/etc/com.github.cubitect.cubiomes-viewer.metainfo.xml @@ -9,11 +9,11 @@ Cubiomes Viewer An efficient Minecraft seed finder and map viewer. -

Cubiomes Viewer offers highly customizable seed-finding utilities and a map viewer for the biome and structure generation of Minecraft Java Edition for the main releases up to 1.19.

+

Cubiomes Viewer offers highly customizable seed-finding utilities and a map viewer for the biome and structure generation of Minecraft Java Edition for the main releases up to 1.21.

- + diff --git a/rc/dark.qss b/rc/dark.qss index 9d754cb..268df81 100644 --- a/rc/dark.qss +++ b/rc/dark.qss @@ -2116,7 +2116,7 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qsplitter --------------------------------------------------------------------------- */ QSplitter { - background-color: #455364; + background-color: #19232D; /*#455364;*/ spacing: 0px; padding: 0px; margin: 0px; diff --git a/src/aboutdialog.h b/src/aboutdialog.h index c38e621..9df5e3e 100644 --- a/src/aboutdialog.h +++ b/src/aboutdialog.h @@ -4,9 +4,9 @@ #include #include -#define VERS_MAJOR 3 -#define VERS_MINOR 4 -#define VERS_PATCH 2 // negative patch number designates a development version +#define VERS_MAJOR 4 +#define VERS_MINOR 0 +#define VERS_PATCH -1 // negative patch number designates a development version // returns +1 if newer, -1 if older and 0 if equal inline int cmpVers(int major, int minor, int patch) diff --git a/src/conditiondialog.cpp b/src/conditiondialog.cpp index 8577e1f..ea2055e 100644 --- a/src/conditiondialog.cpp +++ b/src/conditiondialog.cpp @@ -21,29 +21,16 @@ #include -static QString getTip(int mc, int layer, uint32_t flags, int id) -{ - uint64_t mL = 0, mM = 0; - genPotential(&mL, &mM, layer, mc, flags, id); - QString tip = ConditionDialog::tr("Generates any of:"); - for (int j = 0; j < 64; j++) - if (mL & (1ULL << j)) - tip += QString("\n") + getBiomeDisplay(mc, j); - for (int j = 0; j < 64; j++) - if (mM & (1ULL << j)) - tip += QString("\n") + getBiomeDisplay(mc, 128+j); - return tip; -} - #define WARNING_CHAR QChar(0x26A0) -ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcversion, QListWidgetItem *item, Condition *initcond) +ConditionDialog::ConditionDialog(FormConditions *parent, MapView *mapview, Config *config, WorldInfo wi, QListWidgetItem *item, Condition *initcond) : QDialog(parent, Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint) , ui(new Ui::ConditionDialog) , luahash() + , mapview(mapview) , config(config) , item(item) - , mc(mcversion) + , wi(wi) { memset(&cond, 0, sizeof(cond)); ui->setupUi(this); @@ -55,7 +42,7 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv textDescription->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); ui->collapseDescription->init(tr("Description/Notes"), textDescription, true); - const char *p_mcs = mc2str(mc); + const char *p_mcs = mc2str(wi.mc); QString mcs = tr("MC %1", "Minecraft version").arg(p_mcs ? p_mcs : "?"); ui->labelMC->setText(mcs); @@ -86,20 +73,20 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv if (c.save == initcond->save) continue; if (c.save == initcond->relative) - initindex = ui->comboBoxRelative->count(); + initindex = ui->comboRelative->count(); } QString condstr = c.summary(false).simplified(); - ui->comboBoxRelative->addItem(condstr, c.save); + ui->comboRelative->addItem(condstr, c.save); } if (initindex < 0) { if (initcond && initcond->relative > 0) { - initindex = ui->comboBoxRelative->count(); + initindex = ui->comboRelative->count(); QString condstr = QString("[%1] %2 broken reference") .arg(initcond->relative, 2, 10, QChar('0')) .arg(WARNING_CHAR); - ui->comboBoxRelative->addItem(condstr, initcond->relative); + ui->comboRelative->addItem(condstr, initcond->relative); } else { @@ -117,6 +104,8 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv ui->lineSquare->setValidator(uintval); ui->lineRadius->setValidator(uintval); + ui->lineSpiralStep->setValidator(new QIntValidator(1, 0xffff, this)); + ui->lineBiomeSize->setValidator(new QIntValidator(1, INT_MAX, this)); ui->lineTolerance->setValidator(new QIntValidator(0, 255, this)); @@ -125,28 +114,31 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv ui->lineMin->setValidator(new QDoubleValidator(-1e6, 1e6, 4, this)); ui->lineMax->setValidator(new QDoubleValidator(-1e6, 1e6, 4, this)); - ui->comboBoxCat->setItemDelegate(new ComboBoxDelegate(this, ui->comboBoxCat)); - ui->comboBoxType->setItemDelegate(new ComboBoxDelegate(this, ui->comboBoxType)); + ui->lineCoverage->setValidator(new QDoubleValidator(0.0001, 100.0000, 4, this)); + ui->lineConfidence->setValidator(new QDoubleValidator(0.0001, 99.9999, 4, this)); - //qobject_cast(ui->comboBoxCat->view())->setSpacing(1); - //qobject_cast(ui->comboBoxType->view())->setSpacing(1); + ui->comboCat->setItemDelegate(new ComboBoxDelegate(this, ui->comboCat)); + ui->comboType->setItemDelegate(new ComboBoxDelegate(this, ui->comboType)); - ui->comboBoxCat->addItem(tr("Select category")); - ui->comboBoxCat->insertSeparator(1); - ui->comboBoxCat->addItem(getPix("helper"), tr("Algorithm helpers"), CAT_HELPER); - ui->comboBoxCat->addItem(getPix("quad"), tr("Quad-structure"), CAT_QUAD); - ui->comboBoxCat->addItem(getPix("stronghold"), tr("Structures"), CAT_STRUCT); - ui->comboBoxCat->addItem(getPix("overworld"), tr("Biomes"), CAT_BIOMES); - ui->comboBoxCat->addItem(getPix("nether"), tr("Nether biomes"), CAT_NETHER); - ui->comboBoxCat->addItem(getPix("the_end"), tr("End biomes"), CAT_END); - ui->comboBoxCat->addItem(getPix("slime"), tr("Other"), CAT_OTHER); - int fmh = ui->comboBoxCat->fontMetrics().height() + 8; - ui->comboBoxCat->setIconSize(QSize(fmh, fmh)); - ui->comboBoxType->setIconSize(QSize(fmh, fmh)); + //qobject_cast(ui->comboCat->view())->setSpacing(1); + //qobject_cast(ui->comboType->view())->setSpacing(1); + + ui->comboCat->addItem(tr("Select category")); + ui->comboCat->insertSeparator(1); + ui->comboCat->addItem(getPix("helper"), tr("Algorithm helpers"), CAT_HELPER); + ui->comboCat->addItem(getPix("quad"), tr("Quad-structure"), CAT_QUAD); + ui->comboCat->addItem(getPix("stronghold"), tr("Structures"), CAT_STRUCT); + ui->comboCat->addItem(getPix("overworld"), tr("Biomes"), CAT_BIOMES); + ui->comboCat->addItem(getPix("nether"), tr("Nether biomes"), CAT_NETHER); + ui->comboCat->addItem(getPix("the_end"), tr("End biomes"), CAT_END); + ui->comboCat->addItem(getPix("slime"), tr("Other"), CAT_OTHER); + int fmh = ui->comboCat->fontMetrics().height() + 8; + ui->comboCat->setIconSize(QSize(fmh, fmh)); + ui->comboType->setIconSize(QSize(fmh, fmh)); for (int i = 0; i < 256; i++) { - QString bname = getBiomeDisplay(mc, i); + QString bname = getBiomeDisplay(wi.mc, i); if (bname.isEmpty()) continue; QCheckBox *cb = new QCheckBox(bname); @@ -214,7 +206,7 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv if (i == NP_DEPTH) { LabeledRange *lr; - if (mc <= MC_1_17) + if (wi.mc <= MC_1_17) lr = new LabeledRange(this, 0, 256); else lr = new LabeledRange(this, -64, 320); @@ -253,15 +245,15 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv std::vector ids; for (int id = 0; id < 256; id++) ids.push_back(id); - IdCmp cmp(IdCmp::SORT_LEX, mc, DIM_UNDEF); + IdCmp cmp(IdCmp::SORT_LEX, wi.mc, DIM_UNDEF); std::sort(ids.begin(), ids.end(), cmp); for (int id : ids) { - const int *lim = getBiomeParaLimits(mc, id); + const int *lim = getBiomeParaLimits(wi.mc, id); if (!lim) continue; - NoiseBiomeIndicator *cb = new NoiseBiomeIndicator(getBiomeDisplay(mc, id), this); + NoiseBiomeIndicator *cb = new NoiseBiomeIndicator(getBiomeDisplay(wi.mc, id), this); QString tip = "
";
         for (int j = 0; j < NP_MAX; j++)
         {
@@ -310,6 +302,8 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv
     ui->checkSkipRef->setChecked(false);
     ui->radioSquare->setChecked(true);
     ui->checkRadius->setChecked(false);
+    ui->lineCoverage->setText("100");
+    ui->lineConfidence->setText("95");
     onCheckStartChanged(false);
     on_comboClimatePara_currentIndexChanged(0);
 
@@ -328,22 +322,25 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv
             ui->comboLua->setCurrentIndex(-1); // force index change
         ui->comboLua->setCurrentIndex(ui->comboLua->findData(QVariant::fromValue(cond.hash)));
 
-        ui->comboBoxCat->setCurrentIndex(ui->comboBoxCat->findData(ft.cat));
-        for (int i = 0; i < ui->comboBoxType->count(); i++)
+        ui->comboCat->setCurrentIndex(ui->comboCat->findData(ft.cat));
+        for (int i = 0; i < ui->comboType->count(); i++)
         {
-            int type = ui->comboBoxType->itemData(i, Qt::UserRole).toInt();
+            int type = ui->comboType->itemData(i, Qt::UserRole).toInt();
             if (type == cond.type)
             {
-                ui->comboBoxType->setCurrentIndex(i);
+                ui->comboType->setCurrentIndex(i);
                 break;
             }
         }
 
-        ui->comboBoxRelative->setCurrentIndex(initindex);
-        on_comboBoxRelative_activated(initindex);
+        ui->comboRelative->setCurrentIndex(initindex);
+        on_comboRelative_activated(initindex);
         ui->textEditLua->document()->setModified(false);
 
-        ui->comboMatchBiome->insertItem(0, getBiomeDisplay(mc, cond.biomeId), QVariant::fromValue(cond.biomeId));
+        if (cond.step)
+            ui->lineSpiralStep->setText(QString::number(cond.step));
+
+        ui->comboMatchBiome->insertItem(0, getBiomeDisplay(wi.mc, cond.biomeId), QVariant::fromValue(cond.biomeId));
         ui->comboMatchBiome->setCurrentIndex(0);
 
         ui->comboClimatePara->setCurrentIndex(ui->comboClimatePara->findData(QVariant::fromValue(cond.para)));
@@ -364,6 +361,10 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv
 
         ui->checkApprox->setChecked(cond.flags & Condition::FLG_APPROX);
         ui->checkMatchAny->setChecked(cond.flags & Condition::FLG_MATCH_ANY);
+        ui->lineCoverage->setText(QString::number(cond.converage ? cond.converage * 100 : 100));
+        ui->lineConfidence->setText(QString::number(cond.confidence ? cond.confidence * 100 : 95));
+        ui->checkSamplePos->setChecked(cond.count == 1);
+
         int i, n = ui->comboY->count();
         for (i = 0; i < n; i++)
             if (ui->comboY->itemText(i).section(' ', 0, 0).toInt() == cond.y)
@@ -475,26 +476,40 @@ void ConditionDialog::addTempCat(int temp, QString name)
     int r = temp % Special;
     ui->gridLayoutTemps->addWidget(tempsboxes[temp], r, c * 2 + 0);
     ui->gridLayoutTemps->addWidget(l, r, c * 2 + 1);
-    if (mc > MC_1_6 && mc <= MC_1_17)
-        l->setToolTip(getTip(mc, L_SPECIAL_1024, 0, r + (temp >= Special ? 256 : 0)));
+    if (wi.mc > MC_1_6 && wi.mc <= MC_1_17)
+    {
+        uint64_t mL = 0, mM = 0;
+        genPotential(&mL, &mM, L_SPECIAL_1024, wi.mc, 0, r + (temp >= Special ? 256 : 0));
+        QString tip = ConditionDialog::tr("Generates any of:");
+        for (int j = 0; j < 64; j++)
+            if (mL & (1ULL << j))
+                tip += QString("\n") + getBiomeDisplay(wi.mc, j);
+        for (int j = 0; j < 64; j++)
+            if (mM & (1ULL << j))
+                tip += QString("\n") + getBiomeDisplay(wi.mc, 128+j);
+        l->setToolTip(tip);
+    }
 }
 
 void ConditionDialog::updateMode()
 {
-    int filterindex = ui->comboBoxType->currentData().toInt();
+    int filterindex = ui->comboType->currentData().toInt();
     const FilterInfo &ft = g_filterinfo.list[filterindex];
 
     ui->lineSummary->setPlaceholderText(QApplication::translate("Filter", ft.name));
 
     QPalette pal;
-    if (mc < ft.mcmin || mc > ft.mcmax)
+    if (wi.mc < ft.mcmin || wi.mc > ft.mcmax)
         pal.setColor(QPalette::Normal, QPalette::Button, QColor(255,0,0,127));
-    ui->comboBoxType->setPalette(pal);
+    ui->comboType->setPalette(pal);
 
     ui->groupBoxGeneral->setEnabled(filterindex != F_SELECT);
     ui->groupBoxPosition->setEnabled(filterindex != F_SELECT);
 
-    ui->checkRadius->setEnabled(ft.rmax);
+    ui->checkRadius->setEnabled(ft.loc & FilterInfo::LOC_R);
+
+    bool p1 = ft.loc & FilterInfo::LOC_1;
+    bool p2 = ft.loc & FilterInfo::LOC_2;
 
     if (ui->checkRadius->isEnabled() && ui->checkRadius->isChecked())
     {
@@ -516,28 +531,44 @@ void ConditionDialog::updateMode()
     }
     else
     {
-        bool custom = ui->radioCustom->isChecked();
-
         ui->lineRadius->setEnabled(false);
 
-        ui->radioSquare->setEnabled(ft.area);
-        ui->radioCustom->setEnabled(ft.area);
+        ui->radioSquare->setEnabled(p2);
+        ui->radioCustom->setEnabled(p2);
 
-        ui->lineSquare->setEnabled(!custom && ft.area);
-
-        ui->labelX1->setEnabled(ft.coord && (custom || !ft.area));
-        ui->labelZ1->setEnabled(ft.coord && (custom || !ft.area));
-        ui->labelX2->setEnabled(custom && ft.area);
-        ui->labelZ2->setEnabled(custom && ft.area);
-        ui->lineEditX1->setEnabled(ft.coord && (custom || !ft.area));
-        ui->lineEditZ1->setEnabled(ft.coord && (custom || !ft.area));
-        ui->lineEditX2->setEnabled(custom && ft.area);
-        ui->lineEditZ2->setEnabled(custom && ft.area);
+        if (ui->radioCustom->isChecked())
+        {
+            ui->lineSquare->setEnabled(false);
+            ui->labelX1->setEnabled(p1);
+            ui->labelZ1->setEnabled(p1);
+            ui->labelX2->setEnabled(p2);
+            ui->labelZ2->setEnabled(p2);
+            ui->lineEditX1->setEnabled(p1);
+            ui->lineEditZ1->setEnabled(p1);
+            ui->lineEditX2->setEnabled(p2);
+            ui->lineEditZ2->setEnabled(p2);
+        }
+        else
+        {
+            ui->lineSquare->setEnabled(p2);
+            ui->labelX1->setEnabled(p1 && !p2);
+            ui->labelZ1->setEnabled(p1 && !p2);
+            ui->labelX2->setEnabled(false);
+            ui->labelZ2->setEnabled(false);
+            ui->lineEditX1->setEnabled(p1 && !p2);
+            ui->lineEditZ1->setEnabled(p1 && !p2);
+            ui->lineEditX2->setEnabled(false);
+            ui->lineEditZ2->setEnabled(false);
+        }
     }
 
-    ui->labelSpinBox->setEnabled(ft.count);
-    ui->spinBox->setEnabled(ft.count);
-    ui->checkSkipRef->setEnabled(ft.count);
+    ui->buttonFromVisible->setEnabled(mapview && p2 && ui->comboRelative->currentIndex() == 0);
+
+    bool cnt = ft.branch == FilterInfo::BR_CLUST;
+
+    ui->labelSpinBox->setEnabled(cnt);
+    ui->spinBox->setEnabled(cnt);
+    ui->checkSkipRef->setEnabled(cnt);
 
     ui->labelY->setEnabled(ft.hasy);
     ui->comboY->setEnabled(ft.hasy);
@@ -561,14 +592,18 @@ void ConditionDialog::updateMode()
     else if (ft.cat == CAT_BIOMES || ft.cat == CAT_NETHER || ft.cat == CAT_END)
     {
         ui->stackedWidget->setCurrentWidget(ui->pageBiomes);
-        ui->checkApprox->setEnabled(mc <= MC_1_17 || ft.step == 4);
+        ui->checkApprox->setEnabled(wi.mc <= MC_1_17 || ft.grid == 4);
         ui->checkMatchAny->setEnabled(true);
+        if (filterindex == F_BIOME_SAMPLE)
+            ui->stackedBiome->setCurrentWidget(ui->pageBiomeOptSample);
+        else
+            ui->stackedBiome->setCurrentWidget(ui->pageBiomeOpt);
     }
     else if (filterindex == F_VILLAGE)
     {
         ui->stackedWidget->setCurrentWidget(ui->pageVillage);
-        ui->checkStartPieces->setEnabled(mc >= MC_1_14);
-        ui->checkAbandoned->setEnabled(filterindex == F_VILLAGE && mc >= MC_1_10);
+        ui->checkStartPieces->setEnabled(wi.mc >= MC_1_14);
+        ui->checkAbandoned->setEnabled(filterindex == F_VILLAGE && wi.mc >= MC_1_10);
     }
     else if (filterindex == F_FORTRESS)
     {
@@ -578,22 +613,22 @@ void ConditionDialog::updateMode()
     else if (filterindex == F_BASTION)
     {
         ui->stackedWidget->setCurrentWidget(ui->pageBastion);
-        ui->checkStartBastion->setEnabled(mc >= MC_1_16_1);
+        ui->checkStartBastion->setEnabled(wi.mc >= MC_1_16_1);
     }
     else if (filterindex == F_PORTAL || filterindex == F_PORTALN)
     {
         ui->stackedWidget->setCurrentWidget(ui->pagePortal);
-        ui->checkStartPortal->setEnabled(mc >= MC_1_16_1);
+        ui->checkStartPortal->setEnabled(wi.mc >= MC_1_16_1);
     }
     else if (filterindex == F_ENDCITY)
     {
         ui->stackedWidget->setCurrentWidget(ui->pageEndCity);
-        ui->checkBasement->setEnabled(mc >= MC_1_9);
+        ui->checkBasement->setEnabled(wi.mc >= MC_1_9);
     }
     else if (filterindex == F_IGLOO)
     {
         ui->stackedWidget->setCurrentWidget(ui->pageIgloo);
-        ui->checkEndShip->setEnabled(mc >= MC_1_9);
+        ui->checkEndShip->setEnabled(wi.mc >= MC_1_9);
     }
     else if (filterindex == F_HEIGHT)
     {
@@ -603,6 +638,10 @@ void ConditionDialog::updateMode()
     {
         ui->stackedWidget->setCurrentWidget(ui->pageLua);
     }
+    else if (filterindex == F_SPIRAL)
+    {
+        ui->stackedWidget->setCurrentWidget(ui->pageSpiral);
+    }
     else
     {
         ui->stackedWidget->setCurrentWidget(ui->pageNone);
@@ -610,26 +649,14 @@ void ConditionDialog::updateMode()
 
     updateBiomeSelection();
 
-    QString loc = "";
-    QString areatip = "";
-    QString lowtip = "";
-    QString uptip = "";
+    QString loc = tr("Location");
+    QString areatip = tr("From floor(-x/2) to floor(x/2) on both axes (inclusive)");
+    QString lowtip = tr("Lower bound (inclusive)");
+    QString uptip = tr("Upper bound (inclusive)");
+
+    if (ft.grid > 1)
+        loc += " " + tr("(sampled on a grid of scale 1:%1)").arg(ft.grid);
 
-    if (ft.step > 1)
-    {
-        QString multxt = QString("%1%2").arg(QChar(0xD7)).arg(ft.step);
-        loc = tr("Location (coordinates are multiplied by %1)").arg(multxt);
-        areatip = tr("From floor(-x/2)%1 to floor(x/2)%1 on both axes (inclusive)").arg(multxt);
-        lowtip = tr("Lower bound %1 (inclusive)").arg(multxt);
-        uptip = tr("Upper bound %1 (inclusive)").arg(multxt);
-    }
-    else
-    {
-        loc = tr("Location");
-        areatip = tr("From floor(-x/2) to floor(x/2) on both axes (inclusive)");
-        lowtip = tr("Lower bound (inclusive)");
-        uptip = tr("Upper bound (inclusive)");
-    }
     ui->groupBoxPosition->setTitle(loc);
     ui->radioSquare->setToolTip(areatip);
     ui->labelX1->setToolTip(lowtip);
@@ -646,7 +673,7 @@ void ConditionDialog::updateMode()
 
 void ConditionDialog::updateBiomeSelection()
 {
-    int filterindex = ui->comboBoxType->currentData().toInt();
+    int filterindex = ui->comboType->currentData().toInt();
     const FilterInfo &ft = g_filterinfo.list[filterindex];
 
     // clear tool tips
@@ -669,14 +696,14 @@ void ConditionDialog::updateBiomeSelection()
         available.push_back(cold_ocean);
         available.push_back(frozen_ocean);
     }
-    else if (ft.cat == CAT_BIOMES && mc > MC_B1_7 && mc <= MC_1_17)
+    else if (ft.cat == CAT_BIOMES && wi.mc > MC_B1_7 && wi.mc <= MC_1_17)
     {
         int layerId = ft.layer;
         if (layerId == 0)
         {
             Generator tmp;
-            setupGenerator(&tmp, mc, 0);
-            const Layer *l = getLayerForScale(&tmp, ft.step);
+            setupGenerator(&tmp, wi.mc, 0);
+            const Layer *l = getLayerForScale(&tmp, ft.grid);
             if (l)
                 layerId = l - tmp.ls.layers;
         }
@@ -688,7 +715,7 @@ void ConditionDialog::updateBiomeSelection()
             QCheckBox *cb = it.second;
             uint64_t mL = 0, mM = 0;
             uint32_t flags = 0;
-            genPotential(&mL, &mM, layerId, mc, flags, it.first);
+            genPotential(&mL, &mM, layerId, wi.mc, flags, it.first);
 
             if (mL || mM)
             {
@@ -699,12 +726,12 @@ void ConditionDialog::updateBiomeSelection()
                     for (int j = 0; j < 64; j++)
                     {
                         if (mL & (1ULL << j))
-                            tip += QString("\n") + getBiomeDisplay(mc, j);
+                            tip += QString("\n") + getBiomeDisplay(wi.mc, j);
                     }
                     for (int j = 0; j < 64; j++)
                     {
                         if (mM & (1ULL << j))
-                            tip += QString("\n") + getBiomeDisplay(mc, j+128);
+                            tip += QString("\n") + getBiomeDisplay(wi.mc, j+128);
                     }
                     cb->setToolTip(tip);
                 }
@@ -719,12 +746,12 @@ void ConditionDialog::updateBiomeSelection()
     {
         for (const auto& it : biomecboxes)
         {
-            if (isOverworld(mc, it.first))
+            if (isOverworld(wi.mc, it.first))
                 available.push_back(it.first);
         }
     }
 
-    IdCmp cmp = {IdCmp::SORT_LEX, mc, DIM_UNDEF};
+    IdCmp cmp = {IdCmp::SORT_LEX, wi.mc, DIM_UNDEF};
     std::sort(available.begin(), available.end(), cmp);
 
     if (ui->stackedWidget->currentWidget() == ui->pageBiomes)
@@ -772,7 +799,7 @@ void ConditionDialog::updateBiomeSelection()
 
         for (int id: available)
         {
-            QString s = getBiomeDisplay(mc, id);
+            QString s = getBiomeDisplay(wi.mc, id);
             ui->comboMatchBiome->addItem(getBiomeIcon(id), s, QVariant::fromValue(id));
             allowed_matches.append(s);
         }
@@ -785,7 +812,7 @@ void ConditionDialog::updateBiomeSelection()
             }
             else
             {
-                QString s = QString("%1 %2").arg(WARNING_CHAR).arg(getBiomeDisplay(mc, curid.toInt()));
+                QString s = QString("%1 %2").arg(WARNING_CHAR).arg(getBiomeDisplay(wi.mc, curid.toInt()));
                 ui->comboMatchBiome->insertItem(0, getBiomeIcon(curid.toInt(), true), s, curid);
                 ui->comboMatchBiome->setCurrentIndex(0);
                 allowed_matches.append(s);
@@ -830,21 +857,25 @@ int ConditionDialog::warnIfBad(Condition cond)
     }
     else if (cond.type == F_BIOME_CENTER || cond.type == F_BIOME_CENTER_256)
     {
-        int w = cond.x2 - cond.x1 + 1;
-        int h = cond.z2 - cond.z1 + 1;
+        int s = ft.pow2;
+        int w = (cond.x2 >> s) - (cond.x1 >> s) + 1;
+        int h = (cond.z2 >> s) - (cond.z1 >> s) + 1;
         if ((unsigned int)(w * h) < cond.count * cond.biomeSize)
         {
             QString text = tr(
-                "The biome locator checks for %1 instances of size %2 each, "
-                "which cannot be satisfied by an area of size %3%4%5 = %6.")
-                .arg(cond.count).arg(cond.biomeSize).arg(w).arg(QChar(0xD7)).arg(h).arg(w * h);
+                "The biome locator checks for %n instance(s), each of size %1, "
+                "which cannot be satisfied by an area of size\n"
+                "%2%3%4 = %5 < %6 @ scale 1:%7.", "", cond.count)
+                    .arg(cond.biomeSize)
+                    .arg(w).arg(QChar(0xD7)).arg(h).arg(w*h)
+                    .arg(cond.count * cond.biomeSize).arg(1<= MC_1_18)
+        if (wi.mc >= MC_1_18)
         {
             uint64_t m = cond.biomeToFindM;
             uint64_t underground =
@@ -890,8 +921,8 @@ void ConditionDialog::onAccept()
 
     Condition c = cond;
     c.version = Condition::VER_CURRENT;
-    c.type = ui->comboBoxType->currentData().toInt();
-    c.relative = ui->comboBoxRelative->currentData().toInt();
+    c.type = ui->comboType->currentData().toInt();
+    c.relative = ui->comboRelative->currentData().toInt();
     c.count = ui->spinBox->value();
     c.skipref = ui->checkSkipRef->isChecked();
 
@@ -923,19 +954,21 @@ void ConditionDialog::onAccept()
         c.z2 = ui->lineEditZ2->text().toInt();
     }
 
-    if (ft.area)
+    if (ft.loc & FilterInfo::LOC_2)
     {
         if (c.x1 > c.x2) std::swap(c.x1, c.x2);
         if (c.z1 > c.z2) std::swap(c.z1, c.z2);
     }
 
-    if (ui->checkRadius->isChecked())
+    if (ui->checkRadius->isEnabled() && ui->checkRadius->isChecked())
         c.rmax = ui->lineRadius->text().toInt() + 1;
     else
         c.rmax = 0;
 
     c.y = ui->comboY->currentText().section(' ', 0, 0).toInt();
 
+    c.step = ui->lineSpiralStep->text().toUShort();
+
     if (ui->stackedWidget->currentWidget() == ui->pageBiomes)
     {
         c.biomeToFind = c.biomeToFindM = 0;
@@ -959,7 +992,9 @@ void ConditionDialog::onAccept()
                 }
             }
         }
-        c.count = 0;
+        c.count = ui->checkSamplePos->isChecked() ? 1 : 0;
+        c.converage = ui->lineCoverage->text().toFloat() / 100.0;
+        c.confidence = ui->lineConfidence->text().toFloat() / 100.0;
     }
     if (ui->stackedWidget->currentWidget() == ui->pageBiomeCenter)
     {
@@ -1029,17 +1064,18 @@ void ConditionDialog::onAccept()
     close();
 }
 
-void ConditionDialog::on_comboBoxType_activated(int)
+void ConditionDialog::on_comboType_activated(int)
 {
     updateMode();
 }
 
-void ConditionDialog::on_comboBoxRelative_activated(int)
+void ConditionDialog::on_comboRelative_activated(int)
 {
     QPalette pal;
-    if (ui->comboBoxRelative->currentText().contains(WARNING_CHAR))
+    if (ui->comboRelative->currentText().contains(WARNING_CHAR))
         pal.setColor(QPalette::Normal, QPalette::Button, QColor(255,0,0,127));
-    ui->comboBoxRelative->setPalette(pal);
+    ui->comboRelative->setPalette(pal);
+    updateMode();
 }
 
 void ConditionDialog::on_buttonUncheck_clicked()
@@ -1073,21 +1109,34 @@ void ConditionDialog::on_buttonAreaInfo_clicked()
         "

" "Alternatively, the area can be defined as a centered square " "with a certain side length. In this case the area has the bounds: " - "[-X/2, -X/2] on both axes, rounding down and bounds included. For " + "[-X/2, +X/2] on both axes, rounding down and bounds included. For " "example a centered square with side 3 will go from -2 to 1 for both " "the X and Z axes." "

" "Important to note is that some filters have a scaling associated with " - "them. This means that the area is not defined in blocks, but on a grid " - "with the given spacing (such as chunks instead of blocks). A scaling " - "of 1:16, for example, means that the aforementioned centered square of " - "side 3 will range from -32 to 31 in block coordinates. (Chunk 1 has " - "blocks 16 to 31.)" + "them. This means the condition only checks on a grid with that spacing. " + "An area with a range from -21 to 21 at scale 1:16 may effectively be " + "expanded to -32 to 31, and get sampled at -32, -16, 0 and 16." "

" )); mb.exec(); } +void ConditionDialog::on_buttonFromVisible_clicked() +{ + if (!mapview) + return; + int x1, z1, x2, z2; + mapview->getVisible(&x1, &z1, &x2, &z2); + ui->lineEditX1->setText(QString::number(x1)); + ui->lineEditZ1->setText(QString::number(z1)); + ui->lineEditX2->setText(QString::number(x2)); + ui->lineEditZ2->setText(QString::number(z2)); + ui->checkRadius->setChecked(false); + ui->radioCustom->setChecked(true); + updateMode(); +} + void ConditionDialog::on_checkRadius_toggled(bool) { updateMode(); @@ -1121,15 +1170,15 @@ void ConditionDialog::on_ConditionDialog_finished(int result) item = 0; } -void ConditionDialog::on_comboBoxCat_currentIndexChanged(int) +void ConditionDialog::on_comboCat_currentIndexChanged(int) { - int cat = ui->comboBoxCat->currentData().toInt(); - ui->comboBoxType->setEnabled(cat != CAT_NONE); - ui->comboBoxType->clear(); + int cat = ui->comboCat->currentData().toInt(); + ui->comboType->setEnabled(cat != CAT_NONE); + ui->comboType->clear(); int slot = 0; - ui->comboBoxType->insertItem(slot, tr("Select type"), QVariant::fromValue((int)F_SELECT)); - ui->comboBoxType->insertSeparator(++slot); + ui->comboType->insertItem(slot, tr("Select type"), QVariant::fromValue((int)F_SELECT)); + ui->comboType->insertSeparator(++slot); const FilterInfo *ft_list[FILTER_MAX] = {}; const FilterInfo *ft; @@ -1150,16 +1199,16 @@ void ConditionDialog::on_comboBoxCat_currentIndexChanged(int) QVariant vidx = QVariant::fromValue((int)(ft - g_filterinfo.list)); QString txt = QApplication::translate("Filter", ft->name); if (ft->icon) - ui->comboBoxType->insertItem(slot, getPix(ft->icon), txt, vidx); + ui->comboType->insertItem(slot, getPix(ft->icon), txt, vidx); else - ui->comboBoxType->insertItem(slot, txt, vidx); + ui->comboType->insertItem(slot, txt, vidx); - if (mc < ft->mcmin || mc > ft->mcmax) - ui->comboBoxType->setItemData(slot, false, Qt::UserRole-1); // deactivate + if (wi.mc < ft->mcmin || wi.mc > ft->mcmax) + ui->comboType->setItemData(slot, false, Qt::UserRole-1); // deactivate if (ft == g_filterinfo.list + F_FORTRESS) - ui->comboBoxType->insertSeparator(slot++); + ui->comboType->insertSeparator(slot++); if (ft == g_filterinfo.list + F_ENDCITY) - ui->comboBoxType->insertSeparator(slot++); + ui->comboType->insertSeparator(slot++); } updateMode(); @@ -1253,8 +1302,8 @@ void ConditionDialog::onClimateLimitChanged() getClimateLimits(limok, limex); - getPossibleBiomesForLimits(ok, mc, limok); - getPossibleBiomesForLimits(ex, mc, limex); + getPossibleBiomesForLimits(ok, wi.mc, limok); + getPossibleBiomesForLimits(ex, wi.mc, limex); for (auto& it : noisebiomes) { @@ -1270,7 +1319,7 @@ void ConditionDialog::onClimateLimitChanged() void ConditionDialog::on_lineBiomeSize_textChanged(const QString &) { - int filterindex = ui->comboBoxType->currentData().toInt(); + int filterindex = ui->comboType->currentData().toInt(); double area = ui->lineBiomeSize->text().toInt(); QString s; if (filterindex == F_BIOME_CENTER_256) @@ -1368,7 +1417,7 @@ void ConditionDialog::on_pushLuaSaveAs_clicked() stream.flush(); file.close(); ui->textEditLua->document()->setModified(false); - uint64_t hash = getScriptHash(fnam); + uint64_t hash = getScriptHash(QFileInfo(fnam)); ui->comboLua->addItem(QFileInfo(fnam).baseName(), QVariant::fromValue(hash)); ui->comboLua->setCurrentIndex(ui->comboLua->count() - 1); } @@ -1495,17 +1544,21 @@ void ConditionDialog::on_comboClimatePara_currentIndexChanged(int) { ui->comboOctaves->clear(); int loptidx = LOPT_NOISE_PARA + ui->comboClimatePara->currentData().toInt(); - QStringList items; for (int i = 0; ; i++) { - if (const char *s = getLayerOptionText(loptidx, i)) - items.append(s); - else + LayerOptInfo info; + if (!getLayerOptionInfo(&info, loptidx, i, wi)) break; + ui->comboOctaves->addItem(info.summary); + ui->comboOctaves->setItemData(ui->comboOctaves->count()-1, info.tooltip, Qt::ToolTipRole); } - ui->comboOctaves->addItems(items); + on_comboOctaves_currentIndexChanged(0); } +void ConditionDialog::on_comboOctaves_currentIndexChanged(int) +{ + ui->comboOctaves->setToolTip(ui->comboOctaves->currentData(Qt::ToolTipRole).toString()); +} void ConditionDialog::on_comboY_currentTextChanged(const QString &text) { diff --git a/src/conditiondialog.h b/src/conditiondialog.h index 71e80a2..87ea0e2 100644 --- a/src/conditiondialog.h +++ b/src/conditiondialog.h @@ -19,6 +19,7 @@ #include class MainWindow; +class MapView; namespace Ui { class ConditionDialog; @@ -180,7 +181,7 @@ class ConditionDialog : public QDialog public: - explicit ConditionDialog(FormConditions *parent, Config *config, int mc, QListWidgetItem *item = 0, Condition *initcond = 0); + explicit ConditionDialog(FormConditions *parent, MapView *mapview, Config *config, WorldInfo wi, QListWidgetItem *item = 0, Condition *initcond = 0); virtual ~ConditionDialog(); void addTempCat(int temp, QString name); @@ -199,15 +200,16 @@ signals: void setCond(QListWidgetItem *item, Condition cond, int modified); private slots: - void on_comboBoxType_activated(int); + void on_comboType_activated(int); - void on_comboBoxRelative_activated(int); + void on_comboRelative_activated(int); void on_buttonUncheck_clicked(); void on_buttonInclude_clicked(); void on_buttonExclude_clicked(); void on_buttonAreaInfo_clicked(); + void on_buttonFromVisible_clicked(); void on_checkRadius_toggled(bool checked); void on_radioSquare_toggled(bool checked); @@ -218,7 +220,7 @@ private slots: void on_ConditionDialog_finished(int result); - void on_comboBoxCat_currentIndexChanged(int); + void on_comboCat_currentIndexChanged(int); void onCheckStartChanged(int state); void onClimateLimitChanged(); @@ -236,6 +238,7 @@ private slots: void on_pushInfoLua_clicked(); void on_comboClimatePara_currentIndexChanged(int index); + void on_comboOctaves_currentIndexChanged(int index); void on_comboY_currentTextChanged(const QString &text); void on_comboY2_currentTextChanged(const QString &text); @@ -255,10 +258,11 @@ private: uint64_t luahash; public: + MapView *mapview; Config *config; QListWidgetItem *item; Condition cond; - int mc; + WorldInfo wi; }; #endif // CONDITIONDIALOG_H diff --git a/src/conditiondialog.ui b/src/conditiondialog.ui index 62a786e..55b7ec6 100644 --- a/src/conditiondialog.ui +++ b/src/conditiondialog.ui @@ -13,7 +13,7 @@ - + 2 @@ -26,7 +26,7 @@ - + false @@ -256,6 +256,13 @@ + + + + From visible + + + @@ -344,7 +351,7 @@ QPushButton:hover { - + QComboBox::AdjustToContentsOnFirstShow @@ -388,7 +395,7 @@ QPushButton:hover { 0 - 5 + 1 @@ -460,47 +467,7 @@ QPushButton:hover { Biomes - - - - Enables optimizations that trade some accuracy for speed - - - Approximate - - - - - - - <html><head/><body><p style="white-space:pre">Satisfied if <span style=" font-weight:600;">any</span> of the checked biomes are present</p></body></html> - - - Match any - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - Sample at height (Y): - - - - + true @@ -565,7 +532,24 @@ QPushButton:hover { - + + + + true + + + + + QLayout::SetMinAndMaxSize + + + + + + + + + @@ -590,21 +574,128 @@ QPushButton:hover { - - - - true + + + + Qt::Horizontal - - - - QLayout::SetMinAndMaxSize + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + 0 + + + + + 0 + 0 + + + + + 0 - - + + 0 + + + 0 + + + 0 + + + + + <html><head/><body><p style="white-space:pre">Satisfied if <span style=" font-weight:600;">any</span> of the checked biomes are present</p></body></html> + + + Match any + + + + + + + Enables optimizations that trade some accuracy for speed + + + Approximate + + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Coverage (%): + + + + + + + Confidence (%): + + + + + + + Yield positions + + + + + + + + + + + + + + + + + Sample at height (Y): + @@ -1325,6 +1416,58 @@ QPushButton:hover { + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Spiral iterator + + + + + + Step size: + + + + + + + 512 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + @@ -1669,14 +1812,14 @@ QPushButton:hover { - comboBoxCat - comboBoxType + comboCat + comboType spinBox lineEditX1 lineEditZ1 lineEditX2 lineEditZ2 - comboBoxRelative + comboRelative stackedWidget diff --git a/src/configdialog.cpp b/src/configdialog.cpp index d8080da..6c6f787 100644 --- a/src/configdialog.cpp +++ b/src/configdialog.cpp @@ -52,7 +52,9 @@ ConfigDialog::ConfigDialog(QWidget *parent, Config *config) int hsc = ui->scrollAreaWidgetContents->sizeHint().height(); int hpa = parent->size().height(); int h = size.height(); - h += hsc - hsa + layout()->margin(); + int m1, m2; + layout()->getContentsMargins(0, &m1, 0, &m2); + h += hsc - hsa + m1 + m2; if (h > hpa) h = hpa; size.setHeight(h); resize(size); diff --git a/src/formconditions.cpp b/src/formconditions.cpp index 0e1aa8b..7eaca9f 100644 --- a/src/formconditions.cpp +++ b/src/formconditions.cpp @@ -75,7 +75,7 @@ FormConditions::FormConditions(QWidget *parent) } qRegisterMetaType< Condition >("Condition"); - qRegisterMetaTypeStreamOperators< Condition >("Condition"); +// qRegisterMetaTypeStreamOperators< Condition >("Condition"); } FormConditions::~FormConditions() @@ -117,10 +117,12 @@ void FormConditions::updateSensitivity() ui->buttonEdit->setEnabled(false); } + QVector selcond; int disabled = 0; for (int i = 0; i < selected.size(); i++) { Condition c = qvariant_cast(selected[i]->data(Qt::UserRole)); + selcond.append(c); if (c.meta & Condition::DISABLED) disabled++; } @@ -131,6 +133,8 @@ void FormConditions::updateSensitivity() else ui->buttonDisable->setText(tr("Toggle")); ui->buttonDisable->setEnabled(!selected.empty()); + + emit selectionUpdate(selcond); } @@ -207,7 +211,7 @@ void FormConditions::editCondition(QListWidgetItem *item) return; WorldInfo wi; parent->getSeed(&wi); - ConditionDialog *dialog = new ConditionDialog(this, &parent->config, wi.mc, item, (Condition*)item->data(Qt::UserRole).data()); + ConditionDialog *dialog = new ConditionDialog(this, parent->getMapView(), &parent->config, wi, item, (Condition*)item->data(Qt::UserRole).data()); QObject::connect(dialog, SIGNAL(setCond(QListWidgetItem*,Condition,int)), this, SLOT(addItemCondition(QListWidgetItem*,Condition,int)), Qt::QueuedConnection); dialog->show(); } @@ -266,7 +270,7 @@ void FormConditions::on_buttonAddFilter_clicked() return; WorldInfo wi; parent->getSeed(&wi); - ConditionDialog *dialog = new ConditionDialog(this, &parent->config, wi.mc); + ConditionDialog *dialog = new ConditionDialog(this, parent->getMapView(), &parent->config, wi); QObject::connect(dialog, SIGNAL(setCond(QListWidgetItem*,Condition,int)), this, SLOT(addItemCondition(QListWidgetItem*,Condition)), Qt::QueuedConnection); dialog->show(); } diff --git a/src/formconditions.h b/src/formconditions.h index d0d3d9c..b22e1ab 100644 --- a/src/formconditions.h +++ b/src/formconditions.h @@ -44,6 +44,7 @@ public: signals: void changed(); + void selectionUpdate(const QVector& selected); public slots: void on_buttonRemoveAll_clicked(); diff --git a/src/formsearchcontrol.cpp b/src/formsearchcontrol.cpp index 44db4c7..6850aa9 100644 --- a/src/formsearchcontrol.cpp +++ b/src/formsearchcontrol.cpp @@ -487,21 +487,21 @@ void FormSearchControl::on_buttonSearchHelp_clicked() mb.setText(tr( "

" "The incremental search checks seeds in numerical order, " - "save for grouping into work items for parallelization. This type " - "of search is best suited for a non-exhaustive search space and " - "with strong biome dependencies. You can restrict this type of " - "search to a value range using the "..." button." + "except for grouping seeds into work items for parallelization. " + "This is the recommended option for general searches. You can " + "restrict this type of search to a value range using the " + ""..." button." "

" - "When searching 48-bit only, the search is limited to the seed " - "bases and does not yield matching seeds, but rather checks only the " - "parts of the conditions can be determined from the lower 48-bits. " - "Sessions saved from this search are suitable to be used later with " - "the 48-bit generator to look for matching seeds." + "When using 48-bit only, the search checks partial seeds and " + "will not test the full conditions. Instead it yields seed bases " + "that may satify the conditions without knowing the upper 16-bit of " + "the seed. A session file saved from this search is suitable to be used " + "later with the 48-bit generator to look for matching seeds." "

" "With 48-bit family blocks the search looks for suitable " "48-bit seeds first and parallelizes the search through the upper " - "16-bits. This search type is best suited for exhaustive searches and " - "those with very restrictive structure requirements." + "16-bits. This search type can be a better match for exhaustive searches " + "and those with very restrictive structure requirements." "

" "Load a seed list from a file to search through an " "existing set of seeds. The seeds should be in decimal ASCII text, " @@ -585,7 +585,7 @@ void FormSearchControl::searchResult(uint64_t seed) qbuf.push_back(seed); if (ui->checkStop->isChecked()) { - onBufferTimeout(); + stopSearch(); return; } diff --git a/src/formsearchcontrol.h b/src/formsearchcontrol.h index 085c57b..8bc2f83 100644 --- a/src/formsearchcontrol.h +++ b/src/formsearchcontrol.h @@ -8,6 +8,7 @@ #include #include #include +#include #include diff --git a/src/formsearchcontrol.ui b/src/formsearchcontrol.ui index d971f9c..72cec37 100644 --- a/src/formsearchcontrol.ui +++ b/src/formsearchcontrol.ui @@ -167,7 +167,7 @@ QPushButton:hover { - Queued progress within search set + Queued progress within the search set Qt::LeftToRight diff --git a/src/gotodialog.cpp b/src/gotodialog.cpp index a7d42c6..46d5d43 100644 --- a/src/gotodialog.cpp +++ b/src/gotodialog.cpp @@ -74,7 +74,7 @@ void GotoDialog::keyPressEvent(QKeyEvent *event) { QClipboard *clipboard = QGuiApplication::clipboard(); QString s = clipboard->text().trimmed(); - QStringList xz = s.split(QRegExp("[, ]+")); + QStringList xz = s.split(QRegularExpression("[, ]+")); if (xz.count() == 2) { ui->lineX->setText(xz[0]); diff --git a/src/layerdialog.cpp b/src/layerdialog.cpp index 6a9c0ff..aa23d24 100644 --- a/src/layerdialog.cpp +++ b/src/layerdialog.cpp @@ -5,125 +5,96 @@ #include -const char *getLayerOptionText(int mode, int disp) +bool getLayerOptionInfo(LayerOptInfo *info, int mode, int disp, WorldInfo wi) { - /* - Para 0: - 4096 0.952381 - 1024 0.158730 - Para 1: - 1024 0.564374 - 512 0.282187 - Para 2: - 2048 0.751468 - 1024 0.375734 - 512 0.375734 - 256 0.187867 - 128 0.093933 - 64 0.023483 - 32 0.011742 - 16 0.005871 - 8 0.002935 - Para 3: - 2048 0.716846 - 1024 0.358423 - 256 0.089606 - 128 0.044803 - Para 4: - 32 0.666667 - 16 0.333333 - 8 0.166667 - Para 5: - 512 0.634921 - 256 0.634921 - 128 0.158730 - */ + QString txt; + QString tip; + int nptype = -1; + switch (mode) { case LOPT_BIOMES: - switch (disp) { - case 0: return "1:1"; - case 1: return "1:4"; - case 2: return "1:16"; - case 3: return "1:64"; - case 4: return "1:256"; - default: return nullptr; - } - case LOPT_NOISE_T_4: - switch (disp) { - case 0: return "All"; - case 1: return "+A[0] 1:4096 x0.952381"; - case 2: return "+B[0] 1:4023 x0.952381"; - case 3: return "+A[1] 1:1024 x0.158730"; - case 4: return "+B[1] 1:1005 x0.158730"; - default: return nullptr; - } - case LOPT_NOISE_H_4: - switch (disp) { - case 0: return "All"; - case 1: return "+A[0] 1:1024 x0.564374"; - case 2: return "+B[0] 1:1005 x0.564374"; - case 3: return "+A[1] 1:512 x0.282187"; - case 4: return "+B[1] 1:502 x0.282187"; - default: return nullptr; - } - case LOPT_NOISE_C_4: - switch (disp) { - case 0: return "All"; - case 1: return "+A[0] 1:2048 x0.751468"; - case 2: return "+B[0] 1:2011 x0.751468"; - case 3: return "+A[1] 1:1024 x0.375734"; - case 4: return "+B[1] 1:1005 x0.375734"; - case 5: return "+A[2] 1:512 x0.375734"; - case 6: return "+B[2] 1:502 x0.375734"; - case 7: return "+A[3] 1:256 x0.187867"; - case 8: return "+B[3] 1:251 x0.187867"; - case 9: return "+A[4] 1:128 x0.093933"; - case 10: return "+B[4] 1:125 x0.093933"; - case 11: return "+A[5] 1:64 x0.023483"; - case 12: return "+B[5] 1:62 x0.023483"; - case 13: return "+A[6] 1:32 x0.011742"; - case 14: return "+B[6] 1:31 x0.011742"; - default: return nullptr; - } - case LOPT_NOISE_E_4: - switch (disp) { - case 0: return "All"; - case 1: return "+A[0] 1:2048 x0.716846"; - case 2: return "+B[0] 1:2011 x0.716846"; - case 3: return "+A[1] 1:1024 x0.358423"; - case 4: return "+B[1] 1:1005 x0.358423"; - case 5: return "+A[2] 1:256 x0.089606"; - case 6: return "+B[2] 1:251 x0.089606"; - case 7: return "+A[3] 1:128 x0.044803"; - case 8: return "+B[3] 1:125 x0.044803"; - default: return nullptr; - } - case LOPT_NOISE_W_4: - switch (disp) { - case 0: return "All"; - case 1: return "+A[0] 1:512 x0.634921"; - case 2: return "+B[0] 1:502 x0.634921"; - case 3: return "+A[1] 1:256 x0.634921"; - case 4: return "+B[1] 1:251 x0.634921"; - case 5: return "+A[2] 1:128 x0.158730"; - case 6: return "+B[2] 1:125 x0.158730"; - default: return nullptr; - } + if (disp == 0) txt = "1:1"; + if (disp == 1) txt = "1:4"; + if (disp == 2) txt = "1:16"; + if (disp == 3) txt = "1:64"; + if (disp == 4) txt = "1:256"; + break; case LOPT_HEIGHT_4: - switch (disp) { - case 0: return QT_TRANSLATE_NOOP("LayerDialog", "Grayscale"); - case 1: return QT_TRANSLATE_NOOP("LayerDialog", "Shaded biome map"); - case 2: return QT_TRANSLATE_NOOP("LayerDialog", "Contours on biomes"); - case 3: return QT_TRANSLATE_NOOP("LayerDialog", "Shaded with contours"); - default: return nullptr; - } - default: - return nullptr; + if (disp == 0) txt = QApplication::translate("LayerDialog", "Grayscale"); + if (disp == 1) txt = QApplication::translate("LayerDialog", "Shaded biome map"); + if (disp == 2) txt = QApplication::translate("LayerDialog", "Contours on biomes"); + if (disp == 3) txt = QApplication::translate("LayerDialog", "Shaded with contours"); + break; + case LOPT_NOISE_T_4: nptype = NP_TEMPERATURE; break; + case LOPT_NOISE_H_4: nptype = NP_HUMIDITY; break; + case LOPT_NOISE_C_4: nptype = NP_CONTINENTALNESS; break; + case LOPT_NOISE_E_4: nptype = NP_EROSION; break; + case LOPT_NOISE_W_4: nptype = NP_WEIRDNESS; break; } + + if (nptype != -1 && disp >= 0) + { + BiomeNoise bn; + initBiomeNoise(&bn, wi.mc); + setBiomeSeed(&bn, wi.seed, wi.large); + + DoublePerlinNoise *dpn = bn.climate + nptype; + PerlinNoise *oct[2] = { dpn->octA.octaves, dpn->octB.octaves }; + int noct = dpn->octA.octcnt; + int idx = noct-1; + int ab = 1; + if (disp > 0) + { + idx = (disp - 1) / 2; + ab = (disp - 1) % 2; + if (idx >= noct) + return false; + } + + double ampsum = 0, amptot = 0; + for (int i = 0; i < noct; i++) + { + for (int j = 0; j <= 1; j++) + { + double a = oct[j][i].amplitude; + amptot += a; + if (i < idx || (idx == i && j <= ab)) + ampsum += a; + } + } + amptot *= dpn->amplitude; + ampsum *= dpn->amplitude; + + double f = 337.0 / 331.0; + PerlinNoise *pn = &oct[ab][idx]; + double a = pn->amplitude * dpn->amplitude; + double l = pn->lacunarity * (ab == 0 ? 1.0 : f); + + if (disp == 0) + { + txt += QString("%1.. x%2").arg(QChar(0x03A3)).arg(amptot, 0, 'f', 6); + tip += QApplication::translate("LayerDialog", "All octaves"); + } + else + { + txt += QString("%1..").arg(QChar(0x03A3)); + txt += QString::asprintf("%d%c 1:%-5.0f x%.6f", idx, ab?'B':'A', 1.0/l, a); + tip += QApplication::translate("LayerDialog", "Contribution of the %n most significant octaves out of %1 total.", "", disp).arg(2*noct); + } + tip += "\n" + QApplication::translate("LayerDialog", "Total contribution: %1 = %2%").arg(ampsum).arg(100 * ampsum / amptot, 0, 'f', 1); + tip += "\n" + QApplication::translate("LayerDialog", "Octave amplitude: %1").arg(a); + tip += "\n" + QApplication::translate("LayerDialog", "Octave lacunarity: %1 = 1/%2").arg(l).arg(1.0/l); + } + + if (info) { + info->summary = txt; + info->tooltip = tip; + } + return !txt.isEmpty(); } -LayerDialog::LayerDialog(QWidget *parent, int mc) +LayerDialog::LayerDialog(QWidget *parent, WorldInfo wi) : QDialog(parent) , ui(new Ui::LayerDialog) , radio{} @@ -157,18 +128,21 @@ LayerDialog::LayerDialog(QWidget *parent, int mc) { if (!combo[i]) continue; - QStringList items; for (int j = 0; ; j++) { - const char *item = getLayerOptionText(i, j); - if (!item) + LayerOptInfo info; + if (!getLayerOptionInfo(&info, i, j, wi)) break; - QString s = QApplication::translate("LayerDialog", item).leftJustified(24); + QString s = info.summary.leftJustified(24); if (j < 9) s += "\tALT+"+QString::number(j+1); - items.append(s); + combo[i]->addItem(s); + combo[i]->setItemData(combo[i]->count()-1, info.tooltip, Qt::ToolTipRole); } - combo[i]->addItems(items); + connect(combo[i], QOverload::of(&QComboBox::currentIndexChanged), [=](int){ + this->onComboChange(combo[i]); + }); + onComboChange(combo[i]); } QFont fmono; @@ -182,17 +156,17 @@ LayerDialog::LayerDialog(QWidget *parent, int mc) combo[i]->setFont(fmono); if (i >= LOPT_NOISE_T_4 && i <= LOPT_NOISE_W_4) { - radio[i]->setEnabled(mc > MC_1_17); + radio[i]->setEnabled(wi.mc > MC_1_17); if (combo[i]) - combo[i]->setEnabled(mc > MC_1_17); + combo[i]->setEnabled(wi.mc > MC_1_17); } if (i == LOPT_RIVER_4 || i == LOPT_OCEAN_256) { - radio[i]->setEnabled(mc > MC_1_12 && mc <= MC_1_17); + radio[i]->setEnabled(wi.mc > MC_1_12 && wi.mc <= MC_1_17); } if (i == LOPT_NOOCEAN_1 || i == LOPT_BETA_T_1 || i == LOPT_BETA_H_1) { - radio[i]->setEnabled(mc <= MC_B1_7); + radio[i]->setEnabled(wi.mc <= MC_B1_7); } } } @@ -235,6 +209,11 @@ void LayerDialog::onRadioChange() } } +void LayerDialog::onComboChange(QComboBox *combo) +{ + combo->setToolTip(combo->currentData(Qt::ToolTipRole).toString()); +} + void LayerDialog::on_buttonBox_clicked(QAbstractButton *button) { QDialogButtonBox::StandardButton b = ui->buttonBox->standardButton(button); diff --git a/src/layerdialog.h b/src/layerdialog.h index 01f985f..8979c93 100644 --- a/src/layerdialog.h +++ b/src/layerdialog.h @@ -11,14 +11,20 @@ namespace Ui { class LayerDialog; } -const char *getLayerOptionText(int mode, int disp); +struct LayerOptInfo +{ + QString summary; + QString tooltip; +}; + +bool getLayerOptionInfo(LayerOptInfo *info, int mode, int disp, WorldInfo wi); class LayerDialog : public QDialog { Q_OBJECT public: - explicit LayerDialog(QWidget *parent, int mc); + explicit LayerDialog(QWidget *parent, WorldInfo mc); ~LayerDialog(); void setLayerOptions(LayerOpt lopts); @@ -29,6 +35,7 @@ signals: public slots: void onRadioChange(); + void onComboChange(QComboBox *combo); private slots: void on_buttonBox_clicked(QAbstractButton *button); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index bf79f07..f469a54 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -10,6 +10,7 @@ #include "exportdialog.h" #include "layerdialog.h" #include "tabtriggers.h" +#include "tablocations.h" #include "tabbiomes.h" #include "tabstructures.h" #include "message.h" @@ -52,7 +53,8 @@ MainWindow::MainWindow(QString sessionpath, QString resultspath, QWidget *parent , sessionpath(sessionpath) , prevdir(".") , autosaveTimer() - , prevtab(-1) + , tabidx(-1) + , tabsearch(-1) , dimactions{} , dimgroup() { @@ -86,7 +88,8 @@ MainWindow::MainWindow(QString sessionpath, QString resultspath, QWidget *parent ui->menuHistory->clear(); - ui->tabContainer->addTab(new TabTriggers(this), tr("Triggers")); + ui->tabContainerSearch->addTab(new TabTriggers(this), tr("Triggers")); + ui->tabContainerSearch->addTab(new TabLocations(this), tr("Locations")); ui->tabContainer->addTab(new TabBiomes(this), tr("Biomes")); ui->tabContainer->addTab(new TabStructures(this), tr("Structures")); @@ -185,7 +188,8 @@ MainWindow::MainWindow(QString sessionpath, QString resultspath, QWidget *parent saction[D_GRID]->setChecked(true); ui->splitterMap->setSizes(QList({6500, 10000})); - ui->splitterSearch->setSizes(QList({1000, 1000, 2000})); + ui->splitterSearch->setSizes(QList({1000, 3000})); + ui->splitterSeeds->setSizes(QList({500, 2500})); qRegisterMetaType< int64_t >("int64_t"); qRegisterMetaType< uint64_t >("uint64_t"); @@ -202,6 +206,7 @@ MainWindow::MainWindow(QString sessionpath, QString resultspath, QWidget *parent ui->collapseConstraints->init(tr("Conditions"), formCond, false); connect(formCond, &FormConditions::changed, this, &MainWindow::onConditionsChanged); + connect(formCond, &FormConditions::selectionUpdate, this, &MainWindow::onConditionsSelect); ui->collapseConstraints->setInfo( tr("Help: Conditions"), tr( @@ -304,7 +309,7 @@ bool MainWindow::loadTranslation(QString lang) static QTranslator qt_translator; if (!rc_translator.load(lang, ":/lang")) return false; - QLocale::setDefault(lang); + QLocale::setDefault(QLocale(lang)); QString qt_locale = "qtbase_" + lang; QString qt_trpath = QLibraryInfo::location(QLibraryInfo::TranslationsPath); if (qt_translator.load(qt_locale, qt_trpath)) @@ -432,8 +437,9 @@ bool MainWindow::setSeed(WorldInfo wi, int dim) ui->seedEdit->setEnabled(true); ui->comboY->setEnabled(true); - ISaveTab *tab = dynamic_cast(ui->tabContainer->currentWidget()); - if (tab) + if (ISaveTab *tab = dynamic_cast(ui->tabContainer->currentWidget())) + tab->refresh(); + if (ISaveTab *tab = dynamic_cast(ui->tabContainerSearch->currentWidget())) tab->refresh(); return true; } @@ -466,6 +472,7 @@ void MainWindow::saveSettings() g_extgen.save(settings); on_tabContainer_currentChanged(-1); + on_tabContainerSearch_currentChanged(-1); WorldInfo wi; getSeed(&wi, false); @@ -497,7 +504,7 @@ void MainWindow::loadSettings() showMaximized(); } else { resize(settings.value("mainwindow/size", size()).toSize()); - move(settings.value("mainwindow/pos", pos()).toPoint()); + //move(settings.value("mainwindow/pos", pos()).toPoint()); } prevdir = settings.value("mainwindow/prevdir", prevdir).toString(); @@ -635,7 +642,7 @@ void MainWindow::setDockable(bool dockable) // and avoid a warning about negative size widget QWidget *title = new QWidget(this); QHBoxLayout *l = new QHBoxLayout(title); - l->setMargin(0); + l->setContentsMargins(0, 0, 0, 0); title->setLayout(l); dock->setTitleBarWidget(title); dock->setFloating(false); @@ -885,7 +892,7 @@ void MainWindow::on_actionLayerDisplay_triggered() { WorldInfo wi; getSeed(&wi, false); - LayerDialog *dialog = new LayerDialog(this, wi.mc); + LayerDialog *dialog = new LayerDialog(this, wi); dialog->setLayerOptions(lopt); connect(dialog, &LayerDialog::apply, [=](){ lopt = dialog->getLayerOptions(); @@ -895,14 +902,24 @@ void MainWindow::on_actionLayerDisplay_triggered() dialog->show(); } -void MainWindow::on_tabContainer_currentChanged(int index) +static int tab_switch(QTabWidget *tabs, int idxprev, int idxnext) { QSettings settings(APP_STRING, APP_STRING); - ISaveTab *tabold = dynamic_cast(ui->tabContainer->widget(prevtab)); - ISaveTab *tabnew = dynamic_cast(ui->tabContainer->widget(index)); + ISaveTab *tabold = dynamic_cast(tabs->widget(idxprev)); + ISaveTab *tabnew = dynamic_cast(tabs->widget(idxnext)); if (tabold) tabold->save(settings); if (tabnew) tabnew->load(settings); - prevtab = index; + return idxnext; +} + +void MainWindow::on_tabContainer_currentChanged(int index) +{ + tabidx = tab_switch(ui->tabContainer, tabidx, index); +} + +void MainWindow::on_tabContainerSearch_currentChanged(int index) +{ + tabsearch = tab_switch(ui->tabContainerSearch, tabsearch, index); } void MainWindow::on_actionSearch_seed_list_triggered() @@ -946,16 +963,16 @@ void MainWindow::onActionMapToggled(int sopt, bool show) void MainWindow::onActionBiomeLayerSelect(int mode, int disp) { + WorldInfo wi; + getSeed(&wi, false); + lopt.mode = mode; if (disp >= 0) { - if (!getLayerOptionText(mode, disp)) + if (!getLayerOptionInfo(nullptr, mode, disp, wi)) return; // unsupported display mode lopt.disp[mode] = disp; } - lopt.mode = mode; - WorldInfo wi; - if (getSeed(&wi, false)) - setSeed(wi, DIM_UNDEF); + setSeed(wi, DIM_UNDEF); } void MainWindow::onConditionsChanged() @@ -964,6 +981,37 @@ void MainWindow::onConditionsChanged() formGen48->updateAutoConditions(conds); } +void MainWindow::onConditionsSelect(const QVector& selection) +{ + std::vector shapes; + for (const Condition& c : selection) + { + if (c.meta & Condition::DISABLED) + continue; + if (c.relative) + continue; + const FilterInfo& ft = g_filterinfo.list[c.type]; + + Shape s; + s.dim = ft.dim; + if (c.rmax) + { + s.type = Shape::CIRCLE; + s.p1 = s.p2 = Pos{0,0}; + s.r = c.rmax - 1; + } + else + { + s.type = Shape::RECT; + s.p1 = Pos{c.x1, c.z1}; + s.p2 = Pos{c.x2+1, c.z2+1}; + s.r = 0; + } + shapes.push_back(s); + } + getMapView()->setShapes(shapes); +} + void MainWindow::onGen48Changed() { formGen48->updateCount(); diff --git a/src/mainwindow.h b/src/mainwindow.h index 5171e0a..027d6c6 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -103,6 +103,7 @@ private slots: void on_actionLayerDisplay_triggered(); void on_tabContainer_currentChanged(int index); + void on_tabContainerSearch_currentChanged(int index); void on_actionSearch_seed_list_triggered(); void on_actionSearch_full_seed_space_triggered(); @@ -113,6 +114,7 @@ private slots: void onActionHistory(QAction *act); void onActionBiomeLayerSelect(int lopt, int disp = -1); void onConditionsChanged(); + void onConditionsSelect(const QVector& selection); void onGen48Changed(); void onSelectedSeedChanged(uint64_t seed); void onSearchStatusChanged(bool running); @@ -136,7 +138,8 @@ public: QString sessionpath; QString prevdir; QTimer autosaveTimer; - int prevtab; + int tabidx; + int tabsearch; QVector laction; QVector saction; diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 11a419a..44f22ee 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -211,6 +211,11 @@ + + QToolButton { + background-color: transparent; +} + 0 @@ -224,120 +229,68 @@ Search - + 0 - - 0 - - - 0 - - - 0 - - - 0 - - - - - 540 - 0 - + + + Qt::Vertical - - QAbstractScrollArea#scrollArea { - border: 0px none; -} - -QToolButton { - background-color: transparent; -} - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - Qt::ScrollBarAsNeeded - - - Qt::ScrollBarAlwaysOff - - - true - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - + - + 0 0 - - - 440 - 0 - - - + + + - 0 - - - 0 + 18 - - - - 0 - 0 - + + + 0 - - -QSplitter { - background-color: transparent; -} - - - Qt::Vertical - - - - - 0 - 0 - - - - - - - 0 - 0 - - - - - - - 0 - 0 - - + + + Seeds + + + + 0 + + + 0 + + + + + Qt::Vertical + + + + + 0 + 0 + + + + + + + 0 + 0 + + + + + + diff --git a/src/mapview.cpp b/src/mapview.cpp index 65766bd..11fe039 100644 --- a/src/mapview.cpp +++ b/src/mapview.cpp @@ -59,7 +59,7 @@ MapView::MapView(QWidget *parent) setFont(fmono); QPalette pal = palette(); - pal.setColor(QPalette::Background, Qt::black); + pal.setColor(QPalette::Dark, Qt::black); setAutoFillBackground(true); setPalette(pal); @@ -189,6 +189,13 @@ void MapView::setConfig(const Config& c) update(2); } +void MapView::setShapes(const std::vector& s) +{ + shapes = s; + settingsToWorld(); + update(1); +} + void MapView::refreshBiomeColors() { if (world) @@ -207,6 +214,7 @@ void MapView::settingsToWorld() world->memlimit = (uint64_t) config.mapCacheSize * 1024 * 1024; world->threadlimit = config.mapThreads; world->lopt = lopt; + world->shapes = shapes; } static qreal smoothstep(qreal x) @@ -304,6 +312,27 @@ qreal MapView::getZ() return fz; } +static qreal clampimax(qreal x, bool *ok = nullptr) +{ + const double imax = INT_MAX - 1024.0; + if (x < -imax) x = -imax; + if (x > +imax) x = +imax; + if (ok) *ok = (x > -imax && x < +imax); + return x; +} + +void MapView::getVisible(int *x0, int *z0, int *x1, int *z1) +{ + qreal x = getX(); + qreal z = getZ(); + qreal uiw = width() / blocks2pix; + qreal uih = height() / blocks2pix; + if (x0) *x0 = (int) clampimax( floor(x - uiw/2) ); + if (z0) *z0 = (int) clampimax( floor(z - uih/2) ); + if (x1) *x1 = (int) clampimax( ceil(x + uiw/2) ); + if (z1) *z1 = (int) clampimax( ceil(z + uih/2) ); +} + void MapView::update(int cnt) { updatecounter = cnt; @@ -435,19 +464,12 @@ void MapView::onGoto() dialog->show(); } -static bool clampabs(qreal *x, qreal m) -{ - if (*x < -m) { *x = -m; return true; } - if (*x > +m) { *x = +m; return true; } - return false; -} - void MapView::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); - painter.setRenderHint(QPainter::HighQualityAntialiasing); + //painter.setRenderHint(QPainter::HighQualityAntialiasing); qreal fx = getX(); qreal fz = getZ(); @@ -458,8 +480,11 @@ void MapView::paintEvent(QPaintEvent *) focusz = z_dst; blocks2pix = 1.0 / s_dst; } - if (clampabs(&fx, INT_MAX-1024)) focusx = fx; - if (clampabs(&fz, INT_MAX-1024)) focusz = fz; + bool ok; + fx = clampimax(fx, &ok); + if (!ok) focusx = fx; + fz = clampimax(fz, &ok); + if (!ok) focusz = fz; if (world) { @@ -468,9 +493,7 @@ void MapView::paintEvent(QPaintEvent *) QPoint cur = mapFromGlobal(QCursor::pos()); qreal bx = (cur.x() - width()/2.0) / blocks2pix + fx; qreal bz = (cur.y() - height()/2.0) / blocks2pix + fz; - clampabs(&bx, INT_MAX-1024); - clampabs(&bz, INT_MAX-1024); - Pos p = {(int)bx, (int)bz}; + Pos p = {(int)clampimax(bx), (int)clampimax(bz)}; overlay->pos = p; overlay->bname = world->getBiomeName(p); diff --git a/src/mapview.h b/src/mapview.h index 1994d3a..c99e025 100644 --- a/src/mapview.h +++ b/src/mapview.h @@ -35,6 +35,7 @@ public: qreal getX(); qreal getZ(); qreal getScale() const { return 1.0 / blocks2pix; } + void getVisible(int *x0, int *z0, int *x1, int *z1); void deleteWorld(); void refresh(); @@ -53,6 +54,7 @@ public: bool getShow(int stype); void setShow(int stype, bool v); void setConfig(const Config& config); + void setShapes(const std::vector& shapes); void refreshBiomeColors(); void timeout(); @@ -110,6 +112,8 @@ private: bool measure; int updatecounter; + std::vector shapes; + bool sshow[D_STRUCT_NUM]; LayerOpt lopt; Config config; diff --git a/src/rangeslider.cpp b/src/rangeslider.cpp index ab5efde..565a394 100644 --- a/src/rangeslider.cpp +++ b/src/rangeslider.cpp @@ -207,7 +207,7 @@ LabeledRange::LabeledRange(QWidget *parent, int vmin, int vmax) l->addWidget(minlabel); l->addWidget(slider); l->addWidget(maxlabel); - l->setMargin(0); + l->setContentsMargins(0, 0, 0, 0); setLayout(l); connect(slider, SIGNAL(valueChanged(int)), this, SLOT(rangeChanged(void))); diff --git a/src/scripts.cpp b/src/scripts.cpp index a77b703..d374a49 100644 --- a/src/scripts.cpp +++ b/src/scripts.cpp @@ -72,7 +72,7 @@ void getScripts(QMap& scripts) QDirIterator it(dir, QDirIterator::NoIteratorFlags); while (it.hasNext()) { - QFileInfo f = it.next(); + QFileInfo f = QFileInfo(it.next()); if (f.suffix() != "lua") continue; uint64_t hash = getScriptHash(f); @@ -436,19 +436,32 @@ BlockRule *LuaHighlighter::nextBlockRule(const QString& text, int *pos, int *nex { BlockRule *rule = nullptr; int min = INT_MAX; + int end = 0; for (int i = 0, n = blockrules.size(); i < n; i++) { +#if 1 + QRegularExpressionMatch m = blockrules[i].start.match(text, *pos); + if (m.hasMatch() && m.capturedStart() < min) + { + rule = &blockrules[i]; + min = m.capturedStart(); + end = m.capturedEnd(); + } + } +#else int s = blockrules[i].start.indexIn(text, *pos); if (s >= 0 && s < min) { rule = &blockrules[i]; min = s; + end = s + rule->start.matchedLength(); } } +#endif if (rule) { *pos = min; - *next = min + rule->start.matchedLength(); + *next = end; } return rule; } @@ -467,6 +480,21 @@ void LuaHighlighter::highlightBlock(const QString& text) while (rule) { +#if 1 + QRegularExpressionMatch m = rule->end.match(text, match); + if (m.hasMatch()) + { + markFormated(&line, start, line.length() - start, rule->format); + break; + } + else + { + int end = m.capturedEnd(); + markFormated(&line, start, end - start, rule->format); + start = end; + rule = nextBlockRule(line, &start, &match); + } +#else int end = rule->end.indexIn(text, match); if (end == -1) { @@ -480,6 +508,7 @@ void LuaHighlighter::highlightBlock(const QString& text) start = end; rule = nextBlockRule(line, &start, &match); } +#endif } if (rule) setCurrentBlockState(rule - &blockrules[0]); @@ -489,6 +518,14 @@ void LuaHighlighter::highlightBlock(const QString& text) for (const Rule &rule : qAsConst(rules)) { const QString *l = rule.overlay ? &text : &line; +#if 1 + QRegularExpressionMatch m = rule.pattern.match(*l); + while (m.hasMatch()) + { + setFormat(m.capturedStart(), m.capturedLength(), rule.format); + m = rule.pattern.match(*l, m.capturedEnd()); + } +#else QRegExp expression(rule.pattern); int index = expression.indexIn(*l); while (index >= 0) @@ -497,6 +534,7 @@ void LuaHighlighter::highlightBlock(const QString& text) setFormat(index, length, rule.format); index = expression.indexIn(*l, index + length); } +#endif } } @@ -633,7 +671,7 @@ void ScriptEditor::keyPressEvent(QKeyEvent *event) { const QString text = document()->toPlainText(); const QChar linebreak = QChar(0x2029); - QString ws = linebreak + QStringRef(&text, start, end-start).toString(); + QString ws = linebreak + text.mid(start, end-start); cursor.beginEditBlock(); cursor.insertText(ws); cursor.endEditBlock(); diff --git a/src/scripts.h b/src/scripts.h index 7d320b5..46b5e38 100644 --- a/src/scripts.h +++ b/src/scripts.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "cubiomes/finders.h" @@ -60,7 +61,7 @@ int runCheckScript( struct Rule { - QRegExp pattern; + QRegularExpression pattern; QTextCharFormat format; bool overlay; Rule() : pattern(),format(),overlay() {} @@ -69,7 +70,7 @@ struct Rule }; struct BlockRule { - QRegExp start, end; + QRegularExpression start, end; QTextCharFormat format; BlockRule() : start(), end(), format() {} BlockRule(const QString& s, const QString& e, const QTextCharFormat& f) diff --git a/src/search.cpp b/src/search.cpp index 4d18c8c..273fea6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -34,7 +34,7 @@ QString Condition::summary(bool aligntab) const } QString cnts; - if (ft.count) + if (ft.branch == FilterInfo::BR_CLUST) cnts += MULTIPLY_CHAR + QString::number(count); if (skipref) cnts += "*"; @@ -73,10 +73,10 @@ QString Condition::summary(bool aligntab) const } else { - if (ft.coord) - s += QString::asprintf("(%d,%d)", x1*ft.step, z1*ft.step); - if (ft.area) - s += QString::asprintf(",(%d,%d)", (x2+1)*ft.step-1, (z2+1)*ft.step-1); + if (ft.loc & FilterInfo::LOC_1) + s += QString::asprintf("(%d,%d)", x1, z1); + if (ft.loc & FilterInfo::LOC_2) + s += QString::asprintf(",(%d,%d)", x2, z2); } return s; } @@ -95,15 +95,13 @@ bool Condition::versionUpgrade() memset(pad1, 0, sizeof(pad1)); hash = 0; memset(deps, 0, sizeof(deps)); - memset(pad2, 0, sizeof(pad2)); - biomeId = biomeSize = tol = minmax = para = octave = 0; - varflags = varbiome = varstart = 0; + biomeId = biomeSize = tol = minmax = para = octave = step = 0; } - else if (version == VER_2_3_0) + if (version < VER_2_4_0) { varflags = varbiome = varstart = 0; } - else if (version == VER_2_4_0) + if (version < VER_3_4_0) { if (type == F_CLIMATE_MINMAX) { @@ -126,6 +124,28 @@ bool Condition::versionUpgrade() } } } + if (version < VER_4_0_0) + { + const FilterInfo& ft = g_filterinfo.list[type]; + if (ft.grid > 1) + { + x1 = x1 * ft.grid; + z1 = z1 * ft.grid; + x2 = (x2+1) * ft.grid - 1; + z2 = (z2+1) * ft.grid - 1; + } + switch (type) + { + case F_SPIRAL: step = 1; break; + case F_SPIRAL_4: step = 4; type = F_SPIRAL; break; + case F_SPIRAL_16: step = 16; type = F_SPIRAL; break; + case F_SPIRAL_64: step = 64; type = F_SPIRAL; break; + case F_SPIRAL_256: step = 256; type = F_SPIRAL; break; + case F_SPIRAL_512: step = 512; type = F_SPIRAL; break; + case F_SPIRAL_1024: step = 1024; type = F_SPIRAL; break; + } + } + version = VER_CURRENT; return true; } @@ -312,66 +332,96 @@ int testTreeAt( ConditionTree *tree = env->condtree; Condition& c = tree->condvec[node]; const std::vector& branches = tree->references[c.save]; - int st; + int st, br; int rx1, rz1, rx2, rz2; - int sref; Pos pos; Pos inst[MAX_INSTANCES]; - const FilterInfo *finfo; switch (c.type) { - case F_SPIRAL_1: sref = 0; goto L_ref_pow2; - case F_SPIRAL_4: sref = 2; goto L_ref_pow2; - case F_SPIRAL_16: sref = 4; goto L_ref_pow2; - case F_SPIRAL_64: sref = 6; goto L_ref_pow2; - case F_SPIRAL_256: sref = 8; goto L_ref_pow2; - case F_SPIRAL_512: sref = 9; goto L_ref_pow2; - case F_SPIRAL_1024: sref = 10; goto L_ref_pow2; - L_ref_pow2: - rx1 = ((c.x1 << sref) + at.x) >> sref; - rz1 = ((c.z1 << sref) + at.z) >> sref; - rx2 = ((c.x2 << sref) + at.x) >> sref; - rz2 = ((c.z2 << sref) + at.z) >> sref; + case F_SPIRAL: + st = COND_FAILED; { // run a spiral iterator over the rectangle - int x = (rx1 + rx2) >> 1; - int z = (rz1 + rz2) >> 1; + int step = c.step ? c.step : 512; + int rmax, x1, z1, x2, z2; + if (c.rmax > 0) + { + rmax = c.rmax - 1; + x1 = at.x - rmax; + z1 = at.z - rmax; + x2 = at.x + rmax; + z2 = at.z + rmax; + rmax = rmax * rmax + 1; + } + else + { + rmax = 0; + x1 = c.x1 + at.x; + z1 = c.z1 + at.z; + x2 = c.x2 + at.x; + z2 = c.z2 + at.z; + } + + rx1 = floordiv(x1, step); + rz1 = floordiv(z1, step); + rx2 = floordiv(x2, step); + rz2 = floordiv(z2, step); + + int rx = (rx1 + rx2) >> 1; + int rz = (rz1 + rz2) >> 1; int i = 0, dl = 1; int dx = 1, dz = 0; while (true) { - bool inx = (x >= rx1 && x <= rx2); - bool inz = (z >= rz1 && z <= rz2); + bool inx = (rx >= rx1 && rx <= rx2); + bool inz = (rz >= rz1 && rz <= rz2); if (!inx && !inz) break; if (inx && inz) { - pos.x = (x << sref); - pos.z = (z << sref); - // children are combined via AND - int sta = COND_OK; - for (int b : branches) + pos.x = rx * step; + pos.z = rz * step; + + bool inr = true; + if (rmax) { - int stb = testTreeAt(pos, env, pass, abort, path, b); - if (*abort) - return COND_FAILED; - if (stb < sta) - sta = stb; - if (sta == COND_FAILED) - break; + int dx = pos.x - at.x; + int dz = pos.z - at.z; + int64_t rsq = dx*(int64_t)dx + dz*(int64_t)dz; + inr = (rsq < rmax); + } + else if (pos.x < x1 || pos.x > x2 || pos.z < z1 || pos.z > z2) + { + inr = false; + } + + if (inr) + { + // children are combined via AND + int sta = COND_OK; + for (int b : branches) + { + int stb = testTreeAt(pos, env, pass, abort, path, b); + if (*abort) + return COND_FAILED; + if (stb < sta) + sta = stb; + if (sta == COND_FAILED) + break; + } + if (sta == COND_MAYBE_POS_VALID ) + sta = COND_MAYBE_POS_INVAL; // position moves => invalidate + if (sta > st) + st = sta; + if (path && st >= COND_MAYBE_POS_VALID) + path[c.save] = pos; + if (st == COND_OK) + return COND_OK; } - if (sta == COND_MAYBE_POS_VALID ) - sta = COND_MAYBE_POS_INVAL; // position moves => invalidate - if (sta > st) - st = sta; - if (path && st >= COND_MAYBE_POS_VALID) - path[c.save] = pos; - if (st == COND_OK) - return COND_OK; } - x += dx; - z += dz; + rx += dx; + rz += dz; if (++i == dl) { i = 0; @@ -412,7 +462,7 @@ int testTreeAt( return st; - case F_LOGIC_OR: + case F_LOGIC_OR: //qDebug() << at.x << at.z; return COND_FAILED; if (branches.empty()) { if (path) @@ -499,18 +549,48 @@ int testTreeAt( { if (icnt == 1) path[c.save] = *inst; + else if (icnt > 1 && st == COND_OK) + path[c.save] = *inst; else path[c.save].x = path[c.save].z = -1; } return st; } - finfo = g_filterinfo.list + c.type; - if (c.count == 1 && (finfo->count || finfo->cat == CAT_QUAD)) - { // condition has exactly one required instance so we can check each - // of the found instances individually, i.e. this branch splits the - // instances into independent subbranches (combined via OR) - // quad conditions are also processed here since we want to - // examine all instances without support for averaging + + br = g_filterinfo.list[c.type].branch; + + if (br == FilterInfo::BR_NONE || (br == FilterInfo::BR_CLUST && c.count != 1)) + { // this condition cannot branch, position of multiple instances + // will be averaged to a center point + if (c.type == 0) + { // this is the root condition + st = COND_OK; + pos = at; + } + else + { + st = testCondAt(at, env, pass, abort, inst, NULL, &c); + if (st == COND_FAILED || st == COND_MAYBE_POS_INVAL) + return st; + pos = inst[0]; // center point of instances + } + for (char b : branches) + { + if (st == COND_FAILED) + break; + int sta = testTreeAt(pos, env, pass, abort, path, b); + if (*abort) + return COND_FAILED; + if (sta < st) + st = sta; + } + if (path && st >= COND_MAYBE_POS_VALID) + path[c.save] = pos; + return st; + } + else + { // check each instance individually, splitting the instances into + // independent subbranches that are combined via OR int icnt = MAX_INSTANCES; st = testCondAt(at, env, pass, abort, inst, &icnt, &c); if (st == COND_FAILED || st == COND_MAYBE_POS_INVAL) @@ -548,35 +628,6 @@ int testTreeAt( path[c.save] = inst[iok]; return st; } - else - { // this condition cannot branch, position of multiple instances - // will be averaged to a center point - if (c.type == 0) - { // this is the root condition - st = COND_OK; - pos = at; - } - else - { - st = testCondAt(at, env, pass, abort, inst, NULL, &c); - if (st == COND_FAILED || st == COND_MAYBE_POS_INVAL) - return st; - pos = inst[0]; // center point of instances - } - for (char b : branches) - { - if (st == COND_FAILED) - break; - int sta = testTreeAt(pos, env, pass, abort, path, b); - if (*abort) - return COND_FAILED; - if (sta < st) - st = sta; - } - if (path && st >= COND_MAYBE_POS_VALID) - path[c.save] = pos; - return st; - } } } @@ -895,6 +946,59 @@ static int f_track_minmax(void *data, int x, int z, double p) return 0; } + +struct sample_boime_t +{ + Condition *cond; + Pos at; + int rmaxsq; + int n; + int64_t xsum; + int64_t zsum; + Pos *cent; + int *imax; + std::atomic_bool *stop; +}; + +static int f_biome_sampler(Generator *g, int scale, int x, int y, int z, void *data) +{ + sample_boime_t *info = (sample_boime_t*) data; + if (info->stop && *info->stop) + return -2; + if (info->rmaxsq) + { + int dx = x - info->at.x; + int dz = z - info->at.z; + int64_t rsq = dx*(int64_t)dx + dz*(int64_t)dz; + if (rsq >= info->rmaxsq) + return -1; + } + + int id = getBiomeAt(g, scale, x, y, z); + uint64_t incl = 0, excl = 0; + if (id < 128) { + incl = info->cond->biomeToFind & (1ULL << id); + excl = info->cond->biomeToExcl & (1ULL << id); + } else { + incl = info->cond->biomeToFindM & (1ULL << (id-128)); + excl = info->cond->biomeToExclM & (1ULL << (id-128)); + } + if (incl != 0) + { + if (info->imax && info->n < *info->imax) + info->cent[info->n] = Pos{x, z}; + info->xsum += x; + info->zsum += z; + info->n++; + return 1; + } + if (excl != 0) + { + return 0; + } + return 0; +} + /* Tests if a condition is satisfied with 'at' as origin for a search pass. * If sufficiently satisfied (check return value) then: * when 'imax' is NULL, the center position is written to 'cent[0]' @@ -933,9 +1037,26 @@ testCondAt( return COND_FAILED; } + if (cond->rmax > 0) + { + rmax = cond->rmax - 1; + x1 = at.x - rmax; + z1 = at.z - rmax; + x2 = at.x + rmax; + z2 = at.z + rmax; + } + else + { + rmax = 0; + x1 = cond->x1 + at.x; + z1 = cond->z1 + at.z; + x2 = cond->x2 + at.x; + z2 = cond->z2 + at.z; + } + switch (cond->type) { - case F_SPIRAL_1: + case F_SPIRAL: case F_SPIRAL_4: case F_SPIRAL_16: case F_SPIRAL_64: @@ -968,10 +1089,10 @@ testCondAt( n = sizeof(low20QuadHutBarely) / sizeof(uint64_t); L_qh_any: - rx1 = ((cond->x1 << 9) + at.x) >> 9; - rz1 = ((cond->z1 << 9) + at.z) >> 9; - rx2 = ((cond->x2 << 9) + at.x) >> 9; - rz2 = ((cond->z2 << 9) + at.z) >> 9; + rx1 = x1 >> 9; + rz1 = z1 >> 9; + rx2 = x2 >> 9; + rz2 = z2 >> 9; n = scanForQuads( sconf, 128, (env->seed) & MASK48, seeds, n, 20, sconf.salt, @@ -1009,10 +1130,10 @@ L_qh_any: case F_QM_90: qual = 58*58*4 * 90 / 100; L_qm_any: - rx1 = ((cond->x1 << 9) + at.x) >> 9; - rz1 = ((cond->z1 << 9) + at.z) >> 9; - rx2 = ((cond->x2 << 9) + at.x) >> 9; - rz2 = ((cond->z2 << 9) + at.z) >> 9; + rx1 = x1 >> 9; + rz1 = z1 >> 9; + rx2 = x2 >> 9; + rz2 = z2 >> 9; // we don't really need to check for more than one instance here n = scanForQuads( sconf, 160, (env->seed) & MASK48, g_qm_90, @@ -1068,23 +1189,6 @@ L_qm_any: case F_ENDCITY: case F_GATEWAY: - if (cond->rmax > 0) - { - rmax = (cond->rmax-1) * (cond->rmax-1) + 1; - x1 = at.x - cond->rmax; - z1 = at.z - cond->rmax; - x2 = at.x + cond->rmax; - z2 = at.z + cond->rmax; - } - else - { - rmax = 0; - x1 = cond->x1 + at.x; - z1 = cond->z1 + at.z; - x2 = cond->x2 + at.x; - z2 = cond->z2 + at.z; - } - if (sconf.regionSize == 32) { rx1 = x1 >> 9; @@ -1239,41 +1343,12 @@ L_qm_any: case F_MINESHAFT: - if (cond->rmax > 0) - { - rmax = (cond->rmax-1) * (cond->rmax-1) + 1; - x1 = at.x - cond->rmax; - z1 = at.z - cond->rmax; - x2 = at.x + cond->rmax; - z2 = at.z + cond->rmax; - } - else - { - rmax = 0; - x1 = cond->x1 + at.x; - z1 = cond->z1 + at.z; - x2 = cond->x2 + at.x; - z2 = cond->z2 + at.z; - } rx1 = x1 >> 4; rz1 = z1 >> 4; rx2 = x2 >> 4; rz2 = z2 >> 4; - if (cond->count <= 0) - { // exclusion - icnt = getMineshafts(env->mc, env->seed, rx1, rz1, rx2, rz2, cent, 1); - if (icnt == 1 && cond->skipref && cent->x == at.x && cent->z == at.z) - icnt = 0; - cent->x = (x1 + x2) >> 1; - cent->z = (z1 + z2) >> 1; - if (icnt == 0) - { - if (imax) *imax = 1; - return COND_OK; - } - } - else if (imax) + if (imax && cond->count > 0) { // just check there are at least *inst (== cond->count) instances *imax = icnt = getMineshafts(env->mc, env->seed, rx1, rz1, rx2, rz2, cent, *imax); @@ -1328,7 +1403,17 @@ L_qm_any: zt += p[i].z; j++; } - if (j >= cond->count) + if (cond->count <= 0) + { + cent->x = (x1 + x2) >> 1; + cent->z = (z1 + z2) >> 1; + if (j == 0) + { + if (imax) *imax = 1; + return COND_OK; + } + } + else if (j >= cond->count) { cent->x = xt / j; cent->z = zt / j; @@ -1344,22 +1429,6 @@ L_qm_any: if (pass != PASS_FULL_64) return COND_MAYBE_POS_INVAL; - if (cond->rmax > 0) - { - rmax = (cond->rmax-1) * (cond->rmax-1) + 1; - x1 = at.x - cond->rmax; - z1 = at.z - cond->rmax; - x2 = at.x + cond->rmax; - z2 = at.z + cond->rmax; - } - else - { - rmax = 0; - x1 = cond->x1 + at.x; - z1 = cond->z1 + at.z; - x2 = cond->x2 + at.x; - z2 = cond->z2 + at.z; - } if (*abort) return COND_FAILED; env->init4Dim(DIM_OVERWORLD); pc = getSpawn(&env->g); @@ -1398,10 +1467,6 @@ L_qm_any: } else { - x1 = cond->x1 + at.x; - z1 = cond->z1 + at.z; - x2 = cond->x2 + at.x; - z2 = cond->z2 + at.z; if (pc.x < x1 || pc.x > x2 || pc.z < z1 || pc.z > z2) return COND_FAILED; } @@ -1564,10 +1629,10 @@ L_qm_any: case F_SLIME: - rx1 = ((cond->x1 << 4) + at.x) >> 4; - rz1 = ((cond->z1 << 4) + at.z) >> 4; - rx2 = ((cond->x2 << 4) + at.x) >> 4; - rz2 = ((cond->z2 << 4) + at.z) >> 4; + rx1 = x1 >> 4; + rz1 = z1 >> 4; + rx2 = x2 >> 4; + rz2 = z2 >> 4; icnt = 0; xt = zt = 0; @@ -1602,8 +1667,8 @@ L_qm_any: } if (cond->count == 0) { // exclusion filter - cent->x = (rx1 + rx2) << 3; - cent->z = (rz1 + rz2) << 3; + cent->x = (x1 + x2) >> 1; + cent->z = (z1 + z2) >> 1; if (imax) *imax = 1; return COND_OK; } @@ -1613,13 +1678,65 @@ L_qm_any: } else if (icnt) { - cent->x = (xt << 4) / icnt; - cent->z = (zt << 4) / icnt; + cent->x = (xt << 4) / icnt + 8; + cent->z = (zt << 4) / icnt + 8; } if (icnt >= cond->count) return COND_OK; return COND_FAILED; + + case F_BIOME_SAMPLE: + + if (pass != PASS_FULL_64) + return COND_MAYBE_POS_INVAL; + if (cond->confidence <= 0 || cond->confidence >= 1) + return COND_FAILED; + if (cond->converage <= 0 || cond->converage > 1) + return COND_FAILED; + s = finfo.pow2; + rx1 = x1 >> s; + rz1 = z1 >> s; + rx2 = x2 >> s; + rz2 = z2 >> s; + { + int w = rx2 - rx1 + 1; + int h = rz2 - rz1 + 1; + Range r = {1<y : cond->y >> 2, 1}; + env->init4Dim(DIM_OVERWORLD); + sample_boime_t sample; + sample.cond = cond; + sample.at = at; + sample.n = 0; + sample.xsum = 0; + sample.zsum = 0; + sample.imax = imax; + sample.cent = cent; + sample.stop = abort; + + uint64_t rng; + setSeed(&rng, env->seed); + + int ok = monteCarloBiomes( + &env->g, r, &rng, cond->converage, cond->confidence, + &f_biome_sampler, &sample); + if (imax) + { + *imax = sample.n; + } + else if (sample.n) + { + cent->x = sample.xsum / sample.n + 2; + cent->z = sample.zsum / sample.n + 2; + } + else + { + *cent = at; + } + return ok == 1 ? COND_OK : COND_FAILED; + } + return COND_FAILED; + // biome filters reference specific layers // MAYBE: options for layers in different versions? case F_BIOME: @@ -1632,12 +1749,12 @@ L_qm_any: if (env->mc >= MC_1_18) return COND_FAILED; s = finfo.pow2; - rx1 = ((cond->x1 << s) + at.x) >> s; - rz1 = ((cond->z1 << s) + at.z) >> s; - rx2 = ((cond->x2 << s) + at.x) >> s; - rz2 = ((cond->z2 << s) + at.z) >> s; - cent->x = ((rx1 + rx2) << s) >> 1; - cent->z = ((rz1 + rz2) << s) >> 1; + rx1 = x1 >> s; + rz1 = z1 >> s; + rx2 = x2 >> s; + rz2 = z2 >> s; + cent->x = (x1 + x2) >> 1; + cent->z = (z1 + z2) >> 1; if (pass == PASS_FAST_48) return COND_MAYBE_POS_VALID; if (pass == PASS_FULL_48) @@ -1663,12 +1780,12 @@ L_qm_any: case F_TEMPS: if (env->mc >= MC_1_18) return COND_FAILED; - rx1 = ((cond->x1 << 10) + at.x) >> 10; - rz1 = ((cond->z1 << 10) + at.z) >> 10; - rx2 = ((cond->x2 << 10) + at.x) >> 10; - rz2 = ((cond->z2 << 10) + at.z) >> 10; - cent->x = ((rx1 + rx2) << 10) >> 1; - cent->z = ((rz1 + rz2) << 10) >> 1; + rx1 = x1 >> 10; + rz1 = z1 >> 10; + rx2 = x2 >> 10; + rz2 = z2 >> 10; + cent->x = (x1 + x2) >> 1; + cent->z = (z1 + z2) >> 1; if (pass != PASS_FULL_64) return COND_MAYBE_POS_VALID; env->init4Dim(DIM_OVERWORLD); @@ -1692,12 +1809,12 @@ L_qm_any: L_noise_biome: s = finfo.pow2; - rx1 = ((cond->x1 << s) + at.x) >> s; - rz1 = ((cond->z1 << s) + at.z) >> s; - rx2 = ((cond->x2 << s) + at.x) >> s; - rz2 = ((cond->z2 << s) + at.z) >> s; - cent->x = ((rx1 + rx2) << s) >> 1; - cent->z = ((rz1 + rz2) << s) >> 1; + rx1 = x1 >> s; + rz1 = z1 >> s; + rx2 = x2 >> s; + rz2 = z2 >> s; + cent->x = (x1 + x2) >> 1; + cent->z = (z1 + z2) >> 1; if (pass == PASS_FAST_48) return COND_MAYBE_POS_VALID; // the Nether and End require only the 48-bit seed @@ -1720,13 +1837,13 @@ L_noise_biome: if (pass == PASS_FULL_64) { s = finfo.pow2; - rx1 = ((cond->x1 << s) + at.x) >> s; - rz1 = ((cond->z1 << s) + at.z) >> s; - rx2 = ((cond->x2 << s) + at.x) >> s; - rz2 = ((cond->z2 << s) + at.z) >> s; + rx1 = x1 >> s; + rz1 = z1 >> s; + rx2 = x2 >> s; + rz2 = z2 >> s; int w = rx2 - rx1 + 1; int h = rz2 - rz1 + 1; - Range r = {finfo.step, rx1, rz1, w, h, cond->y >> 2, 1}; + Range r = {1<y >> 2, 1}; env->init4Dim(DIM_OVERWORLD); if (cond->count == 0) @@ -1737,8 +1854,8 @@ L_noise_biome: ); if (icnt == 0) { - cent->x = (rx1 + rx2) << 1; - cent->z = (rz1 + rz2) << 1; + cent->x = (x1 + x2) >> 1; + cent->z = (z1 + z2) >> 1; if (imax) *imax = 1; return COND_OK; } @@ -1782,8 +1899,8 @@ L_noise_biome: } if (j >= cond->count) { - cent->x = xt / j; - cent->z = zt / j; + cent->x = xt / j + (1 << s) / 2; + cent->z = zt / j + (1 << s) / 2; return COND_OK; } } @@ -1794,10 +1911,10 @@ L_noise_biome: case F_CLIMATE_MINMAX: if (env->mc < MC_1_18 || cond->para >= NP_MAX) return COND_FAILED; - rx1 = ((cond->x1 << 2) + at.x) >> 2; - rz1 = ((cond->z1 << 2) + at.z) >> 2; - rx2 = ((cond->x2 << 2) + at.x) >> 2; - rz2 = ((cond->z2 << 2) + at.z) >> 2; + rx1 = x1 >> 2; + rz1 = z1 >> 2; + rx2 = x2 >> 2; + rz2 = z2 >> 2; if (pass != PASS_FULL_64) return COND_MAYBE_POS_INVAL; { @@ -1836,12 +1953,12 @@ L_noise_biome: case F_CLIMATE_NOISE: if (env->mc < MC_1_18) return COND_FAILED; - rx1 = ((cond->x1 << 2) + at.x) >> 2; - rz1 = ((cond->z1 << 2) + at.z) >> 2; - rx2 = ((cond->x2 << 2) + at.x) >> 2; - rz2 = ((cond->z2 << 2) + at.z) >> 2; - cent->x = ((rx1 + rx2) << 2) >> 1; - cent->z = ((rz1 + rz2) << 2) >> 1; + rx1 = x1 >> 2; + rz1 = z1 >> 2; + rx2 = x2 >> 2; + rz2 = z2 >> 2; + cent->x = (x1 + x2) >> 1; + cent->z = (z1 + z2) >> 1; if (pass != PASS_FULL_64) return COND_MAYBE_POS_VALID; { @@ -1897,10 +2014,10 @@ L_noise_biome: case F_HEIGHT: - rx1 = ((cond->x1 << 2) + at.x) >> 2; - rz1 = ((cond->z1 << 2) + at.z) >> 2; - cent->x = rx1 << 2; - cent->z = rz1 << 2; + rx1 = x1 >> 2; + rz1 = z1 >> 2; + cent->x = x1; + cent->z = z1; if (pass != PASS_FULL_64) return COND_MAYBE_POS_VALID; env->init4Dim(DIM_OVERWORLD); @@ -2015,3 +2132,4 @@ void findQuadStructs(int styp, Generator *g, QVector *out) + diff --git a/src/search.h b/src/search.h index 344fda5..26f12bf 100644 --- a/src/search.h +++ b/src/search.h @@ -69,7 +69,7 @@ enum F_PORTALN, F_GATEWAY, F_MINESHAFT, - F_SPIRAL_1, + F_SPIRAL, F_SPIRAL_16, F_SPIRAL_64, F_SPIRAL_256, @@ -91,22 +91,35 @@ enum F_LUA, F_WELL, F_TRAILS, + F_BIOME_SAMPLE, // new filters should be added here at the end to keep some downwards compatibility FILTER_MAX, }; struct FilterInfo { + enum { + LOC_1 = 1, LOC_2 = 2, LOC_R = 4, + LOC_NIL = 0x0, + LOC_POS = 0x1, + LOC_REC = 0x3, + LOC_RAD = 0x7, + }; + enum { + BR_NONE = 0, // one position - no branching + BR_FIRST = 1, // choose only first, no branching + BR_SPLIT = 2, // branches are examined separately + BR_CLUST = 3, // allow clustering to avoid branching + }; + int cat; // seed source category bool dep64; // depends on 64-bit seed - bool coord; // requires coordinate entry - bool area; // requires area entry - bool rmax; // supports radial range + int loc; int layer; // associated generator layer int stype; // structure type - int step; // coordinate multiplier - int pow2; // bit position of step - int count; // can have instances + int grid; // coordinate multiplier + int pow2; // bit position of grid + int branch; // branching behaviour int mcmin; // minimum version int mcmax; // maximum version int dim; // dimension @@ -118,7 +131,7 @@ struct FilterInfo }; // global table of filter data (as constants with enum indexing) -static const struct FilterList +static const struct FilterList : private FilterInfo { FilterInfo list[FILTER_MAX]; @@ -127,13 +140,13 @@ static const struct FilterList int disp = 0; // display order list[F_SELECT] = FilterInfo{ - CAT_NONE, 0, 0, 0, 0, 0, 0, 0, 0, 0, MC_UNDEF, MC_NEWEST, 0, 0, disp++, + CAT_NONE, 0, LOC_NIL, 0, 0, 0, 0, BR_NONE, MC_UNDEF, MC_NEWEST, 0, 0, disp++, NULL, "", "" }; list[F_LOGIC_OR] = FilterInfo{ - CAT_HELPER, 0, 0, 0, 0, 0, 0, 1, 0, 0, MC_UNDEF, MC_NEWEST, DIM_UNDEF, 0, disp++, + CAT_HELPER, 0, LOC_NIL, 0, 0, 1, 0, BR_NONE, MC_UNDEF, MC_NEWEST, DIM_UNDEF, 0, disp++, "helper", QT_TRANSLATE_NOOP("Filter", "OR logic gate"), QT_TRANSLATE_NOOP("Filter", @@ -142,7 +155,7 @@ static const struct FilterList "defined, it defaults to true.") }; list[F_LOGIC_NOT] = FilterInfo{ - CAT_HELPER, 0, 0, 0, 0, 0, 0, 1, 0, 0, MC_UNDEF, MC_NEWEST, DIM_UNDEF, 0, disp++, + CAT_HELPER, 0, LOC_NIL, 0, 0, 1, 0, BR_NONE, MC_UNDEF, MC_NEWEST, DIM_UNDEF, 0, disp++, "helper", QT_TRANSLATE_NOOP("Filter", "NOT logic gate"), QT_TRANSLATE_NOOP("Filter", @@ -151,79 +164,41 @@ static const struct FilterList "defined, it defaults to true.") }; list[F_LUA] = FilterInfo{ - CAT_HELPER, 0, 0, 0, 0, 0, 0, 1, 0, 0, MC_UNDEF, MC_NEWEST, DIM_UNDEF, 0, disp++, + CAT_HELPER, 0, LOC_NIL, 0, 0, 1, 0, BR_NONE, MC_UNDEF, MC_NEWEST, DIM_UNDEF, 0, disp++, "helper", QT_TRANSLATE_NOOP("Filter", "Lua"), QT_TRANSLATE_NOOP("Filter", "Define custom conditions using Lua scripts.") }; list[F_SCALE_TO_NETHER] = FilterInfo{ - CAT_HELPER, 0, 0, 0, 0, 0, 0, 1, 0, 0, MC_UNDEF, MC_NEWEST, DIM_UNDEF, 0, disp++, + CAT_HELPER, 0, LOC_NIL, 0, 0, 1, 0, BR_NONE, MC_UNDEF, MC_NEWEST, DIM_UNDEF, 0, disp++, "portal_lit", QT_TRANSLATE_NOOP("Filter", "Coordinate factor x/8"), QT_TRANSLATE_NOOP("Filter", "Divides relative location by 8, from Overworld to Nether.") }; list[F_SCALE_TO_OVERWORLD] = FilterInfo{ - CAT_HELPER, 0, 0, 0, 0, 0, 0, 1, 0, 0, MC_UNDEF, MC_NEWEST, DIM_UNDEF, 0, disp++, + CAT_HELPER, 0, LOC_NIL, 0, 0, 1, 0, BR_NONE, MC_UNDEF, MC_NEWEST, DIM_UNDEF, 0, disp++, "portal_lit", QT_TRANSLATE_NOOP("Filter", "Coordinate factor x*8"), QT_TRANSLATE_NOOP("Filter", "Multiplies relative location by 8, from Nether to Overworld.") }; - const char *spiral_desc = QT_TRANSLATE_NOOP("Filter", + list[F_SPIRAL] = FilterInfo{ + CAT_HELPER, 0, LOC_RAD, 0, 0, 1, 0, BR_FIRST, MC_UNDEF, MC_NEWEST, DIM_UNDEF, 0, disp++, + "reference", + QT_TRANSLATE_NOOP("Filter", "Spiral iterator"), + QT_TRANSLATE_NOOP("Filter", "" "Spiral iterator conditions can be used to move a testing position across " "a given area using a certain step size. Other conditions that refer to it " "as a relative location will be checked at each step. The iteration is " "performed in a spiral, so positions closer to the center get priority." - "" - ); - list[F_SPIRAL_1] = FilterInfo{ - CAT_HELPER, 0, 1, 1, 0, 0, 0, 1, 0, 0, MC_UNDEF, MC_NEWEST, DIM_UNDEF, 0, disp++, - "reference", - QT_TRANSLATE_NOOP("Filter", "Spiral iterator 1:1"), - spiral_desc - }; - list[F_SPIRAL_4] = FilterInfo{ - CAT_HELPER, 0, 1, 1, 0, 0, 0, 4, 2, 0, MC_UNDEF, MC_NEWEST, DIM_UNDEF, 0, disp++, - "reference", - QT_TRANSLATE_NOOP("Filter", "Spiral iterator 1:4"), - spiral_desc - }; - list[F_SPIRAL_16] = FilterInfo{ - CAT_HELPER, 0, 1, 1, 0, 0, 0, 16, 4, 0, MC_UNDEF, MC_NEWEST, DIM_UNDEF, 0, disp++, - "reference", - QT_TRANSLATE_NOOP("Filter", "Spiral iterator 1:16"), - spiral_desc - }; - list[F_SPIRAL_64] = FilterInfo{ - CAT_HELPER, 0, 1, 1, 0, 0, 0, 64, 6, 0, MC_UNDEF, MC_NEWEST, DIM_UNDEF, 0, disp++, - "reference", - QT_TRANSLATE_NOOP("Filter", "Spiral iterator 1:64"), - spiral_desc - }; - list[F_SPIRAL_256] = FilterInfo{ - CAT_HELPER, 0, 1, 1, 0, 0, 0, 256, 8, 0, MC_UNDEF, MC_NEWEST, DIM_UNDEF, 0, disp++, - "reference", - QT_TRANSLATE_NOOP("Filter", "Spiral iterator 1:256"), - spiral_desc - }; - list[F_SPIRAL_512] = FilterInfo{ - CAT_HELPER, 0, 1, 1, 0, 0, 0, 512, 9, 0, MC_UNDEF, MC_NEWEST, DIM_UNDEF, 0, disp++, - "reference", - QT_TRANSLATE_NOOP("Filter", "Spiral iterator 1:512"), - spiral_desc - }; - list[F_SPIRAL_1024] = FilterInfo{ - CAT_HELPER, 0, 1, 1, 0, 0, 0, 1024, 10, 0, MC_UNDEF, MC_NEWEST, DIM_UNDEF, 0, disp++, - "reference", - QT_TRANSLATE_NOOP("Filter", "Spiral iterator 1:1024"), - spiral_desc + "") }; list[F_QH_IDEAL] = FilterInfo{ - CAT_QUAD, 0, 1, 1, 0, 0, Swamp_Hut, 512, 9, 0, MC_1_4, MC_NEWEST, 0, 0, disp++, + CAT_QUAD, 0, LOC_REC, 0, Swamp_Hut, 512, 9, BR_FIRST, MC_1_4, MC_NEWEST, 0, 0, disp++, "quad", QT_TRANSLATE_NOOP("Filter", "Quad-hut (ideal)"), QT_TRANSLATE_NOOP("Filter", @@ -232,7 +207,7 @@ static const struct FilterList }; list[F_QH_CLASSIC] = FilterInfo{ - CAT_QUAD, 0, 1, 1, 0, 0, Swamp_Hut, 512, 9, 0, MC_1_4, MC_NEWEST, 0, 0, disp++, + CAT_QUAD, 0, LOC_REC, 0, Swamp_Hut, 512, 9, BR_FIRST, MC_1_4, MC_NEWEST, 0, 0, disp++, "quad", QT_TRANSLATE_NOOP("Filter", "Quad-hut (classic)"), QT_TRANSLATE_NOOP("Filter", @@ -243,7 +218,7 @@ static const struct FilterList }; list[F_QH_NORMAL] = FilterInfo{ - CAT_QUAD, 0, 1, 1, 0, 0, Swamp_Hut, 512, 9, 0, MC_1_4, MC_NEWEST, 0, 0, disp++, + CAT_QUAD, 0, LOC_REC, 0, Swamp_Hut, 512, 9, BR_FIRST, MC_1_4, MC_NEWEST, 0, 0, disp++, "quad", QT_TRANSLATE_NOOP("Filter", "Quad-hut (normal)"), QT_TRANSLATE_NOOP("Filter", @@ -254,7 +229,7 @@ static const struct FilterList }; list[F_QH_BARELY] = FilterInfo{ - CAT_QUAD, 0, 1, 1, 0, 0, Swamp_Hut, 512, 9, 0, MC_1_4, MC_NEWEST, 0, 0, disp++, + CAT_QUAD, 0, LOC_REC, 0, Swamp_Hut, 512, 9, BR_FIRST, MC_1_4, MC_NEWEST, 0, 0, disp++, "quad", QT_TRANSLATE_NOOP("Filter", "Quad-hut (barely)"), QT_TRANSLATE_NOOP("Filter", @@ -264,7 +239,7 @@ static const struct FilterList }; list[F_QM_95] = FilterInfo{ - CAT_QUAD, 0, 1, 1, 0, 0, Monument, 512, 9, 0, MC_1_8, MC_NEWEST, 0, 0, disp++, + CAT_QUAD, 0, LOC_REC, 0, Monument, 512, 9, BR_FIRST, MC_1_8, MC_NEWEST, 0, 0, disp++, "quad", QT_TRANSLATE_NOOP("Filter", "Quad-ocean-monument (>95%)"), QT_TRANSLATE_NOOP("Filter", @@ -274,7 +249,7 @@ static const struct FilterList }; list[F_QM_90] = FilterInfo{ - CAT_QUAD, 0, 1, 1, 0, 0, Monument, 512, 9, 0, MC_1_8, MC_NEWEST, 0, 0, disp++, + CAT_QUAD, 0, LOC_REC, 0, Monument, 512, 9, BR_FIRST, MC_1_8, MC_NEWEST, 0, 0, disp++, "quad", QT_TRANSLATE_NOOP("Filter", "Quad-ocean-monument (>90%)"), QT_TRANSLATE_NOOP("Filter", @@ -283,8 +258,17 @@ static const struct FilterList "location.") }; + list[F_BIOME_SAMPLE] = FilterInfo{ + CAT_BIOMES, 1, LOC_RAD, 0, 0, 1, 0, BR_SPLIT, MC_B1_7, MC_NEWEST, 0, 1, disp++, + "map", + QT_TRANSLATE_NOOP("Filter", "Biome samples"), + QT_TRANSLATE_NOOP("Filter", + "Samples biomes in a given area to find if a proportion of the " + "biomes match a set of allowed biomes.") + }; + list[F_BIOME] = FilterInfo{ - CAT_BIOMES, 1, 1, 1, 0, L_VORONOI_1, 0, 1, 0, 0, MC_B1_7, MC_1_17, 0, 1, disp++, // disable for 1.18 + CAT_BIOMES, 1, LOC_REC, L_VORONOI_1, 0, 1, 0, BR_NONE, MC_B1_7, MC_1_17, 0, 1, disp++, // disable for 1.18 "map", QT_TRANSLATE_NOOP("Filter", "Biomes 1:1"), QT_TRANSLATE_NOOP("Filter", @@ -293,7 +277,7 @@ static const struct FilterList }; list[F_BIOME_4] = FilterInfo{ - CAT_BIOMES, 1, 1, 1, 0, 0, 0, 4, 2, 0, MC_B1_7, MC_NEWEST, 0, 1, disp++, + CAT_BIOMES, 1, LOC_REC, 0, 0, 4, 2, BR_NONE, MC_B1_7, MC_NEWEST, 0, 1, disp++, "map", QT_TRANSLATE_NOOP("Filter", "Biomes 1:4"), QT_TRANSLATE_NOOP("Filter", @@ -301,7 +285,7 @@ static const struct FilterList "discard those that have biomes that are explicitly excluded (-).") }; list[F_BIOME_16] = FilterInfo{ - CAT_BIOMES, 1, 1, 1, 0, 0, 0, 16, 4, 0, MC_B1_7, MC_NEWEST, 0, 1, disp++, + CAT_BIOMES, 1, LOC_REC, 0, 0, 16, 4, BR_NONE, MC_B1_7, MC_NEWEST, 0, 1, disp++, "map", QT_TRANSLATE_NOOP("Filter", "Biomes 1:16"), QT_TRANSLATE_NOOP("Filter", @@ -309,7 +293,7 @@ static const struct FilterList "discard those that have biomes that are explicitly excluded (-).") }; list[F_BIOME_64] = FilterInfo{ - CAT_BIOMES, 1, 1, 1, 0, 0, 0, 64, 6, 0, MC_B1_7, MC_NEWEST, 0, 1, disp++, + CAT_BIOMES, 1, LOC_REC, 0, 0, 64, 6, BR_NONE, MC_B1_7, MC_NEWEST, 0, 1, disp++, "map", QT_TRANSLATE_NOOP("Filter", "Biomes 1:64"), QT_TRANSLATE_NOOP("Filter", @@ -317,7 +301,7 @@ static const struct FilterList "discard those that have biomes that are explicitly excluded (-).") }; list[F_BIOME_256] = FilterInfo{ - CAT_BIOMES, 1, 1, 1, 0, 0, 0, 256, 8, 0, MC_B1_7, MC_NEWEST, 0, 1, disp++, + CAT_BIOMES, 1, LOC_REC, 0, 0, 256, 8, BR_NONE, MC_B1_7, MC_NEWEST, 0, 1, disp++, "map", QT_TRANSLATE_NOOP("Filter", "Biomes 1:256"), QT_TRANSLATE_NOOP("Filter", @@ -326,7 +310,7 @@ static const struct FilterList }; list[F_BIOME_4_RIVER] = FilterInfo{ - CAT_BIOMES, 1, 1, 1, 0, L_RIVER_MIX_4, 0, 4, 2, 0, MC_1_13, MC_1_17, 0, 0, disp++, + CAT_BIOMES, 1, LOC_REC, L_RIVER_MIX_4, 0, 4, 2, BR_NONE, MC_1_13, MC_1_17, 0, 0, disp++, "map", QT_TRANSLATE_NOOP("Filter", "Biomes 1:4 RIVER"), QT_TRANSLATE_NOOP("Filter", @@ -336,7 +320,7 @@ static const struct FilterList "This layer does not generate ocean variants.") }; list[F_BIOME_256_OTEMP] = FilterInfo{ - CAT_BIOMES, 0, 1, 1, 0, L_OCEAN_TEMP_256, 0, 256, 8, 0, MC_1_13, MC_1_17, 0, 0, disp++, + CAT_BIOMES, 0, LOC_REC, L_OCEAN_TEMP_256, 0, 256, 8, BR_NONE, MC_1_13, MC_1_17, 0, 0, disp++, "map", QT_TRANSLATE_NOOP("Filter", "Biomes 1:256 O.TEMP"), QT_TRANSLATE_NOOP("Filter", @@ -346,7 +330,7 @@ static const struct FilterList "This generation layer depends only on the lower 48-bits of the seed.") }; list[F_CLIMATE_NOISE] = FilterInfo{ - CAT_BIOMES, 0, 1, 1, 0, 0, 0, 4, 2, 0, MC_1_18, MC_NEWEST, 0, 0, disp++, + CAT_BIOMES, 0, LOC_REC, 0, 0, 4, 2, BR_NONE, MC_1_18, MC_NEWEST, 0, 0, disp++, "map", QT_TRANSLATE_NOOP("Filter", "Climate parameters 1:4"), QT_TRANSLATE_NOOP("Filter", @@ -354,28 +338,28 @@ static const struct FilterList "the specified area should cover.") }; list[F_CLIMATE_MINMAX] = FilterInfo{ - CAT_BIOMES, 0, 1, 1, 0, 0, 0, 4, 2, 0, MC_1_18, MC_NEWEST, 0, 0, disp++, + CAT_BIOMES, 0, LOC_REC, 0, 0, 4, 2, BR_NONE, MC_1_18, MC_NEWEST, 0, 0, disp++, "map", QT_TRANSLATE_NOOP("Filter", "Locate climate extreme 1:4"), QT_TRANSLATE_NOOP("Filter", "Finds the location where a climate parameter reaches its minimum or maximum.") }; list[F_BIOME_CENTER] = FilterInfo{ - CAT_BIOMES, 1, 1, 1, 0, 0, 0, 4, 2, 1, MC_B1_7, MC_NEWEST, 0, 1, disp++, + CAT_BIOMES, 1, LOC_REC, 0, 0, 4, 2, BR_CLUST, MC_B1_7, MC_NEWEST, 0, 1, disp++, "map", QT_TRANSLATE_NOOP("Filter", "Locate biome center 1:4"), QT_TRANSLATE_NOOP("Filter", "Finds the center position of a given biome.") }; list[F_BIOME_CENTER_256] = FilterInfo{ - CAT_BIOMES, 1, 1, 1, 0, 0, 0, 256, 8, 1, MC_B1_7, MC_1_17, 0, 1, disp++, + CAT_BIOMES, 1, LOC_REC, 0, 0, 256, 8, BR_CLUST, MC_B1_7, MC_1_17, 0, 1, disp++, "map", QT_TRANSLATE_NOOP("Filter", "Locate biome center 1:256"), QT_TRANSLATE_NOOP("Filter", "Finds the center position of a given biome. Based on the 1:256 biome layer.") }; list[F_TEMPS] = FilterInfo{ - CAT_BIOMES, 1, 1, 1, 0, 0, 0, 1024, 10, 0, MC_1_7, MC_1_17, 0, 0, disp++, + CAT_BIOMES, 1, LOC_REC, 0, 0, 1024, 10, BR_NONE, MC_1_7, MC_1_17, 0, 0, disp++, "tempcat", QT_TRANSLATE_NOOP("Filter", "Temperature categories"), QT_TRANSLATE_NOOP("Filter", @@ -383,35 +367,35 @@ static const struct FilterList }; list[F_BIOME_NETHER_1] = FilterInfo{ - CAT_NETHER, 1, 1, 1, 0, 0, 0, 1, 0, 0, MC_1_16_1, 0, -1, 1, disp++, // disabled + CAT_NETHER, 1, LOC_REC, 0, 0, 1, 0, BR_NONE, MC_1_16_1, 0, -1, 1, disp++, // disabled "nether", QT_TRANSLATE_NOOP("Filter", "Nether biomes 1:1 (disabled)"), QT_TRANSLATE_NOOP("Filter", "Nether biomes after voronoi scaling to 1:1.") }; list[F_BIOME_NETHER_4] = FilterInfo{ - CAT_NETHER, 0, 1, 1, 0, 0, 0, 4, 2, 0, MC_1_16_1, MC_NEWEST, -1, 0, disp++, + CAT_NETHER, 0, LOC_REC, 0, 0, 4, 2, BR_NONE, MC_1_16_1, MC_NEWEST, -1, 0, disp++, "nether", QT_TRANSLATE_NOOP("Filter", "Nether biomes 1:4"), QT_TRANSLATE_NOOP("Filter", "Nether biomes with normal noise sampling at scale 1:4.") }; list[F_BIOME_NETHER_16] = FilterInfo{ - CAT_NETHER, 0, 1, 1, 0, 0, 0, 16, 4, 0, MC_1_16_1, MC_NEWEST, -1, 0, disp++, + CAT_NETHER, 0, LOC_REC, 0, 0, 16, 4, BR_NONE, MC_1_16_1, MC_NEWEST, -1, 0, disp++, "nether", QT_TRANSLATE_NOOP("Filter", "Nether biomes 1:16"), QT_TRANSLATE_NOOP("Filter", "Nether biomes, but only sampled at scale 1:16.") }; list[F_BIOME_NETHER_64] = FilterInfo{ - CAT_NETHER, 0, 1, 1, 0, 0, 0, 64, 6, 0, MC_1_16_1, MC_NEWEST, -1, 0, disp++, + CAT_NETHER, 0, LOC_REC, 0, 0, 64, 6, BR_NONE, MC_1_16_1, MC_NEWEST, -1, 0, disp++, "nether", QT_TRANSLATE_NOOP("Filter", "Nether biomes 1:64"), QT_TRANSLATE_NOOP("Filter", "Nether biomes, but only sampled at scale 1:64.") }; list[F_BIOME_NETHER_256] = FilterInfo{ - CAT_NETHER, 0, 1, 1, 0, 0, 0, 256, 8, 0, MC_1_16_1, MC_NEWEST, -1, 0, disp++, + CAT_NETHER, 0, LOC_REC, 0, 0, 256, 8, BR_NONE, MC_1_16_1, MC_NEWEST, -1, 0, disp++, "nether", QT_TRANSLATE_NOOP("Filter", "Nether biomes 1:256"), QT_TRANSLATE_NOOP("Filter", @@ -419,28 +403,28 @@ static const struct FilterList }; list[F_BIOME_END_1] = FilterInfo{ - CAT_END, 1, 1, 1, 0, 0, 0, 1, 0, 0, MC_1_9, 0, +1, 1, disp++, // disabled + CAT_END, 1, LOC_REC, 0, 0, 1, 0, BR_NONE, MC_1_9, 0, +1, 1, disp++, // disabled "the_end", QT_TRANSLATE_NOOP("Filter", "End biomes 1:1 (disabled)"), QT_TRANSLATE_NOOP("Filter", "End biomes after voronoi scaling to 1:1.") }; list[F_BIOME_END_4] = FilterInfo{ - CAT_END, 0, 1, 1, 0, 0, 0, 4, 2, 0, MC_1_9, MC_NEWEST, +1, 0, disp++, + CAT_END, 0, LOC_REC, 0, 0, 4, 2, BR_NONE, MC_1_9, MC_NEWEST, +1, 0, disp++, "the_end", QT_TRANSLATE_NOOP("Filter", "End biomes 1:4"), QT_TRANSLATE_NOOP("Filter", "End biomes sampled at scale 1:4. Note this is just a simple upscale of 1:16.") }; list[F_BIOME_END_16] = FilterInfo{ - CAT_END, 0, 1, 1, 0, 0, 0, 16, 4, 0, MC_1_9, MC_NEWEST, +1, 0, disp++, + CAT_END, 0, LOC_REC, 0, 0, 16, 4, BR_NONE, MC_1_9, MC_NEWEST, +1, 0, disp++, "the_end", QT_TRANSLATE_NOOP("Filter", "End biomes 1:16"), QT_TRANSLATE_NOOP("Filter", "End biomes with normal sampling at scale 1:16.") }; list[F_BIOME_END_64] = FilterInfo{ - CAT_END, 0, 1, 1, 0, 0, 0, 64, 6, 0, MC_1_9, MC_NEWEST, +1, 0, disp++, + CAT_END, 0, LOC_REC, 0, 0, 64, 6, BR_NONE, MC_1_9, MC_NEWEST, +1, 0, disp++, "the_end", QT_TRANSLATE_NOOP("Filter", "End biomes 1:64"), QT_TRANSLATE_NOOP("Filter", @@ -448,20 +432,20 @@ static const struct FilterList }; list[F_SPAWN] = FilterInfo{ - CAT_OTHER, 1, 1, 1, 1, 0, 0, 1, 0, 0, MC_B1_8, MC_NEWEST, 0, 0, disp++, + CAT_OTHER, 1, LOC_RAD, 0, 0, 1, 0, BR_NONE, MC_B1_8, MC_NEWEST, 0, 0, disp++, "spawn", QT_TRANSLATE_NOOP("Filter", "Spawn"), "" }; list[F_SLIME] = FilterInfo{ - CAT_OTHER, 0, 1, 1, 0, 0, 0, 16, 4, 1, MC_UNDEF, MC_NEWEST, 0, 0, disp++, + CAT_OTHER, 0, LOC_REC, 0, 0, 16, 4, BR_CLUST, MC_UNDEF, MC_NEWEST, 0, 0, disp++, "slime", QT_TRANSLATE_NOOP("Filter", "Slime chunk"), "" }; list[F_HEIGHT] = FilterInfo{ - CAT_OTHER, 0, 1, 0, 0, 0, 0, 4, 2, 0, MC_1_1, MC_NEWEST, 0, 0, disp++, + CAT_OTHER, 0, LOC_POS, 0, 0, 4, 2, BR_NONE, MC_1_1, MC_NEWEST, 0, 0, disp++, "height", QT_TRANSLATE_NOOP("Filter", "Surface height"), QT_TRANSLATE_NOOP("Filter", @@ -469,7 +453,7 @@ static const struct FilterList }; list[F_FIRST_STRONGHOLD] = FilterInfo{ - CAT_OTHER, 0, 1, 1, 1, 0, 0, 1, 0, 0, MC_B1_8, MC_NEWEST, 0, 0, disp++, + CAT_OTHER, 0, LOC_RAD, 0, 0, 1, 0, BR_NONE, MC_B1_8, MC_NEWEST, 0, 0, disp++, "stronghold", QT_TRANSLATE_NOOP("Filter", "First stronghold"), QT_TRANSLATE_NOOP("Filter", @@ -478,28 +462,28 @@ static const struct FilterList }; list[F_STRONGHOLD] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, 0, 1, 0, 1, MC_B1_8, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, LOC_RAD, 0, 0, 1, 0, BR_CLUST, MC_B1_8, MC_NEWEST, 0, 0, disp++, "stronghold", QT_TRANSLATE_NOOP("Filter", "Stronghold"), "" }; list[F_VILLAGE] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Village, 1, 0, 1, MC_B1_8, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, LOC_RAD, 0, Village, 1, 0, BR_CLUST, MC_B1_8, MC_NEWEST, 0, 0, disp++, "village", QT_TRANSLATE_NOOP("Filter", "Village"), "" }; list[F_MINESHAFT] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 0, 0, Mineshaft, 1, 0, 1, MC_B1_8, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, LOC_RAD, 0, Mineshaft, 1, 0, BR_CLUST, MC_B1_8, MC_NEWEST, 0, 0, disp++, "mineshaft", QT_TRANSLATE_NOOP("Filter", "Abandoned mineshaft"), "" }; list[F_DESERT] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Desert_Pyramid, 1, 0, 1, MC_1_3, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, LOC_RAD, 0, Desert_Pyramid, 1, 0, BR_CLUST, MC_1_3, MC_NEWEST, 0, 0, disp++, "desert", QT_TRANSLATE_NOOP("Filter", "Desert pyramid"), QT_TRANSLATE_NOOP("Filter", @@ -508,7 +492,7 @@ static const struct FilterList }; list[F_JUNGLE] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Jungle_Temple, 1, 0, 1, MC_1_3, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, LOC_RAD, 0, Jungle_Temple, 1, 0, BR_CLUST, MC_1_3, MC_NEWEST, 0, 0, disp++, "jungle", QT_TRANSLATE_NOOP("Filter", "Jungle temple"), QT_TRANSLATE_NOOP("Filter", @@ -517,28 +501,28 @@ static const struct FilterList }; list[F_HUT] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Swamp_Hut, 1, 0, 1, MC_1_4, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, LOC_RAD, 0, Swamp_Hut, 1, 0, BR_CLUST, MC_1_4, MC_NEWEST, 0, 0, disp++, "hut", QT_TRANSLATE_NOOP("Filter", "Swamp hut"), "" }; list[F_MONUMENT] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Monument, 1, 0, 1, MC_1_8, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, LOC_RAD, 0, Monument, 1, 0, BR_CLUST, MC_1_8, MC_NEWEST, 0, 0, disp++, "monument", QT_TRANSLATE_NOOP("Filter", "Ocean monument"), "" }; list[F_IGLOO] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Igloo, 1, 0, 1, MC_1_9, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, LOC_RAD, 0, Igloo, 1, 0, BR_CLUST, MC_1_9, MC_NEWEST, 0, 0, disp++, "igloo", QT_TRANSLATE_NOOP("Filter", "Igloo"), "" }; list[F_MANSION] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Mansion, 1, 0, 1, MC_1_11, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, LOC_RAD, 0, Mansion, 1, 0, BR_CLUST, MC_1_11, MC_NEWEST, 0, 0, disp++, "mansion", QT_TRANSLATE_NOOP("Filter", "Woodland mansion"), QT_TRANSLATE_NOOP("Filter", @@ -547,21 +531,21 @@ static const struct FilterList }; list[F_RUINS] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Ocean_Ruin, 1, 0, 1, MC_1_13, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, LOC_RAD, 0, Ocean_Ruin, 1, 0, BR_CLUST, MC_1_13, MC_NEWEST, 0, 0, disp++, "ruins", QT_TRANSLATE_NOOP("Filter", "Ocean ruins"), "" }; list[F_SHIPWRECK] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Shipwreck, 1, 0, 1, MC_1_13, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, LOC_RAD, 0, Shipwreck, 1, 0, BR_CLUST, MC_1_13, MC_NEWEST, 0, 0, disp++, "shipwreck", QT_TRANSLATE_NOOP("Filter", "Shipwreck"), "" }; list[F_TREASURE] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Treasure, 1, 0, 1, MC_1_13, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, LOC_RAD, 0, Treasure, 1, 0, BR_CLUST, MC_1_13, MC_NEWEST, 0, 0, disp++, "treasure", QT_TRANSLATE_NOOP("Filter", "Buried treasure"), QT_TRANSLATE_NOOP("Filter", @@ -571,70 +555,70 @@ static const struct FilterList }; list[F_WELL] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Desert_Well, 1, 0, 1, MC_1_13, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, LOC_RAD, 0, Desert_Well, 1, 0, BR_CLUST, MC_1_13, MC_NEWEST, 0, 0, disp++, "well", QT_TRANSLATE_NOOP("Filter", "Desert well"), "" }; list[F_OUTPOST] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Outpost, 1, 0, 1, MC_1_14, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, LOC_RAD, 0, Outpost, 1, 0, BR_CLUST, MC_1_14, MC_NEWEST, 0, 0, disp++, "outpost", QT_TRANSLATE_NOOP("Filter", "Pillager outpost"), "" }; list[F_ANCIENT_CITY] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Ancient_City, 1, 0, 1, MC_1_19, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, LOC_RAD, 0, Ancient_City, 1, 0, BR_CLUST, MC_1_19, MC_NEWEST, 0, 0, disp++, "ancient_city", QT_TRANSLATE_NOOP("Filter", "Ancient city"), "" }; list[F_TRAILS] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Trail_Ruin, 1, 0, 1, MC_1_20, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, LOC_RAD, 0, Trail_Ruin, 1, 0, BR_CLUST, MC_1_20, MC_NEWEST, 0, 0, disp++, "trails", QT_TRANSLATE_NOOP("Filter", "Trail ruins"), "" }; list[F_PORTAL] = FilterInfo{ - CAT_STRUCT, 0, 1, 1, 1, 0, Ruined_Portal, 1, 0, 1, MC_1_16_1, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 0, LOC_RAD, 0, Ruined_Portal, 1, 0, BR_CLUST, MC_1_16_1, MC_NEWEST, 0, 0, disp++, "portal", QT_TRANSLATE_NOOP("Filter", "Ruined portal (overworld)"), "" }; list[F_PORTALN] = FilterInfo{ - CAT_STRUCT, 0, 1, 1, 1, 0, Ruined_Portal_N, 1, 0, 1, MC_1_16_1, MC_NEWEST, -1, 0, disp++, + CAT_STRUCT, 0, LOC_RAD, 0, Ruined_Portal_N, 1, 0, BR_CLUST, MC_1_16_1, MC_NEWEST, -1, 0, disp++, "portal", QT_TRANSLATE_NOOP("Filter", "Ruined portal (nether)"), "" }; list[F_FORTRESS] = FilterInfo{ - CAT_STRUCT, 0, 1, 1, 1, 0, Fortress, 1, 0, 1, MC_1_0, MC_NEWEST, -1, 0, disp++, + CAT_STRUCT, 0, LOC_RAD, 0, Fortress, 1, 0, BR_CLUST, MC_1_0, MC_NEWEST, -1, 0, disp++, "fortress", QT_TRANSLATE_NOOP("Filter", "Nether fortress"), "" }; list[F_BASTION] = FilterInfo{ - CAT_STRUCT, 0, 1, 1, 1, 0, Bastion, 1, 0, 1, MC_1_16_1, MC_NEWEST, -1, 0, disp++, + CAT_STRUCT, 0, LOC_RAD, 0, Bastion, 1, 0, BR_CLUST, MC_1_16_1, MC_NEWEST, -1, 0, disp++, "bastion", QT_TRANSLATE_NOOP("Filter", "Bastion remnant"), "" }; list[F_ENDCITY] = FilterInfo{ - CAT_STRUCT, 0, 1, 1, 1, 0, End_City, 1, 0, 1, MC_1_9, MC_NEWEST, +1, 0, disp++, + CAT_STRUCT, 0, LOC_RAD, 0, End_City, 1, 0, BR_CLUST, MC_1_9, MC_NEWEST, +1, 0, disp++, "endcity", QT_TRANSLATE_NOOP("Filter", "End city"), "" }; list[F_GATEWAY] = FilterInfo{ - CAT_STRUCT, 0, 1, 1, 1, 0, End_Gateway, 1, 0, 1, MC_1_13, MC_NEWEST, +1, 0, disp++, + CAT_STRUCT, 0, LOC_RAD, 0, End_Gateway, 1, 0, BR_CLUST, MC_1_13, MC_NEWEST, +1, 0, disp++, "gateway", QT_TRANSLATE_NOOP("Filter", "End gateway"), QT_TRANSLATE_NOOP("Filter", @@ -656,7 +640,8 @@ struct /*__attribute__((packed))*/ Condition VER_2_3_0 = 1, VER_2_4_0 = 2, VER_3_4_0 = 3, - VER_CURRENT = VER_3_4_0, + VER_4_0_0 = 4, + VER_CURRENT = VER_4_0_0, }; enum { // meta flags DISABLED = 0x0001, @@ -699,7 +684,7 @@ struct /*__attribute__((packed))*/ Condition uint8_t minmax; uint8_t para; uint8_t octave; - uint8_t pad2[2]; // legacy zero initialized + uint16_t step; uint16_t version; // condition data version uint64_t biomeToExcl, biomeToExclM; // exclusion biomes int32_t temps[9]; @@ -714,6 +699,8 @@ struct /*__attribute__((packed))*/ Condition int32_t limex[NP_MAX][2]; float vmin; float vmax; + float converage; + float confidence; // generated members - initialized when the search is started uint8_t generated_start[0]; // address dummy @@ -732,7 +719,7 @@ struct /*__attribute__((packed))*/ Condition }; static_assert( - offsetof(Condition, generated_start) == 312, + offsetof(Condition, generated_start) == 320, "Layout of Condition has changed!" ); diff --git a/src/searchthread.cpp b/src/searchthread.cpp index e7ead2d..fc7449d 100644 --- a/src/searchthread.cpp +++ b/src/searchthread.cpp @@ -261,7 +261,7 @@ bool SearchMaster::set(QWidget *widget, const Session& s) { Generator tmp; setupGenerator(&tmp, s.wi.mc, 0); - const Layer *l = getLayerForScale(&tmp, finfo.step); + const Layer *l = getLayerForScale(&tmp, finfo.grid); if (l) layerId = l - tmp.ls.layers; } @@ -281,12 +281,12 @@ bool SearchMaster::set(QWidget *widget, const Session& s) } if (c.type == F_TEMPS) { - int w = c.x2 - c.x1 + 1; - int h = c.z2 - c.z1 + 1; + int w = (c.x2 >> 10) - (c.x1 >> 10) + 1; + int h = (c.z2 >> 10) - (c.z1 >> 10) + 1; if (c.count > w * h) { QString msg = tr("Temperature category condition with ID %1 has too " - "many restrictions (%2) for the area (%3 x %4)."); + "many restrictions (%2) for the area (%3 x %4 @ scale 1:1024)."); warn(widget, msg.arg(cid).arg(c.count).arg(w).arg(h)); return false; } @@ -733,6 +733,7 @@ void SearchMaster::stop() } } workers.clear(); + emit searchFinish(false); } diff --git a/src/tabbiomes.cpp b/src/tabbiomes.cpp index 73b0d32..7c099ea 100644 --- a/src/tabbiomes.cpp +++ b/src/tabbiomes.cpp @@ -836,17 +836,14 @@ void TabBiomes::on_pushExport_clicked() void TabBiomes::on_buttonFromVisible_clicked() { MapView *mapview = parent->getMapView(); - qreal uiw = mapview->width() * mapview->getScale(); - qreal uih = mapview->height() * mapview->getScale(); - int bx0 = (int) floor(mapview->getX() - uiw/2); - int bz0 = (int) floor(mapview->getZ() - uih/2); - int bx1 = (int) ceil(mapview->getX() + uiw/2); - int bz1 = (int) ceil(mapview->getZ() + uih/2); - ui->lineX1->setText( QString::number(bx0) ); - ui->lineZ1->setText( QString::number(bz0) ); - ui->lineX2->setText( QString::number(bx1) ); - ui->lineZ2->setText( QString::number(bz1) ); + int x1, z1, x2, z2; + mapview->getVisible(&x1, &z1, &x2, &z2); + + ui->lineX1->setText( QString::number(x1) ); + ui->lineZ1->setText( QString::number(z1) ); + ui->lineX2->setText( QString::number(x2) ); + ui->lineZ2->setText( QString::number(z2) ); } void TabBiomes::on_radioFullSample_toggled(bool checked) diff --git a/src/tablocations.cpp b/src/tablocations.cpp new file mode 100644 index 0000000..02906a4 --- /dev/null +++ b/src/tablocations.cpp @@ -0,0 +1,437 @@ +#include "tablocations.h" +#include "ui_tablocations.h" + +#include "message.h" +#include "util.h" +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#define SAMPLES_MAX 9999999 + +static +QTreeWidgetItem *setConditionTreeItems(ConditionTree& ctree, int node, int64_t seed, Pos cpos[], QTreeWidgetItem* parent, bool posval) +{ + Condition& c = ctree.condvec[node]; + Pos p = cpos[c.save]; + const std::vector& branches = ctree.references[c.save]; + QTreeWidgetItem* item; + + if (c.type == 0) + { + item = parent; + } + else + { + item = new QTreeWidgetItem(parent); + item->setText(0, c.summary(false)); + + if ((p.x == -1 && p.z == -1) || c.type == F_LOGIC_NOT) + posval = false; + if (posval) + { + const FilterInfo& finfo = g_filterinfo.list[c.type]; + double dist = sqrt((double)p.x*p.x + (double)p.z*p.z); + item->setText(1, QString::number(p.x)); + item->setText(2, QString::number(p.z)); + item->setText(3, QString::asprintf("%.0f", dist)); + item->setData(0, Qt::UserRole+0, QVariant::fromValue(seed)); + item->setData(0, Qt::UserRole+1, QVariant::fromValue(finfo.dim)); + item->setData(0, Qt::UserRole+2, QVariant::fromValue(p)); + } + } + if (!branches.empty()) + { + for (char b : branches) + setConditionTreeItems(ctree, b, seed, cpos, item, posval); + } + return item; +} + +QString AnalysisLocations::set(WorldInfo wi, const QVector& conds) +{ + this->wi = wi; + QString err = condtree.set(conds, wi.mc); + if (err.isEmpty()) + err = env.init(wi.mc, wi.large, &condtree); + if (!err.isEmpty()) + env.setSeed(wi.seed); + return err; +} + +void AnalysisLocations::run() +{ + stop = false; + + for (idx = 0; idx < (long)pos.size(); idx++) + { + if (stop) break; + + Pos at = pos[idx.load()]; + Pos cpos[MAX_INSTANCES] = {}; + if (testTreeAt(at, &env, PASS_FULL_64, &stop, cpos) + != COND_OK) + { + continue; + } + + double dist = sqrt((double)at.x*at.x + (double)at.z*at.z); + + QTreeWidgetItem *item = new QTreeWidgetItem(); + item->setText(0, tr("origin")); + item->setText(1, QString::number(at.x)); + item->setText(2, QString::number(at.z)); + item->setText(3, QString::asprintf("%.0f", dist)); + item->setData(0, Qt::UserRole+0, QVariant::fromValue(env.seed)); + item->setData(0, Qt::UserRole+1, QVariant::Invalid); + item->setData(0, Qt::UserRole+2, QVariant::fromValue(at)); + + setConditionTreeItems(condtree, 0, env.seed, cpos, item, true); + emit itemDone(item); + } +} + + +enum { SMODE_RADIAL_GRID, SMODE_SQUARE_SPIRAL, SMODE_NORMAL }; + +TabLocations::TabLocations(MainWindow *parent) + : QWidget(parent) + , ui(new Ui::TabLocations) + , parent(parent) + , thread() + , timer() + , maxresults(1) + , nextupdate() + , updt(20) +{ + ui->setupUi(this); + ui->treeWidget->setSortingEnabled(false); // sortable triggers are not necessary + + ui->comboSampling->addItem(tr("Lattice points in radial order") + ", ||α·n, α·m|| < r", SMODE_RADIAL_GRID); + ui->comboSampling->addItem(tr("Square spiral") + ", (α·n, α·m)", SMODE_SQUARE_SPIRAL); + ui->comboSampling->addItem(tr("Random Gaussian samples") + ", (α·norm(), α·norm())", SMODE_NORMAL); + + ui->lineN->setValidator(new QIntValidator(1, SAMPLES_MAX, this)); + ui->lineA->setValidator(new QIntValidator(1, (int)3e7, this)); + ui->lineX->setValidator(new QIntValidator((int)-3e7, (int)3e7, this)); + ui->lineZ->setValidator(new QIntValidator((int)-3e7, (int)3e7, this)); + + connect(&thread, &AnalysisLocations::warning, this, &TabLocations::warning, Qt::BlockingQueuedConnection); + connect(&thread, &AnalysisLocations::itemDone, this, &TabLocations::onAnalysisItemDone, Qt::BlockingQueuedConnection); + connect(&thread, &AnalysisLocations::finished, this, &TabLocations::onAnalysisFinished); + + connect(&timer, &QTimer::timeout, this, QOverload<>::of(&TabLocations::onProgressTimeout)); +} + +TabLocations::~TabLocations() +{ + timer.stop(); + thread.stop = true; + thread.wait(500); + delete ui; +} + +bool TabLocations::event(QEvent *e) +{ + if (e->type() == QEvent::LayoutRequest) + { + QFontMetrics fm = QFontMetrics(ui->treeWidget->font()); + ui->treeWidget->setColumnWidth(0, fm.horizontalAdvance('#') * 32); + ui->treeWidget->setColumnWidth(1, fm.horizontalAdvance('#') * 9); + ui->treeWidget->setColumnWidth(2, fm.horizontalAdvance('#') * 9); + ui->treeWidget->setColumnWidth(3, fm.horizontalAdvance('#') * 9); + } + return QWidget::event(e); +} + +void TabLocations::save(QSettings& settings) +{ + settings.setValue("analysis/samplemode", ui->comboSampling->currentData().toInt()); + settings.setValue("analysis/samplecnt", ui->lineN->text().toInt()); + settings.setValue("analysis/samplesep", ui->lineA->text().toInt()); + settings.setValue("analysis/offx", ui->lineX->text().toInt()); + settings.setValue("analysis/offz", ui->lineZ->text().toInt()); +} + +static void loadLine(QSettings *s, QLineEdit *line, const char *key) +{ + qlonglong x = line->text().toLongLong(); + line->setText( QString::number(s->value(key, x).toLongLong()) ); +} +void TabLocations::load(QSettings& settings) +{ + loadLine(&settings, ui->lineN, "analysis/samplecnt"); + loadLine(&settings, ui->lineA, "analysis/samplesep"); + loadLine(&settings, ui->lineX, "analysis/offx"); + loadLine(&settings, ui->lineZ, "analysis/offz"); + QVariant mode = settings.value("analysis/samplemode", ui->comboSampling->currentData()); + ui->comboSampling->setCurrentIndex(ui->comboSampling->findData(mode)); + + maxresults = settings.value("config/maxMatching", maxresults).toInt(); +} + +int TabLocations::warning(QString text, QMessageBox::StandardButtons buttons) +{ + return warn(parent, text, buttons); +} + +void TabLocations::onAnalysisItemDone(QTreeWidgetItem *item) +{ + if (qbuf.size() + ui->treeWidget->topLevelItemCount() >= maxresults) + { + thread.stop = true; + } + + qbuf.push_back(item); + quint64 ns = elapsed.nsecsElapsed(); + if (ns > nextupdate) + { + nextupdate = ns + updt * 1e6; + QTimer::singleShot(updt, this, &TabLocations::onBufferTimeout); + } +} + +void TabLocations::onAnalysisFinished() +{ + timer.stop(); + onBufferTimeout(); + ui->pushExport->setEnabled(ui->treeWidget->topLevelItemCount() > 0); + ui->pushStart->setChecked(false); + ui->pushStart->setText(tr("Analyze")); +} + +void TabLocations::onBufferTimeout() +{ + uint64_t t = -elapsed.elapsed(); + + if (!qbuf.empty()) + { + ui->treeWidget->setUpdatesEnabled(false); + ui->treeWidget->addTopLevelItems(qbuf); + ui->treeWidget->setUpdatesEnabled(true); + qbuf.clear(); + onProgressTimeout(); + } + + QApplication::processEvents(); // force processing of events so we can time correctly + + t += elapsed.elapsed(); + if (8*t > updt) + updt = 4*t; + nextupdate = elapsed.nsecsElapsed() + 1e6 * updt; +} + +void TabLocations::onProgressTimeout() +{ + QString progress = QString::asprintf(" (%ld/%zu)", thread.idx.load(), thread.pos.size()); + ui->pushStart->setText(tr("Stop") + progress); +} + +void TabLocations::on_pushStart_clicked() +{ + if (thread.isRunning()) + { + thread.stop = true; + return; + } + updt = 20; + nextupdate = 0; + elapsed.start(); + + thread.pos.clear(); + int mode = ui->comboSampling->currentData().toInt(); + int a = ui->lineA->text().toInt(); + uint64_t n = ui->lineN->text().toULongLong(); + int x0 = ui->lineX->text().toInt(); + int z0 = ui->lineZ->text().toInt(); + + if (a == 0) + return; + if (n > SAMPLES_MAX) + return; + + WorldInfo wi; + parent->getSeed(&wi); + QVector conds = parent->formCond->getConditions(); + + QString err = thread.set(wi, conds); + if (!err.isEmpty()) + { + emit warning(err, QMessageBox::Ok); + return; + } + + if (mode == SMODE_RADIAL_GRID) + { + struct P { + int x, z; + uint64_t rsq; + bool operator< (const P& x) const { return rsq < x.rsq; } + }; + std::vector

v; + uint64_t rsqmax = (uint64_t) ceil(n / M_PI); + int64_t r = (int64_t) sqrt(rsqmax); + + for (int64_t x = -r; x <= r; x++) + { + for (int64_t z = -r; z <= r; z++) + { + uint64_t rsq = (uint64_t)(x * x + z * z); + if (rsq <= rsqmax) + v.push_back(P{(int)x, (int)z, rsq}); + } + } + std::sort(v.begin(), v.end()); + for (uint64_t i = 0; i < n && i < v.size(); i++) + { + Pos p = { a*v[i].x + x0, a*v[i].z + z0}; + thread.pos.push_back(p); + } + } + else if (mode == SMODE_SQUARE_SPIRAL) + { + int rx = 0; + int rz = 0; + int i = 0, dl = 1; + int dx = 1, dz = 0; + for (uint64_t j = 0; j < n; j++) + { + Pos p = { a*rx + x0, a*rz + z0 }; + thread.pos.push_back(p); + rx += dx; + rz += dz; + if (++i == dl) + { + i = 0; + int tmp = dx; + dx = -dz; + dz = tmp; + if (dz == 0) + dl++; + } + } + } + else if (mode == SMODE_NORMAL) + { + thread_local RandGen rng; + std::normal_distribution norm = std::normal_distribution(0.0, 1.0); + + for (uint64_t j = 0; j < n; j++) + { + int x = (int) round( a * norm(rng.mt) ); + int z = (int) round( a * norm(rng.mt) ); + Pos p = { x + x0, z + z0 }; + thread.pos.push_back(p); + } + } + + //ui->treeWidget->setSortingEnabled(false); + while (ui->treeWidget->topLevelItemCount() > 0) + delete ui->treeWidget->takeTopLevelItem(0); + + ui->pushExport->setEnabled(false); + ui->pushStart->setChecked(true); + QString progress = QString::asprintf(" (0/%zu)", thread.pos.size()); + ui->pushStart->setText(tr("Stop") + progress); + timer.start(250); + thread.start(); +} + +void TabLocations::on_treeWidget_itemClicked(QTreeWidgetItem *item, int column) +{ + (void) column; + QVariant dat; + dat = item->data(0, Qt::UserRole+0); + if (dat.isValid()) + { + uint64_t seed = qvariant_cast(dat); + dat = item->data(0, Qt::UserRole+1); + int dim = dat.isValid() ? dat.toInt() : DIM_UNDEF; + WorldInfo wi; + parent->getSeed(&wi); + wi.seed = seed; + parent->setSeed(wi, dim); + } + + dat = item->data(0, Qt::UserRole+2); + if (dat.isValid()) + { + Pos p = qvariant_cast(dat); + parent->getMapView()->setView(p.x+0.5, p.z+0.5); + } +} + +void TabLocations::on_pushExpand_clicked() +{ + bool expand = false; + for (QTreeWidgetItemIterator it(ui->treeWidget); *it; ++it) + if (!(*it)->isExpanded()) + expand = true; + if (expand) + ui->treeWidget->expandAll(); + else + ui->treeWidget->collapseAll(); +} + +static +void csvline(QTextStream& stream, const QString& qte, const QString& sep, QStringList& cols) +{ + if (qte.isEmpty()) + { + for (QString& s : cols) + if (s.contains(sep)) + s = "\"" + s + "\""; + } + stream << qte << cols.join(sep) << qte << "\n"; +} + +void TabLocations::on_pushExport_clicked() +{ + QString fnam = QFileDialog::getSaveFileName( + this, tr("Export trigger analysis"), parent->prevdir, tr("Text files (*.txt *csv);;Any files (*)")); + if (fnam.isEmpty()) + return; + + QFileInfo finfo(fnam); + QFile file(fnam); + parent->prevdir = finfo.absolutePath(); + + if (!file.open(QIODevice::WriteOnly)) + { + warn(parent, tr("Failed to open file for export:\n\"%1\"").arg(fnam)); + return; + } + + QString qte = parent->config.quote; + QString sep = parent->config.separator; + + QTextStream stream(&file); + stream << "Sep=" + sep + "\n"; + sep = qte + sep + qte; + + QStringList header = { tr("condition"), tr("x"), tr("z") }; + csvline(stream, qte, sep, header); + + QTreeWidgetItemIterator it(ui->treeWidget); + for (; *it; ++it) + { + QTreeWidgetItem *item = *it; + QStringList cols; + for (int i = 0, n = item->columnCount(); i < n; i++) + { + QString s = item->text(i); + if (s == "-") s = ""; + cols.append(s); + } + csvline(stream, qte, sep, cols); + } +} + diff --git a/src/tablocations.h b/src/tablocations.h new file mode 100644 index 0000000..6dcb9e0 --- /dev/null +++ b/src/tablocations.h @@ -0,0 +1,78 @@ +#ifndef TABLOCATIONS_H +#define TABLOCATIONS_H + +#include +#include +#include + +#include "mainwindow.h" +#include "search.h" +#include "world.h" + +namespace Ui { +class TabLocations; +} + +class AnalysisLocations : public QThread +{ + Q_OBJECT +public: + explicit AnalysisLocations(QObject *parent = nullptr) + : QThread(parent), wi(),stop(),idx() {} + + QString set(WorldInfo wi, const QVector& conds); + + virtual void run() override; + +signals: + void itemDone(QTreeWidgetItem *item); + int warning(QString text, QMessageBox::StandardButtons buttons); + +public: + WorldInfo wi; + ConditionTree condtree; + SearchThreadEnv env; + std::vector pos; + std::atomic_bool stop; + std::atomic_long idx; +}; + +class TabLocations : public QWidget, public ISaveTab +{ + Q_OBJECT + +public: + explicit TabLocations(MainWindow *parent = nullptr); + ~TabLocations(); + + virtual bool event(QEvent *e) override; + + virtual void save(QSettings& settings) override; + virtual void load(QSettings& settings) override; + +private slots: + int warning(QString text, QMessageBox::StandardButtons buttons); + void onAnalysisItemDone(QTreeWidgetItem *item); + void onAnalysisFinished(); + void onBufferTimeout(); + void onProgressTimeout(); + + void on_pushStart_clicked(); + void on_pushExpand_clicked(); + void on_pushExport_clicked(); + void on_treeWidget_itemClicked(QTreeWidgetItem *item, int column); + +private: + Ui::TabLocations *ui; + MainWindow *parent; + AnalysisLocations thread; + QTimer timer; + int maxresults; + + QElapsedTimer elapsed; + uint64_t nextupdate; + uint64_t updt; + QList qbuf; +}; + +#endif // TABLOCATIONS_H diff --git a/src/tablocations.ui b/src/tablocations.ui new file mode 100644 index 0000000..65da83d --- /dev/null +++ b/src/tablocations.ui @@ -0,0 +1,175 @@ + + + TabLocations + + + Form + + + + + + Centered on: + + + + + + + Sampling strategy: + + + + + + + <html><head/><body><p>X:</p></body></html> + + + + + + + 512 + + + + + + + + 0 + 0 + + + + + + + + + + + + Monospace + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::NoDragDrop + + + 12 + + + true + + + false + + + 100 + + + + condition + + + + Monospace + + + + + + x + + + + Monospace + + + + + + z + + + + Monospace + + + + + + distance + + + + + + + + + + + + + false + + + Export... + + + + + + + Expand all + + + + + + + Analyze + + + + + + + + + Z: + + + + + + + Spread (α): + + + + + + + 1000 + + + + + + + Number of samples: + + + + + + + + diff --git a/src/tabstructures.cpp b/src/tabstructures.cpp index c7c3906..b94437b 100644 --- a/src/tabstructures.cpp +++ b/src/tabstructures.cpp @@ -607,17 +607,14 @@ void TabStructures::on_pushExport_clicked() void TabStructures::on_buttonFromVisible_clicked() { MapView *mapview = parent->getMapView(); - qreal uiw = mapview->width() * mapview->getScale(); - qreal uih = mapview->height() * mapview->getScale(); - int bx0 = (int) floor(mapview->getX() - uiw/2); - int bz0 = (int) floor(mapview->getZ() - uih/2); - int bx1 = (int) ceil(mapview->getX() + uiw/2); - int bz1 = (int) ceil(mapview->getZ() + uih/2); - ui->lineX1->setText( QString::number(bx0) ); - ui->lineZ1->setText( QString::number(bz0) ); - ui->lineX2->setText( QString::number(bx1) ); - ui->lineZ2->setText( QString::number(bz1) ); + int x1, z1, x2, z2; + mapview->getVisible(&x1, &z1, &x2, &z2); + + ui->lineX1->setText( QString::number(x1) ); + ui->lineZ1->setText( QString::number(z1) ); + ui->lineX2->setText( QString::number(x2) ); + ui->lineZ2->setText( QString::number(z2) ); } void TabStructures::on_tabWidget_currentChanged(int) diff --git a/src/tabtriggers.cpp b/src/tabtriggers.cpp index 42783ba..23b0cda 100644 --- a/src/tabtriggers.cpp +++ b/src/tabtriggers.cpp @@ -136,15 +136,12 @@ bool TabTriggers::event(QEvent *e) return QWidget::event(e); } -void TabTriggers::save(QSettings& settings) +void TabTriggers::save(QSettings&) { - settings.setValue("analysis/seedsrc", ui->comboSeedSource->currentIndex()); } -void TabTriggers::load(QSettings& settings) +void TabTriggers::load(QSettings&) { - int idx = settings.value("analysis/seedsrc", ui->comboSeedSource->currentIndex()).toInt(); - ui->comboSeedSource->setCurrentIndex(idx); } int TabTriggers::warning(QString text, QMessageBox::StandardButtons buttons) @@ -251,7 +248,14 @@ void TabTriggers::on_treeWidget_itemClicked(QTreeWidgetItem *item, int column) void TabTriggers::on_pushExpand_clicked() { - ui->treeWidget->expandAll(); + bool expand = false; + for (QTreeWidgetItemIterator it(ui->treeWidget); *it; ++it) + if (!(*it)->isExpanded()) + expand = true; + if (expand) + ui->treeWidget->expandAll(); + else + ui->treeWidget->collapseAll(); } static diff --git a/src/tabtriggers.ui b/src/tabtriggers.ui index b32190e..c6d1a78 100644 --- a/src/tabtriggers.ui +++ b/src/tabtriggers.ui @@ -83,7 +83,7 @@ - Analyze the search condition triggers. + Examine how the conditions are evaluated. diff --git a/src/util.cpp b/src/util.cpp index c826fed..295b225 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -160,30 +160,24 @@ QString getBiomeDisplay(int mc, int id) return name ? name : ""; } +RandGen::RandGen() +{ + std::random_device rd; + if (rd.entropy()) + mt = std::mt19937_64(rd()); + else + mt = std::mt19937_64(time(0)); +} + // get a random 64-bit integer uint64_t getRnd64() { static QMutex mutex; - static std::random_device rd; - static std::mt19937_64 mt(rd()); - static uint64_t x = (uint64_t) time(0); + static RandGen rng; uint64_t ret = 0; mutex.lock(); - if (rd.entropy()) - { - std::uniform_int_distribution d; - ret = d(mt); - } - else - { - const uint64_t c = 0xd6e8feb86659fd93ULL; - x ^= x >> 32; - x *= c; - x ^= x >> 32; - x *= c; - x ^= x >> 32; - ret = x; - } + std::uniform_int_distribution d; + ret = d(rng.mt); mutex.unlock(); return ret; } diff --git a/src/util.h b/src/util.h index 3a05a08..1cfc475 100644 --- a/src/util.h +++ b/src/util.h @@ -64,6 +64,12 @@ QString getStartPieceName(int stype, const StructureVariant *sv); QString getBiomeDisplay(int mc, int id); +struct RandGen +{ + RandGen(); + std::mt19937_64 mt; +}; + // get a random 64-bit integer uint64_t getRnd64(); diff --git a/src/world.cpp b/src/world.cpp index 9ddde9d..b999653 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -1224,6 +1224,8 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz, painter.drawImage(rec, slimeimg); } + // draw bounding boxes and shapes + painter.setPen(QPen(QColor(192, 0, 0, 160), 1)); if (showBB && blocks2pix >= 1.0 && qsinfo && dim == 0) { @@ -1237,7 +1239,6 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz, qreal x = vw/2.0 + (qi.afk.x - focusx) * blocks2pix; qreal y = vh/2.0 + (qi.afk.z - focusz) * blocks2pix; qreal r = 128.0 * blocks2pix; - painter.setPen(QPen(QColor(192, 0, 0, 160), 1)); painter.drawEllipse(QRectF(x-r, y-r, 2*r, 2*r)); r = 16; painter.drawLine(QPointF(x-r,y), QPointF(x+r,y)); @@ -1245,6 +1246,29 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz, } } + for (Shape& s : shapes) + { + if (s.dim != dim) + continue; + qreal x1 = vw/2.0 + (s.p1.x - focusx) * blocks2pix; + qreal y1 = vh/2.0 + (s.p1.z - focusz) * blocks2pix; + qreal x2 = vw/2.0 + (s.p2.x - focusx) * blocks2pix; + qreal y2 = vh/2.0 + (s.p2.z - focusz) * blocks2pix; + qreal r = s.r * blocks2pix; + switch (s.type) + { + case Shape::RECT: + painter.drawRect(QRectF(x1, y1, x2-x1, y2-y1)); + break; + case Shape::LINE: + painter.drawLine(QLineF(x1, y1, x2, y2)); + break; + case Shape::CIRCLE: + painter.drawEllipse(QPointF(x1, y1), r, r); + break; + } + } + for (int sopt = D_DESERT; sopt < D_SPAWN; sopt++) { Level& l = lvs[sopt]; diff --git a/src/world.h b/src/world.h index fb71930..51750e4 100644 --- a/src/world.h +++ b/src/world.h @@ -121,6 +121,14 @@ struct PosElement Pos p; }; +struct Shape +{ + enum { RECT, LINE, CIRCLE } type; + int dim; + int r; + Pos p1, p2; +}; + class MapWorker : public QThread { Q_OBJECT @@ -206,6 +214,9 @@ public: QImage slimeimg; long slimex, slimez; + // shapes to overlay + std::vector shapes; + // structure selection from mouse position bool seldo; qreal selx, selz;