Crossfire Server, Trunk
FaceMakerDialog.cpp
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 2022 the Crossfire Development Team
5  *
6  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
7  * welcome to redistribute it under certain conditions. For details, please
8  * see COPYING and LICENSE.
9  *
10  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
11  */
12 
13 #include "FaceMakerDialog.h"
14 #include <QtWidgets>
15 #include <QtCore/qiodevice.h>
16 #include <QtCore/qfile.h>
17 #include <QMap>
18 
19 #include "ResourcesManager.h"
20 #include "CREPixmap.h"
21 
22 #include "global.h"
23 #include "assets.h"
24 #include "AssetsManager.h"
25 
26 FaceMakerDialog::FaceMakerDialog(QWidget* parent, ResourcesManager* manager) : QDialog(parent), myManager(manager)
27 {
28  QGridLayout* layout = new QGridLayout(this);
29  int line = 0;
30 
31  layout->addWidget(new QLabel(tr("Settings:"), this), line++, 0);
32 
33  mySettings = new QTextEdit(this);
34  layout->addWidget(mySettings, line++, 0);
35  mySettings->setAcceptRichText(false);
36 
37  QDialogButtonBox* box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Close, Qt::Horizontal, this);
38  layout->addWidget(box, line++, 0);
39  connect(box, SIGNAL(rejected()), this, SLOT(reject()));
40  connect(box, SIGNAL(accepted()), this, SLOT(makeFaces()));
41  box->button(QDialogButtonBox::Ok)->setText("Generate");
42 
43  resize(500, 600);
44 
45  setWindowTitle(tr("Face variant maker"));
46 
47  mySettings->setPlainText(QString(R"RaW(dest: %1/arch/wall/light_blue_cave/
48 name: light_blue_cave
49 faces: cave_15.111, cave1.111, cave2.111, cave3.111, cave4.111, cave5.111
50 faces: cave6.111, cave7.111, cave8.111, cave9.111, cave10.111, cave11.111
51 faces: cave12.111, cave13.111, cave14.111, cave15.111, cave16.111, cave17.111
52 faces: cave18.111, cave19.111, cave20.111, cave21.111, cave22.111, cave23.111
53 faces: cave24.111, cave25.111
54 variants: 2
55 68, 68, 68: 4, 5, 65
56 119, 119, 119:14, 229, 197; 67, 151, 229
57 arch
58 name wall
59 anim_speed 4
60 client_anim_random 1
61 blocksview 1
62 move_block all
63 no_pick 1
64 archend
65 face
66 magicmap blue
67 visibility 100
68 faceend
69 )RaW").arg(settings.datadir));
70 }
71 
72 QColor FaceMakerDialog::parse(const QString& color)
73 {
74  auto split = color.trimmed().split(',');
75  if (split.size() != 3) {
76  QMessageBox::critical(this, tr("Error"), tr("Invalid color %1").arg(color));
77  return QColor();
78  }
79  return QColor::fromRgb(split[0].trimmed().toInt(), split[1].trimmed().toInt(), split[2].trimmed().toInt());
80 }
81 
83 {
84  QMap<QRgb, QList<QColor> > colors;
85  QString dest, name, archContent, faceContent;
86  QStringList faces;
87  int variants = 1;
88 
89  bool inArch = false, inFace = false;
90  auto lines = mySettings->toPlainText().split('\n');
91 
92  foreach (QString line, lines) {
93  if (inArch) {
94  if (line == "archend") {
95  inArch = false;
96  } else {
97  archContent += line + "\n";
98  }
99  continue;
100  }
101  if (inFace) {
102  if (line == "faceend") {
103  inFace = false;
104  } else {
105  faceContent += line + "\n";
106  }
107  continue;
108  }
109 
110  if (line.isEmpty()) {
111  continue;
112  }
113 
114  if (line.startsWith("dest:")) {
115  dest = line.mid(5).trimmed();
116  } else if (line.startsWith("name:")) {
117  name = line.mid(5).trimmed();
118  } else if (line.startsWith("faces:")) {
119  QStringList add(line.mid(6).split(','));
120  foreach (QString face, add) {
121  faces.append(face.trimmed());
122  }
123  } else if (line.startsWith("variants:")) {
124  variants = line.mid(9).trimmed().toInt();
125  } else if (line == "arch") {
126  inArch = true;
127  } else if (line == "face") {
128  inFace = true;
129  } else {
130  auto split = line.split(":");
131  if (split.size() != 2) {
132  QMessageBox::critical(this, tr("Error"), tr("Invalid line %1").arg(line));
133  return;
134  }
135  QColor source = parse(split[0]);
136  auto dest = split[1].split(';');
137  foreach (QString d, dest) {
138  colors[source.rgba()].append(parse(d));
139  }
140  }
141  }
142 
143  if (dest.isEmpty())
144  {
145  QMessageBox::critical(this, tr("Error"), tr("Missing 'dest'!"));
146  return;
147  }
148  if (name.isEmpty())
149  {
150  QMessageBox::critical(this, tr("Error"), tr("Missing 'name'!"));
151  return;
152  }
153  if (faces.isEmpty())
154  {
155  QMessageBox::critical(this, tr("Error"), tr("No face to process!"));
156  return;
157  }
158 
159  if (variants <= 0)
160  {
161  QMessageBox::critical(this, tr("Error"), tr("At least one variant required!"));
162  return;
163  }
164 
165  QFile arc(dest + QDir::separator() + name + ".arc");
166  if (!archContent.isEmpty() && !arc.open(QFile::Truncate | QFile::WriteOnly))
167  {
168  QMessageBox::critical(this, tr("Error"), tr("Error while opening the archetype file %1!").arg(arc.fileName()));
169  return;
170  }
171  QFile fface(dest + QDir::separator() + name + ".face");
172  if ((!faceContent.isEmpty() || variants > 1) && !fface.open(QFile::Truncate | QFile::WriteOnly))
173  {
174  QMessageBox::critical(this, tr("Error"), tr("Error while opening the face file %1!").arg(fface.fileName()));
175  return;
176  }
177 
178  int i = 0;
179  foreach(QString face, faces)
180  {
181  QString archName(name + "_" + QString::number(i));
182  if (!archContent.isEmpty()) {
183  QString item(tr("Object %1\nface %1.111\n").arg(archName));
184  if (variants > 1) {
185  item += tr("animation %1\n").arg(archName);
186  }
187  item += archContent;
188  item += "end\n";
189  arc.write(item.toLocal8Bit());
190  }
191 
192  QString anim(tr("animation %1\n").arg(archName));
193 
194  int faceNumber = getManager()->faces()->get(face.toLocal8Bit().data())->number;
195 
196  for (int var = 1; var <= variants; var++) {
197  QImage image;
198 
199  if (faceNumber >= 0 && CREPixmap::faceset->allocated >= static_cast<size_t>(faceNumber) && CREPixmap::faceset->faces[faceNumber].datalen > 0)
200  {
201  QPixmap face;
202  if (face.loadFromData((uchar*)CREPixmap::faceset->faces[faceNumber].data, CREPixmap::faceset->faces[faceNumber].datalen))
203  {
204  image = face.toImage();
205  }
206  }
207 
208  for (int x = 0; x < image.width(); x++) {
209  for (int y = 0; y < image.height(); y++) {
210  auto rgba = image.pixel(x, y);
211  auto subs = colors.find(rgba);
212  if (subs != colors.end()) {
213  image.setPixelColor(x, y, QColor(subs.value()[rndm(0, subs.value().length() - 1)]));
214  }
215  }
216  }
217 
218  QString base = dest + QDir::separator() + name + "_" + QString::number(i) + ".base.11" + QString::number(var) + ".png";
219  image.save(base, "PNG");
220 
221  if (!faceContent.isEmpty()) {
222  QString fc(tr("face %1\n%2end\n").arg(archName + ".11" + QString::number(var), faceContent));
223  fface.write(fc.toLocal8Bit());
224  }
225  anim += tr("%1.11%2\n").arg(archName).arg(var);
226  }
227  anim += "mina\n";
228  if (variants > 1) {
229  fface.write(anim.toLocal8Bit());
230  }
231  i++;
232  }
233 
234  QMessageBox::information(this, tr("Completed"), tr("Generation completed"));
235 }
global.h
settings
struct Settings settings
Definition: init.cpp:139
uchar
#define uchar
Definition: re-cmp.h:42
layout
Definition: main.cpp:84
diamondslots.x
x
Definition: diamondslots.py:15
ResourcesManager
Definition: ResourcesManager.h:80
AssetsManager.h
Settings::datadir
const char * datadir
Definition: global.h:252
FaceMakerDialog.h
FaceMakerDialog::makeFaces
void makeFaces()
Definition: FaceMakerDialog.cpp:82
rndm
int rndm(int min, int max)
Definition: utils.cpp:162
FaceMakerDialog::parse
QColor parse(const QString &color)
Definition: FaceMakerDialog.cpp:72
smoking_pipe.color
color
Definition: smoking_pipe.py:5
getManager
AssetsManager * getManager()
Definition: assets.cpp:305
FaceMakerDialog::FaceMakerDialog
FaceMakerDialog(QWidget *parent, ResourcesManager *manager)
Definition: FaceMakerDialog.cpp:26
is_valid_types_gen.line
line
Definition: is_valid_types_gen.py:34
CREPixmap::faceset
static face_sets * faceset
Definition: CREPixmap.h:25
Face::number
uint16_t number
Definition: face.h:15
AssetsManager::faces
Faces * faces()
Definition: AssetsManager.h:39
face_info::data
uint8_t * data
Definition: image.h:11
say.box
box
Definition: say.py:152
AssetsCollection::get
T * get(const Key &name)
Definition: AssetsCollection.h:89
animate.anim
string anim
Definition: animate.py:20
ResourcesManager.h
item
Definition: item.py:1
convert.dest
dest
Definition: convert.py:25
diamondslots.y
y
Definition: diamondslots.py:16
assets.h
face_info::datalen
uint16_t datalen
Definition: image.h:12
dragon_attune.faces
dictionary faces
Definition: dragon_attune.py:31
FaceMakerDialog::mySettings
QTextEdit * mySettings
Definition: FaceMakerDialog.h:37
split
static std::vector< std::string > split(const std::string &field, const std::string &by)
Definition: mapper.cpp:2603
say.item
dictionary item
Definition: say.py:149
connect
Definition: connect.py:1
CREPixmap.h
face_sets::faces
face_info * faces
Definition: image.h:26
manager
static AssetsManager * manager
Definition: assets.cpp:60
give.name
name
Definition: give.py:27