1 /*
2  * Copyright (C) 2014-2018 Christopho, Solarus - http://www.solarus-games.org
3  *
4  * Solarus Quest Editor is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * Solarus Quest Editor is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 #include "entities/tile.h"
18 #include "ground_traits.h"
19 #include "map_model.h"
20 #include "quest.h"
21 #include "tileset_model.h"
22 #include <QPainter>
23 
24 namespace SolarusEditor {
25 
26 /**
27  * @brief Creates a normal tile.
28  * @param map The map containing the entity.
29  * @param index Index of the entity in the map.
30  */
Tile(MapModel & map,const EntityIndex & index)31 Tile::Tile(MapModel& map, const EntityIndex& index) :
32   Tile(map, index, EntityType::TILE) {
33 
34 }
35 
36 /**
37  * @brief Constructor.
38  * @param map The map containing the entity.
39  * @param index Index of the entity in the map.
40  * @param type Concrete type of entity: TILE or DYNAMIC_TILE.
41  */
Tile(MapModel & map,const EntityIndex & index,EntityType type)42 Tile::Tile(MapModel& map, const EntityIndex& index, EntityType type) :
43   EntityModel(map, index, type) {
44 
45   set_resizable(true);
46   set_has_preferred_layer(true);
47 }
48 
49 /**
50  * @brief Creates a normal tile from a dynamic one.
51  * @param map The map.
52  * @param tile_index index of the dynamic tile to clone.
53  * @return The tile created. It is not on the map yet.
54  */
create_from_dynamic_tile(MapModel & map,const EntityIndex & dynamic_tile_index)55 EntityModelPtr Tile::create_from_dynamic_tile(MapModel& map, const EntityIndex& dynamic_tile_index) {
56 
57   Q_ASSERT(map.get_entity_type(dynamic_tile_index) == EntityType::DYNAMIC_TILE);
58 
59   EntityModelPtr tile = EntityModel::create(map, EntityType::TILE);
60   tile->set_field("pattern", map.get_entity_field(dynamic_tile_index, "pattern"));
61   tile->set_field("tileset", map.get_entity_field(dynamic_tile_index, "tileset"));
62   tile->set_xy(map.get_entity_xy(dynamic_tile_index));
63   tile->set_size(map.get_entity_size(dynamic_tile_index));
64   return tile;
65 }
66 
67 /**
68  * @brief Returns the pattern id used by this tile.
69  * @return The pattern id.
70  */
get_pattern_id() const71 QString Tile::get_pattern_id() const {
72   return get_field("pattern").toString();
73 }
74 
75 /**
76  * @brief Sets the pattern id used by this tile.
77  * @param pattern_id The new pattern id.
78  */
set_pattern_id(const QString & pattern_id)79 void Tile::set_pattern_id(const QString& pattern_id) {
80 
81   set_field("pattern", pattern_id);
82 }
83 
84 /**
85  * @brief Returns the tileset used by this tile.
86  * @return The tileset.
87  */
get_tileset() const88 const TilesetModel* Tile::get_tileset() const {
89 
90   QString tileset_id = get_field("tileset").toString();
91   if (tileset_id.isEmpty()) {
92     return get_map().get_tileset_model();
93   }
94 
95   return get_quest().get_tileset(tileset_id);
96 }
97 
98 /**
99  * @copydoc EntityModel::notify_field_changed
100  */
notify_field_changed(const QString & key,const QVariant & value)101 void Tile::notify_field_changed(const QString& key, const QVariant& value) {
102 
103   EntityModel::notify_field_changed(key, value);
104 
105   if (key == "pattern" || key == "tileset") {
106     update_pattern();
107   }
108 }
109 
110 /**
111  * @brief Updates the representation of the tile.
112  *
113  * This function should be called when the pattern changes.
114  */
update_pattern()115 void Tile::update_pattern() {
116 
117   const TilesetModel* tileset = get_tileset();
118   if (tileset != nullptr) {
119     int pattern_index = tileset->id_to_index(get_pattern_id());
120     if (pattern_index != -1) {
121       // Update the resizing rules.
122       set_base_size(tileset->get_pattern_frame(pattern_index).size());
123       set_resize_mode(get_pattern_resize_mode());
124 
125       // Update the preferred initial layer.
126       set_preferred_layer(tileset->get_pattern_default_layer(pattern_index));
127 
128       // Update the traversable property.
129       Ground ground = tileset->get_pattern_ground(pattern_index);
130       set_traversable(GroundTraits::is_traversable(ground));
131     }
132   }
133 
134   // Invalidate the cached image.
135   pattern_image = QPixmap();
136 }
137 
138 /**
139  * @brief Computes the resize mode for this tile from its pattern.
140  * @return The appropriate resize mode.
141  */
get_pattern_resize_mode() const142 ResizeMode Tile::get_pattern_resize_mode() const {
143 
144   const TilesetModel* tileset = get_tileset();
145   if (tileset == nullptr) {
146     return ResizeMode::MULTI_DIMENSION_ALL;
147   }
148 
149   int pattern_index = tileset->id_to_index(get_pattern_id());
150   if (pattern_index == -1) {
151     return ResizeMode::MULTI_DIMENSION_ALL;
152   }
153 
154   switch (tileset->get_pattern_repeat_mode(pattern_index)) {
155 
156   case PatternRepeatMode::ALL:
157     return ResizeMode::MULTI_DIMENSION_ALL;
158 
159   case PatternRepeatMode::HORIZONTAL:
160     return ResizeMode::HORIZONTAL_ONLY;
161 
162   case PatternRepeatMode::VERTICAL:
163     return ResizeMode::VERTICAL_ONLY;
164 
165   case PatternRepeatMode::NONE:
166     return ResizeMode::NONE;
167 
168   }
169 
170   return ResizeMode::MULTI_DIMENSION_ALL;
171 }
172 
173 /**
174  * @copydoc EntityModel::draw
175  */
draw(QPainter & painter) const176 void Tile::draw(QPainter& painter) const {
177 
178   if (pattern_image.isNull()) {
179     // Lazily create the image.
180     const TilesetModel* tileset = get_tileset();
181     if (tileset != nullptr) {
182       int pattern_index = tileset->id_to_index(get_pattern_id());
183       if (pattern_index == -1) {
184         // The pattern no longer exists: fallback to a generic tile icon.
185         EntityModel::draw(painter);
186         return;
187       }
188       int pattern_width = tileset->get_pattern_frame(pattern_index).width();
189       pattern_image = tileset->get_pattern_image(pattern_index).scaledToWidth(pattern_width);
190     }
191   }
192 
193   painter.drawTiledPixmap(0, 0, get_width(), get_height(), pattern_image);
194 }
195 
196 /**
197  * @copydoc EntityModel::notify_tileset_changed
198  */
notify_tileset_changed(const QString & tileset_id)199 void Tile::notify_tileset_changed(const QString& tileset_id) {
200 
201   EntityModel::notify_tileset_changed(tileset_id);
202   update_pattern();
203 }
204 
205 }
206