1 // Copyright (C) 2007, 2008 Ole Laursen
2 // Copyright (C) 2007, 2008, 2009 Ben Asselstine
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.
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.
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
28 #include "fight-window.h"
30 #include "glade-helpers.h"
31 #include "image-helpers.h"
32 #include "ucompose.hpp"
37 #include "playerlist.h"
40 #include "GraphicsCache.h"
41 #include "Configuration.h"
44 FightWindow::FightWindow(Fight &fight)
46 Glib::RefPtr<Gtk::Builder> xml
47 = Gtk::Builder::create_from_file(get_glade_path() + "/fight-window.ui");
49 xml->get_widget("window", window);
51 window->signal_key_release_event().connect_notify(sigc::mem_fun(*this, &FightWindow::on_key_release_event));
53 Gtk::VBox *attacker_close_vbox;
54 Gtk::VBox *defender_close_vbox;
55 xml->get_widget("attacker_close_vbox", attacker_close_vbox);
56 xml->get_widget("defender_close_vbox", defender_close_vbox);
58 // extract attackers and defenders
59 armies_type attackers, defenders;
61 Fight::orderArmies (fight.getAttackers(), attackers);
62 Fight::orderArmies (fight.getDefenders(), defenders);
64 int rows = compute_max_rows(attackers, defenders);
67 std::vector<Gtk::HBox *> close_hboxes;
69 std::map<guint32, guint32> initial_hps = fight.getInitialHPs();
73 for (armies_type::iterator i = attackers.begin(); i != attackers.end(); ++i)
75 add_army(*i, initial_hps[(*i)->getId()],
76 close_hboxes, attacker_close_vbox, close++, rows,
84 for (armies_type::iterator i = defenders.begin(); i != defenders.end(); ++i)
86 add_army(*i, initial_hps[(*i)->getId()],
87 close_hboxes, defender_close_vbox, close++, rows,
91 // fill in shield pictures
92 GraphicsCache *gc = GraphicsCache::getInstance();
95 Gtk::Image *defender_shield_image;
96 p = defenders.front()->getOwner();
97 xml->get_widget("defender_shield_image", defender_shield_image);
98 defender_shield_image->property_pixbuf()=gc->getShieldPic(2, p)->to_pixbuf();
100 Gtk::Image *attacker_shield_image;
101 p = attackers.front()->getOwner();
102 xml->get_widget("attacker_shield_image", attacker_shield_image);
103 attacker_shield_image->property_pixbuf()=gc->getShieldPic(2, p)->to_pixbuf();
105 actions = fight.getCourseOfEvents();
109 Configuration::s_displayFightRoundDelayFast; //milliseconds
111 Configuration::s_displayFightRoundDelaySlow; //milliseconds
112 Sound::getInstance()->disableBackground(true);
113 Sound::getInstance()->playMusic("battle", -1, true);
116 FightWindow::~FightWindow()
118 Sound::getInstance()->haltMusic(true);
119 Sound::getInstance()->enableBackground();
123 void FightWindow::set_parent_window(Gtk::Window &parent)
125 window->set_transient_for(parent);
126 window->set_position(Gtk::WIN_POS_CENTER_ON_PARENT);
129 void FightWindow::hide()
134 void FightWindow::run(bool *quick)
138 action_iterator = actions.begin();
140 Timing::instance().register_timer(
141 sigc::mem_fun(this, &FightWindow::do_round),
142 *quick == true ? fast_round_speed : normal_round_speed);
145 main_loop = Glib::MainLoop::create();
147 if (quick && *quick == false)
151 int FightWindow::compute_max_rows(const armies_type &attackers,
152 const armies_type &defenders)
154 assert(!attackers.empty() || !defenders.empty());
156 if (attackers.size() > defenders.size())
157 return (attackers.size() / max_cols) +
158 (attackers.size() % max_cols == 0 ? 0 : 1);
160 return (defenders.size() / max_cols) +
161 (defenders.size() % max_cols == 0 ? 0 : 1);
162 // Find out how to distribute the close range and long range attackers and
163 // defenders, assuming that close range units are put in front.
165 std::vector<int> counts(2, 0);
167 // count the number of melee/ranged units
168 for (armies_type::const_iterator i = attackers.begin(), end = attackers.end();
174 for (armies_type::const_iterator i = defenders.begin(), end = defenders.end();
180 // now find the max number of rows
181 std::vector<int> heights(counts.begin(), counts.end());
182 std::vector<int> widths(2, 0);
183 for (int i = 0; i < 2; ++i)
187 double const wanted_ratio = 4.0 / 3;
188 double old_dist = 10000;
189 int old_height = *std::max_element(heights.begin(), heights.end());
191 int width = std::accumulate(widths.begin(), widths.end(), 0);
192 int height = *std::max_element(heights.begin(), heights.end());
194 double ratio = double(width) / height;
196 std::cerr << "WIDTH " << width
197 << " HEIGHT " << height
198 << " RATIO " << ratio << std::endl;
201 double dist = std::abs(wanted_ratio - ratio);
202 if (dist >= old_dist)
203 break; // we passed the optimal point
209 // compute new heights and widths
210 int max_height = height - 1;
214 for (int i = 0; i < 2; ++i)
215 if (heights[i] > max_height)
217 heights[i] = max_height;
218 // round the division up
219 widths[i] = (counts[i] + max_height - 1) / max_height;
225 std::cerr << "used height " << old_height << std::endl;
227 // use old because the algorithm goes one step too far before it stops
231 void FightWindow::add_army(Army *army, int initial_hp,
232 std::vector<Gtk::HBox *> &hboxes,
234 int current_no, int max_rows,
235 Gtk::AlignmentEnum alignment)
237 GraphicsCache *gc = GraphicsCache::getInstance();
238 // construct the army box
239 Gtk::VBox *army_box = manage(new Gtk::VBox);
242 Glib::RefPtr<Gdk::Pixbuf> pic = gc->getArmyPic(army)->to_pixbuf();
243 Gtk::Image *image = new Gtk::Image();
244 image->property_pixbuf() = pic;
245 army_box->add(*manage(image));
248 Gtk::ProgressBar *progress = manage(new Gtk::ProgressBar);
249 progress->set_fraction(double(initial_hp) / army->getStat(Army::HP));
250 progress->property_width_request() = pic->get_width();
251 progress->property_height_request() = 12;
252 army_box->pack_start(*progress, Gtk::PACK_SHRINK, 4);
254 // then add it to the right hbox
255 int current_row = (current_no / max_cols);
256 if (current_row >= int(hboxes.size()))
259 Gtk::HBox *hbox = manage(new Gtk::HBox);
260 hbox->set_spacing(6);
261 hboxes.push_back(hbox);
263 Gtk::Alignment *a = manage(new Gtk::Alignment(alignment));
264 a->property_xscale() = 0;
266 vbox->pack_start(*a, Gtk::PACK_SHRINK);
269 Gtk::HBox *hbox = hboxes[current_row];
270 hbox->pack_start(*army_box, Gtk::PACK_SHRINK);
272 // finally add an entry for later use
275 item.hp = initial_hp;
278 item.exploding = false;
279 army_items.push_back(item);
282 bool FightWindow::do_round()
284 GraphicsCache *gc = GraphicsCache::getInstance();
285 Glib::RefPtr<Gdk::Pixbuf> expl = gc->getExplosionPic()->to_pixbuf();
287 // first we clear out any explosions
288 for (army_items_type::iterator i = army_items.begin(),
289 end = army_items.end(); i != end; ++i)
294 Glib::RefPtr<Gdk::Pixbuf> empty_pic
295 = Gdk::Pixbuf::create(Gdk::COLORSPACE_RGB, true, 8, expl->get_width(), expl->get_height());
296 empty_pic->fill(0x00000000);
297 i->image->property_pixbuf() = empty_pic;
298 i->exploding = false;
299 return Timing::CONTINUE;
302 while (action_iterator != actions.end())
304 FightItem &f = *action_iterator;
308 for (army_items_type::iterator i = army_items.begin(),
309 end = army_items.end(); i != end; ++i)
310 if (i->army->getId() == f.id)
315 double fraction = double(i->hp) / i->army->getStat(Army::HP);
316 i->bar->set_fraction(fraction);
320 i->image->property_pixbuf() = expl;
331 return Timing::CONTINUE;
340 void FightWindow::on_key_release_event(GdkEventKey* event)
342 Timing::instance().register_timer(
343 sigc::mem_fun(this, &FightWindow::do_round), fast_round_speed);