diff --git a/cubiomes b/cubiomes
index ae58fc4..0af31b4 160000
--- a/cubiomes
+++ b/cubiomes
@@ -1 +1 @@
-Subproject commit ae58fc49833db481ae493015ddffce317f1ace9b
+Subproject commit 0af31b4e7eeb14a58c2bd9a4c4c68b97b4a7d6e8
diff --git a/cubiomes-viewer.pro b/cubiomes-viewer.pro
index 1d2f6e7..9e8b305 100644
--- a/cubiomes-viewer.pro
+++ b/cubiomes-viewer.pro
@@ -13,7 +13,7 @@ QT += core widgets
CHARSET = -finput-charset=UTF-8 -fexec-charset=UTF-8
QMAKE_CFLAGS = $$CHARSET -fwrapv -DSTRUCT_CONFIG_OVERRIDE=1
QMAKE_CXXFLAGS = $$QMAKE_CFLAGS
-QMAKE_CXXFLAGS_RELEASE *= -O3
+QMAKE_CXXFLAGS_RELEASE *= -O3 -g3
greaterThan(QT_MAJOR_VERSION, 5) {
QMAKE_CXXFLAGS += -std=gnu++17
diff --git a/etc/com.github.cubitect.cubiomes-viewer.metainfo.xml b/etc/com.github.cubitect.cubiomes-viewer.metainfo.xml
index 4d284b3..01aeace 100644
--- a/etc/com.github.cubitect.cubiomes-viewer.metainfo.xml
+++ b/etc/com.github.cubitect.cubiomes-viewer.metainfo.xml
@@ -60,6 +60,11 @@
+
+ 900
+ 1600
+
+
https://github.com/cubitect/cubiomes-viewer
https://github.com/cubitect/cubiomes-viewer/issues
diff --git a/rc/icons.qrc b/rc/icons.qrc
index 07b0ae1..91beae3 100644
--- a/rc/icons.qrc
+++ b/rc/icons.qrc
@@ -83,5 +83,7 @@
icons/zoom_in.png
icons/zoom_out.png
icons/zoom.png
+ icons/chambers.png
+ icons/chambers_d.png
diff --git a/rc/icons/chambers.png b/rc/icons/chambers.png
new file mode 100644
index 0000000..880d34b
Binary files /dev/null and b/rc/icons/chambers.png differ
diff --git a/rc/icons/chambers_d.png b/rc/icons/chambers_d.png
new file mode 100644
index 0000000..880d34b
Binary files /dev/null and b/rc/icons/chambers_d.png differ
diff --git a/src/conditiondialog.cpp b/src/conditiondialog.cpp
index ad5e7d1..27b504a 100644
--- a/src/conditiondialog.cpp
+++ b/src/conditiondialog.cpp
@@ -107,13 +107,17 @@ ConditionDialog::ConditionDialog(FormConditions *parent, MapView *mapview, Confi
ui->lineBiomeSize->setValidator(new QIntValidator(1, INT_MAX, this));
ui->lineTolerance->setValidator(new QIntValidator(0, 255, this));
- ui->comboY->lineEdit()->setValidator(new QIntValidator(-64, 320, this));
+ initComboY(ui->comboY1, initcond ? initcond->y : 256);
+ initComboY(ui->comboY2, initcond ? initcond->y : 256);
- ui->lineMin->setValidator(new QDoubleValidator(-1e6, 1e6, 4, this));
- ui->lineMax->setValidator(new QDoubleValidator(-1e6, 1e6, 4, this));
+ QDoubleValidator *paraval = new QDoubleValidator(-1e6, 1e6, 4, this);
+ ui->lineMin->setValidator(paraval);
+ ui->lineMax->setValidator(paraval);
- ui->lineCoverage->setValidator(new QDoubleValidator(0.0001, 100.0000, 4, this));
- ui->lineConfidence->setValidator(new QDoubleValidator(0.0001, 99.9999, 4, this));
+ ui->lineCoverage2->setValidator(new QDoubleValidator(0.0001, 100.0000, 4, this));
+ ui->lineCoverage1->setValidator(new QDoubleValidator(0.0001, 100.0000, 4, this));
+ ui->lineConfidence1->setValidator(new QDoubleValidator(0.0001, 99.9999, 4, this));
+ ui->lineConfidence2->setValidator(new QDoubleValidator(0.0001, 99.9999, 4, this));
//qobject_cast(ui->comboCat->view())->setSpacing(1);
//qobject_cast(ui->comboType->view())->setSpacing(1);
@@ -301,8 +305,10 @@ ConditionDialog::ConditionDialog(FormConditions *parent, MapView *mapview, Confi
ui->checkSkipRef->setChecked(false);
ui->radioSquare->setChecked(true);
ui->checkRadius->setChecked(false);
- ui->lineCoverage->setText("50");
- ui->lineConfidence->setText("95");
+ ui->lineCoverage1->setText("50");
+ ui->lineCoverage2->setText("50");
+ ui->lineConfidence1->setText("95");
+ ui->lineConfidence2->setText("95");
ui->comboScale->setCurrentIndex(1);
onCheckStartChanged(false);
on_comboClimatePara_currentIndexChanged(0);
@@ -341,8 +347,11 @@ ConditionDialog::ConditionDialog(FormConditions *parent, MapView *mapview, Confi
on_comboClimatePara_currentIndexChanged(cond.para);
ui->comboOctaves->setCurrentIndex(cond.octave);
ui->comboMinMax->setCurrentIndex((cond.minmax & Condition::E_LOCATE_MAX) ? 1 : 0);
- ui->lineMin->setText((cond.minmax & Condition::E_TEST_LOWER) ? QString::number(cond.vmin) : "");
- ui->lineMax->setText((cond.minmax & Condition::E_TEST_UPPER) ? QString::number(cond.vmax) : "");
+ QString vmin, vmax;
+ if (cond.minmax & Condition::E_TEST_LOWER) vmin = QString::number(cond.vmin);
+ if (cond.minmax & Condition::E_TEST_UPPER) vmax = QString::number(cond.vmax);
+ ui->lineMin->setText(vmin);
+ ui->lineMax->setText(vmax);
ui->checkInvertRange->setChecked(cond.flags & Condition::FLG_INVERT);
updateMode();
@@ -357,19 +366,10 @@ ConditionDialog::ConditionDialog(FormConditions *parent, MapView *mapview, Confi
ui->checkApprox->setChecked(cond.flags & Condition::FLG_APPROX);
ui->checkMatchAny->setChecked(cond.flags & Condition::FLG_MATCH_ANY);
ui->checkSamplePos->setChecked(cond.count == 1);
- ui->lineCoverage->setText(QString::number(cond.converage ? cond.converage * 100 : 50));
- ui->lineConfidence->setText(QString::number(cond.confidence ? cond.confidence * 100 : 95));
-
- int i, n = ui->comboY->count();
- for (i = 0; i < n; i++)
- if (ui->comboY->itemText(i).section(' ', 0, 0).toInt() == cond.y)
- break;
- if (i >= n)
- {
- ui->comboY->addItem(QString::number(cond.y));
- ui->comboY2->addItem(QString::number(cond.y));
- }
- ui->comboY->setCurrentIndex(i);
+ ui->lineCoverage1->setText(QString::number(cond.converage ? cond.converage * 100 : 50));
+ ui->lineCoverage2->setText(QString::number(cond.converage ? cond.converage * 100 : 50));
+ ui->lineConfidence1->setText(QString::number(cond.confidence ? cond.confidence * 100 : 95));
+ ui->lineConfidence2->setText(QString::number(cond.confidence ? cond.confidence * 100 : 95));
if (cond.x1 == cond.z1 && cond.x1 == -cond.x2 && cond.x1 == -cond.z2)
{
@@ -486,6 +486,19 @@ void ConditionDialog::addTempCat(int temp, QString name)
}
}
+void ConditionDialog::initComboY(QComboBox *cb, int y)
+{
+ if (!cb->lineEdit()->validator())
+ cb->lineEdit()->setValidator(new QIntValidator(-64, 320, this));
+ int i, n = cb->count();
+ for (i = 0; i < n; i++)
+ if (cb->itemText(i).section(' ', 0, 0).toInt() == y)
+ break;
+ if (i >= n)
+ cb->addItem(QString::number(y));
+ cb->setCurrentIndex(i);
+}
+
void ConditionDialog::updateMode()
{
int filterindex = ui->comboType->currentData().toInt();
@@ -566,7 +579,8 @@ void ConditionDialog::updateMode()
ui->checkSkipRef->setEnabled(cnt);
ui->labelY->setEnabled(ft.hasy);
- ui->comboY->setEnabled(ft.hasy);
+ ui->comboY1->setEnabled(ft.hasy);
+ ui->comboY2->setEnabled(ft.hasy);
if (filterindex == F_TEMPS)
{
@@ -578,7 +592,15 @@ void ConditionDialog::updateMode()
}
else if (filterindex == F_CLIMATE_MINMAX)
{
- ui->stackedWidget->setCurrentWidget(ui->pageMinMax);
+ ui->stackedWidget->setCurrentWidget(ui->pageNoise);
+ ui->stackedNoise->setCurrentWidget(ui->pageNoiseMinMax);
+ ui->groupBoxNoise->setTitle("Locate climate minimum/maximum");
+ }
+ else if (filterindex == F_NOISE_SAMPLE)
+ {
+ ui->stackedWidget->setCurrentWidget(ui->pageNoise);
+ ui->stackedNoise->setCurrentWidget(ui->pageNoiseSample);
+ ui->groupBoxNoise->setTitle("Climate noise samples");
}
else if (filterindex == F_BIOME_CENTER || filterindex == F_BIOME_CENTER_256)
{
@@ -1035,7 +1057,7 @@ void ConditionDialog::onAccept()
else
c.rmax = 0;
- c.y = ui->comboY->currentText().section(' ', 0, 0).toInt();
+ c.y = ui->comboY1->currentText().section(' ', 0, 0).toInt();
c.flags = 0;
if (ui->checkApprox->isChecked())
@@ -1071,8 +1093,8 @@ void ConditionDialog::onAccept()
}
}
c.count = ui->checkSamplePos->isChecked() ? 1 : 0;
- c.converage = ui->lineCoverage->text().toFloat() / 100.0;
- c.confidence = ui->lineConfidence->text().toFloat() / 100.0;
+ c.converage = ui->lineCoverage1->text().toFloat() / 100.0;
+ c.confidence = ui->lineConfidence1->text().toFloat() / 100.0;
c.step = 0;
if (c.type == F_BIOME || c.type == F_BIOME_NETHER || c.type == F_BIOME_END)
c.step = ui->comboScale->currentData().toInt();
@@ -1082,15 +1104,15 @@ void ConditionDialog::onAccept()
if (ui->stackedWidget->currentWidget() == ui->pageBiomeCenter)
{
c.biomeId = ui->comboMatchBiome->currentData().toInt();
+ c.y = ui->comboY2->currentText().section(' ', 0, 0).toInt();
c.biomeSize = ui->lineBiomeSize->text().toInt();
c.tol = ui->lineTolerance->text().toInt();
}
- if (ui->stackedWidget->currentWidget() == ui->pageMinMax)
+ if (ui->stackedWidget->currentWidget() == ui->pageNoise)
{
c.para = ui->comboClimatePara->currentData().toInt();
c.octave = ui->comboOctaves->currentIndex();
- c.minmax = ui->comboMinMax->currentIndex() == 0 ?
- Condition::E_LOCATE_MIN : Condition::E_LOCATE_MAX;
+ c.minmax = ui->comboMinMax->currentIndex() == 0 ? Condition::E_LOCATE_MIN : Condition::E_LOCATE_MAX;
bool ok1, ok2;
c.vmin = ui->lineMin->text().toFloat(&ok1);
if (ok1) c.minmax |= Condition::E_TEST_LOWER;
@@ -1098,6 +1120,9 @@ void ConditionDialog::onAccept()
if (ok2) c.minmax |= Condition::E_TEST_UPPER;
if (ok1 && ok2 && c.vmin > c.vmax)
std::swap(c.vmin, c.vmax);
+ c.count = ui->checkSamplePos2->isChecked() ? 1 : 0;
+ c.converage = ui->lineCoverage2->text().toFloat() / 100.0;
+ c.confidence = ui->lineConfidence2->text().toFloat() / 100.0;
}
if (ui->stackedWidget->currentWidget() == ui->pageTemps)
{
@@ -1653,13 +1678,13 @@ void ConditionDialog::on_comboOctaves_currentIndexChanged(int)
ui->comboOctaves->setToolTip(ui->comboOctaves->currentData(Qt::ToolTipRole).toString());
}
-void ConditionDialog::on_comboY_currentTextChanged(const QString &text)
+void ConditionDialog::on_comboY1_currentTextChanged(const QString &text)
{
if (ui->comboY2->currentText() != text)
ui->comboY2->setCurrentText(text);
}
void ConditionDialog::on_comboY2_currentTextChanged(const QString &text)
{
- if (ui->comboY->currentText() != text)
- ui->comboY->setCurrentText(text);
+ if (ui->comboY1->currentText() != text)
+ ui->comboY1->setCurrentText(text);
}
diff --git a/src/conditiondialog.h b/src/conditiondialog.h
index 470c88b..5ba310c 100644
--- a/src/conditiondialog.h
+++ b/src/conditiondialog.h
@@ -56,6 +56,7 @@ public:
virtual ~ConditionDialog();
void addTempCat(int temp, QString name);
+ void initComboY(QComboBox *cb, int y);
void updateMode();
void updateBiomeSelection();
bool warnIfBad(Condition cond);
@@ -112,7 +113,7 @@ private slots:
void on_comboClimatePara_currentIndexChanged(int index);
void on_comboOctaves_currentIndexChanged(int index);
- void on_comboY_currentTextChanged(const QString &text);
+ void on_comboY1_currentTextChanged(const QString &text);
void on_comboY2_currentTextChanged(const QString &text);
private:
diff --git a/src/conditiondialog.ui b/src/conditiondialog.ui
index e8ca63f..f8335e7 100644
--- a/src/conditiondialog.ui
+++ b/src/conditiondialog.ui
@@ -392,7 +392,7 @@ QPushButton:hover {
0
- 2
+ 5
@@ -631,14 +631,14 @@ yield each sampled position individually
-
-
+
Proportion of the area that has to be of the included biomes
-
-
+
Statistical confidence that the coverage has been reached
@@ -649,7 +649,7 @@ yield each sampled position individually
-
-
+
true
@@ -1100,7 +1100,7 @@ yield each sampled position individually
-
+
0
@@ -1115,18 +1115,196 @@ yield each sampled position individually
0
-
-
+
Locate climate minimum/maximum
-
-
-
-
- <html><head/><body><p>Lower bound (inclusive)</p></body></html>
+
-
+
+
+ -
+
+
+ 0
-
- -Infinity
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
-
+
+ Minimum
+
+
+ -
+
+ Maximum
+
+
+
+
+ -
+
+
+ Yield position of:
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Preferred
+
+
+
+ 180
+ 20
+
+
+
+
+
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Instead of estimating the center of the allowed biome area,
+yield each sampled position individually
+
+
+ Yield individual samples
+
+
+
+ -
+
+
+ -
+
+
+ Statistical confidence that the coverage has been reached
+
+
+ Confidence (%):
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Statistical confidence that the coverage has been reached
+
+
+
+ -
+
+
+ Required coverage (%):
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Preferred
+
+
+
+ 180
+ 20
+
+
+
+
+
+
+
+
+ -
+
+
+ If value is in range:
+
+
+
+ -
+
+
+ -
+
+
+
+ -
+
+
+
+ Monospace
+
@@ -1137,36 +1315,6 @@ yield each sampled position individually
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 252
-
-
-
-
- -
-
-
- Only match if value is outside the given range
-
-
- Invert range
-
-
-
- -
-
-
- Yield position of:
-
-
-
-
@@ -1184,45 +1332,41 @@ yield each sampled position individually
- -
-
+
-
+
+
+ <html><head/><body><p>Lower bound (inclusive)</p></body></html>
+
+
+ -Infinity
+
+
+
+ -
+
+
+ Only match if value is outside the given range
+
- If value is in range:
+ Invert range
- -
-
-
-
-
- Minimum
-
-
- -
-
- Maximum
-
-
-
-
- -
-
-
- -
-
-
- -
+
-
+
+
+ Qt::Horizontal
-
-
- -
-
-
-
- Monospace
-
+
+ QSizePolicy::Preferred
-
+
+
+ 180
+ 20
+
+
+
diff --git a/src/config.cpp b/src/config.cpp
index 66e6065..f695c18 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -148,7 +148,7 @@ QString mapopt2display(int opt)
case D_VILLAGE: return QApplication::translate("Map", "Village");
case D_MANSION: return QApplication::translate("Map", "Woodland Mansion");
case D_MONUMENT: return QApplication::translate("Map", "Ocean Monument");
- case D_RUINS: return QApplication::translate("Map", "Ocean Ruins");
+ case D_OCEANRUIN: return QApplication::translate("Map", "Ocean Ruin");
case D_SHIPWRECK: return QApplication::translate("Map", "Shipwreck");
case D_TREASURE: return QApplication::translate("Map", "Buried Treasure");
case D_MINESHAFT: return QApplication::translate("Map", "Mineshaft");
@@ -156,7 +156,8 @@ QString mapopt2display(int opt)
case D_GEODE: return QApplication::translate("Map", "Geode");
case D_OUTPOST: return QApplication::translate("Map", "Pillager Outpost");
case D_ANCIENTCITY: return QApplication::translate("Map", "Ancient City");
- case D_TRAILS: return QApplication::translate("Map", "Trail Ruins");
+ case D_TRAILRUINS: return QApplication::translate("Map", "Trail Ruins");
+ case D_CHAMBERS: return QApplication::translate("Map", "Trial Chambers");
case D_PORTAL: return QApplication::translate("Map", "Ruined Portal");
case D_PORTALN: return QApplication::translate("Map", "Ruined Portal (Nether)");
case D_SPAWN: return QApplication::translate("Map", "Spawn");
@@ -182,7 +183,7 @@ const char *mapopt2str(int opt) // to resource string
case D_VILLAGE: return "village";
case D_MANSION: return "mansion";
case D_MONUMENT: return "monument";
- case D_RUINS: return "ruins";
+ case D_OCEANRUIN: return "ruins";
case D_SHIPWRECK: return "shipwreck";
case D_TREASURE: return "treasure";
case D_MINESHAFT: return "mineshaft";
@@ -190,7 +191,8 @@ const char *mapopt2str(int opt) // to resource string
case D_GEODE: return "geode";
case D_OUTPOST: return "outpost";
case D_ANCIENTCITY: return "ancient_city";
- case D_TRAILS: return "trails";
+ case D_TRAILRUINS: return "trails";
+ case D_CHAMBERS: return "chambers";
case D_PORTAL: return "portal";
case D_PORTALN: return "portaln";
case D_SPAWN: return "spawn";
@@ -214,7 +216,7 @@ int str2mapopt(const char *s) // from resource string
if (!strcmp(s, "village")) return D_VILLAGE;
if (!strcmp(s, "mansion")) return D_MANSION;
if (!strcmp(s, "monument")) return D_MONUMENT;
- if (!strcmp(s, "ruins")) return D_RUINS;
+ if (!strcmp(s, "ruins")) return D_OCEANRUIN;
if (!strcmp(s, "shipwreck")) return D_SHIPWRECK;
if (!strcmp(s, "treasure")) return D_TREASURE;
if (!strcmp(s, "mineshaft")) return D_MINESHAFT;
@@ -222,7 +224,8 @@ int str2mapopt(const char *s) // from resource string
if (!strcmp(s, "geode")) return D_GEODE;
if (!strcmp(s, "outpost")) return D_OUTPOST;
if (!strcmp(s, "ancient_city")) return D_ANCIENTCITY;
- if (!strcmp(s, "trails")) return D_TRAILS;
+ if (!strcmp(s, "trails")) return D_TRAILRUINS;
+ if (!strcmp(s, "chambers")) return D_CHAMBERS;
if (!strcmp(s, "portal")) return D_PORTAL;
if (!strcmp(s, "portaln")) return D_PORTALN;
if (!strcmp(s, "spawn")) return D_SPAWN;
@@ -245,7 +248,7 @@ int mapopt2stype(int opt)
case D_VILLAGE: return Village;
case D_MANSION: return Mansion;
case D_MONUMENT: return Monument;
- case D_RUINS: return Ocean_Ruin;
+ case D_OCEANRUIN: return Ocean_Ruin;
case D_SHIPWRECK: return Shipwreck;
case D_TREASURE: return Treasure;
case D_MINESHAFT: return Mineshaft;
@@ -253,7 +256,8 @@ int mapopt2stype(int opt)
case D_GEODE: return Geode;
case D_OUTPOST: return Outpost;
case D_ANCIENTCITY: return Ancient_City;
- case D_TRAILS: return Trail_Ruin;
+ case D_TRAILRUINS: return Trail_Ruins;
+ case D_CHAMBERS: return Trial_Chambers;
case D_PORTAL: return Ruined_Portal;
case D_PORTALN: return Ruined_Portal_N;
case D_FORTESS: return Fortress;
diff --git a/src/config.h b/src/config.h
index a0c322e..4909816 100644
--- a/src/config.h
+++ b/src/config.h
@@ -109,7 +109,7 @@ enum {
D_VILLAGE,
D_MANSION,
D_MONUMENT,
- D_RUINS,
+ D_OCEANRUIN,
D_SHIPWRECK,
D_TREASURE,
D_MINESHAFT,
@@ -117,7 +117,8 @@ enum {
D_GEODE,
D_OUTPOST,
D_ANCIENTCITY,
- D_TRAILS,
+ D_TRAILRUINS,
+ D_CHAMBERS,
D_PORTAL,
D_PORTALN,
D_FORTESS,
diff --git a/src/formsearchcontrol.cpp b/src/formsearchcontrol.cpp
index bcd1ebd..6e32bae 100644
--- a/src/formsearchcontrol.cpp
+++ b/src/formsearchcontrol.cpp
@@ -155,7 +155,7 @@ FormSearchControl::FormSearchControl(MainWindow *parent)
FormSearchControl::~FormSearchControl()
{
stimer.stop();
- sthread.stop(); // tell search to stop at next convenience
+ sthread.stopSearch();
delete ui;
}
@@ -227,9 +227,7 @@ bool FormSearchControl::setSearchConfig(SearchConfig s, bool quiet)
void FormSearchControl::stopSearch()
{
- sthread.stop();
- //sthread.quit(); // tell the event loop to exit
- //sthread.wait(); // wait for search to finish
+ sthread.stopSearch();
onBufferTimeout();
}
@@ -394,7 +392,7 @@ void FormSearchControl::on_buttonStart_clicked()
searchLockUi(true);
nextupdate = 0;
updt = 20;
- sthread.start();
+ sthread.startSearch();
elapsed.start();
stimer.start(250);
}
@@ -635,12 +633,12 @@ int FormSearchControl::searchResultsAdd(std::vector seeds, bool counto
if (n >= config.maxMatching)
{
- sthread.stop();
+ sthread.stopSearch();
discarded = true;
}
if (n + (ssize_t)seeds.size() > config.maxMatching)
{
- sthread.stop();
+ sthread.stopSearch();
discarded = true;
seeds.resize(config.maxMatching - n);
}
@@ -671,7 +669,7 @@ int FormSearchControl::searchResultsAdd(std::vector seeds, bool counto
int addcnt = n - nold;
if (ui->checkStop->isChecked() && addcnt)
- sthread.stop();
+ sthread.stopSearch();
if (!countonly && discarded)
{
diff --git a/src/headless.cpp b/src/headless.cpp
index 0f251a7..914c727 100644
--- a/src/headless.cpp
+++ b/src/headless.cpp
@@ -149,7 +149,7 @@ void Headless::run()
session.writeHeader(resultstream);
resultstream.flush();
- sthread.start();
+ sthread.startSearch();
elapsed.start();
if (resultfile.isOpen())
diff --git a/src/layerdialog.cpp b/src/layerdialog.cpp
index df01123..e507256 100644
--- a/src/layerdialog.cpp
+++ b/src/layerdialog.cpp
@@ -73,7 +73,7 @@ bool getLayerOptionInfo(LayerOptInfo *info, int mode, int disp, WorldInfo wi)
if (disp == 0)
{
- txt += QString("%1.. x%2").arg(QChar(0x03A3)).arg(amptot, 0, 'f', 6);
+ txt += QString("%1.. (all) x%2").arg(QChar(0x03A3)).arg(amptot, 0, 'f', 6);
tip += QApplication::translate("LayerDialog", "All octaves");
}
else
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index e517cd5..cfcdf51 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -174,7 +174,7 @@ MainWindow::MainWindow(QString sessionpath, QString resultspath, QWidget *parent
addMapAction(D_MONUMENT);
addMapAction(D_IGLOO);
addMapAction(D_MANSION);
- addMapAction(D_RUINS);
+ addMapAction(D_OCEANRUIN);
addMapAction(D_SHIPWRECK);
addMapAction(D_TREASURE);
addMapAction(D_WELL);
@@ -182,7 +182,8 @@ MainWindow::MainWindow(QString sessionpath, QString resultspath, QWidget *parent
addMapAction(D_OUTPOST);
addMapAction(D_PORTAL);
addMapAction(D_ANCIENTCITY);
- addMapAction(D_TRAILS);
+ addMapAction(D_TRAILRUINS);
+ addMapAction(D_CHAMBERS);
ui->toolBar->addSeparator();
addMapAction(D_FORTESS);
addMapAction(D_BASTION);
diff --git a/src/mapview.cpp b/src/mapview.cpp
index a26a39b..5ac7bed 100644
--- a/src/mapview.cpp
+++ b/src/mapview.cpp
@@ -381,6 +381,7 @@ void MapView::showContextMenu(const QPoint &pos)
std::vector<_cpy_dat> cpy_dat;
VarPos vp = getActivePos();
+ const QPixmap *icon = 0;
if (vp.type != -1)
{ // structure has a known size / location
@@ -389,7 +390,7 @@ void MapView::showContextMenu(const QPoint &pos)
{
const Piece& pc = vp.pieces[0];
midx = (pc.bb0.x + pc.bb1.x) >> 1;
- midy = (pc.bb0.y);
+ midy = (pc.bb0.y) + 1;
midz = (pc.bb0.z + pc.bb1.z) >> 1;
}
else
@@ -405,6 +406,7 @@ void MapView::showContextMenu(const QPoint &pos)
}
}
cpy_dat.push_back({ tr("Copy tp:"), QString::asprintf("/tp @p %d %d %d", midx, midy, midz) });
+ icon = &getMapIcon(world ? world->selopt : -1, &vp);
}
cpy_dat.push_back({ tr("Copy tp:"), QString::asprintf("/tp @p %d ~ %d", vp.p.x, vp.p.z) });
cpy_dat.push_back({ tr("Copy coords:"), QString::asprintf("%d %d", vp.p.x, vp.p.z) });
@@ -434,7 +436,10 @@ void MapView::showContextMenu(const QPoint &pos)
while (txtWidth(menu->fontMetrics(), txt + " ") < wmax)
txt += " ";
txt += it.cpy;
- menu->addAction(txt, [=](){ this->copyText(it.cpy); });
+ if (icon)
+ menu->addAction(*icon, txt, [=](){ this->copyText(it.cpy); });
+ else
+ menu->addAction(txt, [=](){ this->copyText(it.cpy); });
}
//menu->addAction(tr("Animation"), this, &MapView::runAni);
diff --git a/src/search.cpp b/src/search.cpp
index c3c5acb..0f830fe 100644
--- a/src/search.cpp
+++ b/src/search.cpp
@@ -249,7 +249,15 @@ QString ConditionTree::set(const std::vector& cv, int mc)
}
SearchThreadEnv::SearchThreadEnv()
- : condtree(),mc(),large(),seed(),surfdim(DIM_UNDEF),octaves(),l_states()
+: condtree()
+, mc()
+, large()
+, seed()
+, surfdim(DIM_UNDEF)
+, octaves()
+, searchpass(PASS_FAST_48)
+, stop()
+, l_states()
{
memset(&g, 0, sizeof(g));
memset(&sn, 0, sizeof(sn));
@@ -323,7 +331,7 @@ void SearchThreadEnv::init4Noise(int nptype, int octaves)
{
if (octaves <= 0)
octaves = INT_MAX;
- if (g.bn.nptype == nptype && this->octaves >= octaves)
+ if (g.bn.nptype == nptype && this->octaves == octaves)
return; // already initialized for parameter
if (seed == g.seed && g.bn.nptype == -1)
return; // fully initialized biome noise
@@ -340,12 +348,22 @@ void SearchThreadEnv::prepareSurfaceNoise(int dim)
}
}
+// The position buffers can exceed the stacksize on some platforms,
+// and dynamic heap allocation is too slow. So instead, we assign a
+// static memory region on the heap.
+static Pos* getPosBuf(uint32_t node)
+{
+#if DEBUG
+ if (node >= 100) return NULL; // fatal
+#endif
+ thread_local std::vector buf(100 * MAX_INSTANCES);
+ return &buf[node * MAX_INSTANCES];
+}
+
static
-int testTreeAt(
+int _testTreeAt(
Pos at, // relative origin
SearchThreadEnv * env, // thread-local environment
- int pass, // search pass
- std::atomic_bool * abort, // abort signal
Pos * path, // output center position(s)
int node
)
@@ -356,8 +374,7 @@ int testTreeAt(
int st, br;
int rx1, rz1, rx2, rz2;
Pos pos;
-
- std::vector inst(MAX_INSTANCES);
+ Pos *inst = getPosBuf(node);
switch (c.type)
{
@@ -424,8 +441,8 @@ int testTreeAt(
int sta = COND_OK;
for (int b : branches)
{
- int stb = testTreeAt(pos, env, pass, abort, path, b);
- if (*abort)
+ int stb = _testTreeAt(pos, env, path, b);
+ if (*env->stop)
return COND_FAILED;
if (stb < sta)
sta = stb;
@@ -471,8 +488,8 @@ int testTreeAt(
st = COND_OK;
for (int b : branches)
{
- int sta = testTreeAt(pos, env, pass, abort, path, b);
- if (*abort)
+ int sta = _testTreeAt(pos, env, path, b);
+ if (*env->stop)
return COND_FAILED;
if (sta < st)
st = sta;
@@ -497,8 +514,8 @@ int testTreeAt(
st = COND_FAILED;
for (int b : branches)
{
- int sta = testTreeAt(at, env, pass, abort, path, b);
- if (*abort)
+ int sta = _testTreeAt(at, env, path, b);
+ if (*env->stop)
return COND_FAILED;
if (sta > st)
st = sta;
@@ -528,8 +545,8 @@ int testTreeAt(
st = COND_OK;
for (int b : branches)
{
- int sta = testTreeAt(at, env, pass, abort, path, b);
- if (*abort)
+ int sta = _testTreeAt(at, env, path, b);
+ if (*env->stop)
return COND_FAILED;
if (sta == COND_OK) { st = COND_FAILED; break; }
else if (sta == COND_FAILED) { st = COND_OK; break; }
@@ -544,8 +561,8 @@ int testTreeAt(
Pos *buf = path ? path : &inst[0];
for (int b : branches)
{
- int sta = testTreeAt(at, env, pass, abort, buf, b);
- if (*abort)
+ int sta = _testTreeAt(at, env, buf, b);
+ if (*env->stop)
return COND_FAILED;
if (sta < st) {
st = sta;
@@ -555,8 +572,8 @@ int testTreeAt(
}
if (st <= COND_MAYBE_POS_INVAL)
return st;
- int sta = runCheckScript(L, at, env, pass, buf, &c);
- if (*abort)
+ int sta = runCheckScript(L, at, env, env->searchpass, buf, &c);
+ if (*env->stop)
return COND_FAILED;
if (sta < st)
st = sta;
@@ -568,7 +585,7 @@ int testTreeAt(
if (branches.empty())
{ // this is a leaf node => check only for presence of instances
int icnt = c.count;
- st = testCondAt(at, env, pass, abort, &inst[0], &icnt, &c);
+ st = testCondAt(at, env, &inst[0], &icnt, &c);
if (path && st >= COND_MAYBE_POS_VALID)
{
if (icnt == 1)
@@ -593,7 +610,7 @@ int testTreeAt(
}
else
{
- st = testCondAt(at, env, pass, abort, &inst[0], NULL, &c);
+ st = testCondAt(at, env, &inst[0], NULL, &c);
if (st == COND_FAILED || st == COND_MAYBE_POS_INVAL)
return st;
pos = inst[0]; // center point of instances
@@ -602,8 +619,8 @@ int testTreeAt(
{
if (st == COND_FAILED)
break;
- int sta = testTreeAt(pos, env, pass, abort, path, b);
- if (*abort)
+ int sta = _testTreeAt(pos, env, path, b);
+ if (*env->stop)
return COND_FAILED;
if (sta < st)
st = sta;
@@ -616,7 +633,7 @@ int testTreeAt(
{ // 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[0], &icnt, &c);
+ st = testCondAt(at, env, &inst[0], &icnt, &c);
if (st == COND_FAILED || st == COND_MAYBE_POS_INVAL)
return st;
int sta = COND_FAILED;
@@ -627,8 +644,8 @@ int testTreeAt(
pos = inst[i];
for (int b : branches) // AND dependent conditions
{
- int stc = testTreeAt(pos, env, pass, abort, path, b);
- if (*abort)
+ int stc = _testTreeAt(pos, env, path, b);
+ if (*env->stop)
return COND_FAILED;
// worst branch dictates status for instance
if (stc < stb)
@@ -659,17 +676,18 @@ int testTreeAt(
Pos at, // relative origin
SearchThreadEnv * env, // thread-local environment
int pass, // search pass
- std::atomic_bool * abort, // abort signal
Pos * path // ok trigger positions
)
{
if (pass != PASS_FAST_48)
{ // do a fast check before continuing with slower checks
- int st = testTreeAt(at, env, PASS_FAST_48, abort, NULL, 0);
+ env->searchpass = PASS_FAST_48;
+ int st = _testTreeAt(at, env, NULL, 0);
if (st == COND_FAILED)
return st;
}
- return testTreeAt(at, env, pass, abort, path, 0);
+ env->searchpass = pass;
+ return _testTreeAt(at, env, path, 0);
}
@@ -1007,6 +1025,48 @@ static int f_biome_sampler(Generator *g, int scale, int x, int y, int z, void *d
return 0;
}
+static int f_noise_sampler(Generator *g, int scale, int x, int y, int z, void *data)
+{
+ (void) y;
+ sample_boime_t *info = (sample_boime_t*) data;
+ if (info->stop && *info->stop)
+ return -2;
+ if (info->rmaxsq)
+ {
+ int dx = (x * scale) - info->at.x;
+ int dz = (z * scale) - info->at.z;
+ int64_t rsq = dx*(int64_t)dx + dz*(int64_t)dz;
+ if (rsq >= info->rmaxsq)
+ return -1;
+ }
+
+ const Condition *cond = info->cond;
+ double v = sampleDoublePerlin(&g->bn.climate[cond->para], x, 0, z);
+ double vmin = cond->minmax & Condition::E_TEST_LOWER ? cond->vmin : -INFINITY;
+ double vmax = cond->minmax & Condition::E_TEST_UPPER ? cond->vmax : +INFINITY;
+
+ v *= 10000;
+
+ bool ok = (v > vmin && v < vmax);
+ if (cond->flags & Condition::FLG_INVERT)
+ ok = !ok;
+
+ if (ok)
+ {
+ x *= scale;
+ z *= scale;
+ if (info->imax && info->n < MAX_INSTANCES)
+ info->cent[info->n] = Pos{x, z};
+ info->xsum += x;
+ info->zsum += z;
+ info->n++;
+ return 1;
+ }
+ 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]'
@@ -1018,8 +1078,6 @@ int
testCondAt(
Pos at, // relative origin
SearchThreadEnv * env, // thread-local environment
- int pass, // search pass
- std::atomic_bool * abort, // abort signal
Pos * cent, // output center position(s)
int * imax, // max instances (NULL for avg)
const Condition * cond // condition to check
@@ -1035,7 +1093,7 @@ testCondAt(
int i, n, icnt;
int64_t s, r, rmin, rmax;
const uint64_t *seeds;
- std::vector p(MAX_INSTANCES);
+ Pos *p = getPosBuf(0);
const FilterInfo& finfo = g_filterinfo.list[cond->type];
@@ -1251,7 +1309,7 @@ L_qm_any:
icnt = 0;
// Note "<="
- for (rz = rz1; rz <= rz2 && !*abort; rz++)
+ for (rz = rz1; rz <= rz2 && !*env->stop; rz++)
{
for (rx = rx1; rx <= rx2; rx++)
{
@@ -1271,9 +1329,10 @@ L_qm_any:
{
continue;
}
- if (pass == PASS_FULL_64 || (pass == PASS_FULL_48 && !finfo.dep64))
+ if ((env->searchpass == PASS_FULL_64) ||
+ (env->searchpass == PASS_FULL_48 && !finfo.dep64))
{
- if (*abort) return COND_FAILED;
+ if (*env->stop) return COND_FAILED;
if (st == Village && cond->varflags)
{ // we can test for abandoned villages before the
@@ -1344,9 +1403,9 @@ L_qm_any:
return COND_OK;
else
{
- if (pass == PASS_FULL_64)
+ if (env->searchpass == PASS_FULL_64)
return COND_FAILED;
- if (pass == PASS_FULL_48 && !finfo.dep64)
+ if (env->searchpass == PASS_FULL_48 && !finfo.dep64)
return COND_FAILED;
return COND_MAYBE_POS_VALID;
}
@@ -1363,9 +1422,9 @@ L_qm_any:
cent->z = zt / icnt;
}
- if (pass == PASS_FULL_64)
+ if (env->searchpass == PASS_FULL_64)
return COND_OK;
- if (pass == PASS_FULL_48 && !finfo.dep64)
+ if (env->searchpass == PASS_FULL_48 && !finfo.dep64)
return COND_OK;
// some non-exhaustive structure clusters do not
// have known center positions with 48-bit seeds
@@ -1460,10 +1519,10 @@ L_qm_any:
case F_SPAWN:
cent->x = cent->z = 0;
- if (pass != PASS_FULL_64)
+ if (env->searchpass != PASS_FULL_64)
return COND_MAYBE_POS_INVAL;
- if (*abort) return COND_FAILED;
+ if (*env->stop) return COND_FAILED;
env->init4Dim(DIM_OVERWORLD);
pc = getSpawn(&env->g);
if (rmax)
@@ -1584,7 +1643,7 @@ L_qm_any:
return cond->count == 0 ? COND_OK : COND_FAILED;
// pre-biome-checks complete, the area appears to line up with possible generation positions
- if (pass != PASS_FULL_64)
+ if (env->searchpass != PASS_FULL_64)
{
return COND_MAYBE_POS_INVAL;
}
@@ -1602,7 +1661,7 @@ L_qm_any:
env->init4Dim(DIM_OVERWORLD);
while (nextStronghold(&sh, &env->g) > 0)
{
- if (*abort)
+ if (*env->stop)
break;
bool inside;
if (rmax)
@@ -1722,13 +1781,16 @@ L_qm_any:
case F_BIOME_SAMPLE:
+ case F_NOISE_SAMPLE:
- if (pass != PASS_FULL_64)
+ if (env->searchpass != 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;
+ if (cond->type == F_NOISE_SAMPLE && env->mc <= MC_1_17)
+ return COND_FAILED;
s = 2;
rx1 = x1 >> s;
@@ -1739,7 +1801,6 @@ L_qm_any:
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;
@@ -1749,14 +1810,24 @@ L_qm_any:
sample.zsum = 0;
sample.imax = imax;
sample.cent = cent;
- sample.stop = abort;
+ sample.stop = env->stop;
uint64_t rng;
setSeed(&rng, env->seed);
- int ok = monteCarloBiomes(
- &env->g, r, &rng, cond->converage, cond->confidence,
- &f_biome_sampler, &sample);
+ int (*f)(Generator *, int, int, int, int, void *);
+
+ if (cond->type == F_NOISE_SAMPLE)
+ {
+ env->init4Noise(cond->para, cond->octave);
+ f = f_noise_sampler;
+ }
+ else
+ {
+ env->init4Dim(DIM_OVERWORLD);
+ f = f_biome_sampler;
+ }
+ int ok = monteCarloBiomes(&env->g, r, &rng, cond->converage, cond->confidence, f, &sample);
if (imax && cond->count == 1)
{
*imax = sample.n;
@@ -1780,7 +1851,7 @@ L_qm_any:
case F_BIOME_4_RIVER:
case F_BIOME_256_OTEMP:
- if (env->mc >= MC_1_18)
+ if (env->mc > MC_1_17)
return COND_FAILED;
s = cond->type == F_BIOME_4_RIVER ? 2 : 8;
@@ -1791,15 +1862,15 @@ L_qm_any:
cent->x = (x1 + x2) >> 1;
cent->z = (z1 + z2) >> 1;
if (imax) *imax = 1;
- if (pass == PASS_FAST_48)
+ if (env->searchpass == PASS_FAST_48)
return COND_MAYBE_POS_VALID;
- if (pass == PASS_FULL_48)
+ if (env->searchpass == PASS_FULL_48)
{
if (env->mc < MC_1_13 || cond->type != F_BIOME_256_OTEMP)
return COND_MAYBE_POS_VALID;
}
valid = COND_FAILED;
- if (rx2 >= rx1 || rz2 >= rz1 || !*abort)
+ if (rx2 >= rx1 || rz2 >= rz1 || !*env->stop)
{
int w = rx2-rx1+1;
int h = rz2-rz1+1;
@@ -1819,7 +1890,7 @@ L_qm_any:
case F_TEMPS:
- if (env->mc >= MC_1_18)
+ if (env->mc > MC_1_17)
return COND_FAILED;
rx1 = x1 >> 10;
rz1 = z1 >> 10;
@@ -1828,7 +1899,7 @@ L_qm_any:
cent->x = (x1 + x2) >> 1;
cent->z = (z1 + z2) >> 1;
if (imax) *imax = 1;
- if (pass != PASS_FULL_64)
+ if (env->searchpass != PASS_FULL_64)
return COND_MAYBE_POS_VALID;
env->init4Dim(DIM_OVERWORLD);
if (checkForTemps(&env->g.ls, env->seed, rx1, rz1, rx2-rx1+1, rz2-rz1+1, cond->temps))
@@ -1857,11 +1928,11 @@ L_qm_any:
cent->x = (x1 + x2) >> 1;
cent->z = (z1 + z2) >> 1;
if (imax) *imax = 1;
- if (pass == PASS_FAST_48)
+ if (env->searchpass == PASS_FAST_48)
return COND_MAYBE_POS_VALID;
// the Nether and End require only the 48-bit seed
// (except voronoi uses the full 64-bits)
- if (pass != PASS_FULL_64 && (finfo.dep64 || s == 0))
+ if (env->searchpass != PASS_FULL_64 && (finfo.dep64 || s == 0))
return COND_MAYBE_POS_VALID;
else
{
@@ -1870,14 +1941,14 @@ L_qm_any:
int y = (s == 0 ? cond->y : cond->y >> 2);
Range r = {1<g, NULL, r, finfo.dim, env->seed,
- &cond->bf, (volatile char*)abort) > 0;
+ &cond->bf, (volatile char*)env->stop) > 0;
}
return valid ? COND_OK : COND_FAILED;
case F_BIOME_CENTER:
case F_BIOME_CENTER_256:
- if (pass == PASS_FULL_64)
+ if (env->searchpass == PASS_FULL_64)
{
s = cond->type == F_BIOME_CENTER ? 2 : 8;
rx1 = x1 >> s;
@@ -1893,7 +1964,7 @@ L_qm_any:
{ // exclusion
icnt = getBiomeCenters(
cent, NULL, 1, &env->g, r, cond->biomeId, cond->biomeSize, cond->tol,
- (volatile char*)abort
+ (volatile char*)env->stop
);
if (icnt == 0)
{
@@ -1907,7 +1978,7 @@ L_qm_any:
{ // just check there are at least *inst (== cond->count) instances
*imax = icnt = getBiomeCenters(
cent, NULL, cond->count, &env->g, r, cond->biomeId, cond->biomeSize, cond->tol,
- (volatile char*)abort
+ (volatile char*)env->stop
);
if (cond->skipref && icnt > 0)
{ // remove origin instance
@@ -1928,7 +1999,7 @@ L_qm_any:
{ // we need the average position of all instances
icnt = getBiomeCenters(
&p[0], NULL, MAX_INSTANCES, &env->g, r, cond->biomeId, cond->biomeSize, cond->tol,
- (volatile char*)abort
+ (volatile char*)env->stop
);
xt = zt = 0;
int j = 0;
@@ -1952,13 +2023,13 @@ L_qm_any:
return COND_MAYBE_POS_INVAL;
case F_CLIMATE_MINMAX:
- if (env->mc < MC_1_18 || cond->para >= NP_MAX)
+ if (env->mc <= MC_1_17 || cond->para >= NP_MAX)
return COND_FAILED;
rx1 = x1 >> 2;
rz1 = z1 >> 2;
rx2 = x2 >> 2;
rz2 = z2 >> 2;
- if (pass != PASS_FULL_64)
+ if (env->searchpass != PASS_FULL_64)
return COND_MAYBE_POS_INVAL;
{
track_minmax_t info = {at, at, +INFINITY, -INFINITY};
@@ -2000,7 +2071,7 @@ L_qm_any:
}
case F_CLIMATE_NOISE:
- if (env->mc < MC_1_18)
+ if (env->mc <= MC_1_17)
return COND_FAILED;
rx1 = x1 >> 2;
rz1 = z1 >> 2;
@@ -2009,8 +2080,9 @@ L_qm_any:
cent->x = (x1 + x2) >> 1;
cent->z = (z1 + z2) >> 1;
if (imax) *imax = 1;
- if (pass != PASS_FULL_64)
+ if (env->searchpass != PASS_FULL_64)
return COND_MAYBE_POS_VALID;
+ else
{
int w = rx2 - rx1 + 1;
int h = rz2 - rz1 + 1;
@@ -2069,7 +2141,7 @@ L_qm_any:
cent->x = x1;
cent->z = z1;
if (imax) *imax = 1;
- if (pass != PASS_FULL_64)
+ if (env->searchpass != PASS_FULL_64)
return COND_MAYBE_POS_VALID;
env->init4Dim(DIM_OVERWORLD);
env->prepareSurfaceNoise(DIM_OVERWORLD);
diff --git a/src/search.h b/src/search.h
index d73a0de..d0c9f77 100644
--- a/src/search.h
+++ b/src/search.h
@@ -90,6 +90,7 @@ enum
F_WELL,
F_TRAILS,
F_BIOME_SAMPLE,
+ F_NOISE_SAMPLE,
// new filters should be added here at the end to keep some downwards compatibility
FILTER_MAX,
};
@@ -308,15 +309,23 @@ static const struct FilterList : private FilterInfo
};
list[F_CLIMATE_NOISE] = FilterInfo{
- CAT_BIOMES, 0, LOC_REC, 0, 4, BR_NONE, MC_1_18, MC_NEWEST, 0, 0, disp++,
+ CAT_BIOMES, 1, LOC_REC, 0, 4, BR_NONE, MC_1_18, MC_NEWEST, 0, 0, disp++,
"overworld",
QT_TRANSLATE_NOOP("Filter", "Climate parameters"),
QT_TRANSLATE_NOOP("Filter",
"Custom limits for the required and allowed climate noise parameters that "
"the specified area should cover.")
};
+ list[F_NOISE_SAMPLE] = FilterInfo{
+ CAT_BIOMES, 1, LOC_RAD, 0, 4, BR_SPLIT, MC_1_18, MC_NEWEST, 0, 0, disp++,
+ "overworld",
+ QT_TRANSLATE_NOOP("Filter", "Climate noise samples"),
+ QT_TRANSLATE_NOOP("Filter",
+ "Samples climate noise in a given area to find if a proportion of the "
+ "biomes match a set of allowed biomes.")
+ };
list[F_CLIMATE_MINMAX] = FilterInfo{
- CAT_BIOMES, 0, LOC_REC, 0, 4, BR_NONE, MC_1_18, MC_NEWEST, 0, 0, disp++,
+ CAT_BIOMES, 1, LOC_REC, 0, 4, BR_NONE, MC_1_18, MC_NEWEST, 0, 0, disp++,
"overworld",
QT_TRANSLATE_NOOP("Filter", "Locate climate extreme"),
QT_TRANSLATE_NOOP("Filter",
@@ -358,7 +367,7 @@ static const struct FilterList : private FilterInfo
""
};
list[F_HEIGHT] = FilterInfo{
- CAT_OTHER, 0, LOC_POS, 0, 4, BR_NONE, MC_1_1, MC_NEWEST, 0, 0, disp++,
+ CAT_OTHER, 1, LOC_POS, 0, 4, BR_NONE, MC_1_1, MC_NEWEST, 0, 0, disp++,
"height",
QT_TRANSLATE_NOOP("Filter", "Surface height"),
QT_TRANSLATE_NOOP("Filter",
@@ -446,7 +455,7 @@ static const struct FilterList : private FilterInfo
list[F_RUINS] = FilterInfo{
CAT_STRUCT, 1, LOC_RAD, Ocean_Ruin, 1, BR_CLUST, MC_1_13, MC_NEWEST, 0, 0, disp++,
"ruins",
- QT_TRANSLATE_NOOP("Filter", "Ocean ruins"),
+ QT_TRANSLATE_NOOP("Filter", "Ocean ruin"),
""
};
@@ -489,7 +498,7 @@ static const struct FilterList : private FilterInfo
};
list[F_TRAILS] = FilterInfo{
- CAT_STRUCT, 1, LOC_RAD, Trail_Ruin, 1, BR_CLUST, MC_1_20, MC_NEWEST, 0, 0, disp++,
+ CAT_STRUCT, 1, LOC_RAD, Trail_Ruins, 1, BR_CLUST, MC_1_20, MC_NEWEST, 0, 0, disp++,
"trails",
QT_TRANSLATE_NOOP("Filter", "Trail ruins"),
""
@@ -637,6 +646,24 @@ static_assert(
"Layout of Condition has changed!"
);
+
+#define MAX_INSTANCES 4096 // should be at least 128
+
+enum
+{
+ COND_FAILED = 0, // seed does not meet the condition
+ COND_MAYBE_POS_INVAL = 1, // search pass insufficient for result
+ COND_MAYBE_POS_VALID = 2, // search pass insufficient, but known center
+ COND_OK = 3, // seed satisfies the condition
+};
+
+enum
+{
+ PASS_FAST_48, // only do fast checks that do not require biome gen
+ PASS_FULL_48, // include possible biome checks for 48-bit seeds
+ PASS_FULL_64, // run full test on a 64-bit seed
+};
+
struct ConditionTree
{
std::vector condvec;
@@ -658,6 +685,9 @@ struct SearchThreadEnv
int surfdim;
int octaves;
+ int searchpass;
+ std::atomic_bool *stop;
+
std::map l_states;
SearchThreadEnv();
@@ -671,24 +701,6 @@ struct SearchThreadEnv
void prepareSurfaceNoise(int dim);
};
-
-#define MAX_INSTANCES 4096 // should be at least 128
-
-enum
-{
- COND_FAILED = 0, // seed does not meet the condition
- COND_MAYBE_POS_INVAL = 1, // search pass insufficient for result
- COND_MAYBE_POS_VALID = 2, // search pass insufficient, but known center
- COND_OK = 3, // seed satisfies the condition
-};
-
-enum
-{
- PASS_FAST_48, // only do fast checks that do not require biome gen
- PASS_FULL_48, // include possible biome checks for 48-bit seeds
- PASS_FULL_64, // run full test on a 64-bit seed
-};
-
/* Checks if a seed satisfies the conditions tree.
* Returns the lowest condition fulfillment status.
*/
@@ -696,15 +708,12 @@ int testTreeAt(
Pos at, // relative origin
SearchThreadEnv * env, // thread-local environment
int pass, // search pass
- std::atomic_bool * abort, // abort signal
- Pos * path = 0 // ok trigger positions
+ Pos * path // ok trigger positions
);
int testCondAt(
Pos at, // relative origin
SearchThreadEnv * env, // thread-local environment
- int pass, // search pass
- std::atomic_bool * abort, // abort signal
Pos * cent, // output center position(s)
int * imax, // max instances (NULL for avg)
const Condition * cond // condition to check
diff --git a/src/searchthread.cpp b/src/searchthread.cpp
index 2cf1379..3b5235f 100644
--- a/src/searchthread.cpp
+++ b/src/searchthread.cpp
@@ -129,7 +129,7 @@ bool Session::load(QWidget *widget, QTextStream& stream, bool quiet)
SearchMaster::SearchMaster(QWidget *parent)
: QObject(parent)
, mutex()
- , abort()
+ , stop()
, proghist()
, progtimer()
, itemtimer()
@@ -151,11 +151,12 @@ SearchMaster::SearchMaster(QWidget *parent)
, smax()
, isdone()
{
+ env.stop = &stop;
}
SearchMaster::~SearchMaster()
{
- stop();
+ stopSearch();
}
bool SearchMaster::set(QWidget *widget, const Session& s)
@@ -313,7 +314,7 @@ bool SearchMaster::set(QWidget *widget, const Session& s)
this->smin = s.sc.smin;
this->smax = s.sc.smax;
this->isdone = false;
- this->abort = false;
+ this->stop = false;
return true;
}
@@ -393,7 +394,7 @@ static bool applyTranspose(std::vector& slist,
return !slist.empty();
}
-void SearchMaster::presearch()
+void SearchMaster::preSearch()
{
uint64_t sstart = seed;
@@ -567,13 +568,13 @@ void SearchMaster::presearch()
}
}
-void SearchMaster::start()
+void SearchMaster::startSearch()
{
- stop();
- abort = false;
- presearch();
+ stopSearch();
+ stop = false;
+ preSearch();
- if (abort)
+ if (stop)
{
emit searchFinish(false);
return;
@@ -607,9 +608,9 @@ void SearchMaster::start()
}
}
-void SearchMaster::stop()
+void SearchMaster::stopSearch()
{
- abort = true;
+ stop = true;
if (workers.empty())
return;
@@ -871,10 +872,10 @@ bool SearchMaster::requestItem(SearchWorker *item)
high = 0;
low++;
- for (; low <= MASK48 && !abort; low++)
+ for (; low <= MASK48 && !stop; low++)
{
env.setSeed(low);
- if (testTreeAt(origin, &env, PASS_FAST_48, &abort)
+ if (testTreeAt(origin, &env, PASS_FAST_48, nullptr)
!= COND_FAILED)
{
break;
@@ -909,7 +910,7 @@ void SearchMaster::onWorkerFinished()
for (SearchWorker *worker: workers)
delete worker;
workers.clear();
- emit searchFinish(isdone && !abort);
+ emit searchFinish(isdone && !stop);
}
@@ -919,7 +920,6 @@ SearchWorker::SearchWorker(SearchMaster *master)
{
this->slist = master->slist.empty() ? NULL : master->slist.data();
this->len = master->slist.size();
- this->abort = &master->abort;
this->prog = master->prog;
this->idx = master->idx;
@@ -927,12 +927,11 @@ SearchWorker::SearchWorker(SearchMaster *master)
this->scnt = 0;
this->seed = master->seed;
- this->env = new SearchThreadEnv();
+ this->env.stop = &master->stop;
}
SearchWorker::~SearchWorker()
{
- delete env;
}
bool SearchWorker::getNextItem()
@@ -944,25 +943,21 @@ bool SearchWorker::getNextItem()
void SearchWorker::run()
{
Pos origin = {0,0};
- env->init(master->mc, master->large, master->condtree);
-
- volatile char b[1024*1024];
- for (size_t i = 0; i < sizeof(b); i++)
- b[i] = (char)i + b[i % 256];
+ env.init(master->mc, master->large, master->condtree);
switch (master->searchtype)
{
case SEARCH_LIST:
- while (!*abort && getNextItem())
+ while (!*env.stop && getNextItem())
{ // seed = slist[..]
uint64_t ie = idx+scnt < len ? idx+scnt : len;
for (uint64_t i = idx; i < ie; i++)
{
seed = slist[i];
- env->setSeed(seed);
- if (testTreeAt(origin, env, PASS_FULL_64, abort) == COND_OK)
+ env.setSeed(seed);
+ if (testTreeAt(origin, &env, PASS_FULL_64, nullptr) == COND_OK)
{
- if (!*abort)
+ if (!*env.stop)
emit result(seed);
}
}
@@ -972,7 +967,7 @@ void SearchWorker::run()
break;
case SEARCH_48ONLY:
- while (!*abort && getNextItem())
+ while (!*env.stop && getNextItem())
{
if (slist)
{
@@ -980,10 +975,10 @@ void SearchWorker::run()
for (uint64_t i = idx; i < ie; i++)
{
seed = slist[i];
- env->setSeed(seed);
- if (testTreeAt(origin, env, PASS_FULL_48, abort) != COND_FAILED)
+ env.setSeed(seed);
+ if (testTreeAt(origin, &env, PASS_FULL_48, nullptr) != COND_FAILED)
{
- if (!*abort)
+ if (!*env.stop)
emit result(seed);
}
}
@@ -993,10 +988,10 @@ void SearchWorker::run()
seed = sstart;
for (int i = 0; i < scnt; i++)
{
- env->setSeed(seed);
- if (testTreeAt(origin, env, PASS_FULL_48, abort) != COND_FAILED)
+ env.setSeed(seed);
+ if (testTreeAt(origin, &env, PASS_FULL_48, nullptr) != COND_FAILED)
{
- if (!*abort)
+ if (!*env.stop)
emit result(seed);
}
@@ -1011,7 +1006,7 @@ void SearchWorker::run()
break;
case SEARCH_INC:
- while (!*abort && getNextItem())
+ while (!*env.stop && getNextItem())
{
if (slist)
{ // seed = (high << 48) | slist[..]
@@ -1022,10 +1017,10 @@ void SearchWorker::run()
{
seed = (high << 48) | slist[lowidx];
- env->setSeed(seed);
- if (testTreeAt(origin, env, PASS_FULL_64, abort) == COND_OK)
+ env.setSeed(seed);
+ if (testTreeAt(origin, &env, PASS_FULL_64, nullptr) == COND_OK)
{
- if (!*abort)
+ if (!*env.stop)
emit result(seed);
}
@@ -1044,10 +1039,10 @@ void SearchWorker::run()
seed = sstart;
for (int i = 0; i < scnt; i++)
{
- env->setSeed(seed);
- if (testTreeAt(origin, env, PASS_FULL_64, abort) == COND_OK)
+ env.setSeed(seed);
+ if (testTreeAt(origin, &env, PASS_FULL_64, nullptr) == COND_OK)
{
- if (!*abort)
+ if (!*env.stop)
emit result(seed);
}
@@ -1062,7 +1057,7 @@ void SearchWorker::run()
break;
case SEARCH_BLOCKS:
- while (!*abort && getNextItem())
+ while (!*env.stop && getNextItem())
{ // seed = ([..] << 48) | low
if (slist && idx >= len)
{
@@ -1076,8 +1071,8 @@ void SearchWorker::run()
else
low = sstart & MASK48;
- env->setSeed(low);
- if (testTreeAt(origin, env, PASS_FULL_48, abort) == COND_FAILED)
+ env.setSeed(low);
+ if (testTreeAt(origin, &env, PASS_FULL_48, nullptr) == COND_FAILED)
{
continue;
}
@@ -1086,10 +1081,10 @@ void SearchWorker::run()
{
seed = (high << 48) | low;
- env->setSeed(seed);
- if (testTreeAt(origin, env, PASS_FULL_64, abort) == COND_OK)
+ env.setSeed(seed);
+ if (testTreeAt(origin, &env, PASS_FULL_64, nullptr) == COND_OK)
{
- if (!*abort)
+ if (!*env.stop)
emit result(seed);
}
diff --git a/src/searchthread.h b/src/searchthread.h
index 436ca77..6293585 100644
--- a/src/searchthread.h
+++ b/src/searchthread.h
@@ -37,10 +37,10 @@ public:
bool set(QWidget *widget, const Session& s);
- void presearch();
+ void preSearch();
- void start();
- void stop();
+ void startSearch();
+ void stopSearch();
// Get search progress:
// status : progress status summary
@@ -69,7 +69,7 @@ public:
std::vector workers;
QMutex mutex;
- std::atomic_bool abort;
+ std::atomic_bool stop;
std::deque proghist;
QElapsedTimer progtimer;
@@ -114,7 +114,6 @@ public:
const uint64_t * slist; // candidate list
uint64_t len; // number of candidates
- std::atomic_bool * abort;
/// current work item
uint64_t prog; // search space progress
@@ -126,7 +125,7 @@ public:
// (or the last entry in the seed list)
private:
- SearchThreadEnv * env;
+ SearchThreadEnv env;
};
diff --git a/src/tablocations.cpp b/src/tablocations.cpp
index 9e3fc74..8fcf704 100644
--- a/src/tablocations.cpp
+++ b/src/tablocations.cpp
@@ -61,6 +61,12 @@ QTreeWidgetItem *setConditionTreeItems(ConditionTree& ctree, int node, int64_t s
return item;
}
+AnalysisLocations::AnalysisLocations(QObject *parent)
+: QThread(parent), wi(),stop(),sidx(),pidx()
+{
+ env.stop = &stop;
+}
+
QString AnalysisLocations::set(WorldInfo wi, const std::vector& conds)
{
this->wi = wi;
@@ -85,7 +91,7 @@ void AnalysisLocations::run()
Pos at = pos[pidx.load()];
Pos cpos[MAX_INSTANCES] = {};
- if (testTreeAt(at, &env, PASS_FULL_64, &stop, cpos)
+ if (testTreeAt(at, &env, PASS_FULL_64, cpos)
!= COND_OK)
{
continue;
diff --git a/src/tablocations.h b/src/tablocations.h
index 02a54e8..cbc4de7 100644
--- a/src/tablocations.h
+++ b/src/tablocations.h
@@ -16,8 +16,7 @@ class AnalysisLocations : public QThread
{
Q_OBJECT
public:
- explicit AnalysisLocations(QObject *parent = nullptr)
- : QThread(parent), wi(),stop(),sidx(),pidx() {}
+ explicit AnalysisLocations(QObject *parent = nullptr);
QString set(WorldInfo wi, const std::vector& conds);
diff --git a/src/util.h b/src/util.h
index f776f34..7fa1359 100644
--- a/src/util.h
+++ b/src/util.h
@@ -80,7 +80,6 @@ struct RandGen
// get a random 64-bit integer
uint64_t getRnd64();
-
enum { S_TEXT, S_NUMERIC, S_RANDOM };
int str2seed(const QString &str, uint64_t *out);
diff --git a/src/world.cpp b/src/world.cpp
index 7311b96..418cb47 100644
--- a/src/world.cpp
+++ b/src/world.cpp
@@ -201,11 +201,15 @@ void getStructs(std::vector *out, const StructureConfig sconf,
{
SurfaceNoise sn;
initSurfaceNoise(&sn, DIM_END, wi.seed);
- id = isViableEndCityTerrain(&g, &sn, p.x, p.z);
- if (!id)
+ int y = isViableEndCityTerrain(&g, &sn, p.x, p.z);
+ if (!y)
continue;
int n = getEndCityPieces(pieces, wi.seed, p.x >> 4, p.z >> 4);
- vp.pieces.assign(pieces, pieces+n);
+ if (n)
+ {
+ vp.pieces.assign(pieces, pieces+n);
+ vp.pieces[0].bb0.y = y; // height of end city pieces are relative to surface
+ }
}
else if (sconf.structType == Ruined_Portal || sconf.structType == Ruined_Portal_N)
{
@@ -235,12 +239,12 @@ void getStructs(std::vector *out, const StructureConfig sconf,
static QMutex g_mutex;
-static qreal cubic_hermite(qreal p[4], qreal u)
+static float cubic_hermite(float p[4], float u)
{
- qreal a = p[1];
- qreal b = 0.5 * (-p[0] + p[2]);
- qreal c = p[0] - 2.5*p[1] + 2*p[2] - 0.5*p[3];
- qreal d = 0.5 * (-p[0] + 3*p[1] - 3*p[2] + p[3]);
+ float a = p[1];
+ float b = 0.5 * (-p[0] + p[2]);
+ float c = p[0] - 2.5*p[1] + 2*p[2] - 0.5*p[3];
+ float d = 0.5 * (-p[0] + 3*p[1] - 3*p[2] + p[3]);
return a + b*u + c*u*u + d*u*u*u;
}
@@ -262,21 +266,28 @@ void applyHeightShading(unsigned char *rgb, Range r,
px -= bd; pz -= bd;
pw += 2*bd; ph += 2*bd;
}
- std::vector buf(pw * ph);
- for (int j = 0; j < ph; j++)
+ std::vector buf(pw * ph);
+ if (ps == 0)
{
- for (int i = 0; i < pw; i++)
+ mapApproxHeight(&buf[0], 0, g, sn, px, pz, pw, ph);
+ }
+ else
+ {
+ for (int j = 0; j < ph; j++)
{
- if (abort && *abort) return;
- int samplex = (((px + i) << ps)) * r.scale / 4;
- int samplez = (((pz + j) << ps)) * r.scale / 4;
- float y = 0;
- mapApproxHeight(&y, 0, g, sn, samplex, samplez, 1, 1);
- buf[j*pw+i] = y;
+ for (int i = 0; i < pw; i++)
+ {
+ if (abort && *abort) return;
+ int samplex = (((px + i) << ps)) * r.scale / 4;
+ int samplez = (((pz + j) << ps)) * r.scale / 4;
+ float y = 0;
+ mapApproxHeight(&y, 0, g, sn, samplex, samplez, 1, 1);
+ buf[j*pw+i] = y;
+ }
}
}
// interpolate height
- std::vector height((w+2) * (h+2));
+ std::vector height((w+2) * (h+2));
for (int j = 0; j < h+2; j++)
{
for (int i = 0; i < w+2; i++)
@@ -284,12 +295,12 @@ void applyHeightShading(unsigned char *rgb, Range r,
if (abort && *abort) return;
int pi = ((x + i - 1) >> ps) - px - bd;
int pj = ((z + j - 1) >> ps) - pz - bd;
- qreal di = ((x + i - 1) & st) / (qreal)(st + 1);
- qreal dj = ((z + j - 1) & st) / (qreal)(st + 1);
- qreal v = 0;
+ float di = ((x + i - 1) & st) / (float)(st + 1);
+ float dj = ((z + j - 1) & st) / (float)(st + 1);
+ float v = 0;
if (bicubic)
{
- qreal p[] = {
+ float p[] = {
cubic_hermite(&buf.at((pj+0)*pw + pi), di),
cubic_hermite(&buf.at((pj+1)*pw + pi), di),
cubic_hermite(&buf.at((pj+2)*pw + pi), di),
@@ -299,10 +310,10 @@ void applyHeightShading(unsigned char *rgb, Range r,
}
else // bilinear
{
- qreal v00 = buf.at((pj+0)*pw + pi+0);
- qreal v01 = buf.at((pj+0)*pw + pi+1);
- qreal v10 = buf.at((pj+1)*pw + pi+0);
- qreal v11 = buf.at((pj+1)*pw + pi+1);
+ float v00 = buf.at((pj+0)*pw + pi+0);
+ float v01 = buf.at((pj+0)*pw + pi+1);
+ float v10 = buf.at((pj+1)*pw + pi+0);
+ float v11 = buf.at((pj+1)*pw + pi+1);
v = lerp2(di, dj, v00, v01, v10, v11);
}
height[j*(w+2)+i] = v;
@@ -310,24 +321,24 @@ void applyHeightShading(unsigned char *rgb, Range r,
}
if (abort && *abort) return;
// apply shading based on height changes
- qreal mul = 0.25 / r.scale;
- qreal lout = 0.65;
- qreal lmin = 0.5;
- qreal lmax = 1.5;
- qreal ymax = r.scale == 1 ? r.y : r.y << 2;
+ float mul = 0.25 / r.scale;
+ float lout = 0.65;
+ float lmin = 0.5;
+ float lmax = 1.5;
+ float ymax = r.scale == 1 ? r.y : r.y << 2;
for (int j = 0; j < h; j++)
{
for (int i = 0; i < w; i++)
{
int tw = w+2;
- qreal t01 = height[(j+0)*tw + i+1];
- qreal t10 = height[(j+1)*tw + i+0];
- qreal t11 = height[(j+1)*tw + i+1];
- qreal t12 = height[(j+1)*tw + i+2];
- qreal t21 = height[(j+2)*tw + i+1];
- qreal d0 = t01 + t10;
- qreal d1 = t12 + t21;
- qreal light = 1.0;
+ float t01 = height[(j+0)*tw + i+1];
+ float t10 = height[(j+1)*tw + i+0];
+ float t11 = height[(j+1)*tw + i+1];
+ float t12 = height[(j+1)*tw + i+2];
+ float t21 = height[(j+2)*tw + i+1];
+ float d0 = t01 + t10;
+ float d1 = t12 + t21;
+ float light = 1.0;
uchar *col = rgb + 3*(j*w + i);
if (mode == HV_GRAYSCALE)
{
@@ -336,7 +347,7 @@ void applyHeightShading(unsigned char *rgb, Range r,
col[0] = 0xff; col[1] = col[2] = 0;
continue;
}
- qreal v = t11;
+ float v = t11;
uchar c = (v <= 0) ? 0 : (v > 0xff) ? 0xff : (uchar)(v);
col[0] = col[1] = col[2] = c;
continue;
@@ -348,14 +359,14 @@ void applyHeightShading(unsigned char *rgb, Range r,
if (light > lmax) light = lmax;
if (mode == HV_CONTOURS || mode == HV_CONTOURS_SHADING)
{
- qreal spacing = 16.0;
- qreal tmin = std::min({t01, t10, t12, t21});
+ float spacing = 16.0;
+ float tmin = std::min({t01, t10, t12, t21});
if (floor(tmin / spacing) != floor(t11 / spacing))
light *= 0.5;
}
for (int k = 0; k < 3; k++)
{
- qreal c = col[k] * light;
+ float c = col[k] * light;
col[k] = (c <= 0) ? 0 : (c > 0xff) ? 0xff : (uchar)(c);
}
}
@@ -401,7 +412,7 @@ void Quad::run()
int nptype = -1;
if (g->mc >= MC_1_18) nptype = g->bn.nptype;
if (g->mc <= MC_B1_7) nptype = g->bnb.nptype;
- if (dim == 0 && nptype >= 0)
+ if (dim == DIM_OVERWORLD && nptype >= 0)
{ // climate parameter
const int *extremes = getBiomeParaExtremes(g->mc);
int cmin = extremes[nptype*2 + 0];
@@ -420,7 +431,7 @@ void Quad::run()
g_mutex.unlock();
biomesToImage(rgb, g_biomeColors, biomes, w, h, 1, 1);
- if (lopt.mode == LOPT_HEIGHT_4 && dim == 0)
+ if (lopt.mode == LOPT_HEIGHT_4)
{
int stepbits = (hd ? 0 : 2);
applyHeightShading(rgb, r, g, sn, stepbits, lopt.disp[lopt.mode], false, isdel);
@@ -782,7 +793,7 @@ void QWorld::setDim(int dim, LayerOpt lopt)
this->dim = dim;
this->lopt = lopt;
applySeed(&g, dim, wi.seed);
- initSurfaceNoise(&sn, DIM_OVERWORLD, g.seed);
+ initSurfaceNoise(&sn, dim, g.seed);
int pixs, lcnt;
if (g.mc >= MC_1_18 || dim != DIM_OVERWORLD)
@@ -862,15 +873,17 @@ QString QWorld::getBiomeName(Pos p)
return c + "=" + QString::number(id);
}
QString ret = getBiomeDisplay(wi.mc, id);
- if (lopt.mode == LOPT_HEIGHT_4 && dim == DIM_OVERWORLD)
- ret = QString::asprintf("Y~%d ", estimateSurface(p)) + ret;
+ if (lopt.mode == LOPT_HEIGHT_4)
+ {
+ int y = estimateSurface(p);
+ if (y > 0)
+ ret = QString::asprintf("Y~%d ", y) + ret;
+ }
return ret;
}
int QWorld::estimateSurface(Pos p)
{
- if (dim != DIM_OVERWORLD)
- return 64;
float y = 0;
mapApproxHeight(&y, 0, &g, &sn, p.x>>2, p.z>>2, 1, 1);
return (int) floor(y);