/* * Copyright 2007-2009 Ben Boeckel * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ // Header include #include "Arena.h" // Sigencore includes #include "Client.h" #include "Player.h" #include "RunScript.h" #include "Team.h" // Sigscript includes #include #include #include #include #include #include // Sigcore includes #include // KDE includes #include #include #include // Qt includes #include #include using namespace Sigcore; using namespace Sigscript; using namespace Sigencore; const int Arena::Spectator = -1; const int Arena::Fighters = -2; const int Arena::AllTeams = -3; const int Arena::NoTeam = -4; TeamMember::RequestedAction Sigencore::requestDecision(TeamMember* teamMember) { return TeamMember::RequestedAction(teamMember, QtConcurrent::run(decision, teamMember->team()->player(), teamMember)); } TeamMember::Action Sigencore::decision(Player* player, TeamMember* teamMember) { return player->requestAction(teamMember); } Arena::Arena(GameWrapper* game, Config* parent) : Config(parent), m_game(game), m_state(Setup), m_id(QUuid::createUuid()) { connect(this, SIGNAL(battleEnded()), SLOT(cleanUp())); m_actions = new Kross::ActionCollection(QString("arena-%1").arg(m_id.toString()), Kross::Manager::self().actionCollection()); } Arena::~Arena() { delete m_actions; } GameWrapper* Arena::game() const { return m_game; } QList Arena::active(const int team) const { QList active; QList clients; if ((team == Fighters) || (team == AllTeams)) clients = m_teams.keys(); else clients = m_teams.keys(team); foreach (Client* client, clients) { Player* player = qobject_cast(client); if (player) active += player->active(); } return active; } QList Arena::active(Client* client) const { QList active; Player* player = qobject_cast(client); if (player) active += player->active(); return active; } QList Arena::teamPlayers(const int team) const { switch (team) { case Fighters: { QList team = m_teams.keys(); for (QMutableListIterator i(team); i.hasNext(); i.next()) { if (m_teams[i.value()] < 0) i.remove(); } return team; } case AllTeams: return m_teams.keys(); default: return m_teams.keys(team); } } Player* Arena::player(TeamMember* teamMember) const { Team* team = qobject_cast(teamMember->containment()); if (team) return team->player(); return NULL; } int Arena::team(TeamMember* teamMember) const { return team(player(teamMember)); } int Arena::team(Client* client) const { if (m_teams.contains(client)) return m_teams[client]; return NoTeam; } int Arena::numTeams() const { QSet teams = m_teams.values().toSet(); teams.remove(Spectator); return teams.size(); } int Arena::numPlayers() const { QList clients = m_teams.keys(); int players = 0; foreach (Client* client, clients) players += !!qobject_cast(client); return players; } int Arena::numTeamMembers() const { QList clients = m_teams.keys(); int teamMembers = 0; foreach (Client* client, clients) { Player* player = qobject_cast(client); if (player) teamMembers += !!player->active().size(); } return teamMembers; } Arena::State Arena::state() const { return m_state; } QList Arena::weathers() const { return m_weathers.keys(); } void Arena::setWeather(TeamMember* teamMember, WeatherWrapper* weather) { // FIXME: Loses memory if a weather is started again ObjectMap objects; objects["arena"] = this; objects["weather"] = weather; objects["game"] = m_game; objects["owner"] = teamMember; objects["client"] = player(teamMember); Kross::Action* action = runScript(QString("arena-weather-%1").arg(QUuid::createUuid().toString()), weather->script(), objects, m_actions); m_weathers[weather] = action; action->trigger(); emit(weatherStarted(teamMember, weather)); } bool Arena::unsetWeather(WeatherWrapper* weather) { if (m_weathers.contains(weather)) { delete m_weathers[weather]; m_weathers.remove(weather); emit(weatherEnded(weather)); return true; } return false; } bool Arena::addClient(Client* client, const int team) { if (0 <= team) { Player* player = qobject_cast(client); if ((m_state != Setup) || !player || !isTeamAllowed(player->team()) || (active(Fighters).size() < m_game->rules()->maxPlayers())) return false; } if (!client->enterArena(this)) return false; m_teams[client] = team; emit(clientAdded(client, team)); return true; } void Arena::removeClient(Client* client) { Player* player = qobject_cast(client); if (player && m_spoils.contains(player)) { Spoil spoil = m_spoils[player]; const int net = spoil.first - spoil.second; player->giveMoney(net); m_spoils.remove(player); } if (player && (0 <= m_teams[player])) player->exitArena(); const int team = m_teams[client]; m_teams.remove(client); emit(clientRemoved(client, team)); } void Arena::registerScript(const Script& script) { if (!script.script().isEmpty()) { Kross::Action* action = new Kross::Action(m_actions, QUuid::createUuid().toString()); action->setInterpreter(script.interpreter()); action->setCode(script.script().toUtf8()); action->addObject(this, "arena"); action->trigger(); } } void Arena::cleanUp() { emit(battleAboutToEnd()); emit(aboutToClearActions()); m_state = Completed; QList actions = m_actions->actions(); foreach (Kross::Action* action, actions) m_actions->removeAction(action); distributeWinnings(); QList clients = m_teams.keys(); foreach (Client* client, clients) client->exitArena(); emit(battleEnded()); emit(cleanupArena()); } void Arena::handleAction(TeamMember* teamMember, TeamMember::Action action) { TeamMember::ActionData data = action.second; switch (action.first) { case TeamMember::Invalid: break; case TeamMember::Timeout: // TODO: Try to save the player? break; case TeamMember::Attack: { MoveWrapper* move = game()->move(data.first.toInt()); if (move) { const Script script = move->battleScript(); if (!script.script().isEmpty()) { ObjectMap objects; objects["arena"] = this; objects["owner"] = teamMember; objects["client"] = player(teamMember); objects["game"] = m_game; for (int i = 0; i < data.second.size(); ++i) objects[QString("target%1").arg(i)] = findMember(data.second[i]); runScript(QUuid::createUuid().toString(), script, objects, m_actions)->trigger(); } } break; } case TeamMember::Item: { ItemWrapper* item = m_game->item(data.first.toInt()); if (item) { const Script script = item->script(); if (!script.script().isEmpty()) { ObjectMap objects; objects["arena"] = this; objects["client"] = player(teamMember); objects["game"] = m_game; for (int i = 0; i < data.second.size(); ++i) objects[QString("target%1").arg(i)] = findMember(data.second[i]); runScript(QUuid::createUuid().toString(), script, objects, m_actions)->trigger(); } } break; } case TeamMember::Switch: { Player* self = player(teamMember); if (self) self->switchOut(teamMember, self->findMember(data.second[0])); break; } case TeamMember::Run: { Player* self = player(teamMember); const int numFlee = self->active().size(); Fraction selfRunChance; self->valueOfType("runchance", &selfRunChance); bool canRun = true; self->valueOfType("canflee", &canRun); QList clients = teamPlayers(Fighters); foreach (Client* client, clients) { if (!canRun) break; Player* player = qobject_cast(client); if (!player || (self == player) || (m_teams[self] == m_teams[player])) continue; Fraction playerRunChance; player->valueOfType("fleechance", &playerRunChance); if (!playerRunChance.poll()) canRun = false; foreach (TeamMember* active, player->active()) { for (int i = 0; canRun && (i < numFlee); ++i) { if (!active->species()->fleeChance().poll()) canRun = false; } } } if (canRun && selfRunChance.poll()) self->exitArena(); break; } default: { ObjectMap objects; objects["arena"] = this; objects["owner"] = teamMember; objects["client"] = player(teamMember); objects["game"] = m_game; for (int i = 0; i < data.second.size(); ++i) objects[QString("target%1").arg(i)] = findMember(data.second[i]); globalScript(m_game, QString("battleaction-handler-%1").arg(action.first), QUuid::createUuid().toString(), objects, m_actions)->trigger(); break; } } } void Arena::setupBattle() { emit(battleAboutToStart()); QList clients = m_teams.keys(); foreach (Client* client, clients) { Player* player = qobject_cast(client); if (player) player->enterArena(this); } m_state = InProgress; emit(battleStarted()); } void Arena::distributeWinnings() { QList clients = m_teams.keys(); foreach (Client* client, clients) { Player* player = qobject_cast(client); if (!player || !m_spoils.contains(player)) continue; Spoil spoil = m_spoils[player]; const int net = spoil.first - spoil.second; player->giveMoney(net); } } void Arena::checkForLosers() { QList clients = m_teams.keys(); QList roundLosers; QMap pot; QList teams; QSet activeTeams; foreach (Client* client, clients) { Player* player = qobject_cast(client); if (player && !m_losers.contains(player)) { activeTeams.insert(m_teams[player]); if (player->isKnockedOut()) roundLosers.append(player); } } foreach (Player* loser, roundLosers) { const int money = loser->money(); Fraction lossFactor(1, 2); m_game->valueOfType("lossFactor", &lossFactor); pot[m_teams[loser]] += money * lossFactor; m_spoils[loser].second = money * lossFactor; } teams = pot.keys(); foreach (int teamPot, teams) { foreach (int team, activeTeams) { if (team == teamPot) continue; QList members = teamPlayers(team); QList survivors; foreach (Client* member, members) { Player* player = qobject_cast(member); m_spoils[player].first += (pot[teamPot] >> 1) / (activeTeams.size() - 1); if (!player->isKnockedOut()) survivors.append(player); } foreach (Player* survivor, survivors) m_spoils[survivor].first += (pot[teamPot] >> 1) / survivors.size(); } } m_losers += roundLosers; if (numTeams() == 1) cleanUp(); } TeamMember* Arena::findMember(const QUuid& id) { QList clients = m_teams.keys(); foreach (Client* client, clients) { Player* player = qobject_cast(client); if (!player) continue; TeamMember* member = player->findMember(id); if (member) return member; } return NULL; }