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