From 08bc6b44166c2dce84ba4aa29ae33f4adeca9f3b Mon Sep 17 00:00:00 2001 From: Cubitect Date: Fri, 29 Jul 2022 20:29:39 +0200 Subject: [PATCH] Feature changes v2.3.0 1) fixed broken system theme on linux since last version (#140) 2) fixed map artifacts at a large zoom (#135) 3) fixed UI freeze when using 48-bit block search with strongly restrictive conditions (#145) 4) fixed analysis of condition trigger not giving proper insight about its activation 5) changed area input to allow flipped coodinates (#133) 6) changed seed type indicator so it is hidden for number input (#149) 7) changed the default scale at which structures are visible so they show up on a coordinate reset (#112) 8) added customization option for the scale at which structures become visible 9) added map loading indicator 10) added "ignore reference" option to exclude the source instance in relative conditions 11) added match-any biome search for 1.17- (#130) 12) added NOT logic gate helper condition (#138) 13) added quick access to biome color editor and structure zoom to preferences dialog --- README.md | 19 +++ cubiomes | 2 +- cubiomes-viewer.pro | 21 ++-- src/aboutdialog.h | 4 +- src/biomecolordialog.cpp | 12 +- src/biomecolordialog.h | 9 +- src/conditiondialog.cpp | 168 +++++++------------------ src/conditiondialog.ui | 128 ++++++++++--------- src/configdialog.cpp | 27 +++- src/configdialog.h | 5 + src/configdialog.ui | 257 +++++++++++++++++++++----------------- src/exportdialog.cpp | 24 ++-- src/formconditions.cpp | 6 +- src/formsearchcontrol.cpp | 5 +- src/main.cpp | 11 ++ src/mainwindow.cpp | 132 ++++++++++++-------- src/mainwindow.h | 6 +- src/mainwindow.ui | 123 +++++++++++------- src/mapview.cpp | 25 +++- src/mapview.h | 1 + src/quad.cpp | 193 ++++++++++++++++------------ src/quad.h | 20 ++- src/search.cpp | 210 ++++++++++++++++++++++++------- src/search.h | 166 ++++++++++++++---------- src/searchthread.cpp | 43 +++++-- src/searchthread.h | 48 +++---- src/structuredialog.cpp | 76 +++++++++++ src/structuredialog.h | 35 ++++++ src/structuredialog.ui | 69 ++++++++++ 29 files changed, 1178 insertions(+), 667 deletions(-) create mode 100644 src/structuredialog.cpp create mode 100644 src/structuredialog.h create mode 100644 src/structuredialog.ui diff --git a/README.md b/README.md index 35a748f..a4146f1 100644 --- a/README.md +++ b/README.md @@ -19,3 +19,22 @@ Non-PC platforms, such as macOS, are not formally supported, but you can check [ Build instructions can be found in the [buildguide](buildguide.md). + +## Primary Features Overview + +The tool features a map viewer that outlines the biomes of the Overworld, +Nether and End dimensions, with a wide zoom range and with toggles for each +supported structure type. It is simple to cycle through a list of seeds or to +adjust the mincraft version on the fly. + +The integrated seed finder is highly customizable using a hierarchical +condition system that allows the user to look for features that are relative to +one another. Conditions can be derived from a large selection of criteria among +structure placement, world spawn point and from the available biomes in an +area. Filters can use logic gates in the form of helper conditions for even +more control and Quad-Hut and Quad-Monument seed generators to quickly look for +seeds that include extremely rare structure constellations. + + + + diff --git a/cubiomes b/cubiomes index 91ba293..0477b60 160000 --- a/cubiomes +++ b/cubiomes @@ -1 +1 @@ -Subproject commit 91ba2936167f673d55963a8fc3e3898fb56c9f78 +Subproject commit 0477b60c10e5321594d470fc1ca0f69e795d7b1a diff --git a/cubiomes-viewer.pro b/cubiomes-viewer.pro index edd9f51..44168e0 100644 --- a/cubiomes-viewer.pro +++ b/cubiomes-viewer.pro @@ -46,15 +46,16 @@ SOURCES += \ src/formgen48.cpp \ src/formsearchcontrol.cpp \ src/gotodialog.cpp \ + src/mapview.cpp \ src/presetdialog.cpp \ src/protobasedialog.cpp \ - src/quadlistdialog.cpp \ - src/mapview.cpp \ src/quad.cpp \ + src/quadlistdialog.cpp \ src/rangedialog.cpp \ src/rangeslider.cpp \ src/search.cpp \ src/searchthread.cpp \ + src/structuredialog.cpp \ src/mainwindow.cpp \ src/main.cpp @@ -68,25 +69,26 @@ HEADERS += \ src/collapsible.h \ src/conditiondialog.h \ src/configdialog.h \ + src/cutil.h \ src/extgendialog.h \ src/exportdialog.h \ src/formconditions.h \ src/formgen48.h \ src/formsearchcontrol.h \ src/gotodialog.h \ + src/mapview.h \ src/presetdialog.h \ src/protobasedialog.h \ src/quadlistdialog.h \ - src/mapview.h \ src/quad.h \ - src/cutil.h \ src/rangedialog.h \ src/rangeslider.h \ src/search.h \ src/searchthread.h \ src/seedtables.h \ - src/mainwindow.h \ - src/settings.h + src/settings.h \ + src/structuredialog.h \ + src/mainwindow.h FORMS += \ src/aboutdialog.ui \ @@ -102,8 +104,9 @@ FORMS += \ src/presetdialog.ui \ src/protobasedialog.ui \ src/quadlistdialog.ui\ - src/mainwindow.ui \ - src/rangedialog.ui + src/rangedialog.ui \ + src/structuredialog.ui \ + src/mainwindow.ui TRANSLATIONS += \ @@ -118,7 +121,7 @@ RESOURCES += \ # ----- translations ----- -!without_translation: { +translations: { # automatically run lupdate for pluralization default translation THIS_FILE = cubiomes-viewer.pro lupdate.input = THIS_FILE diff --git a/src/aboutdialog.h b/src/aboutdialog.h index 796a9e6..6f3c410 100644 --- a/src/aboutdialog.h +++ b/src/aboutdialog.h @@ -5,8 +5,8 @@ #include #define VERS_MAJOR 2 -#define VERS_MINOR 2 -#define VERS_PATCH 2 // negative patch number designates a development version +#define VERS_MINOR 3 +#define VERS_PATCH 0 // 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/biomecolordialog.cpp b/src/biomecolordialog.cpp index a118b8c..3257032 100644 --- a/src/biomecolordialog.cpp +++ b/src/biomecolordialog.cpp @@ -1,6 +1,6 @@ #include "biomecolordialog.h" #include "ui_biomecolordialog.h" -#include "mainwindow.h" + #include "cutil.h" #include @@ -35,10 +35,9 @@ static QIcon getColorIcon(const QColor& col) return QIcon(pixmap); } -BiomeColorDialog::BiomeColorDialog(MainWindow *parent, QString initrc) +BiomeColorDialog::BiomeColorDialog(QWidget *parent, QString initrc) : QDialog(parent) , ui(new Ui::BiomeColorDialog) - , parent(parent) , modified() { ui->setupUi(this); @@ -170,6 +169,11 @@ int BiomeColorDialog::saveColormap(QString rc, QString desc) return index; } +QString BiomeColorDialog::getRc() +{ + return activerc; +} + void BiomeColorDialog::setBiomeColor(int id, const QColor &col) { buttons[id]->setIcon(getColorIcon(col)); @@ -269,7 +273,7 @@ void BiomeColorDialog::on_buttonRemove_clicked() void BiomeColorDialog::on_buttonOk_clicked() { on_comboColormaps_currentIndexChanged(ui->comboColormaps->currentIndex()); - parent->setBiomeColorRc(activerc); + emit yieldBiomeColorRc(activerc); accept(); } diff --git a/src/biomecolordialog.h b/src/biomecolordialog.h index cb7e0af..546b052 100644 --- a/src/biomecolordialog.h +++ b/src/biomecolordialog.h @@ -7,17 +7,19 @@ namespace Ui { class BiomeColorDialog; } -class MainWindow; - class BiomeColorDialog : public QDialog { Q_OBJECT public: - explicit BiomeColorDialog(MainWindow *parent, QString initrc); + explicit BiomeColorDialog(QWidget *parent, QString initrc); ~BiomeColorDialog(); int saveColormap(QString rc, QString desc); + QString getRc(); + +signals: + void yieldBiomeColorRc(QString path); public slots: void setBiomeColor(int id, const QColor &col); @@ -34,7 +36,6 @@ private slots: private: Ui::BiomeColorDialog *ui; - MainWindow *parent; QPushButton *buttons[256]; unsigned char colors[256][3]; QString activerc; diff --git a/src/conditiondialog.cpp b/src/conditiondialog.cpp index b638c11..69eff04 100644 --- a/src/conditiondialog.cpp +++ b/src/conditiondialog.cpp @@ -13,13 +13,6 @@ #include #include - -#define SETUP_BIOME_CHECKBOX(B) do {\ - biomecboxes[B] = new QCheckBox(biome2str(mc, B));\ - ui->gridLayoutBiomes->addWidget(biomecboxes[B], (B) % 128, (B) / 128);\ - biomecboxes[B]->setTristate(true);\ - } while (0) - #define SETUP_TEMPCAT_SPINBOX(B) do {\ tempsboxes[B] = new SpinExclude();\ QLabel *l = new QLabel(#B);\ @@ -69,9 +62,14 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv QString mcs = tr("MC %1", "Minecraft version").arg(p_mcs ? p_mcs : "?"); ui->labelMC->setText(mcs); + QFont dfont = font(); + dfont.setBold(false); + const QList children = ui->groupBoxPosition->findChildren(QString(), Qt::FindDirectChildrenOnly); + for (QWidget *w : children) + w->setFont(dfont); int initindex = -1; - QVector existing = parent->getConditions(); + const QVector existing = parent->getConditions(); for (Condition c : existing) { if (initcond) @@ -114,98 +112,16 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv memset(biomecboxes, 0, sizeof(biomecboxes)); - SETUP_BIOME_CHECKBOX(ocean); - SETUP_BIOME_CHECKBOX(plains); - SETUP_BIOME_CHECKBOX(desert); - SETUP_BIOME_CHECKBOX(mountains); - SETUP_BIOME_CHECKBOX(forest); - SETUP_BIOME_CHECKBOX(taiga); - SETUP_BIOME_CHECKBOX(swamp); - SETUP_BIOME_CHECKBOX(river); - SETUP_BIOME_CHECKBOX(frozen_ocean); - SETUP_BIOME_CHECKBOX(frozen_river); - SETUP_BIOME_CHECKBOX(snowy_tundra); - SETUP_BIOME_CHECKBOX(snowy_mountains); - SETUP_BIOME_CHECKBOX(mushroom_fields); - SETUP_BIOME_CHECKBOX(mushroom_field_shore); - SETUP_BIOME_CHECKBOX(beach); - SETUP_BIOME_CHECKBOX(desert_hills); - SETUP_BIOME_CHECKBOX(wooded_hills); - SETUP_BIOME_CHECKBOX(taiga_hills); - SETUP_BIOME_CHECKBOX(mountain_edge); - SETUP_BIOME_CHECKBOX(jungle); - SETUP_BIOME_CHECKBOX(jungle_hills); - SETUP_BIOME_CHECKBOX(jungle_edge); - SETUP_BIOME_CHECKBOX(deep_ocean); - SETUP_BIOME_CHECKBOX(stone_shore); - SETUP_BIOME_CHECKBOX(snowy_beach); - SETUP_BIOME_CHECKBOX(birch_forest); - SETUP_BIOME_CHECKBOX(birch_forest_hills); - SETUP_BIOME_CHECKBOX(dark_forest); - SETUP_BIOME_CHECKBOX(snowy_taiga); - SETUP_BIOME_CHECKBOX(snowy_taiga_hills); - SETUP_BIOME_CHECKBOX(giant_tree_taiga); - SETUP_BIOME_CHECKBOX(giant_tree_taiga_hills); - SETUP_BIOME_CHECKBOX(wooded_mountains); - SETUP_BIOME_CHECKBOX(savanna); - SETUP_BIOME_CHECKBOX(savanna_plateau); - SETUP_BIOME_CHECKBOX(badlands); - SETUP_BIOME_CHECKBOX(wooded_badlands_plateau); - SETUP_BIOME_CHECKBOX(badlands_plateau); - SETUP_BIOME_CHECKBOX(warm_ocean); - SETUP_BIOME_CHECKBOX(lukewarm_ocean); - SETUP_BIOME_CHECKBOX(cold_ocean); - SETUP_BIOME_CHECKBOX(deep_warm_ocean); - SETUP_BIOME_CHECKBOX(deep_lukewarm_ocean); - SETUP_BIOME_CHECKBOX(deep_cold_ocean); - SETUP_BIOME_CHECKBOX(deep_frozen_ocean); - - SETUP_BIOME_CHECKBOX(sunflower_plains); - SETUP_BIOME_CHECKBOX(desert_lakes); - SETUP_BIOME_CHECKBOX(gravelly_mountains); - SETUP_BIOME_CHECKBOX(flower_forest); - SETUP_BIOME_CHECKBOX(taiga_mountains); - SETUP_BIOME_CHECKBOX(swamp_hills); - SETUP_BIOME_CHECKBOX(ice_spikes); - SETUP_BIOME_CHECKBOX(modified_jungle); - SETUP_BIOME_CHECKBOX(modified_jungle_edge); - SETUP_BIOME_CHECKBOX(tall_birch_forest); - SETUP_BIOME_CHECKBOX(tall_birch_hills); - SETUP_BIOME_CHECKBOX(dark_forest_hills); - SETUP_BIOME_CHECKBOX(snowy_taiga_mountains); - SETUP_BIOME_CHECKBOX(giant_spruce_taiga); - SETUP_BIOME_CHECKBOX(giant_spruce_taiga_hills); - SETUP_BIOME_CHECKBOX(modified_gravelly_mountains); - SETUP_BIOME_CHECKBOX(shattered_savanna); - SETUP_BIOME_CHECKBOX(shattered_savanna_plateau); - SETUP_BIOME_CHECKBOX(eroded_badlands); - SETUP_BIOME_CHECKBOX(modified_wooded_badlands_plateau); - SETUP_BIOME_CHECKBOX(modified_badlands_plateau); - SETUP_BIOME_CHECKBOX(bamboo_jungle); - SETUP_BIOME_CHECKBOX(bamboo_jungle_hills); - - SETUP_BIOME_CHECKBOX(nether_wastes); - SETUP_BIOME_CHECKBOX(the_end); - SETUP_BIOME_CHECKBOX(small_end_islands); - SETUP_BIOME_CHECKBOX(end_midlands); - SETUP_BIOME_CHECKBOX(end_highlands); - SETUP_BIOME_CHECKBOX(end_barrens); - SETUP_BIOME_CHECKBOX(soul_sand_valley); - SETUP_BIOME_CHECKBOX(crimson_forest); - SETUP_BIOME_CHECKBOX(warped_forest); - SETUP_BIOME_CHECKBOX(basalt_deltas); - - SETUP_BIOME_CHECKBOX(dripstone_caves); - SETUP_BIOME_CHECKBOX(lush_caves); - SETUP_BIOME_CHECKBOX(meadow); - SETUP_BIOME_CHECKBOX(grove); - SETUP_BIOME_CHECKBOX(snowy_slopes); - SETUP_BIOME_CHECKBOX(stony_peaks); - SETUP_BIOME_CHECKBOX(jagged_peaks); - SETUP_BIOME_CHECKBOX(frozen_peaks); - - SETUP_BIOME_CHECKBOX(deep_dark); - SETUP_BIOME_CHECKBOX(mangrove_swamp); + for (int i = 0; i < 256; i++) + { + const char *str = biome2str(mc, i); + if (!str) + continue; + QCheckBox *cb = new QCheckBox(str); + ui->gridLayoutBiomes->addWidget(cb, i % 128, i / 128); + cb->setTristate(true); + biomecboxes[i] = cb; + } memset(tempsboxes, 0, sizeof(tempsboxes)); @@ -261,15 +177,15 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv ok->setLimitText(tr("-Inf"), tr("+Inf")); ex->setLimitText(tr("-Inf"), tr("+Inf")); ex->setHighlight(QColor(0,0,0,0), QColor(Qt::red)); - connect(ok, SIGNAL(onRangeChange(void)), this, SLOT(onClimateLimitChanged(void))); - connect(ex, SIGNAL(onRangeChange(void)), this, SLOT(onClimateLimitChanged(void))); + connect(ok, SIGNAL(onRangeChange()), this, SLOT(onClimateLimitChanged())); + connect(ex, SIGNAL(onRangeChange()), this, SLOT(onClimateLimitChanged())); climaterange[0][climates[i].idx] = ok; climaterange[1][climates[i].idx] = ex; QCheckBox *all = new QCheckBox(this); all->setFixedWidth(20); all->setToolTip(tr("Require full range instead of intersection")); - connect(all, SIGNAL(stateChanged(int)), this, SLOT(onClimateLimitChanged(void))); + connect(all, SIGNAL(stateChanged(int)), this, SLOT(onClimateLimitChanged())); climatecomplete[climates[i].idx] = all; int row = ui->gridNoiseAllowed->rowCount(); @@ -329,6 +245,7 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv // defaults ui->spinBox->setValue(1); + ui->checkSkipRef->setChecked(false); ui->radioSquare->setChecked(true); ui->checkRadius->setChecked(false); @@ -354,13 +271,14 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv updateMode(); ui->spinBox->setValue(cond.count); + ui->checkSkipRef->setChecked(cond.skipref); ui->lineEditX1->setText(QString::number(cond.x1)); ui->lineEditZ1->setText(QString::number(cond.z1)); ui->lineEditX2->setText(QString::number(cond.x2)); ui->lineEditZ2->setText(QString::number(cond.z2)); - ui->checkApprox->setChecked(cond.flags & CFB_APPROX); - ui->checkMatchAny->setChecked(cond.flags & CFB_MATCH_ANY); + ui->checkApprox->setChecked(cond.flags & APPROX); + ui->checkMatchAny->setChecked(cond.flags & MATCH_ANY); int i, n = ui->comboY->count(); for (i = 0; i < n; i++) if (ui->comboY->itemText(i).section(' ', 0, 0).toInt() == cond.y) @@ -395,15 +313,15 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv { if (biomecboxes[i]) { - bool c1 = (cond.bfilter.riverToFind | cond.bfilter.oceanToFind) & (1ULL << i); - bool c2 = cond.bfilter.biomeToExcl & (1ULL << i); + bool c1 = cond.biomeToFind & (1ULL << i); + bool c2 = cond.biomeToExcl & (1ULL << i); biomecboxes[i]->setCheckState(c2 ? Qt::Checked : c1 ? Qt::PartiallyChecked : Qt::Unchecked); } if (biomecboxes[i+128]) { - bool c1 = (cond.bfilter.riverToFindM) & (1ULL << i); - bool c2 = cond.bfilter.biomeToExclM & (1ULL << i); + bool c1 = cond.biomeToFindM & (1ULL << i); + bool c2 = cond.biomeToExclM & (1ULL << i); biomecboxes[i+128]->setCheckState(c2 ? Qt::Checked : c1 ? Qt::PartiallyChecked : Qt::Unchecked); } } @@ -417,7 +335,7 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv ui->checkStartPiece->setChecked(cond.variants & Condition::START_PIECE_MASK); ui->checkAbandoned->setChecked(cond.variants & Condition::ABANDONED_MASK); - for (VariantCheckBox *cb : variantboxes) + for (VariantCheckBox *cb : qAsConst(variantboxes)) { cb->setChecked(cond.variants & cb->getMask()); } @@ -515,6 +433,7 @@ void ConditionDialog::updateMode() ui->labelSpinBox->setEnabled(ft.count); ui->spinBox->setEnabled(ft.count); + ui->checkSkipRef->setEnabled(ft.count); ui->labelY->setEnabled(ft.hasy); ui->comboY->setEnabled(ft.hasy); @@ -531,7 +450,7 @@ void ConditionDialog::updateMode() { setActiveTab(ui->tabBiomes); ui->checkApprox->setEnabled(mc <= MC_1_17 || ft.step == 4); - ui->checkMatchAny->setEnabled(mc >= MC_1_18); + ui->checkMatchAny->setEnabled(true); } else if (filterindex == F_VILLAGE) { @@ -727,7 +646,7 @@ int ConditionDialog::warnIfBad(Condition cond) { if (mc >= MC_1_18) { - uint64_t m = cond.bfilter.riverToFindM; + uint64_t m = cond.biomeToFindM; uint64_t underground = (1ULL << (dripstone_caves-128)) | (1ULL << (lush_caves-128)); if ((m & underground) && cond.y > 246) { @@ -851,6 +770,7 @@ void ConditionDialog::on_buttonOk_clicked() c.type = ui->comboBoxType->currentData().toInt(); c.relative = ui->comboBoxRelative->currentData().toInt(); c.count = ui->spinBox->value(); + c.skipref = ui->checkSkipRef->isChecked(); if (ui->radioSquare->isChecked()) { @@ -867,6 +787,8 @@ void ConditionDialog::on_buttonOk_clicked() c.x2 = ui->lineEditX2->text().toInt(); c.z2 = ui->lineEditZ2->text().toInt(); } + 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()) c.rmax = ui->lineRadius->text().toInt() + 1; @@ -875,20 +797,26 @@ void ConditionDialog::on_buttonOk_clicked() if (ui->tabBiomes->isEnabled()) { - int bin[256], bex[256], in = 0, ex = 0; + c.biomeToFind = c.biomeToFindM = 0; + c.biomeToExcl = c.biomeToExclM = 0; for (int i = 0; i < 256; i++) { QCheckBox *cb = biomecboxes[i]; if (cb && cb->isEnabled()) { if (cb->checkState() == Qt::PartiallyChecked) - bin[in++] = i; + { + if (i < 128) c.biomeToFind |= (1ULL << i); + else c.biomeToFindM |= (1ULL << (i-128)); + } if (cb->checkState() == Qt::Checked) - bex[ex++] = i; + { + if (i < 128) c.biomeToExcl |= (1ULL << i); + else c.biomeToExclM |= (1ULL << (i-128)); + } } } - c.bfilter = setupBiomeFilter(bin, in, bex, ex); - c.count = in + ex; + c.count = 0; } if (ui->tabTemps->isEnabled()) { @@ -908,14 +836,14 @@ void ConditionDialog::on_buttonOk_clicked() c.flags = 0; if (ui->checkApprox->isChecked()) - c.flags |= CFB_APPROX; + c.flags |= APPROX; if (ui->checkMatchAny->isChecked()) - c.flags |= CFB_MATCH_ANY; + c.flags |= MATCH_ANY; c.variants = 0; c.variants |= ui->checkStartPiece->isChecked() * Condition::START_PIECE_MASK; c.variants |= ui->checkAbandoned->isChecked() * Condition::ABANDONED_MASK; - for (VariantCheckBox *cb : variantboxes) + for (VariantCheckBox *cb : qAsConst(variantboxes)) if (cb->isChecked()) c.variants |= cb->getMask(); diff --git a/src/conditiondialog.ui b/src/conditiondialog.ui index 8464151..9114e3f 100644 --- a/src/conditiondialog.ui +++ b/src/conditiondialog.ui @@ -170,26 +170,14 @@ Location - - - - QComboBox::AdjustToContentsOnFirstShow - - - - World origin - - - - - - + + - 0 + Within radial distance: - + @@ -286,50 +274,46 @@ - - + + - 0 - - - - - - - 0 (exclude) - - - 0 - - - 99 + Within centred square of side: - - 1 + + true - + Custom: - - + + - Within centred square of side: + Location is relative to: - - true + + + + + + QComboBox::AdjustToContentsOnFirstShow + + + World origin + + - - + + - Within radial distance: + 0 @@ -340,14 +324,39 @@ - - + + + + + 0 + 0 + + + + 0 (exclude) + + + 0 + + + 99 + + + 1 + + + + + + + Skip instances at exactly the relative reference location + - Location is relative to: + Ignore reference - + @@ -376,6 +385,13 @@ + + + + 0 + + + @@ -419,8 +435,8 @@ 0 0 - 648 - 377 + 636 + 345 @@ -703,8 +719,8 @@ 0 0 - 648 - 333 + 636 + 298 @@ -752,8 +768,8 @@ 0 0 - 648 - 413 + 636 + 380 @@ -794,8 +810,7 @@ - - + .. @@ -806,8 +821,7 @@ - - + .. true diff --git a/src/configdialog.cpp b/src/configdialog.cpp index 5b71bc8..912c7dc 100644 --- a/src/configdialog.cpp +++ b/src/configdialog.cpp @@ -1,5 +1,9 @@ #include "configdialog.h" #include "ui_configdialog.h" + +#include "biomecolordialog.h" +#include "structuredialog.h" + #include "cutil.h" #include @@ -7,9 +11,11 @@ #include #include -ConfigDialog::ConfigDialog(QWidget *parent, Config *config) : - QDialog(parent), - ui(new Ui::ConfigDialog) + +ConfigDialog::ConfigDialog(QWidget *parent, Config *config) + : QDialog(parent) + , ui(new Ui::ConfigDialog) + , structVisModified() { ui->setupUi(this); @@ -124,6 +130,13 @@ void ConfigDialog::on_buttonBox_clicked(QAbstractButton *button) } } +void ConfigDialog::on_buttonBiomeColorEditor_clicked() +{ + BiomeColorDialog *dialog = new BiomeColorDialog(this, conf.biomeColorPath); + if (dialog->exec() == QDialog::Accepted) + setBiomeColorPath(dialog->getRc()); +} + void ConfigDialog::on_buttonBiomeColor_clicked() { QFileInfo finfo(conf.biomeColorPath); @@ -136,6 +149,13 @@ void ConfigDialog::on_buttonBiomeColor_clicked() } } +void ConfigDialog::on_buttonStructVisEdit_clicked() +{ + StructureDialog *dialog = new StructureDialog(this); + if (dialog->exec() == QDialog::Accepted) + structVisModified |= dialog->modified; +} + void ConfigDialog::on_buttonClear_clicked() { conf.biomeColorPath.clear(); @@ -162,3 +182,4 @@ void ConfigDialog::on_buttonColorHelp_clicked() QMessageBox::information(this, tr("Help: custom biome colors"), msg, QMessageBox::Ok); } + diff --git a/src/configdialog.h b/src/configdialog.h index 89d6724..200c6dc 100644 --- a/src/configdialog.h +++ b/src/configdialog.h @@ -29,8 +29,11 @@ class ConfigDialog : public QDialog private slots: void on_buttonBox_clicked(QAbstractButton *button); + void on_buttonBiomeColorEditor_clicked(); void on_buttonBiomeColor_clicked(); + void on_buttonStructVisEdit_clicked(); + void on_buttonClear_clicked(); void on_buttonColorHelp_clicked(); @@ -38,6 +41,8 @@ private slots: private: Ui::ConfigDialog *ui; Config conf; +public: + bool structVisModified; }; #endif // CONFIGDIALOG_H diff --git a/src/configdialog.ui b/src/configdialog.ui index 90d4fff..8ab32b9 100644 --- a/src/configdialog.ui +++ b/src/configdialog.ui @@ -10,6 +10,45 @@ :/icons/map.png:/icons/map.png + + + + Session + + + + + + Restore previous session at launch + + + + + + + Autosave every: + + + + + + + min + + + 1 + + + 120 + + + 10 + + + + + + @@ -29,54 +68,71 @@ - - + + - Interface + Miscellaneous - - - - - - + + + - [none] + Check GitHub for updates at startup - - - - - 20 - 16777215 - - - - - :/icons/clear.png:/icons/clear.png + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults + + + + + + + Interface + + + + + + Simulate innertia for the map view - - - - - Biome color customization: + Smooth map motion - + Outline known bounding boxes - - - - GUI style: + + + + MB + + + 16 + + + 8192 + + + 16 + + + 256 @@ -100,17 +156,49 @@ - - + + - Simulate innertia for the map view + Use a fixed grid in blocks instead of outlining the generated map tiles +Leave blank for the default behaviour - Smooth map motion + Custom grid spacing: - + + + + + + + [none] + + + + + + + + 20 + 16777215 + + + + + :/icons/clear.png:/icons/clear.png + + + + + + + Edit biome colors... + + + + @@ -136,105 +224,38 @@ - - - - Use a fixed grid in blocks instead of outlining the generated map tiles -Leave blank for the default behaviour - + + - Custom grid spacing: + Biome color customization: - - + + - Map cache size: - - - - - - - MB - - - 16 - - - 8192 - - - 16 - - - 256 + GUI style: - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults - - - - - - - Session - - - - + + - Restore previous session at launch + Map cache size: - - + + - Autosave every: + Edit map visibility limits... - - - - min - - - 1 - - - 120 - - - 10 - - - - - - - - - - Miscellaneous - - - - + + - Check GitHub for updates at startup + Maximum structure zoom: diff --git a/src/exportdialog.cpp b/src/exportdialog.cpp index 6689dfd..3f2bc5e 100644 --- a/src/exportdialog.cpp +++ b/src/exportdialog.cpp @@ -30,7 +30,7 @@ struct ExportWorker : QRunnable fnam.replace("%x", QString::number(tx)); fnam.replace("%z", QString::number(tz)); fnam = mt->dir.filePath(fnam); - return QFileInfo(fnam).exists(); + return QFileInfo::exists(fnam); } void run() @@ -84,7 +84,7 @@ struct ExportWorker : QRunnable ExportThread::~ExportThread() { - for (ExportWorker* worker : workers) + for (ExportWorker* worker : qAsConst(workers)) delete worker; } @@ -173,8 +173,12 @@ void ExportDialog::update() int s = 2 * ui->comboScale->currentIndex(); int x0 = ui->lineEditX1->text().toInt() >> s; int z0 = ui->lineEditZ1->text().toInt() >> s; - int x1 = (ui->lineEditX2->text().toInt() >> s) + 1; - int z1 = (ui->lineEditZ2->text().toInt() >> s) + 1; + int x1 = ui->lineEditX2->text().toInt() >> s; + int z1 = ui->lineEditZ2->text().toInt() >> s; + if (x0 > x1) std::swap(x0, x1); + if (z0 > z1) std::swap(z0, z1); + x1 += 1; + z1 += 1; if (ui->groupTiled->isChecked()) { @@ -282,8 +286,12 @@ void ExportDialog::on_buttonBox_clicked(QAbstractButton *button) int s = 2 * ui->comboScale->currentIndex(); int x0 = ui->lineEditX1->text().toInt() >> s; int z0 = ui->lineEditZ1->text().toInt() >> s; - int x1 = (ui->lineEditX2->text().toInt() >> s) + 1; - int z1 = (ui->lineEditZ2->text().toInt() >> s) + 1; + int x1 = ui->lineEditX2->text().toInt() >> s; + int z1 = ui->lineEditZ2->text().toInt() >> s; + if (x0 > x1) std::swap(x0, x1); + if (z0 > z1) std::swap(z0, z1); + x1 += 1; + z1 += 1; int y = (s == 0 ? wi.y : wi.y >> 2); if (x1 <= x0 || z1 <= z0) @@ -321,7 +329,7 @@ void ExportDialog::on_buttonBox_clicked(QAbstractButton *button) master->tilesize = tilesize; master->bgmode = bgmode; - for (uint64_t seed : seeds) + for (uint64_t seed : qAsConst(seeds)) { for (int x = tx0; x < tx1; x++) { @@ -350,7 +358,7 @@ void ExportDialog::on_buttonBox_clicked(QAbstractButton *button) } } - for (uint64_t seed : seeds) + for (uint64_t seed : qAsConst(seeds)) { ExportWorker *worker = new ExportWorker(master); existwarn |= worker->init(seed, 0, 0); diff --git a/src/formconditions.cpp b/src/formconditions.cpp index c30f965..572e174 100644 --- a/src/formconditions.cpp +++ b/src/formconditions.cpp @@ -55,8 +55,10 @@ QVector FormConditions::getConditions() const QVector conds; for (int i = 0, ie = ui->listConditionsFull->count(); i < ie; i++) - conds.push_back(qvariant_cast(ui->listConditionsFull->item(i)->data(Qt::UserRole))); - + { + Condition c = qvariant_cast(ui->listConditionsFull->item(i)->data(Qt::UserRole)); + conds.push_back(c); + } return conds; } diff --git a/src/formsearchcontrol.cpp b/src/formsearchcontrol.cpp index 8e39268..4de576e 100644 --- a/src/formsearchcontrol.cpp +++ b/src/formsearchcontrol.cpp @@ -221,7 +221,7 @@ void FormSearchControl::on_buttonStart_clicked() WorldInfo wi; parent->getSeed(&wi); const Config& config = parent->config; - const QVector& condvec = parent->formCond->getConditions(); + QVector condvec = parent->formCond->getConditions(); SearchConfig sc = getSearchConfig(); int ok = true; @@ -243,6 +243,9 @@ void FormSearchControl::on_buttonStart_clicked() if (ok) { + for (Condition& c : condvec) + c.apply(wi); + Gen48Settings gen48 = parent->formGen48->getSettings(true); // the search can either use a full list or a 48-bit list if (sc.searchtype == SEARCH_LIST) diff --git a/src/main.cpp b/src/main.cpp index b97c4de..8fb11e7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,7 @@ #include "mainwindow.h" #include #include +#include #include "quad.h" @@ -23,6 +24,16 @@ int main(int argc, char *argv[]) translator.load("en_US", ":/lang"); a.installTranslator(&translator); + //int fontid = QFontDatabase::addApplicationFont(":/fonts/test.ttf"); + int fontid = QFontDatabase::addApplicationFont(":/fonts/DejaVuSans.ttf"); + if (fontid >= 0) + { + QFontDatabase::addApplicationFont(":/fonts/DejaVuSans-Bold.ttf"); + QFont fontdef = QFontDatabase::applicationFontFamilies(fontid).at(0); + fontdef.setPointSize(10); + a.setFont(fontdef); + } + MainWindow mw; mw.show(); int ret = a.exec(); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index de567ac..7d88fa4 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -7,6 +7,7 @@ #include "conditiondialog.h" #include "extgendialog.h" #include "biomecolordialog.h" +#include "structuredialog.h" #include "exportdialog.h" #if WITH_UPDATER @@ -22,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -232,22 +232,6 @@ MainWindow::MainWindow(QWidget *parent) if (config.checkForUpdates) searchForUpdates(true); #endif - - QCoreApplication::setAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles, true); - int fontid = QFontDatabase::addApplicationFont(":/fonts/DejaVuSans.ttf"); - if (fontid >= 0) - { - QFontDatabase::addApplicationFont(":/fonts/DejaVuSans-Bold.ttf"); - QFont fontdef = QFontDatabase::applicationFontFamilies(fontid).at(0); - fontdef.setPointSize(10); - QApplication::setFont(fontdef); - //setStyleSheet("* { font: 10px '" + fontdef.family() + "'; }"); - } - else - { - fprintf(stderr, "Failed to load recources.\n"); - exit(1); - } } MainWindow::~MainWindow() @@ -263,7 +247,6 @@ void MainWindow::closeEvent(QCloseEvent *event) QMainWindow::closeEvent(event); } - QAction *MainWindow::addMapAction(int sopt, const char *iconpath, QString tip) { QIcon icon; @@ -353,12 +336,6 @@ int MainWindow::getDim() return 0; } -void MainWindow::setBiomeColorRc(QString rc) -{ - config.biomeColorPath = rc; - onBiomeColorChange(); -} - void MainWindow::saveSettings() { QSettings settings("cubiomes-viewer", "cubiomes-viewer"); @@ -759,6 +736,12 @@ void MainWindow::mapGoto(qreal x, qreal z, qreal scale) ui->mapView->setView(x, z, scale); } +void MainWindow::setBiomeColorRc(QString rc) +{ + config.biomeColorPath = rc; + onBiomeColorChange(); +} + void MainWindow::on_comboBoxMC_currentIndexChanged(int) { updateMapSeed(); @@ -784,12 +767,14 @@ void MainWindow::on_seedEdit_textChanged(const QString &a) { uint64_t s; int v = str2seed(a, &s); + QString typ = ""; switch (v) { - case 0: ui->labelSeedType->setText(tr("(text)", "Seed input type")); break; - case 1: ui->labelSeedType->setText(tr("(numeric)", "Seed input type")); break; - case 2: ui->labelSeedType->setText(tr("(random)", "Seed input type")); break; + case S_TEXT: typ = tr("text", "Seed input type"); break; + case S_NUMERIC: typ = ""; break; + case S_RANDOM: typ = tr("random", "Seed input type"); break; } + ui->labelSeedType->setText(typ); } void MainWindow::on_actionSave_triggered() @@ -848,6 +833,11 @@ void MainWindow::on_actionPreferences_triggered() onBiomeColorChange(); } } + if (dialog->structVisModified) + { // NOTE: structure visibility limits are not currently stored in config + // so the changes have to be applied regardless whether the dialog is accepted. + on_actionStructure_visibility_triggered(); + } } void MainWindow::onBiomeColorChange() @@ -893,9 +883,21 @@ void MainWindow::on_actionOpen_shadow_seed_triggered() } } +void MainWindow::on_actionStructure_visibility_triggered() +{ + StructureDialog *dialog = new StructureDialog(this); + if (dialog->exec() != QDialog::Accepted || !dialog->modified) + return; + saveStructVis(dialog->structvis); + getMapView()->deleteWorld(); + updateMapSeed(); + update(); +} + void MainWindow::on_actionBiome_colors_triggered() { BiomeColorDialog *dialog = new BiomeColorDialog(this, config.biomeColorPath); + connect(dialog, SIGNAL(yieldBiomeColorRc(QString)), this, SLOT(setBiomeColorRc(QString))); dialog->show(); } @@ -937,7 +939,7 @@ void MainWindow::on_actionPaste_triggered() void MainWindow::on_actionAddShadow_triggered() { - QVector results = formControl->getResults(); + const QVector results = formControl->getResults(); QVector shadows; shadows.reserve(results.size()); for (uint64_t s : results) @@ -1001,6 +1003,30 @@ void MainWindow::on_buttonFromVisible_clicked() ui->lineEditZ2->setText( QString::number(bz1) ); } +static +QTreeWidgetItem *setConditionTreeItems(ConditionTree& ctree, int node, Pos cpos[], QTreeWidgetItem* parent) +{ + Condition& c = ctree.condvec[node]; + Pos p = cpos[c.save]; + const std::vector& branches = ctree.references[c.save]; + + QTreeWidgetItem* item = new QTreeWidgetItem(parent); + item->setText(0, c.summary()); + item->setData(0, Qt::UserRole, QVariant::fromValue(p)); + + if (branches.empty()) + { + item->setText(1, MainWindow::tr("incomplete")); + } + else + { + item->setText(1, QString::asprintf("%d,\t%d", p.x, p.z)); + for (char b : branches) + setConditionTreeItems(ctree, b, cpos, item); + } + return item; +} + void MainWindow::on_buttonAnalysis_clicked() { int x1, z1, x2, z2; @@ -1020,11 +1046,8 @@ void MainWindow::on_buttonAnalysis_clicked() x2 = ui->lineEditX2->text().toInt(); z2 = ui->lineEditZ2->text().toInt(); } - if (x2 < x1 || z2 < z1) - { - warning(tr("Invalid area for analysis")); - return; - } + if (x2 < x1) std::swap(x1, x2); + if (z2 < z1) std::swap(z1, z2); bool ck_struct = ui->checkStructs->isChecked(); bool ck_biome = ui->checkBiomes->isChecked(); @@ -1248,7 +1271,7 @@ void MainWindow::on_buttonAnalysis_clicked() gen.setSeed(wi.seed); ConditionTree condtree; - condtree.set(conds); + condtree.set(conds, wi); //Condition& c0 = conds[0]; int xr1 = 0; //(int)( cstepx * floor( (x1+c0.x1) / (double)cstepx ) ); @@ -1293,22 +1316,12 @@ void MainWindow::on_buttonAnalysis_clicked() { continue; } - //Pos p = cpos[conds[0].save]; - //if (p.x < x1 || p.x > x2 || p.z < z1 || p.z > z2) - // continue; - QTreeWidgetItem* loc = new QTreeWidgetItem(); - loc->setData(0, Qt::UserRole, QVariant::fromValue(origin)); - loc->setText(0, tr("condition @[%1, %2]").arg(origin.x).arg(origin.z)); + QTreeWidgetItem* loc = setConditionTreeItems(condtree, 0, cpos, nullptr); + //new QTreeWidgetItem(); + //loc->setData(0, Qt::UserRole, QVariant::fromValue(origin)); + //loc->setText(0, tr("condition @[%1, %2]").arg(origin.x).arg(origin.z)); - for (int i = 0; i < conds.size(); i++) - { - Pos p = cpos[conds[i].save]; - QTreeWidgetItem* item = new QTreeWidgetItem(loc); - item->setText(0, conds[i].summary()); - item->setData(0, Qt::UserRole, QVariant::fromValue(p)); - item->setText(1, QString::asprintf("%d,\t%d", p.x, p.z)); - } items.push_back(loc); @@ -1361,16 +1374,23 @@ void MainWindow::on_buttonExport_clicked() for (; *it; ++it) { QTreeWidgetItem *item = *it; + QStringList cols; if (item->type() >= QTreeWidgetItem::UserType) - stream << QString::number(item->type() - QTreeWidgetItem::UserType) << ", "; - stream << item->text(0).replace('\t', ' '); - if (!item->text(1).isEmpty()) - stream << ", " << item->text(1); - stream << "\n"; + cols << QString::number(item->type() - QTreeWidgetItem::UserType); + for (int i = 0; i <= 1; i++) + { + QString txt = item->text(i).replace('\t', ' '); + if (txt.isEmpty()) + continue; + if (txt.contains("[")) + txt = "\"" + txt + "\""; + cols << txt; + } + stream << cols.join(", ") << "\n"; } } -void MainWindow::on_treeAnalysis_itemDoubleClicked(QTreeWidgetItem *item) +void MainWindow::on_treeAnalysis_itemClicked(QTreeWidgetItem *item) { QVariant dat = item->data(0, Qt::UserRole); if (dat.isValid()) @@ -1416,7 +1436,7 @@ void MainWindow::onActionBiomeLayerSelect(bool state, QAction *src, int lopt) { if (state == false) return; - QList actions = ui->menuBiome_layer->actions(); + const QList actions = ui->menuBiome_layer->actions(); for (QAction *act : actions) if (act != src) act->setChecked(false); @@ -1450,6 +1470,7 @@ void MainWindow::onSearchStatusChanged(bool running) void MainWindow::onStyleChanged(int style) { + //QCoreApplication::setAttribute(Qt::AA_UseStyleSheetPropagationInWidgetStyles, false); if (style == STYLE_DARK) { QFile file(":dark.qss"); @@ -1460,6 +1481,7 @@ void MainWindow::onStyleChanged(int style) else { qApp->setStyleSheet(""); + qApp->setStyle(""); } } diff --git a/src/mainwindow.h b/src/mainwindow.h index 3a422fd..a3ae130 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -50,10 +50,8 @@ class MainWindow : public QMainWindow bool setSeed(WorldInfo wi, int dim = INT_MAX, int layeropt = -1); int getDim(); MapView *getMapView(); - void setBiomeColorRc(QString rc); protected: - void saveSettings(); void loadSettings(); bool saveProgress(QString fnam, bool quiet = false); @@ -66,6 +64,7 @@ class MainWindow : public QMainWindow public slots: int warning(QString text, QMessageBox::StandardButtons buttons = QMessageBox::Ok); void mapGoto(qreal x, qreal z, qreal scale); + void setBiomeColorRc(QString rc); private slots: void on_comboBoxMC_currentIndexChanged(int a); @@ -81,6 +80,7 @@ private slots: void on_actionGo_to_triggered(); void on_actionScan_seed_for_Quad_Huts_triggered(); void on_actionOpen_shadow_seed_triggered(); + void on_actionStructure_visibility_triggered(); void on_actionBiome_colors_triggered(); void on_actionPresetLoad_triggered(); void on_actionExamples_triggered(); @@ -95,7 +95,7 @@ private slots: void on_lineRadius_editingFinished(); void on_buttonFromVisible_clicked(); void on_buttonAnalysis_clicked(); - void on_treeAnalysis_itemDoubleClicked(QTreeWidgetItem *item); + void on_treeAnalysis_itemClicked(QTreeWidgetItem *item); void on_buttonExport_clicked(); void on_actionSearch_seed_list_triggered(); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index d0de217..c976119 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -26,31 +26,20 @@ - + + 0 + + + 0 + + + 0 + + 0 - - - - Press enter to accept - - - - - - - Show map for this y-level - - - Y: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - @@ -74,22 +63,6 @@ - - - - - 61 - 25 - - - - Seed can be an integer or text. Leave empty for a random seed - - - (random) - - - @@ -209,6 +182,19 @@ + + + + Show map for this y-level + + + Y: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + @@ -216,8 +202,35 @@ + + + + + + Press enter to accept + + + + + + + Seed can be an integer or text. Leave empty for a random seed + + + random + + + + + + + + 0 + 0 + + true @@ -299,7 +312,16 @@ Search - + + 0 + + + 0 + + + 0 + + 0 @@ -345,8 +367,8 @@ QToolButton { 0 0 - 1233 - 676 + 1228 + 663 @@ -671,7 +693,16 @@ QSplitter { QFrame::Raised - + + 0 + + + 0 + + + 0 + + 0 @@ -745,7 +776,7 @@ QToolButton:checked { 0 0 1280 - 23 + 22 @@ -765,8 +796,9 @@ QToolButton:checked { - + + @@ -967,6 +999,11 @@ QToolButton:checked { Export biomes as image... + + + Structure zoom limits... + + diff --git a/src/mapview.cpp b/src/mapview.cpp index 6e7f5f7..1c5bbd3 100644 --- a/src/mapview.cpp +++ b/src/mapview.cpp @@ -66,6 +66,7 @@ MapView::MapView(QWidget *parent) elapsed1.start(); frameelapsed.start(); + actelapsed.start(); overlay = new MapOverlay(this); overlay->setMouseTracking(true); @@ -274,12 +275,34 @@ void MapView::paintEvent(QPaintEvent *) overlay->pos = p; overlay->bname = biome2str(world->wi.mc, world->getBiome(p)); - if (QThreadPool::globalInstance()->activeThreadCount() > 0 || velx || velz) + bool active = QThreadPool::globalInstance()->activeThreadCount() > 0; + if (active || velx || velz) updatecounter = 2; if (updatecounter > 0) { updatecounter--; QWidget::update(); + + if (active) + { // processing animation + qreal cyc = actelapsed.nsecsElapsed() * 1e-9; + qreal ang = 360 * (1.0 - (cyc - (int) cyc)); + int r = 20; + QRect rec = QRect(r, height() - 2*r, r, r); + + QConicalGradient gradient; + gradient.setCenter(rec.center()); + gradient.setAngle(ang); + gradient.setColorAt(0, QColor(0, 0, 0, 192)); + gradient.setColorAt(1, QColor(255, 255, 255, 192)); + QPen pen(QBrush(gradient), 5, Qt::SolidLine, Qt::SquareCap); + painter.setPen(pen); + painter.drawRect(rec); + } + } + else + { + actelapsed.start(); } } } diff --git a/src/mapview.h b/src/mapview.h index fcb4a98..6d0ce96 100644 --- a/src/mapview.h +++ b/src/mapview.h @@ -79,6 +79,7 @@ public slots: QElapsedTimer elapsed1; QElapsedTimer frameelapsed; + QElapsedTimer actelapsed; qreal decay; MapOverlay *overlay; diff --git a/src/quad.cpp b/src/quad.cpp index 65ce191..8bba617 100644 --- a/src/quad.cpp +++ b/src/quad.cpp @@ -3,11 +3,75 @@ #include "cutil.h" #include +#include #include #include +void loadStructVis(std::map& structvis) +{ + QSettings settings("cubiomes-viewer", "cubiomes-viewer"); + + for (int opt = D_DESERT; opt < D_SPAWN; opt++) + { + const char *name = mapopt2str(opt); + double scale = settings.value(QString("structscale/") + name, 32.0).toDouble(); + structvis[opt] = scale; + } +} + +void saveStructVis(std::map& structvis) +{ + QSettings settings("cubiomes-viewer", "cubiomes-viewer"); + + for (auto it : structvis) + { + const char *name = mapopt2str(it.first); + settings.setValue(QString("structscale/") + name, it.second); + } +} + +const QPixmap& getMapIcon(int opt, int variation) +{ + static QPixmap icons[STRUCT_NUM]; + static QPixmap iconzvil; + static bool init = false; + + if (!init) + { + init = true; + icons[D_DESERT] = QPixmap(":/icons/desert.png"); + icons[D_JUNGLE] = QPixmap(":/icons/jungle.png"); + icons[D_IGLOO] = QPixmap(":/icons/igloo.png"); + icons[D_HUT] = QPixmap(":/icons/hut.png"); + icons[D_VILLAGE] = QPixmap(":/icons/village.png"); + icons[D_MANSION] = QPixmap(":/icons/mansion.png"); + icons[D_MONUMENT] = QPixmap(":/icons/monument.png"); + icons[D_RUINS] = QPixmap(":/icons/ruins.png"); + icons[D_SHIPWRECK] = QPixmap(":/icons/shipwreck.png"); + icons[D_TREASURE] = QPixmap(":/icons/treasure.png"); + icons[D_MINESHAFT] = QPixmap(":/icons/mineshaft.png"); + icons[D_OUTPOST] = QPixmap(":/icons/outpost.png"); + icons[D_ANCIENTCITY]= QPixmap(":/icons/ancient_city.png"); + icons[D_PORTAL] = QPixmap(":/icons/portal.png"); + icons[D_PORTALN] = QPixmap(":/icons/portal.png"); + icons[D_SPAWN] = QPixmap(":/icons/spawn.png"); + icons[D_STRONGHOLD] = QPixmap(":/icons/stronghold.png"); + icons[D_FORTESS] = QPixmap(":/icons/fortress.png"); + icons[D_BASTION] = QPixmap(":/icons/bastion.png"); + icons[D_ENDCITY] = QPixmap(":/icons/endcity.png"); + icons[D_GATEWAY] = QPixmap(":/icons/gateway.png"); + iconzvil = QPixmap(":/icons/zombie.png"); + } + if (variation) + { + if (opt == D_VILLAGE) return iconzvil; + } + return icons[opt]; +} + + Quad::Quad(const Level* l, int i, int j) : wi(l->wi),dim(l->dim),g(&l->g),scale(l->scale) , ti(i),tj(j),blocks(l->blocks),pixs(l->pixs),sopt(l->sopt) @@ -86,7 +150,7 @@ void Quad::run() if (pixs > 0) { - int seam_buf = pixs / 128; + int seam_buf = 0; //pixs / 128; int y = (scale > 1) ? wi.y >> 2 : wi.y; int x = ti*pixs, z = tj*pixs, w = pixs+seam_buf, h = pixs+seam_buf; Range r = {scale, x, z, w, h, y, 1}; @@ -197,7 +261,7 @@ void Level::init4map(QWorld *w, int pix, int layerscale) sopt = D_NONE; } -void Level::init4struct(QWorld *w, int dim, int blocks, int sopt, int lv) +void Level::init4struct(QWorld *w, int dim, int blocks, double vis, int sopt) { this->wi = w->wi; this->dim = dim; @@ -205,7 +269,7 @@ void Level::init4struct(QWorld *w, int dim, int blocks, int sopt, int lv) this->pixs = -1; this->scale = -1; this->sopt = sopt; - this->viewlv = lv; + this->vis = vis; this->isdel = &w->isdel; } @@ -349,52 +413,25 @@ QWorld::QWorld(WorldInfo wi, int dim, int layeropt) setDim(dim, layeropt); - lvs.resize(D_SPAWN); - lvs[D_DESERT] .init4struct(this, 0, 2048, D_DESERT, 2); - lvs[D_JUNGLE] .init4struct(this, 0, 2048, D_JUNGLE, 2); - lvs[D_IGLOO] .init4struct(this, 0, 2048, D_IGLOO, 2); - lvs[D_HUT] .init4struct(this, 0, 2048, D_HUT, 2); - lvs[D_VILLAGE] .init4struct(this, 0, 2048, D_VILLAGE, 2); - lvs[D_MANSION] .init4struct(this, 0, 2048, D_MANSION, 3); - lvs[D_MONUMENT] .init4struct(this, 0, 2048, D_MONUMENT, 2); - lvs[D_RUINS] .init4struct(this, 0, 2048, D_RUINS, 1); - lvs[D_SHIPWRECK] .init4struct(this, 0, 2048, D_SHIPWRECK, 1); - lvs[D_TREASURE] .init4struct(this, 0, 2048, D_TREASURE, 1); - lvs[D_OUTPOST] .init4struct(this, 0, 2048, D_OUTPOST, 2); - lvs[D_ANCIENTCITY] .init4struct(this, 0, 2048, D_ANCIENTCITY, 2); - lvs[D_PORTAL] .init4struct(this, 0, 2048, D_PORTAL, 1); - lvs[D_PORTALN] .init4struct(this,-1, 2048, D_PORTALN, 1); - lvs[D_FORTESS] .init4struct(this,-1, 2048, D_FORTESS, 1); - lvs[D_BASTION] .init4struct(this,-1, 2048, D_BASTION, 1); - lvs[D_ENDCITY] .init4struct(this, 1, 2048, D_ENDCITY, 2); - lvs[D_GATEWAY] .init4struct(this, 1, 2048, D_GATEWAY, 2); - lvs[D_MINESHAFT] .init4struct(this, 0, 2048, D_MINESHAFT, 1); + std::map svis; + loadStructVis(svis); + lvs.resize(D_SPAWN); + for (int opt = D_DESERT; opt < D_SPAWN; opt++) + { + int sdim = 0, qsiz = 512*16; + switch (opt) { + case D_PORTALN: sdim = -1; break; + case D_FORTESS: sdim = -1; break; + case D_BASTION: sdim = -1; break; + case D_ENDCITY: sdim = +1; break; + case D_GATEWAY: sdim = +1; break; + case D_MANSION: qsiz = 1280*16; break; + } + double vis = 1.0 / svis[opt]; + lvs[opt].init4struct(this, sdim, qsiz, vis, opt); + } memset(sshow, 0, sizeof(sshow)); - - icons[D_DESERT] = QPixmap(":/icons/desert.png"); - icons[D_JUNGLE] = QPixmap(":/icons/jungle.png"); - icons[D_IGLOO] = QPixmap(":/icons/igloo.png"); - icons[D_HUT] = QPixmap(":/icons/hut.png"); - icons[D_VILLAGE] = QPixmap(":/icons/village.png"); - icons[D_MANSION] = QPixmap(":/icons/mansion.png"); - icons[D_MONUMENT] = QPixmap(":/icons/monument.png"); - icons[D_RUINS] = QPixmap(":/icons/ruins.png"); - icons[D_SHIPWRECK] = QPixmap(":/icons/shipwreck.png"); - icons[D_TREASURE] = QPixmap(":/icons/treasure.png"); - icons[D_MINESHAFT] = QPixmap(":/icons/mineshaft.png"); - icons[D_OUTPOST] = QPixmap(":/icons/outpost.png"); - icons[D_ANCIENTCITY]= QPixmap(":/icons/ancient_city.png"); - icons[D_PORTAL] = QPixmap(":/icons/portal.png"); - icons[D_PORTALN] = QPixmap(":/icons/portal.png"); - icons[D_SPAWN] = QPixmap(":/icons/spawn.png"); - icons[D_STRONGHOLD] = QPixmap(":/icons/stronghold.png"); - icons[D_FORTESS] = QPixmap(":/icons/fortress.png"); - icons[D_BASTION] = QPixmap(":/icons/bastion.png"); - icons[D_ENDCITY] = QPixmap(":/icons/endcity.png"); - icons[D_GATEWAY] = QPixmap(":/icons/gateway.png"); - - iconzvil = QPixmap(":/icons/zombie.png"); } QWorld::~QWorld() @@ -558,7 +595,6 @@ void QWorld::cleancache(std::vector& cache, unsigned int maxsize) cache.swap(newcache); } - struct SpawnStronghold : public QRunnable { QWorld *world; @@ -581,21 +617,18 @@ struct SpawnStronghold : public QRunnable StrongholdIter sh; initFirstStronghold(&sh, wi.mc, wi.seed); - std::vector *shp = new std::vector; - shp->reserve(wi.mc >= MC_1_9 ? 128 : 3); + // note: pointer to atomic pointer + QAtomicPointer *shpp = &world->strongholds; while (nextStronghold(&sh, &g) > 0) { if (world->isdel) - { - delete shp; return; - } - shp->push_back(sh.pos); + PosElement *shp; + (*shpp) = shp = new PosElement(sh.pos); + shpp = &shp->next; } - world->strongholds = shp; - QVector *qsinfo = new QVector; if (!world->isdel) @@ -667,8 +700,8 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz, qreal px = vw/2.0 + (q->ti) * ps - focusx * blocks2pix; qreal pz = vh/2.0 + (q->tj) * ps - focusz * blocks2pix; // account for the seam buffer pixels - ps += ((q->pixs / 128) * q->blocks / (qreal)q->pixs) * blocks2pix; - QRect rec(px,pz,ps,ps); + //ps += ((q->pixs / 128) * q->blocks / (qreal)q->pixs) * blocks2pix; + QRect rec(floor(px),floor(pz), ceil(ps),ceil(ps)); painter.drawImage(rec, *q->img); if (sshow[D_GRID] && !gridspacing) @@ -699,7 +732,7 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz, } } - if (sshow[D_SLIME] && dim == 0 && blocks2pix*16 > 2.0) + if (sshow[D_SLIME] && dim == 0 && blocks2pix*16 > 0.5) { long x = floor(bx0 / 16), w = floor(bx1 / 16) - x + 1; long z = floor(bz0 / 16), h = floor(bz1 / 16) - z + 1; @@ -709,7 +742,7 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz, x+w >= slimex+slimeimg.width() || z+h >= slimez+slimeimg.height() || w*h*4 >= slimeimg.width()*slimeimg.height()) { - int pad = 64; + int pad = (int)(20 / blocks2pix); // 20*16 pixels movement before recalc x -= pad; z -= pad; w += 2*pad; @@ -734,14 +767,14 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz, qreal px = vw/2.0 + slimex * ps - focusx * blocks2pix; qreal pz = vh/2.0 + slimez * ps - focusz * blocks2pix; - QRect rec(px, pz, ps*slimeimg.width(), ps*slimeimg.height()); + QRect rec(round(px), round(pz), round(ps*slimeimg.width()), round(ps*slimeimg.height())); painter.drawImage(rec, slimeimg); } if (showBB && blocks2pix >= 1.0 && qsinfo && dim == 0) { - for (QuadInfo qi : *qsinfo) + for (QuadInfo qi : qAsConst(*qsinfo)) { if (qi.typ == Swamp_Hut && !sshow[D_HUT]) continue; @@ -762,7 +795,7 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz, for (int sopt = D_DESERT; sopt < D_SPAWN; sopt++) { Level& l = lvs[sopt]; - if (!sshow[sopt] || dim != l.dim || activelv > l.viewlv) + if (!sshow[sopt] || dim != l.dim || l.vis > blocks2pix) continue; std::vector frags; @@ -805,7 +838,7 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz, if (seldo) { // check for structure selection - QRectF r = icons[sopt].rect(); + QRectF r = getMapIcon(sopt).rect(); r.moveCenter(d); if (r.contains(selx, selz)) { @@ -818,12 +851,12 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz, { // don't draw selected structure continue; } - QRectF r = icons[sopt].rect(); + QRectF r = getMapIcon(sopt).rect(); if (sopt == D_VILLAGE && vp.v.abandoned) { int ix = d.x()-r.width()/2; int iy = d.y()-r.height()/2; - painter.drawPixmap(ix, iy, iconzvil); + painter.drawPixmap(ix, iy, getMapIcon(sopt, vp.v.abandoned)); } else { @@ -832,7 +865,7 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz, } } - painter.drawPixmapFragments(frags.data(), frags.size(), icons[sopt]); + painter.drawPixmapFragments(frags.data(), frags.size(), getMapIcon(sopt)); } Pos* sp = spawn; // atomic fetch @@ -842,8 +875,8 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz, qreal y = vh/2.0 + (sp->z - focusz) * blocks2pix; QPointF d = QPointF(x, y); - QRectF r = icons[D_SPAWN].rect(); - painter.drawPixmap(x-r.width()/2, y-r.height()/2, icons[D_SPAWN]); + QRectF r = getMapIcon(D_SPAWN).rect(); + painter.drawPixmap(x-r.width()/2, y-r.height()/2, getMapIcon(D_SPAWN)); if (seldo) { @@ -857,17 +890,17 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz, } } - std::vector* shs = strongholds; // atomic fetch + QAtomicPointer shs = strongholds; if (shs && sshow[D_STRONGHOLD] && dim == 0) { std::vector frags; - frags.reserve(shs->size()); - for (Pos p : *shs) + do { + Pos p = (*shs).p; qreal x = vw/2.0 + (p.x - focusx) * blocks2pix; qreal y = vh/2.0 + (p.z - focusz) * blocks2pix; QPointF d = QPointF(x, y); - QRectF r = icons[D_STRONGHOLD].rect(); + QRectF r = getMapIcon(D_STRONGHOLD).rect(); frags.push_back(QPainter::PixmapFragment::create(d, r)); if (seldo) @@ -881,15 +914,16 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz, } } } - painter.drawPixmapFragments(frags.data(), frags.size(), icons[D_STRONGHOLD]); + while ((shs = (*shs).next)); + painter.drawPixmapFragments(frags.data(), frags.size(), getMapIcon(D_STRONGHOLD)); } for (int sopt = D_DESERT; sopt < D_SPAWN; sopt++) { Level& l = lvs[sopt]; - if (activelv <= l.viewlv && sshow[sopt] && dim == l.dim) + if (l.vis <= blocks2pix && sshow[sopt] && dim == l.dim) l.update(cachedstruct, bx0, bz0, bx1, bz1); - else if (activelv > l.viewlv+1) + else if (l.vis * 4 > blocks2pix) l.update(cachedstruct, 0, 0, 0, 0); } for (int li = lvb.size()-1; li >= 0; --li) @@ -918,12 +952,7 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz, if (seltype != D_NONE) { - QPixmap *icon = &icons[seltype]; - if (selvar) - { - if (seltype == D_VILLAGE) - icon = &iconzvil; - } + const QPixmap *icon = &getMapIcon(seltype, selvar); qreal x = vw/2.0 + (selpos.x - focusx) * blocks2pix; qreal y = vh/2.0 + (selpos.z - focusz) * blocks2pix; QRect iconrec = icon->rect(); diff --git a/src/quad.h b/src/quad.h index ebe5a59..8c4cda7 100644 --- a/src/quad.h +++ b/src/quad.h @@ -145,6 +145,10 @@ inline int mapopt2stype(int opt) } } +void loadStructVis(std::map& structvis); +void saveStructVis(std::map& structvis); +const QPixmap& getMapIcon(int opt, int variation = 0); + struct Level; struct VarPos @@ -197,7 +201,7 @@ struct Level ~Level(); void init4map(QWorld *w, int pix, int layerscale); - void init4struct(QWorld *w, int dim, int blocks, int sopt, int viewlv); + void init4struct(QWorld *w, int dim, int blocks, double vis, int sopt); void resizeLevel(std::vector& cache, int x, int z, int w, int h); void update(std::vector& cache, qreal bx0, qreal bz0, qreal bx1, qreal bz1); @@ -212,10 +216,17 @@ struct Level int blocks; int pixs; int sopt; - int viewlv; + double vis; std::atomic_bool *isdel; }; +struct PosElement +{ + PosElement(Pos p_) : next(), p(p_) {} + ~PosElement() { delete next; next = nullptr; } + QAtomicPointer next; + Pos p; +}; struct QWorld { @@ -257,7 +268,7 @@ struct QWorld // some features such as the world spawn and strongholds will be filled by // a designated worker thread once results are done QAtomicPointer spawn; - QAtomicPointer> strongholds; + QAtomicPointer strongholds; QAtomicPointer> qsinfo; // isdel is a flag for the worker thread to stop std::atomic_bool isdel; @@ -274,9 +285,6 @@ struct QWorld int selvar; qreal qual; // quality, i.e. maximum pixels per 'block' at the current layer - - QPixmap icons[STRUCT_NUM]; - QPixmap iconzvil; }; diff --git a/src/search.cpp b/src/search.cpp index 4140195..3036419 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -8,6 +8,7 @@ #include +#define MULTIPLY_CHAR QChar(0xD7) QString Condition::summary() const { @@ -18,10 +19,20 @@ QString Condition::summary() const else s = QString("[%1]").arg(save, 2, 10, QLatin1Char('0')); - s += QString(" %2%3%4") + if (type == 0) + { + s += " " + QApplication::translate("Filter", "start condition"); + return s; + } + + QString cnts = ""; + if (count) + cnts += MULTIPLY_CHAR + QString::number(count); + if (skipref) + cnts += "*"; + s += QString(" %2%3") .arg(QApplication::translate("Filter", ft.name), -28, ' ') - .arg(QChar(0xD7)) - .arg(count, -3, 10, QLatin1Char(' ')); + .arg(cnts, -4, QLatin1Char(' ')); if (relative) s += QString::asprintf("[%02d]+", relative); @@ -42,24 +53,69 @@ QString Condition::summary() const return s; } +bool Condition::versionUpgrade() +{ + if (version == 0) + { + uint64_t oceanToFind; + memcpy(&oceanToFind, pad2, sizeof(oceanToFind)); + biomeToFind &= ~((1ULL << ocean) | (1ULL << deep_ocean)); + biomeToFind |= oceanToFind; + skipref = 0; + memset(pad1, 0, sizeof(pad1)); + memset(pad2, 0, sizeof(pad2)); + version = 1; + } + return true; +} + +bool Condition::apply(WorldInfo wi) +{ + int in[256] = {}, inlen = 0, ex[256] = {}, exlen = 0; + for (int i = 0; i < 64; i++) + { + if (biomeToFind & (1ULL << i)) + in[inlen++] = i; + if (biomeToFindM & (1ULL << i)) + in[inlen++] = i + 128; + if (biomeToExcl & (1ULL << i)) + ex[exlen++] = i; + if (biomeToExclM & (1ULL << i)) + ex[exlen++] = i + 128; + } + uint32_t bfflags = 0; + if (flags & APPROX) + bfflags |= CFB_APPROX; + if (flags & MATCH_ANY) + setupBiomeFilter(&bf, wi.mc, bfflags, 0, 0, ex, exlen, in, inlen); + else + setupBiomeFilter(&bf, wi.mc, bfflags, in, inlen, ex, exlen, 0, 0); + return true; +} + QString Condition::toHex() const { - return QByteArray((const char*) this, sizeof(Condition)).toHex(); + size_t savsize = offsetof(Condition, generated_start); + return QByteArray((const char*) this, savsize).toHex(); } bool Condition::readHex(const QString& hex) { - if (hex.length()/2 < (char*)&count - (char*)this) + if ((size_t)hex.length()/2 < offsetof(Condition, count)) return false; QByteArray ba = QByteArray::fromHex(QByteArray(hex.toLatin1().data())); size_t minsize = (size_t)ba.size(); - if (sizeof(Condition) < minsize) - minsize = sizeof(Condition); + size_t savsize = offsetof(Condition, generated_start); + if (savsize < minsize) + minsize = savsize; memset(this, 0, sizeof(Condition)); memcpy(this, ba.data(), minsize); - return (size_t)ba.size() >= minsize && + bool ok = (size_t)ba.size() >= minsize && save >= 0 && save < 100 && type >= 0 && type < FILTER_MAX; + if (ok) + ok = versionUpgrade(); + return ok; } @@ -100,7 +156,7 @@ bool Condition::villageOk(int mc, StructureVariant sv) const return mask & variants; } -void ConditionTree::set(const QVector& cv) +void ConditionTree::set(const QVector& cv, WorldInfo wi) { int cmax = 0; for (const Condition& c : cv) @@ -115,6 +171,7 @@ void ConditionTree::set(const QVector& cv) if (c.meta & Condition::DISABLED) continue; condvec[c.save] = c; + condvec[c.save].apply(wi); if (c.relative <= cmax) references[c.relative].push_back(c.save); } @@ -132,7 +189,7 @@ int testTreeAt( ) { Condition& c = tree->condvec[node]; - QVector& branches = tree->references[c.save]; + const std::vector& branches = tree->references[c.save]; int st; int rx1, rz1, rx2, rz2; int sref; @@ -186,7 +243,11 @@ int testTreeAt( if (sta > st) st = sta; if (st == COND_OK) + { + if (path) + path[c.save] = pos; return COND_OK; + } } } return st; @@ -221,12 +282,18 @@ int testTreeAt( if (st == COND_FAILED) break; } + if (path && st == COND_OK) + path[c.save] = pos; return st; case F_LOGIC_OR: if (branches.empty()) + { + if (path) + path[c.save] = pos; return COND_OK; // empty ORs are ignored + } st = COND_FAILED; for (int b : branches) { @@ -246,9 +313,34 @@ int testTreeAt( if (st == COND_OK) break; } + if (path && st == COND_OK) + path[c.save] = pos; return st; + case F_LOGIC_NOT: + st = COND_OK; + for (int b : branches) + { + int sta = testTreeAt( + at, + tree, + b, + pass, + gen, + abort, + path + ); + if (*abort) + return COND_FAILED; + if (sta == COND_OK) { st = COND_FAILED; break; } + else if (sta == COND_FAILED) { st = COND_OK; break; } + else if (sta > st) st = sta; + } + if (path && st == COND_OK) + path[c.save] = pos; + return st; + default: if (branches.empty()) @@ -261,9 +353,9 @@ int testTreeAt( } finfo = g_filterinfo.list + c.type; if (c.count == 1 && (finfo->count || finfo->cat == CAT_QUAD)) - { // condition has exactly one required instance => we can check - // each one individually, i.e. this is a branch that can have - // multiple independent branches (combined via OR) + { // 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 int icnt = MAX_INSTANCES; @@ -272,11 +364,11 @@ int testTreeAt( return st; int sta = COND_FAILED; int iok = 0; - for (int i = 0; i < icnt; i++) + for (int i = 0; i < icnt; i++) // OR instance subbranches { int stb = COND_OK; - pos = inst[i]; // position of instance - for (int b : branches) + pos = inst[i]; + for (int b : branches) // AND dependent conditions { int stc = testTreeAt( pos, @@ -326,7 +418,7 @@ int testTreeAt( return st; pos = inst[0]; // center point of instances } - for (int b : branches) + for (char b : branches) { if (st == COND_FAILED) break; @@ -724,6 +816,8 @@ testCondAt( { if (!getStructurePos(st, gen->mc, gen->seed, rx+0, rz+0, &pc)) continue; + if (cond->skipref && pc.x == at.x && pc.z == at.z) + continue; if (rmax) { int dx = pc.x - at.x; @@ -871,6 +965,8 @@ testCondAt( if (cond->count == 0) { // exclusion icnt = getMineshafts(gen->mc, gen->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) @@ -896,6 +992,18 @@ testCondAt( } *imax = icnt = j; } + if (cond->skipref && icnt > 0) + { // remove origin instance + for (int i = 0; i < icnt; i++) + { + if (cent[i].x == at.x && cent[i].z == at.z) + { + cent[i] = cent[icnt-1]; + *imax = --icnt; + break; + } + } + } if (icnt >= cond->count) return COND_OK; } @@ -916,6 +1024,8 @@ testCondAt( if (rsq >= rmax) continue; } + if (cond->skipref && p[i].x == at.x && p[i].z == at.z) + continue; xt += p[i].x; zt += p[i].z; j++; @@ -967,6 +1077,9 @@ testCondAt( { return COND_FAILED; } + + if (cond->skipref && pc.x == at.x && pc.z == at.z) + return COND_FAILED; *cent = pc; return COND_OK; @@ -982,8 +1095,8 @@ testCondAt( int dx = pc.x - at.x; int dz = pc.z - at.z; uint64_t rsq = dx*(int64_t)dx + dz*(int64_t)dz; - if (rsq <= rsqmax) - return COND_OK; + if (rsq > rsqmax) + return COND_FAILED; } else { @@ -991,10 +1104,12 @@ testCondAt( 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_OK; + if (pc.x < x1 || pc.x > x2 || pc.z < z1 || pc.z > z2) + return COND_FAILED; } - return COND_FAILED; + if (cond->skipref && pc.x == at.x && pc.z == at.z) + return COND_FAILED; + return COND_OK; case F_STRONGHOLD: @@ -1104,10 +1219,12 @@ testCondAt( inside = (sh.pos.x >= x1 && sh.pos.x <= x2 && sh.pos.z >= z1 && sh.pos.z <= z2); } + if (cond->skipref && sh.pos.x == at.x && sh.pos.z == at.z) + inside = false; if (inside) { if (cond->count == 0) - { + { // exclude return COND_FAILED; } else if (imax) @@ -1137,7 +1254,7 @@ testCondAt( { *imax = icnt; } - else + else if (icnt) { cent->x = xt / icnt; cent->z = zt / icnt; @@ -1161,6 +1278,8 @@ testCondAt( { for (int rx = rx1; rx <= rx2; rx++) { + if (cond->skipref && rx == at.x >> 4 && rz == at.z >> 4) + continue; if (isSlimeChunk(gen->seed, rx, rz)) { if (cond->count == 0) @@ -1195,7 +1314,7 @@ testCondAt( { *imax = icnt; } - else + else if (icnt) { cent->x = (xt << 4) / icnt; cent->z = (zt << 4) / icnt; @@ -1206,15 +1325,15 @@ testCondAt( // biome filters reference specific layers // MAYBE: options for layers in different versions? - case F_BIOME: s = 0; - if (gen->mc >= MC_1_18) goto L_noise_biome; - else goto L_biome_filter_layered; - case F_BIOME_4_RIVER: s = 2; goto L_biome_filter_layered; - case F_BIOME_256_OTEMP: s = 8; goto L_biome_filter_layered; + case F_BIOME: + if (gen->mc >= MC_1_18) goto L_noise_biome; + // fallthrough + case F_BIOME_4_RIVER: + case F_BIOME_256_OTEMP: -L_biome_filter_layered: if (gen->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; @@ -1235,7 +1354,7 @@ testCondAt( int h = rz2-rz1+1; //gen->init4Dim(0); // seed gets applied by checkForBiomesAtLayer if (checkForBiomesAtLayer(&gen->g.ls, &gen->g.ls.layers[finfo.layer], - NULL, gen->seed, rx1, rz1, w, h, cond->bfilter, cond->flags) > 0) + NULL, gen->seed, rx1, rz1, w, h, &cond->bf) > 0) { valid = COND_OK; } @@ -1260,20 +1379,21 @@ testCondAt( return COND_FAILED; - case F_BIOME_4: s = 2; goto L_noise_biome; - case F_BIOME_16: s = 4; goto L_noise_biome; - case F_BIOME_64: s = 6; goto L_noise_biome; - case F_BIOME_256: s = 8; goto L_noise_biome; - case F_BIOME_NETHER_1: s = 0; goto L_noise_biome; - case F_BIOME_NETHER_4: s = 2; goto L_noise_biome; - case F_BIOME_NETHER_16: s = 4; goto L_noise_biome; - case F_BIOME_NETHER_64: s = 6; goto L_noise_biome; - case F_BIOME_END_1: s = 0; goto L_noise_biome; - case F_BIOME_END_4: s = 2; goto L_noise_biome; - case F_BIOME_END_16: s = 4; goto L_noise_biome; - case F_BIOME_END_64: s = 6; goto L_noise_biome; + case F_BIOME_4: + case F_BIOME_16: + case F_BIOME_64: + case F_BIOME_256: + case F_BIOME_NETHER_1: + case F_BIOME_NETHER_4: + case F_BIOME_NETHER_16: + case F_BIOME_NETHER_64: + case F_BIOME_END_1: + case F_BIOME_END_4: + case F_BIOME_END_16: + case F_BIOME_END_64: 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; @@ -1292,7 +1412,7 @@ testCondAt( int y = (s == 0 ? cond->y : cond->y >> 2); Range r = {1<g, NULL, r, finfo.dim, gen->seed, - cond->bfilter, cond->flags, (volatile char*)abort) > 0; + &cond->bf, (volatile char*)abort) > 0; } return valid ? COND_OK : COND_FAILED; diff --git a/src/search.h b/src/search.h index b3ce26e..4a9e6f9 100644 --- a/src/search.h +++ b/src/search.h @@ -1,7 +1,7 @@ #ifndef SEARCH_H #define SEARCH_H -#include "cubiomes/finders.h" +#include "settings.h" #include #include @@ -32,6 +32,7 @@ struct FilterInfo 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 mcmin; // minimum version int mcmax; // maximum version @@ -103,6 +104,7 @@ enum F_FIRST_STRONGHOLD, F_CLIMATE_NOISE, F_ANCIENT_CITY, + F_LOGIC_NOT, // new filters should be added here at the end to keep some downwards compatibility FILTER_MAX, }; @@ -117,7 +119,7 @@ static const struct FilterList int disp = 0; // display order list[F_SELECT] = FilterInfo{ - CAT_NONE, 0, 0, 0, 0, 0, 0, 0, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, + CAT_NONE, 0, 0, 0, 0, 0, 0, 0, 0, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, NULL, "", "" @@ -126,21 +128,29 @@ static const struct FilterList #define _(S) QT_TRANSLATE_NOOP("Filter", S) list[F_LOGIC_OR] = FilterInfo{ - CAT_HELPER, 0, 0, 0, 0, 0, 0, 1, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, + CAT_HELPER, 0, 0, 0, 0, 0, 0, 1, 0, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, ":icons/helper.png", _("OR logic gate"), _("Evaluates as true when any of the conditions that reference it " "(by relative location) are met. When no referencing conditions are " - "defined, it also defaults to true.") + "defined, it defaults to true.") + }; + list[F_LOGIC_NOT] = FilterInfo{ + CAT_HELPER, 0, 0, 0, 0, 0, 0, 1, 0, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, + ":icons/helper.png", + _("NOT logic gate"), + _("Evaluates as true when none of the conditions that reference it " + "(by relative location) are met. When no referencing conditions are " + "defined, it defaults to true.") }; list[F_SCALE_TO_NETHER] = FilterInfo{ - CAT_HELPER, 0, 0, 0, 0, 0, 0, 1, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, + CAT_HELPER, 0, 0, 0, 0, 0, 0, 1, 0, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, ":icons/portal.png", _("Coordinate factor x/8"), _("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, MC_1_0, MC_NEWEST, 0, 0, disp++, + CAT_HELPER, 0, 0, 0, 0, 0, 0, 1, 0, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, ":icons/portal.png", _("Coordinate factor x*8"), _("Multiplies relative location by 8, from Nether to Overworld.") @@ -152,50 +162,50 @@ static const struct FilterList "" ); list[F_REFERENCE_1] = FilterInfo{ - CAT_HELPER, 0, 1, 1, 0, 0, 0, 1, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, + CAT_HELPER, 0, 1, 1, 0, 0, 0, 1, 0, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, ":icons/reference.png", _("Reference point 1:1"), ref_desc }; list[F_REFERENCE_4] = FilterInfo{ - CAT_HELPER, 0, 1, 1, 0, 0, 0, 4, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, + CAT_HELPER, 0, 1, 1, 0, 0, 0, 4, 2, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, ":icons/reference.png", _("Reference point 1:4"), ref_desc }; list[F_REFERENCE_16] = FilterInfo{ - CAT_HELPER, 0, 1, 1, 0, 0, 0, 16, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, + CAT_HELPER, 0, 1, 1, 0, 0, 0, 16, 4, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, ":icons/reference.png", _("Reference point 1:16"), ref_desc }; list[F_REFERENCE_64] = FilterInfo{ - CAT_HELPER, 0, 1, 1, 0, 0, 0, 64, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, + CAT_HELPER, 0, 1, 1, 0, 0, 0, 64, 6, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, ":icons/reference.png", _("Reference point 1:64"), ref_desc }; list[F_REFERENCE_256] = FilterInfo{ - CAT_HELPER, 0, 1, 1, 0, 0, 0, 256, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, + CAT_HELPER, 0, 1, 1, 0, 0, 0, 256, 8, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, ":icons/reference.png", _("Reference point 1:256"), ref_desc }; list[F_REFERENCE_512] = FilterInfo{ - CAT_HELPER, 0, 1, 1, 0, 0, 0, 512, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, + CAT_HELPER, 0, 1, 1, 0, 0, 0, 512, 9, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, ":icons/reference.png", _("Reference point 1:512"), ref_desc }; list[F_REFERENCE_1024] = FilterInfo{ - CAT_HELPER, 0, 1, 1, 0, 0, 0, 1024, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, + CAT_HELPER, 0, 1, 1, 0, 0, 0, 1024, 10, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, ":icons/reference.png", _("Reference point 1:1024"), ref_desc }; list[F_QH_IDEAL] = FilterInfo{ - CAT_QUAD, 0, 1, 1, 0, 0, Swamp_Hut, 512, 0, MC_1_4, MC_NEWEST, 0, 0, disp++, + CAT_QUAD, 0, 1, 1, 0, 0, Swamp_Hut, 512, 9, 0, MC_1_4, MC_NEWEST, 0, 0, disp++, ":icons/quad.png", _("Quad-hut (ideal)"), _("The lower 48-bits provide potential for four swamp huts in " @@ -203,7 +213,7 @@ static const struct FilterList }; list[F_QH_CLASSIC] = FilterInfo{ - CAT_QUAD, 0, 1, 1, 0, 0, Swamp_Hut, 512, 0, MC_1_4, MC_NEWEST, 0, 0, disp++, + CAT_QUAD, 0, 1, 1, 0, 0, Swamp_Hut, 512, 9, 0, MC_1_4, MC_NEWEST, 0, 0, disp++, ":icons/quad.png", _("Quad-hut (classic)"), _("The lower 48-bits provide potential for four swamp huts in " @@ -213,7 +223,7 @@ static const struct FilterList }; list[F_QH_NORMAL] = FilterInfo{ - CAT_QUAD, 0, 1, 1, 0, 0, Swamp_Hut, 512, 0, MC_1_4, MC_NEWEST, 0, 0, disp++, + CAT_QUAD, 0, 1, 1, 0, 0, Swamp_Hut, 512, 9, 0, MC_1_4, MC_NEWEST, 0, 0, disp++, ":icons/quad.png", _("Quad-hut (normal)"), _("The lower 48-bits provide potential for four swamp huts in " @@ -223,7 +233,7 @@ static const struct FilterList }; list[F_QH_BARELY] = FilterInfo{ - CAT_QUAD, 0, 1, 1, 0, 0, Swamp_Hut, 512, 0, MC_1_4, MC_NEWEST, 0, 0, disp++, + CAT_QUAD, 0, 1, 1, 0, 0, Swamp_Hut, 512, 9, 0, MC_1_4, MC_NEWEST, 0, 0, disp++, ":icons/quad.png", _("Quad-hut (barely)"), _("The lower 48-bits provide potential for four swamp huts in " @@ -232,7 +242,7 @@ static const struct FilterList }; list[F_QM_95] = FilterInfo{ - CAT_QUAD, 0, 1, 1, 0, 0, Monument, 512, 0, MC_1_8, MC_NEWEST, 0, 0, disp++, + CAT_QUAD, 0, 1, 1, 0, 0, Monument, 512, 9, 0, MC_1_8, MC_NEWEST, 0, 0, disp++, ":icons/quad.png", _("Quad-ocean-monument (>95%)"), _("The lower 48-bits provide potential for 95% of the area of " @@ -241,7 +251,7 @@ static const struct FilterList }; list[F_QM_90] = FilterInfo{ - CAT_QUAD, 0, 1, 1, 0, 0, Monument, 512, 0, MC_1_8, MC_NEWEST, 0, 0, disp++, + CAT_QUAD, 0, 1, 1, 0, 0, Monument, 512, 9, 0, MC_1_8, MC_NEWEST, 0, 0, disp++, ":icons/quad.png", _("Quad-ocean-monument (>90%)"), _("The lower 48-bits provide potential for 90% of the area of " @@ -250,7 +260,7 @@ static const struct FilterList }; list[F_BIOME] = FilterInfo{ - CAT_BIOMES, 1, 1, 1, 0, L_VORONOI_1, 0, 1, 0, MC_1_0, MC_1_17, 0, 1, disp++, // disable for 1.18 + CAT_BIOMES, 1, 1, 1, 0, L_VORONOI_1, 0, 1, 0, 0, MC_1_0, MC_1_17, 0, 1, disp++, // disable for 1.18 ":icons/map.png", _("Biomes 1:1"), _("Only seeds with the included (+) biomes in the specified area and " @@ -258,28 +268,28 @@ static const struct FilterList }; list[F_BIOME_4] = FilterInfo{ - CAT_BIOMES, 1, 1, 1, 0, 0, 0, 4, 0, MC_1_0, MC_NEWEST, 0, 1, disp++, + CAT_BIOMES, 1, 1, 1, 0, 0, 0, 4, 2, 0, MC_1_0, MC_NEWEST, 0, 1, disp++, ":icons/map.png", _("Biomes 1:4"), _("Only seeds with the included (+) biomes in the specified area and " "discard those that have biomes that are explicitly excluded (-).") }; list[F_BIOME_16] = FilterInfo{ - CAT_BIOMES, 1, 1, 1, 0, 0, 0, 16, 0, MC_1_0, MC_NEWEST, 0, 1, disp++, + CAT_BIOMES, 1, 1, 1, 0, 0, 0, 16, 4, 0, MC_1_0, MC_NEWEST, 0, 1, disp++, ":icons/map.png", _("Biomes 1:16"), _("Only seeds with the included (+) biomes in the specified area and " "discard those that have biomes that are explicitly excluded (-).") }; list[F_BIOME_64] = FilterInfo{ - CAT_BIOMES, 1, 1, 1, 0, 0, 0, 64, 0, MC_1_0, MC_NEWEST, 0, 1, disp++, + CAT_BIOMES, 1, 1, 1, 0, 0, 0, 64, 6, 0, MC_1_0, MC_NEWEST, 0, 1, disp++, ":icons/map.png", _("Biomes 1:64"), _("Only seeds with the included (+) biomes in the specified area and " "discard those that have biomes that are explicitly excluded (-).") }; list[F_BIOME_256] = FilterInfo{ - CAT_BIOMES, 1, 1, 1, 0, 0, 0, 256, 0, MC_1_0, MC_NEWEST, 0, 1, disp++, + CAT_BIOMES, 1, 1, 1, 0, 0, 0, 256, 8, 0, MC_1_0, MC_NEWEST, 0, 1, disp++, ":icons/map.png", _("Biomes 1:256"), _("Only seeds with the included (+) biomes in the specified area and " @@ -287,7 +297,7 @@ static const struct FilterList }; list[F_BIOME_4_RIVER] = FilterInfo{ - CAT_BIOMES, 1, 1, 1, 0, L_RIVER_MIX_4, 0, 4, 0, MC_1_13, MC_1_17, 0, 0, disp++, + CAT_BIOMES, 1, 1, 1, 0, L_RIVER_MIX_4, 0, 4, 2, 0, MC_1_13, MC_1_17, 0, 0, disp++, ":icons/map.png", _("Biomes 1:4 RIVER"), _("Only seeds with the included (+) biomes in the specified area and " @@ -296,7 +306,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, 0, MC_1_13, MC_1_17, 0, 0, disp++, + CAT_BIOMES, 0, 1, 1, 0, L_OCEAN_TEMP_256, 0, 256, 8, 0, MC_1_13, MC_1_17, 0, 0, disp++, ":icons/map.png", _("Biomes 1:256 O.TEMP"), _("Only seeds with the included (+) biomes in the specified area and " @@ -305,91 +315,91 @@ 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, 0, MC_1_18, MC_NEWEST, 0, 0, disp++, + CAT_BIOMES, 0, 1, 1, 0, 0, 0, 4, 2, 0, MC_1_18, MC_NEWEST, 0, 0, disp++, ":icons/map.png", _("Climate Parameters 1:4"), _("Custom limits for the required and allowed climate noise parameters that " "the specified area should cover.") }; list[F_TEMPS] = FilterInfo{ - CAT_BIOMES, 1, 1, 1, 0, 0, 0, 1024, 0, MC_1_7, MC_1_17, 0, 0, disp++, + CAT_BIOMES, 1, 1, 1, 0, 0, 0, 1024, 10, 0, MC_1_7, MC_1_17, 0, 0, disp++, ":icons/tempcat.png", _("Temperature categories"), _("Checks that the area has a minimum of all the required temperature categories.") }; list[F_BIOME_NETHER_1] = FilterInfo{ - CAT_NETHER, 1, 1, 1, 0, 0, 0, 1, 0, MC_1_16, 0, -1, 1, disp++, // disabled + CAT_NETHER, 1, 1, 1, 0, 0, 0, 1, 0, 0, MC_1_16, 0, -1, 1, disp++, // disabled ":icons/nether.png", _("Nether biomes 1:1 (disabled)"), _("Nether biomes after voronoi scaling to 1:1.") }; list[F_BIOME_NETHER_4] = FilterInfo{ - CAT_NETHER, 0, 1, 1, 0, 0, 0, 4, 0, MC_1_16, MC_NEWEST, -1, 0, disp++, + CAT_NETHER, 0, 1, 1, 0, 0, 0, 4, 2, 0, MC_1_16, MC_NEWEST, -1, 0, disp++, ":icons/nether.png", _("Nether biomes 1:4"), _("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, 0, MC_1_16, MC_NEWEST, -1, 0, disp++, + CAT_NETHER, 0, 1, 1, 0, 0, 0, 16, 4, 0, MC_1_16, MC_NEWEST, -1, 0, disp++, ":icons/nether.png", _("Nether biomes 1:16"), _("Nether biomes, but only sampled at scale 1:16.") }; list[F_BIOME_NETHER_64] = FilterInfo{ - CAT_NETHER, 0, 1, 1, 0, 0, 0, 64, 0, MC_1_16, MC_NEWEST, -1, 0, disp++, + CAT_NETHER, 0, 1, 1, 0, 0, 0, 64, 6, 0, MC_1_16, MC_NEWEST, -1, 0, disp++, ":icons/nether.png", _("Nether biomes 1:64"), _("Nether biomes, but only sampled at scale 1:64.") }; list[F_BIOME_NETHER_256] = FilterInfo{ - CAT_NETHER, 0, 1, 1, 0, 0, 0, 256, 0, MC_1_16, MC_NEWEST, -1, 0, disp++, + CAT_NETHER, 0, 1, 1, 0, 0, 0, 256, 8, 0, MC_1_16, MC_NEWEST, -1, 0, disp++, ":icons/nether.png", _("Nether biomes 1:256"), _("Nether biomes, but only sampled at scale 1:256.") }; list[F_BIOME_END_1] = FilterInfo{ - CAT_END, 1, 1, 1, 0, 0, 0, 1, 0, MC_1_9, 0, +1, 1, disp++, // disabled + CAT_END, 1, 1, 1, 0, 0, 0, 1, 0, 0, MC_1_9, 0, +1, 1, disp++, // disabled ":icons/the_end.png", _("End biomes 1:1 (disabled)"), _("End biomes after voronoi scaling to 1:1.") }; list[F_BIOME_END_4] = FilterInfo{ - CAT_END, 0, 1, 1, 0, 0, 0, 4, 0, MC_1_9, MC_NEWEST, +1, 0, disp++, + CAT_END, 0, 1, 1, 0, 0, 0, 4, 2, 0, MC_1_9, MC_NEWEST, +1, 0, disp++, ":icons/the_end.png", _("End biomes 1:4"), _("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, 0, MC_1_9, MC_NEWEST, +1, 0, disp++, + CAT_END, 0, 1, 1, 0, 0, 0, 16, 4, 0, MC_1_9, MC_NEWEST, +1, 0, disp++, ":icons/the_end.png", _("End biomes 1:16"), _("End biomes with normal sampling at scale 1:16. ") }; list[F_BIOME_END_64] = FilterInfo{ - CAT_END, 0, 1, 1, 0, 0, 0, 64, 0, MC_1_9, MC_NEWEST, +1, 0, disp++, + CAT_END, 0, 1, 1, 0, 0, 0, 64, 6, 0, MC_1_9, MC_NEWEST, +1, 0, disp++, ":icons/the_end.png", _("End biomes 1:64"), _("End biomes with lossy sampling at scale 1:64. ") }; list[F_SPAWN] = FilterInfo{ - CAT_OTHER, 1, 1, 1, 1, 0, 0, 1, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, + CAT_OTHER, 1, 1, 1, 1, 0, 0, 1, 0, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, ":icons/spawn.png", _("Spawn"), "" }; list[F_SLIME] = FilterInfo{ - CAT_OTHER, 0, 1, 1, 0, 0, 0, 16, 1, MC_1_0, MC_NEWEST, 0, 0, disp++, + CAT_OTHER, 0, 1, 1, 0, 0, 0, 16, 4, 1, MC_1_0, MC_NEWEST, 0, 0, disp++, ":icons/slime.png", _("Slime chunk"), "" }; list[F_FIRST_STRONGHOLD] = FilterInfo{ - CAT_OTHER, 0, 1, 1, 1, 0, 0, 1, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, + CAT_OTHER, 0, 1, 1, 1, 0, 0, 1, 0, 0, MC_1_0, MC_NEWEST, 0, 0, disp++, ":icons/stronghold.png", _("First stronghold"), _("Finds the approxmiate location of the first stronghold " @@ -397,28 +407,28 @@ static const struct FilterList }; list[F_STRONGHOLD] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, 0, 1, 1, MC_1_0, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, 1, 1, 1, 0, 0, 1, 0, 1, MC_1_0, MC_NEWEST, 0, 0, disp++, ":icons/stronghold.png", _("Stronghold"), "" }; list[F_VILLAGE] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Village, 1, 1, MC_1_0, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, 1, 1, 1, 0, Village, 1, 0, 1, MC_1_0, MC_NEWEST, 0, 0, disp++, ":icons/village.png", _("Village"), "" }; list[F_MINESHAFT] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 0, 0, Mineshaft, 1, 1, MC_1_0, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, 1, 1, 0, 0, Mineshaft, 1, 0, 1, MC_1_0, MC_NEWEST, 0, 0, disp++, ":icons/mineshaft.png", _("Abandoned mineshaft"), "" }; list[F_DESERT] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Desert_Pyramid, 1, 1, MC_1_3, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, 1, 1, 1, 0, Desert_Pyramid, 1, 0, 1, MC_1_3, MC_NEWEST, 0, 0, disp++, ":icons/desert.png", _("Desert pyramid"), _("In version 1.18+, desert pyramids depend on surface height and may fail to " @@ -426,7 +436,7 @@ static const struct FilterList }; list[F_JUNGLE] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Jungle_Temple, 1, 1, MC_1_3, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, 1, 1, 1, 0, Jungle_Temple, 1, 0, 1, MC_1_3, MC_NEWEST, 0, 0, disp++, ":icons/jungle.png", _("Jungle temple"), _("In version 1.18+, jungle temples depend on surface height and may fail to " @@ -434,28 +444,28 @@ static const struct FilterList }; list[F_HUT] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Swamp_Hut, 1, 1, MC_1_4, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, 1, 1, 1, 0, Swamp_Hut, 1, 0, 1, MC_1_4, MC_NEWEST, 0, 0, disp++, ":icons/hut.png", _("Swamp hut"), "" }; list[F_MONUMENT] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Monument, 1, 1, MC_1_8, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, 1, 1, 1, 0, Monument, 1, 0, 1, MC_1_8, MC_NEWEST, 0, 0, disp++, ":icons/monument.png", _("Ocean monument"), "" }; list[F_IGLOO] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Igloo, 1, 1, MC_1_9, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, 1, 1, 1, 0, Igloo, 1, 0, 1, MC_1_9, MC_NEWEST, 0, 0, disp++, ":icons/igloo.png", _("Igloo"), "" }; list[F_MANSION] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Mansion, 1, 1, MC_1_11, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, 1, 1, 1, 0, Mansion, 1, 0, 1, MC_1_11, MC_NEWEST, 0, 0, disp++, ":icons/mansion.png", _("Woodland mansion"), _("In version 1.18+, mansions depend on surface height and may fail to " @@ -463,21 +473,21 @@ static const struct FilterList }; list[F_RUINS] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Ocean_Ruin, 1, 1, MC_1_13, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, 1, 1, 1, 0, Ocean_Ruin, 1, 0, 1, MC_1_13, MC_NEWEST, 0, 0, disp++, ":icons/ruins.png", _("Ocean ruins"), "" }; list[F_SHIPWRECK] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Shipwreck, 1, 1, MC_1_13, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, 1, 1, 1, 0, Shipwreck, 1, 0, 1, MC_1_13, MC_NEWEST, 0, 0, disp++, ":icons/shipwreck.png", _("Shipwreck"), "" }; list[F_TREASURE] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Treasure, 1, 1, MC_1_13, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, 1, 1, 1, 0, Treasure, 1, 0, 1, MC_1_13, MC_NEWEST, 0, 0, disp++, ":icons/treasure.png", _("Buried treasure"), _("Buried treasures are always positioned near the center of a chunk " @@ -486,56 +496,56 @@ static const struct FilterList }; list[F_OUTPOST] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Outpost, 1, 1, MC_1_14, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, 1, 1, 1, 0, Outpost, 1, 0, 1, MC_1_14, MC_NEWEST, 0, 0, disp++, ":icons/outpost.png", _("Pillager outpost"), "" }; list[F_ANCIENT_CITY] = FilterInfo{ - CAT_STRUCT, 1, 1, 1, 1, 0, Ancient_City, 1, 1, MC_1_19, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 1, 1, 1, 1, 0, Ancient_City, 1, 0, 1, MC_1_19, MC_NEWEST, 0, 0, disp++, ":icons/ancient_city.png", _("Ancient City"), "" }; list[F_PORTAL] = FilterInfo{ - CAT_STRUCT, 0, 1, 1, 1, 0, Ruined_Portal, 1, 1, MC_1_16, MC_NEWEST, 0, 0, disp++, + CAT_STRUCT, 0, 1, 1, 1, 0, Ruined_Portal, 1, 0, 1, MC_1_16, MC_NEWEST, 0, 0, disp++, ":icons/portal.png", _("Ruined portal (overworld)"), "" }; list[F_PORTALN] = FilterInfo{ - CAT_STRUCT, 0, 1, 1, 1, 0, Ruined_Portal_N, 1, 1, MC_1_16, MC_NEWEST, -1, 0, disp++, + CAT_STRUCT, 0, 1, 1, 1, 0, Ruined_Portal_N, 1, 0, 1, MC_1_16, MC_NEWEST, -1, 0, disp++, ":icons/portal.png", _("Ruined portal (nether)"), "" }; list[F_FORTRESS] = FilterInfo{ - CAT_STRUCT, 0, 1, 1, 1, 0, Fortress, 1, 1, MC_1_0, MC_NEWEST, -1, 0, disp++, + CAT_STRUCT, 0, 1, 1, 1, 0, Fortress, 1, 0, 1, MC_1_0, MC_NEWEST, -1, 0, disp++, ":icons/fortress.png", _("Nether fortress"), "" }; list[F_BASTION] = FilterInfo{ - CAT_STRUCT, 0, 1, 1, 1, 0, Bastion, 1, 1, MC_1_16, MC_NEWEST, -1, 0, disp++, + CAT_STRUCT, 0, 1, 1, 1, 0, Bastion, 1, 0, 1, MC_1_16, MC_NEWEST, -1, 0, disp++, ":icons/bastion.png", _("Bastion remnant"), "" }; list[F_ENDCITY] = FilterInfo{ - CAT_STRUCT, 0, 1, 1, 1, 0, End_City, 1, 1, MC_1_9, MC_NEWEST, +1, 0, disp++, + CAT_STRUCT, 0, 1, 1, 1, 0, End_City, 1, 0, 1, MC_1_9, MC_NEWEST, +1, 0, disp++, ":icons/endcity.png", _("End city"), "" }; list[F_GATEWAY] = FilterInfo{ - CAT_STRUCT, 0, 1, 1, 1, 0, End_Gateway, 1, 1, MC_1_13, MC_NEWEST, +1, 0, disp++, + CAT_STRUCT, 0, 1, 1, 1, 0, End_Gateway, 1, 0, 1, MC_1_13, MC_NEWEST, +1, 0, disp++, ":icons/gateway.png", _("End gateway"), _("Only scattered return gateways. Does not include those generated " @@ -548,9 +558,12 @@ static const struct FilterList } g_filterinfo; + struct /*__attribute__((packed))*/ Condition -{ // data - needs to supports memset/memcpy and memory layout should be - // consistent across versions +{ + // should be POD or at least Standard Layout + // layout needs to remain consistent across versions + enum { // meta flags DISABLED = 0x0001, }; @@ -563,18 +576,31 @@ struct /*__attribute__((packed))*/ Condition int32_t x1, z1, x2, z2; int32_t save; int32_t relative; - uint32_t pad1; // unused - BiomeFilter bfilter; + uint8_t skipref; + uint8_t pad1[67]; // legacy + uint64_t biomeToFind, biomeToFindM; // inclusion biomes + uint8_t pad2[12]; // legacy oceanToFind(8), specialCnt(4) + uint8_t pad3[2]; // legacy zero initialized + uint16_t version; // condition data version + uint64_t biomeToExcl, biomeToExclM; // exclusion biomes int32_t temps[9]; int32_t count; int32_t y; uint32_t flags; int32_t rmax; // (<=0):disabled; (>0):strict upper radius - uint32_t pad2; // unused + uint8_t pad4[4]; // unused uint64_t variants; int32_t limok[6][2]; int32_t limex[6][2]; + // generated members - initialized when the search is started + uint8_t generated_start[0]; // address dummy + BiomeFilter bf; + + // initialize the generated members and perform version upgrades + bool versionUpgrade(); + bool apply(WorldInfo wi); + QString toHex() const; bool readHex(const QString& hex); @@ -588,9 +614,9 @@ struct /*__attribute__((packed))*/ Condition struct ConditionTree { QVector condvec; - QVector> references; + std::vector> references; - void set(const QVector& cv); + void set(const QVector& cv, WorldInfo wi); }; struct StructPos @@ -665,6 +691,12 @@ enum PASS_FULL_64, // run full test on a 64-bit seed }; +enum +{ + APPROX = 0x01, + MATCH_ANY = 0x10, +}; + /* Checks if a seeds satisfies the conditions tree. * Returns the lowest condition fulfillment status. */ diff --git a/src/searchthread.cpp b/src/searchthread.cpp index 97ad691..225e6df 100644 --- a/src/searchthread.cpp +++ b/src/searchthread.cpp @@ -106,7 +106,7 @@ bool SearchMaster::set( const char *mcs = mc2str(finfo.mcmin); QMessageBox::warning(parent, tr("Warning"), tr("Condition %1 requires a minimum Minecraft version of %2.") - .arg(cid).arg(mcs)); + .arg(cid, mcs)); return false; } if (wi.mc > finfo.mcmax) @@ -114,23 +114,21 @@ bool SearchMaster::set( const char *mcs = mc2str(finfo.mcmax); QMessageBox::warning(parent, tr("Warning"), tr("Condition %1 not available for Minecraft versions above %2.") - .arg(cid).arg(mcs)); + .arg(cid, mcs)); return false; } if (finfo.cat == CAT_BIOMES && c.type != F_TEMPS && c.type != F_CLIMATE_NOISE) { - uint64_t b = c.bfilter.riverToFind; - uint64_t m = c.bfilter.riverToFindM; - b &= ~((1ULL << ocean) | (1ULL << deep_ocean)); - b |= c.bfilter.oceanToFind; - if ((c.bfilter.biomeToExcl & b) || (c.bfilter.biomeToExclM & m)) + uint64_t b = c.biomeToFind; + uint64_t m = c.biomeToFindM; + if ((c.biomeToExcl & b) || (c.biomeToExclM & m)) { QMessageBox::warning(parent, tr("Warning"), tr("Biome condition with ID %1 has contradicting " "flags for include and exclude.").arg(cid)); return false; } - if (b == 0 && m == 0 && c.bfilter.biomeToExcl == 0 && c.bfilter.biomeToExclM == 0) + if ((b | m | c.biomeToExcl | c.biomeToExclM) == 0) { int button = QMessageBox::information(parent, tr("Info"), tr("Biome condition with ID %1 specifies no biomes.") @@ -157,7 +155,7 @@ bool SearchMaster::set( int cnt = __builtin_popcountll(b) + __builtin_popcountll(m); QString msg = tr("Biome condition with ID %1 includes %n " "biome(s) that do not generate in MC %2.", "", cnt) - .arg(cid).arg(mc2str(wi.mc)); + .arg(cid, mc2str(wi.mc)); QMessageBox::warning(parent, tr("Warning"), msg); return false; } @@ -185,12 +183,19 @@ bool SearchMaster::set( return false; } } + if (c.skipref && c.x1 == 0 && c.x2 == 0 && c.z1 == 0 && c.z2 == 0) + { + QMessageBox::warning(parent, tr("Warning"), + tr("Condition %1 ignores its only location of size 1.") + .arg(cid)); + return false; + } } this->searchtype = sc.searchtype; this->mc = wi.mc; this->large = wi.large; - this->condtree.set(cv); + this->condtree.set(cv, wi); this->itemsize = 1; //config.seedsPerItem; this->threadcnt = sc.threads; this->slist = slist; @@ -557,8 +562,18 @@ void SearchMaster::stop() bool SearchMaster::getProgress(uint64_t *prog, uint64_t *end, uint64_t *seed) { - QMutexLocker locker(&mutex); - + if (!mutex.tryLock(10)) + { + if (searchtype == SEARCH_BLOCKS && slist.empty()) + { // a block search with no list looks for candidates in the search + // master and can therefore make progress outside of workers + *prog = this->prog; + *end = this->scnt; + *seed = this->seed; + return true; + } + return false; + } *prog = this->prog; *end = this->scnt; *seed = this->seed; @@ -573,6 +588,7 @@ bool SearchMaster::getProgress(uint64_t *prog, uint64_t *end, uint64_t *seed) valid = true; } } + mutex.unlock(); return valid; } @@ -676,6 +692,9 @@ bool SearchMaster::requestItem(SearchWorker *item) { break; } + // update progress for skipped block + seed = low; + prog += 0x10000; } if (low > MASK48) isdone = true; diff --git a/src/searchthread.h b/src/searchthread.h index 1fc768f..55895a2 100644 --- a/src/searchthread.h +++ b/src/searchthread.h @@ -41,30 +41,30 @@ public slots: void searchFinish(bool done); public: - FormSearchControl * parent; - QVector workers; - - QMutex mutex; - std::atomic_bool abort; - - QElapsedTimer timer; - uint64_t count; - - int searchtype; - int mc; - int large; - ConditionTree condtree; - int itemsize; // number of seeds per search item - int threadcnt; // numbr of worker threads - Gen48Settings gen48; // 48-bit generator settings - std::vector slist; // candidate list - uint64_t idx; // index within candidate list - uint64_t scnt; // search space size - uint64_t prog; // search space progress tracker - uint64_t seed; // current seed (next to be processed) - uint64_t smin; - uint64_t smax; - bool isdone; + FormSearchControl * parent; + std::vector workers; + + QMutex mutex; + std::atomic_bool abort; + + QElapsedTimer timer; + uint64_t count; + + int searchtype; + int mc; + int large; + ConditionTree condtree; + int itemsize; // number of seeds per search item + int threadcnt; // numbr of worker threads + Gen48Settings gen48; // 48-bit generator settings + std::vector slist; // candidate list + uint64_t idx; // index within candidate list + uint64_t scnt; // search space size + uint64_t prog; // search space progress tracker + uint64_t seed; // current seed (next to be processed) + uint64_t smin; + uint64_t smax; + bool isdone; }; diff --git a/src/structuredialog.cpp b/src/structuredialog.cpp new file mode 100644 index 0000000..52dcb79 --- /dev/null +++ b/src/structuredialog.cpp @@ -0,0 +1,76 @@ +#include "structuredialog.h" +#include "ui_structuredialog.h" + +#include +#include +#include +#include + +#include "quad.h" +#include "cutil.h" + +StructureDialog::StructureDialog(QWidget *parent) + : QDialog(parent) + , ui(new Ui::StructureDialog) + , modified() +{ + ui->setupUi(this); + QGridLayout *grid = new QGridLayout(ui->groupVis); + + loadStructVis(structvis); + + grid->addWidget(new QLabel(tr("blocks per pixel")), 0, 2); + + int i = 1; + for (auto it : structvis) + { + int opt = it.first; + double scale = it.second; + int j = 0; + + QLabel *icon = new QLabel(); + icon->setPixmap(getMapIcon(opt)); + grid->addWidget(icon, i, j++); + + QString name = struct2str(mapopt2stype(opt)); + QLabel *label = new QLabel(name + ":"); + grid->addWidget(label, i, j++); + + QLineEdit *line = new QLineEdit(); + line->setValidator(new QDoubleValidator(0.125, 256.0, 3, grid)); + line->setText(QString::number(scale)); + entries[it.first] = line; + grid->addWidget(line, i, j++); + i++; + } + + connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &StructureDialog::onAccept); + QPushButton *reset = ui->buttonBox->button(QDialogButtonBox::RestoreDefaults); + connect(reset, &QPushButton::clicked, this, &StructureDialog::onReset); +} + +StructureDialog::~StructureDialog() +{ + delete ui; +} + +void StructureDialog::onAccept() +{ + for (auto it : entries) + { + QLineEdit *line = it.second; + modified |= line->isModified(); + bool ok; + double scale = line->text().toDouble(&ok); + if (ok) + structvis[it.first] = scale; + } +} + +void StructureDialog::onReset() +{ + double scale = 32; + for (auto it : entries) + it.second->setText(QString::number(scale)); + modified = true; +} diff --git a/src/structuredialog.h b/src/structuredialog.h new file mode 100644 index 0000000..d7ff40a --- /dev/null +++ b/src/structuredialog.h @@ -0,0 +1,35 @@ +#ifndef STRUCTUREDIALOG_H +#define STRUCTUREDIALOG_H + +#include + +#include + +namespace Ui { +class StructureDialog; +} + +class QLineEdit; + +class StructureDialog : public QDialog +{ + Q_OBJECT + +public: + explicit StructureDialog(QWidget *parent = nullptr); + ~StructureDialog(); + +public slots: + void onAccept(); + void onReset(); + +private: + Ui::StructureDialog *ui; + std::map entries; + +public: + std::map structvis; + bool modified; +}; + +#endif // STRUCTUREDIALOG_H diff --git a/src/structuredialog.ui b/src/structuredialog.ui new file mode 100644 index 0000000..d7d97b1 --- /dev/null +++ b/src/structuredialog.ui @@ -0,0 +1,69 @@ + + + StructureDialog + + + Structure Zoom Limits + + + + :/icons/map.png:/icons/map.png + + + + + + Maximum scale for structure visibility + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults + + + + + + + + + + + buttonBox + accepted() + StructureDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + StructureDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +