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 // mapwindow.cpp
21 ////////////////////////////////////////////////////////////////////////
22 
23 #include "mapwindow.h"
24 #include "game.h"
25 #include "msgs.h"
26 
27 ////////////////////////////////////////////////////////////////////////
28 // NAME       : MapWindow::MapWindow
29 // DESCRIPTION: Set the basic map window variables.
30 // PARAMETERS : x     - left edge of window
31 //              y     - top edge of window
32 //              w     - window width
33 //              h     - window height
34 //              flags - window flags (see window.h for details)
35 //              view  - pointer to the view the window will be put on
36 // RETURNS    : -
37 ////////////////////////////////////////////////////////////////////////
38 
MapWindow(short x,short y,unsigned short w,unsigned short h,unsigned short flags,View * view)39 MapWindow::MapWindow( short x, short y, unsigned short w, unsigned short h,
40                       unsigned short flags, View *view ) :
41            Window( x, y, w, h, flags, view ) {
42   panel = new Panel( this, view );
43   mview = new MapView( this, *this,
44               MV_AUTOSCROLL|MV_DISABLE|MV_DISABLE_CURSOR|MV_DISABLE_FOG );
45 }
46 
47 ////////////////////////////////////////////////////////////////////////
48 // NAME       : MapWindow::HandleEvent
49 // DESCRIPTION: React to user input.
50 // PARAMETERS : event - SDL_Event
51 // RETURNS    : -
52 ////////////////////////////////////////////////////////////////////////
53 
HandleEvent(const SDL_Event & event)54 GUI_Status MapWindow::HandleEvent( const SDL_Event &event ) {
55   return Gam->HandleEvent(event);
56 }
57 
58 ////////////////////////////////////////////////////////////////////////
59 // NAME       : MapWindow::Draw
60 // DESCRIPTION: Draw the map window.
61 // PARAMETERS : -
62 // RETURNS    : -
63 ////////////////////////////////////////////////////////////////////////
64 
Draw(void)65 void MapWindow::Draw( void ) {
66   if ( mview->Enabled() ) mview->Draw();
67   else Window::DrawBack();
68 }
69 
70 ////////////////////////////////////////////////////////////////////////
71 // NAME       : MapWindow::DrawBack
72 // DESCRIPTION: Draw the map window background into the internal buffer.
73 // PARAMETERS : x - left edge of area to paint
74 //              y - top edge of area to paint
75 //              w - width of area to paint
76 //              h - height of area to paint
77 // RETURNS    : -
78 ////////////////////////////////////////////////////////////////////////
79 
DrawBack(short x,short y,unsigned short w,unsigned short h)80 void MapWindow::DrawBack( short x, short y, unsigned short w, unsigned short h ) {
81   FillRect( x, y, w, h, Color(CF_COLOR_SHADOW) );
82 }
83 
84 ////////////////////////////////////////////////////////////////////////
85 // NAME       : MapWindow::MoveHex
86 // DESCRIPTION: Smoothly move a hex image (usually a unit or cursor)
87 //              from one hex to another (adjacent) one.
88 // PARAMETERS : img   - map tile identifier for the hex image
89 //              tiles - tile set containing the image
90 //              hex1  - source hex position
91 //              hex2  - destination hex position
92 //              speed - total time (ms) for the animation
93 //              blink - if TRUE make target cursor blink (default is
94 //                      FALSE)
95 // RETURNS    : -
96 ////////////////////////////////////////////////////////////////////////
97 
MoveHex(unsigned short img,const TileSet & tiles,const Point & hex1,const Point & hex2,unsigned short speed,bool blink)98 void MapWindow::MoveHex( unsigned short img, const TileSet &tiles,
99                 const Point &hex1, const Point &hex2,
100                 unsigned short speed, bool blink /* = false */ ) {
101   // allocate a new surface where we can store the "background" graphics
102   Point psrc = mview->Hex2Pixel( hex1 );
103   Point pdst = mview->Hex2Pixel( hex2 );
104 
105   Rect bg;
106   bg.x = MIN( psrc.x, pdst.x );
107   bg.y = MIN( psrc.y, pdst.y );
108   bg.w = tiles.TileWidth() + ABS(psrc.x-pdst.x);
109   bg.h = tiles.TileHeight() + ABS(psrc.y-pdst.y);
110   bg.Clip( *this );
111 
112   Surface *bgs = new Surface;
113   if ( !bgs->Create( bg.w, bg.h, DISPLAY_BPP, 0 ) ) {
114     unsigned long oldticks, ticks = 0, blinkticks = 0;
115     Point curp = mview->Cursor();
116 
117     Blit( bgs, bg, 0, 0 );
118 
119     if ( blink && mview->CursorEnabled() ) {
120       mview->DisableCursor();
121       Show( mview->UpdateHex( curp ) );
122       blinkticks = speed / 2;
123     }
124 
125     oldticks = SDL_GetTicks();
126     do {
127       short cx, cy;
128 
129       // don't modify the following three lines; some versions of gcc
130       // (e.g. 2.95.3) seem to produce bogus code when all the calculations
131       // are done in one line
132       cx = (short)((pdst.x - psrc.x) * ticks);
133       cy = (short)((pdst.y - psrc.y) * ticks);
134       tiles.DrawTile( img, this,
135             psrc.x + cx / speed, psrc.y + cy / speed, bg );
136 
137       Show( bg );
138       bgs->Blit( this, Rect( 0, 0, bg.w, bg.h ), bg.x, bg.y );
139 
140       ticks = SDL_GetTicks() - oldticks;
141 
142       if ( blinkticks && ticks >= blinkticks ) {
143         mview->EnableCursor();
144         Show( mview->UpdateHex( curp ) );
145         blinkticks = 0;
146       }
147 
148     } while ( ticks < speed );
149 
150     Show( bg );
151   }
152   delete bgs;
153 }
154 
155 ////////////////////////////////////////////////////////////////////////
156 // NAME       : MapWindow::FadeHex
157 // DESCRIPTION: Fade in a hex image (a unit or terrain image).
158 // PARAMETERS : img  - tile identifier for the hex image
159 //              hex  - destination hex position
160 //              in   - whether to fade in or out
161 //              unit - if TRUE paint a unit image, otherwise the
162 //                     respective terrain
163 // RETURNS    : -
164 ////////////////////////////////////////////////////////////////////////
165 
FadeHex(unsigned short img,const Point & hex,bool in,bool unit)166 void MapWindow::FadeHex( unsigned short img, const Point &hex, bool in, bool unit ) {
167   unsigned long firstticks, oldticks, ticks;
168   Point pos = mview->Hex2Pixel( hex );
169   const Rect dest( pos.x, pos.y, mview->TileWidth(), mview->TileHeight() );
170   const unsigned int speed = 4 * ANIM_SPEED_UNIT;
171   const unsigned short terrain = mview->GetMap()->HexImage( hex );
172 
173   Surface tile;
174   tile.Create( mview->TileWidth(), mview->TileHeight(), DISPLAY_BPP, SDL_HWSURFACE );
175   tile.SetAlpha( SDL_ALPHA_TRANSPARENT, SDL_SRCALPHA );
176   tile.SetColorKey( Color(CF_COLOR_WHITE) );
177   tile.DisplayFormat();
178   tile.Flood( Color(CF_COLOR_WHITE) );
179 
180   Unit *blocker = NULL;
181   if ( unit ) mview->DrawUnit( img, &tile, 0, 0, tile );
182   else {
183     mview->DrawTerrain( img, &tile, 0, 0, tile );
184     blocker = mview->GetMap()->GetUnit( hex );
185   }
186 
187   firstticks = oldticks = SDL_GetTicks();
188   do {
189     ticks = SDL_GetTicks() - firstticks;
190 
191     int alpha = SDL_ALPHA_OPAQUE * ticks / speed;
192     if ( alpha > SDL_ALPHA_OPAQUE ) alpha = SDL_ALPHA_OPAQUE;
193     if ( !in ) alpha = SDL_ALPHA_OPAQUE - alpha;
194 
195     tile.SetAlpha( alpha, SDL_SRCALPHA );
196     mview->DrawTerrain( terrain, this, pos.x, pos.y, dest );
197     tile.Blit( this, tile, pos.x, pos.y );
198     if ( blocker )
199       mview->DrawUnit( blocker->Image(), this, pos.x, pos.y, dest );
200     Show( dest );
201 
202     if ( ticks - oldticks < 30 ) SDL_Delay( 25 );
203     oldticks = ticks;
204   } while ( ticks < speed );
205 
206   Show( dest );
207 }
208 
209 ////////////////////////////////////////////////////////////////////////
210 // NAME       : MapWindow::FlashUnit
211 // DESCRIPTION: Make the unit at the target hex flash with white.
212 // PARAMETERS : hex   - destination hex position
213 //            : times - desired number of times
214 // RETURNS    : -
215 ////////////////////////////////////////////////////////////////////////
216 
FlashUnit(const Point & hex,unsigned short times)217 void MapWindow::FlashUnit( const Point &hex, unsigned short times ) {
218   unsigned long firstticks, oldticks, ticks;
219   Point pos = mview->Hex2Pixel( hex );
220   const Rect dest( pos.x, pos.y, mview->TileWidth(), mview->TileHeight() );
221   const unsigned int speed = ANIM_SPEED_UNIT * 3 / 2;
222   const unsigned int delay = speed / 3;
223   unsigned short img;
224   bool show_cursor;
225 
226   Surface tile;
227   tile.Create( mview->TileWidth(), mview->TileHeight(), DISPLAY_BPP, 0 );
228   tile.SetAlpha( SDL_ALPHA_OPAQUE, SDL_SRCALPHA );
229   tile.SetColorKey( Color(CF_COLOR_WHITE) );
230   tile.DisplayFormat();
231   tile.Flood( Color(CF_COLOR_WHITE) );
232 
233   img = mview->GetMap()->GetUnit( hex )->Image();
234   mview->DrawUnit( img, &tile, 0, 0, tile );
235   show_cursor = mview->CursorEnabled() && (mview->Cursor() == hex);
236   if ( show_cursor )
237     mview->DrawTerrain( mview->GetCursorImage(), &tile, 0, 0, tile );
238 
239   Surface whitetile;
240   whitetile.Create( mview->TileWidth(), mview->TileHeight(), DISPLAY_BPP, 0 );
241   whitetile.SetAlpha( SDL_ALPHA_OPAQUE / 4, SDL_SRCALPHA );
242   whitetile.DisplayFormat();
243   whitetile.Flood( Color(CF_COLOR_WHITE) );
244 
245   for ( int i = 0; i < times; ++i ) {
246     firstticks = oldticks = SDL_GetTicks();
247     do {
248       whitetile.Blit( &tile, whitetile, 0, 0 );
249       if ( show_cursor )
250         mview->DrawTerrain( mview->GetCursorImage(), &tile, 0, 0, tile );
251       tile.Blit( this, tile, pos.x, pos.y );
252       Show( dest );
253 
254       ticks = SDL_GetTicks() - firstticks;
255       if ( ticks - oldticks < 30 ) SDL_Delay( 25 );
256       oldticks = ticks;
257     } while ( ticks < delay );
258 
259     SDL_Delay( delay );
260 
261     mview->DrawUnit( img, &tile, 0, 0, tile );
262     if ( show_cursor )
263       mview->DrawTerrain( mview->GetCursorImage(), &tile, 0, 0, tile );
264     tile.Blit( this, tile, pos.x, pos.y );
265     Show( dest );
266 
267     SDL_Delay( delay );
268   }
269 }
270 
271 ////////////////////////////////////////////////////////////////////////
272 // NAME       : MapWindow::BoxAvoidHexes
273 // DESCRIPTION: Try to place a box on the screen so that it does not
274 //              cover the two given hexes.
275 // PARAMETERS : box  - the box; left and top edge will be modified
276 //              hex1 - first hex position
277 //              hex2 - second hex position
278 // RETURNS    : -
279 ////////////////////////////////////////////////////////////////////////
280 
BoxAvoidHexes(Rect & box,const Point & hex1,const Point & hex2) const281 void MapWindow::BoxAvoidHexes( Rect &box, const Point &hex1, const Point &hex2 ) const {
282   // define some preferred positions; we'll try to take one of these
283   Point defs[4];
284   defs[0] = Point( (w - box.w) / 2, (h - box.h) / 2 );
285   defs[1] = Point( (w - box.w) / 2, 40 );
286   defs[2] = Point( 40, (h - box.h) / 2 );
287   defs[3] = Point( w - box.w - 40, (h - box.h) / 2 );
288 
289   Point p1 = mview->Hex2Pixel( hex1 );
290   Point p2 = mview->Hex2Pixel( hex2 );
291 
292   for ( int i = 0; i < 4; ++i ) {
293     if ( (((defs[i].x > p1.x + mview->TileWidth()) || (defs[i].x + box.w <= p1.x)) ||
294          ((defs[i].y > p1.y + mview->TileHeight()) || (defs[i].y + box.h <= p1.y))) &&
295          (((defs[i].x > p2.x + mview->TileWidth()) || (defs[i].x + box.w <= p2.x)) ||
296          ((defs[i].y > p2.y + mview->TileHeight()) || (defs[i].y + box.h <= p2.y))) ) {
297       box.x = defs[i].x;
298       box.y = defs[i].y;
299       return;
300     }
301   }
302 }
303 
304 ////////////////////////////////////////////////////////////////////////
305 // NAME       : MapWindow::DisplayHex
306 // DESCRIPTION: Make sure the requested hex is visible and update the
307 //              display. Only call this function if the map view is
308 //              enabled.
309 // PARAMETERS : hex - hex to view
310 // RETURNS    : -
311 ////////////////////////////////////////////////////////////////////////
312 
DisplayHex(const Point & hex)313 void MapWindow::DisplayHex( const Point &hex ) {
314   if ( mview->FlagSet( MV_DIRTY ) || !mview->HexVisible( hex ) ) {
315     mview->CenterOnHex( hex );
316     mview->UnsetFlags( MV_DIRTY );
317     Show( *mview );
318   }
319 }
320 
321 ////////////////////////////////////////////////////////////////////////
322 // NAME       : MapWindow::MoveCursor
323 // DESCRIPTION: Move the cursor into the given direction if possible.
324 // PARAMETERS : dir - requested direction
325 // RETURNS    : new cursor position
326 ////////////////////////////////////////////////////////////////////////
327 
MoveCursor(Direction dir)328 Point MapWindow::MoveCursor( Direction dir ) {
329   Point dest, cursor = mview->Cursor();
330 
331   if ( mview->GetMap()->Dir2Hex( cursor, dir, dest ) ) return cursor;
332 
333   mview->DisableCursor();
334   mview->UpdateHex( cursor );
335   MoveHex( mview->GetCursorImage(),
336            *mview->GetMap()->GetTerrainSet(),
337            cursor, dest, ANIM_SPEED_CURSOR );
338   mview->EnableCursor();
339   return dest;
340 }
341 
342 ////////////////////////////////////////////////////////////////////////
343 // NAME       : MapWindow::VideoModeChange
344 // DESCRIPTION: This method is called by the view whenever the video
345 //              resolution changes. The window can then adjust itself
346 //              to the new dimensions. Afterwards a View::Refresh() is
347 //              performed, i. e. the window just needs to reblit its
348 //              contents to its internal buffer, and NOT call a view
349 //              update itself.
350 // PARAMETERS : -
351 // RETURNS    : -
352 ////////////////////////////////////////////////////////////////////////
353 
VideoModeChange(void)354 void MapWindow::VideoModeChange( void ) {
355   if ( (view->Width() != w) || (view->Height() != h) ) {
356     SetSize( view->Width(), view->Height() );
357 
358     mview->Resize( *this );
359     Draw();
360   }
361 }
362 
363 ////////////////////////////////////////////////////////////////////////
364 // NAME       : Panel::Panel
365 // DESCRIPTION: Create the panel window.
366 // PARAMETERS : mw   - pointer to map window
367 //              view - pointer to view
368 // RETURNS    : -
369 ////////////////////////////////////////////////////////////////////////
370 
Panel(MapWindow * mw,View * view)371 Panel::Panel( MapWindow *mw, View *view ) :
372     Window( 0, mw->Height() - DEFAULT_PANEL_HEIGHT, 0, 0, 0, view ),
373     workshop_icon( view->GetSystemIcons(), 148, 32, XP_ICON_WIDTH, XP_ICON_HEIGHT ),
374     factory_icon( view->GetSystemIcons(), 160, 32, XP_ICON_WIDTH, XP_ICON_HEIGHT )
375 {
376   obj = NULL;
377   mapwin = mw;
378 }
379 
380 ////////////////////////////////////////////////////////////////////////
381 // NAME       : Panel::Draw
382 // DESCRIPTION: Draw the panel window.
383 // PARAMETERS : -
384 // RETURNS    : -
385 ////////////////////////////////////////////////////////////////////////
386 
Draw(void)387 void Panel::Draw( void ) {
388   if ( obj ) {
389     short namex = 5;
390 
391     Window::Draw();
392 
393     if ( obj->IsUnit() ) {
394       Unit *u = static_cast<Unit *>(obj);
395       char buf[4];
396       sprintf( buf, "(%d)", u->GroupSize() );
397 
398       namex += 5 + XP_ICON_WIDTH;
399       Images[ICON_XP_BASE + u->XPLevel()]->Draw( this, 5, (h - XP_ICON_HEIGHT)/2 );
400       sfont->Write( buf, this, namex + 5 + sfont->TextWidth( u->Name() ),
401                   (h - sfont->Height())/2 );
402     } else {
403       Building *b = static_cast<Building *>(obj);
404       if ( b->IsWorkshop() ) {
405         workshop_icon.Draw( this, 5, (h - XP_ICON_HEIGHT)/2 );
406         namex += 5 + XP_ICON_WIDTH;
407       }
408       if ( b->IsFactory() ) {
409         factory_icon.Draw( this, namex, (h - XP_ICON_HEIGHT)/2 );
410         namex += 5 + XP_ICON_WIDTH;
411       }
412     }
413     sfont->Write( obj->Name(), this, namex, (h - sfont->Height())/2 );
414   }
415 }
416 
417 ////////////////////////////////////////////////////////////////////////
418 // NAME       : Panel::Update
419 // DESCRIPTION: Set the information displayed on the panel and update
420 //              the display.
421 // PARAMETERS : obj - map object to display information about (may be
422 //                    NULL, in which case the panel will be cleared)
423 // RETURNS    : -
424 ////////////////////////////////////////////////////////////////////////
425 
Update(MapObject * obj)426 void Panel::Update( MapObject *obj ) {
427   Rect refresh = *this;
428 
429   if ( !obj ) {
430     if ( this->obj ) {
431       w = h = 0;
432       y = mapwin->Height() - DEFAULT_PANEL_HEIGHT;
433       view->Refresh( refresh );
434       this->obj = NULL;
435     }
436   } else {
437     this->obj = obj;
438 
439     // resize and reposition if necessary
440     w = sfont->TextWidth( obj->Name() ) + 10;
441     if ( obj->IsUnit() ) w += 5 + XP_ICON_WIDTH + sfont->Width() * 3;
442     else {
443       Building *b = static_cast<Building *>(obj);
444       if ( b->IsWorkshop() ) w += 5 + XP_ICON_WIDTH;
445       if ( b->IsFactory() ) w += 5 + XP_ICON_WIDTH;
446     }
447     h = refresh.h = DEFAULT_PANEL_HEIGHT;
448 
449     MapView *mv = mapwin->GetMapView();
450     Point pcursor = mv->Hex2Pixel( mv->Cursor() );
451     if ( pcursor.x <= w + mv->TileWidth() ) {
452       if ( y == 0 ) {
453         if ( pcursor.y <= h + mv->TileHeight() ) {
454           y = mapwin->Height() - h;
455         }
456       } else if ( pcursor.y > y - mv->TileHeight() ) y = 0;
457     }
458     SetSize( w, h );   // allocate surface
459     Draw();
460 
461     // update the old window position and show the new
462     if ( refresh.y == y ) {
463       if ( w > refresh.w ) refresh.w = w;
464     } else Show();
465     view->Refresh( refresh );
466   }
467 }
468 
469 ////////////////////////////////////////////////////////////////////////
470 // NAME       : Panel::HandleEvent
471 // DESCRIPTION: The window must pass all incoming events to the map
472 //              window which does proper processing.
473 // PARAMETERS : event - event as received from the event handler
474 // RETURNS    : GUI status
475 ////////////////////////////////////////////////////////////////////////
476 
HandleEvent(const SDL_Event & event)477 GUI_Status Panel::HandleEvent( const SDL_Event &event ) {
478   return mapwin->HandleEvent( event );
479 }
480 
481 ////////////////////////////////////////////////////////////////////////
482 // NAME       : Panel::VideoModeChange
483 // DESCRIPTION: This method is called by the view whenever the video
484 //              resolution changes. The window can then adjust itself
485 //              to the new dimensions. Afterwards a View::Refresh() is
486 //              performed, i. e. the window just needs to reblit its
487 //              contents to its internal buffer, and NOT call a view
488 //              update itself.
489 // PARAMETERS : -
490 // RETURNS    : -
491 ////////////////////////////////////////////////////////////////////////
492 
VideoModeChange(void)493 void Panel::VideoModeChange( void ) {
494   Window::VideoModeChange();
495 
496   if ( TopEdge() + DEFAULT_PANEL_HEIGHT < view->Height() )
497     y = view->Height() - DEFAULT_PANEL_HEIGHT;
498 }
499 
500 
501 ////////////////////////////////////////////////////////////////////////
502 // NAME       : TacticalWindow::TacticalWindow
503 // DESCRIPTION: Create a window and show an overview map of the level.
504 // PARAMETERS : mapview - pointer to map viewport
505 //              m       - mission object
506 //              view    - pointer to the window's view
507 // RETURNS    : -
508 ////////////////////////////////////////////////////////////////////////
509 
TacticalWindow(MapView * mapview,Mission & m,View * view)510 TacticalWindow::TacticalWindow( MapView *mapview, Mission &m, View *view ) :
511         Window( WIN_CLOSE_ESC|WIN_CENTER, view ), mv(mapview), mission(m),
512         p1(m.GetPlayer(PLAYER_ONE)), p2(m.GetPlayer(PLAYER_TWO)) {
513   Map *pmap = mv->GetMap();
514 
515   Rect win, map, viewport;
516 
517   unsigned char magnify = MIN( (view->Width() - 20) / pmap->Width(),
518                                (view->Height() * 3 / 4) / pmap->Height() );
519   if ( magnify < 2 ) magnify = 2;
520   else if ( magnify > 6 ) magnify = 6;
521 
522   map.w = MIN( pmap->Width() * magnify + 2, view->Width() - 10 );
523   map.h = MIN( pmap->Height() * magnify + magnify/2 + 2,
524                view->Height() - sfont->Height() * 3 - 40 );
525 
526   // calculate window dimensions
527   const char *ulabel = MSG(MSG_UNITS);
528   const char *slabel = MSG(MSG_SHOPS);
529   unsigned short ulen = sfont->TextWidth( ulabel );
530   unsigned short slen = sfont->TextWidth( slabel );
531   unsigned short labellen = MAX( ulen, slen );
532 
533   win.w = MAX( map.w + 20, labellen + sfont->Width() * 12 + 30 );
534   win.h = MIN( map.h + sfont->Height() * 3 + 40, view->h );
535   win.Center( *view );
536   win.Align( *view );
537 
538   map.x = (win.w - map.w) / 2;
539   map.y = MAX( 5, win.h - 3 * sfont->Height() - map.h - 35 );
540 
541   SetSize( win );
542   CalcViewPort( viewport );
543 
544   new ButtonWidget( GUI_CLOSE, 1, h - sfont->Height() - 9,
545                     w - 2, sfont->Height() + 8,
546                     WIDGET_DEFAULT, MSG(MSG_B_OK), this );
547 
548   mw = new MapWidget( 0, map.x, map.y, map.w, map.h,
549                       WIDGET_HSCROLLKEY|WIDGET_VSCROLLKEY, this );
550   mw->SetPlayerColors( p1.LightColor(), p2.LightColor() );
551   mw->SetMap( pmap, viewport, magnify );
552   mw->SetHook( this );
553 
554   Draw();
555   Show();
556 }
557 
558 ////////////////////////////////////////////////////////////////////////
559 // NAME       : TacticalWindow::WidgetActivated
560 // DESCRIPTION: Handle events from the MapWidget.
561 // PARAMETERS : widget - widget sending the message (unused)
562 //              win    - window the widget belongs to (unused)
563 // RETURNS    : GUI status
564 ////////////////////////////////////////////////////////////////////////
565 
WidgetActivated(Widget * widget,Window * win)566 GUI_Status TacticalWindow::WidgetActivated( Widget *widget, Window *win ) {
567   Point hex = mw->GetLastHex();
568   Rect viewport;
569 
570   view->DisableUpdates();
571   if ( !mv->HexVisible( hex ) ) mv->CenterOnHex( hex );
572   Gam->SetCursor( hex );
573 
574   CalcViewPort( viewport );
575   mw->SetViewPort( viewport );
576   mw->Draw();
577   view->EnableUpdates();
578   view->Refresh();
579 
580   return GUI_OK;
581 }
582 
583 ////////////////////////////////////////////////////////////////////////
584 // NAME       : TacticalWindow::Draw
585 // DESCRIPTION: Draw the window.
586 // PARAMETERS : -
587 // RETURNS    : -
588 ////////////////////////////////////////////////////////////////////////
589 
Draw(void)590 void TacticalWindow::Draw( void ) {
591   Window::Draw();
592 
593   // show unit and building count
594   Color col;
595   unsigned short linewidth = w - 40, lx, l1y, l2y, lh, xoff1, xoff2;
596   char numbuf[4];
597 
598   unsigned short bs1 = 0, bs2 = 0, bstotal = 0;
599   Building *b = static_cast<Building *>( mission.GetShops().Head() );
600   while ( b ) {
601     if ( b->Owner() ) {
602       if ( b->Owner()->ID() == PLAYER_ONE ) ++bs1;
603       else if ( b->Owner()->ID() == PLAYER_TWO ) ++bs2;
604     }
605     ++bstotal;
606     b = static_cast<Building *>( b->Next() );
607   }
608 
609 
610   lh = (h - mw->y - mw->h - sfont->Height() * 3 - 9) / 5;
611   lx = MIN( (w - linewidth - 10) / 2, mw->x + 5 );
612   l1y = mw->y + mw->h + lh * 2;
613   l2y = h - (sfont->Height() + lh) * 2 - 9;
614   DrawBox( Rect( lx - 5, l1y - lh, w - 2*lx + 10, (sfont->Height() * 2 + lh * 3) ),
615            BOX_RECESSED );
616 
617   lx = (w - linewidth - 10) / 2;
618   xoff1 = sfont->Write( MSG(MSG_UNITS), this, lx, l1y );
619   xoff2 = sfont->Write( MSG(MSG_SHOPS), this, lx, l2y );
620 
621   lx += MAX( xoff1, xoff2 ) + 20;
622   col = p1.LightColor();
623   xoff1 = sfont->Write( itoa( p1.Units(0), numbuf ), this, lx, l1y, col );
624   xoff2 = sfont->Write( itoa( bs1, numbuf ), this, lx, l2y, col );
625 
626   lx += MAX( xoff1, xoff2 ) + 20;
627   col = p2.LightColor();
628   xoff1 = sfont->Write( itoa( p2.Units(0), numbuf ), this, lx, l1y, col );
629   xoff2 = sfont->Write( itoa( bs2, numbuf ), this, lx, l2y, col );
630 
631   lx += MAX( xoff1, xoff2 ) + 20;
632   sfont->Write( itoa( mission.GetUnits().CountNodes(), numbuf ), this, lx, l1y );
633   sfont->Write( itoa( bstotal, numbuf ), this, lx, l2y );
634 }
635 
636 ////////////////////////////////////////////////////////////////////////
637 // NAME       : TacticalWindow::CalcViewPort
638 // DESCRIPTION: Determine which part of the map is currently displayed
639 //              on the map window and set the viewport rect accordingly.
640 // PARAMETERS : vp - Rect buffer to hold the view port coordinates
641 // RETURNS    : -
642 ////////////////////////////////////////////////////////////////////////
643 
CalcViewPort(Rect & vp) const644 void TacticalWindow::CalcViewPort( Rect &vp ) const {
645   vp.x = mv->MinXHex(-1);
646   vp.y = mv->MinYHex(-1);
647   vp.w = (mv->MaxXHex(-1,0) + 1) - vp.x;
648   vp.h = (mv->MaxYHex(-1,0) + 1) - vp.y;
649 }
650 
651 
652 ////////////////////////////////////////////////////////////////////////
653 // NAME       : CombatWindow::CombatWindow
654 // DESCRIPTION: The CombatWindow visualizes the fight between two units.
655 // PARAMETERS : combat - pointer to a combat object
656 //              mapwin - pointer to the map window; required to update
657 //                       the display when a unit is destroyed
658 //              view   - view the window will belong to
659 // RETURNS    : -
660 ////////////////////////////////////////////////////////////////////////
661 
CombatWindow(Combat * combat,MapWindow * mapwin,View * view)662 CombatWindow::CombatWindow( Combat *combat, MapWindow *mapwin, View *view ) :
663   Window( 0, view ), mapwin( mapwin ), att( combat->GetAttacker() ),
664     def( combat->GetDefender() ), apos( att->Position() ), dpos( def->Position() ),
665     startgroup1( att->GroupSize() ), startgroup2( def->GroupSize() ) {
666 
667   MapView *mv = mapwin->GetMapView();
668 
669   // calculate window dimensions
670   short wwidth = (MAX( sfont->TextWidth( att->Name() ),
671                        sfont->TextWidth( def->Name() ) ) + XP_ICON_WIDTH + 25) * 2;
672   wwidth = MAX( wwidth, mv->TileWidth() * 6 + 50 );
673   Rect win( 0, 0, wwidth, mv->TileHeight() * 3 + sfont->Height() * 2 + 50 );
674 
675   // place window on map window
676   mv->CenterOnHex( Point( (apos.x + dpos.x) / 2, (apos.y + dpos.y) / 2 ) );
677   mapwin->BoxAvoidHexes( win, apos, dpos );
678   SetSize( win.x, win.y, win.w, win.h );
679 
680   // highlight the fighting units
681   Rect clip;
682   Point punit = mv->Hex2Pixel( apos );
683   clip = Rect( punit.x, punit.y, mv->TileWidth(), mv->TileHeight() );
684   clip.Clip( *mv );
685   mv->DrawTerrain( IMG_CURSOR_HIGHLIGHT, mapwin, punit.x, punit.y, clip );
686   punit = mv->Hex2Pixel( dpos );
687   clip = Rect( punit.x, punit.y, mv->TileWidth(), mv->TileHeight() );
688   clip.Clip( *mv );
689   mv->DrawTerrain( IMG_CURSOR_HIGHLIGHT, mapwin, punit.x, punit.y, clip );
690 
691   // create button widget, message areas, and unit display areas
692   Rect brect( 1, h - sfont->Height() - 10, w - 2, sfont->Height() + 10 );
693 
694   msgbar1 = Rect( 4, h - sfont->Height() - 14 - brect.h, (w - 16) / 2, sfont->Height() + 6 );
695   msgbar2 = Rect( msgbar1.x + msgbar1.w + 8, msgbar1.y, msgbar1.w, msgbar1.h );
696 
697   att_anchor = Rect( w/4 - mv->TileWidth()/2, 10 + mv->TileHeight(), mv->TileWidth(), mv->TileHeight() );
698   def_anchor = Rect( (w * 3)/4 - mv->TileWidth()/2, att_anchor.y, mv->TileWidth(), mv->TileHeight() );
699 
700   clock[0][0] = Rect( att_anchor.x, att_anchor.y - mv->TileHeight(), mv->TileWidth(), mv->TileHeight() );
701   clock[0][1] = Rect( att_anchor.x + mv->TileWidth() - mv->TileShiftX(), att_anchor.y - mv->TileShiftY(), mv->TileWidth(), mv->TileHeight() );
702   clock[0][2] = Rect( att_anchor.x + mv->TileWidth() - mv->TileShiftX(), att_anchor.y + mv->TileShiftY(), mv->TileWidth(), mv->TileHeight() );
703   clock[0][3] = Rect( att_anchor.x, att_anchor.y + mv->TileHeight(), mv->TileWidth(), mv->TileHeight() );
704   clock[0][4] = Rect( att_anchor.x - mv->TileWidth() + mv->TileShiftX(), att_anchor.y + mv->TileShiftY(), mv->TileWidth(), mv->TileHeight() );
705   clock[0][5] = Rect( att_anchor.x - mv->TileWidth() + mv->TileShiftX(), att_anchor.y - mv->TileShiftY(), mv->TileWidth(), mv->TileHeight() );
706   clock[1][0] = Rect( def_anchor.x, def_anchor.y - mv->TileHeight(), mv->TileWidth(), mv->TileHeight() );
707   clock[1][1] = Rect( def_anchor.x + mv->TileWidth() - mv->TileShiftX(), def_anchor.y - mv->TileShiftY(), mv->TileWidth(), mv->TileHeight() );
708   clock[1][2] = Rect( def_anchor.x + mv->TileWidth() - mv->TileShiftX(), def_anchor.y + mv->TileShiftY(), mv->TileWidth(), mv->TileHeight() );
709   clock[1][3] = Rect( def_anchor.x, def_anchor.y + mv->TileHeight(), mv->TileWidth(), mv->TileHeight() );
710   clock[1][4] = Rect( def_anchor.x - mv->TileWidth() + mv->TileShiftX(), def_anchor.y + mv->TileShiftY(), mv->TileWidth(), mv->TileHeight() );
711   clock[1][5] = Rect( def_anchor.x - mv->TileWidth() + mv->TileShiftX(), def_anchor.y - mv->TileShiftY(), mv->TileWidth(), mv->TileHeight() );
712 
713   button = new ButtonWidget( GUI_CLOSE, brect.x, brect.y, brect.w, brect.h, WIDGET_DEFAULT, MSG(MSG_B_OK), this );
714 
715   Draw();
716   view->Refresh();
717 }
718 
719 ////////////////////////////////////////////////////////////////////////
720 // NAME       : CombatWindow::Draw
721 // DESCRIPTION: Draw the window.
722 // PARAMETERS : -
723 // RETURNS    : -
724 ////////////////////////////////////////////////////////////////////////
725 
Draw(void)726 void CombatWindow::Draw( void ) {
727   Window::Draw();
728   DrawBox( msgbar1, BOX_RECESSED );
729   DrawBox( msgbar2, BOX_RECESSED );
730 
731   DrawBox( Rect( msgbar1.x, 4, msgbar1.w, msgbar1.y - 8 ), BOX_CARVED );
732   DrawBox( Rect( msgbar2.x, 4, msgbar2.w, msgbar2.y - 8 ), BOX_CARVED );
733 
734   Images[ICON_XP_BASE + att->XPLevel()]->Draw( this, msgbar1.x + 4, msgbar1.y + (msgbar1.h - XP_ICON_HEIGHT)/2 );
735   Images[ICON_XP_BASE + def->XPLevel()]->Draw( this, msgbar2.x + 4, msgbar2.y + (msgbar2.h - XP_ICON_HEIGHT)/2 );
736 
737   sfont->Write( att->Name(), this, msgbar1.x + XP_ICON_WIDTH + 8,
738                 msgbar1.y + (msgbar1.h - sfont->Height())/2 );
739   sfont->Write( def->Name(), this, msgbar2.x + XP_ICON_WIDTH + 8,
740                 msgbar2.y + (msgbar2.h - sfont->Height())/2 );
741 
742   // draw terrain and unit image to the center of the 'clock'
743   MapView *mv = mapwin->GetMapView();
744   Map *map = mv->GetMap();
745   mv->DrawTerrain( map->HexImage( apos ), this, att_anchor.x, att_anchor.y, att_anchor );
746   mv->DrawTerrain( map->HexImage( dpos ), this, def_anchor.x, def_anchor.y, def_anchor );
747   mv->DrawUnit( att->Image(), this, att_anchor.x, att_anchor.y, att_anchor );
748   mv->DrawUnit( def->Image(), this, def_anchor.x, def_anchor.y, def_anchor );
749 
750   DrawState();
751 }
752 
753 ////////////////////////////////////////////////////////////////////////
754 // NAME       : CombatWindow::DrawState
755 // DESCRIPTION: Update the two 'clocks' to display the current standing.
756 // PARAMETERS : -
757 // RETURNS    : -
758 ////////////////////////////////////////////////////////////////////////
759 
DrawState(void)760 void CombatWindow::DrawState( void ) {
761   short group1 = att->GroupSize(), group2 = def->GroupSize();
762   MapView *mv = mapwin->GetMapView();
763 
764   for ( int i = NORTH; i <= NORTHWEST; ++i ) {
765 
766     if ( i < startgroup1 ) {
767       mv->DrawTerrain( IMG_RECESSED_HEX, this, clock[0][i].x, clock[0][i].y, clock[0][i] );
768       mv->DrawUnit( att->BaseImage() + i, this, clock[0][i].x, clock[0][i].y, clock[0][i] );
769 
770       if ( i >= group1 )
771         mv->DrawFog( this, clock[0][i].x, clock[0][i].y, clock[0][i] );
772     }
773 
774     if ( i < startgroup2 ) {
775       mv->DrawTerrain( IMG_RECESSED_HEX, this, clock[1][i].x, clock[1][i].y, clock[1][i] );
776       mv->DrawUnit( def->BaseImage() + i, this, clock[1][i].x, clock[1][i].y, clock[1][i] );
777 
778       if ( i >= group2 )
779         mv->DrawFog( this, clock[1][i].x, clock[1][i].y, clock[1][i] );
780     }
781   }
782 }
783 
784