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, 2010, 2014, 2015, 2016, 2017,
6 // 2020 Ben Asselstine
7 // Copyright (C) 2007 Ole Laursen
8 //
9 // This program is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation; either version 3 of the License, or
12 // (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU Library General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 // 02110-1301, USA.
23
24 #include <config.h>
25
26 #include <assert.h>
27
28 #include "bigmap.h"
29
30 #include "army.h"
31 #include "Item.h"
32 #include "stacklist.h"
33 #include "stack.h"
34 #include "city.h"
35 #include "ruin.h"
36 #include "signpost.h"
37 #include "temple.h"
38 #include "port.h"
39 #include "bridge.h"
40 #include "road.h"
41 #include "stone.h"
42 #include "playerlist.h"
43 #include "File.h"
44 #include "stacktile.h"
45 #include "GameMap.h"
46 #include "ImageCache.h"
47 #include "MapRenderer.h"
48 #include "FogMap.h"
49 #include "MapBackpack.h"
50 #include "GameScenarioOptions.h"
51 #include "tileset.h"
52
53 #include <iostream>
54 //#define debug(x) {std::cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<std::endl<<std::flush;}
55 #define debug(x)
56
57 bool BigMap::s_show_hidden_ruins;
58
BigMap(bool headless)59 BigMap::BigMap(bool headless)
60 : d_headless (headless), d_renderer(0), buffer(0), d_fighting(LocationBox(Vector<int>(-1,-1)))
61 {
62 // note: we are not fully initialized before set_view is called
63 view.x = view.y = 0;
64 view.w = 0;
65 view.h = 0;
66 deltax = 0;
67 deltay = 0;
68 view_pos = Vector<int>(0,0);
69 input_locked = false;
70 d_grid_toggled = false;
71
72 blank_screen = false;
73 image = Gtk::Allocation(0, 0, 320, 200);
74 }
75
~BigMap()76 BigMap::~BigMap()
77 {
78 if (buffer)
79 buffer.clear();
80
81 delete d_renderer;
82 }
83
set_view(LwRectangle new_view)84 void BigMap::set_view(LwRectangle new_view)
85 {
86 int tilesize = GameMap::getInstance()->getTileSize();
87
88 int width = image.get_width();
89 int height = image.get_height();
90 if (view.dim == new_view.dim && buffer && image.get_width() == width && image.get_height() == height)
91 {
92 // someone wants us to move the view, not resize it, no need to
93 // construct new surfaces and all that stuff
94 //
95 // fixme: if we're moving the view, maybe there's some pixmap in common
96 // between this view and the new view. why render?
97
98 view = new_view;
99 Vector<int> new_view_pos = get_view_pos_from_view();
100
101 if (view_pos != new_view_pos)
102 {
103 view_pos = new_view_pos;
104 draw();
105 }
106
107 return;
108 }
109
110 view = new_view;
111 view_pos = get_view_pos_from_view();
112
113 // now create a buffer surface which is two maptiles wider and
114 // higher than the screen you actually see. That is how smooth scrolling
115 // becomes comparatively easy. You just blit from the extended screen to
116 // the screen with some offset.
117 // this represents a 1 tile border around the outside of the picture.
118 // it gets rid of the black border.
119
120 if (buffer)
121 buffer.clear();
122
123 buffer_view.dim = view.dim + Vector<int>(2, 2);
124
125 Cairo::RefPtr<Cairo::Surface> empty = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, buffer_view.w *tilesize, buffer_view.h * tilesize);
126 buffer = Cairo::Surface::create (empty, Cairo::CONTENT_COLOR_ALPHA, buffer_view.w * tilesize, buffer_view.h * tilesize);
127 buffer_gc = Cairo::Context::create(buffer);
128
129 //now create the part that will go out to the gtk::image
130 if (outgoing)
131 outgoing.clear();
132 outgoing = Cairo::Surface::create(buffer, Cairo::CONTENT_COLOR_ALPHA, image.get_width(), image.get_height());
133
134
135 if (d_renderer)
136 delete d_renderer;
137 // now set the MapRenderer so that it draws on the buffer
138 d_renderer = new MapRenderer(buffer);
139 }
140
clip_viewable_buffer(Cairo::RefPtr<Cairo::Surface> pixmap,Vector<int> pos,Cairo::RefPtr<Cairo::Surface> out)141 void BigMap::clip_viewable_buffer(Cairo::RefPtr<Cairo::Surface> pixmap, Vector<int> pos, Cairo::RefPtr<Cairo::Surface> out)
142 {
143 Cairo::RefPtr<Cairo::Context> out_gc = Cairo::Context::create(out);
144 out_gc->rectangle(0, 0, image.get_width(), image.get_height());
145 out_gc->clip();
146 out_gc->save();
147 out_gc->set_source(pixmap, -pos.x, -pos.y);
148 out_gc->rectangle (0, 0, image.get_width(), image.get_height());
149 out_gc->clip();
150 out_gc->paint();
151 out_gc->restore();
152 return;
153 }
154
draw(bool redraw_buffer)155 void BigMap::draw(bool redraw_buffer)
156 {
157 // no size and buffer yet, return
158 if (!buffer || d_headless)
159 return;
160 //Playerlist::getInstance()->setViewingplayer(player);
161
162 int tilesize = GameMap::getInstance()->getTileSize();
163
164 // align the buffer view
165 Vector<int> new_buffer_view = clip(
166 Vector<int>(0, 0),
167 view.pos - Vector<int>(1, 1),
168 GameMap::get_dim() - buffer_view.dim + Vector<int>(1, 1));
169 buffer_view.pos = new_buffer_view;
170
171 // redraw the buffer
172 if (redraw_buffer)
173 draw_buffer();
174
175 // blit the visible part of buffer to the screen
176 Vector<int> p = view_pos - (buffer_view.pos * tilesize);
177 outgoing.clear();
178 outgoing = Cairo::Surface::create(buffer, Cairo::CONTENT_COLOR_ALPHA, image.get_width(), image.get_height());
179 clip_viewable_buffer(buffer, p, outgoing);
180
181 if (blank_screen)
182 {
183 int width = image.get_width();
184 int height = image.get_height();
185 Cairo::RefPtr<Cairo::Context> outgoing_gc = Cairo::Context::create(outgoing);
186 outgoing_gc->set_source_rgba(FOG_COLOUR.get_red(), FOG_COLOUR.get_green(), FOG_COLOUR.get_blue(), FOG_COLOUR.get_alpha());
187 outgoing_gc->rectangle(0, 0, width, height);
188 outgoing_gc->fill();
189 }
190 map_changed.emit(outgoing);
191 }
192
screen_size_changed(Gtk::Allocation box)193 void BigMap::screen_size_changed(Gtk::Allocation box)
194 {
195 int ts = GameMap::getInstance()->getTileSize();
196
197 LwRectangle new_view = view;
198
199 new_view.w = box.get_width() / ts;
200 new_view.h = box.get_height() / ts;
201
202 if (new_view.w <= GameMap::getWidth() && new_view.h <= GameMap::getHeight()
203 && new_view.w >= 0 && new_view.h >= 0)
204 {
205 new_view.pos = clip(Vector<int>(0,0), new_view.pos,
206 GameMap::get_dim() - new_view.dim);
207 image = box;
208 set_view(new_view);
209 view_changed.emit(view);
210 }
211 image = box;
212 }
213
get_view_pos_from_view()214 Vector<int> BigMap::get_view_pos_from_view()
215 {
216 Vector<int> screen_dim(image.get_width(), image.get_height());
217 int ts = GameMap::getInstance()->getTileSize();
218
219 // clip to make sure we don't see a black border at the bottom and right
220 return clip(Vector<int>(0, 0), view.pos * ts,
221 GameMap::get_dim() * ts - screen_dim);
222 }
223
tile_to_buffer_pos(Vector<int> tile)224 Vector<int> BigMap::tile_to_buffer_pos(Vector<int> tile)
225 {
226 int ts = GameMap::getInstance()->getTileSize();
227 return (tile - buffer_view.pos) * ts;
228 }
229
mouse_pos_to_tile(Vector<int> pos)230 Vector<int> BigMap::mouse_pos_to_tile(Vector<int> pos)
231 {
232 int ts = GameMap::getInstance()->getTileSize();
233 return (view_pos + pos) / ts;
234 }
235
mouse_pos_to_tile_offset(Vector<int> pos)236 Vector<int> BigMap::mouse_pos_to_tile_offset(Vector<int> pos)
237 {
238 int ts = GameMap::getInstance()->getTileSize();
239 return (view_pos + pos) % ts;
240 }
241
map_tip_position(Vector<int> tile)242 MapTipPosition BigMap::map_tip_position(Vector<int> tile)
243 {
244 return map_tip_position (LwRectangle(tile.x, tile.y, 1, 1));
245 }
246
map_tip_position(LwRectangle tile_area)247 MapTipPosition BigMap::map_tip_position(LwRectangle tile_area)
248 {
249 // convert area to pixels on the screen
250 int tilesize = GameMap::getInstance()->getTileSize();
251
252 LwRectangle area(tile_area.pos * tilesize - view_pos,
253 tile_area.dim * tilesize);
254
255 // calculate screen edge distances
256 int left, right, top, bottom;
257
258 left = area.x;
259 right = image.get_width() - (area.x + area.w);
260 top = area.y;
261 bottom = image.get_height() - (area.y + area.h);
262
263 int const MARGIN = 2;
264
265 // then set the position
266 MapTipPosition m;
267 if (right >= left && right >= top && right >= bottom)
268 {
269 m.pos.x = area.x + area.w + MARGIN;
270 m.pos.y = area.y;
271 m.justification = MapTipPosition::LEFT;
272 }
273 else if (left >= top && left >= bottom)
274 {
275 m.pos.x = area.x - MARGIN;
276 m.pos.y = area.y;
277 m.justification = MapTipPosition::RIGHT;
278 }
279 else if (bottom >= top)
280 {
281 m.pos.x = area.x;
282 m.pos.y = area.y + area.h + MARGIN;
283 m.justification = MapTipPosition::TOP;
284 }
285 else
286 {
287 m.pos.x = area.x;
288 m.pos.y = area.y - MARGIN;
289 m.justification = MapTipPosition::BOTTOM;
290 }
291
292 return m;
293 }
294
blit_object(const Location & obj,Vector<int> tile,PixMask * im,Cairo::RefPtr<Cairo::Surface> surface)295 void BigMap::blit_object(const Location &obj, Vector<int> tile, PixMask *im, Cairo::RefPtr<Cairo::Surface> surface)
296 {
297 Vector<int> diff = tile - obj.getPos();
298 int tilesize = GameMap::getInstance()->getTileSize();
299 Vector<int> p = tile_to_buffer_pos(tile);
300 im->blit(diff, tilesize, surface, p);
301 }
302
draw_stack(Stack * s,Cairo::RefPtr<Cairo::Surface> surface)303 void BigMap::draw_stack(Stack *s, Cairo::RefPtr<Cairo::Surface> surface)
304 {
305 //this routine is for drawing the active stack.
306 //for all other stacks see ImageCache::draw_tile_pic
307 Vector<int> p = s->getPos();
308 Player *player = s->getOwner();
309 int tilesize = GameMap::getInstance()->getTileSize();
310
311 // check if the object lies in the viewed part of the map
312 // otherwise we shouldn't draw it
313 if (is_inside(buffer_view, p) && !s->getDeleting())
314 {
315 if (s->empty())
316 {
317 std::cerr << "WARNING: empty stack found" << std::endl;
318 return;
319 }
320
321 p = tile_to_buffer_pos(p);
322
323 // draw stack
324
325 bool show_army = true;
326 //we don't show the army or the flag if we're in fortified tent.
327 if (s->hasShip())
328 {
329 PixMask *ship = ImageCache::getInstance()->getShipPic(player)->copy();
330 ship->scale (ship, tilesize, tilesize);
331 ship->blit(surface, p);
332 delete ship;
333 }
334 else
335 {
336 if (s->getFortified() == true)
337 {
338 //We don't show the active stack here.
339 if (player->getStacklist()->getActivestack() != s &&
340 player == Playerlist::getActiveplayer())
341 show_army = false;
342 Maptile *tile = GameMap::getInstance()->getTile(s->getPos());
343 if (tile->getBuilding() != Maptile::CITY &&
344 tile->getBuilding() != Maptile::RUIN &&
345 tile->getBuilding() != Maptile::TEMPLE)
346 {
347 PixMask *tower = ImageCache::getInstance()->getTowerPic(player)->copy();
348 tower->scale (tower, tilesize, tilesize);
349 tower->blit(surface, p);
350 delete tower;
351 }
352 else
353 show_army = true;
354 }
355
356 if (show_army == true)
357 {
358 Army *a = *s->begin();
359 PixMask *armypic = ImageCache::getInstance()->getArmyPic(a)->copy();
360 armypic->scale (armypic, tilesize, tilesize);
361 armypic->blit(surface, p);
362 delete armypic;
363 }
364 }
365
366 if (show_army)
367 {
368 //does our position have us on that stacktile?
369 /*
370 * sometimes the stack tile isn't updated right away.
371 */
372 StackTile *st = GameMap::getStacks(s->getPos());
373 guint32 stacksize;
374 if (!st->contains(s->getId()))
375 stacksize = st->countNumberOfArmies(player) + s->size();
376 else
377 stacksize = st->countNumberOfArmies(player);
378 if (stacksize > MAX_STACK_SIZE)
379 stacksize = MAX_STACK_SIZE;
380 if (stacksize > 0)
381 {
382 PixMask *flag = ImageCache::getInstance()->getFlagPic(stacksize, player)->copy();
383 flag->scale (flag, tilesize, tilesize);
384 flag->blit(surface, p);
385 delete flag;
386
387 }
388 }
389 }
390 }
391
draw_buffer()392 void BigMap::draw_buffer()
393 {
394 draw_buffer (buffer_view, buffer);
395 // if we're hidden map this is hosed.
396 after_draw();
397 if (blank_screen == false)
398 {
399 ImageCache *gc = ImageCache::getInstance();
400 int tilesize = GameMap::getInstance()->getTileSize();
401 if (d_fighting.getPos() != Vector<int>(-1,-1))
402 {
403 Vector<int> p = tile_to_buffer_pos(d_fighting.getPos());
404 PixMask *tmp = gc->getExplosionPic()->copy();
405 if (d_fighting.getSize() > 1)
406 {
407 PixMask::scale(tmp, d_fighting.getSize() * tilesize,
408 d_fighting.getSize() * tilesize);
409 }
410 tmp->blit(buffer, p);
411 delete tmp;
412 }
413 }
414 }
415
saveAsBitmap(Glib::ustring filename)416 bool BigMap::saveAsBitmap(Glib::ustring filename)
417 {
418 int tilesize = GameMap::getInstance()->getTileSize();
419 int width = GameMap::getWidth() * tilesize;
420 int height = GameMap::getHeight() * tilesize;
421 Cairo::RefPtr<Cairo::Surface> empty = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, width, height);
422 Cairo::RefPtr<Cairo::Surface> surf = Cairo::Surface::create (empty, Cairo::CONTENT_COLOR_ALPHA, width, height);
423
424 bool orig_grid = d_grid_toggled;
425 d_grid_toggled = false;
426 draw_buffer(LwRectangle (0, 0, GameMap::getWidth(), GameMap::getHeight()), surf);
427 d_grid_toggled = orig_grid;
428 Glib::RefPtr<Gdk::Pixbuf> pixbuf = Gdk::Pixbuf::create(surf, 0, 0, width, height);
429 pixbuf->save (filename, "png");
430 return true;
431 }
432
draw_buffer_tile(Vector<int> tile,Cairo::RefPtr<Cairo::Surface> surface)433 void BigMap::draw_buffer_tile(Vector<int> tile, Cairo::RefPtr<Cairo::Surface> surface)
434 {
435 guint32 tilesize = GameMap::getInstance()->getTileSize();
436 Player *viewing = Playerlist::getViewingplayer();
437 ImageCache *gc = ImageCache::getInstance();
438 int tile_style_id = GameMap::getInstance()->getTile(tile)->getTileStyle()->getId();
439 int fog_type_id = 0;
440 if (Playerlist::getViewingplayer()->getType() != Player::HUMAN &&
441 GameScenarioOptions::s_hidden_map == true)
442 fog_type_id = FogMap::ALL;
443 else
444 fog_type_id = viewing->getFogMap()->getShadeTile(tile);
445
446 bool has_bag = false;
447 bool has_standard = false;
448 guint32 player_standard_id = 0;
449 int stack_size = -1;
450 int stack_player_id = -1;
451 int army_type_id = -1;
452 bool has_ship = false;
453 bool has_tower = false;
454 auto building_type = GameMap::getInstance()->getTile(tile)->getBuilding();
455 Vector<int> building_tile = Vector<int>(-1,-1);
456 int building_subtype = -1;
457 int building_player_id = -1;
458 int stone_type = -1;
459
460 if (fog_type_id == FogMap::ALL)
461 {
462 //short circuit. the tile is completely fogged.
463 PixMask *pixmask =
464 gc->getTilePic(tile_style_id, fog_type_id, has_bag, has_standard,
465 player_standard_id, stack_size, stack_player_id,
466 army_type_id, has_tower, has_ship, building_type,
467 building_subtype, building_tile, building_player_id,
468 tilesize, d_grid_toggled, stone_type);
469 pixmask->blit(surface, tile_to_buffer_pos(tile));
470 return;
471 }
472 MapBackpack *backpack = GameMap::getInstance()->getTile(tile)->getBackpack();
473 if (backpack && backpack->empty() == false)
474 {
475 bool standard_planted = false;
476 Item *flag = backpack->getFirstPlantedItem();
477 if (flag)
478 standard_planted = true;
479
480 //only show one of the bag or the flag
481 if (standard_planted && flag)
482 {
483 has_standard = true;
484 player_standard_id = flag->getPlantableOwner()->getId();
485 }
486 else
487 has_bag = true;
488 }
489
490 Stack *stack = GameMap::getStrongestStack(tile);
491 if (stack)
492 {
493 if (viewing->getFogMap()->isCompletelyObscuredFogTile(tile) == false)
494 {
495 //selected stack gets drawn in gamebigmap
496 if (Playerlist::getActiveplayer()->getActivestack() != stack)
497 {
498 stack_player_id = stack->getOwner()->getId();
499 Maptile *m = GameMap::getInstance()->getTile(tile);
500 if (stack->getFortified() == true &&
501 m->getBuilding() != Maptile::CITY &&
502 m->getBuilding() != Maptile::RUIN &&
503 m->getBuilding() != Maptile::TEMPLE)
504 has_tower = true;
505 else if (stack->hasShip() == true)
506 {
507 has_ship = true;
508 stack_size = GameMap::getStacks(stack->getPos())->countNumberOfArmies(Playerlist::getInstance()->getPlayer(stack_player_id));
509 if (stack_size > 0 && (guint)stack_size > MAX_STACK_SIZE)
510 stack_size = stack->size();
511 //here we show the number of armies on the tile.
512 //instead of the number of armies in the stack.
513 //so that stacks appear whole before we click on them.
514 //and that a stack of 1 can't hide a stack of 7.
515 }
516 else
517 {
518 army_type_id = (*stack->begin())->getTypeId();
519 stack_size = GameMap::getStacks(stack->getPos())->countNumberOfArmies(Playerlist::getInstance()->getPlayer(stack_player_id));
520 if (stack_size > 0 && (guint)stack_size > MAX_STACK_SIZE)
521 stack_size = stack->size();
522 }
523 }
524 }
525 }
526
527 if (building_type != Maptile::NONE)
528 {
529 switch (building_type)
530 {
531 case Maptile::CITY:
532 {
533 City *city = GameMap::getCity(tile);
534 building_player_id = city->getOwner()->getId();
535 building_tile = tile - city->getPos();
536 if (city->isBurnt())
537 building_subtype = -1;
538 else
539 building_subtype = 0;
540 }
541 break;
542 case Maptile::RUIN:
543 {
544 Ruin *ruin = GameMap::getRuin(tile);
545 if (ruin->isHidden() == true && ruin->getOwner() == viewing)
546 {
547 building_tile = tile - ruin->getPos();
548 building_subtype = ruin->getType();
549 }
550 else if (ruin->isHidden() == false)
551 {
552 building_tile = tile - ruin->getPos();
553 building_subtype = ruin->getType();
554 }
555 else if (s_show_hidden_ruins)
556 {
557 building_tile = tile - ruin->getPos();
558 building_subtype = ruin->getType();
559 }
560 else
561 building_type = Maptile::NONE;
562 }
563 break;
564 case Maptile::TEMPLE:
565 {
566 Temple *temple = GameMap::getTemple(tile);
567 building_tile = tile - temple->getPos();
568 building_subtype = temple->getType();
569 }
570 break;
571 case Maptile::SIGNPOST:
572 {
573 Signpost *signpost = GameMap::getSignpost(tile);
574 building_tile = tile - signpost->getPos();
575 }
576 break;
577 case Maptile::ROAD:
578 {
579 Road *road = GameMap::getRoad(tile);
580 building_tile = tile - road->getPos();
581 building_subtype = road->getType();
582 Stone *stone = GameMap::getStone(tile);
583 if (stone)
584 stone_type = stone->getType();
585 }
586 break;
587 case Maptile::STONE:
588 {
589 Stone *stone = GameMap::getStone(tile);
590 building_tile = tile - stone->getPos();
591 building_subtype = stone->getType();
592 stone_type = stone->getType();
593 }
594 break;
595 case Maptile::PORT:
596 {
597 Port *port = GameMap::getPort(tile);
598 building_tile = tile - port->getPos();
599 }
600 break;
601 case Maptile::BRIDGE:
602 {
603 Bridge *bridge = GameMap::getBridge(tile);
604 building_tile = tile - bridge->getPos();
605 building_subtype = bridge->getType();
606 }
607 break;
608 case Maptile::NONE: default:
609 break;
610 }
611 }
612 if (GameMap::getTileset()->getStonesFilename().empty() == true)
613 stone_type = -1;
614 PixMask *pixmask =
615 gc->getTilePic(tile_style_id, fog_type_id, has_bag, has_standard,
616 player_standard_id, stack_size, stack_player_id,
617 army_type_id, has_tower, has_ship, building_type,
618 building_subtype, building_tile, building_player_id,
619 tilesize, d_grid_toggled, stone_type);
620 pixmask->blit(surface, tile_to_buffer_pos(tile));
621 }
622
draw_buffer_tiles(LwRectangle map_view,Cairo::RefPtr<Cairo::Surface> surface)623 void BigMap::draw_buffer_tiles(LwRectangle map_view, Cairo::RefPtr<Cairo::Surface> surface)
624 {
625 for (int i = map_view.x; i < map_view.x + map_view.w; i++)
626 for (int j = map_view.y; j < map_view.y + map_view.h; j++)
627 if (i < GameMap::getWidth() && j < GameMap::getHeight())
628 draw_buffer_tile(Vector<int>(i,j), surface);
629 }
630
draw_buffer(LwRectangle map_view,Cairo::RefPtr<Cairo::Surface> surface)631 void BigMap::draw_buffer(LwRectangle map_view, Cairo::RefPtr<Cairo::Surface> surface)
632 {
633 draw_buffer_tiles(map_view, surface);
634 }
635
toggle_grid()636 void BigMap::toggle_grid()
637 {
638 d_grid_toggled = !d_grid_toggled;
639 draw(true);
640 }
641
blank(bool on)642 void BigMap::blank(bool on)
643 {
644 blank_screen = on;
645 draw (Playerlist::getViewingplayer());
646 }
647
scroll(GdkEventScroll * event)648 bool BigMap::scroll(GdkEventScroll *event)
649 {
650 if (input_locked)
651 return true;
652 LwRectangle n = view;
653 switch (event->direction)
654 {
655 case GDK_SCROLL_SMOOTH:
656 {
657 double dx, dy;
658 if (gdk_event_get_scroll_deltas ((GdkEvent*)event, &dx, &dy))
659 {
660 if (event->state & GDK_SHIFT_MASK)
661 {
662 dx*=-1;
663 dy*=-1;
664 }
665 if (event->state & GDK_CONTROL_MASK)
666 {
667 double swap;
668 swap = dx;
669 dx = dy;
670 dy = swap;
671 }
672 deltax += dx;
673 deltay += dy;
674 if (deltax <= -1.0 || deltax >= 1.0)
675 {
676 n.x += deltax;
677 deltax += (int)deltax * -1;
678 }
679 if (deltay <= -1.0 || deltay >= 1.0)
680 {
681 n.y += deltay;
682 deltay += (int)deltay * -1;
683 }
684 }
685 }
686 break;
687 default:
688 break;
689 }
690 if (n.x < 0)
691 n.x = 0;
692 else if (n.x > GameMap::getWidth()-1)
693 n.x = GameMap::getWidth()-1;
694 if (n.y < 0)
695 n.y = 0;
696 else if (n.y > GameMap::getHeight()-1)
697 n.y = GameMap::getHeight()-1;
698 set_view(n);
699 view_changed.emit(view);
700 return true;
701 }
702
get_default_zoom_scale(int screen_height)703 double BigMap::get_default_zoom_scale (int screen_height)
704 {
705 //aiming for 40 pixels on a 768 height
706 double target_ts = (double) screen_height * 0.05208333;
707 guint32 ts = GameMap::getInstance ()->getUnscaledTileSize ();
708 return (double)target_ts / (double) ts;
709 }
710