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 "editor.h"
29
30 #include "mrt/logger.h"
31 #include "mrt/directory.h"
32 #include "mrt/file.h"
33 #include "sdlx/sdlx.h"
34
35 #include "window.h"
36 #include "resource_manager.h"
37 #include "animation_model.h"
38 #include "game_monitor.h"
39
40 #include "object.h"
41 #include "world.h"
42 #include "finder.h"
43
44 #include "i18n.h"
45 #include "var.h"
46 #include "config.h"
47 #include "hud.h"
48 #include "rt_config.h"
49
50 #include "tmx/layer.h"
51 #include "tmx/map.h"
52 #include "tmx/generator.h"
53
54 #include "math/unary.h"
55
56 #include "open_map_dialog.h"
57 #include "tileset_dialog.h"
58 #include "layer_list_dialog.h"
59 #include "add_object_dialog.h"
60 #include "base_brush.h"
61 #include "layer_item.h"
62 #include "command.h"
63 #include "object_properties.h"
64 #include "tilebox_brush.h"
65 #include "morph_dialog.h"
66 #include "resize_dialog.h"
67
68 #include "game.h"
69 #include "math/binary.h"
70
moveObjectHack(Object * object,const v2<int> & screen_pos)71 void Editor::moveObjectHack(Object *object, const v2<int>& screen_pos) {
72 World->move(object, screen_pos.x + map_pos.x, screen_pos.y + map_pos.y);
73 World->purge(0);
74 }
75
addCommand(Command & cmd)76 void Editor::addCommand(Command &cmd) {
77 undo_queue.push_back(cmd);
78 redo_queue.clear();
79 }
80
currentCommand()81 Command &Editor::currentCommand() {
82 if (undo_queue.empty())
83 throw_ex(("undo queue is empty"));
84 return undo_queue.back();
85 }
86
undo()87 void Editor::undo() {
88 if (undo_queue.empty())
89 return;
90
91 LOG_DEBUG(("undo"));
92 undo_queue.back().undo();
93 redo_queue.push_front(undo_queue.back());
94 undo_queue.pop_back();
95 //LOG_DEBUG(("undo. undo queue size = %u", (unsigned)undo_queue.size()));
96 }
97
redo()98 void Editor::redo() {
99 if (redo_queue.empty())
100 return;
101
102 LOG_DEBUG(("redo"));
103 redo_queue.front().exec();
104 undo_queue.push_back(redo_queue.front());
105 redo_queue.pop_front();
106 //LOG_DEBUG(("redo. undo queue size = %u", (unsigned)redo_queue.size()));
107 }
108
109
Editor()110 Editor::Editor() :
111 _map_picker(NULL), _hud(NULL), _brush(NULL), _layer_name_invisible(1.0f, false), _layer_invisible(1.0f, false),
112 _render_objects(true), _highlight_object(NULL), _dragging(false), _selecting(false) {
113 }
114
loadMap(const std::string & map)115 void Editor::loadMap(const std::string &map) {
116 TRY {
117 _render_objects = true;
118 _dragging = false;
119 _selecting = false;
120
121 LOG_DEBUG(("loading map %s", map.c_str()));
122 GameMonitor->clear();
123 World->clear();
124
125 GameMonitor->loadMap(NULL, map);
126
127 v2<int> map_size = Map->get_size();
128 _tile_size = Map->getTileSize();
129 map_pos.w = map_size.x;
130 map_pos.h = map_size.y;
131 map_pos.x = map_pos.y = 0;
132
133 delete _brush;
134 _brush = NULL;
135 undo_queue.clear();
136 redo_queue.clear();
137
138 {
139 Var bt("bool");
140 bt.b = true;
141 Config->setOverride("engine.mark-map-tiles", bt);
142 Config->setOverride("engine.show-waypoints", bt);
143 }
144
145 _current_layer_z = 0;
146 updateLayers();
147 Map->getGenerator()->getPrimaryBoxes(_morph_boxes);
148 } CATCH("loadMap", throw;)
149 }
150
onTick(float dt)151 bool Editor::onTick(float dt) {
152 static const Uint8 *keys = SDL_GetKeyState(0);
153 bool active = (keys[SDLK_LSHIFT] != 0 || keys[SDLK_RSHIFT] != 0 || _layers_dialog->active()) && Map->loaded() && _tileset_dialog->hidden();
154 _layers_dialog->hide(!active);
155
156 tick(dt);
157 Map->tick(dt);
158
159 if (_map_picker->changed()) {
160 _map_picker->reset();
161 _map_picker->getMap(_map_base, _map_file);
162 loadMap(_map_file);
163 }
164
165 if (_tileset_dialog->changed()) {
166 _morph_dialog->initMap();
167 _tileset_dialog->reset();
168 BaseBrush *b = _tileset_dialog->getBrush();
169 delete _brush;
170 _brush = b;
171 }
172 if (_tileset_dialog->tileset_added()) {
173 _morph_dialog->initMap();
174 }
175
176 if (_layers_dialog->changed()) {
177 _layers_dialog->reset();
178 updateLayers();
179 }
180
181 if (_add_object->changed()) {
182 _add_object->reset();
183 std::string classname, animation;
184 int z;
185 if (_add_object->get(classname, animation, z)) {
186 delete _brush;
187 _brush = NULL;
188 TRY {
189 _brush = new ObjectBrush(this, classname, animation, z);
190 } CATCH("adding object brush", {});
191 }
192 /*
193 if (_add_object->get(classname, animation, z)) {
194 SDL_WarpMouse(mouse_pos.x, mouse_pos.y);
195 LOG_DEBUG(("creation of %s:%s requested (z: %d)", classname.c_str(), animation.c_str(), z));
196
197 const Animation *a = ResourceManager.get_const()->getAnimation(animation);
198 v2<int> pos = (mouse_pos.convert<float>() + v2<float>(map_pos.x, map_pos.y) - v2<float>(a->tw, a->th) / 2).convert<int>();
199
200 Command cmd(classname, animation, pos, z);
201 addCommand(cmd);
202 currentCommand().exec();
203 }
204 */
205 }
206
207 if (_object_properties->changed()) {
208 std::set<std::string> vars;
209 _object_properties->get(vars);
210 Command cmd(Command::ChangeObjectProperties, _object_properties->object, _object_properties->get_z(), Variants(vars));
211 addCommand(cmd);
212 currentCommand().exec();
213 displayStatusMessage(_object_properties->object);
214
215 _object_properties->reset();
216 _object_properties->hide();
217 }
218
219 if (_morph_dialog->changed()) {
220 _morph_dialog->reset();
221 int idx = _morph_dialog->get();
222 if (Map->loaded() && idx < (int)_morph_boxes.size()) {
223 LOG_DEBUG(("morph brush"));
224 delete _brush;
225 _brush = new TileBoxBrush(_morph_boxes[idx].first, _morph_boxes[idx].second);
226 }
227 }
228
229 {
230 v2<float> dpos = map_dir;
231 GET_CONFIG_VALUE("editor.scrolling-speed", int, speed, 500);
232 dpos *= speed * dt;
233 map_pos.x += (int)dpos.x;
234 map_pos.y += (int)dpos.y;
235 }
236
237 World->purge(0);
238
239 render(Window->get_surface(), dt);
240 return true;
241 }
242
243
render(sdlx::Surface & surface,const float dt)244 void Editor::render(sdlx::Surface &surface, const float dt) {
245 sdlx::Rect window_size = surface.get_size();
246 surface.fill(surface.map_rgb(255, 255, 255));
247
248 const bool highlight_layer = !_layer_invisible.tick(dt);
249 const bool show_layer_name = !_layer_name_invisible.tick(dt);
250 if (Map->loaded()) {
251 const sdlx::Rect window_size = Window->get_size();
252 const v2<int> map_size = Map->get_size();
253
254 if (map_pos.x < 0)
255 map_pos.x = 0;
256
257 if (map_pos.y < 0)
258 map_pos.y = 0;
259
260 if (map_pos.x + window_size.w > map_size.x)
261 map_pos.x = map_size.x - window_size.w;
262
263 if (map_pos.y + window_size.h > map_size.y)
264 map_pos.y = map_size.y - window_size.h;
265
266 const bool render_objects = _render_objects && !Map->hasSoloLayers();
267
268 if (render_objects)
269 World->render(surface, map_pos, window_size, -10000, _current_layer_z);
270 else
271 Map->render(surface, map_pos, window_size, -10000, _current_layer_z);
272
273 if (highlight_layer) {
274 int p = (int)(_layer_invisible.get() * 6);
275 if (p & 1) {
276 if (render_objects)
277 World->render(surface, map_pos, window_size, _current_layer_z, _current_layer_z + 1);
278 else
279 Map->render(surface, map_pos, window_size, _current_layer_z, _current_layer_z + 1);
280 }
281 } else {
282 if (render_objects)
283 World->render(surface, map_pos, window_size, _current_layer_z, _current_layer_z + 1);
284 else
285 Map->render(surface, map_pos, window_size, _current_layer_z, _current_layer_z + 1);
286 }
287
288 if (_brush && _tileset_dialog->hidden()) {
289 //LOG_DEBUG(("picked up brush %p", _brush));
290 int mx, my;
291 SDL_GetMouseState(&mx, &my);
292 v2<int> window_pos(mx, my);
293 v2<int> base(map_pos.x, map_pos.y);
294 v2<int> tile_pos = (base + window_pos) / _tile_size * _tile_size - base;
295 _brush->render(surface, window_pos, tile_pos);
296 }
297 if (render_objects)
298 World->render(surface, map_pos, window_size, _current_layer_z + 1, 10000);
299 else
300 Map->render(surface, map_pos, window_size, _current_layer_z + 1, 10000);
301
302 if (_selecting || _selection1.quick_distance(_selection2) >= 1024) {
303 static sdlx::Surface selection;
304 if (selection.isNull()) {
305 selection.create_rgb(_tile_size.x, _tile_size.y, 32);
306 selection.display_format_alpha();
307 selection.fill(selection.map_rgba(0x11, 0xdd, 0x11, 0x80));
308 }
309 v2<int> base(map_pos.x, map_pos.y);
310 v2<int> sel1 = _selection1, sel2 = _selection2;
311 if (sel1.x > sel2.x)
312 math::exchange(sel1.x, sel2.x);
313 if (sel1.y > sel2.y)
314 math::exchange(sel1.y, sel2.y);
315
316 v2<int> t1 = v2<int>(sel1.x, sel1.y) / _tile_size * _tile_size - base;
317 v2<int> t2 = v2<int>(sel2.x, sel2.y) / _tile_size * _tile_size - base;
318 //LOG_DEBUG(("%d,%d -> %d,%d", t1.x, t1.y, t2.x, t2.y));
319 for(int y = t1.y; y <= t2.y; y += _tile_size.y)
320 for(int x = t1.x; x <= t2.x; x += _tile_size.x) {
321 surface.blit(selection, x, y);
322 }
323 }
324 }
325
326 if ((show_layer_name || highlight_layer) && !_layer_name.empty()) {
327 //int w = _small_font->render(NULL, 0, 0, _layer_name);
328 _small_font->render(surface, 8, 8, _layer_name);
329 }
330
331 if (_brush == NULL && _highlight_object) {
332 static const sdlx::Surface *hl;
333 if (hl == NULL)
334 hl = ResourceManager->load_surface("object.png");
335
336 v2<float> pos;
337 _highlight_object->get_center_position(pos);
338 surface.blit(*hl,
339 (int)(pos.x - map_pos.x - hl->get_width() / 2),
340 (int)(pos.y - map_pos.y - hl->get_height() / 2)
341 );
342 }
343
344 Container::render(surface, 0, 0);
345 }
346
347 #include "mrt/lang.h"
348
init(int argc,char * argv[])349 void Editor::init(int argc, char *argv[]) {
350 std::string config_path = mrt::Directory::get_app_dir("Battle Tanks", "btanks") + "/";
351 Config->load(config_path + "bt.xml");
352
353 std::string lang; //default
354
355 for(int i = 1; i < argc; ++i) {
356 if (strncmp(argv[i], "--lang=", 7) == 0) { lang = argv[i] + 7; }
357 }
358
359 if (lang.empty()) {
360 if (Config->has("engine.language")) {
361 Config->get("engine.language", lang, std::string());
362 }
363
364 if (lang.empty())
365 lang = mrt::get_lang_code();
366 }
367
368 I18n->load(lang);
369 Window->init(argc, argv);
370 sdlx::Rect window_size = Window->get_size();
371
372 LOG_DEBUG(("initializing hud..."));
373 _hud = new Hud(window_size.w, window_size.h);
374
375 LOG_DEBUG(("installing callbacks..."));
376
377 on_key_slot.assign(this, &Editor::onKeySignal, Window->key_signal);
378 on_mouse_slot.assign(this, &Editor::onMouseSignal, Window->mouse_signal);
379 on_mouse_motion_slot.assign(this, &Editor::onMouseMotionSignal, Window->mouse_motion_signal);
380 on_event_slot.assign(this, &Editor::onEvent, Window->event_signal);
381
382 reset_slot.assign(this, &Editor::resetLoadingBar, Map->reset_progress);
383 notify_slot.assign(this, &Editor::notifyLoadingBar, Map->notify_progress);
384
385 reset_slot.assign(this, &Editor::resetLoadingBar, ResourceManager->reset_progress);
386 notify_slot.assign(this, &Editor::notifyLoadingBar, ResourceManager->notify_progress);
387
388 on_tick_slot.assign(this, &Editor::onTick, Window->tick_signal);
389
390 std::vector<std::pair<std::string, std::string> > files;
391 Finder->findAll(files, "resources.xml");
392
393 ResourceManager->init(files);
394
395 Control * c = _map_picker = new OpenMapDialog;
396 int cw, ch;
397 c->get_size(cw, ch);
398 add((window_size.w - cw) / 2, (window_size.h - ch) / 2, c);
399
400 c = _tileset_dialog = new TilesetDialog(window_size.w, window_size.h);
401 add(0, 0, c);
402 c->hide();
403
404 c = _layers_dialog = new LayerListDialog(window_size.w, window_size.h);
405 c->get_size(cw, ch);
406 add((window_size.w - cw), 0, c);
407 c->hide();
408
409 c = _add_object = new AddObjectDialog(512, 384);
410 c->get_size(cw, ch);
411 add((window_size.w - cw) / 2, (window_size.h - ch) / 2, c);
412 c->hide();
413
414 c = _morph_dialog = new MorphDialog(512, 384);
415 c->get_size(cw, ch);
416 add((window_size.w - cw) / 2, (window_size.h - ch) / 2, c);
417 c->hide();
418
419 RTConfig->editor_mode = true;
420
421 _small_font = ResourceManager->loadFont("small", true);
422
423 _object_properties = new ObjectPropertiesDialog(240);
424 add(0, 0, _object_properties);
425 _object_properties->hide();
426
427 _resize_map = new ResizeDialog();
428 _resize_map->get_size(cw, ch);
429 add((window_size.w - cw) / 2, (window_size.h - ch) / 2, _resize_map);
430 _resize_map->hide();
431 }
432
run()433 void Editor::run() {
434 LOG_DEBUG(("entering main loop"));
435 Window->run();
436 LOG_DEBUG(("exiting main loop"));
437 }
438
deinit()439 void Editor::deinit() {
440
441 delete _hud;
442 _hud = NULL;
443
444 ResourceManager->clear();
445 Window->deinit();
446
447 TRY {
448 Config->save();
449 } CATCH("saving config", {});
450 }
451
onKey(const SDL_keysym sym)452 bool Editor::onKey(const SDL_keysym sym) {
453 if (Container::onKey(sym))
454 return true;
455
456 int dir = 0;
457 switch(sym.sym) {
458 case SDLK_x:
459 case SDLK_c:
460 if ((sym.mod & KMOD_CTRL) == 0)
461 break;
462 {
463 if (_brush != NULL || _selecting || _selection1.quick_distance(_selection2) < 1024)
464 return true;
465
466 Layer * l = Map->getLayer(_current_layer_z);
467 if (!l->isVisible())
468 return true;
469
470 v2<int> t1 = v2<int>(_selection1.x, _selection1.y) / _tile_size;
471 v2<int> t2 = v2<int>(_selection2.x, _selection2.y) / _tile_size;
472 //LOG_DEBUG(("%d,%d -> %d,%d", t1.x, t1.y, t2.x, t2.y));
473
474 std::vector<int> tiles;
475 bool empty = true;
476 for(int y = t1.y; y <= t2.y; ++y)
477 for(int x = t1.x; x <= t2.x; ++x) {
478 int tid = l->get(x, y);
479 //LOG_DEBUG(("tid: %d", tid));
480 tiles.push_back(tid);
481 if (tid)
482 empty = false;
483 }
484 if (empty)
485 return true;
486
487 _brush = new Brush(_tile_size, tiles);
488 _brush->size = t2 - t1 + 1;
489
490 bool cut = sym.sym == SDLK_x;
491 _selection1.clear();
492 _selection2.clear();
493 LOG_DEBUG(("copypastecut: %s", cut?"cut":"copy"));
494 if (cut) {
495 {
496 Command cmd(l);
497 addCommand(cmd);
498 }
499 for(int y = t1.y; y <= t2.y; ++y)
500 for(int x = t1.x; x <= t2.x; ++x) {
501 currentCommand().setTile(x, y, 0);
502 }
503 currentCommand().exec();
504 }
505 }
506 return true;
507
508 case SDLK_q:
509 if (sym.mod & KMOD_ALT) {
510 Window->stop();
511 return true;
512 }
513 break;
514 case SDLK_ESCAPE:
515 if (_brush) {
516 delete _brush;
517 _brush = NULL;
518 }
519 return true;
520
521 case SDLK_INSERT:
522 case SDLK_i:
523 if (Map->loaded()) {
524 SDL_GetMouseState(&mouse_pos.x, &mouse_pos.y);
525 _add_object->hide(false);
526 }
527 return true;
528
529 case SDLK_z:
530 if (!Map->loaded())
531 return true;
532
533 if ((sym.mod & KMOD_CTRL) != 0) {
534 if (sym.mod & KMOD_SHIFT)
535 redo();
536 else
537 undo();
538 return true;
539 }
540 break;
541 case SDLK_r:
542 if (!Map->loaded())
543 return true;
544
545 if ((sym.mod & KMOD_ALT) != 0) {
546 _resize_map->show();
547 return true;
548 }
549
550 if ((sym.mod & KMOD_CTRL) != 0) {
551 redo();
552 return true;
553 }
554 break;
555 case SDLK_b: {
556 if (!Map->loaded())
557 return true;
558
559 BaseBrush *b = _tileset_dialog->getBrush();
560 if (b->size.is0()) {
561 delete b;
562 return true;
563 }
564 delete _brush;
565 _brush = b;
566 return true;
567 }
568 case SDLK_g: {
569 int mx, my;
570 SDL_GetMouseState(&mx, &my);
571 v2<int> window_pos(mx, my);
572 v2<int> base(map_pos.x, map_pos.y);
573 v2<int> pixel_pos = base + window_pos;
574 v2<int> tile_pos = pixel_pos / _tile_size;
575 //mrt::format_string("%d,%d", tile_pos.x, tile_pos.y).c_str());
576 _layer_name = mrt::format_string("grid_position: %d,%d, pixel position: %d,%d", tile_pos.x, tile_pos.y, pixel_pos.x, pixel_pos.y);
577 _layer_name_invisible.reset();
578
579 return true;
580 }
581 case SDLK_f: {
582 //LOG_DEBUG(("f"));
583 if (!Map->loaded())
584 return true;
585
586 Layer * l = Map->getLayer(_current_layer_z);
587 if (!l->isVisible())
588 return true;
589
590 Brush *p_brush = dynamic_cast<Brush *>(_brush);
591 if (p_brush) {
592 //LOG_DEBUG(("tile pos: %d %d", x / _tile_size.x, y / _tile_size.y));
593 //_last_tile_painted = v2<int>((map_pos.x + x)/ _tile_size.x, (map_pos.y + y) / _tile_size.y);
594 int x, y;
595 SDL_GetMouseState(&x, &y);
596
597 v2<int> pos((map_pos.x + x)/ _tile_size.x, (map_pos.y + y) / _tile_size.y);
598 {
599 Command cmd(l);
600 addCommand(cmd);
601 }
602 FillerBrush brush(*p_brush, Map->get_size() / Map->getTileSize());
603 brush.exec(currentCommand(), pos.x, pos.y );
604 currentCommand().exec();
605 } else
606 LOG_DEBUG(("_brush is not Brush. skip fill"));
607 return true;
608 }
609 case SDLK_e:
610 if (!Map->loaded())
611 return true;
612
613 LOG_DEBUG(("eraser"));
614 delete _brush;
615 _brush = new Eraser(_tile_size);
616 return true;
617 case SDLK_m:
618 if (!Map->loaded())
619 return true;
620
621 _morph_dialog->hide(!_morph_dialog->hidden());
622 return true;
623 case SDLK_o:
624 if (sym.mod & KMOD_ALT) {
625 _map_picker->hide(false);
626 } else {
627 _render_objects = !_render_objects;
628 }
629 return true;
630 case SDLK_s:
631 if (sym.mod & KMOD_ALT && Map->loaded()) {
632 std::string result;
633 Map->generateXML(result);
634 //LOG_DEBUG(("map returned:\n%s", result.c_str()));
635 try {
636 mrt::Directory dir;
637 dir.create(_map_base + "/maps", true);
638 } catch(...) {}
639 mrt::File f;
640 std::string fname = _map_base + "/maps/" + _map_file + ".tmx";
641 f.open(fname, "wb");
642 LOG_DEBUG(("saving map to %s...", fname.c_str()));
643 f.write_all(result);
644 f.close();
645 return true;
646 } break;
647 case SDLK_TAB:
648 if (!Map->loaded())
649 return true;
650
651 _tileset_dialog->hide(false);
652 return true;
653 //if (!_tileset_dialog->hidden() || !_map_picker->hidden() || !Map->loaded())
654 // return false;
655 //_layers_dialog->hide(false);
656 case SDLK_LSHIFT:
657 case SDLK_RSHIFT:
658 _layer_invisible.reset();
659 return true;
660 case SDLK_DELETE:
661 if (_highlight_object) {
662 Command cmd(Command::DeleteObject, _highlight_object);
663 addCommand(cmd);
664 currentCommand().exec();
665
666 _highlight_object = NULL;
667 }
668 return true;
669
670 case SDLK_RIGHTBRACKET:
671 dir = -2;
672 case SDLK_LEFTBRACKET:
673 ++dir;
674 if (_highlight_object == NULL)
675 return false;
676 {
677 int dirs = _highlight_object->get_directions_number();
678 if (dirs != 8 && dirs != 16)
679 return false;
680
681 Command cmd(Command::RotateObject, _highlight_object, dir);
682 addCommand(cmd);
683 currentCommand().exec();
684 displayStatusMessage(_highlight_object);
685 }
686 return true;
687 case SDLK_KP_MINUS:
688 case SDLK_MINUS:
689 dir = -20;
690 case SDLK_PLUS:
691 case SDLK_KP_PLUS:
692 dir += 10;
693 if (_highlight_object == NULL)
694 return false;
695 {
696 Command cmd(Command::ChangeObjectProperties, _highlight_object, _highlight_object->get_z() + dir, Variants(_highlight_object->get_variants()));
697 addCommand(cmd);
698 currentCommand().exec();
699 displayStatusMessage(_highlight_object);
700 }
701 return true;
702
703 default:
704 return false;
705 }
706 return false;
707 }
708
displayStatusMessage(const Object * object)709 void Editor::displayStatusMessage(const Object *object) {
710 std::string prop;
711 try {
712 GameItem &item = GameMonitor->find(_highlight_object);
713 prop = " (" + item.property + ")";
714 } catch(...) {}
715 _layer_name = mrt::format_string("%s:%s%s, z: %d, dir: %d%s",
716 object->registered_name.c_str(), object->animation.c_str(), object->get_variants().dump().c_str(), object->get_z(), object->get_direction(), prop.c_str());
717 _layer_name_invisible.reset();
718 }
719
onMouse(const int button,const bool pressed,const int x,const int y)720 bool Editor::onMouse(const int button, const bool pressed, const int x, const int y) {
721 if (Container::onMouse(button, pressed, x, y))
722 return true;
723
724 if (!Map->loaded()) {
725 return false;
726 }
727
728 static const Uint8 *keys = SDL_GetKeyState(0);
729 if (keys[SDLK_LSHIFT] != 0) {
730
731 if (button == SDL_BUTTON_WHEELUP) {
732 if (pressed)
733 _layers_dialog->up();
734 } else if (button == SDL_BUTTON_WHEELDOWN) {
735 if (pressed)
736 _layers_dialog->down();
737 }
738
739 return true;
740 } else if (keys[SDLK_SPACE] != 0) {
741 if (button == SDL_BUTTON_RIGHT) {
742 if (!pressed)
743 return true;
744
745 const sdlx::Rect window_size = Window->get_size();
746
747 map_pos.x += x - window_size.w / 2;
748 map_pos.y += y - window_size.h / 2;
749
750 }
751 return true;
752 } else {
753 if (button == SDL_BUTTON_WHEELUP) {
754 if (pressed)
755 map_pos.y -= 128;
756 return true;
757 } else if (button == SDL_BUTTON_WHEELDOWN) {
758 if (pressed)
759 map_pos.y += 128;
760 return true;
761 }
762
763
764 }
765
766 if (pressed && button == SDL_BUTTON_LEFT && _brush) {
767 if (_layers_dialog->empty()) {
768 _layer_name = "add new layer with shift-n";
769 _layer_name_invisible.reset();
770 return true;
771 }
772 //LOG_DEBUG(("click"));
773 ObjectBrush *object_brush = dynamic_cast<ObjectBrush *>(_brush);
774 if (object_brush != NULL) {
775 Command cmd(object_brush->classname, object_brush->animation,
776 v2<int>(map_pos.x + x - _brush->size.x / 2, map_pos.y + y - _brush->size.y / 2), object_brush->z);
777 addCommand(cmd);
778 currentCommand().exec();
779 return true;
780 }
781 Layer * l = Map->getLayer(_current_layer_z);
782 if (l->isVisible()) {
783 //LOG_DEBUG(("tile pos: %d %d", x / _tile_size.x, y / _tile_size.y));
784 _last_tile_painted = v2<int>((map_pos.x + x)/ _tile_size.x, (map_pos.y + y) / _tile_size.y);
785 Command cmd(l);
786 addCommand(cmd);
787 _brush->exec(currentCommand(), _last_tile_painted.x, _last_tile_painted.y );
788 }
789 return true;
790 }
791
792 if (button == SDL_BUTTON_LEFT && pressed && _highlight_object) {
793 _dragging = true;
794 LOG_DEBUG(("dragging object..."));
795 Command cmd(Command::MoveObject, _highlight_object);
796 addCommand(cmd);
797
798 Object *object = currentCommand().getObject();
799 assert(object != NULL);
800 v2<int> pos;
801 object->get_position(pos);
802 currentCommand().save(pos.x, pos.y);
803 return true;
804 }
805
806 if (button == SDL_BUTTON_LEFT && !pressed && _dragging) {
807 _dragging = false;
808 LOG_DEBUG(("dropping object..."));
809 v2<int> pos;
810 currentCommand().getObject()->get_position(pos);
811 currentCommand().move(pos.x, pos.y);
812 currentCommand().exec();
813 return true;
814 }
815
816 if (button == SDL_BUTTON_RIGHT && !pressed && _highlight_object) {
817 _object_properties->set_base(x, y);
818 std::set<std::string> variants;
819 _add_object->get_variants(variants, _highlight_object->registered_name);
820 _object_properties->show(_highlight_object, variants);
821 return true;
822 }
823
824 if (button == SDL_BUTTON_RIGHT && pressed && !_selecting && _highlight_object == NULL) {
825 _selecting = true;
826 LOG_DEBUG(("start selection"));
827 _selection1 = _selection2 = v2<int>(x + map_pos.x, y + map_pos.y);
828 return true;
829 }
830
831 if (button == SDL_BUTTON_RIGHT && !pressed && _selecting) {
832 _selecting = false;
833 if (_selection1.x > _selection2.x)
834 math::exchange(_selection1.x, _selection2.x);
835 if (_selection1.y > _selection2.y)
836 math::exchange(_selection1.y, _selection2.y);
837 v2<int> size = _selection2 - _selection1 + 1;
838 LOG_DEBUG(("selection: %d,%d %dx%d", _selection1.x, _selection1.y, size.x, size.y));
839 return true;
840 }
841
842 return false;
843 }
844
845
onMouseMotion(const int state,const int x,const int y,const int xrel,const int yrel)846 bool Editor::onMouseMotion(const int state, const int x, const int y, const int xrel, const int yrel) {
847 if (Container::onMouseMotion(state, x, y, xrel, yrel))
848 return true;
849 const bool lmb = (state & SDL_BUTTON_LMASK) != 0;
850
851 if (_render_objects && _brush == NULL && state == 0 && _add_object->hidden()) {
852 //LOG_DEBUG(("mouse %d %d", x, y));
853 _highlight_object = World->getObjectByXY((int)map_pos.x + x, (int)map_pos.y + y);
854 if (_highlight_object) {
855 displayStatusMessage(_highlight_object);
856 }
857 }
858
859 static const Uint8 *keys = SDL_GetKeyState(0);
860
861 if (lmb && _brush != NULL && !_brush->size.is0() && keys[SDLK_SPACE] == 0 && !undo_queue.empty()) { //no space
862 v2<int> tile_pos ((map_pos.x + x)/ _tile_size.x, (map_pos.y + y) / _tile_size.y);
863 v2<int> dpos = tile_pos - _last_tile_painted;
864 v2<int> shift (math::abs(dpos.x), math::abs(dpos.y));
865 if (shift.x > shift.y)
866 shift.y = 0;
867 else
868 shift.x = 0;
869
870 if (shift.x >= _brush->size.x || shift.y >= _brush->size.y) {
871 dpos.x = math::sign(dpos.x);
872 dpos.y = math::sign(dpos.y);
873 do {
874 _last_tile_painted += shift * dpos;
875 //LOG_DEBUG(("last tile: %d %d", _last_tile_painted.x, _last_tile_painted.y ));
876 _brush->exec(currentCommand(), _last_tile_painted.x, _last_tile_painted.y );
877 shift -= _brush->size;
878 } while(shift.x >= 0 && shift.y >= 0);
879 //LOG_DEBUG(("draw line: tile_pos: %d %d", tile_pos.x, tile_pos.y));
880 }
881 }
882 if (_dragging) {
883 v2<int> pos;
884 currentCommand().getObject()->get_position(pos);
885 pos.x += xrel; pos.y += yrel;
886
887 World->move(currentCommand().getObject(), pos.x, pos.y);
888 }
889 if (_selecting) {
890 _selection2 = v2<int>(map_pos.x + x, map_pos.y + y);
891 }
892
893 if (keys[SDLK_SPACE] == 0 || !lmb || !Map->loaded())
894 return false;
895
896 map_pos.x -= xrel;
897 map_pos.y -= yrel;
898
899 return true;
900 }
901
resetLoadingBar(const int total)902 void Editor::resetLoadingBar(const int total) {
903 _loading_bar_now = 0;
904 _loading_bar_total = total;
905 }
906
notifyLoadingBar(const int progress,const char * what)907 void Editor::notifyLoadingBar(const int progress, const char *what) {
908 GET_CONFIG_VALUE("hud.disable-loading-screen", bool, disable_bar, false);
909 if (disable_bar)
910 return;
911
912 float old_progress = 1.0 * _loading_bar_now / _loading_bar_total;
913 _loading_bar_now += progress;
914
915 if (_hud->renderLoadingBar(Window->get_surface(), old_progress, 1.0 * _loading_bar_now / _loading_bar_total, NULL, false)) {
916 Window->flip();
917 Window->get_surface().fill(Window->get_surface().map_rgb(255, 255, 255));
918 }
919 }
920
updateLayers()921 void Editor::updateLayers() {
922 if (!_layers_dialog->empty()) {
923 const LayerItem *li = _layers_dialog->getCurrentItem();
924 _current_layer_z = li->z;
925 _layer_name = li->layer->name;
926 _layer_name_invisible.reset();
927 _layer_invisible.reset();
928 }
929 }
930
931
onKeySignal(const SDL_keysym sym,const bool pressed)932 bool Editor::onKeySignal(const SDL_keysym sym, const bool pressed) {
933 if (pressed && onKey(sym))
934 return true;
935 return false;
936 }
937
onMouseSignal(const int button,const bool pressed,const int x,const int y)938 bool Editor::onMouseSignal(const int button, const bool pressed, const int x, const int y) {
939 return onMouse(button, pressed, x, y);
940 }
941
onMouseMotionSignal(const int state,const int x,const int y,const int xrel,const int yrel)942 bool Editor::onMouseMotionSignal(const int state, const int x, const int y, const int xrel, const int yrel) {
943 return onMouseMotion(state, x, y, xrel, yrel);
944 }
945
onEvent(const SDL_Event & event)946 void Editor::onEvent(const SDL_Event &event) {
947 if (event.type == SDL_QUIT)
948 Window->stop();
949 }
950
951
952 #ifdef _WINDOWS
953 # include "sdlx/SDL_main.h"
954 # define WIN32_LEAN_AND_MEAN
955 # include <windows.h>
956 #endif
957
958 #include "sdlx/system.h"
959
960 #ifdef __cplusplus
961 extern "C"
962 #endif
main(int argc,char * argv[])963 int main(int argc, char *argv[]) {
964 try {
965 LOG_DEBUG(("bt editor"));
966 Game->loadPlugins();
967
968 Editor editor;
969 editor.init(argc, argv);
970 editor.run();
971 editor.deinit();
972 #ifdef _WINDOWS
973 } catch(const std::exception &e) {
974 sdlx::System::deinit();
975 LOG_ERROR(("main:%s", e.what()));
976 MessageBox(NULL, e.what(), "Error", MB_OK | MB_ICONERROR | MB_TASKMODAL);
977 return 1;
978 }
979 #else
980 } CATCH("main", {
981 sdlx::System::deinit();
982 return 1;
983 })
984 #endif
985
986 sdlx::System::deinit();
987 return 0;
988 }
989
990
991 #include "mrt/directory.h"
992 #include <string>
993
994 #ifdef _WINDOWS
995 extern "C" {
996 #ifdef _WIN32_WCE
997 int WINAPI SDLWinMain(HINSTANCE hInst, HINSTANCE hPrev, LPWSTR szCmdLine, int sw);
WinMain(HINSTANCE hInst,HINSTANCE hPrev,LPWSTR szCmdLine,int sw)998 int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPWSTR szCmdLine, int sw)
999 #else
1000 int WINAPI SDLWinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw);
1001 int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
1002 #endif
1003 {
1004 std::string logpath = mrt::format_string("SDL_LOG_PATH=%s", mrt::Directory::get_app_dir("Battle Tanks", "btanks").c_str());
1005 _putenv(_strdup(logpath.c_str()));
1006 return SDLWinMain(hInst, hPrev, szCmdLine, sw);
1007 }
1008 }
1009 #endif
1010