1 // Copyright (C) 2003 Michael Bartl
2 // Copyright (C) 2003, 2004, 2005, 2006, 2007 Ulf Lorenz
3 // Copyright (C) 2004, 2005 Bryan Duff
4 // Copyright (C) 2004, 2005, 2006 Andrea Paternesi
5 // Copyright (C) 2006, 2007, 2008, 2009 Ben Asselstine
6 // Copyright (C) 2007 Ole Laursen
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 3 of the License, or
11 // (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Library General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
32 #include "stacklist.h"
38 #include "signpostlist.h"
40 #include "templelist.h"
42 #include "armysetlist.h"
45 #include "bridgelist.h"
49 #include "playerlist.h"
52 #include "Configuration.h"
53 #include "GraphicsCache.h"
54 #include "MapRenderer.h"
56 #include "MapBackpack.h"
57 #include "GameScenarioOptions.h"
61 //#define debug(x) {cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<endl<<flush;}
65 : d_renderer(0), buffer(0), magnified_buffer(0), magnification_factor(1.0)
67 // note: we are not fully initialized before set_view is called
72 d_grid_toggled = false;
75 image = Gtk::Allocation(0, 0, 320, 200);
83 if (magnified_buffer == true)
84 magnified_buffer.clear();
89 void BigMap::set_view(Rectangle new_view)
91 int tilesize = GameMap::getInstance()->getTileset()->getTileSize();
96 outgoing->get_size(width, height);
97 if (view.dim == new_view.dim && buffer && image.get_width() == width && image.get_height() == height)
99 // someone wants us to move the view, not resize it, no need to
100 // construct new surfaces and all that stuff
102 // fixme: if we're moving the view, maybe there's some pixmap in common
103 // between this view and the new view. why render?
106 Vector<int> new_view_pos = get_view_pos_from_view();
108 if (view_pos != new_view_pos)
110 view_pos = new_view_pos;
111 draw(Playerlist::getViewingplayer());
118 view_pos = get_view_pos_from_view();
120 // now create a buffer surface which is two maptiles wider and
121 // higher than the screen you actually see. That is how smooth scrolling
122 // becomes comparatively easy. You just blit from the extended screen to
123 // the screen with some offset.
124 // this represents a 1 tile border around the outside of the picture.
125 // it gets rid of the black border.
130 buffer_view.dim = view.dim + Vector<int>(2, 2);
132 buffer = Gdk::Pixmap::create (Glib::RefPtr<Gdk::Drawable>(0), buffer_view.w * tilesize, buffer_view.h * tilesize, 24);
133 buffer_gc = Gdk::GC::create(buffer);
135 //now create the part that will go out to the gtk::image
136 if (outgoing == true)
138 outgoing = Gdk::Pixmap::create(Glib::RefPtr<Gdk::Drawable>(buffer), image.get_width(), image.get_height(), 24);
143 // now set the MapRenderer so that it draws on the buffer
144 d_renderer = new MapRenderer(buffer);
147 void BigMap::clip_viewable_buffer(Glib::RefPtr<Gdk::Pixmap> pixmap, Glib::RefPtr<Gdk::GC> gc, Vector<int> pos, Glib::RefPtr<Gdk::Pixmap> out)
149 //Glib::RefPtr<Gdk::Pixmap> outgoing = Gdk::Pixmap::create(Glib::RefPtr<Gdk::Drawable>(pixmap), image.get_width(), image.get_height(), 24) ;
152 pixmap->get_size(width,height);
153 out->draw_drawable(gc, pixmap, pos.x, pos.y, 0, 0, image.get_width(), image.get_height());
157 void BigMap::draw(Player *player, bool redraw_buffer)
159 // no size and buffer yet, return
162 Playerlist::getInstance()->setViewingplayer(player);
164 int tilesize = GameMap::getInstance()->getTileset()->getTileSize();
166 // align the buffer view
167 Vector<int> new_buffer_view = clip(
169 view.pos - Vector<int>(1, 1),
170 GameMap::get_dim() - buffer_view.dim + Vector<int>(1, 1));
171 buffer_view.pos = new_buffer_view;
177 // blit the visible part of buffer to the screen
178 Vector<int> p = view_pos - (buffer_view.pos * tilesize * magnification_factor);
179 //Glib::RefPtr<Gdk::Pixmap> outgoing;
180 if (magnification_factor != 1.0)
182 if (magnified_buffer == true)
183 magnified_buffer.clear();
184 magnified_buffer = magnify(buffer);
185 clip_viewable_buffer(magnified_buffer, buffer_gc, p, outgoing);
189 clip_viewable_buffer(buffer, buffer_gc, p, outgoing);
194 Gdk::Color fog_color = Gdk::Color();
195 fog_color.set_rgb_p(0.0,0.0,0.0);
198 outgoing->get_size(width, height);
199 Glib::RefPtr<Gdk::GC> outgoing_gc = Gdk::GC::create(outgoing);
200 outgoing_gc->set_rgb_fg_color(fog_color);
201 outgoing->draw_rectangle(outgoing_gc, true, 0, 0, width, height);
203 map_changed.emit(outgoing);
206 void BigMap::screen_size_changed(Gtk::Allocation box)
208 int ts = GameMap::getInstance()->getTileset()->getTileSize();
210 Rectangle new_view = view;
212 new_view.w = box.get_width() / (ts * magnification_factor) + 0;
213 new_view.h = box.get_height() / (ts * magnification_factor) + 0;
215 if (new_view.w <= GameMap::getWidth() && new_view.h <= GameMap::getHeight()
216 && new_view.w >= 0 && new_view.h >= 0)
218 new_view.pos = clip(Vector<int>(0,0), new_view.pos,
219 GameMap::get_dim() - new_view.dim);
221 if (image.get_width() != box.get_width() ||
222 image.get_height() != box.get_height() ||
227 view_changed.emit(view);
233 Vector<int> BigMap::get_view_pos_from_view()
235 Vector<int> screen_dim(image.get_width(), image.get_height());
236 int ts = GameMap::getInstance()->getTileset()->getTileSize();
238 // clip to make sure we don't see a black border at the bottom and right
239 return clip(Vector<int>(0, 0), view.pos * ts * magnification_factor,
240 GameMap::get_dim() * ts * magnification_factor - screen_dim);
243 Vector<int> BigMap::tile_to_buffer_pos(Vector<int> tile)
245 int ts = GameMap::getInstance()->getTileset()->getTileSize();
246 return (tile - buffer_view.pos) * ts;
249 Vector<int> BigMap::mouse_pos_to_tile(Vector<int> pos)
251 int ts = GameMap::getInstance()->getTileset()->getTileSize();
253 return (view_pos + pos) / (ts * magnification_factor);
256 Vector<int> BigMap::mouse_pos_to_tile_offset(Vector<int> pos)
258 int ts = GameMap::getInstance()->getTileset()->getTileSize();
260 return (view_pos + pos) % (int)rint(ts * magnification_factor);
263 MapTipPosition BigMap::map_tip_position(Vector<int> tile)
265 return map_tip_position (Rectangle(tile.x, tile.y, 1, 1));
268 MapTipPosition BigMap::map_tip_position(Rectangle tile_area)
270 // convert area to pixels on the screen
271 int tilesize = GameMap::getInstance()->getTileset()->getTileSize();
273 Rectangle area(tile_area.pos * tilesize * magnification_factor - view_pos,
274 tile_area.dim * tilesize * magnification_factor);
276 // calculate screen edge distances
277 int left, right, top, bottom;
280 right = image.get_width() - (area.x + area.w);
282 bottom = image.get_height() - (area.y + area.h);
284 int const MARGIN = 2;
286 // then set the position
288 if (right >= left && right >= top && right >= bottom)
290 m.pos.x = area.x + area.w + MARGIN;
292 m.justification = MapTipPosition::LEFT;
294 else if (left >= top && left >= bottom)
296 m.pos.x = area.x - MARGIN;
298 m.justification = MapTipPosition::RIGHT;
300 else if (bottom >= top)
303 m.pos.y = area.y + area.h + MARGIN;
304 m.justification = MapTipPosition::TOP;
309 m.pos.y = area.y - MARGIN;
310 m.justification = MapTipPosition::BOTTOM;
316 void BigMap::blit_object(const Location &obj, Vector<int> tile, PixMask *image, Glib::RefPtr<Gdk::Pixmap> surface, Glib::RefPtr<Gdk::GC> surface_gc)
318 Vector<int> diff = tile - obj.getPos();
319 int tilesize = GameMap::getInstance()->getTileset()->getTileSize();
320 Vector<int> p = tile_to_buffer_pos(tile);
321 image->blit(diff, tilesize, surface, p);
324 void BigMap::draw_stack(Stack *s, Glib::RefPtr<Gdk::Pixmap> surface, Glib::RefPtr<Gdk::GC> surface_gc)
326 GameMap *gm = GameMap::getInstance();
327 GraphicsCache *gc = GraphicsCache::getInstance();
328 Vector<int> p = s->getPos();
329 Player *player = s->getOwner();
332 // check if the object lies in the viewed part of the map
333 // otherwise we shouldn't draw it
334 if (is_inside(buffer_view, p) && !s->getDeleting())
336 Armysetlist *al = Armysetlist::getInstance();
337 army_tilesize = al->getTileSize(player->getArmyset());
340 std::cerr << "WARNING: empty stack found" << std::endl;
344 p = tile_to_buffer_pos(p);
348 bool show_army = true;
351 gc->getShipPic(player)->blit(surface, p);
355 if (s->getFortified() == true)
357 //We don't show the active stack here.
358 if (player->getStacklist()->getActivestack() != s &&
359 player == Playerlist::getActiveplayer())
361 Maptile *tile = gm->getTile(s->getPos());
362 if (tile->getBuilding() != Maptile::CITY &&
363 tile->getBuilding() != Maptile::RUIN &&
364 tile->getBuilding() != Maptile::TEMPLE)
365 gc->getTowerPic(player)->blit(surface, p);
370 if (show_army == true)
372 Army *a = *s->begin();
373 gc->getArmyPic(a)->blit(surface, p);
381 gc->getFlagPic(s)->blit(surface, p);
386 void BigMap::draw_buffer()
388 draw_buffer (buffer_view, buffer, buffer_gc);
390 //the idea here is that we want to show what happens when an AI-owned
391 //stack moves through our area. in lieu of that, we just block everything
392 //if we're a computer player.
393 if (Playerlist::getViewingplayer()->getType() != Player::HUMAN &&
394 GameScenarioOptions::s_hidden_map == true)
398 buffer->get_size(width, height);
399 Gdk::Color fog_color = Gdk::Color();
400 fog_color.set_rgb_p(0,0,0);
401 buffer_gc->set_rgb_fg_color(fog_color);
402 buffer->draw_rectangle(buffer_gc, true, 0, 0, width, height);
409 bool BigMap::saveViewAsBitmap(std::string filename)
413 buffer->get_size(width, height);
414 remove (filename.c_str());
415 Glib::RefPtr<Gdk::Pixbuf> pixbuf = Gdk::Pixbuf::create(Glib::RefPtr<Gdk::Drawable>(buffer), 0, 0, width, height);
416 pixbuf->save (filename, "png");
420 bool BigMap::saveUnderlyingMapAsBitmap(std::string filename)
422 return d_renderer->saveAsBitmap(filename);
425 bool BigMap::saveAsBitmap(std::string filename)
427 int tilesize = GameMap::getInstance()->getTileset()->getTileSize();
428 int width = GameMap::getWidth() * tilesize;
429 int height = GameMap::getHeight() * tilesize;
430 Glib::RefPtr<Gdk::Pixmap> surf = Gdk::Pixmap::create(Glib::RefPtr<Gdk::Drawable>(0), width, height, 24);
432 bool orig_grid = d_grid_toggled;
433 d_grid_toggled = false;
434 draw_buffer(Rectangle (0, 0, GameMap::getWidth(), GameMap::getHeight()), surf,
435 Gdk::GC::create(surf));
436 d_grid_toggled = orig_grid;
437 Glib::RefPtr<Gdk::Pixbuf> pixbuf = Gdk::Pixbuf::create(Glib::RefPtr<Gdk::Drawable>(surf), 0, 0, width, height);
438 pixbuf->save (filename, "png");
442 void BigMap::draw_buffer_tile(Vector<int> tile, Glib::RefPtr<Gdk::Pixmap> surface, Glib::RefPtr<Gdk::GC> context)
444 guint32 tilesize = GameMap::getInstance()->getTileset()->getTileSize();
445 Player *viewing = Playerlist::getViewingplayer();
446 GraphicsCache *gc = GraphicsCache::getInstance();
447 GameMap *gm = GameMap::getInstance();
448 int tile_style_id = gm->getTile(tile)->getTileStyle()->getId();
450 fog_type_id = viewing->getFogMap()->getShadeTile(tile);
452 bool has_bag = false;
453 bool has_standard = false;
454 guint32 player_standard_id = 0;
456 int stack_player_id = -1;
457 int army_type_id = -1;
458 bool has_ship = false;
459 bool has_tower = false;
460 Maptile::Building building_type = gm->getTile(tile)->getBuilding();
461 Vector<int> building_tile = Vector<int>(-1,-1);
462 int building_subtype = -1;
463 int building_player_id = -1;
465 if (fog_type_id == FogMap::ALL)
467 //short circuit. the tile is completely fogged.
469 gc->getTilePic(tile_style_id, fog_type_id, has_bag, has_standard,
470 player_standard_id, stack_size, stack_player_id,
471 army_type_id, has_tower, has_ship, building_type,
472 building_subtype, building_tile, building_player_id,
473 tilesize, d_grid_toggled);
474 pixmask->blit(surface, tile_to_buffer_pos(tile));
477 MapBackpack *backpack = GameMap::getInstance()->getTile(tile)->getBackpack();
478 if (backpack && backpack->empty() == false)
480 bool standard_planted = false;
481 Item *flag = backpack->getFirstPlantedItem();
483 standard_planted = true;
485 //only show one of the bag or the flag
486 if (standard_planted && flag)
489 player_standard_id = flag->getPlantableOwner()->getId();
495 Stack *stack = GameMap::getStack(tile);
498 if (viewing->getFogMap()->isCompletelyObscuredFogTile(tile) == false)
500 //selected stack gets drawn in gamebigmap
501 if (Playerlist::getActiveplayer()->getActivestack() != stack)
503 stack_player_id = stack->getOwner()->getId();
504 Maptile *m = gm->getTile(tile);
505 if (stack->getFortified() == true &&
506 m->getBuilding() != Maptile::CITY &&
507 m->getBuilding() != Maptile::RUIN &&
508 m->getBuilding() != Maptile::TEMPLE)
510 else if (stack->hasShip() == true)
513 stack_size = stack->size();
517 army_type_id = (*stack->begin())->getTypeId();
518 stack_size = stack->size();
524 if (building_type != Maptile::NONE)
526 switch (building_type)
530 City *city = GameMap::getCity(tile);
531 building_player_id = city->getOwner()->getId();
532 building_tile = tile - city->getPos();
534 building_subtype = -1;
536 building_subtype = 0;
541 Ruin *ruin = GameMap::getRuin(tile);
542 if (ruin->isHidden() == true && ruin->getOwner() == viewing)
544 building_tile = tile - ruin->getPos();
545 building_subtype = ruin->getType();
547 else if (ruin->isHidden() == false)
549 building_tile = tile - ruin->getPos();
550 building_subtype = ruin->getType();
553 building_type = Maptile::NONE;
556 case Maptile::TEMPLE:
558 Temple *temple = GameMap::getTemple(tile);
559 building_tile = tile - temple->getPos();
560 building_subtype = temple->getType();
563 case Maptile::SIGNPOST:
565 Signpost *signpost = GameMap::getSignpost(tile);
566 building_tile = tile - signpost->getPos();
571 Road *road = GameMap::getRoad(tile);
572 building_tile = tile - road->getPos();
573 building_subtype = road->getType();
578 Port *port = GameMap::getPort(tile);
579 building_tile = tile - port->getPos();
582 case Maptile::BRIDGE:
584 Bridge *bridge = GameMap::getBridge(tile);
585 building_tile = tile - bridge->getPos();
586 building_subtype = bridge->getType();
589 case Maptile::NONE: default:
594 gc->getTilePic(tile_style_id, fog_type_id, has_bag, has_standard,
595 player_standard_id, stack_size, stack_player_id,
596 army_type_id, has_tower, has_ship, building_type,
597 building_subtype, building_tile, building_player_id,
598 tilesize, d_grid_toggled);
599 pixmask->blit(surface, tile_to_buffer_pos(tile));
602 void BigMap::draw_buffer_tiles(Rectangle map_view, Glib::RefPtr<Gdk::Pixmap> surface, Glib::RefPtr<Gdk::GC> context)
604 for (int i = map_view.x; i < map_view.x + map_view.w; i++)
605 for (int j = map_view.y; j < map_view.y + map_view.h; j++)
606 if (i < GameMap::getWidth() && j < GameMap::getHeight())
607 draw_buffer_tile(Vector<int>(i,j), surface, context);
610 void BigMap::draw_buffer(Rectangle map_view, Glib::RefPtr<Gdk::Pixmap> surface, Glib::RefPtr<Gdk::GC> context)
612 draw_buffer_tiles(map_view, surface, context);
615 //here we want to magnify the entire buffer, not a subset
616 Glib::RefPtr<Gdk::Pixmap> BigMap::magnify(Glib::RefPtr<Gdk::Pixmap> orig)
618 //magnify the buffer into a buffer of the correct size
622 orig->get_size(width, height);
623 if (width == 0 || height == 0)
625 Glib::RefPtr<Gdk::Pixmap> result =
626 Gdk::Pixmap::create(Glib::RefPtr<Gdk::Drawable>(orig),
627 width * magnification_factor,
628 height * magnification_factor);
629 Glib::RefPtr<Gdk::Pixbuf> unzoomed_buffer;
630 unzoomed_buffer = Gdk::Pixbuf::create(Glib::RefPtr<Gdk::Drawable>(orig), 0, 0, width, height);
632 Glib::RefPtr<Gdk::Pixbuf> zoomed_buffer;
633 zoomed_buffer = unzoomed_buffer->scale_simple(width * magnification_factor, height * magnification_factor, Gdk::INTERP_BILINEAR);
634 Glib::RefPtr<Gdk::GC> tmpgc = Gdk::GC::create(result);
635 result->draw_pixbuf(tmpgc, zoomed_buffer, 0, 0, 0, 0, zoomed_buffer->get_width(),
636 zoomed_buffer->get_height(), Gdk::RGB_DITHER_NONE, 0, 0);
641 void BigMap::toggle_grid()
643 d_grid_toggled = !d_grid_toggled;
644 draw(Playerlist::getViewingplayer(), true);
647 void BigMap::blank(bool on)
650 draw (Playerlist::getViewingplayer());