1 //  Copyright (C) 2007, 2008 Ole Laursen
2 //  Copyright (C) 2007, 2008, 2009, 2010, 2012, 2014, 2015, 2020 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 <numeric>
22 #include <vector>
23 #include <gtkmm.h>
24 
25 #include "fight-window.h"
26 #include "builder-cache.h"
27 
28 #include "timing.h"
29 #include "File.h"
30 #include "player.h"
31 #include "army.h"
32 #include "ImageCache.h"
33 #include "Configuration.h"
34 #include "snd.h"
35 #include "GameMap.h"
36 #include "tileset.h"
37 #include "tilesetlist.h"
38 #include "Tile.h"
39 #include "SmallTile.h"
40 #include "font-size.h"
41 
42 #define method(x) sigc::mem_fun(*this, &FightWindow::x)
43 
44 bool FightWindow::s_quick_all = false;
45 
FightWindow(Gtk::Window & parent,Fight & fight)46 FightWindow::FightWindow(Gtk::Window &parent, Fight &fight)
47 {
48   Glib::RefPtr<Gtk::Builder> xml = BuilderCache::get("fight-window.ui");
49 
50   xml->get_widget("window", window);
51   window->set_transient_for(parent);
52 
53   window->signal_key_release_event().connect_notify
54     (method(on_key_release_event));
55 
56   Gtk::Box *attacker_close_vbox;
57   Gtk::Box *defender_close_vbox;
58   xml->get_widget("attacker_close_vbox", attacker_close_vbox);
59   xml->get_widget("defender_close_vbox", defender_close_vbox);
60 
61   // extract attackers and defenders
62   armies_type attackers, defenders;
63 
64   Fight::orderArmies (fight.getAttackers(), attackers);
65   Fight::orderArmies (fight.getDefenders(), defenders);
66 
67   // add the armies
68   std::vector<Gtk::Box *> close_hboxes;
69   int close;
70   std::map<guint32, guint32> initial_hps = fight.getInitialHPs();
71 
72   // ... attackers
73   close = 0;
74   for (armies_type::iterator i = attackers.begin(); i != attackers.end(); ++i)
75     add_army(*i, initial_hps[(*i)->getId()], close_hboxes, attacker_close_vbox,
76              close++);
77 
78   close_hboxes.clear();
79 
80   // ... defenders
81   close = 0;
82   for (armies_type::iterator i = defenders.begin(); i != defenders.end(); ++i)
83     add_army(*i, initial_hps[(*i)->getId()], close_hboxes, defender_close_vbox,
84              close++);
85 
86   // fill in shield pictures
87   ImageCache *gc = ImageCache::getInstance();
88 
89   Gtk::Image *defender_shield_image;
90   Player *p = defenders.front()->getOwner();
91   xml->get_widget("defender_shield_image", defender_shield_image);
92   defender_shield_image->property_pixbuf()=
93     gc->getShieldPic(2, p, false,
94                      FontSize::getInstance ()->get_height ())->to_pixbuf();
95 
96   Gtk::Image *attacker_shield_image;
97   p = attackers.front()->getOwner();
98   xml->get_widget("attacker_shield_image", attacker_shield_image);
99   attacker_shield_image->property_pixbuf()=
100     gc->getShieldPic(2, p, false,
101                      FontSize::getInstance ()->get_height ())->to_pixbuf();
102 
103   actions = fight.getCourseOfEvents();
104   d_quick = false;
105 
106   fast_round_speed = Configuration::s_displayFightRoundDelayFast; //ms
107   normal_round_speed = Configuration::s_displayFightRoundDelaySlow; //ms
108   Snd::getInstance()->disableBackground();
109   Snd::getInstance()->play("battle", -1, true);
110 }
111 
~FightWindow()112 FightWindow::~FightWindow()
113 {
114   Snd::getInstance()->halt(true);
115   Snd::getInstance()->enableBackground();
116   delete window;
117 }
118 
run(bool * quick)119 void FightWindow::run(bool *quick)
120 {
121   round = 0;
122   action_iterator = actions.begin();
123 
124   if (s_quick_all)
125     Timing::instance().register_timer (method(do_round), fast_round_speed / 3);
126   else
127     Timing::instance().register_timer(method(do_round),
128                                       *quick == true ?
129                                       fast_round_speed : normal_round_speed);
130 
131   window->show_all();
132   main_loop = Glib::MainLoop::create();
133   main_loop->run();
134   if (quick && *quick == false)
135     *quick = d_quick;
136   if (s_quick_all)
137     d_quick = true;
138 }
139 
add_army(Army * army,int initial_hp,std::vector<Gtk::Box * > & hboxes,Gtk::Box * vbox,int current_no)140 void FightWindow::add_army(Army *army, int initial_hp,
141                            std::vector<Gtk::Box *> &hboxes, Gtk::Box *vbox,
142                            int current_no)
143 {
144   Gtk::Box *army_box;
145   Gtk::Image *army_image;
146   Gtk::Image *water_image;
147 
148   Glib::RefPtr<Gtk::Builder> xml = BuilderCache::get("fighter.ui");
149 
150   xml->get_widget("army_box", army_box);
151   xml->get_widget("army_image", army_image);
152   xml->get_widget("water_image", water_image);
153 
154   // image
155   guint32 fs = FontSize::getInstance()->get_height ();
156   PixMask *armypic = ImageCache::getInstance()->getDialogArmyPic(army, fs);
157   army_image->property_pixbuf() = armypic->to_pixbuf();
158   int height = 3;
159   SmallTile *water =
160     Tilesetlist::getInstance()->getSmallTile(GameMap::getTileset()->getBaseName(), Tile::WATER);
161   if (army->getStat(Army::SHIP, false) && water)
162     {
163       Cairo::RefPtr<Cairo::Surface> surf = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, armypic->get_width(), height);
164       Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(surf);
165       Gdk::RGBA w = water->getColor();
166       cr->set_source_rgb(w.get_red(), w.get_green(), w.get_blue());
167       cr->rectangle(0, 0, armypic->get_width(), height);
168       cr->paint();
169       Glib::RefPtr<Gdk::Pixbuf> p =
170         Gdk::Pixbuf::create(surf, 0, 0, armypic->get_width(), height);
171       water_image->property_pixbuf() = p;
172     }
173   else
174     {
175       Glib::RefPtr<Gdk::Pixbuf> empty =
176         Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, height, height);
177       empty->fill(0x00000000);
178       water_image->property_pixbuf() = empty;
179     }
180 
181   // then add it to the right hbox
182   int current_row = (current_no / max_cols);
183   if (current_row >= int(hboxes.size()))
184     {
185       // add an hbox
186       Gtk::Box *hbox = manage(new Gtk::Box (Gtk::ORIENTATION_HORIZONTAL));
187       hbox->set_spacing(6);
188       hboxes.push_back(hbox);
189 
190       Gtk::Alignment *a = manage(new Gtk::Alignment(Gtk::ALIGN_START));
191       a->add(*hbox);
192       vbox->pack_start(*a, Gtk::PACK_SHRINK);
193     }
194 
195   Gtk::Box *hbox = hboxes[current_row];
196   army_box->get_parent()->remove(*army_box);
197   Gtk::Box *box = new Gtk::Box(Gtk::ORIENTATION_VERTICAL );
198   box->pack_start(*army_box, Gtk::PACK_SHRINK);
199   army_box->reparent(*box);
200   hbox->pack_start(*Gtk::manage(box), Gtk::PACK_SHRINK);
201 
202   // finally add an entry for later use
203   ArmyItem item;
204   item.army = army;
205   item.hp = initial_hp;
206   item.water_image = water_image;
207   item.image = army_image;
208   item.exploding = false;
209   army_items.push_back(item);
210 }
211 
do_round()212 bool FightWindow::do_round()
213 {
214   ImageCache *gc = ImageCache::getInstance();
215   Glib::RefPtr<Gdk::Pixbuf> expl = gc->getExplosionPic()->to_pixbuf();
216 
217   // first we clear out any explosions
218   for (army_items_type::iterator i = army_items.begin(), end = army_items.end();
219        i != end; ++i)
220     {
221       if (!i->exploding)
222         continue;
223 
224       Glib::RefPtr<Gdk::Pixbuf> empty_pic
225         = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, expl->get_width(), expl->get_height());
226       empty_pic->fill(0x00000000);
227       i->image->property_pixbuf() = empty_pic;
228       i->exploding = false;
229       return Timing::CONTINUE;
230     }
231 
232   while (action_iterator != actions.end())
233     {
234       FightItem &f = *action_iterator;
235 
236       ++action_iterator;
237 
238       for (army_items_type::iterator i = army_items.begin(),
239            end = army_items.end(); i != end; ++i)
240         if (i->army->getId() == f.id)
241           {
242             i->hp -= f.damage;
243             if (i->hp < 0)
244               i->hp = 0;
245             double fraction = double(i->hp) / i->army->getStat(Army::HP);
246             if (fraction == 0.0)
247               {
248                 i->water_image->hide();
249                 i->image->property_pixbuf() = expl;
250                 i->exploding = true;
251               }
252 
253             break;
254           }
255 
256       if (f.turn > round)
257         {
258           ++round;
259 
260           return Timing::CONTINUE;
261         }
262     }
263 
264   window->hide();
265   main_loop->quit();
266 
267   return Timing::STOP;
268 }
269 
on_key_release_event(GdkEventKey * e)270 void FightWindow::on_key_release_event(GdkEventKey *e)
271 {
272   if (e->keyval == GDK_KEY_exclam)
273     {
274       if (s_quick_all)
275         {
276           Timing::instance().register_timer (method(do_round),
277                                              normal_round_speed);
278           d_quick = false;
279           s_quick_all = false;
280         }
281       else
282         {
283           d_quick = true;
284           s_quick_all = true;
285           Timing::instance().register_timer (method(do_round),
286                                              fast_round_speed / 3);
287         }
288     }
289   else
290     {
291       Timing::instance().register_timer (method(do_round), fast_round_speed);
292       d_quick = true;
293     }
294 }
295