/* * 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" // Sigscript includes #include "../sigscript/ItemWrapper.h" #include "../sigscript/MoveWrapper.h" #include "../sigscript/RulesWrapper.h" #include "../sigscript/SigmodWrapper.h" #include "../sigscript/SpeciesWrapper.h" // Sigcore includes #include "../sigcore/Script.h" // KDE includes #include #include #include // Qt includes #include #include const int Sigencore::Arena::Spectator = -1; const int Sigencore::Arena::Fighters = -2; const int Sigencore::Arena::AllTeams = -3; Sigencore::TeamMember::RequestedAction Sigencore::requestDecision(TeamMember* teamMember) { return TeamMember::RequestedAction(teamMember, QtConcurrent::run(decision, teamMember)); } Sigencore::TeamMember::Action Sigencore::decision(TeamMember* teamMember) { return teamMember->requestAction(); } Sigencore::Arena::Arena(Sigscript::SigmodWrapper* sigmod, const bool isWild, Sigscript::Config* parent) : Sigscript::Config(parent), m_sigmod(sigmod), m_isWild(isWild), m_state(Setup), m_id(QUuid::createUuid()) { connect(this, SIGNAL(battleEnd()), SLOT(cleanUp())); m_actions = new Kross::ActionCollection(QString("arena-%1").arg(m_id.toString()), Kross::Manager::self().actionCollection()); } Sigencore::Arena::~Arena() { delete m_actions; } QList Sigencore::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 Sigencore::Arena::teamMembers(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); } } int Sigencore::Arena::numTeams() const { QSet teams = m_teams.values().toSet(); teams.remove(Spectator); return teams.size(); } int Sigencore::Arena::numActiveTeams() const { int activeTeams = 0; QSet teams = m_teams.values().toSet(); foreach (int team, teams) activeTeams += !active(team).isEmpty(); return activeTeams; } Sigencore::Arena::State Sigencore::Arena::state() const { return m_state; } Sigscript::SigmodWrapper* Sigencore::Arena::sigmod() const { return m_sigmod; } bool Sigencore::Arena::addClient(Client* client, const int team) { if ((team < 0) || ((m_state == Setup) && qobject_cast(client) && (teamMembers(Fighters).size() < m_sigmod->rules()->maxPlayers()))) { m_teams[client] = team; emit(clientAdded(client, team)); return true; } return false; } void Sigencore::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; if (net < 0) player->takeMoney(-net); else if (0 < net) player->giveMoney(net); m_spoils.remove(player); } const int team = m_teams[client]; m_teams.remove(client); emit(clientRemoved(client, team)); } void Sigencore::Arena::registerScript(const Sigcore::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 Sigencore::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(); } void Sigencore::Arena::handleAction(TeamMember* teamMember, TeamMember::Action action) { TeamMember::ActionData data = action.second; switch (action.first) { case TeamMember::Attack: { Sigscript::MoveWrapper* move = sigmod()->move(data.first.toInt()); if (move) { const Sigcore::Script script = move->battleScript(); if (!script.script().isEmpty()) { ObjectMap objects; objects["arena"] = this; objects["owner"] = teamMember; objects["client"] = teamMember->client(); objects["sigmod"] = m_sigmod; 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: { Sigscript::ItemWrapper* item = sigmod()->item(data.first.toInt()); if (item) { const Sigcore::Script script = item->script(); if (!script.script().isEmpty()) { ObjectMap objects; objects["arena"] = this; objects["client"] = teamMember->client(); objects["sigmod"] = m_sigmod; 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* player = qobject_cast(teamMember->client()); if (player) player->switchOut(teamMember, player->findMember(data.second[0])); break; } case TeamMember::Run: { if (!m_isWild) break; Player* self = qobject_cast(teamMember->client()); const int numFlee = self->active().size(); bool canRun = true; QList clients = m_teams.keys(); foreach (Client* client, clients) { Player* player = qobject_cast(client); if (!player) continue; if (self == player) continue; foreach (TeamMember* active, player->active()) { for (int i = 0; canRun && (i < numFlee); ++i) { if (!active->species()->fleeChance().poll()) canRun = false; } } if (!canRun) break; } if (canRun) self->exitArena(); break; } default: break; } } void Sigencore::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 Sigencore::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; if (net < 0) player->takeMoney(-net); else if (0 < net) player->giveMoney(net); } } void Sigencore::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(); Sigcore::Fraction lossFactor(1, 2); m_sigmod->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 = teamMembers(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 (numActiveTeams() == 1) cleanUp(); } Sigencore::TeamMember* Sigencore::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; }