1 //  Copyright (C) 2007 Ole Laursen
2 //  Copyright (C) 2007, 2008, 2009, 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 <sigc++/functors/mem_fun.h>
22 #include <gtkmm.h>
23 
24 #include "city-editor-dialog.h"
25 
26 #include "ucompose.hpp"
27 #include "defs.h"
28 #include "city.h"
29 #include "armyprodbase.h"
30 #include "army.h"
31 #include "armyproto.h"
32 #include "playerlist.h"
33 #include "stacklist.h"
34 #include "citylist.h"
35 #include "CreateScenarioRandomize.h"
36 #include "ImageCache.h"
37 #include "GameMap.h"
38 #include "font-size.h"
39 #include "select-army-dialog.h"
40 
41 #define method(x) sigc::mem_fun(*this, &CityEditorDialog::x)
42 
CityEditorDialog(Gtk::Window & parent,City * cit,CreateScenarioRandomize * randomizer)43 CityEditorDialog::CityEditorDialog(Gtk::Window &parent, City *cit, CreateScenarioRandomize *randomizer)
44  : LwEditorDialog(parent, "city-editor-dialog.ui"),
45     strength_column(_("Strength"), strength_renderer),
46     moves_column(_("Max Moves"), moves_renderer),
47     duration_column(_("Turns"), duration_renderer),
48     upkeep_column(_("Upkeep"), upkeep_renderer)
49 {
50   city = cit;
51   d_randomizer = randomizer;
52 
53   xml->get_widget("capital_switch", capital_switch);
54   capital_switch->set_active(city->isCapital());
55   capital_switch->property_active ().signal_changed ().connect (method (on_capital_changed));
56 
57   xml->get_widget("name_entry", name_entry);
58   name_entry->set_text(city->getName());
59   name_entry->signal_changed ().connect (method (on_name_changed));
60 
61   xml->get_widget("income_spinbutton", income_spinbutton);
62   income_spinbutton->set_value(city->getGold());
63   income_spinbutton->signal_changed().connect (method (on_income_changed));
64 
65   xml->get_widget("burned_switch", burned_switch);
66   burned_switch->set_active(city->isBurnt());
67   burned_switch->property_active ().signal_changed ().connect (method (on_burned_changed));
68 
69   xml->get_widget("build_production_switch", build_production_switch);
70   build_production_switch->set_active(city->getBuildProduction());
71   build_production_switch->property_active ().signal_changed ().connect (method (on_build_production_changed));
72 
73   // setup the player combo
74   player_combobox = manage(new Gtk::ComboBoxText);
75 
76   int c = 0, player_no = 0;
77   for (Playerlist::iterator i = Playerlist::getInstance()->begin(),
78        end = Playerlist::getInstance()->end(); i != end; ++i, ++c)
79     {
80       Player *player = *i;
81       player_combobox->append(player->getName());
82       if (player == city->getOwner())
83         player_no = c;
84     }
85 
86   player_combobox->set_active(player_no);
87   player_combobox->signal_changed().connect (method (on_player_changed));
88   Gtk::Alignment *alignment;
89   xml->get_widget("player_alignment", alignment);
90   alignment->add(*player_combobox);
91 
92 
93   // setup the army list
94   army_list = Gtk::ListStore::create(army_columns);
95 
96   xml->get_widget("army_treeview", army_treeview);
97   army_treeview->set_model(army_list);
98 
99   army_treeview->append_column("", army_columns.image);
100   strength_renderer.property_editable() = true;
101   strength_renderer.signal_edited().connect(method (on_strength_edited));
102   strength_column.set_cell_data_func
103     (strength_renderer, method (cell_data_strength));
104   army_treeview->append_column(strength_column);
105 
106   moves_renderer.property_editable() = true;
107   moves_renderer.signal_edited().connect(method (on_moves_edited));
108   moves_column.set_cell_data_func (moves_renderer, method (cell_data_moves));
109   army_treeview->append_column(moves_column);
110 
111   upkeep_renderer.property_editable() = true;
112   upkeep_renderer.signal_edited().connect(method (on_upkeep_edited));
113   upkeep_column.set_cell_data_func (upkeep_renderer, method (cell_data_upkeep));
114   army_treeview->append_column(upkeep_column);
115 
116   duration_renderer.property_editable() = true;
117   duration_renderer.signal_edited().connect(method (on_turns_edited));
118   duration_column.set_cell_data_func
119     (duration_renderer, method (CityEditorDialog::cell_data_turns));
120 
121   army_treeview->append_column(_("Name"), army_columns.name);
122 
123   xml->get_widget("add_button", add_button);
124   xml->get_widget("remove_button", remove_button);
125   xml->get_widget("randomize_armies_button", randomize_armies_button);
126   xml->get_widget("randomize_name_button", randomize_name_button);
127   xml->get_widget("randomize_income_button", randomize_income_button);
128 
129   add_button->signal_clicked().connect (method (on_add_clicked));
130   remove_button->signal_clicked().connect (method (on_remove_clicked));
131   randomize_armies_button->signal_clicked().connect
132     (method (on_randomize_armies_clicked));
133   randomize_name_button->signal_clicked().connect
134     (method (on_randomize_name_clicked));
135   randomize_income_button->signal_clicked().connect
136     (method (on_randomize_income_clicked));
137 
138   army_treeview->get_selection()->signal_changed().connect
139     (method (on_selection_changed));
140 
141   for (unsigned int i = 0; i < city->getMaxNoOfProductionBases(); i++)
142     {
143       const ArmyProdBase* a = city->getProductionBase(i);
144       if (a)
145         add_army(a);
146     }
147 
148   Player *player = get_selected_player ();
149   bool neutral = player == Playerlist::getInstance ()->getNeutral ();
150   if (city->isBurnt () && neutral)
151     burned_switch->set_active (false);
152   burned_switch->set_sensitive (!neutral);
153 
154   update_buttons();
155 }
156 
~CityEditorDialog()157 CityEditorDialog::~CityEditorDialog ()
158 {
159   for (Gtk::TreeIter i = army_list->children().begin(),
160        end = army_list->children().end(); i != end; ++i)
161     {
162       const ArmyProdBase *a = (*i)[army_columns.army];
163       delete a;
164     }
165 }
166 
change_city_ownership()167 void CityEditorDialog::change_city_ownership()
168 {
169   // set allegiance
170   Player *player = get_selected_player();
171   if (player == city->getOwner()) //no change? do nothing.
172     return;
173   city->setOwner(player);
174   //look for stacks in the city, and set them to this player
175   for (unsigned int x = 0; x < city->getSize(); x++)
176     {
177       for (unsigned int y = 0; y < city->getSize(); y++)
178 	{
179 	  Stack *s = GameMap::getStack(city->getPos() + Vector<int>(x,y));
180 	  if (s)
181 	    Stacklist::changeOwnership(s, player);
182 	}
183     }
184 }
185 
run()186 int CityEditorDialog::run()
187 {
188   dialog->show_all();
189   return dialog->run();
190 }
191 
on_add_clicked()192 void CityEditorDialog::on_add_clicked()
193 {
194   SelectArmyDialog d(*dialog, SelectArmyDialog::SELECT_NORMAL,
195                      city->getOwner(), -1);
196   d.run();
197 
198   const ArmyProto *army = d.get_selected_army();
199   if (army)
200     add_army(new ArmyProdBase(*army));
201   update_armies ();
202 }
203 
on_remove_clicked()204 void CityEditorDialog::on_remove_clicked()
205 {
206   Gtk::TreeIter i = army_treeview->get_selection()->get_selected();
207   if (i)
208     {
209       const ArmyProdBase *a = (*i)[army_columns.army];
210       delete a;
211       (*i)[army_columns.army] = NULL;
212       army_list->erase(i);
213     }
214 
215   update_armies ();
216   set_button_sensitivity();
217 }
218 
on_randomize_armies_clicked()219 void CityEditorDialog::on_randomize_armies_clicked()
220 {
221   const ArmyProdBase *army;
222   army_list->clear();
223   city->setRandomArmytypes(true, 1);
224   for (unsigned int i = 0; i < city->getMaxNoOfProductionBases(); i++)
225     {
226       army = city->getProductionBase(i);
227       if (army)
228 	add_army(army);
229     }
230   set_button_sensitivity();
231 }
232 
on_randomize_name_clicked()233 void CityEditorDialog::on_randomize_name_clicked()
234 {
235   Glib::ustring existing_name = name_entry->get_text();
236   if (existing_name == City::getDefaultName())
237     name_entry->set_text(d_randomizer->popRandomCityName());
238   else
239     {
240       name_entry->set_text(d_randomizer->popRandomCityName());
241       d_randomizer->pushRandomCityName(existing_name);
242     }
243 }
244 
on_randomize_income_clicked()245 void CityEditorDialog::on_randomize_income_clicked()
246 {
247   int gold = d_randomizer->getRandomCityIncome(capital_switch->get_active());
248   income_spinbutton->set_value(gold);
249 }
250 
add_army(const ArmyProdBase * a)251 void CityEditorDialog::add_army(const ArmyProdBase *a)
252 {
253   Player *player = get_selected_player();
254   ImageCache *gc = ImageCache::getInstance();
255   Gtk::TreeIter i = army_list->append();
256   const ArmyProdBase *aa = new ArmyProdBase (*a);
257   (*i)[army_columns.army] = aa;
258   guint32 fs = FontSize::getInstance ()->get_height ();
259   (*i)[army_columns.image] = gc->getArmyPic(player->getArmyset(),
260 					    aa->getTypeId(), player,
261 					    NULL, false, fs)->to_pixbuf();
262   (*i)[army_columns.strength] = aa->getStrength();
263   (*i)[army_columns.moves] = aa->getMaxMoves();
264   (*i)[army_columns.upkeep] = a->getUpkeep();
265   (*i)[army_columns.duration] = aa->getProduction();
266   (*i)[army_columns.name] = aa->getName();
267   army_treeview->get_selection()->select(i);
268 
269   set_button_sensitivity();
270 }
271 
update_armies()272 void CityEditorDialog::update_armies ()
273 {
274   guint32 c = 0;
275   for (; c < city->getMaxNoOfProductionBases(); ++c)
276     city->removeProductionBase(c);
277   c = 0;
278   for (Gtk::TreeIter i = army_list->children().begin(),
279        end = army_list->children().end(); i != end; ++i, ++c)
280     {
281       const ArmyProdBase *a = (*i)[army_columns.army];
282       ArmyProdBase *army = new ArmyProdBase(*a);
283       army->setStrength((*i)[army_columns.strength]);
284       army->setProduction((*i)[army_columns.duration]);
285       army->setMaxMoves((*i)[army_columns.moves]);
286       army->setUpkeep ((*i)[army_columns.upkeep]);
287       city->addProductionBase(c, army);
288     }
289 }
290 
on_selection_changed()291 void CityEditorDialog::on_selection_changed()
292 {
293   set_button_sensitivity();
294 }
295 
set_button_sensitivity()296 void CityEditorDialog::set_button_sensitivity()
297 {
298   Gtk::TreeIter i = army_treeview->get_selection()->get_selected();
299   unsigned int armies = army_list->children().size();
300   add_button->set_sensitive(armies < city->getMaxNoOfProductionBases());
301   remove_button->set_sensitive(i);
302 }
303 
cell_data_strength(Gtk::CellRenderer * renderer,const Gtk::TreeIter & i)304 void CityEditorDialog::cell_data_strength(Gtk::CellRenderer *renderer,
305 				     const Gtk::TreeIter& i)
306 {
307   dynamic_cast<Gtk::CellRendererSpin*>(renderer)->property_adjustment()
308     = Gtk::Adjustment::create((*i)[army_columns.strength],
309                               MIN_STRENGTH_FOR_ARMY_UNITS,
310                               MAX_STRENGTH_FOR_ARMY_UNITS, 1);
311   dynamic_cast<Gtk::CellRendererSpin*>(renderer)->property_text() =
312     String::ucompose("%1", (*i)[army_columns.strength]);
313 }
314 
on_strength_edited(const Glib::ustring & path,const Glib::ustring & new_text)315 void CityEditorDialog::on_strength_edited(const Glib::ustring &path,
316 				   const Glib::ustring &new_text)
317 {
318   int str = atoi(new_text.c_str());
319   if (str < (int)MIN_STRENGTH_FOR_ARMY_UNITS ||
320       str > (int)MAX_STRENGTH_FOR_ARMY_UNITS)
321     return;
322   (*army_list->get_iter(Gtk::TreePath(path)))[army_columns.strength] = str;
323   update_armies ();
324 }
325 
cell_data_moves(Gtk::CellRenderer * renderer,const Gtk::TreeIter & i)326 void CityEditorDialog::cell_data_moves(Gtk::CellRenderer *renderer,
327 				  const Gtk::TreeIter& i)
328 {
329   dynamic_cast<Gtk::CellRendererSpin*>(renderer)->property_adjustment()
330     = Gtk::Adjustment::create((*i)[army_columns.moves],
331                               MIN_MOVES_FOR_ARMY_UNITS,
332                               MAX_MOVES_FOR_ARMY_UNITS, 1);
333   dynamic_cast<Gtk::CellRendererSpin*>(renderer)->property_text() =
334     String::ucompose("%1", (*i)[army_columns.moves]);
335 }
336 
on_moves_edited(const Glib::ustring & path,const Glib::ustring & new_text)337 void CityEditorDialog::on_moves_edited(const Glib::ustring &path,
338 				   const Glib::ustring &new_text)
339 {
340   int moves = atoi(new_text.c_str());
341   if (moves < (int)MIN_MOVES_FOR_ARMY_UNITS ||
342       moves > (int)MAX_MOVES_FOR_ARMY_UNITS)
343     return;
344   (*army_list->get_iter(Gtk::TreePath(path)))[army_columns.moves] = moves;
345   update_armies ();
346 }
347 
cell_data_turns(Gtk::CellRenderer * renderer,const Gtk::TreeIter & i)348 void CityEditorDialog::cell_data_turns(Gtk::CellRenderer *renderer,
349 				   const Gtk::TreeIter& i)
350 {
351   dynamic_cast<Gtk::CellRendererSpin*>(renderer)->property_adjustment()
352     = Gtk::Adjustment::create((*i)[army_columns.duration],
353                               MIN_PRODUCTION_TURNS_FOR_ARMY_UNITS,
354                               MAX_PRODUCTION_TURNS_FOR_ARMY_UNITS, 1);
355   dynamic_cast<Gtk::CellRendererSpin*>(renderer)->property_text() =
356     String::ucompose("%1", (*i)[army_columns.duration]);
357 }
358 
on_turns_edited(const Glib::ustring & path,const Glib::ustring & new_text)359 void CityEditorDialog::on_turns_edited(const Glib::ustring &path,
360 				   const Glib::ustring &new_text)
361 {
362   int turns = atoi(new_text.c_str());
363   if (turns < (int)MIN_PRODUCTION_TURNS_FOR_ARMY_UNITS ||
364       turns > (int)MAX_PRODUCTION_TURNS_FOR_ARMY_UNITS)
365     return;
366   (*army_list->get_iter(Gtk::TreePath(path)))[army_columns.duration] = turns;
367   update_armies ();
368 }
369 
cell_data_upkeep(Gtk::CellRenderer * renderer,const Gtk::TreeIter & i)370 void CityEditorDialog::cell_data_upkeep(Gtk::CellRenderer *renderer,
371 				   const Gtk::TreeIter& i)
372 {
373   dynamic_cast<Gtk::CellRendererSpin*>(renderer)->property_adjustment()
374     = Gtk::Adjustment::create((*i)[army_columns.upkeep], 0, 20, 1);
375   dynamic_cast<Gtk::CellRendererSpin*>(renderer)->property_text() =
376     String::ucompose("%1", (*i)[army_columns.upkeep]);
377 }
378 
on_upkeep_edited(const Glib::ustring & path,const Glib::ustring & new_text)379 void CityEditorDialog::on_upkeep_edited(const Glib::ustring &path,
380 				   const Glib::ustring &new_text)
381 {
382   int upkeep = atoi(new_text.c_str());
383   if (upkeep < (int) MIN_UPKEEP_FOR_ARMY_UNITS ||
384       upkeep > (int) MAX_UPKEEP_FOR_ARMY_UNITS)
385     return;
386   (*army_list->get_iter(Gtk::TreePath(path)))[army_columns.upkeep] = upkeep;
387   update_armies ();
388 }
389 
get_selected_player()390 Player *CityEditorDialog::get_selected_player()
391 {
392   int c = 0, row = player_combobox->get_active_row_number();
393   Player *player = Playerlist::getInstance()->getNeutral();
394   for (Playerlist::iterator i = Playerlist::getInstance()->begin(),
395        end = Playerlist::getInstance()->end(); i != end; ++i, ++c)
396     if (c == row)
397       {
398 	player = *i;
399 	break;
400       }
401   return player;
402 }
403 
on_player_changed()404 void CityEditorDialog::on_player_changed()
405 {
406   ImageCache *gc = ImageCache::getInstance();
407   Player *player = get_selected_player();
408   guint32 fs = FontSize::getInstance ()->get_height ();
409   for (Gtk::TreeIter j = army_list->children().begin(),
410        jend = army_list->children().end(); j != jend; ++j)
411     {
412       const ArmyProdBase *a = (*j)[army_columns.army];
413       (*j)[army_columns.image] = gc->getArmyPic(player->getArmyset(),
414 						a->getTypeId(),
415 						player, NULL, false,
416                                                 fs)->to_pixbuf();
417     }
418   if (capital_switch->get_active())
419     capital_switch->set_active(false);
420 
421   bool neutral = player == Playerlist::getInstance ()->getNeutral ();
422   if (city->isBurnt () && neutral)
423     burned_switch->set_active (false);
424   burned_switch->set_sensitive (!neutral);
425   change_city_ownership ();
426   update_buttons();
427 }
428 
update_buttons()429 void CityEditorDialog::update_buttons ()
430 {
431   Player *player = get_selected_player();
432   capital_switch->set_sensitive
433     (player != Playerlist::getInstance()->getNeutral());
434   bool burned = burned_switch->get_active ();
435   add_button->set_sensitive (!burned);
436   remove_button->set_sensitive (!burned);
437   randomize_armies_button->set_sensitive (!burned);
438 }
439 
on_burned_changed()440 void CityEditorDialog::on_burned_changed ()
441 {
442   city->setBurnt (burned_switch->get_active ());
443   if (city->isBurnt ())
444     {
445       guint32 c = 0;
446       for (; c < city->getMaxNoOfProductionBases(); ++c)
447         city->removeProductionBase(c);
448       army_list->clear ();
449     }
450   update_buttons ();
451 }
452 
on_capital_changed()453 void CityEditorDialog::on_capital_changed ()
454 {
455   Player *player = get_selected_player();
456   // make sure player doesn't have other capitals
457   Citylist* cl = Citylist::getInstance();
458   for (Citylist::iterator i = cl->begin(); i != cl->end(); ++i)
459     if ((*i)->isCapital() && (*i)->getOwner() == player)
460       {
461         (*i)->setCapital(false);
462         (*i)->setCapitalOwner(NULL);
463       }
464   if (capital_switch->get_active ())
465     {
466       city->setCapital(true);
467       city->setCapitalOwner(player);
468     }
469   else
470     {
471       city->setCapital(false);
472       city->setCapitalOwner(NULL);
473     }
474 }
475 
on_name_changed()476 void CityEditorDialog::on_name_changed ()
477 {
478   city->setName (String::utrim (name_entry->get_text ()));
479 }
480 
on_income_changed()481 void CityEditorDialog::on_income_changed ()
482 {
483   city->setGold(income_spinbutton->get_value_as_int());
484 }
485 
on_income_text_changed()486 void CityEditorDialog::on_income_text_changed ()
487 {
488   income_spinbutton->set_value(atoi(income_spinbutton->get_text().c_str()));
489   on_income_changed();
490 }
491 
on_build_production_changed()492 void CityEditorDialog::on_build_production_changed ()
493 {
494   city->setBuildProduction(build_production_switch->get_active ());
495 }
496