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
line
Install Bug reporting Credits so make sure you have version or later There are files involved in the automatic convert convertall and filelist py GuildList has the list of guilds for the server GuildLocations is what is used by the install script for setting up the maps It has columns in the first is the name of the no spaces The second is the region of the the third is the destination folder for the the fourth is the exit the fifth and sixth are the x and y coords within the exit the seventh eighth and ninth are the exit location for the storage hall If field seven is then it uses the same exit map as for the guild hall itself filelist py has a list of which files to process for each guild hall convert py takes all the files in filelist py and customises them to the specific guild then outputs them into a in the same order that they are listed in GuildLocations convertall py reads the lines from GuildLocations and runs line by line
Definition: README.txt:12
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
AssetsCollection::get
T * get(const Key &name)
Definition: AssetsCollection.h:89
FaceMakerDialog::FaceMakerDialog
FaceMakerDialog(QWidget *parent, ResourcesManager *manager)
Definition: FaceMakerDialog.cpp:26
source
Almost all the spell_ *base png files are taken from JXClient s source
Definition: readme-icons.txt:1
smoking_pipe.color
color
Definition: smoking_pipe.py:5
AssetsManager.h
FaceMakerDialog.h
getManager
AssetsManager * getManager()
Definition: assets.cpp:305
name
Plugin animator file specs[Config] name
Definition: animfiles.txt:4
FaceMakerDialog::mySettings
QTextEdit * mySettings
Definition: FaceMakerDialog.h:37
FaceMakerDialog::makeFaces
void makeFaces()
Definition: FaceMakerDialog.cpp:82
ResourcesManager
Definition: ResourcesManager.h:80
face_info::data
uint8_t * data
Definition: image.h:11
say.box
box
Definition: say.py:152
animate.anim
string anim
Definition: animate.py:20
d
How to Install a Crossfire Server on you must install a python script engine on your computer Python is the default script engine of Crossfire You can find the python engine you have only to install them The VisualC Crossfire settings are for d
Definition: INSTALL_WIN32.txt:13
CREPixmap::faceset
static face_sets * faceset
Definition: CREPixmap.h:25
ResourcesManager.h
CREPixmap.h
FaceMakerDialog::parse
QColor parse(const QString &color)
Definition: FaceMakerDialog.cpp:72
item
Definition: item.py:1
AssetsManager::faces
Faces * faces()
Definition: AssetsManager.h:39
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
Settings::datadir
const char * datadir
Definition: global.h:248
rndm
int rndm(int min, int max)
Definition: utils.cpp:162
split
static std::vector< std::string > split(const std::string &field, const std::string &by)
Definition: mapper.cpp:2608
say.item
dictionary item
Definition: say.py:149
Face::number
uint16_t number
Definition: face.h:15
connect
Definition: connect.py:1
face_sets::faces
face_info * faces
Definition: image.h:26
manager
static AssetsManager * manager
Definition: assets.cpp:60