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