--- /dev/null
+// Copyright (C) 2006, 2007, 2008, 2009 Ben Asselstine
+// Copyright (C) 2007, 2008 Ole Laursen
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Library General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301, USA.
+
+#include "config.h"
+
+#include <algorithm>
+#include <vector>
+#include <assert.h>
+#include <gtkmm.h>
+#include <sigc++/functors/mem_fun.h>
+#include <sigc++/adaptors/bind.h>
+
+#include "game.h"
+
+#include "ucompose.hpp"
+#include "rectangle.h"
+#include "sound.h"
+#include "GraphicsCache.h"
+#include "GameScenario.h"
+#include "NextTurnNetworked.h"
+#include "NextTurnHotseat.h"
+#include "NextTurnPbm.h"
+
+#include "gamebigmap.h"
+#include "smallmap.h"
+
+#include "army.h"
+#include "fight.h"
+#include "hero.h"
+#include "heroproto.h"
+#include "stacklist.h"
+#include "citylist.h"
+#include "ruinlist.h"
+#include "templelist.h"
+#include "signpostlist.h"
+#include "city.h"
+#include "ruin.h"
+#include "signpost.h"
+#include "temple.h"
+#include "GameMap.h"
+#include "playerlist.h"
+#include "path.h"
+#include "Configuration.h"
+#include "File.h"
+#include "Quest.h"
+#include "reward.h"
+#include "action.h"
+#include "game-parameters.h"
+#include "FogMap.h"
+#include "GameMap.h"
+#include "history.h"
+#include "pbm-game-server.h"
+#include "LocationBox.h"
+#include "Backpack.h"
+#include "MapBackpack.h"
+#include "stacktile.h"
+
+#include "herotemplates.h"
+#include "GameScenarioOptions.h"
+#include "ai_fast.h"
+#include "ai_smart.h"
+#include "Sage.h"
+#include "Commentator.h"
+
+Game *Game::current_game = 0;
+
+//#define debug(x) {cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<flush<<endl;}
+#define debug(x)
+void Game::addPlayer(Player *p)
+{
+
+ //disconnect prior players' connections
+ for (std::list<sigc::connection>::iterator it =
+ connections[p->getId()].begin();
+ it != connections[p->getId()].end(); it++)
+ (*it).disconnect();
+ connections[p->getId()].clear();
+
+ //now setup the connections that are specific for human players
+ if (p->getType() == Player::HUMAN)
+ {
+ connections[p->getId()].push_back
+ (p->sheroGainsLevel.connect(sigc::mem_fun(this, &Game::heroGainsLevel)));
+ connections[p->getId()].push_back
+ (p->snewMedalArmy.connect(sigc::mem_fun(this, &Game::newMedalArmy)));
+
+ connections[p->getId()].push_back
+ (p->streachery.connect
+ (sigc::bind<0>
+ (sigc::mem_fun
+ (stack_considers_treachery,
+ &sigc::signal<bool, Player *, Stack *, Player *, Vector<int> >::emit), p)));
+ connections[p->getId()].push_back
+ (p->hero_arrives_with_allies.connect
+ (sigc::mem_fun
+ (hero_arrives, &sigc::signal<void, int>::emit)));
+ connections[p->getId()].push_back
+ (p->advice_asked.connect
+ (sigc::mem_fun(advice_asked, &sigc::signal<void, float>::emit)));
+ connections[p->getId()].push_back
+ (p->shaltedStack.connect
+ (sigc::mem_fun(this, &Game::on_stack_halted)));
+ connections[p->getId()].push_back
+ (p->getStacklist()->sgrouped.connect
+ (sigc::mem_fun(this, &Game::on_stack_grouped)));
+ }
+
+
+ //now do all of the common connections
+
+ connections[p->getId()].push_back
+ (p->getStacklist()->snewpos.connect
+ (sigc::mem_fun(stack_moves, &sigc::signal<void, Stack*, Vector<int> >::emit)));
+ connections[p->getId()].push_back
+ (p->srecruitingHero.connect(sigc::mem_fun(this, &Game::recruitHero)));
+ connections[p->getId()].push_back
+ (p->svisitingTemple.connect
+ (sigc::mem_fun(this, &Game::stack_searches_temple)));
+ connections[p->getId()].push_back
+ (p->ssearchingRuin.connect
+ (sigc::mem_fun(this, &Game::stack_searches_ruin)));
+ connections[p->getId()].push_back
+ (p->getStacklist()->snewpos.connect
+ (sigc::mem_fun(this, &Game::stack_arrives_on_tile)));
+ connections[p->getId()].push_back
+ (p->getStacklist()->soldpos.connect
+ (sigc::mem_fun(this, &Game::stack_leaves_tile)));
+ connections[p->getId()].push_back
+ (p->aborted_turn.connect (sigc::mem_fun
+ (game_stopped, &sigc::signal<void>::emit)));
+
+ connections[p->getId()].push_back
+ (p->schangingStats.connect
+ (sigc::mem_fun(this, &Game::update_sidebar_stats)));
+
+ connections[p->getId()].push_back
+ (p->schangingStatus.connect
+ (sigc::mem_fun(progress_status_changed, &sigc::signal<void, std::string>::emit)));
+
+ connections[p->getId()].push_back
+ (p->sbusy.connect (sigc::mem_fun (progress_changed,
+ &sigc::signal<void>::emit)));
+ connections[p->getId()].push_back
+ (p->supdatingStack.connect (sigc::mem_fun(this, &Game::stackUpdate)));
+ connections[p->getId()].push_back
+ (p->sinvadingCity.connect(sigc::mem_fun(this, &Game::invading_city)));
+ connections[p->getId()].push_back
+ (p->streacheryStack.connect(sigc::mem_fun(this, &Game::maybeTreachery)));
+ connections[p->getId()].push_back
+ (p->fight_started.connect (sigc::mem_fun(*this, &Game::on_fight_started)));
+ connections[p->getId()].push_back
+ (p->ruinfight_started.connect
+ (sigc::mem_fun
+ (ruinfight_started, &sigc::signal<void, Stack *, Stack *>::emit)));
+ connections[p->getId()].push_back
+ (p->ruinfight_finished.connect
+ (sigc::mem_fun
+ (ruinfight_finished, &sigc::signal<void, Fight::Result>::emit)));
+ connections[p->getId()].push_back
+ (p->cityfight_finished.connect (sigc::mem_fun(*this, &Game::on_city_fight_finished)));
+ if (p->getType() == Player::NETWORKED && p == Playerlist::getActiveplayer())
+ lock_inputs();
+ if (p->getType() == Player::HUMAN && p == Playerlist::getActiveplayer())
+ unlock_inputs();
+}
+
+void Game::on_stack_halted(Stack *stack)
+{
+ if (stack == NULL)
+ return;
+ bigmap->reset_path_calculator(stack);
+ //tell gamebigmap that a stack just stopped
+}
+void Game::on_stack_grouped(Stack *stack, bool grouped)
+{
+ bigmap->reset_path_calculator(stack);
+ //tell gamebigmap that we just grouped/ungrouped a stack.
+ return;
+}
+#include "game-server.h"
+
+Game::Game(GameScenario* gameScenario, NextTurn *nextTurn)
+ : d_gameScenario(gameScenario), d_nextTurn(nextTurn)
+{
+ current_game = this;
+ input_locked = false;
+
+ // init the bigmap
+ bigmap.reset(new GameBigMap
+ (GameScenario::s_intense_combat,
+ GameScenario::s_see_opponents_production,
+ GameScenario::s_see_opponents_stacks,
+ GameScenario::s_military_advisor));
+ bigmap->stack_selected.connect(
+ sigc::mem_fun(this, &Game::on_stack_selected));
+ bigmap->stack_grouped_or_ungrouped.connect(
+ sigc::mem_fun(this, &Game::on_stack_grouped_or_ungrouped));
+ bigmap->path_set.connect(
+ sigc::mem_fun(this, &Game::update_control_panel));
+ bigmap->city_visited.connect(
+ sigc::mem_fun(this, &Game::on_city_visited));
+ bigmap->city_queried.connect(
+ sigc::mem_fun(this, &Game::on_city_queried));
+ bigmap->city_unqueried.connect(
+ sigc::mem_fun(this, &Game::on_city_unqueried));
+ bigmap->ruin_queried.connect(
+ sigc::mem_fun(this, &Game::on_ruin_queried));
+ bigmap->signpost_queried.connect(
+ sigc::mem_fun(this, &Game::on_signpost_queried));
+ bigmap->temple_queried.connect(
+ sigc::mem_fun(this, &Game::on_temple_queried));
+ bigmap->stack_queried.connect(
+ sigc::mem_fun(this, &Game::on_stack_queried));
+ bigmap->stack_unqueried.connect(
+ sigc::mem_fun(this, &Game::on_stack_unqueried));
+
+ // init the smallmap
+ smallmap.reset(new SmallMap);
+ // pass map changes directly through
+ smallmap->resize();
+ smallmap->map_changed.connect(
+ sigc::mem_fun(smallmap_changed,
+ &sigc::signal<void, Glib::RefPtr<Gdk::Pixmap>,
+ Gdk::Rectangle>::emit));
+
+ // connect the two maps
+ bigmap->view_changed.connect(
+ sigc::mem_fun(smallmap.get(), &SmallMap::set_view));
+ bigmap->map_changed.connect(
+ sigc::mem_fun(bigmap_changed,
+ &sigc::signal<void, Glib::RefPtr<Gdk::Pixmap> >::emit));
+ smallmap->view_changed.connect(
+ sigc::mem_fun(bigmap.get(), &GameBigMap::set_view));
+
+ // get the maps up and running
+ bigmap->screen_size_changed(Gtk::Allocation(0,0,320,200));
+
+ // connect player callbacks
+ Playerlist* pl = Playerlist::getInstance();
+ for (Playerlist::iterator i = pl->begin(); i != pl->end(); ++i)
+ {
+ Player *p = *i;
+ addPlayer(p);
+ }
+ if (gameScenario->getPlayMode() == GameScenario::HOTSEAT)
+ pl->splayerDead.connect(sigc::mem_fun(this, &Game::on_player_died));
+ pl->ssurrender.connect(sigc::mem_fun(this, &Game::on_surrender_offered));
+
+ d_nextTurn->splayerStart.connect(
+ sigc::mem_fun(this, &Game::init_turn_for_player));
+ d_nextTurn->snextRound.connect(
+ sigc::mem_fun(d_gameScenario, &GameScenario::nextRound));
+ d_nextTurn->snextRound.connect(
+ sigc::mem_fun(this, &Game::nextRound));
+ d_nextTurn->supdating.connect(
+ sigc::mem_fun(this, &Game::redraw));
+
+
+ center_view_on_city();
+ update_control_panel();
+
+ HeroTemplates::getInstance();
+}
+
+Game::~Game()
+{
+ for (unsigned int i = 0; i < MAX_PLAYERS + 1; i++)
+ {
+ for (std::list<sigc::connection>::iterator it = connections[i].begin();
+ it != connections[i].end(); it++)
+ (*it).disconnect();
+ connections[i].clear();
+ }
+ delete d_gameScenario;
+ delete d_nextTurn;
+
+ HeroTemplates::deleteInstance();
+}
+
+GameScenario *Game::getScenario()
+{
+ return current_game->d_gameScenario;
+}
+
+void Game::end_turn()
+{
+ //only human players hit this.
+ unselect_active_stack();
+ clear_stack_info();
+ update_control_panel();
+ lock_inputs();
+
+ bool trigger_next_remote_player = false;
+ if (d_gameScenario->getPlayMode() == GameScenario::NETWORKED &&
+ Playerlist::getActiveplayer()->getType() == Player::NETWORKED)
+ trigger_next_remote_player = true;
+ d_nextTurn->endTurn();
+ if (trigger_next_remote_player)
+ remote_next_player_turn.emit();
+}
+
+void Game::update_stack_info()
+{
+ Stack* stack = Playerlist::getActiveplayer()->getActivestack();
+
+ //if (Playerlist::getActiveplayer()->getType() != Player::HUMAN &&
+ //GameScenario::s_hidden_map == true)
+ //return;
+ stack_info_changed.emit(stack);
+}
+
+void Game::clear_stack_info()
+{
+ stack_info_changed.emit(0);
+}
+
+void Game::update_sidebar_stats()
+{
+ SidebarStats s;
+ Player *player = Playerlist::getActiveplayer();
+ if (player == Playerlist::getInstance()->getNeutral())
+ return;
+
+ s.name = player->getName();
+ s.gold = player->getGold();
+ s.income = s.cities = 0;
+ s.income = player->getIncome();
+ s.cities = Citylist::getInstance()->countCities(player);
+
+ s.units = 0;
+ s.upkeep = player->getUpkeep() + player->getCostOfUnitsProducedThisTurn();
+ Stacklist *sl = player->getStacklist();
+ for (Stacklist::iterator i = sl->begin(), iend = sl->end(); i != iend; ++i)
+ s.units += (*i)->size();
+
+ s.turns = d_gameScenario->getRound();
+
+ sidebar_stats_changed.emit(s);
+}
+
+void Game::redraw()
+{
+ if (bigmap.get())
+ {
+ bigmap->draw(Playerlist::getViewingplayer());
+ }
+ if (smallmap.get())
+ {
+ //if (Playerlist::getActiveplayer()->getType() == Player::HUMAN ||
+ //GameScenario::s_hidden_map == false)
+ smallmap->draw(Playerlist::getActiveplayer());
+ }
+}
+
+void Game::select_next_movable_stack()
+{
+ Stacklist *sl = Playerlist::getActiveplayer()->getStacklist();
+ Stack* stack = sl->getNextMovable();
+ sl->setActivestack(stack);
+ select_active_stack();
+}
+
+void Game::move_map_dir(int diffx, int diffy)
+{
+ smallmap->move_map_in_dir(Vector<int>(diffx, diffy));
+}
+
+void Game::move_selected_stack_dir(int diffx, int diffy)
+{
+ Stack *stack = Playerlist::getActiveplayer()->getActivestack();
+ if (!stack)
+ return;
+ // Get rid of the old path if there is one
+ if (stack->getPath()->size())
+ stack->getPath()->clear();
+ //See if we can move there
+ Vector<int> dest = stack->getPos();
+ dest += Vector<int>(diffx, diffy);
+ int mp = stack->getPath()->calculate(stack, dest);
+ if (mp > 0)
+ move_selected_stack_along_path();
+ else
+ {
+ Playerlist::getActiveplayer()->setActivestack(0);
+ unselect_active_stack();
+ }
+}
+
+void Game::move_selected_stack_northwest()
+{
+ if (Playerlist::getActiveplayer()->getActivestack() != NULL)
+ move_selected_stack_dir(-1, -1);
+ else
+ move_map_dir(-1,-1);
+}
+
+void Game::move_selected_stack_north()
+{
+ if (Playerlist::getActiveplayer()->getActivestack() != NULL)
+ move_selected_stack_dir(0, -1);
+ else
+ move_map_dir(0, -1);
+}
+
+void Game::move_selected_stack_northeast()
+{
+ if (Playerlist::getActiveplayer()->getActivestack() != NULL)
+ move_selected_stack_dir(1, -1);
+ else
+ move_map_dir(1, -1);
+}
+
+void Game::move_selected_stack_east()
+{
+ if (Playerlist::getActiveplayer()->getActivestack() != NULL)
+ move_selected_stack_dir(1, 0);
+ else
+ move_map_dir(1, 0);
+}
+
+void Game::move_selected_stack_west()
+{
+ if (Playerlist::getActiveplayer()->getActivestack() != NULL)
+ move_selected_stack_dir(-1, 0);
+ else
+ move_map_dir(-1, 0);
+}
+
+void Game::move_selected_stack_southwest()
+{
+ if (Playerlist::getActiveplayer()->getActivestack() != NULL)
+ move_selected_stack_dir(-1, 1);
+ else
+ move_map_dir(-1, 1);
+}
+
+void Game::move_selected_stack_south()
+{
+ if (Playerlist::getActiveplayer()->getActivestack() != NULL)
+ move_selected_stack_dir(0, 1);
+ else
+ move_map_dir(0, 1);
+}
+
+void Game::move_selected_stack_southeast()
+{
+ if (Playerlist::getActiveplayer()->getActivestack() != NULL)
+ move_selected_stack_dir(1, 1);
+ else
+ move_map_dir(1, 1);
+}
+
+void Game::move_selected_stack_along_path()
+{
+ Stack *stack = Playerlist::getActiveplayer()->getActivestack();
+
+ Playerlist::getActiveplayer()->stackMove(stack);
+
+ //maybe we joined another stack
+ stack = Playerlist::getActiveplayer()->getActivestack();
+ if (stack && stack->canMove() == false)
+ {
+ Playerlist::getActiveplayer()->setActivestack(0);
+ unselect_active_stack();
+ }
+}
+
+void Game::move_all_stacks()
+{
+ Player *player = Playerlist::getActiveplayer();
+ Stacklist* sl = player->getStacklist();
+
+ for (Stacklist::iterator i = sl->begin(), end = sl->end(); i != end; ++i)
+ {
+ Stack &s = **i;
+ if (!(s.empty()) && !(s.getPath()->empty()) && s.enoughMoves())
+ {
+ sl->setActivestack(&s);
+ select_active_stack();
+ bool moved = player->stackMove(player->getActivestack());
+ if (!moved)
+ break;
+ i = sl->begin();
+ }
+ }
+
+ if (sl->getActivestack()->canMove() == false)
+ {
+ Playerlist::getActiveplayer()->setActivestack(0);
+ unselect_active_stack();
+ }
+}
+
+void Game::defend_selected_stack()
+{
+ Player *player = Playerlist::getActiveplayer();
+ Stack *stack = player->getActivestack();
+ assert(stack);
+
+ stack->setDefending(true);
+
+ stack = player->getStacklist()->getNextMovable();
+ player->setActivestack(stack);
+
+ if (stack)
+ select_active_stack();
+ else
+ unselect_active_stack();
+}
+
+void Game::park_selected_stack()
+{
+ Player *player = Playerlist::getActiveplayer();
+ Stack *stack = player->getActivestack();
+ assert(stack);
+ stack->setParked(true);
+
+ stack = player->getStacklist()->getNextMovable();
+ player->setActivestack(stack);
+
+ if (stack)
+ select_active_stack();
+ else
+ unselect_active_stack();
+}
+
+void Game::deselect_selected_stack()
+{
+ Player *player = Playerlist::getActiveplayer();
+ player->setActivestack(0);
+ unselect_active_stack();
+}
+
+void Game::center_selected_stack()
+{
+ Stack *stack = Playerlist::getActiveplayer()->getActivestack();
+ if (stack)
+ select_active_stack();
+}
+
+void Game::search_stack(Stack *stack)
+{
+ Player *player = Playerlist::getActiveplayer();
+ Ruin* ruin = GameMap::getRuin(stack);
+ Temple* temple = GameMap::getTemple(stack);
+
+ if (ruin && !ruin->isSearched() && stack->hasHero() &&
+ stack->getFirstHero()->getMoves() > 0 &&
+ ((ruin->isHidden() == true && ruin->getOwner() == player) ||
+ ruin->isHidden() == false))
+ {
+ Reward *reward;
+
+ reward = player->stackSearchRuin(stack, ruin);
+ if (ruin->hasSage() == true)
+ {
+ Sage *sage = ruin->generateSage();
+ if (player->isComputer() == false)
+ reward = sage_visited.emit(ruin, sage, stack);
+ else
+ reward = player->chooseReward(ruin, sage, stack);
+ }
+
+ if (reward)
+ {
+ player->giveReward(stack, reward);
+ //FIXME: delete this reward, but don't delete the item, or map
+ redraw();
+ update_stack_info();
+ update_control_panel();
+ if (player->isComputer() == false)
+ ruin_searched.emit(ruin, stack, reward);
+ }
+ else
+ {
+ redraw();
+ update_stack_info();
+ update_control_panel();
+ }
+
+ update_sidebar_stats();
+ }
+ else if (temple && temple->searchable() && stack->getMoves() > 0)
+ {
+ int blessCount;
+ blessCount = player->stackVisitTemple(stack, temple);
+ bool wants_quest;
+ Hero *hero = stack->getFirstHeroWithoutAQuest();
+ if (player->isComputer() == false)
+ wants_quest = temple_searched.emit(hero, temple, blessCount);
+ else
+ wants_quest = player->chooseQuest(hero);
+ if (wants_quest && stack->hasHero())
+ {
+ Quest *q = player->heroGetQuest
+ (hero, temple,
+ GameScenario::s_razing_cities != GameParameters::NEVER);
+
+ if (q)
+ {
+ if (player->isComputer() == false)
+ {
+ Hero *hero;
+ Army *a = stack->getArmyById(q->getHeroId());
+ if (a)
+ hero = dynamic_cast<Hero*>(a);
+ quest_assigned.emit(hero, q);
+ }
+ }
+ }
+ }
+}
+
+void Game::search_selected_stack()
+{
+ Player *player = Playerlist::getActiveplayer();
+ Stack* stack = player->getActivestack();
+ return search_stack(stack);
+}
+
+void Game::stackUpdate(Stack* s)
+{
+ if (!s)
+ s = Playerlist::getActiveplayer()->getActivestack();
+
+ //if player is not to be observed, bail now
+ if (s != NULL && s->getOwner()->isObservable() == false)
+ return;
+
+ if (s)
+ smallmap->center_view_on_tile(s->getPos(), true);
+
+ //redraw();
+
+ update_stack_info();
+ update_control_panel();
+
+}
+
+Army::Stat Game::heroGainsLevel(Hero * h)
+{
+ // don't show a dialog if computer or enemy's armies advance
+ if (h->getOwner()->isComputer() == true ||
+ h->getOwner() != Playerlist::getInstance()->getActiveplayer())
+ return Playerlist::getInstance()->getActiveplayer()->chooseStat(h);
+
+ return hero_gains_level.emit(h);
+}
+
+void Game::newMedalArmy(Army* a, int medaltype)
+{
+ // We don't want to have medal awards of computer players displayed
+ if (!a->getOwner()
+ || (a->getOwner()->getType() != Player::HUMAN)
+ || a->getOwner() != Playerlist::getInstance()->getActiveplayer())
+ return;
+
+ medal_awarded_to_army.emit(a, medaltype);
+ update_stack_info();
+}
+
+void Game::on_stack_grouped_or_ungrouped(Stack *s)
+{
+ //this only happens when we double-click on a stack on the bigmap.
+ update_stack_info();
+ update_control_panel();
+}
+
+void Game::on_stack_selected(Stack* s)
+{
+ update_stack_info();
+ update_control_panel();
+}
+
+void Game::on_city_queried (Vector<int> tile, City *c)
+{
+ MapTipPosition mpos = bigmap->map_tip_position(tile);
+ city_tip_changed.emit(c, mpos);
+}
+
+void Game::on_city_unqueried ()
+{
+ city_tip_changed.emit(NULL, MapTipPosition());
+}
+
+void Game::on_city_visited(City* c)
+{
+ if (c)
+ {
+ city_visited.emit(c);
+ // some visible city properties (razed) may have changed
+ redraw();
+ }
+}
+
+void Game::on_ruin_queried (Ruin* r, bool brief)
+{
+ if (r)
+ {
+ if (brief)
+ {
+ Glib::ustring str;
+
+ str = r->getName();
+ str += "\n";
+ if (r->isSearched())
+ // note to translators: whether a ruin has been searched
+ str += _("Explored");
+ else
+ // note to translators: whether a ruin has been searched
+ str += _("Unexplored");
+
+ MapTipPosition mpos = bigmap->map_tip_position(r->getArea());
+ map_tip_changed.emit(str, mpos);
+ }
+ else
+ {
+ ruin_visited.emit(r);
+ }
+ }
+ else
+ map_tip_changed.emit("", MapTipPosition());
+}
+
+void Game::on_signpost_queried (Signpost* s)
+{
+ if (s)
+ {
+ Glib::ustring str;
+
+ str = s->getName();
+
+ MapTipPosition mpos = bigmap->map_tip_position(s->getArea());
+ map_tip_changed.emit(str, mpos);
+ }
+ else
+ map_tip_changed.emit("", MapTipPosition());
+}
+
+void Game::on_stack_unqueried ()
+{
+ stack_tip_changed.emit(NULL, MapTipPosition());
+}
+void Game::on_stack_queried (Vector<int> tile)
+{
+ MapTipPosition mpos = bigmap->map_tip_position(tile);
+ stack_tip_changed.emit(GameMap::getStacks(tile), mpos);
+}
+
+void Game::on_temple_queried (Temple* t, bool brief)
+{
+ if (t)
+ {
+ if (brief)
+ {
+ Glib::ustring str;
+
+ str = t->getName();
+
+ MapTipPosition mpos = bigmap->map_tip_position(t->getArea());
+ map_tip_changed.emit(str, mpos);
+ }
+ else
+ {
+ temple_visited.emit(t);
+ }
+ }
+ else
+ map_tip_changed.emit("", MapTipPosition());
+}
+
+void Game::looting_city(City* city, int &gold)
+{
+ Citylist *clist = Citylist::getInstance();
+ Playerlist *plist = Playerlist::getInstance();
+ Player *attacker = plist->getActiveplayer();
+ Player *defender = city->getOwner();
+ int amt = (defender->getGold() / (2 * clist->countCities (defender)) * 2);
+ // give (Enemy-Gold/(2Enemy-Cities)) to the attacker
+ // and then take away twice that from the defender.
+ // the idea here is that some money is taken in the invasion
+ // and other monies are lost forever
+ defender->withdrawGold (amt);
+ amt /= 2;
+ attacker->addGold (amt);
+ gold = amt;
+ return;
+}
+
+void Game::invading_city(City* city, int gold)
+{
+ Player *player = Playerlist::getInstance()->getActiveplayer();
+
+ if (player->getType() == Player::HUMAN)
+ {
+ redraw();
+ CityDefeatedAction a = city_defeated.emit(city, gold);
+ gold = 0;
+
+ switch (a) {
+ case CITY_DEFEATED_OCCUPY:
+ player->cityOccupy(city);
+ break;
+
+ case CITY_DEFEATED_RAZE:
+ //the razing just happened in the are-you-sure dialog, and the user
+ //was sure.
+ city_razed.emit(city);
+ player->deteriorateDiplomaticRelationship (5);
+ break;
+
+ case CITY_DEFEATED_PILLAGE:
+ {
+ int pillaged_army_type = -1;
+ player->cityPillage(city, gold, &pillaged_army_type);
+ city_pillaged.emit(city, gold, pillaged_army_type);
+ }
+ break;
+
+ case CITY_DEFEATED_SACK:
+ std::list<guint32> sacked_types;
+ player->citySack(city, gold, &sacked_types);
+ city_sacked.emit(city, gold, sacked_types);
+ break;
+ }
+
+ if (!city->isBurnt())
+ city_visited.emit(city);
+ }
+
+ redraw();
+ update_stack_info();
+ update_sidebar_stats();
+ update_control_panel();
+}
+
+void Game::lock_inputs()
+{
+ // don't accept modifying user input from now on
+ bigmap->reset_zoom();
+ bigmap->set_input_locked(true);
+ smallmap->set_input_locked(true);
+ input_locked = true;
+ update_control_panel();
+}
+
+void Game::unlock_inputs()
+{
+ bigmap->set_input_locked(false);
+ smallmap->set_input_locked(false);
+ input_locked = false;
+ update_control_panel();
+}
+
+void Game::update_control_panel()
+{
+ if (input_locked)
+ {
+ can_select_next_movable_stack.emit(false);
+ can_center_selected_stack.emit(false);
+ can_defend_selected_stack.emit(false);
+ can_park_selected_stack.emit(false);
+ can_deselect_selected_stack.emit(false);
+ can_search_selected_stack.emit(false);
+ can_plant_standard_selected_stack.emit(false);
+ can_move_selected_stack.emit(false);
+ can_move_selected_stack_along_path.emit(false);
+ can_move_all_stacks.emit(false);
+ can_group_ungroup_selected_stack.emit(false);
+ can_end_turn.emit(false);
+ can_disband_stack.emit(false);
+ can_change_signpost.emit(false);
+ can_see_history.emit(false);
+ can_see_diplomacy.emit(false);
+
+ return;
+ }
+
+ Player *player = Playerlist::getActiveplayer();
+ Stacklist* sl = player->getStacklist();
+
+ bool all_defending_or_parked = true;
+ for (Stacklist::iterator i = sl->begin(); i != sl->end(); ++i)
+ if (!(*i)->getDefending() && !(*i)->getParked()
+ && *i != sl->getActivestack())
+ {
+ all_defending_or_parked = false;
+ break;
+ }
+
+ bool all_immobile = true;
+ for (Stacklist::iterator i = sl->begin(); i != sl->end(); ++i)
+ if (!(*i)->getDefending() && !(*i)->getParked() && (*i)->canMove()
+ && *i != sl->getActivestack())
+ {
+ all_immobile = false;
+ break;
+ }
+ can_select_next_movable_stack.emit(!all_defending_or_parked && !all_immobile);
+
+ // if any stack can move, enable the moveall button
+ can_move_all_stacks.emit(sl->enoughMoves());
+
+ Stack *stack = player->getActivestack();
+
+ can_defend_selected_stack.emit(stack != 0);
+ can_park_selected_stack.emit(stack != 0);
+ can_deselect_selected_stack.emit(stack != 0);
+ can_center_selected_stack.emit(stack != 0);
+
+ if (stack)
+ {
+ can_move_selected_stack_along_path.emit
+ ((!stack->getPath()->empty() && stack->enoughMoves()) ||
+ (!stack->getPath()->empty() && stack->getPath()->getMovesExhaustedAtPoint() > 0));
+
+ /*
+ * a note about searching.
+ * ruins can be searched by stacks that have a hero, and when the
+ * hero has moves left. also the ruin must be unexplored.
+ * temples can be searched by any stack, when the stack has
+ * movement left.
+ */
+ if (stack->getMoves() > 0)
+ {
+ Temple *temple;
+ temple = GameMap::getTemple(stack);
+ can_search_selected_stack.emit(temple);
+ can_move_selected_stack.emit(true);
+ }
+
+ if (stack->hasHero())
+ {
+ Ruin *ruin = GameMap::getRuin(stack);
+ if (stack->getFirstHero()->getMoves() > 0 && ruin)
+ can_search_selected_stack.emit(!ruin->isSearched());
+
+ //does the hero have the player's standard?
+ for (Stack::iterator it = stack->begin(); it != stack->end(); it++)
+ {
+ if ((*it)->isHero())
+ {
+ Hero *hero = dynamic_cast<Hero*>((*it));
+ if (hero->getBackpack()->getPlantableItem(player))
+ {
+ //can't plant on city/ruin/temple/signpost
+ City *city = GameMap::getCity(stack->getPos());
+ Temple *temple = GameMap::getTemple(stack);
+ Ruin *ruin = GameMap::getRuin(stack);
+ Signpost *sign = GameMap::getSignpost(stack);
+ if (!city && !temple && !ruin && !sign)
+ {
+ GameMap *gm = GameMap::getInstance();
+ MapBackpack *backpack;
+ Vector<int> pos = stack->getPos();
+ backpack = gm->getTile(pos)->getBackpack();
+ bool standard_already_planted =
+ backpack->getFirstPlantedItem() != NULL;
+ //are there any other standards here?
+ if (standard_already_planted == false)
+ can_plant_standard_selected_stack.emit(true);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ can_plant_standard_selected_stack.emit(false);
+ }
+
+ if (GameMap::getSignpost(stack))
+ can_change_signpost.emit(true);
+
+ can_disband_stack.emit(true);
+ can_group_ungroup_selected_stack.emit(true);
+ }
+ else
+ {
+ can_search_selected_stack.emit(false);
+ can_move_selected_stack.emit(false);
+ can_move_selected_stack_along_path.emit(false);
+ can_disband_stack.emit(false);
+ can_group_ungroup_selected_stack.emit(false);
+ can_plant_standard_selected_stack.emit(false);
+ }
+
+ if (d_gameScenario->getRound() > 1)
+ can_see_history.emit(true);
+ else
+ can_see_history.emit(false);
+
+ can_see_diplomacy.emit(GameScenarioOptions::s_diplomacy);
+
+ can_end_turn.emit(true);
+}
+
+GameBigMap &Game::get_bigmap()
+{
+ assert(bigmap.get());
+ return *bigmap.get();
+}
+
+SmallMap &Game::get_smallmap()
+{
+ assert(smallmap.get());
+ return *smallmap.get();
+}
+
+void Game::startGame()
+{
+ debug ("start_game()");
+
+ center_view_on_city();
+ update_sidebar_stats();
+ update_control_panel();
+ update_stack_info();
+ lock_inputs();
+
+ d_nextTurn->start();
+ if (Playerlist::getInstance()->countPlayersAlive())
+ update_control_panel();
+}
+
+void Game::loadGame()
+{
+ //if pbm and human
+ //if pbm and not human, then bail
+
+ Player *player = Playerlist::getActiveplayer();
+ if (!player)
+ {
+ Playerlist::getInstance()->nextPlayer();
+ player = Playerlist::getActiveplayer();
+ }
+
+ if (player->getType() == Player::HUMAN && (d_gameScenario->getPlayMode() == GameScenario::HOTSEAT))
+ {
+ //human players want access to the controls and an info box
+ unlock_inputs();
+ player->setActivestack(0);
+ center_view_on_city();
+ update_sidebar_stats();
+ update_control_panel();
+ update_stack_info();
+ game_loaded.emit(player);
+ if (player->getType() == Player::HUMAN)
+ d_nextTurn->setContinuingTurn();
+ }
+ else if (player->getType() == Player::HUMAN &&
+ d_gameScenario->getPlayMode() == GameScenario::PLAY_BY_MAIL)
+ {
+ if (player->hasAlreadyInitializedTurn())
+ {
+ unlock_inputs();
+ player->setActivestack(0);
+ center_view_on_city();
+ update_sidebar_stats();
+ update_control_panel();
+ update_stack_info();
+ game_loaded.emit(player);
+ player->loadPbmGame();
+ d_nextTurn->setContinuingTurn();
+ }
+ else
+ {
+ lock_inputs();
+ }
+ }
+ else
+ lock_inputs();
+
+ d_nextTurn->start();
+#if 0
+ else
+ {
+ lock_inputs();
+ update_sidebar_stats();
+ player->startTurn();
+ d_nextTurn->endTurn();
+ if (Playerlist::getInstance()->countPlayersAlive())
+ update_control_panel();
+ }
+#endif
+}
+
+void Game::stopGame()
+{
+ d_nextTurn->stop();
+}
+
+bool Game::saveGame(std::string file)
+{
+ return d_gameScenario->saveGame(file);
+}
+
+void Game::blank(bool on)
+{
+ if (GameScenarioOptions::s_hidden_map == true)
+ {
+ bigmap->blank(on);
+ smallmap->blank(on);
+ }
+}
+
+void Game::init_turn_for_player(Player* p)
+{
+ Playerlist* pl = Playerlist::getInstance();
+
+ blank(true);
+
+ next_player_turn.emit(p, d_gameScenario->getRound());
+
+ blank(false);
+
+ if (p->isObservable() == true)
+ center_view_on_city();
+
+ if (p->getType() == Player::HUMAN)
+ {
+ if (Commentator::getInstance()->hasComment() == true)
+ {
+ std::vector<std::string> comments =
+ Commentator::getInstance()->getComments(p);
+ if (comments.size() > 0)
+ commentator_comments.emit(comments[rand() % comments.size()]);
+ }
+ }
+
+ p->maybeRecruitHero();
+
+ if (p->getType() == Player::HUMAN)
+ {
+ unlock_inputs();
+
+ update_sidebar_stats();
+ update_stack_info();
+ update_control_panel();
+ redraw();
+
+ // update the diplomacy icon if we've received a proposal
+ bool proposal_received = false;
+ for (Playerlist::iterator it = pl->begin(); it != pl->end(); ++it)
+ {
+ if ((*it) == pl->getNeutral())
+ continue;
+ if ((*it) == p)
+ continue;
+ if((*it)->isDead())
+ continue;
+ if ((*it)->getDiplomaticProposal(p) != Player::NO_PROPOSAL)
+ {
+ proposal_received = true;
+ break;
+ }
+ }
+ received_diplomatic_proposal.emit(proposal_received);
+ //check to see if we've turned off production due to destitution.
+ bool destitute = false;
+ if (p->countDestituteCitiesThisTurn() > 0)
+ destitute = true;
+ city_too_poor_to_produce.emit(destitute);
+ }
+ else
+ {
+ //SDL_Delay(250);
+ }
+}
+
+void Game::on_player_died(Player *player)
+{
+ const Playerlist* pl = Playerlist::getInstance();
+ if (pl->getNoOfPlayers() <= 1)
+ game_over.emit(pl->getFirstLiving());
+ else
+ player_died.emit(player);
+}
+
+
+void Game::on_fight_started(Fight &fight)
+{
+
+ //don't show the battle if the ai is attacking neutral
+ bool ai_attacking_neutral = false;
+ if (fight.getDefenders().front()->getOwner() == Playerlist::getInstance()->getNeutral() && Playerlist::getActiveplayer()->getType() != Player::HUMAN)
+ ai_attacking_neutral = true;
+
+ //show the battle if we're attacking an observable player
+ bool attacking_observable_player = false;
+ if (fight.getDefenders().front()->getOwner()->isObservable())
+ attacking_observable_player = true;
+
+ //don't show the battle if we're ai and we're on a hidden map
+ bool ai_attacking_on_hidden_map = false;
+ if (fight.getAttackers().front()->getOwner()->getType() != Player::HUMAN &&
+ GameScenario::s_hidden_map == true)
+ ai_attacking_on_hidden_map = true;
+
+ if ((Playerlist::getActiveplayer()->isObservable() == true ||
+ attacking_observable_player) && !ai_attacking_neutral &&
+ !ai_attacking_on_hidden_map)
+ {
+ Vector<int> pos = fight.getAttackers().front()->getPos();
+ if (GameScenario::s_hidden_map == false)
+ smallmap->center_view_on_tile(pos, true);
+ fight_started.emit(Fight::calculateFightBox(fight), fight);
+ }
+ else if ((Playerlist::getActiveplayer()->isObservable() == true ||
+ attacking_observable_player) && ai_attacking_neutral &&
+ !ai_attacking_on_hidden_map)
+ {
+ Vector<int> pos = fight.getAttackers().front()->getPos();
+ if (GameScenario::s_hidden_map == false)
+ smallmap->center_view_on_tile(pos, true);
+ abbreviated_fight_started.emit(Fight::calculateFightBox(fight));
+ }
+}
+
+void Game::center_view_on_city()
+{
+ const Player* p = Playerlist::getInstance()->getActiveplayer();
+
+ if (p == Playerlist::getInstance()->getNeutral())
+ return;
+ if (Playerlist::getActiveplayer()->getType() != Player::HUMAN &&
+ GameScenario::s_hidden_map == true)
+ return;
+ //FIXME: if player is not to be observed, bail now
+ // preferred city is a capital city that belongs to the player
+ for (Citylist::iterator i = Citylist::getInstance()->begin();
+ i != Citylist::getInstance()->end(); i++)
+ {
+ City *c = *i;
+ if (c->getOwner() == p && c->isCapital() &&
+ c->getCapitalOwner() == p)
+ {
+ smallmap->center_view_on_tile(c->getPos(),
+ !GameScenario::s_hidden_map);
+ return;
+ }
+ }
+
+ // okay, then find any city that belongs to the player and center on it
+ for (Citylist::iterator i = Citylist::getInstance()->begin();
+ i != Citylist::getInstance()->end(); i++)
+ {
+ City *c = *i;
+ if (c->getOwner() == p)
+ {
+ smallmap->center_view_on_tile(c->getPos(),
+ !GameScenario::s_hidden_map);
+ break;
+ }
+ }
+}
+void Game::select_active_stack()
+{
+ //if (Playerlist::getActiveplayer()->getType() != Player::HUMAN &&
+ //GameScenario::s_hidden_map == true)
+ //return;
+ Player *p = Playerlist::getInstance()->getActiveplayer();
+ smallmap->center_view_on_tile (p->getActivestack()->getPos(), true);
+ bigmap->select_active_stack();
+}
+void Game::unselect_active_stack()
+{
+ bigmap->unselect_active_stack();
+}
+
+bool Game::maybeTreachery(Stack *stack, Player *them, Vector<int> pos)
+{
+ Player *me = stack->getOwner();
+ bool treachery = false;
+ if (me->isComputer())
+ {
+ if (me->getType() == Player::AI_FAST)
+ {
+ AI_Fast *ai = dynamic_cast<AI_Fast*>(me);
+ treachery = ai->chooseTreachery (stack, them, pos);
+ }
+ else if (me->getType() == Player::AI_SMART)
+ {
+ AI_Smart *ai = dynamic_cast<AI_Smart*>(me);
+ treachery = ai->chooseTreachery (stack, them, pos);
+ }
+ }
+ if (treachery == false)
+ return false;
+ me->proposeDiplomacy (Player::NO_PROPOSAL, them);
+ me->declareDiplomacy (Player::AT_WAR, them);
+ them->proposeDiplomacy (Player::NO_PROPOSAL, me);
+ them->declareDiplomacy (Player::AT_WAR, me);
+ History_DiplomacyTreachery *item = new History_DiplomacyTreachery();
+ item->fillData(them);
+ me->addHistory(item);
+
+ me->deteriorateDiplomaticRelationship (5);
+ them->improveDiplomaticRelationship (2, me);
+
+ return true;
+}
+
+void Game::nextRound()
+{
+ Playerlist::getInstance()->nextRound
+ (GameScenarioOptions::s_diplomacy,
+ &GameScenarioOptions::s_surrender_already_offered);
+}
+
+void Game::on_surrender_offered(Player *recipient)
+{
+ Playerlist *plist = Playerlist::getInstance();
+ if (enemy_offers_surrender.emit(plist->countPlayersAlive() - 1))
+ {
+ plist->surrender();
+ surrender_answered.emit(true);
+ game_over.emit(recipient);
+ }
+ else
+ surrender_answered.emit(false);
+}
+
+void Game::recalculate_moves_for_stack(Stack *s)
+{
+ if (!s)
+ s = Playerlist::getActiveplayer()->getActivestack();
+ if (s)
+ {
+ s->getPath()->recalculate(s);
+ redraw();
+ update_control_panel();
+ }
+}
+
+void Game::on_city_fight_finished(City *city, Fight::Result result)
+{
+ if (result != Fight::ATTACKER_WON)
+ {
+ // we didn't suceed in defeating the defenders
+ //if this is a neutral city, and we're playing with
+ //active neutral cities, AND it hasn't already been attacked
+ //then it's production gets turned on
+ Player *neu = city->getOwner(); //neutral player
+ if (GameScenario::s_neutral_cities == GameParameters::ACTIVE &&
+ neu == Playerlist::getInstance()->getNeutral() &&
+ city->getActiveProductionSlot() == -1)
+ {
+ //great, then let's turn on the production.
+ //well, we already made a unit, and we want to produce more
+ //of it.
+ Stack *o = GameMap::getStacks(city->getPos())->getFriendlyStack(neu);
+ if (o)
+ {
+ int army_type = o->getStrongestArmy()->getTypeId();
+ for (guint32 i = 0; i < city->getMaxNoOfProductionBases(); i++)
+ {
+ if (city->getArmytype(i) == army_type)
+ {
+ // hey, we found the droid we were looking for
+ city->setActiveProductionSlot(i);
+ break;
+ }
+ }
+ }
+ }
+ }
+ return;
+}
+
+bool Game::recruitHero(HeroProto *hero, City *city, int gold)
+{
+ bool retval;
+ if (city->getOwner()->isComputer())
+ retval = city->getOwner()->chooseHero (hero, city, gold);
+ else
+ {
+ retval = hero_offers_service.emit (city->getOwner(), hero, city, gold);
+ if (d_gameScenario->getRound() == 1)
+ city_visited.emit(city);
+ }
+ return retval;
+}
+
+bool Game::saveTurnFile(std::string turnfile)
+{
+ bool broken;
+ //trigger the GameServer to spit out a set of networkactions and networkhistory events for the active player, into a file.
+ if (d_gameScenario->getPlayMode() == GameScenario::PLAY_BY_MAIL)
+ PbmGameServer::getInstance()->endTurn(turnfile, broken);
+ return true;
+}
+
+void Game::inhibitAutosaveRemoval(bool inhibit)
+{
+ if (d_gameScenario)
+ d_gameScenario->inhibitAutosaveRemoval(inhibit);
+}
+
+void Game::endOfGameRoaming(Player *winner)
+{
+ Playerlist* pl = Playerlist::getInstance();
+ pl->setWinningPlayer(winner);
+
+ winner->immobilize();
+ d_gameScenario->s_see_opponents_stacks = true;
+ d_gameScenario->s_see_opponents_production = true;
+ bigmap->d_see_opponents_stacks = true;
+ bigmap->d_see_opponents_production = true;
+ center_view_on_city();
+
+ unlock_inputs();
+
+ update_sidebar_stats();
+ update_stack_info();
+ update_control_panel();
+ redraw();
+}
+
+void Game::stack_arrives_on_tile(Stack *stack, Vector<int> tile)
+{
+ StackTile *stile = GameMap::getInstance()->getTile(tile)->getStacks();
+ stile->arriving(stack);
+}
+
+void Game::stack_leaves_tile(Stack *stack, Vector<int> tile)
+{
+ StackTile *stile = GameMap::getInstance()->getTile(tile)->getStacks();
+ bool left = stile->leaving(stack);
+ if (left == false)
+ {
+ if (stack == NULL)
+ {
+ printf("stack is %p\n", stack);
+ printf("WTFFF!!!!!!!!!!!!!!!!!!!!\n");
+ return;
+ }
+ }
+}
+
+void Game::stack_searches_ruin(Ruin *ruin, Stack *stack)
+{
+ search_stack(stack);
+}
+
+void Game::stack_searches_temple(Temple *temple, Stack *stack)
+{
+ search_stack(stack);
+}