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