initial commit, lordsawar source, slightly modified
[lordsawar] / src / editor / editorbigmap.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
24 #include "editorbigmap.h"
25
26 #include "army.h"
27 #include "stacklist.h"
28 #include "stack.h"
29 #include "citylist.h"
30 #include "city.h"
31 #include "ruinlist.h"
32 #include "ruin.h"
33 #include "signpostlist.h"
34 #include "signpost.h"
35 #include "templelist.h"
36 #include "temple.h"
37 #include "bridgelist.h"
38 #include "bridge.h"
39 #include "portlist.h"
40 #include "port.h"
41 #include "roadlist.h"
42 #include "road.h"
43 #include "playerlist.h"
44 #include "defs.h"
45 #include "File.h"
46 #include "GameMap.h"
47 #include "Configuration.h"
48 #include "rewardlist.h"
49 #include "GraphicsCache.h"
50 #include "armysetlist.h"
51 #include "MapRenderer.h"
52 #include "CreateScenario.h"
53 #include "Backpack.h"
54 #include "MapBackpack.h"
55 #include "backpack-editor-dialog.h"
56
57
58 EditorBigMap::EditorBigMap()
59 {
60     mouse_pos = prev_mouse_pos = Vector<int>(0, 0);
61
62     moving_objects_from = Vector<int>(-1,-1);
63     mouse_state = NONE;
64     input_locked = false;
65     pointer = POINTER;
66     pointer_size = 1;
67     pointer_terrain = Tile::GRASS;
68     pointer_tile_style_id = -1;
69     show_tile_types_instead_of_tile_styles = false;
70 }
71
72 EditorBigMap::~EditorBigMap()
73 {
74 }
75
76 void EditorBigMap::set_pointer(Pointer p, int size, Tile::Type t, 
77                                int tile_style_id)
78 {
79     bool redraw = false;
80     if (pointer != p || pointer_size != size || 
81         pointer_tile_style_id != tile_style_id)
82       redraw = true;
83     pointer = p;
84     pointer_terrain = t;
85     pointer_size = size;
86     pointer_tile_style_id = tile_style_id;
87
88     moving_objects_from = Vector<int>(-1,-1);
89     if (redraw)
90       draw(Playerlist::getViewingplayer());
91     
92 }
93
94 void EditorBigMap::mouse_button_event(MouseButtonEvent e)
95 {
96     if (input_locked)
97         return;
98     
99     mouse_pos = e.pos;
100     Vector<int> tile = mouse_pos_to_tile(mouse_pos);
101     
102     if (e.button == MouseButtonEvent::LEFT_BUTTON
103         && e.state == MouseButtonEvent::PRESSED)
104     {
105         change_map_under_cursor();
106     }
107
108     else if (e.button == MouseButtonEvent::RIGHT_BUTTON
109              && e.state == MouseButtonEvent::PRESSED)
110     {
111         map_selection_seq seq;
112         
113         if (Stack* s = GameMap::getStack(tile))
114             seq.push_back(s);
115         if (City* c = GameMap::getCity(tile))
116             seq.push_back(c);
117         if (Ruin* r = GameMap::getRuin(tile))
118             seq.push_back(r);
119         if (Signpost* s = GameMap::getSignpost(tile))
120             seq.push_back(s);
121         if (Temple* t = GameMap::getTemple(tile))
122             seq.push_back(t);
123         MapBackpack *b = GameMap::getInstance()->getTile(tile)->getBackpack();
124         if (b->empty() == false)
125           seq.push_back(b);
126
127
128         if (!seq.empty())
129             objects_selected.emit(seq);
130     }
131 }
132
133 void EditorBigMap::mouse_motion_event(MouseMotionEvent e)
134 {
135     if (input_locked)
136         return;
137
138     bool redraw = false;
139
140     mouse_pos = e.pos;
141     Vector<int> new_tile = mouse_pos_to_tile(mouse_pos);
142     if (new_tile != mouse_pos_to_tile(prev_mouse_pos))
143     {
144         mouse_on_tile.emit(new_tile);
145         redraw = true;
146     }
147
148     // draw with left mouse button
149     if (e.pressed[MouseMotionEvent::LEFT_BUTTON])
150     {
151         change_map_under_cursor();
152     }
153     
154     // drag with right mouse button
155     if (e.pressed[MouseMotionEvent::RIGHT_BUTTON]
156         && (mouse_state == NONE || mouse_state == DRAGGING))
157     {
158         Vector<int> delta = -(mouse_pos - prev_mouse_pos);
159
160         // ignore very small drags to ensure that a shaking mouse does not
161         // prevent the user from making right clicks
162         if (mouse_state == NONE && length(delta) <= 2)
163             return;
164         
165         // FIXME: show a drag cursor
166         
167         int ts = GameMap::getInstance()->getTileset()->getTileSize();
168         Vector<int> screen_dim(image.get_width(), image.get_height());
169         view_pos = clip(Vector<int>(0, 0),
170                         view_pos + delta,
171                         GameMap::get_dim() * ts - screen_dim);
172
173         // calculate new view position in tiles, rounding up
174         Vector<int> new_view = (view_pos + Vector<int>(ts - 1, ts - 1)) / ts;
175
176         bool redraw_buffer = false;
177         
178         if (new_view != view.pos)
179         {
180             view.x = new_view.x;
181             view.y = new_view.y;
182             view_changed.emit(view);
183             redraw_buffer = true;
184         }
185
186         draw(Playerlist::getViewingplayer(),redraw_buffer);
187         redraw = false;
188         mouse_state = DRAGGING;
189     }
190
191     if (redraw && pointer != POINTER)
192         draw(Playerlist::getViewingplayer());
193     
194     prev_mouse_pos = mouse_pos;
195 }
196
197 void EditorBigMap::mouse_leave_event()
198 {
199     mouse_pos.x = mouse_pos.y = -10000;
200     mouse_on_tile.emit(Vector<int>(-100, -100));
201     draw(Playerlist::getViewingplayer());
202 }
203
204 std::vector<Vector<int> > EditorBigMap::get_screen_tiles()
205 {
206     // find out which tiles are within bounds
207     std::vector<Vector<int> > tiles;
208
209     for (int y = buffer_view.y; y < buffer_view.y + buffer_view.h; y++)
210       for (int x = buffer_view.x; x < buffer_view.x + buffer_view.w; x++)
211         {
212             Vector<int> tile(x, y);
213             if (tile.x >= 0 && tile.x < GameMap::getWidth() &&
214                 tile.y >= 0 && tile.y < GameMap::getHeight())
215                 tiles.push_back(tile);
216         }
217     
218     return tiles;
219 }
220
221 std::vector<Vector<int> > EditorBigMap::get_cursor_tiles()
222 {
223     // find out which cursor tiles are within bounds
224     std::vector<Vector<int> > tiles;
225
226     Vector<int> current_tile = mouse_pos_to_tile(mouse_pos);
227         
228     for (int y = 0; y < pointer_size; ++y)
229         for (int x = 0; x < pointer_size; ++x)
230         {
231             int offset = - (pointer_size - 1) / 2;
232             Vector<int> tile(x + offset, y + offset);
233             tile += current_tile;
234
235             if (tile.x >= 0 && tile.x < GameMap::getWidth() &&
236                 tile.y >= 0 && tile.y < GameMap::getHeight())
237                 tiles.push_back(tile);
238         }
239     
240     return tiles;
241 }
242
243 Rectangle EditorBigMap::get_cursor_rectangle()
244 {
245     // find out which cursor tiles are within bounds
246     std::vector<Vector<int> > tiles;
247
248     Vector<int> current_tile = mouse_pos_to_tile(mouse_pos);
249     int offset = (pointer_size - 1) / 2;
250     Vector<int> tile = current_tile - Vector<int>(offset, offset);
251
252     return Rectangle (tile.x, tile.y, pointer_size, pointer_size);
253 }
254
255 int EditorBigMap::tile_to_bridge_type(Vector<int> t)
256 {
257     // examine neighbour tiles to discover whether there's a road on them
258     bool u = Roadlist::getInstance()->getObjectAt(t + Vector<int>(0, -1));
259     bool b = Roadlist::getInstance()->getObjectAt(t + Vector<int>(0, 1));
260     bool l = Roadlist::getInstance()->getObjectAt(t + Vector<int>(-1, 0));
261     bool r = Roadlist::getInstance()->getObjectAt(t + Vector<int>(1, 0));
262
263     // then translate this to the type
264     int type = 0;
265     if (!u && !b && !l && !r)
266         type = 0;
267     else if (u && b && l && r)
268         type = 0;
269     else if (!u && b && l && r)
270         type = 0;
271     else if (u && !b && l && r)
272         type = 0;
273     else if (u && b && !l && r)
274         type = 1;
275     else if (u && b && l && !r)
276         type = 1;
277     else if (u && b && !l && !r)
278         type = 1;
279     else if (!u && !b && l && r)
280         type = 0;
281     else if (u && !b && l && !r)
282         type = 0;
283     else if (u && !b && !l && r)
284         type = 2;
285     else if (!u && b && l && !r)
286         type = 0;
287     else if (!u && b && !l && r)
288         type = 2;
289     else if (u && !b && !l && !r)
290         type = 3;
291     else if (!u && b && !l && !r)
292         type = 1;
293     else if (!u && !b && l && !r)
294         type = 0;
295     else if (!u && !b && !l && r)
296         type = 2;
297     return type;
298 }
299
300 void EditorBigMap::change_map_under_cursor()
301 {
302
303   Player* active = Playerlist::getInstance()->getActiveplayer();
304   std::vector<Vector<int> > tiles = get_cursor_tiles();
305   Cityset* cs = GameMap::getInstance()->getCityset();
306
307   Vector<int> tile = tiles.front();
308   Rectangle changed_tiles(tile, Vector<int>(-1, -1));
309   Maptile* maptile = GameMap::getInstance()->getTile(tile);
310   switch (pointer)
311     {
312     case POINTER:
313       break;
314
315     case TERRAIN:
316
317       // don't change terrain to water if there is a building underneath
318       if (maptile->getBuilding() != Maptile::NONE && 
319           pointer_terrain == Tile::WATER)
320         break;
321       // don't change the terrain to anything else than grass if there is
322       // a city
323       if (maptile->getBuilding() == Maptile::CITY && 
324           pointer_terrain != Tile::GRASS)
325         break;
326
327       changed_tiles = GameMap::getInstance()->putTerrain(get_cursor_rectangle(), pointer_terrain, pointer_tile_style_id, true);
328       break;
329
330     case MOVE:
331       if (moving_objects_from == Vector<int>(-1,-1))
332         {
333           if (GameMap::getInstance()->getBuilding(tile) != 
334               Maptile::NONE || 
335               GameMap::getStack(tile) != NULL ||
336               GameMap::getBackpack(tile)->empty() == false)
337             moving_objects_from = tile;
338         }
339       else
340         {
341           Vector<int> from = moving_objects_from;
342           //here we go with the move!
343           GameMap *gm = GameMap::getInstance();
344           if (gm->getBuilding(from) != Maptile::NONE)
345             {
346               guint32 s = gm->getBuildingSize(from);
347               if (gm->canPutBuilding
348                   (gm->getBuilding(from), s, tile, false) == true)
349                 {
350                   gm->moveBuilding(from, tile);
351                   moving_objects_from = Vector<int>(-1,-1);
352                 }
353               else
354                 {
355                   if (gm->getLocation(from)->contains(tile) ||
356                       LocationBox(tile, s).contains(from))
357                     {
358                       gm->moveBuilding(from, tile);
359                       moving_objects_from = Vector<int>(-1,-1);
360                     }
361                 }
362             }
363           else if (gm->getStack(from) != NULL)
364             {
365               Stack *s = gm->getFriendlyStack(from);
366               if (!s)
367                 s = gm->getStack(from);
368               if (gm->canPutStack(s->size(), s->getOwner(), tile) == true)
369                 {
370                   gm->moveStack(s, tile);
371                   moving_objects_from = Vector<int>(-1,-1);
372                 }
373             }
374           else if (gm->getBackpack(from)->empty() == false)
375             {
376               gm->moveBackpack(from, tile);
377               moving_objects_from = Vector<int>(-1,-1);
378             }
379         }
380       break;
381     case ERASE:
382       // check if there is a building or a stack there and remove it
383
384       // first stack, it's above everything else
385       while  (GameMap::getStack(tile) != NULL)
386         {
387           Stack *s = GameMap::getStack(tile);
388           GameMap::getInstance()->removeStack(s);
389         }
390
391       // ... or a temple ...
392       GameMap::getInstance()->removeTemple(tile);
393
394       // ... or a port ...
395       GameMap::getInstance()->removePort(tile);
396
397       // ... or a ruin ...
398       if (GameMap::getRuin(tile) != NULL)
399         {
400           Rewardlist *rl = Rewardlist::getInstance();
401           for (Rewardlist::iterator i = rl->begin(); i != rl->end(); i++)
402             {
403               if ((*i)->getType() == Reward::RUIN)
404                 {
405                   Reward_Ruin *rr = static_cast<Reward_Ruin*>(*i);
406                   if (rr->getRuin()->getPos() == tile)
407                     rl->remove(*i);
408                 }
409             }
410         }
411       GameMap::getInstance()->removeRuin(tile);
412
413       // ... or a road ...
414       GameMap::getInstance()->removeRoad(tile);
415
416       // ... or a bridge...
417       GameMap::getInstance()->removeBridge(tile);
418
419       // ... or a signpost ...
420       GameMap::getInstance()->removeSignpost(tile);
421
422       // ... or a city
423       GameMap::getInstance()->removeCity(tile);
424
425       // ... or a bag
426       GameMap::getInstance()->getTile(tile)->getBackpack()->removeAllFromBackpack();
427       break;
428
429     case STACK:
430       if (GameMap::getInstance()->canPutStack(1, active, tile) == true)
431         {
432           // Create a new dummy stack. As we don't want to have empty
433           // stacks hanging around, it's assumed that the default armyset
434           // has at least one entry.
435           Stack* s = new Stack(active, tile);
436           const Armysetlist* al = Armysetlist::getInstance();
437           Army* a = new Army(*al->getArmy(active->getArmyset(), 0), 
438                              active);
439           s->push_back(a);
440           GameMap::getInstance()->putStack(s);
441           //if we're on a city, change the allegiance of the stack
442           //and it's armies to that of the city
443           if (GameMap::getInstance()->getBuilding(s->getPos()) == Maptile::CITY)
444             {
445               City *c = GameMap::getCity(s->getPos());
446               Stacklist::changeOwnership(s, c->getOwner());
447             }
448         }
449
450       break;
451
452     case CITY:
453         {
454           // check if we can place the city
455           bool city_placeable =
456             GameMap::getInstance()->canPutBuilding
457             (Maptile::CITY,cs->getCityTileWidth(), tile);
458
459           if (!city_placeable)
460             break;
461
462           City *c = new City(tile, cs->getCityTileWidth());
463           GameMap::getInstance()->putCity(c);
464           break;
465         }
466
467     case RUIN:
468         {
469           bool ruin_placeable = GameMap::getInstance()->canPutBuilding
470             (Maptile::RUIN, cs->getRuinTileWidth(), tile);
471           if (!ruin_placeable)
472             break;
473           Ruin *r = new Ruin(tile, cs->getRuinTileWidth());
474           GameMap::getInstance()->putRuin(r);
475           break;
476         }
477
478     case TEMPLE:
479         {
480           bool temple_placeable = GameMap::getInstance()->canPutBuilding
481             (Maptile::TEMPLE, cs->getRuinTileWidth(), tile);
482           if (!temple_placeable)
483             break;
484           Temple *t = new Temple(tile, cs->getTempleTileWidth());
485           GameMap::getInstance()->putTemple(t);
486           break;
487         }
488
489     case SIGNPOST:
490         {
491           bool signpost_placeable = GameMap::getInstance()->canPutBuilding
492             (Maptile::SIGNPOST, 1, tile);
493           if (!signpost_placeable)
494             break;
495           Signpost *s = new Signpost(tile);
496           GameMap::getInstance()->putSignpost(s);
497           break;
498         }
499
500     case PORT:
501         {
502           bool port_placeable = GameMap::getInstance()->canPutBuilding
503             (Maptile::PORT, 1, tile);
504           if (!port_placeable)
505             break;
506           Port *p = new Port(tile);
507           GameMap::getInstance()->putPort(p);
508           break;
509         }
510
511     case BRIDGE:
512         {
513           bool bridge_placeable = GameMap::getInstance()->canPutBuilding
514             (Maptile::BRIDGE, 1, tile);
515           if (!bridge_placeable)
516             break;
517           Bridge *b = new Bridge(tile, tile_to_bridge_type (tile));
518           GameMap::getInstance()->putBridge(b);
519           break;
520         }
521
522     case ROAD:
523         {
524           bool road_placeable = GameMap::getInstance()->canPutBuilding
525             (Maptile::ROAD, 1, tile);
526           if (!road_placeable)
527             {
528               //overwrite existing roads
529               if (GameMap::getRoad(tile) != NULL)
530                 GameMap::getInstance()->removeRoad(tile);
531               else
532                 break;
533             }
534
535           int type = CreateScenario::calculateRoadType(tile);
536           Road *r = new Road(tile, type);
537           GameMap::getInstance()->putRoad(r);
538
539           changed_tiles.pos -= Vector<int>(1, 1);
540           changed_tiles.dim = Vector<int>(3, 3);
541           break;
542         }
543     case BAG:
544       if (maptile->getMaptileType() != Tile::WATER)
545         {
546           //open the dialog
547           MapBackpack *bag = 
548             GameMap::getInstance()->getTile(tile)->getBackpack();
549           BackpackEditorDialog d(dynamic_cast<Backpack*>(bag));
550           d.run();
551         }
552       break;
553
554     }
555
556   if (changed_tiles.w > 0 && changed_tiles.h > 0)
557     map_tiles_changed.emit(changed_tiles);
558
559   draw(Playerlist::getViewingplayer());
560 }
561
562 void EditorBigMap::smooth_view()
563 {
564   GameMap::getInstance()->applyTileStyles(view.y, view.x, view.y+view.h, 
565                                           view.x+view.w, true);
566   draw(Playerlist::getViewingplayer());
567 }
568
569 void EditorBigMap::after_draw()
570 {
571     int tilesize = GameMap::getInstance()->getTileset()->getTileSize();
572     std::vector<Vector<int> > tiles;
573
574     if (show_tile_types_instead_of_tile_styles)
575       {
576         tiles = get_screen_tiles();
577         for (std::vector<Vector<int> >::iterator i = tiles.begin(),
578              end = tiles.end(); i != end; ++i)
579           {
580             Vector<int> pos = tile_to_buffer_pos(*i);
581             buffer_gc->set_rgb_fg_color(GameMap::getInstance()->getTile(*i)->getColor());
582             int x = pos.x;
583             int y = pos.y;
584             int ts = tilesize;
585             if (d_grid_toggled)
586               {
587                 x++;
588                 y++;
589                 ts--;
590               }
591             buffer->draw_rectangle (buffer_gc, true, x, y, ts, ts);
592           }
593       }
594
595     // we need to draw a drawing cursor on the map
596     tiles = get_cursor_tiles();
597     // draw each tile
598         
599     Gdk::Color terrain_box_color = Gdk::Color();
600     terrain_box_color.set_rgb_p(200.0/255.0, 200.0/255.0, 200.0/255.0);
601     Gdk::Color erase_box_color = Gdk::Color();
602     erase_box_color.set_rgb_p(200.0/255.0, 50.0/255.0, 50.0/255.0);
603     Gdk::Color move_box_color = Gdk::Color();
604     move_box_color.set_rgb_p(50.0/255.0, 200.0/255.0, 50.0/255.0);
605     Gdk::Color moving_box_color = Gdk::Color();
606     moving_box_color.set_rgb_p(50.0/255.0, 50.0/255.0, 200.0/255.0);
607     for (std::vector<Vector<int> >::iterator i = tiles.begin(),
608              end = tiles.end(); i != end; ++i)
609       {
610         Vector<int> pos = tile_to_buffer_pos(*i);
611
612         PixMask *pic;
613
614
615         switch (pointer)
616           {
617           case POINTER:
618             break;
619
620           case TERRAIN:
621             buffer_gc->set_rgb_fg_color (terrain_box_color);
622             buffer->draw_rectangle (buffer_gc, false, pos.x + 1, pos.y + 1, 
623                                     tilesize - 1, tilesize -1);
624             break;
625
626           case ERASE:
627             buffer_gc->set_rgb_fg_color (erase_box_color);
628             buffer->draw_rectangle (buffer_gc, false, pos.x + 1, pos.y + 1, 
629                                     tilesize - 1, tilesize -1);
630             break;
631
632           case MOVE:
633             if (moving_objects_from != Vector<int>(-1,-1))
634               buffer_gc->set_rgb_fg_color (moving_box_color);
635             else
636               buffer_gc->set_rgb_fg_color (move_box_color);
637             buffer->draw_rectangle (buffer_gc, false, pos.x + 1, pos.y + 1, 
638                                     tilesize - 1, tilesize -1);
639             break;
640
641           case STACK:
642             pic = GraphicsCache::getInstance()->getArmyPic
643               (Playerlist::getInstance()->getActiveplayer()->getArmyset(), 0,
644                Playerlist::getInstance()->getActiveplayer(), NULL);
645             pic->blit(buffer, pos);
646             break;
647
648           case CITY:
649             pic = GraphicsCache::getInstance()->getCityPic(0, Playerlist::getInstance()->getActiveplayer(), GameMap::getInstance()->getCityset()->getId());
650             pic->blit(buffer, pos);
651             break;
652
653           case RUIN:
654             pic = GraphicsCache::getInstance()->getRuinPic(0, GameMap::getInstance()->getCityset()->getId());
655             pic->blit(buffer, pos);
656             break;
657
658           case TEMPLE:
659             pic = GraphicsCache::getInstance()->getTemplePic(0, GameMap::getInstance()->getCityset()->getId());
660             pic->blit(buffer, pos);
661             break;
662
663           case SIGNPOST:
664             pic = GraphicsCache::getInstance()->getSignpostPic();
665             pic->blit(buffer, pos);
666             break;
667
668           case ROAD:
669             pic = GraphicsCache::getInstance()->getRoadPic(CreateScenario::calculateRoadType(*i));
670             pic->blit(buffer, pos);
671             break;
672           case PORT:
673             pic = GraphicsCache::getInstance()->getPortPic();
674             pic->blit(buffer, pos);
675             break;
676           case BRIDGE:
677             pic = GraphicsCache::getInstance()->getBridgePic(tile_to_bridge_type(*i));
678             pic->blit(buffer, pos);
679             break;
680           case BAG:
681             pic = GraphicsCache::getInstance()->getBagPic();
682             pic->blit(buffer, pos);
683             break;
684           }
685       }
686 }