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