54 myArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
55 myArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
82 statusBar()->showMessage(tr(
"Ready"));
86 setWindowTitle(tr(
"Crossfire Resource Editor"));
91 addDockWidget(Qt::RightDockWidgetArea,
myChanges);
98 restoreGeometry(
settings.mainWindowGeometry());
101 for (
int idx = 0; idx <
count; idx++) {
115 if (QMessageBox::question(
this,
"Discard changes?",
"You have unsaved changes, really discard them?") != QMessageBox::Yes) {
123 auto windows =
myArea->subWindowList();
124 settings.setSubWindowCount(windows.size());
125 for (
int idx = 0; idx < windows.size(); idx++) {
126 settings.setSubWindowPosition(idx, windows[idx]->saveGeometry());
127 auto widget = windows[idx]->widget();
129 if (crew !=
nullptr) {
130 settings.setSubWindowType(idx, crew->rootIndex());
137 settings.setMainWindowGeometry(saveGeometry());
140 myArea->closeAllSubWindows();
142 QMainWindow::closeEvent(
event);
153 action->setStatusTip(statusTip);
170 myReportPlayer->setStatusTip(tr(
"Compute statistics related to player vs monster combat."));
175 myReportShops =
new QAction(tr(
"Shop specialization"),
this);
176 myReportShops->setStatusTip(tr(
"Display the list of shops and their specialization for items."));
181 myReportQuests =
new QAction(tr(
"Quest solved by players"),
this);
182 myReportQuests->setStatusTip(tr(
"Display quests the players have solved."));
193 myClearMapCache->setStatusTip(tr(
"Force a refresh of all map information at next start."));
208 auto add = [
this] (
int index,
const QString &
name,
const QString &tip) {
210 action->setStatusTip(tip);
215 add(-1, tr(
"Assets"), tr(
"Display all defined assets, except the experience table."));
223 QAction* exit =
myOpenMenu->addAction(tr(
"&Exit"));
224 exit->setStatusTip(tr(
"Close the application."));
225 connect(exit, SIGNAL(triggered()),
this, SLOT(close()));
236 QMenu* reportMenu = menuBar()->addMenu(tr(
"&Reports"));
237 reportMenu->addAction(
createAction(tr(
"Faces and animations report"), tr(
"Show faces and animations which are used by multiple archetypes, or not used."),
this, SLOT(
onReportDuplicate())));
243 reportMenu->addAction(
createAction(tr(
"Summoned pets statistics"), tr(
"Display wc, hp, speed and other statistics for summoned pets."),
this, SLOT(
onReportSummon())));
254 auto resist =
createAction(tr(
"Monster resistances overview"), tr(
"Display an overview of resistances of monsters"));
255 connect(resist, &QAction::triggered, [&] {
270 myWindows = menuBar()->addMenu(tr(
"&Windows"));
272 auto store =
createAction(tr(
"Restore windows positions at launch"), tr(
"If enabled then opened windows are automatically opened again when the application starts"));
273 store->setCheckable(
true);
276 connect(store, &QAction::triggered, [store] {
281 myWindows->addAction(
createAction(tr(
"Close current window"), tr(
"Close the currently focused window"),
myArea, SLOT(closeActiveSubWindow())));
286 auto sep =
new QAction(
this);
287 sep->setSeparator(
true);
290 auto helpMenu = menuBar()->addMenu(tr(
"&Help"));
292 help->setShortcut(Qt::Key_F1);
293 helpMenu->addAction(
help);
296 auto about =
createAction(tr(
"About"), tr(
"About CRE"));
297 helpMenu->addAction(about);
298 connect(about, &QAction::triggered, [=] () { QMessageBox::about(
this, tr(
"About CRE"), tr(
"Crossfire Resource Editor")); });
301 auto show =
createAction(tr(
"Show changes after updating"), tr(
"If checked, then show latest changes at first startup after an update"));
302 show->setCheckable(
true);
303 show->setChecked(
settings.showChanges());
304 helpMenu->addAction(show);
305 connect(show, &QAction::triggered, [&] (
bool checked) {
310 auto changes =
createAction(tr(
"Changes"), tr(
"Display CRE changes"));
311 helpMenu->addAction(changes);
312 connect(changes, &QAction::triggered, [=] () {
myChanges->setVisible(
true); });
323 resources->setAttribute(Qt::WA_DeleteOnClose);
328 auto widget =
myArea->addSubWindow(resources);
329 widget->setAttribute(Qt::WA_DeleteOnClose);
330 if (position.isEmpty()) {
331 if (
myArea->subWindowList().size() == 1) {
332 widget->setWindowState(Qt::WindowMaximized);
335 widget->restoreGeometry(position);
343 auto widget =
myArea->addSubWindow(experience);
344 if (!position.isEmpty()) {
345 widget->restoreGeometry(position);
353 const QString select =
settings.facesetToDisplay();
354 const bool use =
settings.facesetUseFallback();
361 QAction *
a =
new QAction(f->
fullname, fs);
362 a->setCheckable(
true);
395 statusBar()->showMessage(tr(
"Finished browsing maps."), 5000);
433 if (
arch->clone.animation == NULL)
435 if (arch->clone.face) {
436 faces[QString::fromLatin1(arch->clone.face->name)].append(QString(arch->name) +
" (arch)");
437 sstring key = object_get_value(&arch->clone,
"identified_face");
440 faces[QString(key)].append(QString(arch->name) +
" (arch)");
446 anims[
arch->clone.animation->name].append(QString(
arch->name) +
" (arch)");
450 anims[QString(
key)].append(QString(
arch->name) +
" (arch)");
459 for (
int i = 0; i <
anim->num_animations; i++)
462 if (!
done.contains(QString::fromLatin1(
anim->faces[i]->name)))
464 faces[QString::fromLatin1(anim->faces[i]->name)].append(QString(anim->name) +
" (animation)");
465 done.append(QString::fromLatin1(anim->faces[i]->name));
474 for (
auto art :
list->items)
476 if (art->item->animation == 0)
478 if (art->item->face) {
479 faces[QString::fromLatin1(art->item->face->name)].append(QString(art->item->name) +
" (art)");
483 faces[QString(
key)].append(QString(art->item->name) +
" (art)");
489 anims[art->item->animation->name].append(QString(art->item->name) +
" (art)");
493 anims[QString(
key)].append(QString(art->item->name) +
" (arch)");
500 if (
quest->face !=
nullptr)
502 faces[quest->face->name].append(QString(quest->quest_code) +
" (quest)");
510 faces[message->face->name].append(QString(message->identifier) +
" (message)");
514 for (
const auto map : myMapManager->allMaps())
516 for (
const auto face :
map->faces())
518 faces[face].append(QString(
map->path()) +
" (map)");
520 for (
const auto animation :
map->animations())
522 anims[animation].append(
map->path() +
" (map)");
526 QString
report(
"<p><strong>Warning:</strong> this list doesn't take into account faces for all artifacts, especially the 'animation_suffix' ones.</p><h1>Faces used multiple times:</h1><ul>");
532 if (
faces[
name].size() <= 1 ||
name.compare(
"blank.111") == 0)
543 report +=
"<h1>Unused faces:</h1><ul>";
546 if (
faces[face->name].size() > 0)
548 report += QString(
"<li>") + face->name +
"</li>";
552 report +=
"<h1>Animations used multiple times:</h1><ul>";
567 report +=
"<h1>Unused animations:</h1><ul>";
570 if (
anims[
anim->name].size() > 0 || !strcmp(
anim->name,
"###none"))
572 report += QString(
"<li>") +
anim->name +
"</li>";
577 report +=
"<h1>Objects having a face not part of their animation:</h1><ul>";
581 if (
arch->clone.animation == NULL ||
arch->clone.face == NULL) {
584 bool included =
false;
585 for (
int f = 0; f <
arch->clone.animation->num_animations && !included; f++) {
586 if (
arch->clone.animation->faces[f] ==
arch->clone.face) {
591 report += QString(
"<li>%1 (%2) has face %3 not in animation %4</li>\n").arg(
arch->name,
arch->clone.name,
arch->clone.face->name,
arch->clone.animation->name);
602 QList<QStringList> damage;
610 spell.append(arch->clone.name);
612 for (int l = 0; l < settings.max_level; l++)
615 int dm = arch->clone.stats.dam + SP_level_dam_adjust(caster, &arch->clone);
616 int cost = SP_level_spellpoint_cost(caster, &arch->clone, SPELL_GRACE);
617 dam.append(tr(
"%1 [%2]").arg(dm).arg(cost));
625 QString
report(
"<table><thead><tr><th>level</th>");
627 for (
int i = 0; i < spell.size(); i++)
629 report +=
"<th>" + spell[i] +
"</th>";
632 report +=
"</tr></thead><tbody>";
636 report +=
"<tr><td>" + QString::number(
l) +
"</td>";
637 for (
int s = 0; s < spell.size(); s++)
638 report +=
"<td>" + damage[s][
l] +
"</td>";
642 report +=
"</tbody></table>";
648 static QString
alchemyTable(
const QString& skill, QStringList& noChance, std::vector<std::pair<QString, int>> &allIngredients)
652 QHash<int, QStringList> recipes;
657 for (
int ing = 1; ; ing++)
679 if (
arch->clone.title == NULL)
682 name = QString(
"%1 %2").arg(
arch->clone.name,
arch->clone.title);
689 QStringList ingredients;
692 ingredients.append(ingred->name);
693 const char*
name = ingred->name;
694 if (isdigit(ingred->name[0])) {
695 name = strchr(ingred->name,
' ') + 1;
697 auto ing = std::find_if(allIngredients.begin(), allIngredients.end(), [&] (
auto i) { return i.first == name; } );
698 if (ing != allIngredients.end()) {
701 allIngredients.push_back(std::make_pair(
name, 1));;
709 noChance.append(
name);
718 QString
report = QString(
"<h2>%1 (%2 recipes)</h2><table><thead><tr><th>product</th><th>difficulty</th><th>ingredients count</th><th>experience</th><th>Ingredients</th>").arg(skill).arg(
count);
719 report +=
"</tr></thead><tbody>";
721 QList<int> difficulties = recipes.keys();
723 foreach(
int difficulty, difficulties)
725 QStringList
line = recipes[difficulty];
730 report +=
"</tbody></table>";
735 static void doIngredients(
const std::vector<std::pair<QString, int>> &allIngredients,
const QString &criteria, QString &
report) {
736 report += QString(
"<h1>All items used as ingredients (%1)</h1>").arg(criteria);
738 for (
auto ing : allIngredients) {
739 report += QString(
"<li>%1 (%2 recipes)</li>").arg(ing.first).arg(ing.second);
751 skills.append(
arch->clone.name);
755 QString
report(
"<h1>Alchemy formulae</h1>");
756 QStringList noChance;
757 std::vector<std::pair<QString, int>> allIngredients;
759 foreach(
const QString skill, skills)
765 report += tr(
"<h1>Formulae with chance of 0</h1>");
767 foreach(
const QString&
name, noChance) {
770 report +=
"</th></table>";
772 std::sort(allIngredients.begin(), allIngredients.end(), [] (
auto i1,
auto i2) {
773 return i1.first.toLower() < i2.first.toLower();
777 std::sort(allIngredients.begin(), allIngredients.end(), [] (
auto i1,
auto i2) {
778 if (i1.second == i2.second) {
779 return i1.first.toLower() < i2.first.toLower();
781 return i1.second > i2.second;
791 QString output = QFileDialog::getSaveFileName(
this, tr(
"Destination file"),
"", tr(
"Dot files (*.dot);;All files (*.*)"));
792 if (output.isEmpty()) {
797 if (!
file.open(QIODevice::WriteOnly | QIODevice::Text)) {
798 QMessageBox::critical(
this,
"Write error", tr(
"Unable to write to %1").arg(output));
803 out <<
"digraph alchemy {\n";
805 QVector<const recipe*> recipes;
806 QHash<const recipe*, QString>
names;
807 QHash<QString, QVector<const recipe*> > products;
809 for (
int ing = 1; ; ing++)
817 QString product(
"???");
826 product =
arch->clone.name;
828 products[product].append(
recipe);
835 QHash<const recipe*, bool> added;
837 foreach (
const recipe* rec, recipes) {
839 const char*
name = ing->name;
840 if (isdigit(
name[0]) && strchr(
name,
' ')) {
843 QHash<QString, QVector<const recipe*> >::iterator
item = products.find(
name);
844 if (
item != products.end()) {
846 out << tr(
"alchemy_%1 [label=\"%2\"]\n").arg(recipes.indexOf(rec)).arg(
names[rec]);
849 for (
auto r =
item->begin();
r !=
item->end();
r++) {
851 out << tr(
"alchemy_%1 [label=\"%2\"]\n").arg(recipes.indexOf(*
r)).arg(
names[*
r]);
854 out << tr(
"alchemy_%1 -> alchemy_%2\n").arg(recipes.indexOf(*
r)).arg(recipes.indexOf(rec));
861 foreach (
const recipe* rec, recipes) {
866 out <<
"graph [labelloc=\"b\" labeljust=\"c\" label=\"Alchemy graph, formulae producing ingredients of other formulae";
868 out << tr(
" (%1 formulae not displayed)").arg(ignored);
879 QString
report = QString(
"<h2>%1</h2><table><thead><tr><th>Spell</th><th>Level</th>").arg(skill);
880 report +=
"</tr></thead><tbody>";
882 QHash<int, QStringList>
spells;
887 spells[spell->clone.level].append(QString(
"<tr><td>%1</td><td>%2</td></tr>").arg(spell->clone.name).arg(spell->clone.level));
903 report +=
"</tbody></table>";
915 skills.append(
arch->clone.name);
920 QString
report(
"<h1>Spell list</h1>");
922 foreach(
const QString skill, skills)
942 int limit = 50,
result = 1;
946 memset(&sock, 0,
sizeof(sock));
947 strncpy(
pl.savebed_map,
"/HallOfSelection",
MAX_BUF);
955 if (dwarf_player_arch == NULL) {
967 if (skill_arch == NULL) {
988 qDebug() <<
"second removed??";
991 while (limit-- > 0 && obfirst->
stats.
hp >= 0 && obsecond->
stats.
hp >= 0)
1078 while (first !=
max)
1122 return "<td colspan=\"2\">-</td>";
1123 return "<td>" + QString::number(min) +
"</td><td>" + ((half != 0) ? QString::number(half) :
"") +
"</td>";
1134 QString
line =
"<tr>";
1137 line +=
"<td>" + QString::number(monster->
clone.
level) +
"</td>";
1138 line +=
"<td>" + QString::number(monster->
clone.
speed) +
"</td>";
1160 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1163 QMap<QString, archetype*>
monsters;
1164 QList<archetype*> skills;
1170 QString name(QString(arch->clone.name).toLower());
1171 if (monsters.contains(name))
1176 name = QString(arch->clone.name).toLower() +
"_" + QString::number(suffix);
1178 } while (monsters.contains(name));
1185 if (strcmp(arch->name,
"skill_missile_weapon") == 0 || strcmp(arch->name,
"skill_throwing") == 0)
1187 skills.append(arch);
1194 QString
report(tr(
"<h1>Player vs monsters</h1><p><strong>fv</strong> is the level at which the first victory happened, <strong>hv</strong> is the level at which at least 50% of fights were victorious.</p>\n")),
line;
1195 report +=
"<table border=\"1\"><tbody>\n";
1197 report +=
"<th rowspan=\"2\">Monster</th>";
1198 report +=
"<th rowspan=\"2\">level</th>";
1199 report +=
"<th rowspan=\"2\">speed</th>";
1200 report +=
"<th rowspan=\"2\">wc</th>";
1201 report +=
"<th rowspan=\"2\">dam</th>";
1202 report +=
"<th rowspan=\"2\">ac</th>";
1203 report +=
"<th rowspan=\"2\">hp</th>";
1204 report +=
"<th rowspan=\"2\">regen</th>";
1209 report +=
"<th colspan=\"2\">" + QString(skill->
clone.
name) +
"</th>";
1210 line +=
"<th>fv</th><th>hv</th>";
1222 report +=
"</tbody></table>\n";
1225 QApplication::restoreOverrideCursor();
1232 int level, wc_adj = 0;
1234 const object* spell = &summon->
clone;
1237 wc_adj = atoi(rate);
1242 QString
ac(
"<tr><td>ac</td>");
1243 QString hp(
"<tr><td>hp</td>");
1244 QString dam(
"<tr><td>dam</td>");
1245 QString speed(
"<tr><td>speed</td>");
1246 QString wc(
"<tr><td>wc</td>");
1247 int ihp, idam, iwc, diff;
1252 if (level < spell->
level)
1257 speed +=
"<td></td>";
1269 iwc -= (diff / wc_adj);
1271 ac +=
"<td>" + QString::number(other->
stats.
ac) +
"</td>";
1272 hp +=
"<td>" + QString::number(ihp) +
"</td>";
1273 dam +=
"<td>" + QString::number(idam) +
"</td>";
1274 speed +=
"<td>" + QString::number(fspeed) +
"</td>";
1275 wc +=
"<td>" + QString::number(iwc) +
"</td>";
1278 report +=
"<tr><td colspan=\"13\"><strong>" +
name +
"</strong></td></tr>\n";
1280 report +=
ac +
"</tr>\n" + hp +
"</tr>\n" + dam +
"</tr>\n" + speed +
"</tr>\n" + wc +
"</tr>\n\n";
1287 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1291 QString
report(tr(
"<h1>Summoned pet statistics</h1>\n")),
line;
1292 report +=
"<table border=\"1\">\n<thead>\n";
1294 report +=
"<th rowspan=\"2\">Spell</th>";
1295 report +=
"<th colspan=\"12\">Level</th>";
1301 report +=
"<th>" + QString::number(
level) +
"</th>";
1303 report +=
"</tr>\n</thead>\n<tbody>\n";
1305 QMap<QString, QString>
spells;
1313 spells[summon->clone.name] = reportSummon(summon, &summon->clone.other_arch->clone, QString(summon->clone.name));
1339 report +=
"</tbody>\n</table>\n";
1342 QApplication::restoreOverrideCursor();
1349 report +=
"<table border=\"1\">\n<thead>\n";
1351 report +=
"<th>Shop</th>";
1352 report +=
"<th>Greed</th>";
1353 report +=
"<th>Race</th>";
1354 report +=
"<th>Min</th>";
1355 report +=
"<th>Max</th>";
1359 items.removeAll(
item);
1361 report +=
"</tr>\n</thead><tbody>";
1368 if (
map->shopItems().size() == 0)
1373 line +=
"<td>" +
map->name() +
" " +
map->path() +
"</td>";
1374 line +=
"<td>" + QString::number(
map->shopGreed()) +
"</td>";
1375 line +=
"<td>" +
map->shopRace() +
"</td>";
1376 line +=
"<td>" + (
map->shopMin() != 0 ? QString::number(
map->shopMin()) :
"") +
"</td>";
1377 line +=
"<td>" + (
map->shopMax() != 0 ? QString::number(
map->shopMax()) :
"") +
"</td>";
1381 if (
map->shopItems()[
item] == 0)
1383 if (
map->shopItems()[
"*"] == 0)
1384 line +=
"<td></td>";
1386 line +=
"<td>" + QString::number(
map->shopItems()[
"*"]) +
"</td>";
1390 line +=
"<td>" + QString::number(
map->shopItems()[
item]) +
"</td>";
1398 report +=
"</tbody></table>";
1404 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1406 QString
report(tr(
"<h1>Shop information</h1>\n"));
1412 QStringList add =
map->shopItems().keys();
1413 foreach(
const QString
item, add)
1415 if (!items.contains(
item))
1423 part <<
"weapon" <<
"weapon improver" <<
"bow" <<
"arrow";
1427 part <<
"armour" <<
"armour improver" <<
"boots" <<
"bracers" <<
"cloak" <<
"girdle" <<
"gloves" <<
"helmet" <<
"shield";
1431 part <<
"amulet" <<
"potion" <<
"power_crystal" <<
"ring" <<
"rod" <<
"scroll" <<
"skillscroll" <<
"spellbook" <<
"wand";
1435 part <<
"container" <<
"food" <<
"key" <<
"lamp" <<
"skill tool" <<
"special key";
1438 if (!items.isEmpty())
1446 QApplication::restoreOverrideCursor();
1453 QStringList subdirs = dir.entryList(QStringList(
"*"), QDir::Dirs | QDir::NoDotAndDotDot);
1454 foreach(QString subdir, subdirs)
1459 QStringList
quests = dir.entryList(QStringList(
"*.quest"), QDir::Files);
1462 qDebug() <<
"read quest:" <<
path <<
file;
1464 QFile read(
path + QDir::separator() +
file);
1465 read.open(QFile::ReadOnly);
1466 QTextStream stream(&read);
1469 while (!(
line = stream.readLine(0)).isNull())
1471 if (
line.startsWith(
"quest "))
1479 if (
line ==
"end_quest")
1481 states[code][
name] = completed;
1485 if (
line.startsWith(
"state "))
1487 if (
line ==
"completed 1")
1495 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1497 QHash<QString, QHash<QString, bool> > states;
1499 directory += QDir::separator();
1505 codes.append(
quest->quest_code);
1508 QString
report(
"<html><body>\n<h1>Quests</h1>\n");
1510 QStringList
keys = states.keys();
1515 codes.removeAll(
key);
1517 report +=
"<h2>Quest: " + (
quest != NULL ?
quest->quest_title : (
key +
" ???")) +
"</h2>\n";
1519 QHash<QString, bool>
done = states[
key];
1539 report +=
"<p>" + tr(
"%1 completed out of %2 (%3%)").arg(completed).arg(
players.size()).arg(completed * 100 /
players.size()) +
"</p>\n";
1542 if (codes.length() > 0)
1546 report +=
"<h2>Quests never done</h2>\n<p>\n";
1547 foreach(QString code, codes)
1557 report +=
"</body>\n</html>\n";
1560 QApplication::restoreOverrideCursor();
1566 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1571 report +=
"<h1>Materials</h1>";
1572 report +=
"<table><tr><th>Name</th><th>Description</th></tr>";
1574 report += tr(
"<tr><td>%1</td><td>%2</td></tr>").arg(mat->name, mat->description);
1578 for (
int s = 0; s < 2; s++) {
1579 QString
name(s == 0 ?
"Saves" :
"Resistances");
1581 report += tr(
"<tr><th rowspan='2'>Name</th><th colspan='%1'>%2</th></tr>").arg(
NROFATTACKS).arg(
name);
1589 report += tr(
"<tr><td>%1</td>").arg(mat->name);
1591 int8_t val = (s == 0 ? mat->save[
r] : mat->mod[
r]);
1592 report += tr(
"<td>%1</td>").arg(val == 0 ? QString() : QString::number(val));
1602 QApplication::restoreOverrideCursor();
1608 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1613 report +=
"<h1>Apparently unused archetypes</h1>";
1614 report +=
"<h3>Warning: this list contains skills, items on style maps, and other things which are actually used.</h3>";
1616 report +=
"<tr><th>Image</th><th>Archetype name</th><th>Item name</th><th>Type</th></tr>";
1622 if (strstr(
arch->name,
"hpbar") !=
nullptr)
1636 QByteArray byteArray;
1637 QBuffer buffer(&byteArray);
1638 image.save(&buffer,
"PNG");
1639 QString iconBase64 = QString::fromLatin1(byteArray.toBase64().data());
1641 report += tr(
"<tr><td><img src='data:image/png;base64,%1'></td><td>%2</td><td>%3</td><td>%4</td></tr>")
1642 .arg(iconBase64,
arch->name,
arch->clone.name, td ? td->name : tr(
"unknown: %1").arg(
arch->clone.type));
1650 QApplication::restoreOverrideCursor();
1656 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1662 std::set<std::string>
faces, facesets, fields;
1664 for (
auto item : all)
1667 for (
auto fs :
item.second)
1669 facesets.insert(fs.first);
1670 for (
auto items : fs.second)
1672 fields.insert(items.first);
1679 facesets.erase(fs->
prefix);
1686 if (facesets.empty())
1688 report +=
"<h1>No invalid faceset</h1>\n";
1692 report +=
"<h1>Invalid faceset found</h1>\n";
1693 report +=
"<p>The faceset of the license file doesn't match any defined facesets.</p>";
1695 for (
auto fs : facesets)
1697 report +=
"<li>" + QString(fs.c_str()) +
"</li>\n";
1704 report +=
"<h1>No invalid face name</h1>\n";
1708 report +=
"<h1>Invalid face names found</h1>\n";
1709 report +=
"<p>The face name from the license file doesn't match any defined face.</p>";
1711 for (
auto f :
faces)
1713 report +=
"<li>" + QString(f.c_str()) +
"</li>\n";
1718 report +=
"<h1>All fields used in license descriptions</h1>\n";
1720 for (
auto f : fields)
1722 report +=
"<li>" + QString(f.c_str()) +
"</li>\n";
1728 QApplication::restoreOverrideCursor();
1734 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1740 auto groupCmp = [] (
const std::string &left,
const std::string &right) {
return left < right; };
1743 int name = left->
name().compare(right->name());
1748 return left->
path().compare(right->path()) < 0;
1750 std::map<std::string, std::vector<CREMapInformation *>, decltype(groupCmp)> groups(groupCmp);
1754 if (!
map->resetGroup().isEmpty())
1756 groups[
map->resetGroup().toStdString()].push_back(
map);
1762 report +=
"<h1>No reset group defined</h1>\n";
1766 for (
auto group : groups)
1768 report +=
"<h1>" + QString(group.first.c_str()) +
" (" + QString::number(group.second.size()) +
" maps)</h1>\n";
1770 std::sort(group.second.begin(), group.second.end(), mapCmp);
1771 for (
auto map : group.second)
1773 report += tr(
"<li>%1 (%2)</li>").arg(
map->name(),
map->path());
1781 QApplication::restoreOverrideCursor();
1818 confirm.setWindowTitle(tr(
"Crossfire Resource Editor"));
1819 confirm.setText(
"Really clear map cache?");
1820 confirm.setInformativeText(
"This will force cache rebuild at next application start.");
1821 confirm.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
1822 confirm.setDefaultButton(QMessageBox::No);
1823 confirm.setIcon(QMessageBox::Question);
1824 if (
confirm.exec() == QMessageBox::Yes)
1842 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1846 QApplication::restoreOverrideCursor();
1847 QMessageBox::information(
this,
"Reload complete",
"Assets reload complete, you may need to change the selected item to see updated versions.");
1853 QString dir = QFileDialog::getExistingDirectory(
this, tr(
"Please select the 'sounds' directory"),
settings.soundsDirectory());
1862 auto windows =
myArea->subWindowList();
1863 bool hasWindows = !windows.empty();
1865 while (
myWindows->actions().size() > 6) {
1869 if (
a->isSeparator()) {
1870 a->setVisible(hasWindows);
1872 a->setEnabled(hasWindows);
1876 for (
int i = 0; i < windows.size(); ++i) {
1877 QMdiSubWindow *mdiSubWindow = windows.at(i);
1879 QString
title(mdiSubWindow->widget()->windowTitle());
1886 myArea->setActiveSubWindow(mdiSubWindow);
1888 action->setCheckable(
true);
1889 action ->setChecked(mdiSubWindow ==
myArea->activeSubWindow());
1896 for (
auto rm :
map->randomMaps()) {