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