1/*******************************************************************
2 *
3 * Copyright 2006-2009 Dmitry Suzdalev <dimsuz@gmail.com>
4 *
5 * This file is part of the KDE project "KAtomic"
6 *
7 * KAtomic is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
10 * any later version.
11 *
12 * KAtomic is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with KAtomic; see the file COPYING. If not, write to
19 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 *
22 ********************************************************************/
23#include "levelset.h"
24
25#include <KStandardDirs>
26#include <KConfigGroup>
27#include <KDebug>
28#include <KLocale>
29#include <kdefakes.h>
30#include <QFileInfo>
31
32#include "atom.h"
33#include "molecule.h"
34#include "commondefs.h"
35
36LevelData::LevelData(const QList<Element>& elements, const Molecule* mol)
37 : m_molecule(mol)
38{
39 memset(m_field, 0, sizeof(m_field));
40 foreach (const Element& el, elements)
41 {
42 if (el.atom == -1)
43 {
44 m_field[el.x][el.y] = true; // a wall
45 }
46 else
47 {
48 m_atoms.append(el);
49 }
50 }
51}
52
53LevelData::~LevelData()
54{
55 delete m_molecule;
56}
57
58QList<LevelData::Element> LevelData::atomElements() const
59{
60 return m_atoms;
61}
62
63bool LevelData::containsWallAt(int x, int y) const
64{
65 return m_field[x][y];
66}
67
68const Molecule* LevelData::molecule() const
69{
70 return m_molecule;
71}
72
73// ==================================================
74
75LevelSet::LevelSet()
76{
77 reset();
78}
79
80LevelSet::~LevelSet()
81{
82 qDeleteAll(m_levelCache);
83}
84
85void LevelSet::reset()
86{
87 m_name = QString();
88 m_visibleName = QString();
89 m_description = QString();
90 m_author = QString();
91 m_authorEmail = QString();
92 m_levelCount = 0;
93
94 qDeleteAll(m_levelCache);
95 m_levelCache.clear();
96}
97
98bool LevelSet::load(const QString& levelSetName)
99{
100 QString file = KStandardDirs::locate("appdata", QString("levels/%1.dat").arg(levelSetName));
101 if (file.isEmpty())
102 {
103 kDebug() << "level set \"" << levelSetName << "\" data file not found. Check your installation";
104 return false;
105 }
106
107 bool res = loadFromFile(file);
108 if (!res)
109 kDebug() << "warning: failed to load level set" << levelSetName;
110
111 return res;
112}
113
114bool LevelSet::loadFromFile(const QString& fileName)
115{
116 reset();
117
118 m_levelsFile = KSharedConfig::openConfig(fileName, KConfig::SimpleConfig);
119 KConfigGroup gr = m_levelsFile->group("LevelSet");
120
121 m_visibleName = gr.readEntry("Name");
122 m_description = gr.readEntry("Description");
123 m_author = gr.readEntry("Author");
124 m_authorEmail = gr.readEntry("AuthorEmail");
125 m_levelCount = gr.readEntry("LevelCount", 0);
126
127 m_name = QFileInfo(fileName).baseName();
128
129 if (m_levelCount <= 0)
130 kDebug() << "warning: in level set" << m_name << "level count not specified or invalid";
131
132 return true;
133}
134
135QString LevelSet::name() const
136{
137 return m_name;
138}
139
140QString LevelSet::visibleName() const
141{
142 return m_visibleName;
143}
144
145QString LevelSet::author() const
146{
147 return m_author;
148}
149
150QString LevelSet::authorEmail() const
151{
152 return m_authorEmail;
153}
154
155QString LevelSet::description() const
156{
157 return m_description;
158}
159
160int LevelSet::levelCount() const
161{
162 return m_levelCount;
163}
164
165const LevelData* LevelSet::levelData(int levelNum) const
166{
167 LevelData* data = m_levelCache.value(levelNum, 0);
168 return data ? data : readLevel(levelNum);
169}
170
171const LevelData* LevelSet::readLevel(int levelNum) const
172{
173 KConfigGroup config = m_levelsFile->group("Level"+QString::number(levelNum));
174 QString key;
175
176 QList<LevelData::Element> elements;
177
178 for (int j = 0; j < FIELD_SIZE; j++)
179 {
180 key.sprintf("feld_%02d", j);
181 QString line = config.readEntry(key,QString());
182
183 for (int i = 0; i < FIELD_SIZE; i++)
184 {
185 if (line.isEmpty())
186 {
187 kDebug() << "error while reading level" << levelNum << "data from" << m_name;
188 return 0;
189 }
190
191 QChar c = line.at(i);
192 if( c == '#' )
193 {
194 LevelData::Element el;
195 el.x = i;
196 el.y = j;
197 el.atom = -1; // indicates wall
198
199 elements.append(el);
200 }
201 else if (c != '.')//atom
202 {
203 LevelData::Element el;
204 el.x = i;
205 el.y = j;
206 el.atom = atom2int(c.toLatin1());
207
208 elements.append(el);
209 }
210 }
211 }
212
213 // Molecule object will be deleted by LevelData, it takes ownership
214 LevelData* level = new LevelData(elements, readLevelMolecule(levelNum));
215 m_levelCache[levelNum] = level;
216
217 return level;
218}
219
220const Molecule* LevelSet::readLevelMolecule(int levelNum) const
221{
222 Molecule* mol = new Molecule();
223 KConfigGroup config = m_levelsFile->group("Level"+QString::number(levelNum));
224
225 QString key;
226
227 atom current;
228
229 int atom_index = 1;
230 QString value;
231 while (true) {
232 key.sprintf("atom_%c", int2atom(atom_index));
233 value = config.readEntry(key,QString());
234 if (value.isEmpty())
235 break;
236
237 current.obj = value.at(0).toLatin1();
238 value = value.mid(2);
239
240 strlcpy(current.conn, value.toAscii(), sizeof(current.conn));
241 kWarning( mol->m_atoms.indexOf(current) != -1 )
242 << "OOOPS, duplicate atom definition in" << key;
243 mol->m_atoms.append(current);
244 atom_index++;
245 }
246
247 QString line;
248
249 mol->m_width = 0;
250 mol->m_height = 0;
251 mol->m_weight = 0.0;
252
253 int max_i = -1;
254 for (int j = 0; j < MOLECULE_SIZE; j++) {
255
256 key.sprintf("mole_%d", j);
257 line = config.readEntry(key,QString());
258
259 int max_non_null_i = -1;
260 for (int i = 0; i < MOLECULE_SIZE; i++)
261 {
262 if (i >= line.size())
263 mol->m_molek[i][j] = 0;
264 else
265 {
266 mol->m_molek[i][j] = atom2int(line.at(i).toLatin1());
267 mol->m_weight += mol->getAtom(mol->m_molek[i][j]).weight();
268 max_non_null_i = i;
269 }
270 }
271 if( max_non_null_i != -1 )
272 mol->m_height++;
273 max_i = qMax( max_i, max_non_null_i );
274 }
275
276 mol->m_width = max_i+1;
277
278 mol->m_name = i18n(config.readEntry("Name", I18N_NOOP("Noname")).toUtf8());
279
280 return mol;
281}
282
283bool LevelSet::isDefaultLevelsAvailable()
284{
285 QString file = KStandardDirs::locate("appdata", QString("levels/%1.dat").arg(DEFAULT_LEVELSET_NAME));
286 if (file.isEmpty())
287 {
288 kDebug() << "default level set \"" << DEFAULT_LEVELSET_NAME << "\" data file not found. Check your installation";
289 return false;
290 }
291
292 return true;
293}
294