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