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