1 //  Copyright (C) 2007 Ole Laursen
2 //  Copyright (C) 2007, 2008, 2009, 2014, 2015, 2017, 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 #include <algorithm>
24 #include <map>
25 #include <vector>
26 
27 #include "new-random-map-dialog.h"
28 
29 #include "defs.h"
30 #include "Configuration.h"
31 #include "File.h"
32 #include "tileset.h"
33 #include "tilesetlist.h"
34 #include "armysetlist.h"
35 #include "citysetlist.h"
36 #include "shieldsetlist.h"
37 #include "ucompose.hpp"
38 #include "GameScenarioOptions.h"
39 #include "CreateScenarioRandomize.h"
40 #include "CreateScenario.h"
41 #include "player.h"
42 #include "counter.h"
43 #include "rnd.h"
44 
45 #define method(x) sigc::mem_fun(*this, &NewRandomMapDialog::x)
46 
NewRandomMapDialog(Gtk::Window & parent)47 NewRandomMapDialog::NewRandomMapDialog(Gtk::Window &parent)
48         : LwDialog(parent, "new-random-map-dialog.ui")
49 {
50   xml->get_widget("dialog-vbox1", dialog_vbox);
51   xml->get_widget("dialog-action_area1", dialog_action_area);
52   xml->get_widget("map_size_combobox", map_size_combobox);
53   xml->get_widget("grass_scale", grass_scale);
54   ActiveTerrainType terrain = GRASS;
55   grass_scale->signal_value_changed().connect
56     (sigc::bind(sigc::mem_fun (this, &NewRandomMapDialog::on_value_changed), terrain));
57   xml->get_widget("water_scale", water_scale);
58   terrain = WATER;
59   water_scale->signal_value_changed().connect
60     (sigc::bind(sigc::mem_fun (this, &NewRandomMapDialog::on_value_changed), terrain));
61   xml->get_widget("swamp_scale", swamp_scale);
62   terrain = SWAMP;
63   swamp_scale->signal_value_changed().connect
64     (sigc::bind(sigc::mem_fun (this, &NewRandomMapDialog::on_value_changed), terrain));
65   xml->get_widget("forest_scale", forest_scale);
66   terrain = FOREST;
67   forest_scale->signal_value_changed().connect
68     (sigc::bind(sigc::mem_fun (this, &NewRandomMapDialog::on_value_changed), terrain));
69   xml->get_widget("hills_scale", hills_scale);
70   terrain = HILLS;
71   hills_scale->signal_value_changed().connect
72     (sigc::bind(sigc::mem_fun (this, &NewRandomMapDialog::on_value_changed), terrain));
73   xml->get_widget("mountains_scale", mountains_scale);
74   terrain = MOUNTAINS;
75   mountains_scale->signal_value_changed().connect
76     (sigc::bind(sigc::mem_fun (this, &NewRandomMapDialog::on_value_changed), terrain));
77   xml->get_widget("cities_scale", cities_scale);
78   xml->get_widget("accept2_button", accept_button);
79   accept_button->signal_clicked().connect (method(on_accept_clicked));
80   xml->get_widget("cancel2_button", cancel_button);
81   cancel_button->signal_clicked().connect (method(on_cancel_clicked));
82   xml->get_widget("grass_random_checkbutton", grass_random_checkbutton);
83   grass_random_checkbutton->signal_toggled().connect
84     (method(on_grass_random_toggled));
85   xml->get_widget("water_random_checkbutton", water_random_checkbutton);
86   water_random_checkbutton->signal_toggled().connect
87     (method(on_water_random_toggled));
88   xml->get_widget("swamp_random_checkbutton", swamp_random_checkbutton);
89   swamp_random_checkbutton->signal_toggled().connect
90     (method(on_swamp_random_toggled));
91   xml->get_widget("forest_random_checkbutton", forest_random_checkbutton);
92   forest_random_checkbutton->signal_toggled().connect
93     (method(on_forest_random_toggled));
94   xml->get_widget("hills_random_checkbutton", hills_random_checkbutton);
95   hills_random_checkbutton->signal_toggled().connect
96     (method(on_hills_random_toggled));
97   xml->get_widget("mountains_random_checkbutton", mountains_random_checkbutton);
98   mountains_random_checkbutton->signal_toggled().connect
99     (method(on_mountains_random_toggled));
100   xml->get_widget("cities_random_checkbutton", cities_random_checkbutton);
101   cities_random_checkbutton->signal_toggled().connect
102     (method(on_cities_random_toggled));
103   xml->get_widget("progress_treeview", progress_treeview);
104 
105   // fill in tile themes combobox
106 
107   guint32 counter = 0;
108   guint32 default_id = 0;
109   Gtk::Box *box;
110 
111   //fill in tile sizes combobox
112   tile_size_combobox = manage(new Gtk::ComboBoxText);
113   std::list<guint32> sizes;
114   Tilesetlist::getInstance()->getSizes(sizes);
115   Citysetlist::getInstance()->getSizes(sizes);
116   Armysetlist::getInstance()->getSizes(sizes);
117   for (std::list<guint32>::iterator it = sizes.begin(); it != sizes.end();
118        it++)
119     {
120       Glib::ustring s = String::ucompose("%1x%1", *it);
121       tile_size_combobox->append(s);
122       if ((*it) == Tileset::getDefaultTileSize())
123         default_id = counter;
124       counter++;
125     }
126   tile_size_combobox->set_active(default_id);
127   xml->get_widget("tile_size_box", box);
128   box->pack_start(*tile_size_combobox, Gtk::PACK_SHRINK);
129   tile_size_combobox->signal_changed().connect (method(on_tile_size_changed));
130 
131   // make new tile themes combobox
132   tile_theme_combobox = manage(new Gtk::ComboBoxText);
133   xml->get_widget("tile_theme_box", box);
134   box->pack_start(*tile_theme_combobox, Gtk::PACK_SHRINK);
135 
136   // make new army themes combobox
137   army_theme_combobox = manage(new Gtk::ComboBoxText);
138   xml->get_widget("army_theme_box", box);
139   box->pack_start(*army_theme_combobox, Gtk::PACK_SHRINK);
140 
141   // make new city themes combobox
142   city_theme_combobox = manage(new Gtk::ComboBoxText);
143   xml->get_widget("city_theme_box", box);
144   box->pack_start(*city_theme_combobox, Gtk::PACK_SHRINK);
145 
146   counter = 0;
147   default_id = 0;
148   shield_theme_combobox = manage(new Gtk::ComboBoxText);
149   Shieldsetlist *sl = Shieldsetlist::getInstance();
150   std::list<Glib::ustring> shield_themes = sl->getValidNames();
151   for (std::list<Glib::ustring>::iterator i = shield_themes.begin(),
152        end = shield_themes.end(); i != end; ++i)
153     {
154       if (*i == _("Default"))
155         default_id = counter;
156       shield_theme_combobox->append(Glib::filename_to_utf8(*i));
157       counter++;
158     }
159 
160   shield_theme_combobox->set_active(default_id);
161 
162   xml->get_widget("shield_theme_box", box);
163   box->pack_start(*shield_theme_combobox, Gtk::PACK_SHRINK);
164 
165   on_tile_size_changed();
166 
167   // map size
168   map_size_combobox->set_active(MAP_SIZE_NORMAL);
169   map_size_combobox->signal_changed().connect(method(on_map_size_changed));
170 
171   xml->get_widget("cities_can_produce_allies_checkbutton",
172                   cities_can_produce_allies_checkbutton);
173 
174   xml->get_widget("notebook", notebook);
175 
176   grass_scale->set_value(78);
177   water_scale->set_value(7);
178   swamp_scale->set_value(2);
179   forest_scale->set_value(3);
180   hills_scale->set_value(5);
181   mountains_scale->set_value(5);
182   on_map_size_changed();
183   dialog_response = Gtk::RESPONSE_CANCEL;
184   d_active_terrain = NONE;
185   d_inhibit_scales = false;
186 
187   //progressbar, what a pain
188   progress_treeview->property_headers_visible () = false;
189   progress_liststore = Gtk::ListStore::create(progress_columns);
190   progress_treeview->set_model (progress_liststore);
191   row = *(progress_liststore->append());
192   auto cell = Gtk::manage (new Gtk::CellRendererProgress());
193   cell->property_text () = "";
194   int cols_count = progress_treeview->append_column ("progress", *cell);
195   auto pColumn = progress_treeview->get_column(cols_count -1);
196   if (pColumn)
197     pColumn->add_attribute(cell->property_value (), progress_columns.perc);
198 
199 }
200 
run()201 int NewRandomMapDialog::run()
202 {
203   dialog->show_all();
204   dialog_action_area->hide();
205   progress_treeview->hide();
206   //we're not using the buttons from the action area.
207   //we have our own buttons so that we can show a progress bar after the
208   //button is clicked.
209   dialog->run();
210   return dialog_response;
211 }
212 
on_map_size_changed()213 void NewRandomMapDialog::on_map_size_changed()
214 {
215   switch (map_size_combobox->get_active_row_number()) {
216   case MAP_SIZE_SMALL:
217     map.width = MAP_SIZE_SMALL_WIDTH;
218     map.height = MAP_SIZE_SMALL_HEIGHT;
219     cities_scale->set_value(15);
220     break;
221 
222   case MAP_SIZE_TINY:
223     map.width = MAP_SIZE_TINY_WIDTH;
224     map.height = MAP_SIZE_TINY_HEIGHT;
225     cities_scale->set_value(10);
226     break;
227 
228   case MAP_SIZE_NORMAL:
229   default:
230     map.width = MAP_SIZE_NORMAL_WIDTH;
231     map.height = MAP_SIZE_NORMAL_HEIGHT;
232     cities_scale->set_value(20);
233     break;
234   }
235 }
236 
get_active_tile_size()237 guint32 NewRandomMapDialog::get_active_tile_size()
238 {
239   return (guint32) atoi(tile_size_combobox->get_active_text().c_str());
240 }
241 
on_tile_size_changed()242 void NewRandomMapDialog::on_tile_size_changed()
243 {
244   guint32 default_id = 0;
245   guint32 counter = 0;
246 
247   accept_button->set_sensitive(true);
248   tile_theme_combobox->remove_all();
249 
250   Tilesetlist *tl = Tilesetlist::getInstance();
251   std::list<Glib::ustring> tile_themes = tl->getValidNames(get_active_tile_size());
252   for (std::list<Glib::ustring>::iterator i = tile_themes.begin(),
253        end = tile_themes.end(); i != end; ++i)
254     {
255       if (*i == _("Default"))
256         default_id = counter;
257       tile_theme_combobox->append(Glib::filename_to_utf8(*i));
258       counter++;
259     }
260 
261   if (counter > 0)
262     tile_theme_combobox->set_active(default_id);
263   else
264     accept_button->set_sensitive(false);
265 
266 
267   army_theme_combobox->remove_all();
268 
269   Armysetlist *al = Armysetlist::getInstance();
270   std::list<Glib::ustring> army_themes = al->getValidNames(get_active_tile_size());
271   counter = 0;
272   default_id = 0;
273   for (std::list<Glib::ustring>::iterator i = army_themes.begin(),
274        end = army_themes.end(); i != end; ++i)
275     {
276       if (*i == _("Default"))
277         default_id = counter;
278       army_theme_combobox->append(Glib::filename_to_utf8(*i));
279       counter++;
280     }
281 
282   if (counter > 0)
283     army_theme_combobox->set_active(default_id);
284   else
285     accept_button->set_sensitive(false);
286 
287   city_theme_combobox->remove_all();
288 
289   Citysetlist *cl = Citysetlist::getInstance();
290   std::list<Glib::ustring> city_themes = cl->getValidNames(get_active_tile_size());
291   counter = 0;
292   default_id = 0;
293   for (std::list<Glib::ustring>::iterator i = city_themes.begin(),
294        end = city_themes.end(); i != end; ++i)
295     {
296       if (*i == _("Default"))
297         default_id = counter;
298       city_theme_combobox->append(Glib::filename_to_utf8(*i));
299       counter++;
300     }
301 
302   if (counter > 0)
303     city_theme_combobox->set_active(default_id);
304   else
305     accept_button->set_sensitive(false);
306 }
307 
assign_random_terrain(GameParameters & g)308 void NewRandomMapDialog::assign_random_terrain (GameParameters &g)
309 {
310   double sum = 0;
311   std::vector<ActiveTerrainType> ter;
312 
313   if (!grass_random_checkbutton->get_active())
314     sum += grass_scale->get_value ();
315   else
316     {
317       ter.push_back(GRASS);
318       g.map.grass = 0;
319     }
320 
321 
322   if (!water_random_checkbutton->get_active())
323     sum += water_scale->get_value ();
324   else
325     {
326       ter.push_back(WATER);
327       g.map.water = 0;
328     }
329 
330   if (!forest_random_checkbutton->get_active())
331     sum += forest_scale->get_value ();
332   else
333     {
334       ter.push_back(FOREST);
335       g.map.forest = 0;
336     }
337 
338   if (!hills_random_checkbutton->get_active())
339     sum += hills_scale->get_value ();
340   else
341     {
342       ter.push_back(HILLS);
343       g.map.hills = 0;
344     }
345 
346   if (!swamp_random_checkbutton->get_active())
347     sum += swamp_scale->get_value ();
348   else
349     {
350       ter.push_back(SWAMP);
351       g.map.swamp = 0;
352     }
353 
354   if (!mountains_random_checkbutton->get_active())
355     sum += mountains_scale->get_value ();
356   else
357     {
358       ter.push_back(MOUNTAINS);
359       g.map.mountains= 0;
360     }
361   double excess = 100 - sum;
362   if (excess <= 0)
363     return;
364   if (excess == 1)
365     {
366       g.map.grass++;
367       return;
368     }
369   if (ter.empty () == false)
370     {
371       for (int i = 0; i < int(excess); i++)
372         {
373           ActiveTerrainType type = ter[Rnd::rand() % ter.size()];
374           switch (type)
375             {
376             case GRASS:
377               g.map.grass++;
378               break;
379             case WATER:
380               g.map.water++;
381               break;
382             case FOREST:
383               g.map.forest++;
384               break;
385             case HILLS:
386               g.map.hills++;
387               break;
388             case SWAMP:
389               g.map.swamp++;
390               break;
391             case MOUNTAINS:
392               g.map.mountains++;
393               break;
394             default:
395               break;
396             }
397         }
398     }
399 }
400 
getParams()401 GameParameters NewRandomMapDialog::getParams()
402 {
403   CreateScenarioRandomize random;
404   GameParameters g;
405   GameParameters::Player p;
406   p.type = GameParameters::Player::HUMAN;
407   g.players.clear();
408   p.name = random.getPlayerName(Shield::WHITE);
409   p.id = int(Shield::WHITE);
410   g.players.push_back(p);
411   p.id = int(Shield::GREEN);
412   p.name = random.getPlayerName(Shield::Colour(p.id));
413   g.players.push_back(p);
414   p.id = int(Shield::YELLOW);
415   p.name = random.getPlayerName(Shield::Colour(p.id));
416   g.players.push_back(p);
417   p.id = int(Shield::DARK_BLUE);
418   p.name = random.getPlayerName(Shield::Colour(p.id));
419   g.players.push_back(p);
420   p.id = int(Shield::ORANGE);
421   p.name = random.getPlayerName(Shield::Colour(p.id));
422   g.players.push_back(p);
423   p.id = int(Shield::LIGHT_BLUE);
424   p.name = random.getPlayerName(Shield::Colour(p.id));
425   g.players.push_back(p);
426   p.id = int(Shield::RED);
427   p.name = random.getPlayerName(Shield::Colour(p.id));
428   g.players.push_back(p);
429   p.id = int(Shield::BLACK);
430   p.name = random.getPlayerName(Shield::Colour(p.id));
431   g.players.push_back(p);
432 
433   g.map_path = "";
434   switch (map_size_combobox->get_active_row_number()) {
435   case MAP_SIZE_SMALL:
436     g.map.width = MAP_SIZE_SMALL_WIDTH;
437     g.map.height = MAP_SIZE_SMALL_HEIGHT;
438     g.map.ruins = 20;
439     g.map.temples = 4;
440     break;
441 
442   case MAP_SIZE_TINY:
443     g.map.width = MAP_SIZE_TINY_WIDTH;
444     g.map.height = MAP_SIZE_TINY_HEIGHT;
445     g.map.ruins = 15;
446     g.map.temples = 4;
447     break;
448 
449   case MAP_SIZE_NORMAL:
450   default:
451     g.map.width = MAP_SIZE_NORMAL_WIDTH;
452     g.map.height = MAP_SIZE_NORMAL_HEIGHT;
453     g.map.ruins = 25;
454     g.map.temples = 4;
455     break;
456   }
457   g.map.signposts =
458     CreateScenario::calculateNumberOfSignposts(g.map.width, g.map.height,
459                                                int(grass_scale->get_value()));
460 
461   if (!grass_random_checkbutton->get_active())
462     g.map.grass = int(grass_scale->get_value());
463 
464   if (!water_random_checkbutton->get_active())
465     g.map.water = int(water_scale->get_value());
466 
467   if (!swamp_random_checkbutton->get_active())
468     g.map.swamp = int(swamp_scale->get_value());
469 
470   if (!forest_random_checkbutton->get_active())
471     g.map.forest = int(forest_scale->get_value());
472 
473   if (!hills_random_checkbutton->get_active())
474     g.map.hills = int(hills_scale->get_value());
475 
476   if (!mountains_random_checkbutton->get_active())
477     g.map.mountains = int(mountains_scale->get_value());
478 
479   assign_random_terrain (g);
480 
481   if (cities_random_checkbutton->get_active())
482     g.map.cities =
483       int(cities_scale->get_adjustment()->get_lower()) +
484       (Rnd::rand() % (int(cities_scale->get_adjustment()->get_upper()) -
485 		 int(cities_scale->get_adjustment()->get_lower()) + 1));
486   else
487     g.map.cities = int(cities_scale->get_value());
488 
489   Tilesetlist *tl = Tilesetlist::getInstance();
490   Armysetlist *al = Armysetlist::getInstance();
491   Shieldsetlist *sl = Shieldsetlist::getInstance();
492   Citysetlist *cl = Citysetlist::getInstance();
493   g.tile_theme = tl->getSetDir
494     (Glib::filename_from_utf8(tile_theme_combobox->get_active_text()),
495      get_active_tile_size());
496 
497   g.army_theme = al->getSetDir
498     (Glib::filename_from_utf8(army_theme_combobox->get_active_text()),
499      get_active_tile_size());
500 
501   g.shield_theme = sl->getSetDir
502     (Glib::filename_from_utf8(shield_theme_combobox->get_active_text()));
503 
504   g.city_theme = cl->getSetDir
505     (Glib::filename_from_utf8(city_theme_combobox->get_active_text()),
506      get_active_tile_size());
507 
508   g.see_opponents_stacks = GameScenarioOptions::s_see_opponents_stacks;
509   g.see_opponents_production = GameScenarioOptions::s_see_opponents_production;
510   g.play_with_quests = GameScenarioOptions::s_play_with_quests;
511   g.hidden_map = GameScenarioOptions::s_hidden_map;
512   g.neutral_cities = GameScenarioOptions::s_neutral_cities;
513   g.razing_cities = GameScenarioOptions::s_razing_cities;
514   g.diplomacy = GameScenarioOptions::s_diplomacy;
515   g.random_turns = GameScenarioOptions::s_random_turns;
516   g.quick_start = Configuration::s_quick_start;
517   g.intense_combat = GameScenarioOptions::s_intense_combat;
518   g.military_advisor = GameScenarioOptions::s_military_advisor;
519   g.cities_can_produce_allies =
520     cities_can_produce_allies_checkbutton->get_active();
521   g.cusp_of_war = GameScenarioOptions::s_cusp_of_war;
522   g.vectoring_mode = GameScenarioOptions::s_vectoring_mode;
523   g.build_production_mode = GameScenarioOptions::s_build_production_mode;
524   g.sacking_mode = GameScenarioOptions::s_sacking_mode;
525 
526   g.name = _("Autogenerated");
527   random.cleanup();
528   return g;
529 }
530 
on_grass_random_toggled()531 void NewRandomMapDialog::on_grass_random_toggled()
532 {
533   grass_scale->set_sensitive(!grass_random_checkbutton->get_active());
534 }
535 
on_water_random_toggled()536 void NewRandomMapDialog::on_water_random_toggled()
537 {
538   water_scale->set_sensitive(!water_random_checkbutton->get_active());
539 }
540 
on_swamp_random_toggled()541 void NewRandomMapDialog::on_swamp_random_toggled()
542 {
543   swamp_scale->set_sensitive(!swamp_random_checkbutton->get_active());
544 }
545 
on_forest_random_toggled()546 void NewRandomMapDialog::on_forest_random_toggled()
547 {
548   forest_scale->set_sensitive(!forest_random_checkbutton->get_active());
549 }
550 
on_hills_random_toggled()551 void NewRandomMapDialog::on_hills_random_toggled()
552 {
553   hills_scale->set_sensitive(!hills_random_checkbutton->get_active());
554 }
555 
on_mountains_random_toggled()556 void NewRandomMapDialog::on_mountains_random_toggled()
557 {
558   mountains_scale->set_sensitive(!mountains_random_checkbutton->get_active());
559 }
560 
on_cities_random_toggled()561 void NewRandomMapDialog::on_cities_random_toggled()
562 {
563   cities_scale->set_sensitive(!cities_random_checkbutton->get_active());
564 }
565 
create_and_dump_scenario(const Glib::ustring & file,const GameParameters & g,sigc::slot<void> * pulse)566 Glib::ustring NewRandomMapDialog::create_and_dump_scenario(const Glib::ustring &file,
567                                                          const GameParameters &g, sigc::slot<void> *pulse)
568 {
569   CreateScenario creator (g.map.width, g.map.height);
570 
571   // then fill the other players
572   Armyset *as = Armysetlist::getInstance()->get(g.army_theme);
573   int army_id = as->getId();
574   Shieldsetlist *ssl = Shieldsetlist::getInstance();
575   guint32 id = ssl->get(g.shield_theme)->getId();
576   for (std::vector<GameParameters::Player>::const_iterator
577        i = g.players.begin(), end = g.players.end();
578        i != end; ++i) {
579 
580     if (i->type == GameParameters::Player::OFF)
581       {
582         fl_counter->getNextId();
583         continue;
584       }
585 
586     Player::Type type;
587     if (i->type == GameParameters::Player::EASY)
588       type = Player::AI_FAST;
589     else if (i->type == GameParameters::Player::HARD)
590       type = Player::AI_SMART;
591     else
592       type = Player::HUMAN;
593 
594     creator.addPlayer(i->name, army_id, ssl->getColor(id, i->id), type);
595   }
596 
597 
598   CreateScenarioRandomize random;
599   // the neutral player must come last so it has the highest id among players
600   creator.addNeutral(random.getPlayerName(Shield::NEUTRAL), army_id,
601                      ssl->getColor(id, MAX_PLAYERS), Player::AI_DUMMY);
602 
603   // now fill in some map information
604   creator.setMapTiles(g.tile_theme);
605   creator.setShieldset(g.shield_theme);
606   creator.setCityset(g.city_theme);
607   creator.setNoCities(g.map.cities);
608   creator.setNoRuins(g.map.ruins);
609   creator.setNoTemples(g.map.temples);
610   int num_signposts = g.map.signposts;
611   if (num_signposts == -1)
612     num_signposts = CreateScenario::calculateNumberOfSignposts(g.map.width,
613                                                                g.map.height,
614                                                                g.map.grass);
615   creator.setNoSignposts(num_signposts);
616 
617   // terrain: the scenario generator also accepts input with a sum of
618   // more than 100%, so the thing is rather easy here
619   creator.setPercentages(g.map.grass, g.map.water, g.map.forest, g.map.swamp,
620                          g.map.hills, g.map.mountains);
621 
622   // now create the map and dump the created map
623   Glib::ustring path = File::getSaveFile(file);
624 
625   if (pulse)
626     creator.progress.connect(*pulse);
627 
628   creator.create(g);
629   creator.dump(path);
630   random.cleanup();
631   return path;
632 }
633 
on_accept_clicked()634 void NewRandomMapDialog::on_accept_clicked()
635 {
636   dialog_vbox->set_sensitive(false);
637   progress_treeview->show_all();
638 
639   while (g_main_context_iteration(NULL, FALSE)); //doEvents
640   GameParameters g = getParams();
641 
642   sigc::slot<void> progress = method(pulse);
643   g.difficulty = GameScenarioOptions::calculate_difficulty_rating(g);
644   d_filename = create_and_dump_scenario("random.map", g, &progress);
645 
646   //finish off the progressbar
647   while (row[progress_columns.perc] < 100)
648     {
649       row[progress_columns.perc] = row[progress_columns.perc] + 1;
650       while (g_main_context_iteration(NULL, FALSE)); //doEvents
651       Glib::usleep (10000);
652     }
653   row[progress_columns.perc] = 100;
654 
655   dialog_response = Gtk::RESPONSE_ACCEPT;
656   dialog->hide();
657 }
658 
on_cancel_clicked()659 void NewRandomMapDialog::on_cancel_clicked()
660 {
661   dialog_response = Gtk::RESPONSE_CANCEL;
662   dialog->hide();
663 }
pulse()664 void NewRandomMapDialog::pulse()
665 {
666   if (row[progress_columns.perc] < 98)
667     {
668       row[progress_columns.perc] = row[progress_columns.perc] + 3;
669       while (g_main_context_iteration(NULL, FALSE)); //doEvents
670     }
671 }
672 
take_percentages()673 void NewRandomMapDialog::take_percentages ()
674 {
675   percentages[GRASS] = grass_scale->get_value();
676   percentages[WATER] = water_scale->get_value();
677   percentages[FOREST] = forest_scale->get_value();
678   percentages[HILLS] = hills_scale->get_value();
679   percentages[MOUNTAINS] = mountains_scale->get_value();
680   percentages[SWAMP] = swamp_scale->get_value();
681 }
682 
alter_grass()683 void NewRandomMapDialog::alter_grass ()
684 {
685   double excess = 100.0 - grass_scale->get_value() - water_scale->get_value() -
686     forest_scale->get_value() - hills_scale->get_value() -
687     swamp_scale->get_value() - mountains_scale->get_value();
688 
689   std::map<int,double> per;
690   //go get the sum
691   double sum = 0;
692   for (int i = GRASS + 1; i < MAX_TERRAINS; i++)
693     sum += percentages[i];
694 
695   // how much is each of the other terrains of the whole, multiplied by excess
696   for (int i = GRASS + 1; i < MAX_TERRAINS; i++)
697     {
698       if (percentages[i] > 0)
699         per[i] = (double)percentages[i] / sum * excess;
700     }
701 
702   //we can't sort the map, so we load a copy of it into a vector.
703   std::vector<std::pair<int, double> > percopy(per.begin(), per.end());
704 
705   std::sort(percopy.begin(), percopy.end(), cmp);
706 
707   sum = 0;
708   for (auto p : percopy)
709     {
710       p.second = round (p.second);
711       sum += p.second;
712     }
713 
714   if (sum > excess)
715     {
716       //decrement from the bottom
717       int extra = sum - excess;
718       for (std::vector<std::pair<int, double> >::reverse_iterator it = percopy.rbegin();
719            it != percopy.rend(); ++it)
720         {
721           if ((*it).second == 0)
722             continue;
723           (*it).second -= 1;
724           extra--;
725           if (extra <= 0)
726             break;
727         }
728     }
729   else if (sum < excess)
730     {
731       int extra = excess - sum;
732       //add to the top.
733       for (auto it : percopy)
734         {
735           it.second++;
736           extra--;
737           if (extra <= 0)
738             break;
739         }
740     }
741   for (auto p : percopy)
742     augment_scale_value_by_type (ActiveTerrainType(p.first), p.second);
743 }
744 
augment_scale_value_by_type(ActiveTerrainType type,double amt)745 void NewRandomMapDialog::augment_scale_value_by_type (ActiveTerrainType type, double amt)
746 {
747   switch (type)
748     {
749     case GRASS:
750       grass_scale->set_value(grass_scale->get_value() + amt);
751       break;
752     case WATER:
753       water_scale->set_value(water_scale->get_value() + amt);
754       break;
755     case FOREST:
756       forest_scale->set_value(forest_scale->get_value() + amt);
757       break;
758     case HILLS:
759       hills_scale->set_value(hills_scale->get_value() + amt);
760       break;
761     case SWAMP:
762       swamp_scale->set_value(swamp_scale->get_value() + amt);
763       break;
764     case MOUNTAINS:
765       mountains_scale->set_value(mountains_scale->get_value() + amt);
766       break;
767     default:
768       break;
769     }
770 }
771 
cmp(std::pair<int,double> const & a,std::pair<int,double> const & b)772 int NewRandomMapDialog::cmp (std::pair<int,double> const &a, std::pair<int,double> const &b)
773 {
774   return a.second != b.second ?  fabs(a.second) > fabs(b.second) : a.first > b.first;
775 }
776 
alter_terrain(ActiveTerrainType type)777 void NewRandomMapDialog::alter_terrain (ActiveTerrainType type)
778 {
779   double grass = 100.0 - water_scale->get_value() - forest_scale->get_value() -
780     hills_scale->get_value() - swamp_scale->get_value() -
781     mountains_scale->get_value();
782   int excess = 0;
783   if (grass < 0)
784     {
785       excess = int(grass) * -1;
786       grass = 0;
787     }
788   grass_scale->set_value (grass);
789   if (!excess)
790     return;
791   //okay we have EXCESS to take away from every other terrain that isn't TYPE
792   //and isn't grass.
793   std::map<int,double> per;
794   //go get the sum
795   double sum = 0;
796   for (int i = GRASS + 1; i < MAX_TERRAINS; i++)
797     {
798       if (ActiveTerrainType(i) == type)
799         continue;
800       sum += percentages[i];
801     }
802 
803   // how much is each of the other terrains of the whole, multiplied by excess
804   for (int i = GRASS + 1; i < MAX_TERRAINS; i++)
805     {
806       if (ActiveTerrainType (i) == type)
807         continue;
808       if (percentages[i] > 0)
809         per[i] = (double)percentages[i] / sum * excess;
810     }
811 
812   //we can't sort the map, so we load a copy of it into a vector.
813   std::vector<std::pair<int, double> > percopy(per.begin(), per.end());
814 
815   std::sort(percopy.begin(), percopy.end(), cmp);
816 
817   sum = 0;
818   for (auto p : percopy)
819     {
820       p.second = round (p.second);
821       sum += p.second;
822     }
823   if (sum > excess)
824     {
825       //decrement from the bottom
826       int extra = sum - excess;
827       for (std::vector<std::pair<int, double> >::reverse_iterator it = percopy.rbegin();
828            it != percopy.rend(); ++it)
829         {
830           if ((*it).second == 0)
831             continue;
832           (*it).second -= 1;
833           extra--;
834           if (extra <= 0)
835             break;
836         }
837     }
838   else if (sum < excess)
839     {
840       int extra = excess - sum;
841       //add to the top.
842       for (auto it : percopy)
843         {
844           it.second++;
845           extra--;
846           if (extra <= 0)
847             break;
848         }
849     }
850   for (auto p : percopy)
851     augment_scale_value_by_type (ActiveTerrainType(p.first), -p.second);
852 }
853 
on_value_changed(ActiveTerrainType type)854 void NewRandomMapDialog::on_value_changed (ActiveTerrainType type)
855 {
856   if (d_inhibit_scales)
857     return;
858   if (type != d_active_terrain)
859     {
860       take_percentages ();
861       d_active_terrain = type;
862     }
863   switch (type)
864     {
865     case GRASS:
866       d_inhibit_scales = true;
867       alter_grass ();
868       d_inhibit_scales = false;
869       break;
870     case WATER:
871     case FOREST:
872     case HILLS:
873     case MOUNTAINS:
874     case SWAMP:
875       d_inhibit_scales = true;
876       alter_terrain (type);
877       d_inhibit_scales = false;
878       break;
879     default:
880       break;
881     }
882 }
883 
~NewRandomMapDialog()884 NewRandomMapDialog::~NewRandomMapDialog()
885 {
886   notebook->property_show_tabs () = false;
887 }
888