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);
151 template <
typename F>
157 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
158 QProgressDialog prog(this);
159 prog.setLabelText(tr(
"Waiting for maps browing to finish..."));
160 prog.setWindowModality(Qt::ApplicationModal);
162 prog.setVisible(true);
164 while (!myMapManager->browseFinished())
166 if (prog.wasCanceled()) {
167 QApplication::restoreOverrideCursor();
170 QApplication::processEvents();
173 QApplication::restoreOverrideCursor();
185 action->setStatusTip(statusTip);
196 myClearMapCache->setStatusTip(tr(
"Force a refresh of all map information at next start."));
211 auto add = [
this] (
int index,
const QString &
name,
const QString &tip) {
213 action->setStatusTip(tip);
218 add(-1, tr(
"Assets"), tr(
"Display all defined assets, except the experience table."));
226 QAction* exit =
myOpenMenu->addAction(tr(
"&Exit"));
227 exit->setStatusTip(tr(
"Close the application."));
228 connect(exit, SIGNAL(triggered()),
this, SLOT(close()));
239 QMenu* reportMenu = menuBar()->addMenu(tr(
"&Reports"));
240 reportMenu->addAction(
createAction(tr(
"Faces and animations report"), tr(
"Show faces and animations which are used by multiple archetypes, or not used."), [
this] {
onReportDuplicate(); }));
245 reportMenu->addAction(
createAction(tr(
"Player vs monsters"), tr(
"Compute statistics related to player vs monster combat."), [=] {
onReportPlayer(); },
true));
246 reportMenu->addAction(
createAction(tr(
"Summoned pets statistics"), tr(
"Display wc, hp, speed and other statistics for summoned pets."), [=] {
onReportSummon(); }));
247 reportMenu->addAction(
createAction(tr(
"Shop specialization"), tr(
"Display the list of shops and their specialization for items."), [=] {
onReportShops(); },
true));
248 reportMenu->addAction(
createAction(tr(
"Quest solved by players"), tr(
"Display quests the players have solved."), [=] {
onReportQuests(); },
true));
250 reportMenu->addAction(
createAction(tr(
"Unused archetypes"), tr(
"Display all archetypes which seem unused."), [=] {
onReportArchetypes(); },
true));
256 auto resist =
createAction(tr(
"Monster resistances overview"), tr(
"Display an overview of resistances of monsters"));
257 connect(resist, &QAction::triggered, [&] {
272 myWindows = menuBar()->addMenu(tr(
"&Windows"));
274 auto store =
createAction(tr(
"Restore windows positions at launch"), tr(
"If enabled then opened windows are automatically opened again when the application starts"));
275 store->setCheckable(
true);
278 connect(store, &QAction::triggered, [store] {
283 myWindows->addAction(
createAction(tr(
"Close current window"), tr(
"Close the currently focused window"), [
this] {
myArea->closeActiveSubWindow(); }));
284 myWindows->addAction(
createAction(tr(
"Close all windows"), tr(
"Close all opened windows"), [=] {
myArea->closeAllSubWindows(); }));
288 auto sep =
new QAction(
this);
289 sep->setSeparator(
true);
292 auto helpMenu = menuBar()->addMenu(tr(
"&Help"));
294 help->setShortcut(Qt::Key_F1);
295 helpMenu->addAction(
help);
297 helpMenu->addAction(
createAction(tr(
"About"), tr(
"About CRE"), [=] () { QMessageBox::about(
this, tr(
"About CRE"), tr(
"Crossfire Resource Editor")); }));
300 auto show =
createAction(tr(
"Show changes after updating"), tr(
"If checked, then show latest changes at first startup after an update"));
301 show->setCheckable(
true);
302 show->setChecked(
settings.showChanges());
303 helpMenu->addAction(show);
304 connect(show, &QAction::triggered, [&] (
bool checked) {
309 helpMenu->addAction(
createAction(tr(
"Changes"), tr(
"Display CRE changes"), [=] () {
myChanges->setVisible(
true); }));
320 resources->setAttribute(Qt::WA_DeleteOnClose);
325 auto widget =
myArea->addSubWindow(resources);
326 widget->setAttribute(Qt::WA_DeleteOnClose);
327 if (position.isEmpty()) {
328 if (
myArea->subWindowList().size() == 1) {
329 widget->setWindowState(Qt::WindowMaximized);
332 widget->restoreGeometry(position);
340 auto widget =
myArea->addSubWindow(experience);
341 if (!position.isEmpty()) {
342 widget->restoreGeometry(position);
350 const QString select =
settings.facesetToDisplay();
351 const bool use =
settings.facesetUseFallback();
358 QAction *
a =
new QAction(f->
fullname, fs);
359 a->setCheckable(
true);
392 statusBar()->showMessage(tr(
"Finished browsing maps."), 5000);
425 if (
arch->clone.animation == NULL)
427 if (arch->clone.face) {
428 faces[QString::fromLatin1(arch->clone.face->name)].append(QString(arch->name) +
" (arch)");
429 sstring key = object_get_value(&arch->clone,
"identified_face");
432 faces[QString(key)].append(QString(arch->name) +
" (arch)");
438 anims[
arch->clone.animation->name].append(QString(
arch->name) +
" (arch)");
442 anims[QString(
key)].append(QString(
arch->name) +
" (arch)");
451 for (
int i = 0; i <
anim->num_animations; i++)
454 if (!
done.contains(QString::fromLatin1(
anim->faces[i]->name)))
456 faces[QString::fromLatin1(anim->faces[i]->name)].append(QString(anim->name) +
" (animation)");
457 done.append(QString::fromLatin1(anim->faces[i]->name));
466 for (
auto art :
list->items)
468 if (art->item->animation == 0)
470 if (art->item->face) {
471 faces[QString::fromLatin1(art->item->face->name)].append(QString(art->item->name) +
" (art)");
475 faces[QString(
key)].append(QString(art->item->name) +
" (art)");
481 anims[art->item->animation->name].append(QString(art->item->name) +
" (art)");
485 anims[QString(
key)].append(QString(art->item->name) +
" (arch)");
492 if (
quest->face !=
nullptr)
494 faces[quest->face->name].append(QString(quest->quest_code) +
" (quest)");
502 faces[message->face->name].append(QString(message->identifier) +
" (message)");
506 for (
const auto map : myMapManager->allMaps())
508 for (
const auto face :
map->faces())
510 faces[face].append(QString(
map->path()) +
" (map)");
512 for (
const auto animation :
map->animations())
514 anims[animation].append(
map->path() +
" (map)");
518 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>");
524 if (
faces[
name].size() <= 1 ||
name.compare(
"blank.111") == 0)
535 report +=
"<h1>Unused faces:</h1><ul>";
538 if (
faces[face->name].size() > 0)
540 report += QString(
"<li>") + face->name +
"</li>";
544 report +=
"<h1>Animations used multiple times:</h1><ul>";
559 report +=
"<h1>Unused animations:</h1><ul>";
562 if (
anims[
anim->name].size() > 0 || !strcmp(
anim->name,
"###none"))
564 report += QString(
"<li>") +
anim->name +
"</li>";
569 report +=
"<h1>Objects having a face not part of their animation:</h1><ul>";
573 if (
arch->clone.animation == NULL ||
arch->clone.face == NULL) {
576 bool included =
false;
577 for (
int f = 0; f <
arch->clone.animation->num_animations && !included; f++) {
578 if (
arch->clone.animation->faces[f] ==
arch->clone.face) {
583 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);
594 QList<QStringList> damage;
602 spell.append(arch->clone.name);
604 for (int l = 0; l < settings.max_level; l++)
607 int dm = arch->clone.stats.dam + SP_level_dam_adjust(caster, &arch->clone);
608 int cost = SP_level_spellpoint_cost(caster, &arch->clone, SPELL_GRACE);
609 dam.append(tr(
"%1 [%2]").arg(dm).arg(cost));
617 QString
report(
"<table><thead><tr><th>level</th>");
619 for (
int i = 0; i < spell.size(); i++)
621 report +=
"<th>" + spell[i] +
"</th>";
624 report +=
"</tr></thead><tbody>";
628 report +=
"<tr><td>" + QString::number(
l) +
"</td>";
629 for (
int s = 0; s < spell.size(); s++)
630 report +=
"<td>" + damage[s][
l] +
"</td>";
634 report +=
"</tbody></table>";
640 static QString
alchemyTable(
const QString& skill, QStringList& noChance, std::vector<std::pair<QString, int>> &allIngredients)
644 QHash<int, QStringList> recipes;
649 for (
int ing = 1; ; ing++)
671 if (
arch->clone.title == NULL)
674 name = QString(
"%1 %2").arg(
arch->clone.name,
arch->clone.title);
681 QStringList ingredients;
684 ingredients.append(ingred->name);
685 const char*
name = ingred->name;
686 if (isdigit(ingred->name[0])) {
687 name = strchr(ingred->name,
' ') + 1;
689 auto ing = std::find_if(allIngredients.begin(), allIngredients.end(), [&] (
auto i) { return i.first == name; } );
690 if (ing != allIngredients.end()) {
693 allIngredients.push_back(std::make_pair(
name, 1));;
701 noChance.append(
name);
710 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);
711 report +=
"</tr></thead><tbody>";
713 QList<int> difficulties = recipes.keys();
715 foreach(
int difficulty, difficulties)
717 QStringList
line = recipes[difficulty];
722 report +=
"</tbody></table>";
727 static void doIngredients(
const std::vector<std::pair<QString, int>> &allIngredients,
const QString &criteria, QString &
report) {
728 report += QString(
"<h1>All items used as ingredients (%1)</h1>").arg(criteria);
730 for (
auto ing : allIngredients) {
731 report += QString(
"<li>%1 (%2 recipes)</li>").arg(ing.first).arg(ing.second);
743 skills.append(
arch->clone.name);
747 QString
report(
"<h1>Alchemy formulae</h1>");
748 QStringList noChance;
749 std::vector<std::pair<QString, int>> allIngredients;
751 foreach(
const QString skill, skills)
757 report += tr(
"<h1>Formulae with chance of 0</h1>");
759 foreach(
const QString&
name, noChance) {
762 report +=
"</th></table>";
764 std::sort(allIngredients.begin(), allIngredients.end(), [] (
auto i1,
auto i2) {
765 return i1.first.toLower() < i2.first.toLower();
769 std::sort(allIngredients.begin(), allIngredients.end(), [] (
auto i1,
auto i2) {
770 if (i1.second == i2.second) {
771 return i1.first.toLower() < i2.first.toLower();
773 return i1.second > i2.second;
783 QString output = QFileDialog::getSaveFileName(
this, tr(
"Destination file"),
"", tr(
"Dot files (*.dot);;All files (*.*)"));
784 if (output.isEmpty()) {
789 if (!
file.open(QIODevice::WriteOnly | QIODevice::Text)) {
790 QMessageBox::critical(
this,
"Write error", tr(
"Unable to write to %1").arg(output));
795 out <<
"digraph alchemy {\n";
797 QVector<const recipe*> recipes;
798 QHash<const recipe*, QString>
names;
799 QHash<QString, QVector<const recipe*> > products;
801 for (
int ing = 1; ; ing++)
809 QString product(
"???");
818 product =
arch->clone.name;
820 products[product].append(
recipe);
827 QHash<const recipe*, bool> added;
829 foreach (
const recipe* rec, recipes) {
831 const char*
name = ing->name;
832 if (isdigit(
name[0]) && strchr(
name,
' ')) {
835 QHash<QString, QVector<const recipe*> >::iterator
item = products.find(
name);
836 if (
item != products.end()) {
838 out << tr(
"alchemy_%1 [label=\"%2\"]\n").arg(recipes.indexOf(rec)).arg(
names[rec]);
841 for (
auto r =
item->begin();
r !=
item->end();
r++) {
843 out << tr(
"alchemy_%1 [label=\"%2\"]\n").arg(recipes.indexOf(*
r)).arg(
names[*
r]);
846 out << tr(
"alchemy_%1 -> alchemy_%2\n").arg(recipes.indexOf(*
r)).arg(recipes.indexOf(rec));
853 foreach (
const recipe* rec, recipes) {
858 out <<
"graph [labelloc=\"b\" labeljust=\"c\" label=\"Alchemy graph, formulae producing ingredients of other formulae";
860 out << tr(
" (%1 formulae not displayed)").arg(ignored);
871 QString
report = QString(
"<h2>%1</h2><table><thead><tr><th>Spell</th><th>Level</th><th>Path</th>").arg(skill);
872 report +=
"</tr></thead><tbody>";
874 QHash<int, QStringList>
spells;
879 auto buf = stringbuffer_finish(describe_spellpath_attenuation(nullptr, spell->clone.path_attuned, nullptr));
880 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));
897 report +=
"</tbody></table>";
909 skills.append(
arch->clone.name);
914 QString
report(
"<h1>Spell list</h1>");
916 foreach(
const QString skill, skills)
936 int limit = 50,
result = 1;
940 memset(&sock, 0,
sizeof(sock));
941 strncpy(
pl.savebed_map,
"/HallOfSelection",
MAX_BUF);
949 if (dwarf_player_arch == NULL) {
961 if (skill_arch == NULL) {
982 qDebug() <<
"second removed??";
985 while (limit-- > 0 && obfirst->
stats.
hp >= 0 && obsecond->
stats.
hp >= 0)
1072 while (first !=
max)
1116 return "<td colspan=\"2\">-</td>";
1117 return "<td>" + QString::number(min) +
"</td><td>" + ((half != 0) ? QString::number(half) :
"") +
"</td>";
1128 QString
line =
"<tr>";
1131 line +=
"<td>" + QString::number(monster->
clone.
level) +
"</td>";
1132 line +=
"<td>" + QString::number(monster->
clone.
speed) +
"</td>";
1154 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1157 QMap<QString, archetype*>
monsters;
1158 QList<archetype*> skills;
1164 QString name(QString(arch->clone.name).toLower());
1165 if (monsters.contains(name))
1170 name = QString(arch->clone.name).toLower() +
"_" + QString::number(suffix);
1172 } while (monsters.contains(name));
1179 if (strcmp(arch->name,
"skill_missile_weapon") == 0 || strcmp(arch->name,
"skill_throwing") == 0)
1181 skills.append(arch);
1188 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;
1189 report +=
"<table border=\"1\"><tbody>\n";
1191 report +=
"<th rowspan=\"2\">Monster</th>";
1192 report +=
"<th rowspan=\"2\">level</th>";
1193 report +=
"<th rowspan=\"2\">speed</th>";
1194 report +=
"<th rowspan=\"2\">wc</th>";
1195 report +=
"<th rowspan=\"2\">dam</th>";
1196 report +=
"<th rowspan=\"2\">ac</th>";
1197 report +=
"<th rowspan=\"2\">hp</th>";
1198 report +=
"<th rowspan=\"2\">regen</th>";
1203 report +=
"<th colspan=\"2\">" + QString(skill->
clone.
name) +
"</th>";
1204 line +=
"<th>fv</th><th>hv</th>";
1216 report +=
"</tbody></table>\n";
1219 QApplication::restoreOverrideCursor();
1226 int level, wc_adj = 0;
1228 const object* spell = &summon->
clone;
1231 wc_adj = atoi(rate);
1236 QString
ac(
"<tr><td>ac</td>");
1237 QString hp(
"<tr><td>hp</td>");
1238 QString dam(
"<tr><td>dam</td>");
1239 QString speed(
"<tr><td>speed</td>");
1240 QString wc(
"<tr><td>wc</td>");
1241 int ihp, idam, iwc, diff;
1246 if (level < spell->
level)
1251 speed +=
"<td></td>";
1263 iwc -= (diff / wc_adj);
1265 ac +=
"<td>" + QString::number(other->
stats.
ac) +
"</td>";
1266 hp +=
"<td>" + QString::number(ihp) +
"</td>";
1267 dam +=
"<td>" + QString::number(idam) +
"</td>";
1268 speed +=
"<td>" + QString::number(fspeed) +
"</td>";
1269 wc +=
"<td>" + QString::number(iwc) +
"</td>";
1272 report +=
"<tr><td colspan=\"13\"><strong>" +
name +
"</strong></td></tr>\n";
1274 report +=
ac +
"</tr>\n" + hp +
"</tr>\n" + dam +
"</tr>\n" + speed +
"</tr>\n" + wc +
"</tr>\n\n";
1281 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1285 QString
report(tr(
"<h1>Summoned pet statistics</h1>\n")),
line;
1286 report +=
"<table border=\"1\">\n<thead>\n";
1288 report +=
"<th rowspan=\"2\">Spell</th>";
1289 report +=
"<th colspan=\"12\">Level</th>";
1295 report +=
"<th>" + QString::number(
level) +
"</th>";
1297 report +=
"</tr>\n</thead>\n<tbody>\n";
1299 QMap<QString, QString>
spells;
1307 spells[summon->clone.name] = reportSummon(summon, &summon->clone.other_arch->clone, QString(summon->clone.name));
1333 report +=
"</tbody>\n</table>\n";
1336 QApplication::restoreOverrideCursor();
1343 report +=
"<table border=\"1\">\n<thead>\n";
1345 report +=
"<th>Shop</th>";
1346 report +=
"<th>Greed</th>";
1347 report +=
"<th>Race</th>";
1348 report +=
"<th>Min</th>";
1349 report +=
"<th>Max</th>";
1353 items.removeAll(
item);
1355 report +=
"</tr>\n</thead><tbody>";
1362 if (
map->shopItems().size() == 0)
1367 line +=
"<td>" +
map->name() +
" " +
map->path() +
"</td>";
1368 line +=
"<td>" + QString::number(
map->shopGreed()) +
"</td>";
1369 line +=
"<td>" +
map->shopRace() +
"</td>";
1370 line +=
"<td>" + (
map->shopMin() != 0 ? QString::number(
map->shopMin()) :
"") +
"</td>";
1371 line +=
"<td>" + (
map->shopMax() != 0 ? QString::number(
map->shopMax()) :
"") +
"</td>";
1375 if (
map->shopItems()[
item] == 0)
1377 if (
map->shopItems()[
"*"] == 0)
1378 line +=
"<td></td>";
1380 line +=
"<td>" + QString::number(
map->shopItems()[
"*"]) +
"</td>";
1384 line +=
"<td>" + QString::number(
map->shopItems()[
item]) +
"</td>";
1392 report +=
"</tbody></table>";
1398 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1400 QString
report(tr(
"<h1>Shop information</h1>\n"));
1406 QStringList add =
map->shopItems().keys();
1407 foreach(
const QString
item, add)
1409 if (!items.contains(
item))
1417 part <<
"weapon" <<
"weapon improver" <<
"bow" <<
"arrow";
1421 part <<
"armour" <<
"armour improver" <<
"boots" <<
"bracers" <<
"cloak" <<
"girdle" <<
"gloves" <<
"helmet" <<
"shield";
1425 part <<
"amulet" <<
"potion" <<
"power_crystal" <<
"ring" <<
"rod" <<
"scroll" <<
"skillscroll" <<
"spellbook" <<
"wand";
1429 part <<
"container" <<
"food" <<
"key" <<
"lamp" <<
"skill tool" <<
"special key";
1432 if (!items.isEmpty())
1440 QApplication::restoreOverrideCursor();
1447 QStringList subdirs = dir.entryList(QStringList(
"*"), QDir::Dirs | QDir::NoDotAndDotDot);
1448 foreach(QString subdir, subdirs)
1453 QStringList
quests = dir.entryList(QStringList(
"*.quest"), QDir::Files);
1456 qDebug() <<
"read quest:" <<
path <<
file;
1458 QFile read(
path + QDir::separator() +
file);
1459 read.open(QFile::ReadOnly);
1460 QTextStream stream(&read);
1463 while (!(
line = stream.readLine(0)).isNull())
1465 if (
line.startsWith(
"quest "))
1473 if (
line ==
"end_quest")
1475 states[code][
name] = completed;
1479 if (
line.startsWith(
"state "))
1481 if (
line ==
"completed 1")
1489 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1491 QHash<QString, QHash<QString, bool> > states;
1493 directory += QDir::separator();
1499 codes.append(
quest->quest_code);
1502 QString
report(
"<html><body>\n<h1>Quests</h1>\n");
1504 QStringList
keys = states.keys();
1509 codes.removeAll(
key);
1511 report +=
"<h2>Quest: " + (
quest != NULL ?
quest->quest_title : (
key +
" ???")) +
"</h2>\n";
1513 QHash<QString, bool>
done = states[
key];
1533 report +=
"<p>" + tr(
"%1 completed out of %2 (%3%)").arg(completed).arg(
players.size()).arg(completed * 100 /
players.size()) +
"</p>\n";
1536 if (codes.length() > 0)
1540 report +=
"<h2>Quests never done</h2>\n<p>\n";
1541 foreach(QString code, codes)
1551 report +=
"</body>\n</html>\n";
1554 QApplication::restoreOverrideCursor();
1560 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1565 report +=
"<h1>Materials</h1>";
1566 report +=
"<table><tr><th>Name</th><th>Description</th></tr>";
1568 report += tr(
"<tr><td>%1</td><td>%2</td></tr>").arg(mat->name, mat->description);
1572 for (
int s = 0; s < 2; s++) {
1573 QString
name(s == 0 ?
"Saves" :
"Resistances");
1575 report += tr(
"<tr><th rowspan='2'>Name</th><th colspan='%1'>%2</th></tr>").arg(
NROFATTACKS).arg(
name);
1583 report += tr(
"<tr><td>%1</td>").arg(mat->name);
1585 int8_t val = (s == 0 ? mat->save[
r] : mat->mod[
r]);
1586 report += tr(
"<td>%1</td>").arg(val == 0 ? QString() : QString::number(val));
1596 QApplication::restoreOverrideCursor();
1602 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1607 report +=
"<h1>Apparently unused archetypes</h1>";
1608 report +=
"<h3>Warning: this list contains skills, items on style maps, and other things which are actually used.</h3>";
1610 report +=
"<tr><th>Image</th><th>Archetype name</th><th>Item name</th><th>Type</th></tr>";
1616 if (strstr(
arch->name,
"hpbar") !=
nullptr)
1630 QByteArray byteArray;
1631 QBuffer buffer(&byteArray);
1632 image.save(&buffer,
"PNG");
1633 QString iconBase64 = QString::fromLatin1(byteArray.toBase64().data());
1635 report += tr(
"<tr><td><img src='data:image/png;base64,%1'></td><td>%2</td><td>%3</td><td>%4</td></tr>")
1636 .arg(iconBase64,
arch->name,
arch->clone.name, td ? td->name : tr(
"unknown: %1").arg(
arch->clone.type));
1644 QApplication::restoreOverrideCursor();
1650 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1656 std::set<std::string>
faces, facesets, fields;
1658 for (
auto item : all)
1661 for (
auto fs :
item.second)
1663 facesets.insert(fs.first);
1664 for (
auto items : fs.second)
1666 fields.insert(items.first);
1673 facesets.erase(fs->
prefix);
1680 if (facesets.empty())
1682 report +=
"<h1>No invalid faceset</h1>\n";
1686 report +=
"<h1>Invalid faceset found</h1>\n";
1687 report +=
"<p>The faceset of the license file doesn't match any defined facesets.</p>";
1689 for (
auto fs : facesets)
1691 report +=
"<li>" + QString(fs.c_str()) +
"</li>\n";
1698 report +=
"<h1>No invalid face name</h1>\n";
1702 report +=
"<h1>Invalid face names found</h1>\n";
1703 report +=
"<p>The face name from the license file doesn't match any defined face.</p>";
1705 for (
auto f :
faces)
1707 report +=
"<li>" + QString(f.c_str()) +
"</li>\n";
1712 report +=
"<h1>All fields used in license descriptions</h1>\n";
1714 for (
auto f : fields)
1716 report +=
"<li>" + QString(f.c_str()) +
"</li>\n";
1722 QApplication::restoreOverrideCursor();
1728 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1734 auto groupCmp = [] (
const std::string &left,
const std::string &right) {
return left < right; };
1737 int name = left->
name().compare(right->name());
1742 return left->
path().compare(right->path()) < 0;
1744 std::map<std::string, std::vector<CREMapInformation *>, decltype(groupCmp)> groups(groupCmp);
1748 if (!
map->resetGroup().isEmpty())
1750 groups[
map->resetGroup().toStdString()].push_back(
map);
1756 report +=
"<h1>No reset group defined</h1>\n";
1760 for (
auto group : groups)
1762 report +=
"<h1>" + QString(group.first.c_str()) +
" (" + QString::number(group.second.size()) +
" maps)</h1>\n";
1764 std::sort(group.second.begin(), group.second.end(), mapCmp);
1765 for (
auto map : group.second)
1767 report += tr(
"<li>%1 (%2)</li>").arg(
map->name(),
map->path());
1775 QApplication::restoreOverrideCursor();
1812 confirm.setWindowTitle(tr(
"Crossfire Resource Editor"));
1813 confirm.setText(tr(
"Really clear map cache?"));
1814 confirm.setInformativeText(tr(
"This will force cache rebuild at next application start."));
1815 confirm.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
1816 confirm.setDefaultButton(QMessageBox::No);
1817 confirm.setIcon(QMessageBox::Question);
1818 if (
confirm.exec() == QMessageBox::Yes)
1836 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
1840 QApplication::restoreOverrideCursor();
1841 QMessageBox::information(
this,
"Reload complete",
"Assets reload complete, you may need to change the selected item to see updated versions.");
1847 QString dir = QFileDialog::getExistingDirectory(
this, tr(
"Please select the 'sounds' directory"),
settings.soundsDirectory());
1856 auto windows =
myArea->subWindowList();
1857 bool hasWindows = !windows.empty();
1859 while (
myWindows->actions().size() > 6) {
1863 if (
a->isSeparator()) {
1864 a->setVisible(hasWindows);
1866 a->setEnabled(hasWindows);
1870 for (
int i = 0; i < windows.size(); ++i) {
1871 QMdiSubWindow *mdiSubWindow = windows.at(i);
1873 QString
title(mdiSubWindow->widget()->windowTitle());
1880 myArea->setActiveSubWindow(mdiSubWindow);
1882 action->setCheckable(
true);
1883 action ->setChecked(mdiSubWindow ==
myArea->activeSubWindow());
1890 for (
auto rm :
map->randomMaps()) {