1 //  Copyright (C) 2017, 2020 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 <config.h>
19 
20 #include <gtkmm.h>
21 #include <sigc++/functors/mem_fun.h>
22 
23 #include "battle-calculator-dialog.h"
24 
25 #include "ucompose.hpp"
26 #include "defs.h"
27 #include "stack.h"
28 #include "army.h"
29 #include "armyproto.h"
30 #include "hero.h"
31 #include "heroproto.h"
32 #include "ImageCache.h"
33 #include "playerlist.h"
34 #include "stacklist.h"
35 #include "stacktile.h"
36 #include "hero-editor-dialog.h"
37 #include "GameMap.h"
38 #include "tileset.h"
39 #include "fight.h"
40 #include "font-size.h"
41 
42 #include "select-army-dialog.h"
43 
44 #define method(x) sigc::mem_fun(*this, &BattleCalculatorDialog::x)
45 
BattleCalculatorDialog(Gtk::Window & parent,std::list<Army * > & attackers,std::list<Army * > & defenders)46 BattleCalculatorDialog::BattleCalculatorDialog(Gtk::Window &parent, std::list<Army*> &attackers, std::list<Army *> &defenders)
47  : LwEditorDialog(parent, "battle-calculator-dialog.ui"),
48     attacker_strength_column(_("Strength"), attacker_strength_renderer),
49     defender_strength_column(_("Strength"), defender_strength_renderer),
50     d_attackers(attackers), d_defenders(defenders)
51 {
52   attacker_player_combobox = NULL;
53   attacker_player_combobox = manage(new Gtk::ComboBoxText);
54   for (auto p : *Playerlist::getInstance())
55     attacker_player_combobox->append(p->getName());
56   attacker_player_combobox->set_active(0);
57   attacker_player_combobox->signal_changed().connect (method(on_attacker_player_changed));
58   Gtk::Box *box1;
59   xml->get_widget("attacker_hbox", box1);
60   box1->pack_start(*attacker_player_combobox, Gtk::PACK_SHRINK);
61 
62   defender_player_combobox = NULL;
63   defender_player_combobox = manage(new Gtk::ComboBoxText);
64   for (auto p : *Playerlist::getInstance())
65     defender_player_combobox->append(p->getName());
66   defender_player_combobox->set_active(0);
67   defender_player_combobox->signal_changed().connect (method(on_defender_player_changed));
68   Gtk::Box *box2;
69   xml->get_widget("defender_hbox", box2);
70   box2->pack_start(*defender_player_combobox, Gtk::PACK_SHRINK);
71 
72   attackers_list = Gtk::ListStore::create(combatant_columns);
73   xml->get_widget("attackers_treeview", attackers_treeview);
74   attackers_treeview->set_model(attackers_list);
75 
76   attackers_treeview->append_column("", combatant_columns.image);
77 
78   attacker_strength_renderer.property_editable() = true;
79   attacker_strength_renderer.signal_edited().connect(method(on_attacker_strength_edited));
80   attacker_strength_column.set_cell_data_func(attacker_strength_renderer, method(cell_data_attacker_strength));
81   attackers_treeview->append_column(attacker_strength_column);
82   attackers_treeview->append_column(_("Augmented Str"), combatant_columns.augmented_strength);
83   attackers_treeview->append_column(_("HP"), combatant_columns.hp);
84   for (guint32 i = 0; i < attackers_treeview->get_n_columns(); i++)
85     attackers_treeview->get_column(i)->set_expand();
86 
87   defenders_list = Gtk::ListStore::create(combatant_columns);
88   xml->get_widget("defenders_treeview", defenders_treeview);
89   defenders_treeview->set_model(defenders_list);
90 
91   defenders_treeview->append_column("", combatant_columns.image);
92 
93   defender_strength_renderer.property_editable() = true;
94   defender_strength_renderer.signal_edited().connect(method(on_defender_strength_edited));
95   defender_strength_column.set_cell_data_func(defender_strength_renderer, method(cell_data_defender_strength));
96   defenders_treeview->append_column(defender_strength_column);
97   defenders_treeview->append_column(_("Augmented Str"), combatant_columns.augmented_strength);
98   defenders_treeview->append_column(_("HP"), combatant_columns.hp);
99   for (guint32 i = 0; i < attackers_treeview->get_n_columns(); i++)
100     defenders_treeview->get_column(i)->set_expand();
101   xml->get_widget("fortified_switch", fortified_switch);
102 
103   xml->get_widget("attacker_add_button", attacker_add_button);
104   xml->get_widget("attacker_remove_button", attacker_remove_button);
105   xml->get_widget("attacker_copy_button", attacker_copy_button);
106   xml->get_widget("attacker_edit_hero_button", attacker_edit_hero_button);
107 
108   attacker_add_button->signal_clicked().connect(method(on_attacker_add_clicked));
109   attacker_remove_button->signal_clicked().connect(method(on_attacker_remove_clicked));
110   attacker_copy_button->signal_clicked().connect(method(on_attacker_copy_clicked));
111   attacker_edit_hero_button->signal_clicked().connect(method(on_attacker_edit_hero_clicked));
112 
113   attackers_treeview->get_selection()->signal_changed().connect(method(on_attacker_selection_changed));
114 
115   xml->get_widget("defender_add_button", defender_add_button);
116   xml->get_widget("defender_remove_button", defender_remove_button);
117   xml->get_widget("defender_copy_button", defender_copy_button);
118   xml->get_widget("defender_edit_hero_button", defender_edit_hero_button);
119 
120   defender_add_button->signal_clicked().connect(method(on_defender_add_clicked));
121   defender_remove_button->signal_clicked().connect(method(on_defender_remove_clicked));
122   defender_copy_button->signal_clicked().connect(method(on_defender_copy_clicked));
123   defender_edit_hero_button->signal_clicked().connect(method(on_defender_edit_hero_clicked));
124 
125   defenders_treeview->get_selection()->signal_changed().connect(method(on_defender_selection_changed));
126   xml->get_widget("fight_button", fight_button);
127   fight_button->signal_clicked().connect(method(on_fight_clicked));
128   xml->get_widget("fight100_button", fight100_button);
129   fight100_button->signal_clicked().connect(method(on_fight100_clicked));
130 
131   xml->get_widget("city_switch", city_switch);
132   city_switch->property_active().signal_changed().connect(method(on_city_toggled));
133   xml->get_widget("terrain_box", terrain_box);
134   terrain_combobox = manage(new Gtk::ComboBoxText);
135   Tileset *tileset = GameMap::getTileset();
136   for (auto t : *tileset)
137     terrain_combobox->append(t->getName());
138   terrain_combobox->set_active(0);
139   terrain_box->pack_start(*terrain_combobox, Gtk::PACK_SHRINK);
140   xml->get_widget("die_sides_combobox", die_sides_combobox);
141   for (auto a : d_attackers)
142     add_attacker_army (a, false);
143   for (auto a : d_defenders)
144     add_defender_army (a, false);
145   set_button_sensitivity();
146 }
147 
run()148 int BattleCalculatorDialog::run()
149 {
150   dialog->show_all();
151   return dialog->run();
152 }
153 
get_attacker_player()154 Player *BattleCalculatorDialog::get_attacker_player()
155 {
156   int c = 0, row = attacker_player_combobox->get_active_row_number();
157   Player *player = Playerlist::getInstance()->getNeutral();
158   for (Playerlist::iterator i = Playerlist::getInstance()->begin(),
159        end = Playerlist::getInstance()->end(); i != end; ++i, ++c)
160     if (c == row)
161       {
162 	player = *i;
163 	break;
164       }
165   return player;
166 }
167 
on_attacker_copy_clicked()168 void BattleCalculatorDialog::on_attacker_copy_clicked()
169 {
170   Gtk::TreeIter i = attackers_treeview->get_selection()->get_selected();
171   if (i)
172     {
173       Player *player = get_attacker_player();
174       Army *army = (*i)[combatant_columns.army];
175       Army *new_army = new Army(*army, player);
176       new_army->assignNewId();
177       add_attacker_army(new_army, true);
178     }
179 
180   set_button_sensitivity();
181 }
182 
on_attacker_add_clicked()183 void BattleCalculatorDialog::on_attacker_add_clicked()
184 {
185   SelectArmyDialog d(*dialog, SelectArmyDialog::SELECT_NORMAL_WITH_HERO,
186                      get_attacker_player(), -1);
187   d.run();
188 
189   Player *player = get_attacker_player();
190   const ArmyProto *army = d.get_selected_army();
191   if (army)
192     {
193       if (army->isHero() == true)
194         {
195           HeroProto *hp = new HeroProto(*army);
196           hp->setOwnerId(player->getId());
197           add_attacker_army(new Hero (*hp), true);
198           delete hp;
199         }
200       else
201         add_attacker_army(new Army(*army, player), true);
202     }
203 }
204 
205 
on_attacker_edit_hero_clicked()206 void BattleCalculatorDialog::on_attacker_edit_hero_clicked()
207 {
208   Gtk::TreeIter i = attackers_treeview->get_selection()->get_selected();
209   if (i)
210     {
211       Army *army = (*i)[combatant_columns.army];
212       Hero *hero = dynamic_cast<Hero*>(army);
213       HeroEditorDialog d(*dialog, hero);
214       d.run();
215     }
216 }
217 
on_attacker_remove_clicked()218 void BattleCalculatorDialog::on_attacker_remove_clicked()
219 {
220   Gtk::TreeIter i = attackers_treeview->get_selection()->get_selected();
221   if (i)
222     {
223       Army *army = (*i)[combatant_columns.army];
224       d_attackers.erase(std::remove (d_attackers.begin(), d_attackers.end(), army), d_attackers.end());
225       delete army;
226       attackers_list->erase(i);
227 
228     }
229 
230   set_button_sensitivity();
231 }
232 
add_attacker_army(Army * a,bool add)233 void BattleCalculatorDialog::add_attacker_army(Army *a, bool add)
234 {
235   if (add)
236     d_attackers.push_back(a);
237   ImageCache *gc = ImageCache::getInstance();
238   Gtk::TreeIter i = attackers_list->append();
239   (*i)[combatant_columns.army] = a;
240   guint32 fs = FontSize::getInstance ()->get_height ();
241   (*i)[combatant_columns.image] = gc->getDialogArmyPic (a, fs)->to_pixbuf ();
242   (*i)[combatant_columns.strength] = a->getStat(Army::STRENGTH, false);
243   (*i)[combatant_columns.hp] = a->getStat(Army::HP, false);
244 
245   attackers_treeview->get_selection()->select(i);
246 
247   set_button_sensitivity();
248 }
249 
on_attacker_selection_changed()250 void BattleCalculatorDialog::on_attacker_selection_changed()
251 {
252   set_button_sensitivity();
253 }
254 
on_attacker_player_changed()255 void BattleCalculatorDialog::on_attacker_player_changed()
256 {
257   ImageCache *gc = ImageCache::getInstance();
258   Player *player = get_attacker_player();
259   set_button_sensitivity();
260 
261   guint32 fs = FontSize::getInstance ()->get_height ();
262   for (Gtk::TreeIter j = attackers_list->children().begin(),
263        jend = attackers_list->children().end(); j != jend; ++j)
264     {
265       Army *a = (*j)[combatant_columns.army];
266       a->setOwner (player);
267       (*j)[combatant_columns.image] =
268         gc->getDialogArmyPic(a, fs)->to_pixbuf ();
269     }
270 }
271 
cell_data_attacker_strength(Gtk::CellRenderer * renderer,const Gtk::TreeIter & i)272 void BattleCalculatorDialog::cell_data_attacker_strength(Gtk::CellRenderer *renderer, const Gtk::TreeIter& i)
273 {
274     dynamic_cast<Gtk::CellRendererSpin*>(renderer)->property_adjustment()
275           = Gtk::Adjustment::create((*i)[combatant_columns.strength],
276                                     MIN_STRENGTH_FOR_ARMY_UNITS,
277                                     MAX_STRENGTH_FOR_ARMY_UNITS, 1);
278     dynamic_cast<Gtk::CellRendererSpin*>(renderer)->property_text() =
279       String::ucompose("%1", (*i)[combatant_columns.strength]);
280 }
281 
on_attacker_strength_edited(const Glib::ustring & path,const Glib::ustring & new_text)282 void BattleCalculatorDialog::on_attacker_strength_edited(const Glib::ustring &path, const Glib::ustring &new_text)
283 {
284   int str = atoi(new_text.c_str());
285   if (str < (int)MIN_STRENGTH_FOR_ARMY_UNITS || str >
286       (int)MAX_STRENGTH_FOR_ARMY_UNITS)
287     return;
288   (*attackers_list->get_iter(Gtk::TreePath(path)))[combatant_columns.strength] = str;
289 }
290 
set_button_sensitivity()291 void BattleCalculatorDialog::set_button_sensitivity()
292 {
293   int att = 0, def = 0;
294     {
295       int max_number_of_attackers = 8;
296       Gtk::TreeIter i = attackers_treeview->get_selection()->get_selected();
297       att = attackers_list->children().size();
298       attacker_add_button->set_sensitive(att < max_number_of_attackers);
299       attacker_copy_button->set_sensitive(i && att < max_number_of_attackers);
300       attacker_remove_button->set_sensitive(i);
301       if (i)
302         {
303           Army *army = (*i)[combatant_columns.army];
304           if (army->isHero())
305             {
306               attacker_edit_hero_button->set_sensitive(true);
307               attacker_copy_button->set_sensitive(false);
308             }
309           else
310             attacker_edit_hero_button->set_sensitive(false);
311         }
312       else
313         attacker_edit_hero_button->set_sensitive(false);
314     }
315     {
316       int max_number_of_defenders = 32;
317       Gtk::TreeIter i = defenders_treeview->get_selection()->get_selected();
318       def = defenders_list->children().size();
319       defender_add_button->set_sensitive(def < max_number_of_defenders);
320       defender_copy_button->set_sensitive(i && def < max_number_of_defenders);
321       defender_remove_button->set_sensitive(i);
322       if (i)
323         {
324           Army *army = (*i)[combatant_columns.army];
325           if (army->isHero())
326             {
327               defender_edit_hero_button->set_sensitive(true);
328               defender_copy_button->set_sensitive(false);
329             }
330           else
331             defender_edit_hero_button->set_sensitive(false);
332         }
333       else
334         defender_edit_hero_button->set_sensitive(false);
335     }
336   fight_button->set_sensitive (att && def);
337   fight100_button->set_sensitive (att && def);
338 }
339 
get_defender_player()340 Player *BattleCalculatorDialog::get_defender_player()
341 {
342   int c = 0, row = defender_player_combobox->get_active_row_number();
343   Player *player = Playerlist::getInstance()->getNeutral();
344   for (Playerlist::iterator i = Playerlist::getInstance()->begin(),
345        end = Playerlist::getInstance()->end(); i != end; ++i, ++c)
346     if (c == row)
347       {
348 	player = *i;
349 	break;
350       }
351   return player;
352 }
353 
on_defender_copy_clicked()354 void BattleCalculatorDialog::on_defender_copy_clicked()
355 {
356   Gtk::TreeIter i = defenders_treeview->get_selection()->get_selected();
357   if (i)
358     {
359       Player *player = get_defender_player();
360       Army *army = (*i)[combatant_columns.army];
361       Army *new_army = new Army(*army, player);
362       new_army->assignNewId();
363       add_defender_army(new_army, true);
364     }
365 
366   set_button_sensitivity();
367 }
368 
on_defender_add_clicked()369 void BattleCalculatorDialog::on_defender_add_clicked()
370 {
371   SelectArmyDialog d(*dialog, SelectArmyDialog::SELECT_NORMAL_WITH_HERO,
372                      get_defender_player(), -1);
373   d.run();
374 
375   Player *player = get_defender_player();
376   const ArmyProto *army = d.get_selected_army();
377   if (army)
378     {
379       if (army->isHero() == true)
380         {
381           HeroProto *hp = new HeroProto(*army);
382           hp->setOwnerId(player->getId());
383           add_defender_army(new Hero(*hp), true);
384           delete hp;
385         }
386       else
387         add_defender_army(new Army(*army, player), true);
388     }
389 }
390 
on_defender_edit_hero_clicked()391 void BattleCalculatorDialog::on_defender_edit_hero_clicked()
392 {
393   Gtk::TreeIter i = defenders_treeview->get_selection()->get_selected();
394   if (i)
395     {
396       Army *army = (*i)[combatant_columns.army];
397       Hero *hero = dynamic_cast<Hero*>(army);
398       HeroEditorDialog d(*dialog, hero);
399       d.run();
400     }
401 }
402 
on_defender_remove_clicked()403 void BattleCalculatorDialog::on_defender_remove_clicked()
404 {
405   Gtk::TreeIter i = defenders_treeview->get_selection()->get_selected();
406   if (i)
407     {
408       Army *army = (*i)[combatant_columns.army];
409       d_defenders.erase(std::remove (d_defenders.begin(), d_defenders.end(), army), d_defenders.end());
410       delete army;
411       defenders_list->erase(i);
412     }
413 
414   set_button_sensitivity();
415 }
416 
add_defender_army(Army * a,bool add)417 void BattleCalculatorDialog::add_defender_army(Army *a, bool add)
418 {
419   if (add)
420     d_defenders.push_back(a);
421   ImageCache *gc = ImageCache::getInstance();
422   Gtk::TreeIter i = defenders_list->append();
423   (*i)[combatant_columns.army] = a;
424   guint32 fs = FontSize::getInstance ()->get_height ();
425   (*i)[combatant_columns.image] = gc->getDialogArmyPic (a, fs)->to_pixbuf ();
426   (*i)[combatant_columns.strength] = a->getStat(Army::STRENGTH, false);
427   //(*i)[combatant_columns.augmented_strength] =
428     //a->getStat(Army::STRENGTH, false);
429   (*i)[combatant_columns.hp] = a->getStat(Army::HP, false);
430 
431   defenders_treeview->get_selection()->select(i);
432 
433   set_button_sensitivity();
434 }
435 
on_defender_selection_changed()436 void BattleCalculatorDialog::on_defender_selection_changed()
437 {
438   set_button_sensitivity();
439 }
440 
on_defender_player_changed()441 void BattleCalculatorDialog::on_defender_player_changed()
442 {
443   ImageCache *gc = ImageCache::getInstance();
444   Player *player = get_defender_player();
445   set_button_sensitivity();
446 
447   guint32 fs = FontSize::getInstance ()->get_height ();
448   for (Gtk::TreeIter j = defenders_list->children().begin(),
449        jend = defenders_list->children().end(); j != jend; ++j)
450     {
451       Army *a = (*j)[combatant_columns.army];
452       a->setOwner (player);
453       (*j)[combatant_columns.image] =
454         gc->getDialogArmyPic(a, fs)->to_pixbuf ();
455     }
456 }
457 
cell_data_defender_strength(Gtk::CellRenderer * renderer,const Gtk::TreeIter & i)458 void BattleCalculatorDialog::cell_data_defender_strength(Gtk::CellRenderer *renderer, const Gtk::TreeIter& i)
459 {
460     dynamic_cast<Gtk::CellRendererSpin*>(renderer)->property_adjustment()
461           = Gtk::Adjustment::create((*i)[combatant_columns.strength],
462                                     MIN_STRENGTH_FOR_ARMY_UNITS,
463                                     MAX_STRENGTH_FOR_ARMY_UNITS, 1);
464     dynamic_cast<Gtk::CellRendererSpin*>(renderer)->property_text() =
465       String::ucompose("%1", (*i)[combatant_columns.strength]);
466 }
467 
on_defender_strength_edited(const Glib::ustring & path,const Glib::ustring & new_text)468 void BattleCalculatorDialog::on_defender_strength_edited(const Glib::ustring &path, const Glib::ustring &new_text)
469 {
470   int str = atoi(new_text.c_str());
471   if (str < (int)MIN_STRENGTH_FOR_ARMY_UNITS || str >
472       (int)MAX_STRENGTH_FOR_ARMY_UNITS)
473     return;
474   (*defenders_list->get_iter(Gtk::TreePath(path)))[combatant_columns.strength] = str;
475 }
476 
on_city_toggled()477 void BattleCalculatorDialog::on_city_toggled()
478 {
479   if (city_switch->property_active () == true)
480     terrain_combobox->set_active (0);
481   terrain_combobox->set_sensitive(!city_switch->property_active());
482 }
483 
run_battle()484 Fight::Result BattleCalculatorDialog::run_battle ()
485 {
486   //load the attackers into a single stack
487   std::list<Stack*> attackers;
488   Stack *stack = new Stack(get_attacker_player(), Vector<int>(-1,-1));
489   attackers.push_back (stack);
490   for (auto a: attackers_list->children())
491     {
492       Army *army = (*a)[combatant_columns.army];
493       stack->add (army);
494     }
495 
496   //load the defenders into a bunch of stacks
497   std::list<Stack*> defenders;
498   stack = new Stack(get_defender_player(), Vector<int>(-1,-1));
499   defenders.push_back (stack);
500   for (auto d: defenders_list->children())
501     {
502       if (stack->size() == 8)
503         {
504           stack = new Stack(get_defender_player(), Vector<int>(-1,-1));
505           defenders.push_back (stack);
506         }
507       Army *army = (*d)[combatant_columns.army];
508       stack->add (army);
509     }
510   Tileset *tileset = GameMap::getTileset();
511   int row = terrain_combobox->get_active_row_number();
512   bool water = (*tileset)[row]->getType() == Tile::WATER;
513   //put them all in the water if we're doing that.
514   for (auto s : attackers)
515     for (auto a : *s)
516       a->setInShip(water);
517   for (auto s : defenders)
518     for (auto a : *s)
519       a->setInShip(water);
520 
521   //fortify them if we're doing that
522   for (auto s : defenders)
523     for (auto a : *s)
524       a->setFortified(fortified_switch->get_active());
525   Fight f(attackers, defenders, city_switch->get_active(), Tile::Type(row));
526   for (auto a: attackers_list->children())
527     {
528       Army *army = (*a)[combatant_columns.army];
529       (*a)[combatant_columns.augmented_strength] =
530         f.getModifiedStrengthBonus(army);
531     }
532   for (auto d: defenders_list->children())
533     {
534       Army *army = (*d)[combatant_columns.army];
535       (*d)[combatant_columns.augmented_strength] =
536         f.getModifiedStrengthBonus(army);
537     }
538   f.battle(die_sides_combobox->get_active_row_number() == 1);
539   for (auto a: attackers_list->children())
540     {
541       Army *army = (*a)[combatant_columns.army];
542       (*a)[combatant_columns.hp] = army->getHP();
543     }
544   for (auto d: defenders_list->children())
545     {
546       Army *army = (*d)[combatant_columns.army];
547       (*d)[combatant_columns.hp] = army->getHP();
548     }
549   //reset the HP
550   std::map<guint32,guint32> initial_hitpoints = f.getInitialHPs();
551   for (auto a: attackers_list->children())
552     {
553       Army *army = (*a)[combatant_columns.army];
554       army->setHP(initial_hitpoints[army->getId()]);
555     }
556   for (auto d: defenders_list->children())
557     {
558       Army *army = (*d)[combatant_columns.army];
559       army->setHP(initial_hitpoints[army->getId()]);
560     }
561   //delete the stacks we made, but keep the armies
562   for (auto s: attackers)
563     {
564       s->clear();
565       delete s;
566     }
567   for (auto d: defenders)
568     {
569       d->clear();
570       delete d;
571     }
572   return f.getResult();
573 }
574 
on_fight_clicked()575 void BattleCalculatorDialog::on_fight_clicked()
576 {
577   run_battle ();
578 }
579 
on_fight100_clicked()580 void BattleCalculatorDialog::on_fight100_clicked()
581 {
582   int attacker_wins = 0;
583   int defender_wins = 0;
584   for (int i = 0; i < 100; i++)
585     {
586       switch (run_battle())
587         {
588         case Fight::ATTACKER_WON:
589           attacker_wins++;
590           break;
591         case Fight::DEFENDER_WON:
592           defender_wins++;
593           break;
594         case Fight::DRAW:
595           break;
596         }
597     }
598 
599   //show results
600     {
601       Gtk::Dialog *d = new Gtk::Dialog();
602       d->property_transient_for() = dialog;
603       d->add_button(Gtk::Stock::CLOSE, Gtk::RESPONSE_ACCEPT);
604       Gtk::Box *box = d->get_content_area ();
605       d->set_title (_("Battle Outcome"));
606       Glib::ustring s =
607         String::ucompose(ngettext("The attacker won %1 battle and lost %2.",
608                                   "The attacker won %1 battles and lost %2.",
609                                   attacker_wins),
610                          attacker_wins, defender_wins);
611       Gtk::Label l;
612       l.set_text (s);
613       l.set_margin_left (10);
614       l.set_margin_right (10);
615       l.set_margin_top (10);
616       l.set_margin_bottom (10);
617       box->add(l);
618       box->show_all();
619       d->run();
620       delete d;
621     }
622 }
623