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