1 // Crimson Fields -- a game of tactical warfare
2 // Copyright (C) 2000-2007 Jens Granseuer
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 2 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, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 //
18
19 ////////////////////////////////////////////////////////////////////////
20 // mapview.cpp
21 ////////////////////////////////////////////////////////////////////////
22
23 #include "mapview.h"
24
25 ////////////////////////////////////////////////////////////////////////
26 // NAME : MapView::MapView
27 // DESCRIPTION: Partly set up a map viewport. To be usable, SetMap() and
28 // Enable() must be called afterwards.
29 // PARAMETERS : display - surface on which to display the map
30 // bounds - viewport position and size on surface
31 // flags - viewport flags (see mapview.h for details)
32 // RETURNS : -
33 ////////////////////////////////////////////////////////////////////////
34
MapView(Surface * display,const Rect & bounds,unsigned short flags)35 MapView::MapView( Surface *display, const Rect &bounds, unsigned short flags ) :
36 Rect( bounds ) {
37 surface = display;
38
39 map = NULL;
40 shader_map = NULL;
41
42 SetFlags( flags );
43 }
44
45 ////////////////////////////////////////////////////////////////////////
46 // NAME : MapView::InitOffsets
47 // DESCRIPTION: Set offsets for map display and scrolling. Call after
48 // having set a new map for the viewport.
49 // PARAMETERS : -
50 // RETURNS : -
51 ////////////////////////////////////////////////////////////////////////
52
InitOffsets(void)53 void MapView::InitOffsets( void ) {
54 maxx = MAX( 0, map->Width() * (TileWidth() - TileShiftX()) + TileShiftX() - w );
55 maxy = MAX( 0, map->Height() * TileHeight() + TileShiftY() - h );
56 }
57
58 ////////////////////////////////////////////////////////////////////////
59 // NAME : MapView::Resize
60 // DESCRIPTION: Set a new viewport size and position.
61 // PARAMETERS : bounds - new viewport size and position
62 // RETURNS : -
63 ////////////////////////////////////////////////////////////////////////
64
Resize(const Rect & bounds)65 void MapView::Resize( const Rect &bounds ) {
66 x = bounds.x;
67 y = bounds.y;
68 w = bounds.w;
69 h = bounds.h;
70 InitOffsets();
71
72 if ( curx > maxx ) curx = maxx;
73 if ( cury > maxy ) cury = maxy;
74
75 if ( HexVisible( cursor ) ) Draw();
76 else CenterOnHex( cursor );
77 }
78
79 ////////////////////////////////////////////////////////////////////////
80 // NAME : MapView::SetMap
81 // DESCRIPTION: Assign another map to this viewport.
82 // PARAMETERS : map - map to display; the map must have already had
83 // a level set assigned
84 // RETURNS : -
85 ////////////////////////////////////////////////////////////////////////
86
SetMap(Map * map)87 void MapView::SetMap( Map *map ) {
88 if ( shader_map ) delete [] shader_map;
89
90 cursor = Point( -1, -1 );
91 cursor_image = IMG_CURSOR_IDLE;
92 this->map = map;
93
94 InitOffsets();
95 shader_map = new signed char [map->Width() * map->Height()];
96 }
97
98 ////////////////////////////////////////////////////////////////////////
99 // NAME : MapView::Enable
100 // DESCRIPTION: Set map offsets back to 0 and enable map display.
101 // PARAMETERS : -
102 // RETURNS : -
103 ////////////////////////////////////////////////////////////////////////
104
Enable(void)105 void MapView::Enable( void ) {
106 curx = cury = 0;
107 UnsetFlags( MV_DISABLE );
108 }
109
110 ////////////////////////////////////////////////////////////////////////
111 // NAME : MapView::Disable
112 // DESCRIPTION: Hide the actual map and display just blackness.
113 // PARAMETERS : -
114 // RETURNS : -
115 ////////////////////////////////////////////////////////////////////////
116
Disable(void)117 void MapView::Disable( void ) {
118 SetFlags( MV_DISABLE );
119 }
120
121 ////////////////////////////////////////////////////////////////////////
122 // NAME : MapView::Draw
123 // DESCRIPTION: Draw a part of the map onto the window surface.
124 // PARAMETERS : x - of the viewport (!) area to update
125 // y - of the viewport area to update
126 // w - width of the update area
127 // h - height of the update area
128 // RETURNS : -
129 ////////////////////////////////////////////////////////////////////////
130
Draw(short x,short y,unsigned short w,unsigned short h) const131 void MapView::Draw( short x, short y, unsigned short w, unsigned short h ) const {
132 surface->FillRect( *this, Color(CF_COLOR_SHADOW) );
133 if ( Enabled() ) {
134 DrawMap( curx + x, cury + y, w, h, surface, x + this->x, y + this->y );
135 }
136 }
137
138 ////////////////////////////////////////////////////////////////////////
139 // NAME : MapView::DrawMap
140 // DESCRIPTION: Draw a part of the map onto a surface.
141 // PARAMETERS : x - leftmost pixel of the map (!) to paint
142 // y - topmost pixel to paint
143 // w - width of the map part to draw
144 // h - height of the map part
145 // dest - destination surface
146 // dx - where to start drawing on the surface
147 // dy - where to start drawing vertically
148 // RETURNS : -
149 ////////////////////////////////////////////////////////////////////////
150
DrawMap(short x,short y,unsigned short w,unsigned short h,Surface * dest,short dx,short dy) const151 void MapView::DrawMap( short x, short y, unsigned short w,
152 unsigned short h, Surface *dest, short dx, short dy ) const {
153 int hx1 = MinXHex( x );
154 int hy1 = MinYHex( y );
155 int hx2 = MaxXHex( x, w );
156 int hy2 = MaxYHex( y, h );
157
158 Rect clip( dx, dy, w, h );
159 int sx, sy, tx, ty, yoff;
160 Point hex;
161
162 for ( tx = hx1; tx <= hx2; ++tx ) {
163 sx = tx * (TileWidth() - TileShiftX()) - x + this->x;
164
165 if ( tx & 1 ) yoff = TileShiftY() - y;
166 else yoff = -y;
167 yoff += this->y;
168
169 for ( ty = hy1; ty <= hy2; ++ty ) {
170 sy = ty * TileHeight() + yoff;
171 hex = Point( tx, ty );
172
173 // draw the terrain image
174 DrawTerrain( map->HexImage( hex ), dest, sx, sy, clip );
175
176 // draw unit
177 if ( Unit *u = map->GetUnit( hex ) ) {
178 DrawUnit( u->Image(), dest, sx, sy, clip );
179 if ( !u->IsReady() ) DrawTerrain( IMG_NOT_AVAILABLE, dest, sx, sy, clip );
180
181 // draw unit health
182 if ( UnitStatsEnabled() )
183 DrawUnitHealth( u->GroupSize(), dest, sx, sy, clip );
184 }
185
186 // draw fog
187 if ( FogEnabled() && (shader_map[map->Hex2Index(hex)] == -1) )
188 DrawFog( dest, sx, sy, clip );
189
190 // draw cursor
191 if ( CursorEnabled() && (hex == cursor) )
192 DrawTerrain( cursor_image, dest, sx, sy, clip );
193 }
194 }
195 }
196
197 ////////////////////////////////////////////////////////////////////////
198 // NAME : MapView::Pixel2Hex
199 // DESCRIPTION: Convert viewport coordinates to hex coordinates.
200 // PARAMETERS : px - pixel x relative to viewport border
201 // py - pixel y relative to viewport border
202 // hex - buffer to hold the resulting hex
203 // RETURNS : 0 on success, -1 on error (invalid pixels);
204 // hex will contain -1, -1 then
205 ////////////////////////////////////////////////////////////////////////
206
Pixel2Hex(short px,short py,Point & hex) const207 int MapView::Pixel2Hex( short px, short py, Point &hex ) const {
208
209 if ( Contains( px, py ) ) {
210 short hx = px + curx - x, hy = py + cury - y;
211
212 hex.x = hx / (TileWidth() - TileShiftX());
213 hex.y = (hy + (hex.x & 1) * TileShiftY()) / TileHeight() - (hex.x & 1);
214
215 // calculate pixel position relative to selected hex
216 hx %= TileWidth() - TileShiftX();
217 hy -= hex.y * TileHeight() + (hex.x & 1) * TileShiftY();
218
219 const Surface &mask = map->GetTerrainSet()->HexMask();
220 if ( mask.GetPixel( hx, hy ) == mask.GetColorKey() ) {
221 if ( hx < (TileWidth() / 2) ) --hex.x;
222 else ++hex.x;
223
224 if ( hy < (TileHeight() / 2) ) {
225 if ( hex.x & 1 ) --hex.y; // odd columns
226 } else {
227 if ( !(hex.x & 1) ) ++hex.y; // even columns
228 }
229 }
230
231 if ( map->Contains( hex ) ) return 0;
232 }
233
234 hex = Point( -1, -1 );
235 return -1;
236 }
237
238 ////////////////////////////////////////////////////////////////////////
239 // NAME : MapView::Hex2Pixel
240 // DESCRIPTION: Get the pixel coordinates (top left edge) of a hex
241 // relative to the display surface offsets.
242 // PARAMETERS : hex - hex coordinates
243 // RETURNS : Point containing the position of the hex in pixels
244 ////////////////////////////////////////////////////////////////////////
245
Hex2Pixel(const Point & hex) const246 Point MapView::Hex2Pixel( const Point &hex ) const {
247 return Point( hex.x * (TileWidth() - TileShiftX()) - curx + x,
248 hex.y * TileHeight() + (hex.x & 1) * TileShiftY() - cury + y );
249 }
250
251 ////////////////////////////////////////////////////////////////////////
252 // NAME : MapView::HexVisible
253 // DESCRIPTION: Check whether a given hex is currently visible in the
254 // map viewport area.
255 // PARAMETERS : hex - hex position
256 // RETURNS : true if hex (or part of it) on screen, false otherwise
257 ////////////////////////////////////////////////////////////////////////
258
HexVisible(const Point & hex) const259 bool MapView::HexVisible( const Point &hex ) const {
260 Point p = Hex2Pixel( hex ); // get absolute pixel values
261
262 return( (p.x >= x) && (p.x <= x + w - TileWidth()) &&
263 (p.y >= y) && (p.y <= y + h - TileHeight()) );
264 }
265
266 ////////////////////////////////////////////////////////////////////////
267 // NAME : MapView::UpdateHex
268 // DESCRIPTION: Redraw a single hex.
269 // PARAMETERS : hex - hex to update
270 // RETURNS : Rect describing the surface area that has been updated
271 // and needs to be refreshed
272 ////////////////////////////////////////////////////////////////////////
273
UpdateHex(const Point & hex)274 Rect MapView::UpdateHex( const Point &hex ) {
275 if ( !Enabled() ) return Rect(0,0,0,0);
276
277 Point p = Hex2Pixel( hex ); // get absolute position
278
279 Rect clip( p.x, p.y, TileWidth(), TileHeight() ); // create clip rect
280 clip.Clip( *this );
281
282 // draw the terrain image
283 DrawTerrain( map->HexImage( hex ), surface, p.x, p.y, clip );
284
285 // draw unit
286 if ( Unit *u = map->GetUnit( hex ) ) {
287 DrawUnit( u->Image(), surface, p.x, p.y, clip );
288 if ( !u->IsReady() ) DrawTerrain( IMG_NOT_AVAILABLE, surface, p.x, p.y, clip );
289
290 // draw unit health
291 if ( UnitStatsEnabled() )
292 DrawUnitHealth( u->GroupSize(), surface, p.x, p.y, clip );
293 }
294
295 // draw fog
296 if ( FogEnabled() && (shader_map[map->Hex2Index(hex)] == -1) )
297 DrawFog( surface, p.x, p.y, clip );
298
299 // draw cursor
300 if ( CursorEnabled() && (hex == cursor) )
301 DrawTerrain( cursor_image, surface, p.x, p.y, clip );
302
303 return clip;
304 }
305
306 ////////////////////////////////////////////////////////////////////////
307 // NAME : MapView::DrawUnitHealth
308 // DESCRIPTION: Draw a unit health bar.
309 // PARAMETERS : health - unit health indicator
310 // dest - destination surface
311 // px - horizontal offset on surface
312 // py - vertical offset on surface
313 // clip - clipping rectangle
314 // RETURNS : -
315 ////////////////////////////////////////////////////////////////////////
316
DrawUnitHealth(unsigned char health,Surface * dest,short px,short py,const Rect & clip) const317 void MapView::DrawUnitHealth( unsigned char health, Surface *dest,
318 short px, short py, const Rect &clip ) const {
319 Color hcol;
320 if ( health >= 5 ) hcol = Color(0x62,0xAA,0x46); // green
321 else if ( health >= 3 ) hcol = Color(0xFE,0xAA,0x04); // yellow
322 else hcol = Color(0xFE,0x26,0x26); // red
323
324 unsigned short hp = (TileWidth() - 2 * TileShiftX() - 4) /
325 (MAX_GROUP_SIZE + 2);
326 unsigned short barw = hp * MAX_GROUP_SIZE + 2;
327
328 Rect outer( px + (TileWidth() - barw) / 2,
329 py + TileHeight() - hp - 4, barw, hp + 3 );
330 Rect inner( outer.x + 1, outer.y + 1, health * hp, hp + 1 );
331
332 outer.Clip( clip );
333 inner.Clip( clip );
334
335 dest->FillRect( outer, Color(CF_COLOR_BLACK) );
336 dest->FillRect( inner, hcol );
337 }
338
339 ////////////////////////////////////////////////////////////////////////
340 // NAME : MapView::CenterOnHex
341 // DESCRIPTION: Center the display on a given hex if possible.
342 // PARAMETERS : hex - hex position
343 // RETURNS : -
344 ////////////////////////////////////////////////////////////////////////
345
CenterOnHex(const Point & hex)346 void MapView::CenterOnHex( const Point &hex ) {
347 Point p = Hex2Pixel( hex );
348
349 short off = p.x + (TileWidth() - w) / 2 + curx;
350 if ( off > maxx ) curx = maxx;
351 else if ( off < 0 ) curx = 0;
352 else curx = off;
353
354 off = p.y + (TileHeight() - h) / 2 + cury;
355 if ( off > maxy ) cury = maxy;
356 else if ( off < 0 ) cury = 0;
357 else cury = off;
358 Draw();
359 }
360
361 ////////////////////////////////////////////////////////////////////////
362 // NAME : MapView::MinXHex
363 // DESCRIPTION: Get the leftmost visible hex number.
364 // PARAMETERS : x - leftmost pixel. If a value of -1 is given, use the
365 // current viewport settings (curx) to determine the
366 // hex.
367 // RETURNS : leftmost visible hex column
368 ////////////////////////////////////////////////////////////////////////
369
MinXHex(short x) const370 unsigned short MapView::MinXHex( short x ) const {
371 if ( x == -1 ) x = curx;
372 return MAX( (x - TileShiftX()) / (TileWidth() - TileShiftX()), 0 );
373 }
374
375 ////////////////////////////////////////////////////////////////////////
376 // NAME : MapView::MinYHex
377 // DESCRIPTION: Get the topmost visible hex number.
378 // PARAMETERS : y - topmost pixel. If a value of -1 is given, use the
379 // current viewport settings (cury) to determine the
380 // hex.
381 // RETURNS : topmost visible hex row
382 ////////////////////////////////////////////////////////////////////////
383
MinYHex(short y) const384 unsigned short MapView::MinYHex( short y ) const {
385 if ( y == -1 ) y = cury;
386 return MAX( (y - TileShiftY()) / TileHeight(), 0 );
387 }
388
389 ////////////////////////////////////////////////////////////////////////
390 // NAME : MapView::MaxXHex
391 // DESCRIPTION: Get the rightmost visible hex number.
392 // PARAMETERS : x - leftmost pixel. If a value of -1 is given, use the
393 // current viewport settings (curx/MapView::Width())
394 // to determine the hex.
395 // w - display width
396 // RETURNS : rightmost visible hex column
397 ////////////////////////////////////////////////////////////////////////
398
MaxXHex(short x,unsigned short w) const399 unsigned short MapView::MaxXHex( short x, unsigned short w ) const {
400 if ( x == -1 ) {
401 x = curx;
402 w = Width();
403 }
404 return MIN( (x + w) / (TileWidth() - TileShiftX()), map->Width() - 1 );
405 }
406
407 ////////////////////////////////////////////////////////////////////////
408 // NAME : MapView::MaxYHex
409 // DESCRIPTION: Get the lowest visible hex number.
410 // PARAMETERS : y - topmost pixel. If a value of -1 is given, use the
411 // current viewport settings (cury/MapView::Width())
412 // to determine the hex.
413 // h - display height
414 // RETURNS : lowest visible hex row
415 ////////////////////////////////////////////////////////////////////////
416
MaxYHex(short y,unsigned short h) const417 unsigned short MapView::MaxYHex( short y, unsigned short h ) const {
418 if ( y == -1 ) {
419 y = cury;
420 h = Height();
421 }
422 return MIN( (y + h) / TileHeight(), map->Height() - 1 );
423 }
424
425 ////////////////////////////////////////////////////////////////////////
426 // NAME : MapView::CheckScroll
427 // DESCRIPTION: Look at the current cursor position and map offsets and
428 // decide whether we need to scroll the display. If so, do
429 // it.
430 // PARAMETERS : -
431 // RETURNS : TRUE if display was scrolled, FALSE otherwise
432 ////////////////////////////////////////////////////////////////////////
433
CheckScroll(void)434 bool MapView::CheckScroll( void ) {
435 if ( cursor.x == -1 ) return false;
436
437 Point p = Hex2Pixel( cursor );
438
439 if ( (curx > 0) && (p.x <= x + TileWidth()) ) p.x = -w / 2;
440 else if ( (curx < maxx) && (w + x - p.x - TileWidth() <= TileWidth()) )
441 p.x = w / 2;
442 else p.x = 0;
443
444 if ( (cury > 0) && (p.y <= y + TileHeight()) ) p.y = -h / 2;
445 else if ( (cury < maxy) && (h + y - p.y - TileHeight() <= TileHeight()) )
446 p.y = h / 2;
447 else p.y = 0;
448
449 if ( p.x || p.y ) {
450 Scroll( p.x, p.y );
451 return true;
452 }
453 return false;
454 }
455
456 ////////////////////////////////////////////////////////////////////////
457 // NAME : MapView::Scroll
458 // DESCRIPTION: Scroll the currently visible map area.
459 // PARAMETERS : px - pixels to scroll horizontally
460 // py - pixels to scroll vertically
461 // RETURNS : -
462 ////////////////////////////////////////////////////////////////////////
463
Scroll(short px,short py)464 void MapView::Scroll( short px, short py ) {
465 if ( curx + px < 0 ) px = -curx;
466 else if ( curx + px > maxx ) px = maxx - curx;
467 curx += px;
468
469 if ( cury + py < 0 ) py = -cury;
470 else if ( cury + py > maxy ) py = maxy - cury;
471 cury += py;
472
473 #ifdef CF_SDL_LOCAL_BLIT
474 // right now SDL cannot blit parts of a surface to another
475 // place on the same surface if both areas overlap
476
477 // calculate dirty rectangles
478 Rect d1( 0, 0, 0, 0 ); // dirty 1
479 Rect d2( 0, 0, 0, 0 ); // dirty 2
480 Rect c( 0, 0, w, h ); // copy, still valid
481
482 if ( px > 0 ) {
483 d1 = Rect( w - px, 0, px, h );
484 c.x = px;
485 c.w = w - px;
486 } else if ( px < 0 ) {
487 d1 = Rect( 0, 0, -px, h );
488 c.w = w + px;
489 }
490
491 if ( py > 0 ) {
492 d2 = Rect( 0, h - py, w, py );
493 c.y = py;
494 c.h = h - py;
495 } else if ( py < 0 ) {
496 d2 = Rect( 0, 0, w, -py );
497 c.h = h + py;
498 }
499
500 // eliminate overlapping parts
501 if ( (d1.w > 0) && (d2.w > 0) ) {
502 if ( d1.x == d2.x ) d2.x += d1.w;
503 d2.w = MAX( 0, d2.w - d1.w );
504 }
505
506 // copy valid part to current position
507 if ( c.w && c.h )
508 Blit( this, c, (c.x == 0) ? -px : 0, (c.y == 0) ? -py : 0 );
509
510 // update dirty parts
511 if ( d1.w && d1.h ) Draw( d1.x, d1.y, d1.w, d1.h );
512 if ( d2.w && d2.h ) Draw( d2.x, d2.y, d2.w, d2.h );
513 #else
514 Draw();
515 #endif
516 }
517
518 ////////////////////////////////////////////////////////////////////////
519 // NAME : MapView::SetCursor
520 // DESCRIPTION: Move the cursor onto another hex or remove it from the
521 // viewport.
522 // PARAMETERS : hex - hex to set the cursor to. A hex of (-1, -1) will
523 // remove the cursor entirely. If the cursor was
524 // disabled and hex denotes a valid hex, this will
525 // reenable the cursor.
526 // RETURNS : Rect describing the surface area that was updated
527 ////////////////////////////////////////////////////////////////////////
528
SetCursor(const Point & hex)529 Rect MapView::SetCursor( const Point &hex ) {
530 Rect update( 0, 0, 0, 0 );
531
532 if ( (hex.x == -1) && (hex.y == -1) ) { // remove cursor
533 if ( CursorEnabled() ) {
534 Point old = cursor;
535 cursor = Point( -1, -1 );
536
537 // update old cursor position
538 update = UpdateHex( old );
539 SetFlags(MV_DISABLE_CURSOR);
540 }
541 } else {
542 UnsetFlags(MV_DISABLE_CURSOR);
543 cursor = hex;
544 update = UpdateHex( hex );
545 if ( FlagSet(MV_AUTOSCROLL) && CheckScroll() ) update = *this;
546 }
547 return update;
548 }
549
550