1 /*
2  *  This file is part of Dune Legacy.
3  *
4  *  Dune Legacy 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 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  Dune Legacy 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 Dune Legacy.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #ifndef RADARVIEWBASE_H
19 #define RADARVIEWBASE_H
20 
21 #include <GUI/Widget.h>
22 
23 #include <DataTypes.h>
24 
25 #include <SDL.h>
26 #include <functional>
27 
28 
29 #define NUM_STATIC_FRAMES 21
30 #define NUM_STATIC_FRAME_TIME 5
31 
32 #define RADARVIEW_BORDERTHICKNESS 2
33 #define RADARWIDTH 128
34 #define RADARHEIGHT 128
35 
36 
37 /// This class manages the mini map at the top right corner of the screen
38 class RadarViewBase : public Widget
39 {
40 public:
41     /**
42         Constructor
43     */
RadarViewBase()44     RadarViewBase() : Widget(), bRadarInteraction(false) {
45         resize(RADARWIDTH + (2 * RADARVIEW_BORDERTHICKNESS), RADARHEIGHT + (2 * RADARVIEW_BORDERTHICKNESS));
46     }
47 
48     /**
49         Destructor
50     */
~RadarViewBase()51     virtual ~RadarViewBase() {
52 
53     }
54 
55     /**
56         Get the map size in x direction
57         \return map width
58     */
59     virtual int getMapSizeX() const = 0;
60 
61     /**
62         Get the map size in y direction
63         \return map height
64     */
65     virtual int getMapSizeY() const = 0;
66 
67 
68     /**
69         Draws the radar to screen. This method is called before drawOverlay().
70         \param  Position    Position to draw the radar to
71     */
draw(Point position)72     virtual inline void draw(Point position) { ; };
73 
74 
75     /**
76         This method checks if position is inside the radar view
77         \param mouseX the x-coordinate to check (relative to the top left corner of the radar)
78         \param mouseY the y-coordinate to check (relative to the top left corner of the radar)
79         \return true, if inside the radar view; false otherwise
80     */
isOnRadar(int mouseX,int mouseY)81     bool isOnRadar(int mouseX, int mouseY) const {
82         int scale = 1;
83         int offsetX = 0;
84         int offsetY = 0;
85 
86         calculateScaleAndOffsets(getMapSizeX(), getMapSizeY(), scale, offsetX, offsetY);
87 
88         int offsetFromRightX = 128 - getMapSizeX()*scale - offsetX;
89         int offsetFromBottomY = 128 - getMapSizeY()*scale - offsetY;
90 
91         return ((mouseX >= offsetX + RADARVIEW_BORDERTHICKNESS)
92                 && (mouseX < RADARWIDTH - offsetFromRightX + RADARVIEW_BORDERTHICKNESS)
93                 && (mouseY >= offsetY + RADARVIEW_BORDERTHICKNESS)
94                 && (mouseY < RADARHEIGHT - offsetFromBottomY + RADARVIEW_BORDERTHICKNESS) );
95     }
96 
97     /**
98         This method returns the corresponding world coordinates for a point on the radar
99         \param mouseX  the position on the radar screen (relative to the top left corner of the radar)
100         \param mouseY  the position on the radar screen (relative to the top left corner of the radar)
101         \return the world coordinates
102     */
getWorldCoords(int mouseX,int mouseY)103     Coord getWorldCoords(int mouseX, int mouseY) const {
104         Coord positionOnRadar(mouseX - RADARVIEW_BORDERTHICKNESS, mouseY - RADARVIEW_BORDERTHICKNESS);
105 
106         int scale = 1;
107         int offsetX = 0;
108         int offsetY = 0;
109 
110         calculateScaleAndOffsets(getMapSizeX(), getMapSizeY(), scale, offsetX, offsetY);
111 
112         return Coord( ((positionOnRadar.x - offsetX) * getMapSizeX() * TILESIZE) / (getMapSizeX() * scale),
113                       ((positionOnRadar.y - offsetY) * getMapSizeY() * TILESIZE) / (getMapSizeY() * scale));
114     }
115 
116 
117     /**
118         This method calculates the scale and the offsets that are neccessary to show a minimap centered inside a 128x128 rectangle.
119         \param  MapSizeX    The width of the map in tiles
120         \param  MapSizeY    The height of the map in tiles
121         \param  scale       The scale factor is saved here
122         \param  offsetX     The offset in x direction is saved here
123         \param  offsetY     The offset in y direction is saved here
124     */
calculateScaleAndOffsets(int MapSizeX,int MapSizeY,int & scale,int & offsetX,int & offsetY)125     static void calculateScaleAndOffsets(int MapSizeX, int MapSizeY, int& scale, int& offsetX, int& offsetY) {
126         scale = 1;
127         offsetX = 0;
128         offsetY = 0;
129 
130         if(MapSizeX <= 32 && MapSizeY <= 32) {
131             scale*=2;
132         }
133 
134         if(MapSizeX <= 64 && MapSizeY <= 64) {
135             scale*=2;
136         }
137 
138         if(MapSizeX <= 21 && MapSizeY <= 21) {
139             scale++;
140         }
141 
142         offsetX = (128 - (MapSizeX*scale))/2;
143         offsetY = (128 - (MapSizeY*scale))/2;
144     }
145 
146 
147     /**
148         Returns the minimum size of this widget. The widget should not
149         resized to a size smaller than this. If the widget is not resizeable
150         in a direction this method returns the size in that direction.
151         \return the minimum size of this widget
152     */
getMinimumSize()153     virtual Point getMinimumSize() const { return Point(RADARWIDTH + (2 * RADARVIEW_BORDERTHICKNESS),RADARHEIGHT + (2 * RADARVIEW_BORDERTHICKNESS)); };
154 
155 
156     /**
157         Handles a mouse movement.
158         \param  x               x-coordinate (relative to the left top corner of the widget)
159         \param  y               y-coordinate (relative to the left top corner of the widget)
160         \param  insideOverlay   true, if (x,y) is inside an overlay and this widget may be behind it, false otherwise
161     */
handleMouseMovement(Sint32 x,Sint32 y,bool insideOverlay)162     virtual void handleMouseMovement(Sint32 x, Sint32 y, bool insideOverlay) {
163         if(bRadarInteraction && isOnRadar(x,y)) {
164             if(pOnRadarClick) {
165                 bRadarInteraction = pOnRadarClick(getWorldCoords(x,y), false, true);
166             }
167         }
168     }
169 
170 
171     /**
172         Handles a left mouse click.
173         \param  x x-coordinate (relative to the left top corner of the widget)
174         \param  y y-coordinate (relative to the left top corner of the widget)
175         \param  pressed true = mouse button pressed, false = mouse button released
176         \return true = click was processed by the widget, false = click was not processed by the widget
177     */
handleMouseLeft(Sint32 x,Sint32 y,bool pressed)178     virtual bool handleMouseLeft(Sint32 x, Sint32 y, bool pressed) {
179         if(pressed) {
180             if(isOnRadar(x,y)) {
181                 if(pOnRadarClick) {
182                     bRadarInteraction = pOnRadarClick(getWorldCoords(x,y), false, false);
183                 }
184                 return true;
185             }
186             return false;
187         } else {
188             bRadarInteraction = false;
189             return false;
190         }
191     }
192 
193 
194     /**
195         Handles a right mouse click.
196         \param  x x-coordinate (relative to the left top corner of the widget)
197         \param  y y-coordinate (relative to the left top corner of the widget)
198         \param  pressed true = mouse button pressed, false = mouse button released
199         \return true = click was processed by the widget, false = click was not processed by the widget
200     */
handleMouseRight(Sint32 x,Sint32 y,bool pressed)201     virtual bool handleMouseRight(Sint32 x, Sint32 y, bool pressed) {
202         if(pressed) {
203             if(isOnRadar(x,y)) {
204                 if(pOnRadarClick) {
205                     bRadarInteraction = pOnRadarClick(getWorldCoords(x,y), true, false);
206                 }
207                 return true;
208             }
209             return false;
210         } else {
211             bRadarInteraction = false;
212             return false;
213         }
214     }
215 
216 
217     /**
218         Sets the function that should be called when the radar view is clicked.
219         \param  pOnRadarClick   A function to be called on click
220     */
setOnRadarClick(std::function<bool (Coord,bool,bool)> pOnRadarClick)221     inline void setOnRadarClick(std::function<bool (Coord,bool,bool)> pOnRadarClick) {
222         this->pOnRadarClick = pOnRadarClick;
223     }
224 
225 protected:
226     std::function<bool (Coord,bool,bool)> pOnRadarClick;  ///< this function is called when the user clicks on the radar (1st parameter is world coordinate; 2nd parameter is whether the right mouse button was pressed; 3rd parameter is whether the mouse was moved while being pressed, e.g. dragging; return value shall be true if dragging should start or continue)
227 
228     bool bRadarInteraction;                               ///< currently dragging on the radar? (e.g. moving the view rectangle on the radar)
229 };
230 
231 #endif // RADARVIEWBASE_H
232