initial commit, lordsawar source, slightly modified
[lordsawar] / src / overviewmap.cpp
1 // Copyright (C) 2006, 2007 Ulf Lorenz
2 // Copyright (C) 2006, 2007, 2008, 2009 Ben Asselstine
3 // Copyright (C) 2007 Ole Laursen
4 //
5 //  This program is free software; you can redistribute it and/or modify
6 //  it under the terms of the GNU General Public License as published by
7 //  the Free Software Foundation; either version 3 of the License, or
8 //  (at your option) any later version.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU Library General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program; if not, write to the Free Software
17 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
18 //  02110-1301, USA.
19
20 #include <assert.h>
21 #include <math.h>
22 #include <stdlib.h>
23 #include "gui/image-helpers.h"
24
25 #include "overviewmap.h"
26 #include "stacklist.h"
27 #include "citylist.h"
28 #include "ruinlist.h"
29 #include "templelist.h"
30 #include "city.h"
31 #include "ruin.h"
32 #include "temple.h"
33 #include "playerlist.h"
34 #include "player.h"
35 #include "GameMap.h"
36 #include "GraphicsCache.h"
37 #include "FogMap.h"
38 #include "GameScenarioOptions.h"
39
40 OverviewMap::OverviewMap()
41 {
42   blank_screen = false;
43 }
44
45 OverviewMap::~OverviewMap()
46 {
47 }
48
49 bool OverviewMap::isShadowed(guint32 type, int i, int j)
50 {
51   GameMap *gm = GameMap::getInstance();
52   int x = int(i / pixels_per_tile);
53   int y = int(j / pixels_per_tile);
54   int x2;
55   int y2;
56   //what's this tile?
57   //is it water?
58   //no?  then false;
59   //if yes, then maybe
60   //if the tile above us or beside us is land then this might be a shadow pixel
61
62   if (gm->getTile(x,y)->getType() != type)
63     return false;
64   if (x > 0 && gm->getTile(x-1,y)->getType() != type)
65     {
66       x2 = int((i-1) / pixels_per_tile);
67       y2 = int(j / pixels_per_tile);
68       if (gm->getTile(x-1,y) == gm->getTile(x2,y2))
69         return true;
70     }
71   if (y > 0 && gm->getTile(x,y-1)->getType() != type)
72     {
73       x2 = int(i / pixels_per_tile);
74       y2 = int((j-1) / pixels_per_tile);
75       if (gm->getTile(x,y-1) == gm->getTile(x2,y2))
76         return true;
77     }
78   if (y > 0 && x > 0 && gm->getTile(x-1,y-1)->getType() != type)
79     {
80       x2 = int((i-1) / pixels_per_tile);
81       y2 = int((j-1) / pixels_per_tile);
82       if (gm->getTile(x-1,y-1) == gm->getTile(x2,y2))
83         return true;
84     }
85
86   return false;
87 }
88
89 static int 
90 prand(int i, int j)
91 {
92   return (rand () % 3);
93 }
94
95 static int 
96 crand(int i, int j)
97 {
98   return (i + 1) ^ (j + 1);
99   //return i + j + (i*j) + (i*100) + (j*43) / 43;
100 }
101
102 static int 
103 drand(int i, int j)
104 {
105   float f = i / 43 * j / 43;
106   f *= 10000000;
107   return (int) roundf(f) | (i  + i + j);
108 }
109
110
111 void OverviewMap::choose_surface(bool front, Glib::RefPtr<Gdk::Pixmap> &surf,
112                                  Glib::RefPtr<Gdk::GC> &gc)
113 {
114   if (front)
115     {
116       gc = surface_gc;
117       surf = surface;
118     }
119   else
120     {
121       gc = static_surface_gc;
122       surf = static_surface;
123     }
124 }
125 void
126 OverviewMap::draw_pixel(Glib::RefPtr<Gdk::Pixmap> surf, Glib::RefPtr<Gdk::GC> gc, int x, int y, Gdk::Color color)
127 {
128   gc->set_rgb_fg_color(color);
129   surf->draw_point(gc, x, y);
130   return;
131 }
132
133 void
134 OverviewMap::draw_filled_rect(int x, int y, int width, int height, Gdk::Color color)
135 {
136   draw_filled_rect(true, x, y, width, height, color);
137 }
138
139 void
140 OverviewMap::draw_filled_rect(bool front, int x, int y, int width, int height, Gdk::Color color)
141 {
142   Glib::RefPtr<Gdk::Pixmap> surf;
143   Glib::RefPtr<Gdk::GC> gc;
144   choose_surface (front, surf, gc);
145   gc->set_rgb_fg_color(color);
146   surf->draw_rectangle(gc, true, x, y, width, height);
147 }
148
149 void
150 OverviewMap::draw_line(int src_x, int src_y, int dst_x, int dst_y, Gdk::Color color)
151 {
152   draw_line(true, src_x, src_y, dst_x, dst_y, color);
153 }
154
155 void
156 OverviewMap::draw_line(bool front, int src_x, int src_y, int dst_x, int dst_y, Gdk::Color color)
157 {
158   Glib::RefPtr<Gdk::Pixmap> surf;
159   Glib::RefPtr<Gdk::GC> gc;
160   choose_surface (front, surf, gc);
161   gc->set_rgb_fg_color(color);
162   surf->draw_line(gc, src_x, src_y, dst_x, dst_y);
163 }
164
165 void
166 OverviewMap::draw_rect(int x, int y, int width, int height, Gdk::Color color)
167 {
168   draw_rect (true, x, y, width, height, color);
169 }
170
171 void
172 OverviewMap::draw_rect(bool front, int x, int y, int width, int height, Gdk::Color color)
173 {
174   Glib::RefPtr<Gdk::Pixmap> surf;
175   Glib::RefPtr<Gdk::GC> gc;
176   choose_surface (front, surf, gc);
177   gc->set_rgb_fg_color(color);
178   surf->draw_rectangle(gc, false, x, y, width, height);
179 }
180
181 void OverviewMap::draw_terrain_tile(Glib::RefPtr<Gdk::Pixmap> surf,
182                                     Glib::RefPtr<Gdk::GC> gc,
183                                     SmallTile::Pattern pattern,
184                                     Gdk::Color first, 
185                                     Gdk::Color second,
186                                     Gdk::Color third,
187                                     int i, int j, bool shadowed)
188 {
189
190   switch (pattern)
191     {
192       case SmallTile::SOLID:
193         draw_pixel(surf, gc, i, j, first);
194         break;
195       case SmallTile::STIPPLED:
196         {
197           if ((i+j) % 2 == 0)
198             draw_pixel(surf, gc, i, j, first);
199           else
200             draw_pixel(surf, gc, i, j, second);
201         }
202         break;
203       case SmallTile::RANDOMIZED:
204         {
205           int num = prand(i, j) % 3;
206           if (num == 0)
207             draw_pixel(surf, gc, i, j, first);
208           else if (num == 1)
209             draw_pixel(surf, gc, i, j, second);
210           else
211             draw_pixel(surf, gc, i, j, third);
212         }
213         break;
214       case SmallTile::DIAGONAL:
215         {
216           int num = drand(i, j) % 3;
217           if (num == 0)
218             draw_pixel(surf, gc, i, j, first);
219           else if (num == 1)
220             draw_pixel(surf, gc, i, j, second);
221           else
222             draw_pixel(surf, gc, i, j, third);
223         }
224         break;
225       case SmallTile::CROSSHATCH:
226         {
227           int num = crand(i, j) % 3;
228           if (num == 0)
229             draw_pixel(surf, gc, i, j, first);
230           else if (num == 1)
231             draw_pixel(surf, gc , i, j, second);
232           else
233             draw_pixel(surf, gc, i, j, third);
234         }
235         break;
236       case SmallTile::SUNKEN:
237         if (shadowed == false)
238           draw_pixel(surf, gc, i, j, first);
239         else
240           {
241             draw_pixel(surf, gc, i, j, second);
242           }
243         break;
244       case SmallTile::SUNKEN_STRIPED:
245         if (shadowed == false)
246           {
247             if (j % 1 == 0)
248               draw_pixel(surf, gc, i, j, first);
249             else
250               draw_pixel(surf, gc, i, j, third);
251           }
252         else
253           {
254             draw_pixel(surf, gc, i, j, second);
255           }
256         break;
257       case SmallTile::TABLECLOTH:
258           {
259             if (i % 4 == 0 && j % 4 == 0)
260               draw_pixel(surf, gc, i, j, first);
261             else if (i % 4 == 0 && j % 4 == 1)
262               draw_pixel(surf, gc, i, j, second);
263             else if (i % 4 == 0 && j % 4 == 2)
264               draw_pixel(surf, gc, i, j, first);
265             else if (i % 4 == 0 && j % 4 == 3)
266               draw_pixel(surf, gc, i, j, second);
267             else if (i % 4 == 1 && j % 4 == 0)
268               draw_pixel(surf, gc, i, j, second);
269             else if (i % 4 == 1 && j % 4 == 1)
270               draw_pixel(surf, gc, i, j, third);
271             else if (i % 4 == 1 && j % 4 == 2)
272               draw_pixel(surf, gc, i, j, second);
273             else if (i % 4 == 1 && j % 4 == 3)
274               draw_pixel(surf, gc, i, j, third);
275             else if (i % 4 == 2 && j % 4 == 0)
276               draw_pixel(surf, gc, i, j, first);
277             else if (i % 4 == 2 && j % 4 == 1)
278               draw_pixel(surf, gc, i, j, second);
279             else if (i % 4 == 2 && j % 4 == 2)
280               draw_pixel(surf, gc, i, j, first);
281             else if (i % 4 == 2 && j % 4 == 3)
282               draw_pixel(surf, gc, i, j, second);
283             else if (i % 4 == 3 && j % 4 == 0)
284               draw_pixel(surf, gc, i, j, second);
285             else if (i % 4 == 3 && j % 4 == 1)
286               draw_pixel(surf, gc, i, j, third);
287             else if (i % 4 == 3 && j % 4 == 2)
288               draw_pixel(surf, gc, i, j, second);
289             else if (i % 4 == 3 && j % 4 == 3)
290               draw_pixel(surf, gc, i, j, third);
291           }
292         break;
293     }
294 }
295
296 void OverviewMap::draw_terrain_tile(Maptile *t, int i, int j)
297 {
298   bool shadowed = isShadowed(t->getType(), i, j);
299   draw_terrain_tile (static_surface, static_surface_gc, t->getPattern(), 
300                      t->getColor(), 
301                      t->getSecondColor(), 
302                      t->getThirdColor(),
303                      i, j, shadowed);
304 }
305
306 int OverviewMap::calculateResizeFactor()
307 {
308   if (GameMap::getWidth() <= (int)MAP_SIZE_TINY_WIDTH && 
309       GameMap::getHeight() <= (int)MAP_SIZE_TINY_HEIGHT)
310     return 4;
311   else if (GameMap::getWidth() <= (int)MAP_SIZE_SMALL_WIDTH && 
312            GameMap::getHeight() <= (int)MAP_SIZE_SMALL_HEIGHT)
313     return 3;
314   else
315     return 2;
316 }
317
318 void OverviewMap::resize()
319 {
320   int factor = calculateResizeFactor();
321   resize(GameMap::get_dim() * factor);
322 }
323
324 void OverviewMap::resize(Vector<int> max_dimensions)
325 {
326   surface.reset();
327
328     // calculate the width and height relations between pixels and maptiles
329     Vector<int> bigmap_dim = GameMap::get_dim();
330     Vector<int> d;
331
332     // first try scaling to horizontal size
333     pixels_per_tile = max_dimensions.x / double(bigmap_dim.x);
334     d.x = max_dimensions.x;
335     d.y = int(round(bigmap_dim.y * pixels_per_tile));
336     
337     if (d.y > max_dimensions.y)
338     {
339         // if too big, scale to vertical
340         pixels_per_tile = max_dimensions.y / double(bigmap_dim.y);
341         d.x = int(round(bigmap_dim.x * pixels_per_tile));
342         d.y = max_dimensions.y;
343     }
344
345     
346     static_surface = Gdk::Pixmap::create(Glib::RefPtr<Gdk::Drawable>(0), d.x, d.y, 24);
347     static_surface_gc = Gdk::GC::create(static_surface);
348
349     draw_terrain_tiles(Rectangle(0, 0, d.x, d.y));
350     surface = Gdk::Pixmap::create(static_surface, d.x, d.y, 24);
351     surface_gc = Gdk::GC::create(surface);
352
353 }
354
355 void OverviewMap::redraw_tiles(Rectangle tiles)
356 {
357     if (tiles.w > 0 && tiles.h > 0)
358     {
359         tiles.pos -= Vector<int>(1, 1);
360         tiles.dim += Vector<int>(2, 2);
361         
362         // translate to pixel coordinates 
363         Vector<int> pos(int(round(tiles.x * pixels_per_tile)),
364                         int(round(tiles.y * pixels_per_tile)));
365         Vector<int> dim(int(round(tiles.w * pixels_per_tile)),
366                         int(round(tiles.h * pixels_per_tile)));
367
368         int width;
369         int height;
370         static_surface->get_size(width, height);
371         // ensure we're within bounds
372         pos = clip(Vector<int>(0, 0), pos,
373                    Vector<int>(width, height) - Vector<int>(1, 1));
374
375         if (pos.x + dim.x >= int(GameMap::getWidth() * pixels_per_tile))
376             dim.x = int(GameMap::getWidth() * pixels_per_tile) - pos.x;
377         
378         if (pos.y + dim.y >= int(GameMap::getHeight() * pixels_per_tile))
379             dim.y = int(GameMap::getHeight() * pixels_per_tile) - pos.y;
380
381         draw_terrain_tiles(Rectangle(pos, dim));
382     }
383     draw(Playerlist::getViewingplayer());
384 }
385
386 void OverviewMap::draw_terrain_tiles(Rectangle r)
387 {
388     GameMap *gm = GameMap::getInstance();
389     unsigned int oldrand = rand();
390     srand(0);
391     Gdk::Color rd = GameMap::getInstance()->getTileset()->getRoadColor();
392     for (int i = r.x; i < r.x + r.w; ++i)
393         for (int j = r.y; j < r.y + r.h; ++j)
394         {
395             int x = int(i / pixels_per_tile);
396             int y = int(j / pixels_per_tile);
397
398             if (gm->getTile(x,y)->getBuilding() == Maptile::ROAD ||
399                      gm->getTile(x,y)->getBuilding() == Maptile::BRIDGE)
400                          draw_pixel(static_surface, static_surface_gc, i, j, rd);
401             else
402             {
403                 draw_terrain_tile (GameMap::getInstance()->getTile(x,y), i, j);
404             }
405         }
406     srand(oldrand);
407 }
408
409 void OverviewMap::after_draw()
410 {
411 }
412
413 void OverviewMap::draw(Player *player)
414 {
415     Playerlist::getInstance()->setViewingplayer(player);
416     int size = int(pixels_per_tile) > 1 ? int(pixels_per_tile) : 1;
417     assert(surface);
418
419
420     // During the whole drawing stuff, ALWAYS consider that 
421     // there is an offset of 1 between map coordinates and coordinates
422     // of the surface when drawing. I will implcitely assume this during this
423     // function.
424     
425     //Gdk::Color black = Gdk::Color();
426     //black.set_rgb_p(0,0,0);
427     //surface_gc->set_rgb_fg_color(black);
428     //surface->draw_rectangle(surface_gc, true, 0, 0, -1, -1);
429     //Glib::RefPtr<Gdk::Pixbuf> buf = Gdk::Pixbuf::create(static_surface, 0, 0, -1, -1);
430     surface->draw_drawable(surface_gc, static_surface, 0, 0, 0, 0, -1, -1);
431
432     // Draw ruins as a white dot
433         
434     Gdk::Color ruindotcolor = Gdk::Color();
435     ruindotcolor.set_rgb_p(100,100,100);
436     for (Ruinlist::iterator it = Ruinlist::getInstance()->begin();
437         it != Ruinlist::getInstance()->end(); it++)
438     {
439         Ruin *r = *it;
440         if (r->isHidden() == true && 
441             r->getOwner() != Playerlist::getViewingplayer())
442           continue;
443         if (r->isVisible(Playerlist::getViewingplayer()) == false)
444           continue;
445         Vector<int> pos = r->getPos();
446         pos = mapToSurface(pos);
447
448         draw_filled_rect(true, pos.x, pos.y, size, size, ruindotcolor);
449     }
450
451     // Draw temples as a white dot
452     Gdk::Color templedotcolor = Gdk::Color();
453     templedotcolor.set_rgb_p(100,100,100);
454     for (Templelist::iterator it = Templelist::getInstance()->begin();
455         it != Templelist::getInstance()->end(); it++)
456     {
457       Temple *t = *it;
458         if (t->isVisible(Playerlist::getViewingplayer()) == false)
459           continue;
460         Vector<int> pos = t->getPos();
461         pos = mapToSurface(pos);
462
463         draw_filled_rect(true, pos.x, pos.y, size, size, templedotcolor);
464     }
465
466     //fog it up
467     Gdk::Color fog_color = Gdk::Color();
468     fog_color.set_rgb_p(0,0,0);
469     for (int i = 0; i < GameMap::getWidth(); i++)
470         for (int j = 0; j < GameMap::getHeight(); j++)
471         {
472           Vector <int> pos;
473           pos.x = i;
474           pos.y = j;
475           if (Playerlist::getViewingplayer()->getFogMap()->isFogged(pos) == true)
476             {
477               pos = mapToSurface(pos);
478               draw_filled_rect(true, pos.x, pos.y, size, size, fog_color);
479             }
480         }
481
482     //the idea here is that we want to show what happens when an AI-owned
483     //stack moves through our area.  so we block the view of computer
484     //players after the fact.
485     if (Playerlist::getViewingplayer()->getType() != Player::HUMAN &&
486         GameScenarioOptions::s_hidden_map == true)
487       {
488         int width = 0;
489         int height = 0;
490         surface->get_size(width, height);
491         draw_filled_rect(true, 0, 0, width, height, fog_color);
492       }
493
494   if (blank_screen)
495     {
496       Gdk::Color fog_color = Gdk::Color();
497       fog_color.set_rgb_p(0.0,0.0,0.0);
498       int width = 0;
499       int height = 0;
500       surface->get_size(width, height);
501       surface_gc->set_rgb_fg_color(fog_color);
502       surface->draw_rectangle(surface_gc, true, 0,0,width, height);
503     }
504     // let derived classes do their job
505     after_draw();
506
507 }
508
509 Glib::RefPtr<Gdk::Pixmap> OverviewMap::get_surface()
510 {
511     return surface;
512 }
513
514 Vector<int> OverviewMap::mapFromScreen(Vector<int> pos)
515 {
516     int x = int(pos.x / pixels_per_tile);
517     int y = int(pos.y / pixels_per_tile);
518     
519     if (x >= GameMap::getWidth())
520         x = GameMap::getWidth() - 1;
521
522     if (y >= GameMap::getHeight())
523         y = GameMap::getHeight() - 1;
524     
525     return Vector<int>(x,y);
526 }
527
528 Vector<int> OverviewMap::mapToSurface(Vector<int> pos)
529 {
530     if (pos.x < 0 || pos.y < 0
531            || pos.x >= GameMap::getWidth() || pos.y >= GameMap::getHeight())
532       {
533         printf ("pos.x is %d, pos.y is %d\n", pos.x, pos.y);
534         printf ("width is %d, height is %d\n", GameMap::getWidth(),
535                 GameMap::getHeight());
536       }
537     assert(pos.x >= 0 && pos.y >= 0
538            && pos.x < GameMap::getWidth() && pos.y < GameMap::getHeight());
539
540     int x = int(round(pos.x * pixels_per_tile));
541     int y = int(round(pos.y * pixels_per_tile));
542
543     if (pixels_per_tile > 2)
544         // try to take the center position of the pixel
545         x += int(0.5 * pixels_per_tile);
546
547     if (pixels_per_tile > 2)
548         y += int(0.5 * pixels_per_tile);
549     
550     return Vector<int>(x, y);
551 }
552
553 void OverviewMap::draw_cities (bool all_razed)
554 {
555   GraphicsCache *gc = GraphicsCache::getInstance();
556
557   // Draw all cities as shields over the city location, in the colors of
558   // the players.
559   for (Citylist::iterator it = Citylist::getInstance()->begin();
560       it != Citylist::getInstance()->end(); it++)
561   {
562       City *c = *it;
563       PixMask *tmp;
564       if (c->isVisible(Playerlist::getViewingplayer()) == false)
565         continue;
566       if (c->isBurnt() == true || all_razed == true)
567         tmp = gc->getSmallRuinedCityPic();
568       else
569         tmp = gc->getShieldPic(0, c->getOwner());
570   
571       Vector<int> pos = c->getPos();
572       pos = mapToSurface(pos);
573       tmp->blit_centered(surface, pos);
574
575   }
576 }
577
578 void OverviewMap::blank(bool on)
579 {
580   blank_screen = on;
581   draw(Playerlist::getViewingplayer());
582 }