1 //
2 // Copyright (C) 2009-2011 Nick Gasson
3 //
4 // This program 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 // 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, see <http://www.gnu.org/licenses/>.
16 //
17
18 #include "GameScreens.hpp"
19 #include "ILogger.hpp"
20 #include "IModel.hpp"
21 #include "IMap.hpp"
22 #include "Maths.hpp"
23 #include "ILight.hpp"
24 #include "ISceneryPicker.hpp"
25 #include "Random.hpp"
26 #include "IRenderStats.hpp"
27 #include "OpenGLHelper.hpp"
28
29 #include "gui/ILayout.hpp"
30 #include "gui/Label.hpp"
31
32 #include <algorithm>
33 #include <stdexcept>
34
35 // Concrete editor class
36 class Editor : public IScreen {
37 public:
38 Editor(IMapPtr a_map);
39 Editor(const string& a_map_name);
40 ~Editor();
41
42 void display(IGraphicsPtr a_context) const;
43 void overlay() const;
44 void update(IPickBufferPtr pick_buffer, int a_delta);
45 void on_key_down(SDLKey a_key);
46 void on_key_up(SDLKey a_key);
47 void on_mouse_move(IPickBufferPtr pick_buffer,
48 int x, int y, int xrel, int yrel);
49 void on_mouse_click(IPickBufferPtr pick_buffer, int x, int y,
50 MouseButton a_button);
51 void on_mouse_release(IPickBufferPtr pick_buffer, int x, int y,
52 MouseButton a_button);
53
54 // Different tools the user can be using
55 enum Tool {
56 TRACK_TOOL, RAISE_TOOL, LOWER_TOOL, DELETE_TOOL,
57 LEVEL_TOOL, START_TOOL, STATION_TOOL, BUILDING_TOOL,
58 TREE_TOOL, SMOOTH_TOOL
59 };
set_tool(Tool a_tool)60 void set_tool(Tool a_tool) { my_tool = a_tool; }
61
get_map()62 IMapPtr get_map() { return map; }
63 void set_map(IMapPtr a_map);
64
65 private:
66 void build_gui();
67 void draw_dragged_track();
68 bool draw_track_tile(PointI where, track::Direction axis);
69 void draw_dragged_straight(const track::Direction& axis, int length);
70 void draw_diagonal_straight(const track::Direction& axis, int length);
71 void draw_initial_track();
72 void draw_unconstrained_track(const track::Direction& start_dir);
73 void draw_constrained_track(const track::Direction& start_dir,
74 const track::Direction& end_dir);
75 void draw_curve(const track::Direction& entry_dir,
76 const track::Direction& exit_dir);
77 void draw_s_bend(const track::Direction& dir);
78 bool can_connect(const PointI& a_first_point,
79 const PointI& a_second_point) const;
80 bool can_place_track(ITrackSegmentPtr track);
81 bool guess_track_dir(const PointI& p, track::Direction& d) const;
82 void drag_box_bounds(int& x_min, int& x_max, int &y_min, int& y_max) const;
83 void drag_box_size(int& xlen, int& ylen) const;
84 void delete_objects();
85 void plant_trees();
86 void save();
87 bool is_diagonal(const track::Direction& dir) const;
88
89 IMapPtr map;
90
91 ILightPtr my_sun;
92 VectorF my_position;
93
94 Tool my_tool;
95 bool am_scrolling;
96
97 // Variables for dragging track segments
98 PointI drag_begin, drag_end;
99 bool am_dragging, is_shift_down;
100
101 // GUI elements
102 gui::ILayoutPtr layout;
103 ISceneryPickerPtr building_picker, tree_picker;
104 IRenderStatsPtr render_stats;
105 };
106
Editor(IMapPtr a_map)107 Editor::Editor(IMapPtr a_map)
108 : map(a_map), my_position(4.5f, -17.5f, -21.5f),
109 my_tool(TRACK_TOOL), am_scrolling(false), am_dragging(false),
110 is_shift_down(false)
111 {
112 my_sun = make_sun_light();
113
114 build_gui();
115
116 map->set_grid(true);
117
118 log() << "Editing " << a_map->name();
119 }
120
~Editor()121 Editor::~Editor()
122 {
123
124 }
125
build_gui()126 void Editor::build_gui()
127 {
128 using namespace placeholders;
129
130 layout = gui::make_layout("layouts/editor.xml");
131
132 layout->get("/tool_wnd/tools/track").connect(gui::Widget::SIG_CLICK,
133 bind(&Editor::set_tool, this, TRACK_TOOL));
134 layout->get("/tool_wnd/tools/raise").connect(gui::Widget::SIG_CLICK,
135 bind(&Editor::set_tool, this, RAISE_TOOL));
136 layout->get("/tool_wnd/tools/lower").connect(gui::Widget::SIG_CLICK,
137 bind(&Editor::set_tool, this, LOWER_TOOL));
138 layout->get("/tool_wnd/tools/level").connect(gui::Widget::SIG_CLICK,
139 bind(&Editor::set_tool, this, LEVEL_TOOL));
140 layout->get("/tool_wnd/tools/delete").connect(gui::Widget::SIG_CLICK,
141 bind(&Editor::set_tool, this, DELETE_TOOL));
142 layout->get("/tool_wnd/tools/start").connect(gui::Widget::SIG_CLICK,
143 bind(&Editor::set_tool, this, START_TOOL));
144 layout->get("/tool_wnd/tools/station").connect(gui::Widget::SIG_CLICK,
145 bind(&Editor::set_tool, this, STATION_TOOL));
146 layout->get("/tool_wnd/tools/building").connect(gui::Widget::SIG_CLICK,
147 bind(&Editor::set_tool, this, BUILDING_TOOL));
148 layout->get("/tool_wnd/tools/tree").connect(gui::Widget::SIG_CLICK,
149 bind(&Editor::set_tool, this, TREE_TOOL));
150 layout->get("/tool_wnd/tools/smooth").connect(gui::Widget::SIG_CLICK,
151 bind(&Editor::set_tool, this, SMOOTH_TOOL));
152
153 layout->get("/lower/action_wnd/save").connect(gui::Widget::SIG_CLICK,
154 bind(&Editor::save, this));
155
156 building_picker = make_building_picker(layout);
157 tree_picker = make_tree_picker(layout);
158
159 render_stats = make_render_stats(layout, "/fps/fps_label");
160 }
161
set_map(IMapPtr a_map)162 void Editor::set_map(IMapPtr a_map)
163 {
164 map = a_map;
165 map->set_grid(true);
166 }
167
save()168 void Editor::save()
169 {
170 map->save();
171 }
172
173 // Calculate the bounds of the drag box accounting for the different
174 // possible directions of dragging
drag_box_bounds(int & x_min,int & x_max,int & y_min,int & y_max) const175 void Editor::drag_box_bounds(int& x_min, int& x_max, int &y_min, int& y_max) const
176 {
177 x_min = min(drag_begin.x, drag_end.x);
178 x_max = max(drag_begin.x, drag_end.x);
179
180 y_min = min(drag_begin.y, drag_end.y);
181 y_max = max(drag_begin.y, drag_end.y);
182 }
183
drag_box_size(int & xlen,int & ylen) const184 void Editor::drag_box_size(int& xlen, int& ylen) const
185 {
186 int xmin, xmax, ymin, ymax;
187 drag_box_bounds(xmin, xmax, ymin, ymax);
188
189 xlen = abs(xmax - xmin) + 1;
190 ylen = abs(ymax - ymin) + 1;
191 }
192
193 // Render the next frame
display(IGraphicsPtr a_context) const194 void Editor::display(IGraphicsPtr a_context) const
195 {
196 if (!map)
197 return;
198
199 a_context->set_camera(my_position, make_vector(45.0f, 45.0f, 0.0f));
200
201 my_sun->apply();
202
203 // Draw the highlight if we are dragging track
204 if (am_dragging) {
205 int xmin, xmax, ymin, ymax;
206 drag_box_bounds(xmin, xmax, ymin, ymax);
207
208 for (int x = xmin; x <= xmax; x++) {
209 for (int y = ymin; y <= ymax; y++)
210 map->highlight_tile(make_point(x, y), colour::WHITE);
211 }
212 }
213
214 map->render(a_context);
215 }
216
217 // Render the overlay
overlay() const218 void Editor::overlay() const
219 {
220 layout->render();
221 }
222
223 // Prepare the next frame
update(IPickBufferPtr pick_buffer,int a_delta)224 void Editor::update(IPickBufferPtr pick_buffer, int a_delta)
225 {
226 render_stats->update(a_delta);
227 }
228
229 // True if the `a_first_point' is a valid track segment and it can
230 // connect to `a_second_point'
can_connect(const PointI & a_first_point,const PointI & a_second_point) const231 bool Editor::can_connect(const PointI& a_first_point,
232 const PointI& a_second_point) const
233 {
234 if (!map->is_valid_track(a_first_point))
235 return false;
236
237 ITrackSegmentPtr track = map->track_at(a_first_point);
238
239 Vector<int> dir = make_vector(
240 a_first_point.x - a_second_point.x,
241 0,
242 a_first_point.y - a_second_point.y).normalise();
243
244 return track->is_valid_direction(dir)
245 || track->is_valid_direction(-dir);
246 }
247
248 // Try to guess the direction of a track endpoint by looking at the
249 // surrounding tiles
guess_track_dir(const PointI & p,track::Direction & d) const250 bool Editor::guess_track_dir(const PointI& p, track::Direction& d) const
251 {
252 if (can_connect(p.left(), p)) {
253 d = axis::X;
254 }
255 else if (can_connect(p.right(), p)) {
256 d = -axis::X;
257 }
258 else if (can_connect(p.up(), p)) {
259 d = -axis::Y;
260 }
261 else if (can_connect(p.down(), p)) {
262 d = axis::Y;
263 }
264 else if (can_connect(p.up_left(), p)) {
265 d = -make_vector(-1, 0, 1);
266 }
267 else if (can_connect(p.up_right(), p)) {
268 d = -make_vector(1, 0, 1);
269 }
270 else if (can_connect(p.down_left(), p)) {
271 d = -make_vector(-1, 0, -1);
272 }
273 else if (can_connect(p.down_right(), p)) {
274 d = -make_vector(1, 0, -1);
275 }
276 else
277 return false;
278
279 return true;
280 }
281
282 // Draw a single tile of straight track and check for collisions
283 // Returns `false' if track cannot be placed here
draw_track_tile(PointI where,track::Direction axis)284 bool Editor::draw_track_tile(PointI where, track::Direction axis)
285 {
286 // Ensure axis is only in the positive direction
287 if (axis == -axis::X)
288 axis = axis::X;
289 else if (axis == -axis::Y)
290 axis = axis::Y;
291
292 if (map->is_valid_track(where)) {
293 ITrackSegmentPtr merged = map->track_at(where)->merge_exit(where, axis);
294 if (merged) {
295 map->set_track_at(where, merged);
296 return true;
297 }
298 else {
299 warn() << "Cannot merge track";
300 return false;
301 }
302 }
303 else {
304 bool level;
305 const VectorF slope = map->slope_at(where, axis, level);
306
307 bool b_valid, a_valid;
308 const VectorF slope_before = map->slope_before(where, axis, b_valid);
309 const VectorF slope_after = map->slope_after(where, axis, a_valid);
310
311 if (level) {
312 const bool flat =
313 abs(slope.y) < 0.001f
314 && (!b_valid || abs(slope_before.y) < 0.001f)
315 && (!a_valid || abs(slope_after.y) < 0.001);
316
317 if (flat) {
318 map->set_track_at(where, make_straight_track(axis));
319 return true;
320 }
321 else {
322 if (!b_valid || !a_valid) {
323 warn() << "Cannot place track here";
324 return false;
325 }
326 else {
327 debug() << "slope=" << slope
328 << " before=" << slope_before
329 << " after=" << slope_after;
330
331 map->set_track_at(
332 where,
333 make_slope_track(axis, slope, slope_before, slope_after));
334
335 return true;
336 }
337 }
338 }
339 else {
340 warn() << "Track must be placed on level ground";
341 return false;
342 }
343 }
344 }
345
346 // Special case where the user drags a rectangle of width 1
347 // This just draws straight track along the rectangle
draw_dragged_straight(const track::Direction & axis,int length)348 void Editor::draw_dragged_straight(const track::Direction& axis, int length)
349 {
350 PointI where = drag_begin;
351
352 for (int i = 0; i < length; i++) {
353 draw_track_tile(where, axis);
354
355 where.x += axis.x;
356 where.y += axis.z;
357 }
358 }
359
360 // The user draws a square and one of the corners meets a diagnonal
361 // track segment
draw_diagonal_straight(const track::Direction & axis,int length)362 void Editor::draw_diagonal_straight(const track::Direction& axis, int length)
363 {
364 PointI where = drag_begin;
365
366 for (int i = 0; i < length; i++) {
367 VectorI delta = make_vector(axis.x, axis.z, 0);
368 ITrackSegmentPtr track = make_spline_track(delta, axis, axis);
369 map->set_track_at(where, track);
370
371 where.x += axis.x;
372 where.y += axis.z;
373 }
374 }
375
376 // True if a track segment could be placed in its present location
can_place_track(ITrackSegmentPtr track)377 bool Editor::can_place_track(ITrackSegmentPtr track)
378 {
379 PointList covered;
380 track->get_endpoints(covered);
381 track->get_covers(covered);
382
383 for (auto it = covered.begin(); it != covered.end(); ++it) {
384 if (map->is_valid_track(*it)) {
385 warn() << "Cannot place track here";
386 return false;
387 }
388 }
389
390 return true;
391 }
392
is_diagonal(const track::Direction & dir) const393 bool Editor::is_diagonal(const track::Direction& dir) const
394 {
395 return !(dir == axis::X || dir == axis::Y
396 || dir == -axis::X || dir == -axis::Y);
397 }
398
399 // The direction of neither endpoint is known
draw_initial_track()400 void Editor::draw_initial_track()
401 {
402 debug() << __func__ << ": drag_begin=" << drag_begin
403 << " drag_end=" << drag_end;
404
405 int xmin, xmax, ymin, ymax, xlen, ylen;
406 drag_box_bounds(xmin, xmax, ymin, ymax);
407 drag_box_size(xlen, ylen);
408
409 if (xlen == 1)
410 draw_dragged_straight(drag_begin.y > drag_end.y ? -axis::Y : axis::Y,
411 ylen);
412 else if (ylen == 1)
413 draw_dragged_straight(drag_begin.x > drag_end.x ? -axis::X : axis::X,
414 xlen);
415 else if (is_shift_down && xlen == ylen)
416 warn() << "draw_diagonal_straight";
417 else
418 warn() << "cannot infer track";
419 }
420
draw_curve(const track::Direction & entry_dir,const track::Direction & exit_dir)421 void Editor::draw_curve(const track::Direction& entry_dir,
422 const track::Direction& exit_dir)
423 {
424 int xmin, xmax, ymin, ymax, xlen, ylen;
425 drag_box_bounds(xmin, xmax, ymin, ymax);
426 drag_box_size(xlen, ylen);
427
428 VectorI delta = make_vector(drag_end.x - drag_begin.x,
429 drag_end.y - drag_begin.y,
430 0);
431 ITrackSegmentPtr curve = make_spline_track(delta, entry_dir, exit_dir);
432 map->set_track_at(drag_begin, curve);
433 }
434
draw_s_bend(const track::Direction & dir)435 void Editor::draw_s_bend(const track::Direction& dir)
436 {
437 draw_curve(dir, dir);
438 }
439
440 // The direction of the start is known but not the end
draw_unconstrained_track(const track::Direction & start_dir)441 void Editor::draw_unconstrained_track(const track::Direction& start_dir)
442 {
443 debug() << __func__ << ": drag_begin=" << drag_begin
444 << " drag_end=" << drag_end << " start_dir=" << start_dir;
445
446 int xmin, xmax, ymin, ymax, xlen, ylen;
447 drag_box_bounds(xmin, xmax, ymin, ymax);
448 drag_box_size(xlen, ylen);
449
450 bool start_is_ortho = (start_dir == axis::X || start_dir == axis::Y
451 || start_dir == -axis::X || start_dir == -axis::Y);
452 bool could_be_curve = (xlen >= 3 && ylen >= 3);
453 bool could_be_90_curve = (start_is_ortho && could_be_curve
454 && !is_shift_down);
455 bool could_be_45_curve = (could_be_curve && xlen != ylen
456 && (!start_is_ortho || is_shift_down));
457 bool could_be_s_bend = (start_is_ortho && xlen != ylen && !is_shift_down);
458 bool could_be_straight = ((start_is_ortho && (xlen == 1 || ylen == 1))
459 || (!start_is_ortho && xlen == ylen
460 && !is_shift_down));
461
462 if (could_be_straight) {
463 if (start_is_ortho)
464 draw_dragged_straight(start_dir, xlen == 1 ? ylen : xlen);
465 else
466 draw_diagonal_straight(start_dir, xlen);
467 }
468 else if (could_be_90_curve) {
469
470 track::Direction exit_dir;
471 if (start_dir == axis::X || start_dir == -axis::X) {
472 if (drag_end.y < drag_begin.y)
473 exit_dir = -axis::Y;
474 else
475 exit_dir = axis::Y;
476 }
477 else if (start_dir == axis::Y || start_dir == -axis::Y) {
478 if (drag_end.x < drag_begin.x)
479 exit_dir = -axis::X;
480 else
481 exit_dir = axis::X;
482 }
483 else
484 assert(false);
485
486 // Draw straight track until we have a square
487 while (xlen > ylen) {
488 if (start_dir == axis::X || start_dir == -axis::X) {
489 draw_track_tile(drag_begin, axis::X);
490 drag_begin += make_point(start_dir.x, 0);
491 }
492 else if (start_dir == axis::Y || start_dir == -axis::Y) {
493 draw_track_tile(drag_end, axis::X);
494 drag_end -= make_point(exit_dir.x, 0);
495 }
496 xlen--;
497 }
498 while (ylen > xlen) {
499 if (start_dir == axis::Y || start_dir == -axis::Y) {
500 draw_track_tile(drag_begin, axis::Y);
501 drag_begin += make_point(0, start_dir.z);
502 }
503 else if (start_dir == axis::X || start_dir == -axis::X) {
504 draw_track_tile(drag_end, axis::Y);
505 drag_end -= make_point(0, exit_dir.z);
506 }
507 ylen--;
508 }
509
510 draw_curve(start_dir, exit_dir);
511 }
512 else if (could_be_45_curve) {
513
514 track::Direction exit_dir;
515 if (start_dir == axis::X || start_dir == -axis::X) {
516 if (drag_end.y < drag_begin.y)
517 exit_dir = make_vector(start_dir.x, 0, -1);
518 else
519 exit_dir = make_vector(start_dir.x, 0, 1);
520 }
521 else if (start_dir == axis::Y || start_dir == -axis::Y) {
522 if (drag_end.x < drag_begin.x)
523 exit_dir = make_vector(-1, 0, start_dir.z);
524 else
525 exit_dir = make_vector(1, 0, start_dir.z);
526 }
527 else
528 assert(false);
529
530 draw_curve(start_dir, exit_dir);
531 }
532 else if (could_be_s_bend) {
533 draw_s_bend(start_dir);
534 }
535 else
536 warn() << "cannot infer track";
537
538 }
539
540 // The direction of both endpoints is known
draw_constrained_track(const track::Direction & start_dir,const track::Direction & end_dir)541 void Editor::draw_constrained_track(const track::Direction& start_dir,
542 const track::Direction& end_dir)
543 {
544 debug() << __func__ << ": drag_begin=" << drag_begin
545 << " drag_end=" << drag_end << " start_dir=" << start_dir
546 << " end_dir=" << end_dir;
547
548 int xlen, ylen;
549 drag_box_size(xlen, ylen);
550
551 bool start_is_ortho = (start_dir == axis::X || start_dir == axis::Y
552 || start_dir == -axis::X || start_dir == -axis::Y);
553
554 bool is_straight = ((start_dir == end_dir || start_dir == -end_dir)
555 && ((start_is_ortho && (xlen == 1 || ylen == 1))
556 || (!start_is_ortho && xlen == ylen)));
557
558 if (is_straight) {
559 if (xlen == 1 || ylen == 1)
560 draw_dragged_straight(start_dir, max(xlen, ylen));
561 else
562 draw_diagonal_straight(start_dir, xlen);
563 }
564 else {
565 VectorI delta = make_vector(drag_end.x - drag_begin.x,
566 drag_end.y - drag_begin.y,
567 0);
568 ITrackSegmentPtr curve = make_spline_track(delta, start_dir, -end_dir);
569 map->set_track_at(drag_begin, curve);
570 }
571 }
572
573 // Called when the user has finished dragging a rectangle for track
574 // Connect the beginning and end up in the simplest way possible
draw_dragged_track()575 void Editor::draw_dragged_track()
576 {
577 track::Direction straight; // Orientation for straight track section
578
579 int xmin, xmax, ymin, ymax, xlen, ylen;
580 drag_box_bounds(xmin, xmax, ymin, ymax);
581 drag_box_size(xlen, ylen);
582
583 // Try to merge the start and end directly
584 const track::Direction merge_axis =
585 xlen > ylen ? (drag_begin.x < drag_end.x ? -axis::X : axis::X)
586 : (drag_begin.y < drag_end.y ? -axis::Y : axis::Y);
587 if (map->is_valid_track(drag_end)) {
588 ITrackSegmentPtr merged =
589 map->track_at(drag_end)->merge_exit(drag_begin, merge_axis);
590
591 if (merged) {
592 // Erase all the tiles covered
593 for (int x = xmin; x <= xmax; x++) {
594 for (int y = ymin; y <= ymax; y++)
595 map->erase_tile(x, y);
596 }
597
598 map->set_track_at(drag_end, merged);
599 return;
600 }
601 }
602
603 track::Direction start_dir, end_dir;
604 bool start_was_guess = !guess_track_dir(drag_begin, start_dir);
605 bool end_was_guess = !guess_track_dir(drag_end, end_dir);
606
607 if (start_was_guess) {
608 if (end_was_guess)
609 draw_initial_track();
610 else {
611 swap(drag_begin, drag_end);
612 draw_unconstrained_track(end_dir);
613 }
614 }
615 else {
616 if (end_was_guess)
617 draw_unconstrained_track(start_dir);
618 else
619 draw_constrained_track(start_dir, end_dir);
620 }
621 }
622
623 // Delete all objects in the area selected by the user
delete_objects()624 void Editor::delete_objects()
625 {
626 int xmin, xmax, ymin, ymax;
627 drag_box_bounds(xmin, xmax, ymin, ymax);
628
629 for (int x = xmin; x <= xmax; x++) {
630 for (int y = ymin; y <= ymax; y++)
631 map->erase_tile(x, y);
632 }
633 }
634
635 // Plant trees at random locations in the dragged region
plant_trees()636 void Editor::plant_trees()
637 {
638 int xmin, xmax, ymin, ymax;
639 drag_box_bounds(xmin, xmax, ymin, ymax);
640
641 const bool is_single_tile = (xmin == xmax) && (ymin == ymax);
642 const float threshold = 0.9f;
643
644 static Uniform<float> tree_rand(0.0f, 1.0f);
645
646 for (int x = xmin; x <= xmax; x++) {
647 for (int y = ymin; y <= ymax; y++) {
648 const PointI p = make_point(x, y);
649
650 if ((is_single_tile || tree_rand() > threshold) && map->empty_tile(p))
651 map->add_scenery(p, tree_picker->get());
652 }
653 }
654 }
655
on_mouse_move(IPickBufferPtr pick_buffer,int x,int y,int xrel,int yrel)656 void Editor::on_mouse_move(IPickBufferPtr pick_buffer, int x, int y,
657 int xrel, int yrel)
658 {
659 if (am_dragging) {
660 // Extend the selection rectangle
661 map->set_pick_mode(true);
662 IGraphicsPtr pick_context = pick_buffer->begin_pick(x, y);
663 display(pick_context);
664 int id = pick_buffer->end_pick();
665 map->set_pick_mode(false);
666
667 if (id > 0)
668 drag_end = map->pick_position(id);
669 }
670 else if (am_scrolling) {
671 const float speed = 0.05f;
672
673 const VectorF xrelv(-xrel * speed, 0.0f, -xrel * speed);
674 const VectorF yrelv(yrel * speed, 0.0f, -yrel * speed);
675
676 my_position += xrelv;
677 my_position += yrelv;
678 }
679 }
680
on_mouse_click(IPickBufferPtr pick_buffer,int x,int y,MouseButton a_button)681 void Editor::on_mouse_click(IPickBufferPtr pick_buffer, int x, int y,
682 MouseButton a_button)
683 {
684 if (a_button == MOUSE_RIGHT) {
685 // Start scrolling
686 am_scrolling = true;
687 }
688 else if (a_button == MOUSE_LEFT) {
689 bool clicked_onGUI = layout->click(x, y);
690
691 if (!clicked_onGUI) {
692 // See if the user clicked on something in the map
693 map->set_pick_mode(true);
694 IGraphicsPtr pick_context = pick_buffer->begin_pick(x, y);
695 display(pick_context);
696 int id = pick_buffer->end_pick();
697 map->set_pick_mode(false);
698
699 if (id > 0) {
700 // Begin dragging a selection rectangle
701 PointI where = map->pick_position(id);
702
703 drag_begin = drag_end = where;
704 am_dragging = true;
705 }
706 }
707 }
708 else if (a_button == MOUSE_WHEEL_UP) {
709 my_position -= VectorF(0.0f, 0.5f, 0.0f);
710 }
711 else if (a_button == MOUSE_WHEEL_DOWN) {
712 my_position += VectorF(0.0f, 0.5f, 0.0f);
713 }
714 }
715
on_mouse_release(IPickBufferPtr pick_buffer,int x,int y,MouseButton a_button)716 void Editor::on_mouse_release(IPickBufferPtr pick_buffer, int x, int y,
717 MouseButton a_button)
718 {
719 if (am_dragging) {
720 // Stop dragging and perform the action
721 switch (my_tool) {
722 case TRACK_TOOL:
723 draw_dragged_track();
724 break;
725 case RAISE_TOOL:
726 map->raise_area(drag_begin, drag_end);
727 break;
728 case LOWER_TOOL:
729 map->lower_area(drag_begin, drag_end);
730 break;
731 case LEVEL_TOOL:
732 map->level_area(drag_begin, drag_end);
733 break;
734 case DELETE_TOOL:
735 delete_objects();
736 break;
737 case START_TOOL:
738 map->set_start(drag_begin.x, drag_begin.y);
739 break;
740 case STATION_TOOL:
741 map->extend_station(drag_begin, drag_end);
742 break;
743 case BUILDING_TOOL:
744 map->add_scenery(drag_begin, building_picker->get());
745 break;
746 case TREE_TOOL:
747 plant_trees();
748 break;
749 case SMOOTH_TOOL:
750 map->smooth_area(drag_begin, drag_end);
751 break;
752 }
753
754 am_dragging = false;
755 }
756 else if (am_scrolling) {
757 am_scrolling = false;
758 }
759 }
760
on_key_up(SDLKey key)761 void Editor::on_key_up(SDLKey key)
762 {
763 switch (key) {
764 case SDLK_LSHIFT:
765 case SDLK_RSHIFT:
766 is_shift_down = false;
767 break;
768 default:
769 break;
770 }
771 }
772
on_key_down(SDLKey a_key)773 void Editor::on_key_down(SDLKey a_key)
774 {
775 switch (a_key) {
776 case SDLK_g:
777 // Toggle grid
778 map->set_grid(true);
779 break;
780 case SDLK_LSHIFT:
781 case SDLK_RSHIFT:
782 is_shift_down = true;
783 break;
784 case SDLK_PRINT:
785 get_game_window()->take_screen_shot();
786 break;
787 default:
788 break;
789 }
790 }
791
792 // Create an instance of the editor screen
make_editor_screen(IMapPtr a_map)793 IScreenPtr make_editor_screen(IMapPtr a_map)
794 {
795 return IScreenPtr(new Editor(a_map));
796 }
797