/*
* Hedgewars, a free turn based strategy game
* Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
*
* 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; version 2 of the License
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <QGridLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QComboBox>
#include <QLabel>
#include <QLineEdit>
#include <QTabWidget>
#include <QGroupBox>
#include <QToolBox>
#include <QMessageBox>
#include <QStandardItemModel>
#include <QDebug>
#include "SquareLabel.h"
#include "HWApplication.h"
#include "keybinder.h"
#include "DataManager.h"
#include "hatbutton.h"
#include "pageeditteam.h"
QLayout * PageEditTeam::bodyLayoutDefinition()
{
QGridLayout * pageLayout = new QGridLayout();
tbw = new QTabWidget();
QWidget * page1 = new QWidget(this);
binder = new KeyBinder(this, tr("Select an action to choose a custom key bind for this team"), tr("Use my default"), tr("Reset all binds"));
connect(binder, SIGNAL(resetAllBinds()), this, SLOT(resetAllBinds()));
tbw->addTab(page1, tr("General"));
tbw->addTab(binder, tr("Custom Controls"));
pageLayout->addWidget(tbw, 0, 0, 1, 3);
QHBoxLayout * page1Layout = new QHBoxLayout(page1);
page1Layout->setAlignment(Qt::AlignTop);
// ====== Page 1 ======
QVBoxLayout * vbox1 = new QVBoxLayout();
QVBoxLayout * vbox2 = new QVBoxLayout();
page1Layout->addLayout(vbox1);
page1Layout->addLayout(vbox2);
GBoxHedgehogs = new QGroupBox(this);
GBoxHedgehogs->setTitle(QGroupBox::tr("Team Members"));
GBoxHedgehogs->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QGridLayout * GBHLayout = new QGridLayout(GBoxHedgehogs);
GBHLayout->addWidget(new QLabel(tr("Hat")), 0, 0);
GBHLayout->addWidget(new QLabel(tr("Name")), 0, 1);
for(int i = 0; i < HEDGEHOGS_PER_TEAM; i++)
{
HHHats[i] = new HatButton(GBoxHedgehogs);
GBHLayout->addWidget(HHHats[i], i + 1, 0);
HHNameEdit[i] = new QLineEdit(GBoxHedgehogs);
HHNameEdit[i]->setMaxLength(64);
HHNameEdit[i]->setMinimumWidth(120);
HHNameEdit[i]->setFixedHeight(36);
HHNameEdit[i]->setWhatsThis(tr("This hedgehog's name"));
HHNameEdit[i]->setStyleSheet("padding: 6px;");
GBHLayout->addWidget(HHNameEdit[i], i + 1, 1, 1, 2);
btnRandomHogName[i] = addButton(":/res/dice.png", GBHLayout, i + 1, 5, 1, 1, true);
btnRandomHogName[i]->setFixedHeight(HHNameEdit[i]->height());
btnRandomHogName[i]->setWhatsThis(tr("Randomize this hedgehog's name"));
}
btnRandomHats = new QPushButton();
btnRandomHats->setText(tr("Random Hats"));
btnRandomHats->setStyleSheet("padding: 6px 10px;");
GBHLayout->addWidget(btnRandomHats, 9, 1, 1, 1, Qt::AlignCenter);
btnRandomHats->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
btnRandomNames = new QPushButton();
btnRandomNames->setText(tr("Random Names"));
btnRandomNames->setStyleSheet("padding: 6px 10px;");
GBHLayout->addWidget(btnRandomNames, 9, 2, 1, 1, Qt::AlignCenter);
btnRandomNames->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
vbox1->addWidget(GBoxHedgehogs);
btnRandomTeam = new QPushButton();
btnRandomTeam->setText(tr("Random Team"));
btnRandomTeam->setStyleSheet("padding: 6px 10px;");
btnRandomTeam->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
vbox1->addWidget(btnRandomTeam, 0, Qt::AlignCenter);
GBoxTeam = new QGroupBox(this);
GBoxTeam->setTitle(QGroupBox::tr("Team Settings"));
GBoxTeam->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
QGridLayout * GBTLayout = new QGridLayout(GBoxTeam);
QLabel * tmpLabel = new QLabel(GBoxTeam);
tmpLabel->setText(QLabel::tr("Name"));
GBTLayout->addWidget(tmpLabel, 0, 0);
tmpLabel = new QLabel(GBoxTeam);
tmpLabel->setText(QLabel::tr("Player"));
GBTLayout->addWidget(tmpLabel, 1, 0);
tmpLabel = new QLabel(GBoxTeam);
tmpLabel->setText(QLabel::tr("Grave"));
GBTLayout->addWidget(tmpLabel, 2, 0);
tmpLabel = new QLabel(GBoxTeam);
tmpLabel->setText(QLabel::tr("Flag"));
GBTLayout->addWidget(tmpLabel, 3, 0);
tmpLabel = new QLabel(GBoxTeam);
tmpLabel->setText(QLabel::tr("Voice"));
GBTLayout->addWidget(tmpLabel, 4, 0);
TeamNameEdit = new QLineEdit(GBoxTeam);
TeamNameEdit->setMaxLength(64);
TeamNameEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
GBTLayout->addWidget(TeamNameEdit, 0, 1, 1, 2);
vbox2->addWidget(GBoxTeam);
CBTeamLvl = new QComboBox(GBoxTeam);
CBTeamLvl->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
CBTeamLvl->setIconSize(QSize(32, 32));
CBTeamLvl->addItem(QIcon(":/res/botlevels/small0.png"), QComboBox::tr("Human"));
for(int i = 5; i > 0; i--)
CBTeamLvl->addItem(
QIcon(QString(":/res/botlevels/small%1.png").arg(6 - i)),
QComboBox::tr("Computer (Level %1)").arg(i)
);
CBTeamLvl->setFixedHeight(38);
GBTLayout->addWidget(CBTeamLvl, 1, 1, 1, 2);
CBGrave = new QComboBox(GBoxTeam);
CBGrave->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
CBGrave->setMaxCount(65535);
CBGrave->setMaxVisibleItems(20);
CBGrave->setIconSize(QSize(32, 32));
CBGrave->setFixedHeight(44);
GBTLayout->addWidget(CBGrave, 2, 1, 1, 2);
// Player flags, combobox to select flag
CBFlag = new QComboBox(GBoxTeam);
CBFlag->setMaxCount(65535);
CBFlag->setMaxVisibleItems(50);
CBFlag->setIconSize(QSize(22, 15));
CBFlag->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
GBTLayout->addWidget(CBFlag, 3, 1, 1, 2);
// CPU level flag. Static image, only displayed when computer player is selected
QImage imgBotlevels = QImage("physfs://Graphics/botlevels.png");
int botlevelOffsets[5]= { 19, 14, 10, 6, 0 };
for(int i=0; i<5; i++) {
QImage imgCPU = QImage("physfs://Graphics/Flags/cpu.png");
QPainter painter(&imgCPU);
painter.drawImage(botlevelOffsets[i], 0, imgBotlevels, botlevelOffsets[i]);
pixCPU[i] = QPixmap::fromImage(imgCPU);
}
QHBoxLayout* hboxCPU = new QHBoxLayout();
hboxCPU->setContentsMargins(0, 0, 0, 0);
hboxCPUWidget = new QWidget();
hboxCPUWidget->setLayout(hboxCPU);
CPUFlag = new QLabel();
CPUFlag->setPixmap(pixCPU[0]);
CPUFlag->setFixedHeight(38);
hboxCPU->addWidget(CPUFlag);
CPUFlagLabel = new QLabel("CPU");
CPUFlagLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
hboxCPU->addWidget(CPUFlagLabel);
hboxCPUWidget->setHidden(true);
GBTLayout->addWidget(hboxCPUWidget, 3, 1, 1, 1);
btnRandomTeamName = addButton(":/res/dice.png", GBTLayout, 0, 3, 1, 1, true);
btnRandomTeamName->setWhatsThis(tr("Randomize the team name"));
btnRandomGrave = addButton(":/res/dice.png", GBTLayout, 2, 3, 1, 1, true);
btnRandomGrave->setWhatsThis(tr("Randomize the grave"));
btnRandomFlag = addButton(":/res/dice.png", GBTLayout, 3, 3, 1, 1, true);
btnRandomFlag->setWhatsThis(tr("Randomize the flag"));
CBVoicepack = new QComboBox(GBoxTeam);
CBVoicepack->setMaxVisibleItems(50);
CBVoicepack->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
GBTLayout->addWidget(CBVoicepack, 4, 1, 1, 1);
btnTestSound = addSoundlessButton(":/res/PlaySound.png", GBTLayout, 4, 2, 1, 1, true);
btnTestSound->setWhatsThis(tr("Play a random example of this voice"));
btnRandomVoice = addButton(":/res/dice.png", GBTLayout, 4, 3, 1, 1, true);
btnRandomVoice->setWhatsThis(tr("Randomize the voice"));
GBoxFort = new QGroupBox(this);
GBoxFort->setTitle(QGroupBox::tr("Fort"));
QGridLayout * GBFLayout = new QGridLayout(GBoxFort);
CBFort = new QComboBox(GBoxFort);
CBFort->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
CBFort->setMaxVisibleItems(25);
CBFort->setMaxCount(65535);
GBFLayout->addWidget(CBFort, 0, 0);
btnRandomFort = addButton(":/res/dice.png", GBFLayout, 0, 2, 1, 1, true);
btnRandomFort->setWhatsThis(tr("Randomize the fort"));
FortPreview = new SquareLabel(GBoxFort);
FortPreview->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
FortPreview->setMinimumSize(128, 128);
FortPreview->setPixmap(QPixmap());
// perhaps due to handling its own paintevents, SquareLabel doesn't play nice with the stars
//FortPreview->setAttribute(Qt::WA_PaintOnScreen, true);
GBFLayout->addWidget(FortPreview, 1, 0, 1, 2);
vbox2->addWidget(GBoxFort);
vbox1->addStretch();
vbox2->addStretch();
return pageLayout;
}
QLayout * PageEditTeam::footerLayoutDefinition()
{
return NULL;
}
void PageEditTeam::connectSignals()
{
connect(this, SIGNAL(pageLeave()), this, SLOT(saveTeam()));
signalMapper1 = new QSignalMapper(this);
signalMapper2 = new QSignalMapper(this);
connect(signalMapper1, SIGNAL(mapped(int)), this, SLOT(fixHHname(int)));
connect(signalMapper2, SIGNAL(mapped(int)), this, SLOT(setRandomHogName(int)));
for(int i = 0; i < HEDGEHOGS_PER_TEAM; i++)
{
connect(HHNameEdit[i], SIGNAL(editingFinished()), signalMapper1, SLOT(map()));
signalMapper1->setMapping(HHNameEdit[i], i);
connect(btnRandomHogName[i], SIGNAL(clicked()), signalMapper2, SLOT(map()));
signalMapper2->setMapping(btnRandomHogName[i], i);
}
connect(btnRandomTeam, SIGNAL(clicked()), this, SLOT(setRandomTeam()));
connect(btnRandomNames, SIGNAL(clicked()), this, SLOT(setRandomHogNames()));
connect(btnRandomHats, SIGNAL(clicked()), this, SLOT(setRandomHats()));
connect(CBTeamLvl, SIGNAL(currentIndexChanged(const int)), this, SLOT(CBTeamLvl_activated(const int)));
connect(btnRandomTeamName, SIGNAL(clicked()), this, SLOT(setRandomTeamName()));
connect(btnRandomGrave, SIGNAL(clicked()), this, SLOT(setRandomGrave()));
connect(btnRandomFlag, SIGNAL(clicked()), this, SLOT(setRandomFlag()));
connect(btnRandomVoice, SIGNAL(clicked()), this, SLOT(setRandomVoice()));
connect(btnRandomFort, SIGNAL(clicked()), this, SLOT(setRandomFort()));
connect(btnTestSound, SIGNAL(clicked()), this, SLOT(testSound()));
connect(CBFort, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(CBFort_activated(const QString &)));
}
PageEditTeam::PageEditTeam(QWidget* parent) :
AbstractPage(parent)
{
initPage();
m_playerHash = "0000000000000000000000000000000000000000";
m_loaded = false;
}
void PageEditTeam::lazyLoad()
{
if(m_loaded) return;
m_loaded = true;
qDebug("[LAZINESS] PageEditTeam::lazyLoad()");
HatModel * hatsModel = DataManager::instance().hatModel();
for(int i = 0; i < HEDGEHOGS_PER_TEAM; i++)
HHHats[i]->setModel(hatsModel);
QRegExp pngSuffix("\\.png$");
DataManager & dataMgr = DataManager::instance();
QStringList list;
// voicepacks
list = dataMgr.entryList("Sounds/voices",
QDir::AllDirs | QDir::NoDotAndDotDot);
CBVoicepack->addItems(list);
// forts
list = dataMgr.entryList("Forts", QDir::Files, QStringList("*L.png"));
list.replaceInStrings(QRegExp("L\\.png$"), "");
CBFort->addItems(list);
// graves
list =
dataMgr.entryList("Graphics/Graves", QDir::Files, QStringList("*.png"));
foreach (QString file, list)
{
QPixmap pix("physfs://Graphics/Graves/" + file);
if ((pix.height() > 32) || pix.width() > 32)
pix = pix.copy(0, 0, 32, 32);
QIcon icon(pix);
QString grave = file.remove(pngSuffix);
CBGrave->addItem(icon, grave);
}
// flags
list =
dataMgr.entryList("Graphics/Flags", QDir::Files, QStringList("*.png"));
// skip cpu and hedgewars flags
int idx = list.indexOf("cpu.png");
if (idx >= 0)
list.removeAt(idx);
idx = list.indexOf("cpu_plain.png");
if (idx >= 0)
list.removeAt(idx);
idx = list.indexOf("hedgewars.png");
if (idx >= 0)
list.removeAt(idx);
// add the default flag
QPixmap hwFlag("physfs://Graphics/Flags/hedgewars.png");
CBFlag->addItem(QIcon(hwFlag.copy(0, 0, 22, 15)), "Hedgewars", "hedgewars");
// add seperator after
CBFlag->insertSeparator(1);
int insertAt = 2; // insert country flags after Hedgewars flag and seperator
// add all country flags
foreach (const QString & file, list)
{
QIcon icon(QPixmap("physfs://Graphics/Flags/" + file));
QString flag = QString(file).remove(pngSuffix);
bool isCountryFlag = !file.startsWith("cm_");
if (isCountryFlag)
{
CBFlag->insertItem(insertAt, icon, flag.replace("_", " "), flag);
insertAt++;
}
else // append community flags at end
CBFlag->addItem(icon, flag.replace("cm_", QComboBox::tr("Community") + ": "), flag);
}
// add separator between country flags and community flags
CBFlag->insertSeparator(insertAt);
}
void PageEditTeam::fixHHname(int idx)
{
HHNameEdit[idx]->setText(HHNameEdit[idx]->text().trimmed());
if (HHNameEdit[idx]->text().isEmpty())
HHNameEdit[idx]->setText(QLineEdit::tr("hedgehog %1").arg(idx+1));
}
void PageEditTeam::CBFort_activated(const QString & fortname)
{
QPixmap pix("physfs://Forts/" + fortname + "L.png");
FortPreview->setPixmap(pix);
}
void PageEditTeam::CBTeamLvl_activated(const int index)
{
CBFlag->setHidden(index != 0);
btnRandomFlag->setHidden(index != 0);
if(index > 0)
{
int cpuLevel = 6 - index;
CPUFlag->setPixmap(pixCPU[cpuLevel - 1]);
//: Name of a flag for computer-controlled enemies. %1 is replaced with the computer level
CPUFlagLabel->setText(tr("CPU %1").arg(cpuLevel));
}
hboxCPUWidget->setHidden(index == 0);
}
void PageEditTeam::testSound()
{
DataManager & dataMgr = DataManager::instance();
QString voiceDir = QString("Sounds/voices/") + CBVoicepack->currentText();
QStringList list = dataMgr.entryList(
voiceDir,
QDir::Files,
QStringList() <<
"Illgetyou.ogg" <<
"Incoming.ogg" <<
"Stupid.ogg" <<
"Coward.ogg" <<
"Firstblood.ogg"
);
if (!list.isEmpty())
SDLInteraction::instance().playSoundFile("/" + voiceDir + "/" +
list[rand() % list.size()]);
}
void PageEditTeam::createTeam(const QString & name, const QString & playerHash)
{
m_playerHash = playerHash;
lazyLoad();
// Mostly create a default team, with 2 important exceptions:
HWTeam newTeam(name);
// Randomize grave to make it less likely that default teams have equal graves (important for resurrector)
HWNamegen::teamRandomGrave(newTeam, false);
// Randomize fort for greater variety in fort mode with default teams
HWNamegen::teamRandomFort(newTeam, false);
// DLC forts and graves intentionally filtered out to prevent desyncs and missing grave error
// TODO: Remove DLC filter as soon it is not needed anymore
loadTeam(newTeam);
}
void PageEditTeam::editTeam(const QString & name, const QString & playerHash)
{
m_playerHash = playerHash;
lazyLoad();
HWTeam team(name);
team.loadFromFile();
loadTeam(team);
}
void PageEditTeam::deleteTeam(const QString & name)
{
QMessageBox reallyDeleteMsg(this);
reallyDeleteMsg.setIcon(QMessageBox::Question);
reallyDeleteMsg.setWindowTitle(QMessageBox::tr("Teams - Are you sure?"));
reallyDeleteMsg.setText(QMessageBox::tr("Do you really want to delete the team '%1'?").arg(name));
reallyDeleteMsg.setWindowModality(Qt::WindowModal);
reallyDeleteMsg.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
if (reallyDeleteMsg.exec() == QMessageBox::Ok)
HWTeam(name).deleteFile();
}
void PageEditTeam::setRandomTeam()
{
HWTeam team = data();
HWNamegen::teamRandomEverything(team, HWNamegen::rtmEverything);
loadTeam(team);
}
void PageEditTeam::setRandomHogNames()
{
HWTeam team = data();
HWNamegen::teamRandomEverything(team, HWNamegen::rtmHogNames);
loadTeam(team);
}
void PageEditTeam::setRandomHats()
{
HWTeam team = data();
HWNamegen::teamRandomHats(team);
loadTeam(team);
}
void PageEditTeam::setRandomHogName(int hh_index)
{
HWTeam team = data();
HWNamegen::teamRandomHogName(team,hh_index);
loadTeam(team);
}
void PageEditTeam::setRandomTeamName()
{
HWTeam team = data();
HWNamegen::teamRandomTeamName(team);
loadTeam(team);
}
void PageEditTeam::setRandomGrave()
{
HWTeam team = data();
HWNamegen::teamRandomGrave(team);
loadTeam(team);
}
void PageEditTeam::setRandomFlag()
{
HWTeam team = data();
HWNamegen::teamRandomFlag(team);
loadTeam(team);
}
void PageEditTeam::setRandomVoice()
{
HWTeam team = data();
HWNamegen::teamRandomVoice(team);
loadTeam(team);
}
void PageEditTeam::setRandomFort()
{
HWTeam team = data();
HWNamegen::teamRandomFort(team);
loadTeam(team);
}
void PageEditTeam::loadTeam(const HWTeam & team)
{
tbw->setCurrentIndex(0);
binder->resetInterface();
TeamNameEdit->setText(team.name());
CBTeamLvl->setCurrentIndex(team.difficulty());
for(int i = 0; i < HEDGEHOGS_PER_TEAM; i++)
{
HWHog hh = team.hedgehog(i);
HHNameEdit[i]->setText(hh.Name);
if (hh.Hat.startsWith("Reserved"))
hh.Hat = "Reserved "+hh.Hat.remove(0,40);
HHHats[i]->setCurrentHat(hh.Hat);
}
CBGrave->setCurrentIndex(CBGrave->findText(team.grave()));
CBFlag->setCurrentIndex(CBFlag->findData(team.flag()));
CBFort->setCurrentIndex(CBFort->findText(team.fort()));
CBVoicepack->setCurrentIndex(CBVoicepack->findText(team.voicepack()));
QStandardItemModel * binds = DataManager::instance().bindsModel();
for(int i = 0; i < BINDS_NUMBER; i++)
{
if (team.keyBind(i).isEmpty()) continue;
QModelIndexList mdl = binds->match(binds->index(0, 0), Qt::UserRole + 1, team.keyBind(i), 1, Qt::MatchExactly);
if(mdl.size() == 1)
binder->setBindIndex(i, mdl[0].row());
else
qDebug() << "Binds: cannot find" << team.keyBind(i);
}
}
HWTeam PageEditTeam::data()
{
HWTeam team(TeamNameEdit->text());
team.setDifficulty(CBTeamLvl->currentIndex());
for(int i = 0; i < HEDGEHOGS_PER_TEAM; i++)
{
HWHog hh;
hh.Name = HHNameEdit[i]->text();
hh.Hat = HHHats[i]->currentHat();
if (hh.Hat.startsWith("Reserved"))
hh.Hat = "Reserved"+m_playerHash+hh.Hat.remove(0,9);
team.setHedgehog(i,hh);
}
team.setGrave(CBGrave->currentText());
team.setFort(CBFort->currentText());
team.setVoicepack(CBVoicepack->currentText());
team.setFlag(CBFlag->itemData(CBFlag->currentIndex()).toString());
QStandardItemModel * binds = DataManager::instance().bindsModel();
for(int i = 0; i < BINDS_NUMBER; i++)
{
team.bindKey(i, binds->index(binder->bindIndex(i), 0).data(Qt::UserRole + 1).toString());
}
return team;
}
void PageEditTeam::saveTeam()
{
data().saveToFile();
}
// When the "Use default for all binds" is pressed...
void PageEditTeam::resetAllBinds()
{
for (int i = 0; i < BINDS_NUMBER; i++)
binder->setBindIndex(i, 0);
}