initial commit, lordsawar source, slightly modified
[lordsawar] / src / vectormap.cpp
1 //  Copyright (C) 2007, 2008, 2009 Ben Asselstine
2 //
3 //  This program is free software; you can redistribute it and/or modify
4 //  it under the terms of the GNU General Public License as published by
5 //  the Free Software Foundation; either version 3 of the License, or
6 //  (at your option) any later version.
7 //
8 //  This program is distributed in the hope that it will be useful,
9 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 //  GNU Library General Public License for more details.
12 //
13 //  You should have received a copy of the GNU General Public License
14 //  along with this program; if not, write to the Free Software
15 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
16 //  02110-1301, USA.
17
18 #include "vectormap.h"
19 #include "gui/image-helpers.h"
20
21 #include "city.h"
22 #include "citylist.h"
23 #include "playerlist.h"
24 #include "GraphicsCache.h"
25 #include "GameMap.h"
26 #include "LocationBox.h"
27 #include "shieldsetlist.h"
28
29 VectorMap::VectorMap(City *c, enum ShowVectoring v, bool see_opponents_production)
30 {
31   d_see_opponents_production = see_opponents_production;
32   show_vectoring = v;
33   city = c;
34   click_action = CLICK_SELECTS;
35 }
36
37 void VectorMap::draw_planted_standard(Vector<int> flag)
38 {
39   //it can't possibly be fogged
40   GraphicsCache *gc = GraphicsCache::getInstance();
41
42   Vector<int> start;
43   
44   start = flag;
45   start = mapToSurface(start);
46       
47   PixMask *heropic = gc->getSmallHeroPic(true);
48   heropic->blit_centered(surface, start);
49 }
50
51 void VectorMap::draw_city (City *c, guint32 &type, bool &prod)
52 {
53   if (c->isVisible(Playerlist::getViewingplayer()) == false)
54     return;
55   GraphicsCache *gc = GraphicsCache::getInstance();
56   PixMask *tmp;
57   if (c->isBurnt() == true)
58     tmp = gc->getSmallRuinedCityPic ();
59   //else if(type == 4 && prod == false)
60     //tmp = gc->getProdShieldPic (type, prod);
61   else
62     {
63       if (Playerlist::getInstance()->getViewingplayer() != c->getOwner())
64         {
65           guint32 s = GameMap::getInstance()->getShieldset()->getId();
66           tmp = gc->getShieldPic (s, 0, c->getOwner()->getId());
67         }
68       else
69         tmp = gc->getProdShieldPic (type, prod);
70     }
71
72   Vector<int> start;
73   
74   start  = c->getPos();
75   start = mapToSurface(start);
76   if (tmp)
77     {
78       tmp->blit_centered(surface, start);
79     }
80 }
81 void VectorMap::draw_cities (std::list<City*> citylist, guint32 type)
82 {
83   bool prod;
84
85   std::list<City*>::iterator it;
86   for (it = citylist.begin(); it != citylist.end(); it++)
87     {
88       switch (click_action)
89         {
90         case CLICK_VECTORS:
91         case CLICK_CHANGES_DESTINATION:
92           if ((*it)->canAcceptMoreVectoring() == false)
93             {
94               prod = false; //the inn is full
95               type = 4;
96             }
97           else
98             {
99               if ((*it)->getActiveProductionSlot() == -1)
100                 prod = false;
101               else
102                 prod = true;
103             }
104           break;
105         case CLICK_SELECTS:
106           if ((*it)->getActiveProductionSlot() == -1)
107             prod = false;
108           else
109             prod = true;
110           break;
111         }
112       draw_city ((*it), type, prod);
113     }
114 }
115
116 void VectorMap::draw_vectoring_line(Vector<int> src, Vector<int> dest, bool to)
117 {
118   Vector<int> start = src;
119   Vector <int> end = dest;
120   start = mapToSurface(start);
121   end = mapToSurface(end);
122   Gdk::Color line_color = Gdk::Color();
123   if (to) //yellow
124     line_color.set_rgb_p(252.0/255.0, 236.0/255.0, 
125                           32.0/255.0);
126   else //orange
127     line_color.set_rgb_p(252.0/255.0, 160.0/255.0, 0);
128   draw_line(start.x, start.y, end.x, end.y, line_color);
129 }
130 void VectorMap::draw_vectoring_line_from_here_to (Vector<int> dest)
131 {
132   draw_vectoring_line (city->getPos(), dest, true);
133 }
134
135 void VectorMap::draw_vectoring_line_to_here_from (Vector<int> src)
136 {
137   draw_vectoring_line (src, city->getPos(), false);
138 }
139
140 void VectorMap::draw_lines (std::list<City*> srcs, std::list<City*> dests)
141 {
142   Citylist *cl = Citylist::getInstance();
143   Vector<int> end;
144   std::list<City*>::iterator it;
145   std::list<City*>::iterator cit;
146   //yellow lines first.  cities vectoring units to their destinations.
147   for (it = srcs.begin(); it != srcs.end(); it++)
148     {
149       if ((*it)->getVectoring() == Vector<int>(-1, -1))
150         continue;
151       if ((*it)->isVisible(Playerlist::getViewingplayer()) == false)
152         continue;
153       City *c = cl->getNearestObjectBefore((*it)->getVectoring(), 2);
154       if (c)
155         end = c->getPos();
156       else
157         end = planted_standard;
158
159       Vector<int> pos = (*it)->getVectoring();
160       draw_vectoring_line ((*it)->getPos(), end, true);
161     }
162   //orange lines next.  cities receiving units from their sources.
163   for (it = dests.begin(); it != dests.end(); it++)
164     {
165       //who is vectoring to this (*it) city?
166       std::list<City*> sources = cl->getCitiesVectoringTo(*it);
167       for (cit = sources.begin(); cit != sources.end(); cit++)
168         draw_vectoring_line ((*it)->getPos(), (*cit)->getPos(), false);
169     }
170 }
171
172 void VectorMap::after_draw()
173 {
174   Vector<int> start;
175   guint32 type = 0;
176   bool prod = false;
177   Vector<int> end;
178   Citylist *cl = Citylist::getInstance();
179   std::list<City*> dests; //destination cities
180   std::list<City*> srcs; //source cities
181     
182   Vector<int> flag;
183   flag = GameMap::getInstance()->findPlantedStandard(city->getOwner());
184   planted_standard = flag;
185
186   //only show cities that can accept more vectoring when
187   //the click action is vector
188   
189   std::list<City*> sources;
190
191   if (click_action == CLICK_CHANGES_DESTINATION)
192     sources = cl->getCitiesVectoringTo(city);
193
194   // draw special shield for every city that player owns.
195   for (Citylist::iterator it = cl->begin(); it != cl->end(); it++)
196     {
197       if ((*it)->getOwner() == Playerlist::getViewingplayer())
198         {
199       
200           if ((*it)->getActiveProductionSlot() == -1)
201             prod = false;
202           else
203             prod = true;
204           if (show_vectoring == SHOW_ALL_VECTORING)
205             {
206               // first pass, identify every city that's a source or dest
207               if ((*it)->getVectoring() != Vector<int>(-1, -1))
208                 {
209                   City *c = cl->getNearestCity((*it)->getVectoring(), 2);
210                   if (c)
211                     dests.push_back(c);
212                   srcs.push_back((*it));
213                 }
214               //paint them all as away first, and then overwrite them
215               //later in the second pass.
216               type = 1;
217             }
218           else
219             {
220               //is this the originating city?
221               if ((*it)->getId() == city->getId())
222                 {
223                   //then it's a "home" city.
224                   type = 0; 
225                 }
226               //is this the city i'm vectoring to?
227               else if (city->getVectoring() != Vector<int>(-1, -1) &&
228                        cl->getNearestCity(city->getVectoring(), 2)->getId() == 
229                          (*it)->getId() && show_vectoring != SHOW_NO_VECTORING)
230                 {
231                   // then it's a "destination" city.
232                   type = 2;
233                 }
234               //is this a city that is vectoring to me?
235               else if ((*it)->getVectoring() != Vector<int>(-1, -1) &&
236                        cl->getNearestCity((*it)->getVectoring(), 2)->getId() ==
237                        city->getId() && show_vectoring != SHOW_NO_VECTORING)
238                 type = 3; 
239               //otherwise it's just another city, "away" from me
240               else
241                 type = 1; //away
242               //show it as a ruined city if we can't vector to it.
243               if (click_action == CLICK_VECTORS && (*it)->canAcceptMoreVectoring() == false)
244                 {
245                   prod = false;
246                   type = 4; //the inn is full
247                 }
248               if (click_action == CLICK_CHANGES_DESTINATION)
249                 {
250                   if ((*it)->canAcceptMoreVectoring (sources.size()) == false)
251                     {
252                       prod = false; //the inn is full
253                       type = 4;
254                     }
255                 }
256             }
257           draw_city ((*it), type, prod);
258         }
259       else
260         {
261           type = 2;
262           prod = false;
263           draw_city ((*it), type, prod); //an impossible combo
264         }
265     }
266
267   //second pass, identify all the destination and source cities
268   if (show_vectoring == SHOW_ALL_VECTORING)
269     {
270       draw_cities (dests, 2);
271       draw_cities (srcs, 3);
272     }
273
274   if (show_vectoring == SHOW_ORIGIN_CITY_VECTORING && click_action == CLICK_SELECTS)
275     {
276       // draw lines from origination to city/planted standard
277       for (Citylist::iterator it = cl->begin(); it != cl->end(); it++)
278         {
279           if ((*it)->isVisible(Playerlist::getViewingplayer()) == false)
280             continue;
281           if ((*it)->getOwner() != city->getOwner())
282             continue;
283           if ((*it)->getVectoring() == Vector<int>(-1, -1))
284             continue;
285           if ((*it)->getVectoring() == planted_standard)
286             continue;
287
288           //is this a city that is vectoring to me?
289           if (city->contains((*it)->getVectoring()))
290             draw_vectoring_line_to_here_from((*it)->getPos());
291         }
292       // draw line from city to destination
293       if (city->getVectoring().x != -1)
294         draw_vectoring_line_from_here_to(city->getVectoring());
295     }
296   else if (show_vectoring == SHOW_ALL_VECTORING && click_action == CLICK_SELECTS)
297     draw_lines (srcs, dests);
298
299   if (flag.x != -1 && flag.y != -1)
300     draw_planted_standard(flag);
301
302   draw_square_around_active_city();  
303   map_changed.emit(surface);
304 }
305
306 void VectorMap::draw_square_around_active_city()
307 {
308   Gdk::Color box_color = Gdk::Color();
309   box_color.set_rgb_p(252.0/255.0, 1.0, 1.0);
310   Vector<int> start = city->getPos();
311   start = mapToSurface(start);
312   std::string s = GameMap::getInstance()->getShieldset()->getSubDir();
313   Shieldset *ss = Shieldsetlist::getInstance()->getShieldset(s);
314   guint32 width = ss->getSmallWidth();
315   guint32 height = ss->getSmallHeight();
316   start -= Vector<int>(width,height)/2;
317   Vector<int> end = start + Vector<int>(width,height);
318   draw_rect (start.x-3, start.y-3, end.x-start.x+4, end.y-start.y+4, box_color);
319 }
320
321 void VectorMap::mouse_button_event(MouseButtonEvent e)
322 {
323   Player *active = Playerlist::getActiveplayer();
324   if (e.button == MouseButtonEvent::LEFT_BUTTON && 
325       e.state == MouseButtonEvent::PRESSED)
326     {
327       Citylist *cl = Citylist::getInstance();
328       City *nearestCity;
329       Vector<int> dest;
330       dest = mapFromScreen(e.pos);
331
332       switch (click_action)
333         {
334         case CLICK_VECTORS:
335
336           nearestCity = cl->getNearestVisibleFriendlyCity(dest, 4);
337           if (nearestCity == NULL)
338             {
339               //no city near there.  are we close to the planted standard?
340               Vector<int>flag = planted_standard;
341               if (planted_standard.x != -1 && planted_standard.y != -1)
342                 {
343                   unsigned int dist = 4;
344                   LocationBox loc(dest - Vector<int>(dist,dist), dist * 2);
345                   if (loc.contains(flag))
346                     dest = planted_standard;
347                   else
348                     return;
349                 }
350               else
351                 return; //no cities, no planted flag
352             }
353           else if (nearestCity == city)
354             /* clicking on own city, makes vectoring stop */
355             dest = Vector<int>(-1, -1);
356
357           if (dest != Vector<int>(-1, -1) && dest != planted_standard &&
358               nearestCity != NULL)
359             {
360               //make sure that dest is the top left tile of the city
361               dest = nearestCity->getPos();
362             }
363           if (dest != city->getVectoring() && dest != Vector<int>(-1, -1))
364             {
365               if (active->vectorFromCity(city, dest) == true)
366                 destination_chosen.emit(dest);
367               setClickAction(CLICK_SELECTS);
368               draw(Playerlist::getViewingplayer());
369             }
370           else if (dest == Vector<int>(-1, -1)) //stop vectoring
371             {
372               if (active->vectorFromCity(city, dest) == true)
373                 destination_chosen.emit(dest);
374               setClickAction(CLICK_SELECTS);
375               draw(Playerlist::getViewingplayer());
376             }
377           break;
378         case CLICK_SELECTS:
379           if (d_see_opponents_production == true)
380             nearestCity = cl->getNearestVisibleCity(dest, 4);
381           else
382             nearestCity = cl->getNearestVisibleFriendlyCity(dest, 4);
383           if (nearestCity == NULL)
384             return;
385           city = nearestCity;
386           draw(Playerlist::getViewingplayer());
387           break;
388         case CLICK_CHANGES_DESTINATION:
389           nearestCity = cl->getNearestVisibleFriendlyCity(dest, 4);
390           if (nearestCity == NULL)
391             {
392               //no city near there.  are we close to the planted standard?
393               Vector<int>flag = planted_standard;
394               if (planted_standard.x != -1 && planted_standard.y != -1)
395                 {
396                   unsigned int dist = 4;
397                   LocationBox loc(dest - Vector<int>(dist,dist), dist * 2);
398                   if (loc.contains(flag))
399                     dest = planted_standard;
400                   else
401                     return;
402                 }
403               else
404                 return; //no cities, no planted flag
405             }
406           else if (nearestCity == city)
407             /* clicking on own city, makes vectoring stop */
408             dest = Vector<int>(-1, -1);
409           else
410             dest = nearestCity->getPos();
411
412           if (dest == Vector<int>(-1, -1)) //stop vectoring
413             {
414               //we were doing change destination,
415               //and we clicked back on our own city
416               //this is the same thing as a cancel.
417               destination_chosen.emit(dest);
418               setClickAction(CLICK_SELECTS);
419               draw(Playerlist::getViewingplayer());
420               return;
421             }
422           bool is_source_city = false;
423           std::list<City*> sources = cl->getCitiesVectoringTo(city);
424           std::list<City*>::iterator cit = sources.begin();
425           for (;cit != sources.end(); cit++)
426             {
427               if ((*cit)->contains(dest))
428                 {
429                   is_source_city = true;
430                   break;
431                 }
432             }
433           //if it's not one of our sources, then select it
434           //why do we care if it's one of our sources anyway?
435           if (is_source_city == false)
436             {
437               if (active->changeVectorDestination(city->getPos(), dest) == true)
438                 destination_chosen.emit(dest);
439               setClickAction(CLICK_SELECTS);
440               draw(Playerlist::getViewingplayer());
441               if (dest != planted_standard)
442                 city = nearestCity;
443               draw(Playerlist::getViewingplayer());
444             }
445           break;
446         }
447     }
448 }
449
450 void VectorMap::setCity(City *c)
451 {
452   city = c;
453   draw(Playerlist::getViewingplayer());
454 }