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