1 ///////////////////////////////////////////////////////////////////////////////
2 // Copyright (C) 2004-2011 by The Allacrost Project
3 // Copyright (C) 2012-2016 by Bertram (Valyria Tear)
4 // All Rights Reserved
5 //
6 // This code is licensed under the GNU GPL version 2. It is free software
7 // and you may modify it and/or redistribute it under the terms of this license.
8 // See https://www.gnu.org/copyleft/gpl.html for details.
9 ///////////////////////////////////////////////////////////////////////////////
10
11 /** ****************************************************************************
12 *** \file map_tiles.cpp
13 *** \author Tyler Olsen, roots@allacrost.org
14 *** \author Yohann Ferreira, yohann ferreira orange fr
15 *** \brief Source file for map mode tile management.
16 *** ***************************************************************************/
17
18 #include "modes/map/map_tiles.h"
19
20 #include "modes/map/map_mode.h"
21
22 #include "engine/video/video.h"
23
24 using namespace vt_utils;
25 using namespace vt_script;
26 using namespace vt_video;
27
28 namespace vt_map
29 {
30
31 namespace private_map
32 {
33
34 //! \brief A helper function to convert a string to a layer type.
StringToLayerType(const std::string & type)35 static LAYER_TYPE StringToLayerType(const std::string& type)
36 {
37 if(type == "ground")
38 return GROUND_LAYER;
39 else if(type == "sky")
40 return SKY_LAYER;
41 return INVALID_LAYER;
42 }
43
TileSupervisor()44 TileSupervisor::TileSupervisor() :
45 _num_tile_on_x_axis(0),
46 _num_tile_on_y_axis(0)
47 {
48 }
49
~TileSupervisor()50 TileSupervisor::~TileSupervisor()
51 {
52 // Delete all objects in _tile_images but *not* _animated_tile_images.
53 // This is because _animated_tile_images is a subset of _tile_images.
54 for(uint32_t i = 0; i < _tile_images.size(); i++)
55 delete(_tile_images[i]);
56
57 _tile_grid.clear();
58 _tile_images.clear();
59 _animated_tile_images.clear();
60 }
61
Load(ReadScriptDescriptor & map_file)62 bool TileSupervisor::Load(ReadScriptDescriptor &map_file)
63 {
64 // Load the map dimensions and do some basic sanity checks
65 _num_tile_on_y_axis = map_file.ReadInt("num_tile_rows");
66 _num_tile_on_x_axis = map_file.ReadInt("num_tile_cols");
67
68 // Load all of the tileset images that are used by this map
69
70 // Contains all of the tileset filenames used (string does not contain path information or file extensions)
71 std::vector<std::string> tileset_filenames;
72 // Temporarily retains all tile images loaded for each tileset. Each inner vector contains 256 StillImage objects
73 std::vector<std::vector<StillImage> > tileset_images;
74
75 map_file.ReadStringVector("tileset_filenames", tileset_filenames);
76
77 for(uint32_t i = 0; i < tileset_filenames.size(); i++) {
78 std::string tileset_file = tileset_filenames[i];
79
80 ReadScriptDescriptor tileset_script;
81 if (!tileset_script.OpenFile(tileset_file)) {
82 PRINT_ERROR << "Couldn't open the tileset definition file: " << tileset_file << std::endl;
83 return false;
84 }
85
86 if (!tileset_script.OpenTable("tileset")) {
87 PRINT_ERROR << "Couldn't open the 'tileset' table from file: " << tileset_file << std::endl;
88 tileset_script.CloseFile();
89 return false;
90 }
91
92 std::string image_filename = tileset_script.ReadString("image");
93 tileset_script.CloseFile();
94
95 tileset_images.push_back(std::vector<StillImage>(TILES_PER_TILESET));
96
97 // Each tileset image is 512x512 pixels, yielding 16 * 16 (== 256) 32x32 pixel tiles each
98 if(!ImageDescriptor::LoadMultiImageFromElementGrid(tileset_images[i], image_filename, 16, 16)) {
99 PRINT_ERROR << "failed to load tileset image: " << image_filename << std::endl;
100 return false;
101 }
102
103 for(uint32_t j = 0; j < TILES_PER_TILESET; j++) {
104 tileset_images[i][j].SetDimensions(TILE_LENGTH, TILE_LENGTH);
105 }
106 }
107
108 if(!map_file.DoesTableExist("layers")) {
109 PRINT_ERROR << "No 'layers' table in the map file." << std::endl;
110 return false;
111 }
112
113 // Read in the map tile indeces from all tile layers.
114 // The indeces stored for the map layers in this file directly correspond to a location within a tileset. Tilesets contain a total of 256 tiles
115 // each, so 0-255 correspond to the first tileset, 256-511 the second, etc. The tile location within the tileset is also determined by the index,
116 // where the first 16 indeces in the tileset range are the tiles of the first row (left to right), and so on.
117
118 // Clears out the tiles grid
119 _tile_grid.clear();
120
121 std::vector<int32_t> table_x_indeces; // Used to temporarily store a row of table indeces
122
123 map_file.OpenTable("layers");
124
125 uint32_t layers_number = map_file.GetTableSize();
126
127 // layers[0]-[n]
128 for(uint32_t layer_id = 0; layer_id < layers_number; ++layer_id) {
129 // Opens the sub-table: layers[layer_id]
130 if(!map_file.DoesTableExist(layer_id))
131 continue;
132
133 map_file.OpenTable(layer_id);
134
135 // Add a layer for the base context
136 _tile_grid.resize(layer_id + 1);
137
138 LAYER_TYPE layer_type = StringToLayerType(map_file.ReadString("type"));
139
140 if(layer_type == INVALID_LAYER) {
141 PRINT_WARNING << "Ignoring unexisting layer type: " << layer_type
142 << " in file: " << map_file.GetFilename() << std::endl;
143 map_file.CloseTable(); // layers[i]
144 continue;
145 }
146
147 _tile_grid[layer_id].layer_type = layer_type;
148
149 // Add the new tile rows (y axis)
150 _tile_grid[layer_id].tiles.resize(_num_tile_on_y_axis);
151
152 // Read the tile data
153 for(uint32_t y = 0; y < _num_tile_on_y_axis; ++y) {
154 table_x_indeces.clear();
155
156 // Check to make sure tables are of the proper size
157 if(!map_file.DoesTableExist(y)) {
158 PRINT_ERROR << "the layers[" << layer_id << "] table size was not equal to the number of tile rows specified by the map, "
159 " first missing row: " << y << std::endl;
160 return false;
161 }
162
163 map_file.ReadIntVector(y, table_x_indeces);
164
165 // Check the number of columns
166 if(table_x_indeces.size() != _num_tile_on_x_axis) {
167 PRINT_ERROR << "the layers[" << layer_id << "][" << y << "] table size was not equal to the number of tile columns specified by the map, "
168 "should have " << _num_tile_on_x_axis << " values." << std::endl;
169 return false;
170 }
171
172 // Prepare the columns (x axis)
173 _tile_grid[layer_id].tiles[y].resize(_num_tile_on_x_axis);
174
175 for(uint32_t x = 0; x < _num_tile_on_x_axis; ++x) {
176 _tile_grid[layer_id].tiles[y][x] = table_x_indeces[x];
177 }
178 }
179 map_file.CloseTable(); // layers[layer_id]
180 }
181
182 map_file.CloseTable(); // layers
183
184 // Determine which tiles in each tileset are referenced in this map
185
186 // Used to determine whether each tile is used by the map or not. An entry of -1 indicates that particular tile is not used
187 std::vector<int16_t> tile_references;
188 // Set size to be equal to the total number of tiles and initialize all entries to -1 (unreferenced)
189 tile_references.assign(tileset_filenames.size() * TILES_PER_TILESET, -1);
190
191 // For each layer
192 for(uint32_t layer_id = 0; layer_id < layers_number; ++layer_id) {
193 // For each tile id
194 for(uint32_t y = 0; y < _num_tile_on_y_axis; ++y) {
195 for(uint32_t x = 0; x < _num_tile_on_x_axis; ++x) {
196 if(_tile_grid[layer_id].tiles[y][x] >= 0)
197 tile_references[_tile_grid[layer_id].tiles[y][x] ] = 0;
198 }
199 }
200 }
201
202 // Translate the tileset tile indeces into indeces for the vector of tile images
203
204 // Here, we have to convert the original tile indeces defined in the map file into a new form. The original index
205 // indicates the tileset where the tile is used and its location in that tileset. We need to convert those indeces
206 // so that they serve as an index to the MapMode::_tile_images vector, where the tile images will soon be stored.
207
208 // Keeps track of the next translated index number to assign
209 uint32_t next_index = 0;
210
211 for(uint32_t i = 0; i < tile_references.size(); ++i) {
212 if(tile_references[i] >= 0) {
213 tile_references[i] = next_index;
214 next_index++;
215 }
216 }
217
218 // Now, go back and re-assign all tile layer indeces with the translated indeces
219 // For each layer
220 for(uint32_t layer_id = 0; layer_id < layers_number; ++layer_id) {
221 // For each tile id
222 for(uint32_t y = 0; y < _num_tile_on_y_axis; ++y) {
223 for(uint32_t x = 0; x < _num_tile_on_x_axis; ++x) {
224 if(_tile_grid[layer_id].tiles[y][x] >= 0)
225 _tile_grid[layer_id].tiles[y][x] = tile_references[_tile_grid[layer_id].tiles[y][x] ];
226 }
227 }
228 }
229
230 // Parse all of the tileset definition files and create any animated tile images that will be used
231
232 // Used to access the tileset definition file
233 ReadScriptDescriptor tileset_script;
234 // Temporarily retains the animation data (every two elements corresponds to a pair of tile frame index and display time)
235 std::vector<uint32_t> animation_info;
236 // Temporarily holds all animated tile images. The map key is the value of the tile index, before reference translation is done in the next step
237 std::map<uint32_t, AnimatedImage *> tile_animations;
238
239 for(uint32_t i = 0; i < tileset_filenames.size(); i++) {
240 if (!tileset_script.OpenFile(tileset_filenames[i])) {
241 PRINT_ERROR << "map failed to load because it could not open a tileset definition file: "
242 << tileset_filenames[i] << std::endl;
243 return false;
244 }
245
246 if (!tileset_script.OpenTable("tileset")) {
247 PRINT_ERROR << "map failed to load because it could not open the 'tileset' table from file: "
248 << tileset_filenames[i] << std::endl;
249 tileset_script.CloseFile();
250 return false;
251 }
252
253 if(tileset_script.DoesTableExist("animated_tiles")) {
254 tileset_script.OpenTable("animated_tiles");
255 for(uint32_t j = 1; j <= tileset_script.GetTableSize(); j++) {
256 animation_info.clear();
257 tileset_script.ReadUIntVector(j, animation_info);
258
259 // The index of the first frame in the animation. (i * TILES_PER_TILESET) factors in which tileset the frame comes from
260 uint32_t first_frame_index = animation_info[0] + (i * TILES_PER_TILESET);
261
262 // If the first tile frame index of this animation was not referenced anywhere in the map, then the animation is unused and
263 // we can safely skip over it and move on to the next one. Otherwise if it is referenced, we have to construct the animated image
264 if(tile_references[first_frame_index] == -1) {
265 continue;
266 }
267
268 AnimatedImage *new_animation = new AnimatedImage();
269 new_animation->SetDimensions(TILE_LENGTH, TILE_LENGTH);
270
271 // Each pair of entries in the animation info indicate the tile frame index (k) and the time (k+1)
272 for(uint32_t k = 0; k < animation_info.size(); k += 2) {
273 new_animation->AddFrame(tileset_images[i][animation_info[k]], animation_info[k + 1]);
274 }
275 tile_animations.insert(std::make_pair(first_frame_index, new_animation));
276 }
277 tileset_script.CloseTable();
278 }
279
280 tileset_script.CloseTable();
281 tileset_script.CloseFile();
282 } // for (uint32_t i = 0; i < tileset_filenames.size(); i++)
283
284 // Add all referenced tiles to the _tile_images vector, in the proper order
285
286 for(uint32_t i = 0; i < tileset_images.size(); i++) {
287 for(uint32_t j = 0; j < TILES_PER_TILESET; j++) {
288 uint32_t reference = (i * TILES_PER_TILESET) + j;
289
290 if(tile_references[reference] >= 0) {
291 // Add the tile as a StillImage
292 if(tile_animations.find(reference) == tile_animations.end()) {
293 _tile_images.push_back(new StillImage(tileset_images[i][j]));
294 }
295
296 // Add the tile as an AnimatedImage
297 else {
298 _tile_images.push_back(tile_animations[reference]);
299 _animated_tile_images.push_back(tile_animations[reference]);
300 tile_animations.erase(reference);
301 }
302 }
303 }
304 }
305
306 if(tile_animations.empty() == false) {
307 IF_PRINT_WARNING(MAP_DEBUG) << "one or more tile animations that were created were not added into the map -- this is a memory leak" << std::endl;
308 }
309
310 // Remove all tileset images. Any tiles which were not added to _tile_images will no longer exist in memory
311 tileset_images.clear();
312
313 return true;
314 }
315
Update()316 void TileSupervisor::Update()
317 {
318 for(uint32_t i = 0; i < _animated_tile_images.size(); i++) {
319 _animated_tile_images[i]->Update();
320 }
321 }
322
DrawLayers(const MapFrame * frame,const LAYER_TYPE & layer_type)323 void TileSupervisor::DrawLayers(const MapFrame *frame, const LAYER_TYPE &layer_type)
324 {
325 // We'll use the top-left positions to render the tiles.
326 VideoManager->SetDrawFlags(VIDEO_BLEND, VIDEO_X_LEFT, VIDEO_Y_TOP, 0);
327
328 // Map frame ends
329 uint32_t y_end = static_cast<uint32_t>(frame->tile_y_start + frame->num_draw_y_axis);
330 uint32_t x_end = static_cast<uint32_t>(frame->tile_x_start + frame->num_draw_x_axis);
331
332 uint32_t layer_number = _tile_grid.size();
333 for(uint32_t layer_id = 0; layer_id < layer_number; ++layer_id) {
334
335 const Layer &layer = _tile_grid.at(layer_id);
336 if(layer.layer_type != layer_type)
337 continue;
338
339 // We substract 0.5 horizontally and 1.0 vertically here
340 // because the video engine will display the map tiles using their
341 // top left coordinates to avoid a position computation flaw when specifying the tile
342 // coordinates from the bottom center point, as the engine does for everything else.
343 VideoManager->Move(GRID_LENGTH * (frame->tile_offset.x - 1.0f),
344 GRID_LENGTH * (frame->tile_offset.y - 2.0f));
345 for(uint32_t y = static_cast<uint32_t>(frame->tile_y_start); y < y_end; ++y) {
346 for(uint32_t x = static_cast<uint32_t>(frame->tile_x_start); x < x_end; ++x) {
347 // Draw a tile image if it exists at this location
348 if(layer.tiles[y][x] >= 0)
349 _tile_images[ layer.tiles[y][x] ]->Draw();
350
351 VideoManager->MoveRelative(TILE_LENGTH, 0.0f);
352 } // x
353 VideoManager->MoveRelative(-static_cast<float>(frame->num_draw_x_axis) * TILE_LENGTH, TILE_LENGTH);
354 } // y
355 } // layer_id
356
357 // Restore the previous draw flags.
358 VideoManager->SetDrawFlags(VIDEO_BLEND, VIDEO_X_CENTER, VIDEO_Y_BOTTOM, 0);
359 }
360
361 } // namespace private_map
362
363 } // namespace vt_map
364