More 2.4 changes

added nether fortress structure piece generation
added advanced option to skip terrain checks
fixed regression of completely wrong Mansion positions in v2.3.2 (#155, #161)
fixed inaccuracy of end city pieces generation
fixed invalid biome color refresh with climate parameter view
fixed repeated opening of structure scale limits when starting from preferences
This commit is contained in:
Cubitect 2022-09-11 13:10:08 +02:00
parent 15b90aaa61
commit e405a62628
20 changed files with 521 additions and 68 deletions

View File

@ -16,8 +16,8 @@ The builds are statically linked against [Qt](https://www.qt.io) and should run
as-is on most newer distributions. For the Linux build you will probably have to
add the executable flags to the binary.
A Flatpak for the tool is available on the
[flathub](https://flathub.org/apps/details/com.github.cubitect.cubiomes-viewer).
A Flatpak for the tool is available on
[Flathub](https://flathub.org/apps/details/com.github.cubitect.cubiomes-viewer).
For Arch Linux users, the tool may be found in the
[AUR](https://aur.archlinux.org/packages/cubiomes-viewer) thanks to
@ -33,7 +33,7 @@ information on this issue.
Build instructions can be found in the [buildguide](buildguide.md).
## Basic Feature Overview
## Basic feature 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
@ -50,3 +50,15 @@ look for seeds that include extremely rare structure constellations.
![maingui](etc/screenshot_maingui.png)
### Known issues
Desert Pyramids, Jungle Temples and, to a lesser extent, Woodland Mansions can
fail to generate in 1.18+ due to unsuitable terrain. Cubiomes will make an
attempt to estimate the terrain based on the biomes and cimate noise. However,
expect some inaccurate results.
The World Spawn point for pre-1.18 versions can sometimes be off, since it
depends on the presence of a grass block that cubiomes cannot test for.

@ -1 +1 @@
Subproject commit b9d374445b7497b0a90963c6370cc9691741a9c6
Subproject commit 8b87d9379a3999ded2d575b0627c8771c15e93b2

View File

@ -69,6 +69,7 @@ SOURCES += \
src/searchthread.cpp \
src/mainwindow.cpp \
src/main.cpp \
src/statsdialog.cpp \
src/structuredialog.cpp \
src/world.cpp
@ -102,6 +103,7 @@ HEADERS += \
src/seedtables.h \
src/mainwindow.h \
src/settings.h \
src/statsdialog.h \
src/structuredialog.h \
src/world.h
@ -121,6 +123,7 @@ FORMS += \
src/quadlistdialog.ui\
src/mainwindow.ui \
src/rangedialog.ui \
src/statsdialog.ui \
src/structuredialog.ui

View File

@ -6,7 +6,7 @@
#define VERS_MAJOR 2
#define VERS_MINOR 4
#define VERS_PATCH -1 // negative patch number designates a development version
#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)

View File

@ -2,6 +2,7 @@
#include "ui_biomecolordialog.h"
#include "cutil.h"
#include "settings.h"
#include <QPixmap>
#include <QPainter>
@ -44,7 +45,7 @@ BiomeColorDialog::BiomeColorDialog(QWidget *parent, QString initrc, int mc, int
ui->setupUi(this);
ui->buttonOk->setIcon(style()->standardIcon(QStyle::SP_DialogOkButton));
memcpy(colors, biomeColors, sizeof(colors));
memcpy(colors, g_biomeColors, sizeof(colors));
memset(buttons, 0, sizeof(buttons));
unsigned char coldefault[256][3];

View File

@ -241,7 +241,7 @@ ConditionDialog::ConditionDialog(FormConditions *parent, Config *config, int mcv
{
int id = it.first;
QCheckBox *cb = it.second;
QColor col(biomeColors[id][0], biomeColors[id][1], biomeColors[id][2]);
QColor col(g_biomeColors[id][0], g_biomeColors[id][1], g_biomeColors[id][2]);
p.fillPath(path, col);
p.drawPath(path);
cb->setIcon(QIcon(pixmap));

View File

@ -3,7 +3,7 @@
#include "biomecolordialog.h"
#include "structuredialog.h"
#include "world.h"
#include "cutil.h"
#include <QThread>
@ -155,7 +155,10 @@ void ConfigDialog::on_buttonStructVisEdit_clicked()
{
StructureDialog *dialog = new StructureDialog(this);
if (dialog->exec() == QDialog::Accepted)
structVisModified |= dialog->modified;
{
if ((structVisModified |= dialog->modified))
saveStructVis(dialog->structvis);
}
}
void ConfigDialog::on_buttonClear_clicked()

View File

@ -9,9 +9,6 @@
#include "cubiomes/quadbase.h"
#include "cubiomes/util.h"
extern unsigned char biomeColors[256][3];
extern unsigned char tempsColors[256][3];
inline const char* struct2str(int stype)
{

View File

@ -52,7 +52,7 @@ struct ExportWorker : QRunnable
uchar *rgb = new uchar[r.sx * r.sz * 3];
enum { BG_NONE, BG_TRANSP, BG_BLACK };
biomesToImage(rgb, biomeColors, ids, r.sx, r.sz, 1, 1);
biomesToImage(rgb, g_biomeColors, ids, r.sx, r.sz, 1, 1);
QImage img(rgb, r.sx, r.sz, 3*r.sx, QImage::Format_RGB888);

View File

@ -61,6 +61,8 @@ ExtGenDialog::~ExtGenDialog()
void ExtGenDialog::initSettings(ExtGenSettings *extgen)
{
ui->checkEstimate->setChecked(extgen->estimateTerrain);
// start checked, otherwise Qt doesn't respond to initial uncheck
ui->groupSalts->setChecked(true);
@ -87,6 +89,7 @@ void ExtGenDialog::initSettings(ExtGenSettings *extgen)
ExtGenSettings ExtGenDialog::getSettings()
{
extgen.estimateTerrain = ui->checkEstimate->isChecked();
extgen.saltOverride = ui->groupSalts->isChecked();
for (int i = 0; i < FEATURE_NUM; i++)

View File

@ -10,7 +10,7 @@
<normaloff>:/icons/logo.png</normaloff>:/icons/logo.png</iconset>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0" colspan="2">
<item row="2" column="0" colspan="2">
<widget class="QGroupBox" name="groupSalts">
<property name="title">
<string>Override structure salt</string>
@ -23,7 +23,7 @@
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<item row="3" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -33,6 +33,13 @@
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="checkEstimate">
<property name="text">
<string>Estimate terrain (1.18+)</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources>

View File

@ -8,14 +8,33 @@
#include "cubiomes/generator.h"
#include "cubiomes/util.h"
unsigned char biomeColors[256][3];
unsigned char tempsColors[256][3];
/// globals
unsigned char g_biomeColors[256][3];
unsigned char g_tempsColors[256][3];
ExtGenSettings g_extgen;
extern "C"
int getStructureConfig_override(int stype, int mc, StructureConfig *sconf)
{
if unlikely(mc == INT_MAX) // to check if override is enabled in cubiomes
mc = 0;
int ok = getStructureConfig(stype, mc, sconf);
if (ok && g_extgen.saltOverride)
{
uint64_t salt = g_extgen.salts[stype];
if (salt <= MASK48)
sconf->salt = salt;
}
return ok;
}
int main(int argc, char *argv[])
{
initBiomes();
initBiomeColors(biomeColors);
initBiomeTypeColors(tempsColors);
initBiomeColors(g_biomeColors);
initBiomeTypeColors(g_tempsColors);
QApplication a(argc, argv);
QCoreApplication::setApplicationName("cubiomes-viewer");

View File

@ -34,26 +34,6 @@
#include <QFile>
// Keep the extended generator settings in global scope, but we mainly need
// them in this file. (Pass through via pointer elsewhere.)
static ExtGenSettings g_extgen;
extern "C"
int getStructureConfig_override(int stype, int mc, StructureConfig *sconf)
{
if unlikely(mc == INT_MAX) // to check if override is enabled in cubiomes
mc = 0;
int ok = getStructureConfig(stype, mc, sconf);
if (ok && g_extgen.saltOverride)
{
uint64_t salt = g_extgen.salts[stype];
if (salt <= MASK48)
sconf->salt = salt;
}
return ok;
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
@ -383,6 +363,7 @@ void MainWindow::saveSettings()
settings.setValue("config/mapCacheSize", config.mapCacheSize);
settings.setValue("config/biomeColorPath", config.biomeColorPath);
settings.setValue("world/estimateTerrain", g_extgen.estimateTerrain);
settings.setValue("world/saltOverride", g_extgen.saltOverride);
for (int st = 0; st < FEATURE_NUM; st++)
{
@ -468,6 +449,7 @@ void MainWindow::loadSettings()
onStyleChanged(config.uistyle);
setDockable(config.dockable);
g_extgen.estimateTerrain = settings.value("world/estimateTerrain", g_extgen.estimateTerrain).toBool();
g_extgen.saltOverride = settings.value("world/saltOverride", g_extgen.saltOverride).toBool();
for (int st = 0; st < FEATURE_NUM; st++)
{
@ -894,9 +876,10 @@ void MainWindow::on_actionPreferences_triggered()
applyConfigChanges(config, dialog->getSettings());
}
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();
{
getMapView()->deleteWorld();
updateMapSeed();
update();
}
}
@ -911,13 +894,13 @@ void MainWindow::onBiomeColorChange()
if (siz >= 0)
{
buf[siz] = 0;
initBiomeColors(biomeColors);
parseBiomeColors(biomeColors, buf);
initBiomeColors(g_biomeColors);
parseBiomeColors(g_biomeColors, buf);
}
}
else
{
initBiomeColors(biomeColors);
initBiomeColors(g_biomeColors);
}
getMapView()->refreshBiomeColors();
}

View File

@ -907,8 +907,11 @@ L_qm_any:
}
if (gen->mc >= MC_1_18)
{
if (!isViableStructureTerrain(st, &gen->g, pc.x, pc.z))
if (g_extgen.estimateTerrain &&
!isViableStructureTerrain(st, &gen->g, pc.x, pc.z))
{
continue;
}
}
}

View File

@ -8,9 +8,12 @@
#include <vector>
extern unsigned char g_biomeColors[256][3];
extern unsigned char g_tempsColors[256][3];
struct ExtGenSettings
{
bool estimateTerrain;
bool saltOverride;
uint64_t salts[FEATURE_NUM];
@ -18,12 +21,16 @@ struct ExtGenSettings
void reset()
{
estimateTerrain = true;
saltOverride = false;
for (int i = 0; i < FEATURE_NUM; i++)
salts[i] = ~(uint64_t)0;
}
};
// Keep the extended generator settings in global scope.
extern ExtGenSettings g_extgen;
struct WorldInfo
{
int mc;

14
src/statsdialog.cpp Normal file
View File

@ -0,0 +1,14 @@
#include "statsdialog.h"
#include "ui_statsdialog.h"
StatsDialog::StatsDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::StatsDialog)
{
ui->setupUi(this);
}
StatsDialog::~StatsDialog()
{
delete ui;
}

22
src/statsdialog.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef STATSDIALOG_H
#define STATSDIALOG_H
#include <QDialog>
namespace Ui {
class StatsDialog;
}
class StatsDialog : public QDialog
{
Q_OBJECT
public:
explicit StatsDialog(QWidget *parent = nullptr);
~StatsDialog();
private:
Ui::StatsDialog *ui;
};
#endif // STATSDIALOG_H

357
src/statsdialog.ui Normal file
View File

@ -0,0 +1,357 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>StatsDialog</class>
<widget class="QDialog" name="StatsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>868</width>
<height>636</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0" colspan="3">
<layout class="QHBoxLayout" name="layoutArea_2">
<item>
<widget class="QLabel" name="labelX1">
<property name="toolTip">
<string>Lower bound (inclusive)</string>
</property>
<property name="text">
<string>X&lt;sub&gt;1&lt;/sub&gt;=</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEditX1">
<property name="toolTip">
<string>Lower bound (inclusive)</string>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelZ1">
<property name="toolTip">
<string>Lower bound (inclusive)</string>
</property>
<property name="text">
<string>Z&lt;sub&gt;1&lt;/sub&gt;=</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEditZ1">
<property name="toolTip">
<string>Lower bound (inclusive)</string>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="labelX2">
<property name="toolTip">
<string>Upper bound (inclusive)</string>
</property>
<property name="text">
<string>X&lt;sub&gt;2&lt;/sub&gt;=</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEditX2">
<property name="toolTip">
<string>Upper bound (inclusive)</string>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelZ2">
<property name="toolTip">
<string>Upper bound (inclusive)</string>
</property>
<property name="text">
<string>Z&lt;sub&gt;2&lt;/sub&gt;=</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEditZ2">
<property name="toolTip">
<string>Upper bound (inclusive)</string>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0" colspan="4" alignment="Qt::AlignBottom">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Biomes</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="1">
<widget class="QCheckBox" name="checkBox">
<property name="text">
<string>Overworld</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QRadioButton" name="radioButton_4">
<property name="text">
<string>Full area sampling with scale:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QRadioButton" name="radioButton_3">
<property name="text">
<string>Stochastic with maximum samples:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Dimensions:</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QCheckBox" name="checkBox_2">
<property name="text">
<string>Nether</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QCheckBox" name="checkBox_3">
<property name="text">
<string>End</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="3">
<widget class="QLineEdit" name="lineEdit"/>
</item>
<item row="0" column="1" colspan="3">
<widget class="QComboBox" name="comboScale">
<item>
<property name="text">
<string>1:1</string>
</property>
</item>
<item>
<property name="text">
<string>1:4</string>
</property>
</item>
<item>
<property name="text">
<string>1:16</string>
</property>
</item>
<item>
<property name="text">
<string>1:64</string>
</property>
</item>
<item>
<property name="text">
<string>1:256</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0" colspan="4">
<widget class="QLabel" name="label">
<property name="text">
<string>Area in blocks (bounds inclusive):</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QComboBox" name="comboSeed">
<item>
<property name="text">
<string>Current seed</string>
</property>
</item>
<item>
<property name="text">
<string>From matching seeds list</string>
</property>
</item>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Seed(s):</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QPushButton" name="buttonFromVisible">
<property name="text">
<string>From visible</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="4">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Structures</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="1">
<widget class="QRadioButton" name="radioButton_2">
<property name="text">
<string>All structures</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QRadioButton" name="radioButton">
<property name="text">
<string>Structures from map selection</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="checkBox_4">
<property name="text">
<string>Collect individual structure instances</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="5" column="0" colspan="4">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Statistics</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>Run analysis</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="pushButton_2">
<property name="text">
<string>Export...</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QTreeWidget" name="treeWidget">
<column>
<property name="text">
<string notr="true">item</string>
</property>
</column>
<column>
<property name="text">
<string>count</string>
</property>
</column>
<column>
<property name="text">
<string>coordinates</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</item>
<item row="7" column="0" colspan="4">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>StatsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>StatsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -69,8 +69,8 @@ void StructureDialog::onAccept()
void StructureDialog::onReset()
{
double scale = 32;
QString scalestr = "32";
for (auto it : entries)
it.second->setText(QString::number(scale));
it.second->setText(scalestr);
modified = true;
}

View File

@ -16,8 +16,9 @@ void loadStructVis(std::map<int, double>& structvis)
for (int opt = D_DESERT; opt < D_SPAWN; opt++)
{
double defval = 32;
const char *name = mapopt2str(opt);
double scale = settings.value(QString("structscale/") + name, 32.0).toDouble();
double scale = settings.value(QString("structscale/") + name, defval).toDouble();
structvis[opt] = scale;
}
}
@ -128,6 +129,7 @@ void getStructs(std::vector<VarPos> *out, const StructureConfig sconf,
if (!id)
continue;
VarPos vp = VarPos(p);
Piece pieces[1024];
if (sconf.structType == End_City)
{
@ -136,7 +138,6 @@ void getStructs(std::vector<VarPos> *out, const StructureConfig sconf,
id = isViableEndCityTerrain(&g.en, &sn, p.x, p.z);
if (!id)
continue;
Piece pieces[END_CITY_PIECES_MAX];
int n = getEndCityPieces(pieces, wi.seed, p.x >> 4, p.z >> 4);
vp.pieces.assign(pieces, pieces+n);
}
@ -144,10 +145,19 @@ void getStructs(std::vector<VarPos> *out, const StructureConfig sconf,
{
id = getBiomeAt(&g, 4, p.x >> 2, 0, p.z >> 2);
}
else if (sconf.structType == Fortress)
{
int n = getFortressPieces(pieces, sizeof(pieces)/sizeof(pieces[0]),
wi.mc, wi.seed, p.x >> 4, p.z >> 4);
vp.pieces.assign(pieces, pieces+n);
}
else if (g.mc >= MC_1_18)
{
if (!isViableStructureTerrain(sconf.structType, &g, p.x, p.z))
if (g_extgen.estimateTerrain &&
!isViableStructureTerrain(sconf.structType, &g, p.x, p.z))
{
continue;
}
}
getVariant(&vp.v, sconf.structType, wi.mc, wi.seed, p.x, p.z, id);
@ -186,14 +196,13 @@ void Quad::run()
biomes[i] = -1;
}
// sync biomeColors
g_mutex.lock();
g_mutex.unlock();
rgb = new uchar[w*h * 3];
if (lopt <= LOPT_OCEAN_256 || g->mc < MC_1_18 || dim != 0 || g->bn.nptype < 0)
{
biomesToImage(rgb, biomeColors, biomes, w, h, 1, 1);
// sync biomeColors
g_mutex.lock();
g_mutex.unlock();
biomesToImage(rgb, g_biomeColors, biomes, w, h, 1, 1);
}
else // climate parameter
{
@ -621,26 +630,25 @@ QString QWorld::getBiomeName(Pos p)
return s ? s : "";
}
static void refreshQuadColor(Quad *q)
{
QImage *img = q->img;
if (!img)
return;
if (q->lopt <= LOPT_OCEAN_256 || q->g->mc < MC_1_18 || q->dim != 0 || q->g->bn.nptype < 0)
biomesToImage(q->rgb, g_biomeColors, q->biomes, img->width(), img->height(), 1, 1);
}
void QWorld::refreshBiomeColors()
{
g_mutex.lock();
for (Level& l : lvb)
{
for (Quad *q : l.cells)
{
QImage *img = q->img;
if (!img)
continue;
biomesToImage(q->rgb, biomeColors, q->biomes, img->width(), img->height(), 1, 1);
}
refreshQuadColor(q);
}
for (Quad *q : cachedbiomes)
{
QImage *img = q->img;
if (!img)
continue;
biomesToImage(q->rgb, biomeColors, q->biomes, img->width(), img->height(), 1, 1);
}
refreshQuadColor(q);
g_mutex.unlock();
}
@ -1104,6 +1112,20 @@ void QWorld::draw(QPainter& painter, int vw, int vh, qreal focusx, qreal focusz,
if (selvp.v.ship)
sinfo.append("ship");
}
else if (seltype == D_FORTESS)
{
sinfo.append(QString::asprintf("size=%zu", selvp.pieces.size()));
int spawner = 0, wart = 0;
for (Piece& p : selvp.pieces)
{
spawner += p.type == BRIDGE_SPAWNER;
wart += p.type == CORRIDOR_NETHER_WART;
}
if (spawner)
sinfo.append(QString::asprintf("spawners=%d", spawner));
if (wart)
sinfo.append(QString::asprintf("nether_wart=%d", wart));
}
if (!sinfo.empty())
{
f = QFont();