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