1 /* Battle Tanks Game
2 * Copyright (C) 2006-2009 Battle Tanks team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program 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
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 /*
20 * Additional rights can be granted beyond the GNU General Public License
21 * on the terms provided in the Exception. If you modify this file,
22 * you may extend this exception to your version of the file,
23 * but you are not obligated to do so. If you do not wish to provide this
24 * exception without modification, you must delete this exception statement
25 * from your version and license this file solely under the GPL without exception.
26 */
27
28 #include <algorithm>
29 #include "tileset_dialog.h"
30 #include "finder.h"
31 #include "mrt/directory.h"
32 #include "menu/box.h"
33 #include "resource_manager.h"
34 #include "sdlx/surface.h"
35 #include "tmx/map.h"
36 #include "math/binary.h"
37 #include "config.h"
38 #include "menu/scroll_list.h"
39 #include "base_brush.h"
40 #include "add_tileset_dialog.h"
41 #include "mrt/fs_node.h"
42
TilesetDialog(const int w,const int h)43 TilesetDialog::TilesetDialog(const int w, const int h) :
44 _w(w), _h(h), _current_tileset(NULL), _current_tileset_idx(-1), _current_tileset_gid(0), _selecting(false), _selected(false),
45 _tileset_added(false)
46 {
47 init_map_slot.assign(this, &TilesetDialog::initMap, Map->load_map_signal);
48
49 std::vector<std::string> path;
50 Finder->getPath(path);
51 std::reverse(path.begin(), path.end());
52
53 for(size_t i = 0; i < path.size(); ++i) {
54 LOG_DEBUG(("%u: %s", (unsigned)i, path[i].c_str()));
55 TRY {
56 std::vector<std::string> dir;
57 Finder->enumerate(dir, path[i], "/tilesets");
58 for(size_t j = 0; j < dir.size(); ++j) {
59 std::string fname = dir[j];
60 if (fname.size() < 5 || fname.compare(fname.size() - 4, 4, ".png") != 0)
61 continue;
62 fname.insert(0, path[i] + "/tilesets/");
63 _all_tilesets.push_back(fname);
64 LOG_DEBUG(("found tileset %s", fname.c_str()));
65 }
66 } CATCH("scanning tilesets directory", continue);
67 }
68 add(0, 0, _tileset_bg = new Box("transparent_background_box.png", w, h));
69
70 _sl_tilesets = new ScrollList("menu/background_box.png", "small", 200, h);
71 int cw, ch;
72 _sl_tilesets->get_size(cw, ch);
73 add(w - cw, 0, _sl_tilesets);
74
75 _add_tileset = new AddTilesetDialog(200, h);
76 _add_tileset->get_size(cw, ch);
77 add(w - cw, 0, _add_tileset);
78 _add_tileset->hide();
79 }
80
initMap()81 void TilesetDialog::initMap() {
82 _fname = Finder->find("maps/" + Map->getName() + ".tmx");
83 LOG_DEBUG(("fixme: finder returns %s", _fname.c_str()));
84
85 _tile_size = Map->getTileSize();
86 _brush.create_rgb(_tile_size.x, _tile_size.y, 32);
87 _brush.display_format_alpha();
88 _brush.fill_rect(_brush.get_size(), _brush.map_rgba(0xdd, 0xdd, 0x11, 0x80));
89 _tilesets = Map->getTilesets();
90
91 _sl_tilesets->clear();
92 size_t n = _tilesets.size();
93 for(size_t i = 0; i < n; ++i) {
94 std::string name = _tilesets[i].first;
95 {
96 //beautify name
97 name = mrt::FSNode::get_filename(name, false);
98 }
99
100 char key = '1' + (char)i;
101 if (i == 9)
102 key = '0';
103 if (i >= 10)
104 key = 'a' + i - 10;
105 _sl_tilesets->append(mrt::format_string("%c.%s", key, name.c_str()));
106 }
107 }
108
onMouse(const int button,const bool pressed,const int x,const int y)109 bool TilesetDialog::onMouse(const int button, const bool pressed, const int x, const int y) {
110 if (!Map->loaded())
111 return false;
112 if (_current_tileset != NULL && x <= _current_tileset->get_width()) {
113 switch(button) {
114 case SDL_BUTTON_LEFT:
115 if (pressed) {
116 _brush_2 = _brush_1 = (_pos.convert<int>() + v2<int>(x, y)) / _tile_size;
117 //add validation
118 _selecting = true;
119 } else if (_selecting /*&&!pressed*/){
120 _selecting = false;
121 _selected = true;
122
123 int y0 = math::min(_brush_1.y, _brush_2.y);
124 int y1 = math::max(_brush_1.y, _brush_2.y);
125 int x0 = math::min(_brush_1.x, _brush_2.x);
126 int x1 = math::max(_brush_1.x, _brush_2.x);
127 int tileset_width = (_current_tileset->get_width() - 1) / _tile_size.x + 1;
128
129 LOG_DEBUG(("brush : %dx%d-%dx%d", x0, y0, x1, y1));
130 std::vector<int> tiles;
131
132 for(int yb = y0; yb <= y1; ++yb) {
133 for(int xb = x0; xb <= x1; ++xb) {
134 int tid = _current_tileset_gid + xb + yb * tileset_width;
135 //LOG_DEBUG(("adding tile %d to brush (base %d)", tid, _current_tileset_gid));
136 tiles.push_back(tid);
137 }
138 }
139
140 _editor_brush = Brush(_tile_size, tiles);
141 _editor_brush.size = v2<int>(x1 - x0 + 1, y1 - y0 + 1);
142 invalidate();
143
144 static const Uint8 *keys = SDL_GetKeyState(0);
145 if (keys[SDLK_LCTRL] == 0)
146 hide();
147 }
148 break;
149
150 case SDL_BUTTON_RIGHT:
151 _selected = false;
152 break;
153
154 case SDL_BUTTON_WHEELUP:
155 if (_current_tileset)
156 _pos.y -= _tile_size.y;
157 break;
158
159 case SDL_BUTTON_WHEELDOWN:
160 if (_current_tileset)
161 _pos.y += _tile_size.y;
162 break;
163 }
164
165 return true;
166 }
167
168 return Container::onMouse(button, pressed, x, y);
169 }
170
onMouseMotion(const int state,const int x,const int y,const int xrel,const int yrel)171 bool TilesetDialog::onMouseMotion(const int state, const int x, const int y, const int xrel, const int yrel) {
172 if (!Map->loaded())
173 return false;
174 if (_current_tileset && _current_tileset->get_height() > _h) {
175 if (y + _tile_size.y / 2 >= _h) {
176 _vel.y = 1;
177 } else if (y <= _tile_size.y / 2) {
178 _vel.y = -1;
179 } else _vel.y = 0;
180 }
181
182 if (_selecting) {
183 _brush_2 = (_pos.convert<int>() + v2<int>(x, y)) / _tile_size;
184 }
185 return true;
186 }
187
tick(const float dt)188 void TilesetDialog::tick(const float dt) {
189 Container::tick(dt);
190 if (_sl_tilesets->changed()) {
191 _sl_tilesets->reset();
192 if (!_sl_tilesets->empty())
193 set(_sl_tilesets->get());
194 }
195
196 std::string tileset = _add_tileset->getTileset();
197 if (!tileset.empty()) {
198 LOG_DEBUG(("adding tileset!"));
199 Map->addTileset(tileset);
200 initMap();
201 _tileset_added = true;
202 }
203
204 if (_current_tileset == NULL || hidden())
205 return;
206
207 GET_CONFIG_VALUE("editor.scrolling-speed", int, ss, 500);
208 _pos += _vel * (ss * dt);
209 if (_pos.x + _w > _current_tileset->get_width())
210 _pos.x = _current_tileset->get_width() - _w;
211 if (_pos.y + _h > _current_tileset->get_height())
212 _pos.y = _current_tileset->get_height() - _h;
213
214 if (_pos.x < 0)
215 _pos.x = 0;
216 if (_pos.y < 0)
217 _pos.y = 0;
218 //LOG_DEBUG(("%g %g", _pos.x, _pos.y));
219
220 }
221
222
onKey(const SDL_keysym sym)223 bool TilesetDialog::onKey(const SDL_keysym sym) {
224 if (Container::onKey(sym)) {
225 return true;
226 }
227
228 //LOG_DEBUG(("sym.sym == '%c' (%02x), mod: %04x", (char)sym.sym, (unsigned)sym.sym, (unsigned)sym.mod));
229 if ((sym.sym == SDLK_ESCAPE || sym.sym == SDLK_TAB) && !hidden()) {
230 hide();
231 return true;
232 }
233
234 if (sym.sym == SDLK_n) {
235 if (_add_tileset->init(_fname, _tilesets, _all_tilesets))
236 _add_tileset->hide(false);
237 return true;
238 }
239
240 if (sym.mod & (KMOD_CTRL | KMOD_ALT | KMOD_META | KMOD_SHIFT))
241 return false;
242
243 int cd = (int)(sym.sym - SDLK_0);
244 int cc = (int)(sym.sym - SDLK_a);
245 int tileset = -1;
246 if(cd >= 0 && cd <= 9)
247 tileset = cd?cd-1:9;
248 else if (cc >= 0 && cc < 26) {
249 tileset = cc + 10;
250 }
251
252 //LOG_DEBUG(("pop tileset %d", tileset));
253 if (hidden()) {
254 hide(false);
255 } else if (tileset == _current_tileset_idx) {
256 hide();
257 return true;
258 }
259 set(tileset);
260 return true;
261 }
262
set(const int tileset)263 void TilesetDialog::set(const int tileset) {
264 if (tileset < 0 || tileset >= (int)_tilesets.size())
265 return;
266 if (tileset != _current_tileset_idx) {
267 //do it per tileset.
268 _brush_1.clear();
269 _brush_2.clear();
270 _pos.clear();
271 _vel.clear();
272 }
273
274 std::string name = _tilesets[tileset].first;
275 _current_tileset_idx = tileset;
276 _current_tileset_gid = _tilesets[tileset].second;
277
278 _current_tileset = ResourceManager->load_surface("../tilesets/" + name);
279 _tileset_bg->init("transparent_background_box.png", _current_tileset->get_width(), _current_tileset->get_height());
280 }
281
render(sdlx::Surface & surface,const int x,const int y) const282 void TilesetDialog::render(sdlx::Surface &surface, const int x, const int y) const {
283 Container::render(surface, x, y);
284 if (_current_tileset == NULL)
285 return;
286 surface.blit(*_current_tileset, -(int)_pos.x, -(int)_pos.y);
287
288 if (!_brush.isNull() && (_selected || _selecting)) {
289 int y0 = math::min(_brush_1.y, _brush_2.y);
290 int y1 = math::max(_brush_1.y, _brush_2.y);
291 int x0 = math::min(_brush_1.x, _brush_2.x);
292 int x1 = math::max(_brush_1.x, _brush_2.x);
293
294 for(int yb = y0; yb <= y1; ++yb) {
295 for(int xb = x0; xb <= x1; ++xb) {
296 surface.blit(_brush, x + xb * _tile_size.x - (int)_pos.x, y + yb * _tile_size.y - (int)_pos.y);
297 }
298 }
299 }
300 }
301
getBrush()302 BaseBrush *TilesetDialog::getBrush() {
303 return new Brush(_editor_brush);
304 }
305