1 //  Copyright (C) 2007 Ole Laursen
2 //  Copyright (C) 2007, 2008, 2009, 2010, 2012, 2014, 2015, 2017 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 #include <iomanip>
21 
22 #include <gtkmm.h>
23 #include <sigc++/functors/mem_fun.h>
24 
25 #include "hero-dialog.h"
26 
27 #include "input-helpers.h"
28 #include "ucompose.hpp"
29 #include "defs.h"
30 #include "hero.h"
31 #include "Item.h"
32 #include "GameMap.h"
33 #include "Backpack.h"
34 #include "MapBackpack.h"
35 #include "history.h"
36 #include "playerlist.h"
37 
38 #define method(x) sigc::mem_fun(*this, &HeroDialog::x)
39 
HeroDialog(Gtk::Window & parent,Hero * h,Vector<int> p)40 HeroDialog::HeroDialog(Gtk::Window &parent, Hero *h, Vector<int> p)
41  : LwDialog(parent, "hero-dialog.ui")
42 {
43   inhibit_hero_changed = false;
44   hero = h;
45   pos = p;
46 
47   xml->get_widget("map_image", map_image);
48 
49   std::list<Hero*> heroes = Playerlist::getActiveplayer()->getHeroes();
50   heroesmap = new HeroesMap(heroes);
51   if (hero)
52     heroesmap->setSelectedHero(hero);
53   else
54     {
55       hero = *heroes.begin();
56       pos = Playerlist::getActiveplayer()->getPositionOfArmyById(hero->getId());
57       heroesmap->setSelectedHero(hero);
58     }
59   heroesmap->map_changed.connect (method(on_map_changed));
60   Gtk::EventBox *map_eventbox;
61   xml->get_widget("map_eventbox", map_eventbox);
62   map_eventbox->add_events(Gdk::BUTTON_PRESS_MASK);
63   map_eventbox->signal_button_press_event().connect
64     (method(on_map_mouse_button_event));
65 
66   xml->get_widget("battle_label", battle_label);
67   xml->get_widget("strength_label", strength_label);
68   xml->get_widget("command_label", command_label);
69   xml->get_widget("moves_label", moves_label);
70   xml->get_widget("level_label", level_label);
71   xml->get_widget("upkeep_label", upkeep_label);
72   xml->get_widget("experience_label", experience_label);
73 
74   xml->get_widget("drop_button", drop_button);
75   xml->get_widget("pickup_button", pickup_button);
76   drop_button->signal_clicked().connect(method(on_drop_clicked));
77   pickup_button->signal_clicked().connect(method(on_pickup_clicked));
78 
79   xml->get_widget("next_button", next_button);
80   xml->get_widget("prev_button", prev_button);
81   next_button->signal_clicked().connect(method(on_next_clicked));
82   prev_button->signal_clicked().connect(method(on_prev_clicked));
83   if (heroes.size() <= 1)
84     {
85       next_button->set_sensitive(false);
86       prev_button->set_sensitive(false);
87     }
88 
89   heroes_list = Gtk::ListStore::create(heroes_columns);
90   xml->get_widget("heroes_treeview", heroes_treeview);
91   heroes_treeview->set_model(heroes_list);
92   heroes_treeview->append_column(_("Hero"), heroes_columns.name);
93 
94   heroes_list->clear();
95   guint32 count = 0;
96   for (std::list<Hero*>::iterator it = heroes.begin(); it != heroes.end(); it++)
97     {
98       add_hero (*it);
99       if (*it == hero)
100         {
101           Gtk::TreeModel::Row row = heroes_treeview->get_model()->children()[count];
102           heroes_treeview->get_selection()->select(row);
103         }
104       count++;
105     }
106   heroes_treeview->get_selection()->signal_changed().connect(method(on_hero_changed));
107 
108   item_list = Gtk::ListStore::create(item_columns);
109   xml->get_widget("treeview", item_treeview);
110   item_treeview->set_model(item_list);
111   item_treeview->append_column("", item_columns.image);
112   item_treeview->append_column(_("Name"), item_columns.name);
113   item_treeview->append_column(_("Attributes"), item_columns.attributes);
114   item_treeview->append_column(_("Status"), item_columns.status);
115 
116   item_treeview->get_selection()->signal_changed().connect(method(on_item_selection_changed));
117 
118   events_list = Gtk::ListStore::create(events_columns);
119   xml->get_widget("events_treeview", events_treeview);
120   events_treeview->append_column("", events_columns.desc);
121   events_treeview->set_model(events_list);
122   events_list->clear();
123 
124   on_item_selection_changed();
125 }
126 
~HeroDialog()127 HeroDialog::~HeroDialog()
128 {
129   delete heroesmap;
130 }
131 
addHistoryEvent(History * history)132 void HeroDialog::addHistoryEvent(History *history)
133 {
134   Glib::ustring s = "";
135   Gtk::TreeIter i = events_list->append();
136 
137   switch (history->getType())
138     {
139     case History::FOUND_SAGE:
140 	{
141 	  auto *ev = static_cast<History_FoundSage *>(history);
142 	  s = String::ucompose(_("%1 finds a sage!"), ev->getHeroName());
143 	  break;
144 	}
145     case History::HERO_EMERGES:
146 	{
147 	  auto *ev = static_cast<History_HeroEmerges *>(history);
148 	  s = String::ucompose(_("%1 emerges in %2!"), ev->getHeroName(),
149 			       ev->getCityName());
150 	  break;
151 	}
152     case History::HERO_QUEST_STARTED:
153 	{
154 	  auto *ev = static_cast<History_HeroQuestStarted*>(history);
155 	  s = String::ucompose(_("%1 begins a quest!"), ev->getHeroName());
156 	  break;
157 	}
158     case History::HERO_QUEST_COMPLETED:
159 	{
160 	  auto *ev = static_cast<History_HeroQuestCompleted *>(history);
161 	  s = String::ucompose(_("%1 finishes a quest!"), ev->getHeroName());
162 	  break;
163 	}
164     case History::HERO_KILLED_IN_CITY:
165 	{
166 	  auto *ev = static_cast<History_HeroKilledInCity *>(history);
167 	  s = String::ucompose(_("%1 is killed in %2!"), ev->getHeroName(),
168 			       ev->getCityName());
169 	  break;
170 	}
171     case History::HERO_KILLED_IN_BATTLE:
172 	{
173 	  auto *ev = static_cast<History_HeroKilledInBattle *>(history);
174 	  s = String::ucompose(_("%1 is killed in battle!"), ev->getHeroName());
175 	  break;
176 	}
177     case History::HERO_KILLED_SEARCHING:
178 	{
179 	  auto *ev = static_cast<History_HeroKilledSearching *>(history);
180 	  s = String::ucompose(_("%1 is killed while searching!"),
181 			       ev->getHeroName());
182 	  break;
183 	}
184     case History::HERO_CITY_WON:
185 	{
186 	  auto *ev = static_cast<History_HeroCityWon *>(history);
187 	  s = String::ucompose(_("%1 conquers %2!"), ev->getHeroName(),
188 			       ev->getCityName());
189 	  break;
190 	}
191     case History::HERO_FINDS_ALLIES:
192 	{
193 	  auto *ev = static_cast<History_HeroFindsAllies*>(history);
194 	  s = String::ucompose(_("%1 finds allies!"), ev->getHeroName());
195 	  break;
196 	}
197     default:
198       s = _("unknown");
199       break;
200     }
201 
202   (*i)[events_columns.desc] = s;
203   (*i)[events_columns.history] = history;
204 }
205 
run()206 void HeroDialog::run()
207 {
208   heroesmap->resize();
209   heroesmap->draw();
210   GameMap *gm = GameMap::getInstance();
211   dialog->show_all();
212   show_hero();
213   dialog->run();
214   if (gm->getTile(pos)->getBackpack()->size() > 0 &&
215       gm->getTile(pos)->getType() == Tile::WATER)
216     {
217       // splash, items lost forever
218       while (gm->getTile(pos)->getBackpack()->size())
219         {
220 	  MapBackpack::iterator i = gm->getTile(pos)->getBackpack()->begin();
221           gm->getTile(pos)->getBackpack()->removeFromBackpack(*i);
222         }
223     }
224 }
225 
update_hero_list()226 void HeroDialog::update_hero_list()
227 {
228   inhibit_hero_changed = true;
229   std::list<Hero*> heroes;
230   heroes = Playerlist::getActiveplayer()->getHeroes();
231   guint32 count = 0;
232   for (std::list<Hero*>::iterator it = heroes.begin(); it != heroes.end(); it++)
233     {
234       if (*it == hero)
235         {
236           Gtk::TreeModel::Row row;
237           row = heroes_treeview->get_model()->children()[count];
238           heroes_treeview->get_selection()->select(row);
239         }
240       count++;
241     }
242   inhibit_hero_changed = false;
243 }
244 
on_hero_changed()245 void HeroDialog::on_hero_changed()
246 {
247   if (inhibit_hero_changed == true)
248     return;
249   Glib::RefPtr<Gtk::TreeSelection> selection = heroes_treeview->get_selection();
250   Gtk::TreeModel::iterator iterrow = selection->get_selected();
251 
252   if (iterrow)
253     {
254       Gtk::TreeModel::Row row = *iterrow;
255       hero = row[heroes_columns.hero];
256       pos = Playerlist::getActiveplayer()->getPositionOfArmyById(hero->getId());
257       heroesmap->setSelectedHero(hero);
258       show_hero();
259       heroesmap->draw();
260     }
261 }
262 
on_item_selection_changed()263 void HeroDialog::on_item_selection_changed()
264 {
265   Gtk::TreeIter i = item_treeview->get_selection()->get_selected();
266   if (i)
267     {
268       bool droppable = (*i)[item_columns.status] == _("In backpack");
269       drop_button->set_sensitive(droppable);
270       pickup_button->set_sensitive(!droppable);
271     }
272   else
273     {
274       drop_button->set_sensitive(false);
275       pickup_button->set_sensitive(false);
276     }
277 }
278 
on_drop_clicked()279 void HeroDialog::on_drop_clicked()
280 {
281   Gtk::TreeIter i = item_treeview->get_selection()->get_selected();
282   if (i)
283     {
284       bool splash = false;
285       Item *item = (*i)[item_columns.item];
286       hero->getOwner()->heroDropItem (hero, item, pos, splash);
287       if (splash == false)
288         (*i)[item_columns.status] = _("On the ground");
289       else
290         item_list->erase(i); //splash
291 
292       on_item_selection_changed();
293       fill_in_info_labels();
294     }
295 }
296 
on_next_clicked()297 void HeroDialog::on_next_clicked()
298 {
299   std::list<Hero*> heroes;
300   heroes = Playerlist::getActiveplayer()->getHeroes();
301   std::list<Hero*>::iterator next;
302   next = find (heroes.begin(), heroes.end(), hero);
303   if (next != heroes.end())
304     {
305       next++;
306       if (next == heroes.end())
307 	next = heroes.begin();
308       hero = *next;
309       heroesmap->setSelectedHero(hero);
310       show_hero();
311       heroesmap->draw();
312     }
313   update_hero_list();
314 }
on_prev_clicked()315 void HeroDialog::on_prev_clicked()
316 {
317   std::list<Hero*> heroes;
318   heroes = Playerlist::getActiveplayer()->getHeroes();
319   std::list<Hero*>::reverse_iterator prev;
320   prev = find (heroes.rbegin(), heroes.rend(), hero);
321   if (prev != heroes.rend())
322     {
323       prev++;
324       if (prev == heroes.rend())
325 	prev = heroes.rbegin();
326       hero = *prev;
327       heroesmap->setSelectedHero(hero);
328       show_hero();
329       heroesmap->draw();
330     }
331   update_hero_list();
332 }
333 
on_pickup_clicked()334 void HeroDialog::on_pickup_clicked()
335 {
336   Gtk::TreeIter i = item_treeview->get_selection()->get_selected();
337   if (i)
338     {
339       Item *item = (*i)[item_columns.item];
340       if (item->getPlanted() == true)
341         item->setPlanted(false);
342       hero->getOwner()->heroPickupItem (hero, item, pos);
343       (*i)[item_columns.status] = _("In backpack");
344       on_item_selection_changed();
345       fill_in_info_labels();
346     }
347 }
348 
add_hero(Hero * h)349 void HeroDialog::add_hero(Hero *h)
350 {
351   Gtk::TreeIter i = heroes_list->append();
352   (*i)[heroes_columns.name] = h->getName();
353   (*i)[heroes_columns.hero] = h;
354 }
355 
add_item(Item * item,bool in_backpack)356 void HeroDialog::add_item(Item *item, bool in_backpack)
357 {
358   Gtk::TreeIter i = item_list->append();
359   (*i)[item_columns.name] = item->getName();
360 
361   (*i)[item_columns.attributes] = item->getBonusDescription();
362 
363   if (in_backpack)
364     (*i)[item_columns.status] = _("In backpack");
365   else
366     (*i)[item_columns.status] = _("On the ground");
367 
368   (*i)[item_columns.item] = item;
369 }
370 
fill_in_info_labels()371 void HeroDialog::fill_in_info_labels()
372 {
373   guint32 bonus = 0;
374   Backpack *backpack = hero->getBackpack();
375   for (Backpack::iterator i = backpack->begin(); i != backpack->end(); ++i)
376     {
377       if ((*i)->getBonus(Item::ADD1STR))
378         bonus += 1;
379       if ((*i)->getBonus(Item::ADD2STR))
380         bonus += 2;
381       if ((*i)->getBonus(Item::ADD3STR))
382         bonus += 3;
383     }
384   battle_label->set_text(String::ucompose("%1", bonus));
385 
386   bonus = 0;
387   for (Backpack::iterator i = backpack->begin(); i != backpack->end(); ++i)
388     {
389       if ((*i)->getBonus(Item::ADD1STACK))
390         bonus += 1;
391       if ((*i)->getBonus(Item::ADD2STACK))
392         bonus += 2;
393       if ((*i)->getBonus(Item::ADD3STACK))
394         bonus += 3;
395     }
396 
397   //now add natural command
398   bonus += hero->calculateNaturalCommand ();
399 
400   command_label->set_text (String::ucompose("%1", bonus));
401   level_label->set_text(String::ucompose("%1", hero->getLevel()));
402   experience_label->set_text(String::ucompose("%1", int(hero->getXP())));
403 
404   strength_label->set_text(String::ucompose("%1",
405                                             hero->getStat(Army::STRENGTH)));
406 
407   // note to translators: %1 is remaining moves, %2 is total moves
408   moves_label->set_text(String::ucompose(_("%1/%2"),
409                         hero->getMoves(), hero->getStat(Army::MOVES)));
410   upkeep_label->set_text(String::ucompose("%1", hero->getUpkeep()));
411 }
412 
on_map_changed(Cairo::RefPtr<Cairo::Surface> map)413 void HeroDialog::on_map_changed(Cairo::RefPtr<Cairo::Surface> map)
414 {
415   Glib::RefPtr<Gdk::Pixbuf> pixbuf =
416     Gdk::Pixbuf::create(map, 0, 0,
417                         heroesmap->get_width(), heroesmap->get_height());
418   map_image->property_pixbuf() = pixbuf;
419 }
420 
on_map_mouse_button_event(GdkEventButton * e)421 bool HeroDialog::on_map_mouse_button_event(GdkEventButton *e)
422 {
423     if (e->type != GDK_BUTTON_PRESS)
424 	return true;	// useless event
425 
426     heroesmap->mouse_button_event(to_input_event(e));
427 
428     hero = heroesmap->getSelectedHero();
429     pos = Playerlist::getActiveplayer()->getPositionOfArmyById(hero->getId());
430     show_hero();
431     heroesmap->draw();
432     update_hero_list();
433     return true;
434 }
435 
show_hero()436 void HeroDialog::show_hero()
437 {
438   dialog->set_title(hero->getName());
439 
440   fill_in_info_labels();
441   std::list<History* > events;
442   events = hero->getOwner()->getHistoryForHeroId(hero->getId());
443   events_list->clear();
444   for (std::list<History*>::iterator i = events.begin(); i != events.end();
445        i++)
446     addHistoryEvent(*i);
447 
448   // populate the item list
449   item_list->clear();
450   Backpack *backpack = hero->getBackpack();
451   for (Backpack::iterator i = backpack->begin(); i != backpack->end(); ++i)
452     add_item(*i, true);
453 
454   MapBackpack *ground = GameMap::getInstance()->getTile(pos)->getBackpack();
455   for (MapBackpack::iterator i = ground->begin(); i != ground->end(); i++)
456     add_item(*i, false);
457 
458   return;
459 }
460