initial commit, lordsawar source, slightly modified
[lordsawar] / src / gamebigmap.cpp
1 //  Copyright (C) 2007 Ole Laursen
2 //  Copyright (C) 2007, 2008, 2009 Ben Asselstine
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 3 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 
17 //  02110-1301, USA.
18
19 #include <config.h>
20
21 #include <assert.h>
22 #include <stdlib.h>
23 #include <glibmm/timeval.h>
24
25 #include "gamebigmap.h"
26
27 #include "army.h"
28 #include "path.h"
29 #include "stack.h"
30 #include "city.h"
31 #include "ruin.h"
32 #include "signpost.h"
33 #include "temple.h"
34 #include "roadlist.h"
35 #include "road.h"
36 #include "bridgelist.h"
37 #include "bridge.h"
38 #include "playerlist.h"
39 #include "File.h"
40 #include "GameMap.h"
41 #include "GraphicsCache.h"
42 #include "game.h"
43 #include "FogMap.h"
44 #include "LocationBox.h"
45 #include "Configuration.h"
46 #include "gui/image-helpers.h"
47 #include "PathCalculator.h"
48 #include "stacktile.h"
49
50 #include "timing.h"
51
52
53 #include <iostream>
54 using namespace std;
55 //#define debug(x) {cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<endl<<flush;}
56 #define debug(x)
57
58 namespace 
59 {
60     // controls speed of selector rotation
61     int selection_timeout = TIMER_BIGMAP_SELECTOR;
62 }
63
64 GameBigMap::GameBigMap(bool intense_combat, bool see_opponents_production,
65                        bool see_opponents_stacks, bool military_advisor)
66 :d_fighting(LocationBox(Vector<int>(-1,-1)))
67 {
68   path_calculator = NULL;
69   d_intense_combat = intense_combat;
70   d_see_opponents_production = see_opponents_production;
71   d_see_opponents_stacks = see_opponents_stacks;
72   d_military_advisor = military_advisor;
73
74   current_tile.x = current_tile.y = 0;
75   mouse_state = NONE;
76   input_locked = false;
77
78   d_waypoint = disassemble_row(File::getMiscFile("various/waypoints.png"), 2);
79   prev_mouse_pos = Vector<int>(0, 0);
80
81   // setup timeout
82   selection_timeout_handler = 
83     Timing::instance().register_timer
84     (sigc::mem_fun(*this, &GameBigMap::on_selection_timeout),
85      selection_timeout);
86   shift_key_is_down = false;
87   control_key_is_down = false;
88 }
89
90 GameBigMap::~GameBigMap()
91 {
92   if (path_calculator)
93     delete path_calculator;
94 }
95
96 void GameBigMap::select_active_stack()
97 {
98   Stack* stack = Playerlist::getActiveplayer()->getActivestack();
99   if (!stack)
100     return;
101   reset_path_calculator(stack);
102
103   if (stack->getPath()->checkPath(stack) == false)
104     {
105       assert (Playerlist::getActiveplayer()->getType() == Player::HUMAN);
106       //original path was blocked, so let's find a new way there.
107       //this shouldn't happen because nextTurn of stack recalculates.
108       //cerr << "original path of stack was blocked\n";
109       stack->getPath()->recalculate(stack);
110     }
111
112   stack_selected.emit(stack);
113 }
114
115 void GameBigMap::unselect_active_stack()
116 {
117   draw(Playerlist::getViewingplayer());
118   stack_selected.emit(0);
119   if (path_calculator)
120     {
121       delete path_calculator;
122       path_calculator = NULL;
123     }
124   determine_mouse_cursor(NULL, current_tile);
125 }
126
127 bool GameBigMap::on_selection_timeout()
128 {
129   // redraw to update the selection
130   if (Playerlist::getActiveplayer()->getActivestack())
131     draw(Playerlist::getViewingplayer());
132
133   return Timing::CONTINUE;
134 }
135
136 void GameBigMap::mouse_button_event(MouseButtonEvent e)
137 {
138   if (input_locked)
139     return;
140
141   Player *active = Playerlist::getActiveplayer();
142   Player *viewing = Playerlist::getViewingplayer();
143   Vector<int> tile = mouse_pos_to_tile(e.pos);
144   current_tile = tile;
145
146   if (e.button == MouseButtonEvent::LEFT_BUTTON
147       && e.state == MouseButtonEvent::PRESSED)
148     {
149       bool double_clicked = false;
150       static Glib::TimeVal last_clicked;
151       if (last_clicked.as_double() == 0.0)
152         last_clicked.assign_current_time();
153       else
154         {
155           Glib::TimeVal clicked_now;
156           clicked_now.assign_current_time();
157           double click_delta = clicked_now.as_double() - 
158             last_clicked.as_double();
159           if (click_delta <= Configuration::s_double_click_threshold / 1000.0)
160             double_clicked = true;
161           last_clicked = clicked_now;
162         }
163       if (viewing->getFogMap()->isCompletelyObscuredFogTile(tile) == true)
164         return;
165
166       Stack* stack = Playerlist::getActiveplayer()->getActivestack();
167
168       if (d_cursor == GraphicsCache::HAND)
169         return;
170
171       if (stack)
172         {
173           bool path_already_set = stack->getPath()->size() > 0;
174           // ask for military advice
175           if (d_cursor == GraphicsCache::QUESTION)
176             {
177               set_shift_key_down (false);
178               Playerlist::getActiveplayer()->stackFightAdvise
179                 (stack, tile, d_intense_combat); 
180               return;
181             }
182           else if (d_cursor == GraphicsCache::RUIN)
183             {
184               if (Ruin *r = GameMap::getRuin(tile))
185                 {
186                   if ((r->isHidden() == true && 
187                        r->getOwner() == viewing) ||
188                       r->isHidden() == false)
189                     {
190                       set_shift_key_down (false);
191                       ruin_queried (r, false);
192                     }
193                 }
194               else if (Temple *t = GameMap::getTemple(tile))
195                 {
196                   temple_queried (t, false);
197                   set_shift_key_down (false);
198                 }
199               return;
200             }
201           else if (d_cursor == GraphicsCache::ROOK)
202             {
203               City* c = GameMap::getCity(tile);
204               if (c != NULL)
205                 {
206                   if (!c->isBurnt())
207                     {
208                       set_control_key_down (false);
209                       if (d_see_opponents_production == true)
210                         {
211                           city_visited.emit (c);
212                           set_shift_key_down (false);
213                           return;
214                         }
215                       else
216                         {
217                           if (c->getOwner() == Playerlist::getActiveplayer())
218                             {
219                               city_visited.emit(c);
220                               set_shift_key_down (false);
221                               return;
222                             }
223                         }
224                     }
225                 }
226             }
227           else if (d_cursor == GraphicsCache::GOTO_ARROW)
228             {
229               //set in a course, mr crusher.
230               stack->getPath()->calculate(stack, tile);
231               path_set.emit();
232               draw(Playerlist::getViewingplayer());
233               return;
234             }
235           Vector<int> p;
236           p.x = tile.x; p.y = tile.y;
237
238           // clicked on the already active stack
239           if (stack->getPos() == tile)
240             {
241               if (double_clicked == true)
242                 {
243                   StackTile *stile = GameMap::getStacks(stack->getPos());
244                   std::list<Stack *> stks= stile->getFriendlyStacks(active);
245                   if (stks.size() == 1)
246                     stile->ungroup(active);
247                   else
248                     {
249                       stile->group(active);
250                       active->setActivestack(GameMap::getStack(tile));
251                       stack_selected.emit(GameMap::getStack(tile));
252                     }
253                   if (path_calculator)
254                     delete path_calculator;
255                   path_calculator = new PathCalculator(stack);
256                   draw(Playerlist::getViewingplayer());
257                   stack_grouped_or_ungrouped.emit(stack);
258                   return;
259                 }
260               else
261                 {
262                   // clear the path
263                   stack->getPath()->clear();
264                   path_set.emit();
265                   draw(Playerlist::getViewingplayer());
266                   return;
267                 }
268             }
269
270           //clicked on an enemy city that is too far away
271           City *c = GameMap::getCity(tile);
272           if (c)
273             {
274               //restrict going into enemy cities unless they're only
275               //one square away
276               if (c->getOwner() != Playerlist::getActiveplayer())
277                 {
278                   int delta = abs(tile.x - stack->getPos().x);
279                   if (delta <= 1)
280                     delta = abs(tile.y - stack->getPos().y);
281                   if (delta > 1)
282                     return;
283                 }
284             }
285
286           int dist = stack->getPath()->calculate(stack, p);
287           if (dist == -2)
288             cerr << "error calculating path!";
289
290           Vector<int> dest = Vector<int>(-1,-1);
291           if (!stack->getPath()->empty())
292             dest = stack->getLastPointInPath();
293
294           if (dest.x == tile.x && dest.y == tile.y)
295             {
296               Playerlist::getActiveplayer()->stackMove(stack);
297               if (!Playerlist::getActiveplayer()->getActivestack())
298                 {
299                   unselect_active_stack();
300                   return;
301                 }
302               else
303                 {
304                   //grab our stack again because maybe we joined another stack
305                   stack = Playerlist::getActiveplayer()->getActivestack();
306
307                   //deslect when:
308                   //1. we've moved our stack too far and we've gone as far
309                   //   as we can on our path.
310                   //2. we've set in a second path and we've gone as far as
311                   //   we can on our path.
312                   //3. we're next to an enemy city, out of moves, and we 
313                   //   try to attack the city.
314                   //note that special care is taken to not deselect when
315                   //we've proceeded along our path and ran out of nodes
316                   //to follow, but we still have moves to make.
317                   bool deselect = false;
318                   if (path_already_set)
319                     {
320                       if (!stack->getPath()->empty() && 
321                           stack->enoughMoves() == false)
322                         deselect = true;
323                       else if (!stack->getPath()->empty() &&
324                                stack->getPath()->getMovesExhaustedAtPoint() == 0)
325                         deselect = true;
326                     }
327                   else
328                     {
329                       if (stack->getPath()->empty() == false && 
330                           stack->getPath()->getMovesExhaustedAtPoint() == 0)
331                         deselect = true;
332                       if ((d_cursor == GraphicsCache::SWORD ||
333                            d_cursor == GraphicsCache::HEART) &&
334                           stack->canMove() == false)
335                         deselect = true;
336                       if ((d_cursor == GraphicsCache::FEET ||
337                            d_cursor == GraphicsCache::SHIP) &&
338                           stack->canMove() == false)
339                         deselect = true;
340                     }
341
342                   if (deselect)
343                     {
344                       Player *player = Playerlist::getActiveplayer();
345                       player->setActivestack(0);
346                       unselect_active_stack();
347                     }
348                 }
349             }
350
351           path_set.emit();
352
353           draw(Playerlist::getViewingplayer());
354         }
355       // Stack hasn't been active yet
356       else
357         {
358           stack = GameMap::getStack(tile);
359           if (stack && stack->isFriend(Playerlist::getActiveplayer()) && 
360               d_cursor == GraphicsCache::TARGET)
361             {
362               Playerlist::getActiveplayer()->setActivestack(stack);
363               select_active_stack();
364             }
365           else
366             {
367               City* c = GameMap::getCity(tile);
368               if (c != NULL && d_cursor == GraphicsCache::ROOK)
369                 {
370                   if (!c->isBurnt())
371                     {
372                       set_control_key_down (false);
373                       if (d_see_opponents_production == true)
374                         {
375                           city_visited.emit (c);
376                           set_shift_key_down (false);
377                         }
378                       else
379                         {
380                           if (c->getOwner() == Playerlist::getActiveplayer())
381                             {
382                               city_visited.emit (c);
383                               set_shift_key_down (false);
384                             }
385                         }
386                     }
387                 }
388               else if (Ruin *r = GameMap::getRuin(tile))
389                 {
390                   if ((r->isHidden() == true && r->getOwner() == viewing) ||
391                       r->isHidden() == false)
392                     ruin_queried (r, false);
393                 }
394               else if (Temple *t = GameMap::getTemple(tile))
395                 {
396                   temple_queried (t, false);
397                 }
398             }
399         }
400     }
401   else if (e.button == MouseButtonEvent::LEFT_BUTTON
402       && e.state == MouseButtonEvent::RELEASED)
403     {
404       if (mouse_state == DRAGGING_ENDPOINT)
405         {
406           mouse_state = NONE;
407           d_cursor = GraphicsCache::FEET;
408           cursor_changed.emit(d_cursor);
409           path_set.emit();
410         }
411       else if (mouse_state == DRAGGING_STACK)
412         {
413           Stack* stack = Playerlist::getActiveplayer()->getActivestack();
414           //march a dragged stack!
415           mouse_state = NONE;
416           d_cursor = GraphicsCache::FEET;
417           cursor_changed.emit(d_cursor);
418           //watch out here.  
419           //we recurse for least amount of code and most programmer confusion.
420           e.state = MouseButtonEvent::PRESSED;
421           //go get the final spot in the path
422           if (stack->getPath()->empty() == false)
423             {
424               //check if we dropped on the same tile that the stack lives on.
425               if (mouse_pos_to_tile(e.pos) != stack->getPos())
426                 {
427                   int ts = GameMap::getInstance()->getTileset()->getTileSize();
428                   e.pos = tile_to_buffer_pos (stack->getLastPointInPath());
429                   e.pos.x -= ts/2;
430                   e.pos.y -= ts/2;
431                   mouse_button_event(e);
432                 }
433             }
434         }
435       else
436         mouse_state = NONE;
437     }
438
439
440   // right mousebutton to get information about things on the map and to
441   // unselect the active stack
442   else if (e.button == MouseButtonEvent::RIGHT_BUTTON)
443     {
444       if (e.state == MouseButtonEvent::PRESSED)
445         {
446           if (viewing->getFogMap()->isCompletelyObscuredFogTile(tile) == true)
447             return;
448           if (City* c = GameMap::getCity(tile))
449             {
450               city_queried.emit (tile, c);
451               mouse_state = SHOWING_CITY;
452             }
453           else if (Ruin* r = GameMap::getRuin(tile))
454             {
455               if ((r->isHidden() == true && 
456                    r->getOwner() == Playerlist::getViewingplayer()) ||
457                   r->isHidden() == false)
458                 {
459                   ruin_queried (r, true);
460                   mouse_state = SHOWING_RUIN;
461                 }
462             }
463           else if (Signpost* s = GameMap::getSignpost(tile))
464             {
465               signpost_queried (s);
466               mouse_state = SHOWING_SIGNPOST;
467             }
468           else if (Temple* t = GameMap::getTemple(tile))
469             {
470               temple_queried.emit(t, true);
471               mouse_state = SHOWING_TEMPLE;
472             }
473           else if (Stack *st = GameMap::getStack(tile))
474             {
475               if (d_see_opponents_stacks == true)
476                 {
477                   stack_queried.emit(tile);
478                   mouse_state = SHOWING_STACK;
479                 }
480               else if (st->getOwner() == Playerlist::getActiveplayer() && 
481                        d_see_opponents_stacks == false)
482                 {
483                   stack_queried.emit(tile);
484                   mouse_state = SHOWING_STACK;
485                 }
486             }
487         }
488       else // button released
489         {
490           switch(mouse_state)
491             {
492
493             case SHOWING_CITY:
494               city_unqueried.emit();
495               break;
496
497             case SHOWING_RUIN:
498               ruin_queried.emit(0, true);
499               break;
500
501             case SHOWING_TEMPLE:
502               temple_queried.emit(0, true);
503               break;
504
505             case SHOWING_SIGNPOST:
506               signpost_queried.emit(0);
507               break;
508
509             case SHOWING_STACK:
510               stack_unqueried.emit();
511               break;
512
513             case DRAGGING_ENDPOINT:
514             case DRAGGING_STACK:
515             case DRAGGING_MAP:
516             case NONE:
517               Stack* stack = Playerlist::getActiveplayer()->getActivestack();
518               if (stack)
519                 {
520                   Playerlist::getActiveplayer()->setActivestack(0);
521                   unselect_active_stack();
522                   mouse_state = NONE;
523                   determine_mouse_cursor(NULL, current_tile);
524                 }
525               break;
526             }
527
528           // in any case reset mouse state
529           mouse_state = NONE;
530         }
531     }
532   else if (e.button == MouseButtonEvent::WHEEL_UP)
533     {
534       //zoom_in();
535     }
536   else if (e.button == MouseButtonEvent::WHEEL_DOWN)
537     {
538       //zoom_out();
539     }
540 }
541
542 void GameBigMap::zoom_in()
543 {
544   if (input_locked)
545     return;
546   if ((zoom_step / 100.0) + magnification_factor <= max_magnification_factor / 100.0)
547     {
548       int ts = GameMap::getInstance()->getTileset()->getTileSize();
549       Rectangle new_view;
550       double mag = magnification_factor + (zoom_step / 100.0);
551       new_view.w = image.get_width() / (ts * mag) + 1;
552       new_view.h = image.get_height() / (ts * mag) + 1;
553       if (new_view.w <= GameMap::getWidth() && 
554           new_view.h <= GameMap::getHeight() && 
555           new_view.w >= 0 && new_view.h >= 0)
556         zoom_view(zoom_step);
557     }
558 }
559
560 void GameBigMap::zoom_out()
561 {
562   if (input_locked)
563     return;
564   if (magnification_factor - (zoom_step / 100.0) >= min_magnification_factor / 100.0)
565     {
566       int ts = GameMap::getInstance()->getTileset()->getTileSize();
567       Rectangle new_view;
568       double mag = magnification_factor - (zoom_step / 100.0);
569       new_view.w = image.get_width() / (ts * mag) + 1;
570       new_view.h = image.get_height() / (ts * mag) + 1;
571       if (new_view.w <= GameMap::getWidth() && 
572           new_view.h <= GameMap::getHeight() && 
573           new_view.w >= 0 && new_view.h >= 0)
574         zoom_view(-(const double)zoom_step);
575     }
576 }
577
578 void GameBigMap::determine_mouse_cursor(Stack *stack, Vector<int> tile)
579 {
580   Player *active = Playerlist::getActiveplayer();
581   Player *viewing = Playerlist::getViewingplayer();
582   if (viewing->getFogMap()->isCompletelyObscuredFogTile(tile))
583     {
584       d_cursor = GraphicsCache::HAND;
585     }
586   else if (mouse_state == DRAGGING_MAP)
587     d_cursor = GraphicsCache::HAND;
588   else if (stack && 
589            (mouse_state == DRAGGING_STACK || mouse_state == DRAGGING_ENDPOINT))
590     {
591       d_cursor = GraphicsCache::GOTO_ARROW;
592     }
593   else if (stack)
594     {
595       d_cursor = GraphicsCache::FEET;
596       if (stack->getPos() == tile)
597         d_cursor = GraphicsCache::TARGET;
598       else
599         {
600           City *c = GameMap::getCity(tile);
601           if (c)
602             {
603               if (c->getOwner() == active)
604                 d_cursor = GraphicsCache::FEET;
605               else if (c->isBurnt() == true)
606                 d_cursor = GraphicsCache::FEET;
607               else
608                 {
609                   int delta = abs(tile.x - stack->getPos().x);
610                   if (delta <= 1)
611                     delta = abs(tile.y - stack->getPos().y);
612                   if (delta <= 1)
613                     {
614                       if (is_shift_key_down())
615                         d_cursor = GraphicsCache::QUESTION;
616                       else
617                         {
618                           Player *me = stack->getOwner();
619                           Player *them = c->getOwner();
620                           bool friendly = (me->getDiplomaticState(them) == 
621                                            Player::AT_PEACE);
622                           if (friendly)
623                             d_cursor = GraphicsCache::HEART;
624                           else
625                             d_cursor = GraphicsCache::SWORD;
626                         }
627                     }
628                   else
629                     {
630                       //can i see other ppl's cities?
631                       if (d_see_opponents_production == true)
632                         d_cursor = GraphicsCache::ROOK;
633                       else
634                         d_cursor = GraphicsCache::HAND;
635                     }
636                 }
637             }
638           else
639             {
640               Maptile *t = GameMap::getInstance()->getTile(tile);
641               Stack *st = GameMap::getStack(tile);
642               if (st && st->getOwner() != active)
643                 {
644                   int delta = abs(stack->getPos().x - st->getPos().x);
645                   if (delta <= 1)
646                     delta = abs(stack->getPos().y - st->getPos().y);
647                   if (delta <= 1)
648                     {
649                       if (is_shift_key_down())
650                         d_cursor = GraphicsCache::QUESTION;
651                       else
652                         {
653                           Player *me = stack->getOwner();
654                           Player *them = st->getOwner();
655                           bool friendly = (me->getDiplomaticState(them) == 
656                                            Player::AT_PEACE);
657                           if (friendly)
658                             d_cursor = GraphicsCache::HEART;
659                           else
660                             d_cursor = GraphicsCache::SWORD;
661                         }
662                     }
663                   else
664                     d_cursor = GraphicsCache::HAND;
665                 }
666               else
667                 {
668                   //Path path;
669                   //why is this slower than without a stack selected?
670                   //because we need to see if we can get there eventually!
671
672                   //int moves = path.calculate(stack, tile);
673                   if (path_calculator == NULL)
674                     path_calculator = new PathCalculator(stack);
675                   if (path_calculator->isReachable(tile) == false)
676                   //if (moves == 0)
677                     d_cursor = GraphicsCache::HAND;
678                   else
679                     {
680                       if (t->getMaptileType() == Tile::WATER &&
681                           GameMap::getBridge(tile) == NULL)
682                         {
683                           if (stack->isFlying() == true)
684                             d_cursor = GraphicsCache::FEET;
685                           else
686                             d_cursor = GraphicsCache::SHIP;
687                         }
688                       else
689                         d_cursor = GraphicsCache::FEET;
690                     }
691                 }
692             }
693           if (d_cursor == GraphicsCache::FEET && is_control_key_down())
694             d_cursor = GraphicsCache::GOTO_ARROW;
695         }
696     }
697   else
698     {
699       d_cursor = GraphicsCache::HAND;
700       Stack *st;
701
702       st = GameMap::getStack(tile);
703       if (st)
704         {
705           if (st->getOwner() == active)
706             d_cursor = GraphicsCache::TARGET;
707           else
708             d_cursor = GraphicsCache::HAND;
709         }
710       else
711         {
712           Maptile *t = GameMap::getInstance()->getTile(tile);
713           if (t->getBuilding() == Maptile::CITY)
714             {
715               City *c = GameMap::getCity(tile);
716               if (c->isBurnt() == true)
717                 d_cursor = GraphicsCache::HAND;
718               else if (c->getOwner() == active)
719                 d_cursor = GraphicsCache::ROOK;
720               else if (d_see_opponents_production == true)
721                 d_cursor = GraphicsCache::ROOK;
722             }
723           else if (t->getBuilding() == Maptile::RUIN)
724             {
725               Ruin *ruin = GameMap::getRuin(tile);
726               if (ruin->isHidden() == true && ruin->getOwner() == active)
727                 d_cursor = GraphicsCache::RUIN;
728               else if (ruin->isHidden() == false)
729                 d_cursor = GraphicsCache::RUIN;
730             }
731           else if (t->getBuilding() == Maptile::TEMPLE)
732             d_cursor = GraphicsCache::RUIN;
733         }
734
735     }
736   cursor_changed.emit(d_cursor);
737   //debugFogTile(tile.x, tile.y);
738 }
739
740 void GameBigMap::mouse_motion_event(MouseMotionEvent e)
741 {
742   static Vector<int> last_tile;
743   if (input_locked)
744     return;
745
746   Player *active = Playerlist::getActiveplayer();
747   Player *viewing = Playerlist::getViewingplayer();
748   Stack* stack = active->getActivestack();
749   Vector<int> tile = mouse_pos_to_tile(e.pos);
750   current_tile = tile;
751   if (tile.x < 0)
752     tile.x = 0;
753   if (tile.y < 0)
754     tile.y = 0;
755   if (tile.x >= GameMap::getWidth())
756     tile.x = GameMap::getWidth() - 1;
757   if (tile.y >= GameMap::getHeight())
758     tile.y = GameMap::getHeight() - 1;
759
760
761   if (e.pressed[MouseMotionEvent::LEFT_BUTTON]
762       && (mouse_state == NONE || mouse_state == SHOWING_STACK) && 
763       stack && stack->getPos() == tile && viewing->getFogMap()->isCompletelyObscuredFogTile(tile) == false && d_cursor != GraphicsCache::HAND)
764     {
765       //initial dragging of stack from it's tile
766       mouse_state = DRAGGING_STACK;
767     }
768   else if (e.pressed[MouseMotionEvent::LEFT_BUTTON] && stack &&
769            d_cursor == GraphicsCache::GOTO_ARROW && mouse_state == NONE)
770     {
771       //initial dragging of endpoint from it's tile
772       mouse_state = DRAGGING_ENDPOINT;
773     }
774   else if (e.pressed[MouseMotionEvent::LEFT_BUTTON]
775            && (mouse_state == NONE || mouse_state == DRAGGING_MAP) && d_cursor == GraphicsCache::HAND)
776     {
777       Vector<int> delta = -(e.pos - prev_mouse_pos);
778
779       // ignore very small drags to ensure that a shaking mouse does not
780       // prevent the user from making right clicks
781       if (mouse_state == NONE && length(delta) <= 2)
782         return;
783
784       int ts = GameMap::getInstance()->getTileset()->getTileSize();
785       Vector<int> screen_dim(image.get_width(), image.get_height());
786       view_pos = clip(Vector<int>(0, 0),
787                       view_pos + delta,
788                       GameMap::get_dim() * ts *magnification_factor - screen_dim);
789
790       // calculate new view position in tiles, rounding up
791       Vector<int> new_view = (view_pos + Vector<int>(ts * magnification_factor - 1, ts * magnification_factor - 1)) / (ts * magnification_factor);
792
793       if (new_view != view.pos)
794         {
795           //here we have a case of the new view overlapping with the view.
796           //why redraw what we've already drawn?
797           Rectangle old_view = view;
798           view.x = new_view.x;
799           view.y = new_view.y;
800           view_changed.emit(view);
801           draw(Playerlist::getViewingplayer(),true);
802         }
803       else
804         draw(Playerlist::getViewingplayer(), false);
805       mouse_state = DRAGGING_MAP;
806     }
807
808   // the following block of code shows the correct mouse cursor
809   if (tile == last_tile)
810     {
811       prev_mouse_pos = e.pos;
812       return;
813     }
814
815   // drag stack with left mouse button
816   if (e.pressed[MouseMotionEvent::LEFT_BUTTON]
817       && (mouse_state == DRAGGING_STACK || mouse_state == DRAGGING_ENDPOINT) && 
818       viewing->getFogMap()->isCompletelyObscuredFogTile(tile) == false)
819     {
820       //subsequent dragging
821       //alright.  calculate the path, and show it but don't move
822       //be careful that we don't drop our path on bad objects
823       //also, slide the whole view if we drag out of view
824       if (is_inside(view, tile) == false)
825         {
826           Vector<int> delta(0,0);
827           if (tile.x >= view.x + view.w)
828             delta.x += 1;
829           if (tile.x < view.x)
830             delta.x -= 1;
831           if (tile.y > view.y + view.h)
832             delta.y += 1;
833           if (tile.y < view.y)
834             delta.y -= 1;
835           Rectangle new_view = view;
836           new_view.pos += delta;
837           set_view (new_view);
838           view_changed.emit(view);
839         }
840       mouse_state_enum orig_state = mouse_state;
841       mouse_state = NONE;
842       determine_mouse_cursor(stack, tile);
843       mouse_state = orig_state;
844       if (d_cursor == GraphicsCache::FEET ||
845           d_cursor == GraphicsCache::SHIP || 
846           d_cursor == GraphicsCache::GOTO_ARROW || 
847           d_cursor == GraphicsCache::TARGET)
848         {
849           guint32 moves = 0, turns = 0;
850           Path *new_path = path_calculator->calculate(tile, moves, turns, true);
851           if (new_path->size())
852             stack->setPath(*new_path);
853           delete new_path;
854           //stack->getPath()->calculate(stack, tile);
855           path_set.emit();
856           draw(Playerlist::getViewingplayer());
857         }
858     }
859
860   determine_mouse_cursor (stack, tile);
861   if (control_key_is_down == true)
862     set_control_key_down (true);
863   if (shift_key_is_down == true)
864     set_shift_key_down (true);
865
866   prev_mouse_pos = e.pos;
867   last_tile = tile;
868 }
869
870 void GameBigMap::reset_zoom()
871 {
872   magnification_factor = 1.0;
873   screen_size_changed(image);
874   draw(Playerlist::getViewingplayer(), true);
875   view_changed.emit(view);
876 }
877
878 void GameBigMap::zoom_view(double percent)
879 {
880   magnification_factor += percent / 100.0;
881   //call with +2, or -2
882   //Rectangle new_view = view;
883   //new_view.dim += Vector<int>(tiles, tiles);
884   //new_view.pos += Vector<int>(tiles*-1/2, tiles*-1/2);
885   //set_view (new_view);
886   screen_size_changed(image);
887   draw(Playerlist::getViewingplayer(), true);
888   view_changed.emit(view);
889 }
890
891 void GameBigMap::after_draw()
892 {
893   if (blank_screen == true)
894     return;
895   GraphicsCache *gc = GraphicsCache::getInstance();
896   int tilesize = GameMap::getInstance()->getTileset()->getTileSize();
897
898   Stack* stack = Playerlist::getActiveplayer()->getActivestack();
899
900   // Draw Path
901   if (stack && stack->getPath()->size() && 
902       stack->getOwner()->getType() == Player::HUMAN)
903     {
904       Vector<int> pos;
905
906       // draw all waypoints
907       guint32 pathcount = 0;
908       bool canMoveThere = true;
909       Path::iterator end = stack->getPath()->end();
910       //if we're dragging, we don't draw the last waypoint circle
911       if (stack->getPath()->size() > 0 && 
912           (mouse_state == DRAGGING_STACK || mouse_state == DRAGGING_ENDPOINT))
913         end--;
914       for (Path::iterator it = stack->getPath()->begin();
915            it != end; it++)
916         {
917           pos = tile_to_buffer_pos(*it);
918
919           canMoveThere = (pathcount < stack->getPath()->getMovesExhaustedAtPoint());
920           if (canMoveThere)
921             d_waypoint[0]->blit_centered(buffer, pos + (Vector<int>(tilesize,tilesize)/2));
922           else
923             d_waypoint[1]->blit_centered(buffer, pos + (Vector<int>(tilesize,tilesize)/2));
924
925           pathcount++;
926
927         }
928
929       if (mouse_state == DRAGGING_STACK || mouse_state == DRAGGING_ENDPOINT 
930           || d_cursor == GraphicsCache::GOTO_ARROW)
931         {
932           Path::iterator it = stack->getPath()->end();
933           it--;
934           //this is where the ghosted army unit picture goes.
935           PixMask *armypic = gc->getArmyPic(*stack->begin(), true);
936           pos = tile_to_buffer_pos(*it);
937           armypic->blit_centered(buffer, pos + (Vector<int>(tilesize,tilesize)/2));
938         }
939     }
940
941   if (stack && d_fighting.getPos() == Vector<int>(-1,-1))
942     {
943       Player *viewer = Playerlist::getViewingplayer();
944       // draw the selection
945       Vector<int> p = stack->getPos();
946       if (is_inside(buffer_view, Vector<int>(p.x, p.y)) &&
947           Playerlist::getViewingplayer()->getFogMap()->isFogged(p) == false)
948         {
949           static int bigframe = -1;
950           static int smallframe = -1;
951
952           Tileset *t = GameMap::getInstance()->getTileset();
953           bigframe++;
954           if (bigframe >= (int)t->getNumberOfSelectorFrames())
955             bigframe = 0;
956
957           smallframe++;
958           if (smallframe >= (int)t->getNumberOfSmallSelectorFrames())
959             smallframe = 0;
960
961           p = tile_to_buffer_pos(p);
962
963           draw_stack (stack, buffer, buffer_gc);
964
965           PixMask *tmp = NULL;
966           if (stack->size() > 1)
967             tmp = gc->getSelectorPic(0, bigframe, stack->getOwner());
968           else
969             tmp = gc->getSelectorPic(1, smallframe, stack->getOwner());
970           tmp->blit(buffer, p);
971           //now re-fog it up because we just drew over the fog.
972           if (viewer->getFogMap()->isFogged(stack->getPos()))
973             {
974               int fog_type_id = 
975                 viewer->getFogMap()->getShadeTile(stack->getPos());
976               PixMask *fog = gc->getFogPic(fog_type_id);
977               fog->blit(buffer, p);
978             }
979         }
980     }
981
982   if (d_fighting.getPos() != Vector<int>(-1,-1))
983     {
984       Vector<int> p = tile_to_buffer_pos(d_fighting.getPos());
985       PixMask *tmp = gc->getExplosionPic()->copy();
986       if (d_fighting.getSize() > 1)
987         {
988           PixMask::scale(tmp, d_fighting.getSize() * tilesize,
989                          d_fighting.getSize() * tilesize);
990         }
991       tmp->blit(buffer, p);
992       delete tmp;
993     }
994 }
995
996 void GameBigMap::set_control_key_down (bool down)
997 {
998   control_key_is_down = down;
999   Player *active = Playerlist::getActiveplayer();
1000   Player *viewing = Playerlist::getViewingplayer();
1001   if (viewing->getFogMap()->isCompletelyObscuredFogTile(current_tile) == true)
1002     return;
1003   Stack* active_stack = active->getActivestack();
1004   //if the key has been released, just show what we'd normally show.
1005   if (control_key_is_down == false)
1006     {
1007       determine_mouse_cursor(active_stack, current_tile);
1008       return;
1009     }
1010   if (!active_stack)
1011     {
1012       if (GameMap::getInstance()->getTile(current_tile)->getBuilding() != 
1013           Maptile::CITY)
1014         return;
1015
1016       Stack* stack;
1017       stack = GameMap::getFriendlyStack(current_tile);
1018       if (!stack)
1019         return;
1020
1021       if (d_cursor == GraphicsCache::TARGET)
1022         {
1023           City *city = GameMap::getCity(current_tile);
1024           if (city->isBurnt() == false)
1025             {
1026               d_cursor = GraphicsCache::ROOK;
1027               cursor_changed.emit(d_cursor);
1028             }
1029         }
1030       else if (d_cursor == GraphicsCache::HAND && 
1031                d_see_opponents_production == true)
1032         {
1033           City *city = GameMap::getCity(current_tile);
1034           if (city->isBurnt() == false)
1035             {
1036               d_cursor = GraphicsCache::ROOK;
1037               cursor_changed.emit(d_cursor);
1038             }
1039         }
1040     }
1041   else
1042     {
1043       if (d_cursor == GraphicsCache::FEET)
1044         {
1045           d_cursor = GraphicsCache::GOTO_ARROW;
1046           cursor_changed.emit(d_cursor);
1047         }
1048     }
1049 }
1050
1051 void GameBigMap::set_shift_key_down (bool down)
1052 {
1053   shift_key_is_down = down;
1054   Player *active = Playerlist::getActiveplayer();
1055   Player *viewing = Playerlist::getViewingplayer();
1056   if (viewing->getFogMap()->isCompletelyObscuredFogTile(current_tile) == true)
1057       return;
1058
1059   Stack* active_stack = active->getActivestack();
1060
1061   //if the key has been released, just show what we'd normally show.
1062   if (shift_key_is_down == false)
1063     {
1064       determine_mouse_cursor(active_stack, current_tile);
1065       return;
1066     }
1067
1068   //otherwise the shift key is down and we need to do some more checking
1069   Maptile::Building b = GameMap::getInstance()->getTile(current_tile)->getBuilding();
1070   if (b == Maptile::RUIN)
1071     {
1072       Ruin *r = GameMap::getRuin(current_tile);
1073       if (r)
1074         {
1075           if ((r->isHidden() == true && 
1076                r->getOwner() == Playerlist::getActiveplayer()) ||
1077               r->isHidden() == false)
1078             b = Maptile::RUIN;
1079           else
1080             b = Maptile::NONE;
1081         }
1082     }
1083   else if (b == Maptile::CITY)
1084     {
1085       if (d_cursor == GraphicsCache::TARGET)
1086         {
1087           d_cursor = GraphicsCache::ROOK;
1088           cursor_changed.emit(d_cursor);
1089         }
1090       else if (d_cursor == GraphicsCache::FEET)
1091         {
1092           d_cursor = GraphicsCache::ROOK;
1093           cursor_changed.emit(d_cursor);
1094         }
1095       else if (d_cursor == GraphicsCache::HAND &&
1096                d_see_opponents_production == true)
1097         {
1098           d_cursor = GraphicsCache::ROOK;
1099           cursor_changed.emit(d_cursor);
1100         }
1101     }
1102
1103   if (active_stack)
1104     {
1105       if (d_cursor == GraphicsCache::SHIP || d_cursor == GraphicsCache::FEET) 
1106         {
1107           if (b == Maptile::RUIN || b == Maptile::TEMPLE)
1108             d_cursor = GraphicsCache::RUIN;
1109           else
1110             d_cursor = GraphicsCache::HAND;
1111           cursor_changed.emit(d_cursor);
1112         }
1113     }
1114   else
1115     {
1116       if (d_cursor != GraphicsCache::RUIN)
1117         {
1118           if (b == Maptile::RUIN || b == Maptile::TEMPLE)
1119             {
1120               d_cursor = GraphicsCache::RUIN;
1121               cursor_changed.emit(d_cursor);
1122             }
1123         }
1124     }
1125
1126   if (d_military_advisor == true)
1127     {
1128       if (active_stack && d_cursor == GraphicsCache::SWORD)
1129         {
1130               d_cursor = GraphicsCache::QUESTION;
1131               cursor_changed.emit(d_cursor);
1132         }
1133     }
1134 }
1135     
1136 void GameBigMap::reset_path_calculator(Stack *s)
1137 {
1138   if (path_calculator)
1139     delete path_calculator;
1140   path_calculator = new PathCalculator(s);
1141 }