1 ///////////////////////////////////////////////////////////////////////////////
2 // Copyright (C) 2004-2010 by The Allacrost Project
3 // All Rights Reserved
4 //
5 // This code is licensed under the GNU GPL version 2. It is free software
6 // and you may modify it and/or redistribute it under the terms of this license.
7 // See http://www.gnu.org/copyleft/gpl.html for details.
8 ///////////////////////////////////////////////////////////////////////////////
9
10 /*!****************************************************************************
11 * \file tileset.cpp
12 * \author Philip Vorsilak, gorzuate@allacrost.org
13 * \brief Source file for editor's tileset, used for maintaining a visible
14 * "list" of tiles to select from for painting on the map.
15 *****************************************************************************/
16
17 #include "tileset.h"
18
19 using namespace std;
20 using namespace hoa_video;
21 using namespace hoa_script;
22
23
24 namespace hoa_editor
25 {
26
27 ////////////////////////////////////////////////////////////////////////////////
28 ////////// Tileset class
29 ////////////////////////////////////////////////////////////////////////////////
30
Tileset()31 Tileset::Tileset() :
32 tileset_name(""),
33 _initialized(false)
34 {}
35
36
37
~Tileset()38 Tileset::~Tileset()
39 {
40 for (std::vector<hoa_video::StillImage>::iterator it = tiles.begin(); it != tiles.end(); it++)
41 (*it).Clear();
42 tiles.clear();
43 }
44
45
46
CreateImageFilename(const QString & tileset_name)47 QString Tileset::CreateImageFilename(const QString& tileset_name)
48 {
49 return QString("img/tilesets/" + tileset_name + ".png");
50 }
51
52
53
CreateDataFilename(const QString & tileset_name)54 QString Tileset::CreateDataFilename(const QString& tileset_name)
55 {
56 return QString("dat/tilesets/" + tileset_name + ".lua");
57 }
58
59
60
CreateTilesetName(const QString & filename)61 QString Tileset::CreateTilesetName(const QString& filename)
62 {
63 QString tname = filename;
64 // Remove everything up to and including the final '/' character
65 tname.remove(0, tname.lastIndexOf("/") + 1);
66 // Chop off the appendend four characters the (filename extension)
67 tname.chop(4);
68 return tname;
69 }
70
71
72
New(const QString & img_filename,bool one_image)73 bool Tileset::New(const QString& img_filename, bool one_image)
74 {
75 _initialized = false;
76
77 // Retreive the tileset name from the image filename
78 tileset_name = CreateTilesetName(img_filename);
79
80 // Prepare the tile vector and load the tileset image
81 if (one_image == true) {
82 tiles.clear();
83 tiles.resize(1);
84 tiles[0].SetDimensions(16.0f, 16.0f);
85 if (tiles[0].Load(string(img_filename.toAscii()), 16, 16) == false) {
86 qDebug("Failed to load tileset image: " + img_filename);
87 return false;
88 }
89 }
90 else {
91 tiles.clear();
92 tiles.resize(256);
93 for (uint32 i = 0; i < 256; i++)
94 tiles[i].SetDimensions(1.0f, 1.0f);
95 if (ImageDescriptor::LoadMultiImageFromElementGrid(tiles, string(img_filename.toAscii()), 16, 16) == false) {
96 qDebug("Failed to load tileset image: " + img_filename);
97 return false;
98 }
99 }
100
101 // Initialize the rest of the tileset data
102 vector<int32> blank_entry(4, 0);
103 for (uint32 i = 0; i < 16; i++) {
104 for (uint32 j = 0; j < 16; j++) {
105 walkability.insert(make_pair(i * 16 + j, blank_entry));
106 }
107 }
108
109 autotileability.clear();
110 // _animated_tiles.clear();
111
112 _initialized = true;
113 return true;
114 }
115
116
117
Load(const QString & set_name,bool one_image)118 bool Tileset::Load(const QString& set_name, bool one_image)
119 {
120 _initialized = false;
121 tileset_name = set_name;
122
123 // Create filenames from the tileset name
124 QString img_filename = CreateImageFilename(set_name);
125 QString dat_filename = CreateDataFilename(set_name);
126
127 // Prepare the tile vector and load the tileset image
128 if (one_image == true) {
129 tiles.clear();
130 tiles.resize(1);
131 tiles[0].SetDimensions(16.0f, 16.0f);
132 if (tiles[0].Load(string(img_filename.toAscii()), 16, 16) == false)
133 return false;
134 }
135 else {
136 tiles.clear();
137 tiles.resize(256);
138 for (uint32 i = 0; i < 256; i++)
139 tiles[i].SetDimensions(1.0f, 1.0f);
140 if (ImageDescriptor::LoadMultiImageFromElementGrid(tiles,
141 string(img_filename.toAscii()), 16, 16) == false)
142 return false;
143 }
144
145 // Set up for reading the tileset definition file.
146 ReadScriptDescriptor read_data;
147 if (read_data.OpenFile(string(dat_filename.toAscii()), true) == false) {
148 _initialized = false;
149 return false;
150 }
151
152 read_data.OpenTable(string(tileset_name.toAscii()));
153
154 // Read in walkability information.
155 if (read_data.DoesTableExist("walkability") == true)
156 {
157 vector<int32> vect; // used to read in vectors from the data file
158 read_data.OpenTable("walkability");
159 for (int32 i = 0; i < 16; i++)
160 {
161 read_data.OpenTable(i);
162 // Make sure that at least one row exists
163 if (read_data.IsErrorDetected() == true)
164 {
165 read_data.CloseTable();
166 read_data.CloseTable();
167 read_data.CloseFile();
168 _initialized = false;
169 return false;
170 }
171
172 for (int32 j = 0; j < 16; j++)
173 {
174 read_data.ReadIntVector(j, vect);
175 if (read_data.IsErrorDetected() == false)
176 walkability[i * 16 + j] = vect;
177 vect.clear();
178 } // iterate through all tiles in a row
179 read_data.CloseTable();
180 } // iterate through all rows of the walkability table
181 read_data.CloseTable();
182 } // make sure table exists first
183
184 // Read in autotiling information.
185 if (read_data.DoesTableExist("autotiling") == true)
186 {
187 uint32 table_size = read_data.GetTableSize("autotiling");
188 read_data.OpenTable("autotiling");
189 vector<int32> keys; // will contain the keys (indeces, if you will) of this table's entries
190 read_data.ReadTableKeys(keys);
191 for (uint32 i = 0; i < table_size; i++)
192 autotileability[keys[i]] = read_data.ReadString(keys[i]);
193 read_data.CloseTable();
194 } // make sure table exists first
195
196 // TODO: editor does not yet have animated tile support
197 /*
198 // Read in animated tiles.
199 uint32 animated_table_size = read_data.GetTableSize("animated_tiles");
200 read_data.OpenTable("animated_tiles");
201 for (uint32 i = 1; i <= animated_table_size; i++)
202 {
203 _animated_tiles.push_back(vector<AnimatedTileData>());
204 vector<AnimatedTileData>& tiles = _animated_tiles.back();
205 // Calculate loop end: an animated tile is comprised of a tile id and a time, so the loop end
206 // is really half the table size.
207 uint32 tile_count = read_data.GetTableSize(i) / 2;
208 read_data.OpenTable(i);
209 for(uint32 index = 1; index <= tile_count; index++)
210 {
211 tiles.push_back(AnimatedTileData());
212 AnimatedTileData& tile_data = tiles.back();
213 tile_data.tile_id = read_data.ReadUInt(index * 2 - 1);
214 tile_data.time = read_data.ReadUInt(index * 2);
215 }
216 read_data.CloseTable();
217 }
218 read_data.CloseTable();
219 */
220
221 read_data.CloseTable();
222 read_data.CloseFile();
223
224 _initialized = true;
225 return true;
226 } // bool Tileset::Load(const QString& name)
227
228
229
Save()230 bool Tileset::Save() {
231 string dat_filename = string(CreateDataFilename(tileset_name).toAscii());
232 string img_filename = string(CreateImageFilename(tileset_name).toAscii());
233 WriteScriptDescriptor write_data;
234
235 if (write_data.OpenFile(dat_filename) == false) {
236 return false;
237 }
238
239 // Write the localization namespace for the tileset file
240 write_data.WriteNamespace(tileset_name.toStdString());
241 write_data.InsertNewLine();
242
243 // Write basic tileset properties
244 write_data.WriteString("file_name", dat_filename);
245 write_data.WriteString("image", img_filename);
246 write_data.WriteInt("num_tile_cols", 16);
247 write_data.WriteInt("num_tile_rows", 16);
248 write_data.InsertNewLine();
249
250 // Write walkability data
251 write_data.BeginTable("walkability");
252 for (uint32 row = 0; row < 16; row++) {
253 write_data.BeginTable(row);
254 for (uint32 col = 0; col < 16; col++) {
255 write_data.WriteIntVector(col, walkability[row * 16 + col]);
256 }
257 write_data.EndTable();
258 }
259 write_data.EndTable();
260
261 // Write animated tile data
262 // TODO: animated tiles not supported in editor yet
263 // write_data.BeginTable("animated_tiles");
264 // for (uint32 i = 0; i < _animated_tiles.size(); i++) {
265 // vector<int32> data;
266 // for (uint32 c = 0; c <_animated_tiles[i].size(); c++) {
267 // data.push_back(_animated_tiles[i][c].tile_id);
268 // data.push_back(_animated_tiles[i][c].time);
269 // }
270 // write_data.WriteIntVector(i + 1,data);
271 // }
272 // write_data.EndTable();
273
274 // Write autotiling data
275 // TODO: autotiling not supported in editor yet
276 // write_data.BeginTable("autotiling");
277 // for (map<int, string>::iterator i = autotileability.begin(); i != autotileability.end(); i++) {
278 // write_data.WriteString((*i).first, (*i).second);
279 // }
280 // write_data.EndTable();
281
282 if (write_data.IsErrorDetected() == true) {
283 cerr << "Errors were detected when saving tileset file. The errors include: " << endl;
284 cerr << write_data.GetErrorMessages() << endl;
285 write_data.CloseFile();
286 return false;
287 }
288 else {
289 write_data.CloseFile();
290 return true;
291 }
292 } // bool Tileset::Save()
293
294 ////////////////////////////////////////////////////////////////////////////////
295 ////////// TilesetTable class
296 ////////////////////////////////////////////////////////////////////////////////
297
TilesetTable()298 TilesetTable::TilesetTable() :
299 Tileset()
300 {
301 // Set up the QT table
302 table = new Q3Table(16, 16);
303 table->setReadOnly(true);
304 table->setShowGrid(false);
305 table->setSelectionMode(Q3Table::Multi);
306 table->setTopMargin(0);
307 table->setLeftMargin(0);
308 for (int32 i = 0; i < table->numRows(); i++)
309 table->setRowHeight(i, TILE_HEIGHT);
310 for (int32 i = 0; i < table->numCols(); i++)
311 table->setColumnWidth(i, TILE_WIDTH);
312 }
313
314
315
~TilesetTable()316 TilesetTable::~TilesetTable()
317 {
318 delete table;
319 }
320
321
322 // TilesetTable::New(const QString& img_filename)
323 // {
324 // }
325
326
Load(const QString & set_name)327 bool TilesetTable::Load(const QString& set_name)
328 {
329 if (Tileset::Load(set_name) == false)
330 return false;
331
332 // Read in tiles and create table items.
333 // FIXME: this is one ugly hack. It loads each individual tile's image and puts it into a table. But each
334 // tile's image only exists together with a bunch of other tiles in a tileset image. So we have to split them
335 // up. Qt has no built-in function to split a big image into little ones. This image information has already
336 // been loaded by the above call to LoadMultiImageFromElementGrid(...). If we could somehow take that info
337 // and put it into a Qt table, that would be ideal.
338 // This piece of code is what takes so long for the editor to load a tileset.
339 // <<FIXED>>: The ugly hack has been fixed, I use the QImage to handle directly to the bits, it's much faster.
340 // Contact me if there's any problem with this fix, eguitarz (dalema22@gmail.com)
341 QRect rectangle;
342 QImage entire_tileset;
343 entire_tileset.load(CreateImageFilename(set_name), "png");
344 for (uint32 row = 0; row < 16; row++)
345 {
346 for (uint32 col = 0; col < 16; col++)
347 {
348 rectangle.setRect(col * TILE_WIDTH, row * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
349 QVariant variant = entire_tileset.copy(rectangle);
350 if (!variant.isNull())
351 {
352 QPixmap tile_pixmap = variant.value<QPixmap>();
353 table->setPixmap(row, col, tile_pixmap);
354 }
355 else
356 qDebug(QString("%1").arg("Image loading error!!"));
357 } // iterate through the columns of the tileset
358 } // iterate through the rows of the tileset
359
360 return true;
361 }
362
363 } // namespace hoa_editor
364