53 myArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
54 myArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
81 statusBar()->showMessage(tr(
"Ready"));
85 setWindowTitle(tr(
"Crossfire Resource Editor"));
90 addDockWidget(Qt::RightDockWidgetArea,
myChanges);
97 restoreGeometry(
settings.mainWindowGeometry());
100 for (
int idx = 0; idx <
count; idx++) {
114 if (QMessageBox::question(
this,
"Discard changes?",
"You have unsaved changes, really discard them?") != QMessageBox::Yes) {
122 auto windows =
myArea->subWindowList();
123 settings.setSubWindowCount(windows.size());
124 for (
int idx = 0; idx < windows.size(); idx++) {
125 settings.setSubWindowPosition(idx, windows[idx]->saveGeometry());
126 auto widget = windows[idx]->widget();
128 if (crew !=
nullptr) {
129 settings.setSubWindowType(idx, crew->rootIndex());
136 settings.setMainWindowGeometry(saveGeometry());
139 myArea->closeAllSubWindows();
141 QMainWindow::closeEvent(
event);
150 template <
typename F>
156 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
157 QProgressDialog prog(this);
158 prog.setLabelText(tr(
"Waiting for maps browing to finish..."));
159 prog.setWindowModality(Qt::ApplicationModal);
161 prog.setVisible(true);
163 while (!myMapManager->browseFinished())
165 if (prog.wasCanceled()) {
166 QApplication::restoreOverrideCursor();
169 QApplication::processEvents();
172 QApplication::restoreOverrideCursor();
184 action->setStatusTip(statusTip);
195 myClearMapCache->setStatusTip(tr(
"Force a refresh of all map information at next start."));
210 auto add = [
this] (
int index,
const QString &
name,
const QString &tip) {
212 action->setStatusTip(tip);
217 add(-1, tr(
"Assets"), tr(
"Display all defined assets, except the experience table."));
225 QAction* exit =
myOpenMenu->addAction(tr(
"&Exit"));
226 exit->setStatusTip(tr(
"Close the application."));
227 connect(exit, SIGNAL(triggered()),
this, SLOT(close()));
238 QMenu* reportMenu = menuBar()->addMenu(tr(
"&Reports"));
239 reportMenu->addAction(
createAction(tr(
"Faces and animations report"), tr(
"Show faces and animations which are used by multiple archetypes, or not used."), [
this] {
onReportDuplicate(); }));
244 reportMenu->addAction(
createAction(tr(
"Player vs monsters"), tr(
"Compute statistics related to player vs monster combat."), [=] {
onReportPlayer(); },
true));
245 reportMenu->addAction(
createAction(tr(
"Summoned pets statistics"), tr(
"Display wc, hp, speed and other statistics for summoned pets."), [=] {
onReportSummon(); }));
246 reportMenu->addAction(
createAction(tr(
"Shop specialization"), tr(
"Display the list of shops and their specialization for items."), [=] {
onReportShops(); },
true));
247 reportMenu->addAction(
createAction(tr(
"Quest solved by players"), tr(
"Display quests the players have solved."), [=] {
onReportQuests(); },
true));
249 reportMenu->addAction(
createAction(tr(
"Unused archetypes"), tr(
"Display all archetypes which seem unused."), [=] {
onReportArchetypes(); },
true));
265 myWindows = menuBar()->addMenu(tr(
"&Windows"));
267 auto store =
createAction(tr(
"Restore windows positions at launch"), tr(
"If enabled then opened windows are automatically opened again when the application starts"));
268 store->setCheckable(
true);
271 connect(store, &QAction::triggered, [store] {
276 myWindows->addAction(
createAction(tr(
"Close current window"), tr(
"Close the currently focused window"), [
this] {
myArea->closeActiveSubWindow(); }));
277 myWindows->addAction(
createAction(tr(
"Close all windows"), tr(
"Close all opened windows"), [=] {
myArea->closeAllSubWindows(); }));
281 auto sep =
new QAction(
this);
282 sep->setSeparator(
true);
285 auto helpMenu = menuBar()->addMenu(tr(
"&Help"));
287 help->setShortcut(Qt::Key_F1);
288 helpMenu->addAction(
help);
290 helpMenu->addAction(
createAction(tr(
"About"), tr(
"About CRE"), [=] () { QMessageBox::about(
this, tr(
"About CRE"), tr(
"Crossfire Resource Editor")); }));
293 auto show =
createAction(tr(
"Show changes after updating"), tr(
"If checked, then show latest changes at first startup after an update"));
294 show->setCheckable(
true);
295 show->setChecked(
settings.showChanges());
296 helpMenu->addAction(show);
297 connect(show, &QAction::triggered, [&] (
bool checked) {
302 helpMenu->addAction(
createAction(tr(
"Changes"), tr(
"Display CRE changes"), [=] () {
myChanges->setVisible(
true); }));
313 resources->setAttribute(Qt::WA_DeleteOnClose);
316 auto widget =
myArea->addSubWindow(resources);
317 widget->setAttribute(Qt::WA_DeleteOnClose);
318 if (position.isEmpty()) {
319 if (
myArea->subWindowList().size() == 1) {
320 widget->setWindowState(Qt::WindowMaximized);
323 widget->restoreGeometry(position);
331 auto widget =
myArea->addSubWindow(experience);
332 if (!position.isEmpty()) {
333 widget->restoreGeometry(position);
341 const QString select =
settings.facesetToDisplay();
342 const bool use =
settings.facesetUseFallback();
349 QAction *
a =
new QAction(f->
fullname, fs);
350 a->setCheckable(
true);
383 statusBar()->showMessage(tr(
"Finished browsing maps."), 5000);
411 if (
arch->clone.animation == NULL)
413 if (arch->clone.face) {
414 faces[QString::fromLatin1(arch->clone.face->name)].append(QString(arch->name) +
" (arch)");
415 sstring key = object_get_value(&arch->clone,
"identified_face");
418 faces[QString(key)].append(QString(arch->name) +
" (arch)");
424 anims[
arch->clone.animation->name].append(QString(
arch->name) +
" (arch)");
428 anims[QString(
key)].append(QString(
arch->name) +
" (arch)");
437 for (
int i = 0; i <
anim->num_animations; i++)
440 if (!
done.contains(QString::fromLatin1(
anim->faces[i]->name)))
442 faces[QString::fromLatin1(anim->faces[i]->name)].append(QString(anim->name) +
" (animation)");
443 done.append(QString::fromLatin1(anim->faces[i]->name));
452 for (
auto art :
list->items)
454 if (art->item->animation == 0)
456 if (art->item->face) {
457 faces[QString::fromLatin1(art->item->face->name)].append(QString(art->item->name) +
" (art)");
461 faces[QString(
key)].append(QString(art->item->name) +
" (art)");
467 anims[art->item->animation->name].append(QString(art->item->name) +
" (art)");
471 anims[QString(
key)].append(QString(art->item->name) +
" (arch)");
478 if (
quest->face !=
nullptr)
480 faces[quest->face->name].append(QString(quest->quest_code) +
" (quest)");
488 faces[message->face->name].append(QString(message->identifier) +
" (message)");
492 for (
const auto map : myMapManager->allMaps())
494 for (
const auto face :
map->faces())
496 faces[face].append(QString(
map->path()) +
" (map)");
498 for (
const auto animation :
map->animations())
500 anims[animation].append(
map->path() +
" (map)");
504 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>");
510 if (
faces[
name].size() <= 1 ||
name.compare(
"blank.111") == 0)
521 report +=
"<h1>Unused faces:</h1><ul>";
524 if (
faces[face->name].size() > 0)
526 report += QString(
"<li>") + face->name +
"</li>";
530 report +=
"<h1>Animations used multiple times:</h1><ul>";
545 report +=
"<h1>Unused animations:</h1><ul>";
548 if (
anims[
anim->name].size() > 0 || !strcmp(
anim->name,
"###none"))
550 report += QString(
"<li>") +
anim->name +
"</li>";
555 report +=
"<h1>Objects having a face not part of their animation:</h1><ul>";
559 if (
arch->clone.animation == NULL ||
arch->clone.face == NULL) {
562 bool included =
false;
563 for (
int f = 0; f <
arch->clone.animation->num_animations && !included; f++) {
564 if (
arch->clone.animation->faces[f] ==
arch->clone.face) {
569 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);
580 QList<QStringList> damage;
588 spell.append(arch->clone.name);
590 for (int l = 0; l < settings.max_level; l++)
593 int dm = arch->clone.stats.dam + SP_level_dam_adjust(caster, &arch->clone);
594 int cost = SP_level_spellpoint_cost(caster, &arch->clone, SPELL_HIGHEST);
595 dam.append(tr(
"%1 [%2]").arg(dm).arg(cost));
603 QString
report(
"<table><thead><tr><th>level</th>");
605 for (
int i = 0; i < spell.size(); i++)
607 report +=
"<th>" + spell[i] +
"</th>";
610 report +=
"</tr></thead><tbody>";
614 report +=
"<tr><td>" + QString::number(
l) +
"</td>";
615 for (
int s = 0; s < spell.size(); s++)
616 report +=
"<td>" + damage[s][
l] +
"</td>";
620 report +=
"</tbody></table>";
626 static QString
alchemyTable(
const QString& skill, QStringList& noChance, std::vector<std::pair<QString, int>> &allIngredients)
630 QHash<int, QStringList> recipes;
635 for (
int ing = 1; ; ing++)
657 if (
arch->clone.title == NULL)
660 name = QString(
"%1 %2").arg(
arch->clone.name,
arch->clone.title);
667 QStringList ingredients;
670 ingredients.append(ingred->name);
671 const char*
name = ingred->name;
672 if (isdigit(ingred->name[0])) {
673 name = strchr(ingred->name,
' ') + 1;
675 auto ing = std::find_if(allIngredients.begin(), allIngredients.end(), [&] (
auto i) { return i.first == name; } );
676 if (ing != allIngredients.end()) {
679 allIngredients.push_back(std::make_pair(
name, 1));;
687 noChance.append(
name);
696 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);
697 report +=
"</tr></thead><tbody>";
699 QList<int> difficulties = recipes.keys();
701 foreach(
int difficulty, difficulties)
703 QStringList
line = recipes[difficulty];
708 report +=
"</tbody></table>";
713 static void doIngredients(
const std::vector<std::pair<QString, int>> &allIngredients,
const QString &criteria, QString &
report) {
714 report += QString(
"<h1>All items used as ingredients (%1)</h1>").arg(criteria);
716 for (
auto ing : allIngredients) {
717 report += QString(
"<li>%1 (%2 recipes)</li>").arg(ing.first).arg(ing.second);
729 skills.append(
arch->clone.name);
733 QString
report(
"<h1>Alchemy formulae</h1>");
734 QStringList noChance;
735 std::vector<std::pair<QString, int>> allIngredients;
737 foreach(
const QString skill, skills)
743 report += tr(
"<h1>Formulae with chance of 0</h1>");
745 foreach(
const QString&
name, noChance) {
748 report +=
"</th></table>";
750 std::sort(allIngredients.begin(), allIngredients.end(), [] (
auto i1,
auto i2) {
751 return i1.first.toLower() < i2.first.toLower();
755 std::sort(allIngredients.begin(), allIngredients.end(), [] (
auto i1,
auto i2) {
756 if (i1.second == i2.second) {
757 return i1.first.toLower() < i2.first.toLower();
759 return i1.second > i2.second;
769 QString output = QFileDialog::getSaveFileName(
this, tr(
"Destination file"),
"", tr(
"Dot files (*.dot);;All files (*.*)"));
770 if (output.isEmpty()) {
775 if (!
file.open(QIODevice::WriteOnly | QIODevice::Text)) {
776 QMessageBox::critical(
this,
"Write error", tr(
"Unable to write to %1").arg(output));
781 out <<
"digraph alchemy {\n";
783 QVector<const recipe*> recipes;
784 QHash<const recipe*, QString>
names;
785 QHash<QString, QVector<const recipe*> > products;
787 for (
int ing = 1; ; ing++)
795 QString product(
"???");
804 product =
arch->clone.name;
806 products[product].append(
recipe);
813 QHash<const recipe*, bool> added;
815 foreach (
const recipe* rec, recipes) {
817 const char*
name = ing->name;
818 if (isdigit(
name[0]) && strchr(
name,
' ')) {
821 QHash<QString, QVector<const recipe*> >::iterator
item = products.find(
name);
822 if (
item != products.end()) {
824 out << tr(
"alchemy_%1 [label=\"%2\"]\n").arg(recipes.indexOf(rec)).arg(
names[rec]);
827 for (
auto r =
item->begin();
r !=
item->end();
r++) {
829 out << tr(
"alchemy_%1 [label=\"%2\"]\n").arg(recipes.indexOf(*
r)).arg(
names[*
r]);
832 out << tr(
"alchemy_%1 -> alchemy_%2\n").arg(recipes.indexOf(*
r)).arg(recipes.indexOf(rec));
839 foreach (
const recipe* rec, recipes) {
844 out <<
"graph [labelloc=\"b\" labeljust=\"c\" label=\"Alchemy graph, formulae producing ingredients of other formulae";
846 out << tr(
" (%1 formulae not displayed)").arg(ignored);
857 QString
report = QString(
"<h2>%1</h2><table><thead><tr><th>Spell</th><th>Level</th><th>Path</th>").arg(skill);
858 report +=
"</tr></thead><tbody>";
860 QHash<int, QStringList>
spells;
865 auto buf = stringbuffer_finish(describe_spellpath_attenuation(nullptr, spell->clone.path_attuned, nullptr));
866 spells[spell->clone.level].append(QString(
"<tr><td>%1</td><td>%2</td><td>%3</td></tr>").arg(spell->clone.name).arg(spell->clone.level).arg(buf));
883 report +=
"</tbody></table>";
895 skills.append(
arch->clone.name);
900 QString
report(
"<h1>Spell list</h1>");
902 foreach(
const QString skill, skills)
922 int limit = 50,
result = 1;
926 memset(&sock, 0,
sizeof(sock));
927 strncpy(
pl.savebed_map,
"/HallOfSelection",
MAX_BUF);
935 if (dwarf_player_arch == NULL) {
947 if (skill_arch == NULL) {
968 qDebug() <<
"second removed??";
971 while (limit-- > 0 && obfirst->
stats.
hp >= 0 && obsecond->
stats.
hp >= 0)
1058 while (first !=
max)
1102 return "<td colspan=\"2\">-</td>";
1103 return "<td>" + QString::number(min) +
"</td><td>" + ((half != 0) ? QString::number(half) :
"") +
"</td>";
1114 QString
line =
"<tr>";
1117 line +=
"<td>" + QString::number(monster->
clone.
level) +
"</td>";
1118 line +=
"<td>" + QString::number(monster->
clone.
speed) +
"</td>";
1140 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1143 QMap<QString, archetype*>
monsters;
1144 QList<archetype*> skills;
1150 QString name(QString(arch->clone.name).toLower());
1151 if (monsters.contains(name))
1156 name = QString(arch->clone.name).toLower() +
"_" + QString::number(suffix);
1158 } while (monsters.contains(name));
1165 if (strcmp(arch->name,
"skill_missile_weapon") == 0 || strcmp(arch->name,
"skill_throwing") == 0)
1167 skills.append(arch);
1174 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;
1175 report +=
"<table border=\"1\"><tbody>\n";
1177 report +=
"<th rowspan=\"2\">Monster</th>";
1178 report +=
"<th rowspan=\"2\">level</th>";
1179 report +=
"<th rowspan=\"2\">speed</th>";
1180 report +=
"<th rowspan=\"2\">wc</th>";
1181 report +=
"<th rowspan=\"2\">dam</th>";
1182 report +=
"<th rowspan=\"2\">ac</th>";
1183 report +=
"<th rowspan=\"2\">hp</th>";
1184 report +=
"<th rowspan=\"2\">regen</th>";
1189 report +=
"<th colspan=\"2\">" + QString(skill->
clone.
name) +
"</th>";
1190 line +=
"<th>fv</th><th>hv</th>";
1202 report +=
"</tbody></table>\n";
1205 QApplication::restoreOverrideCursor();
1212 int level, wc_adj = 0;
1214 const object* spell = &summon->
clone;
1217 wc_adj = atoi(rate);
1222 QString
ac(
"<tr><td>ac</td>");
1223 QString hp(
"<tr><td>hp</td>");
1224 QString dam(
"<tr><td>dam</td>");
1225 QString speed(
"<tr><td>speed</td>");
1226 QString wc(
"<tr><td>wc</td>");
1227 int ihp, idam, iwc, diff;
1232 if (level < spell->
level)
1237 speed +=
"<td></td>";
1249 iwc -= (diff / wc_adj);
1251 ac +=
"<td>" + QString::number(other->
stats.
ac) +
"</td>";
1252 hp +=
"<td>" + QString::number(ihp) +
"</td>";
1253 dam +=
"<td>" + QString::number(idam) +
"</td>";
1254 speed +=
"<td>" + QString::number(fspeed) +
"</td>";
1255 wc +=
"<td>" + QString::number(iwc) +
"</td>";
1258 report +=
"<tr><td colspan=\"13\"><strong>" +
name +
"</strong></td></tr>\n";
1260 report +=
ac +
"</tr>\n" + hp +
"</tr>\n" + dam +
"</tr>\n" + speed +
"</tr>\n" + wc +
"</tr>\n\n";
1267 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1271 QString
report(tr(
"<h1>Summoned pet statistics</h1>\n")),
line;
1272 report +=
"<table border=\"1\">\n<thead>\n";
1274 report +=
"<th rowspan=\"2\">Spell</th>";
1275 report +=
"<th colspan=\"12\">Level</th>";
1281 report +=
"<th>" + QString::number(
level) +
"</th>";
1283 report +=
"</tr>\n</thead>\n<tbody>\n";
1285 QMap<QString, QString>
spells;
1293 spells[summon->clone.name] = reportSummon(summon, &summon->clone.other_arch->clone, QString(summon->clone.name));
1319 report +=
"</tbody>\n</table>\n";
1322 QApplication::restoreOverrideCursor();
1329 report +=
"<table border=\"1\">\n<thead>\n";
1331 report +=
"<th>Shop</th>";
1332 report +=
"<th>Greed</th>";
1333 report +=
"<th>Race</th>";
1334 report +=
"<th>Min</th>";
1335 report +=
"<th>Max</th>";
1339 items.removeAll(
item);
1341 report +=
"</tr>\n</thead><tbody>";
1348 if (
map->shopItems().size() == 0)
1353 line +=
"<td>" +
map->name() +
" " +
map->path() +
"</td>";
1354 line +=
"<td>" + QString::number(
map->shopGreed()) +
"</td>";
1355 line +=
"<td>" +
map->shopRace() +
"</td>";
1356 line +=
"<td>" + (
map->shopMin() != 0 ? QString::number(
map->shopMin()) :
"") +
"</td>";
1357 line +=
"<td>" + (
map->shopMax() != 0 ? QString::number(
map->shopMax()) :
"") +
"</td>";
1361 if (
map->shopItems()[
item] == 0)
1363 if (
map->shopItems()[
"*"] == 0)
1364 line +=
"<td></td>";
1366 line +=
"<td>" + QString::number(
map->shopItems()[
"*"]) +
"</td>";
1370 line +=
"<td>" + QString::number(
map->shopItems()[
item]) +
"</td>";
1378 report +=
"</tbody></table>";
1384 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1386 QString
report(tr(
"<h1>Shop information</h1>\n"));
1392 QStringList add =
map->shopItems().keys();
1393 foreach(
const QString
item, add)
1395 if (!items.contains(
item))
1403 part <<
"weapon" <<
"weapon improver" <<
"bow" <<
"arrow";
1407 part <<
"armour" <<
"armour improver" <<
"boots" <<
"bracers" <<
"cloak" <<
"girdle" <<
"gloves" <<
"helmet" <<
"shield";
1411 part <<
"amulet" <<
"potion" <<
"power_crystal" <<
"ring" <<
"rod" <<
"scroll" <<
"skillscroll" <<
"spellbook" <<
"wand";
1415 part <<
"container" <<
"food" <<
"key" <<
"lamp" <<
"skill tool" <<
"special key";
1418 if (!items.isEmpty())
1426 QApplication::restoreOverrideCursor();
1433 QStringList subdirs = dir.entryList(QStringList(
"*"), QDir::Dirs | QDir::NoDotAndDotDot);
1434 foreach(QString subdir, subdirs)
1439 QStringList
quests = dir.entryList(QStringList(
"*.quest"), QDir::Files);
1442 qDebug() <<
"read quest:" <<
path <<
file;
1444 QFile read(
path + QDir::separator() +
file);
1445 read.open(QFile::ReadOnly);
1446 QTextStream stream(&read);
1449 while (!(
line = stream.readLine(0)).isNull())
1451 if (
line.startsWith(
"quest "))
1459 if (
line ==
"end_quest")
1461 states[code][
name] = completed;
1465 if (
line.startsWith(
"state "))
1467 if (
line ==
"completed 1")
1475 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1477 QHash<QString, QHash<QString, bool> > states;
1479 directory += QDir::separator();
1485 codes.append(
quest->quest_code);
1488 QString
report(
"<html><body>\n<h1>Quests</h1>\n");
1490 QStringList
keys = states.keys();
1495 codes.removeAll(
key);
1497 report +=
"<h2>Quest: " + (
quest != NULL ?
quest->quest_title : (
key +
" ???")) +
"</h2>\n";
1499 QHash<QString, bool>
done = states[
key];
1519 report +=
"<p>" + tr(
"%1 completed out of %2 (%3%)").arg(completed).arg(
players.size()).arg(completed * 100 /
players.size()) +
"</p>\n";
1522 if (codes.length() > 0)
1526 report +=
"<h2>Quests never done</h2>\n<p>\n";
1527 foreach(QString code, codes)
1537 report +=
"</body>\n</html>\n";
1540 QApplication::restoreOverrideCursor();
1546 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1551 report +=
"<h1>Materials</h1>";
1552 report +=
"<table><tr><th>Name</th><th>Description</th></tr>";
1554 report += tr(
"<tr><td>%1</td><td>%2</td></tr>").arg(mat->name, mat->description);
1558 for (
int s = 0; s < 2; s++) {
1559 QString
name(s == 0 ?
"Saves" :
"Resistances");
1561 report += tr(
"<tr><th rowspan='2'>Name</th><th colspan='%1'>%2</th></tr>").arg(
NROFATTACKS).arg(
name);
1569 report += tr(
"<tr><td>%1</td>").arg(mat->name);
1571 int8_t val = (s == 0 ? mat->save[
r] : mat->mod[
r]);
1572 report += tr(
"<td>%1</td>").arg(val == 0 ? QString() : QString::number(val));
1582 QApplication::restoreOverrideCursor();
1588 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1593 report +=
"<h1>Apparently unused archetypes</h1>";
1594 report +=
"<h3>Warning: this list contains skills, items on style maps, and other things which are actually used.</h3>";
1596 report +=
"<tr><th>Image</th><th>Archetype name</th><th>Item name</th><th>Type</th></tr>";
1602 if (strstr(
arch->name,
"hpbar") !=
nullptr)
1616 QByteArray byteArray;
1617 QBuffer buffer(&byteArray);
1618 image.save(&buffer,
"PNG");
1619 QString iconBase64 = QString::fromLatin1(byteArray.toBase64().data());
1621 report += tr(
"<tr><td><img src='data:image/png;base64,%1'></td><td>%2</td><td>%3</td><td>%4</td></tr>")
1622 .arg(iconBase64,
arch->name,
arch->clone.name, td ? td->name : tr(
"unknown: %1").arg(
arch->clone.type));
1630 QApplication::restoreOverrideCursor();
1636 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1642 std::set<std::string>
faces, facesets, fields;
1644 for (
auto item : all)
1647 for (
auto fs :
item.second)
1649 facesets.insert(fs.first);
1650 for (
auto items : fs.second)
1652 fields.insert(items.first);
1659 facesets.erase(fs->
prefix);
1666 if (facesets.empty())
1668 report +=
"<h1>No invalid faceset</h1>\n";
1672 report +=
"<h1>Invalid faceset found</h1>\n";
1673 report +=
"<p>The faceset of the license file doesn't match any defined facesets.</p>";
1675 for (
auto fs : facesets)
1677 report +=
"<li>" + QString(fs.c_str()) +
"</li>\n";
1684 report +=
"<h1>No invalid face name</h1>\n";
1688 report +=
"<h1>Invalid face names found</h1>\n";
1689 report +=
"<p>The face name from the license file doesn't match any defined face.</p>";
1691 for (
auto f :
faces)
1693 report +=
"<li>" + QString(f.c_str()) +
"</li>\n";
1698 report +=
"<h1>All fields used in license descriptions</h1>\n";
1700 for (
auto f : fields)
1702 report +=
"<li>" + QString(f.c_str()) +
"</li>\n";
1708 QApplication::restoreOverrideCursor();
1714 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1720 auto groupCmp = [] (
const std::string &left,
const std::string &right) {
return left < right; };
1723 int name = left->
name().compare(right->name());
1728 return left->
path().compare(right->path()) < 0;
1730 std::map<std::string, std::vector<CREMapInformation *>, decltype(groupCmp)> groups(groupCmp);
1734 if (!
map->resetGroup().isEmpty())
1736 groups[
map->resetGroup().toStdString()].push_back(
map);
1742 report +=
"<h1>No reset group defined</h1>\n";
1746 for (
auto group : groups)
1748 report +=
"<h1>" + QString(group.first.c_str()) +
" (" + QString::number(group.second.size()) +
" maps)</h1>\n";
1750 std::sort(group.second.begin(), group.second.end(), mapCmp);
1751 for (
auto map : group.second)
1753 report += tr(
"<li>%1 (%2)</li>").arg(
map->name(),
map->path());
1761 QApplication::restoreOverrideCursor();
1798 confirm.setWindowTitle(tr(
"Crossfire Resource Editor"));
1799 confirm.setText(tr(
"Really clear map cache?"));
1800 confirm.setInformativeText(tr(
"This will force cache rebuild at next application start."));
1801 confirm.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
1802 confirm.setDefaultButton(QMessageBox::No);
1803 confirm.setIcon(QMessageBox::Question);
1804 if (
confirm.exec() == QMessageBox::Yes)
1822 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1826 QApplication::restoreOverrideCursor();
1827 QMessageBox::information(
this,
"Reload complete",
"Assets reload complete, you may need to change the selected item to see updated versions.");
1833 QString dir = QFileDialog::getExistingDirectory(
this, tr(
"Please select the 'sounds' directory"),
settings.soundsDirectory());
1842 auto windows =
myArea->subWindowList();
1843 bool hasWindows = !windows.empty();
1845 while (
myWindows->actions().size() > 6) {
1849 if (
a->isSeparator()) {
1850 a->setVisible(hasWindows);
1852 a->setEnabled(hasWindows);
1856 for (
int i = 0; i < windows.size(); ++i) {
1857 QMdiSubWindow *mdiSubWindow = windows.at(i);
1859 QString
title(mdiSubWindow->widget()->windowTitle());
1866 myArea->setActiveSubWindow(mdiSubWindow);
1868 action->setCheckable(
true);
1869 action ->setChecked(mdiSubWindow ==
myArea->activeSubWindow());
1876 for (
auto rm :
map->randomMaps()) {