--- a/QTfrontend/game.cpp Wed May 16 18:22:28 2018 +0200
+++ b/QTfrontend/game.cpp Wed Jul 31 23:14:27 2019 +0200
@@ -29,6 +29,8 @@
#include "hwform.h"
#include "ui/page/pageoptions.h"
+#include "ui/page/pagetraining.h"
+#include "ui/page/pagecampaign.h"
#include "game.h"
#include "hwconsts.h"
#include "gameuiconfig.h"
@@ -44,15 +46,14 @@
// last game info
QList<QVariant> lastGameStartArgs = QList<QVariant>();
GameType lastGameType = gtNone;
-QString lastTrainingSubFolder = NULL;
GameCFGWidget * lastGameCfg = NULL;
QString lastGameAmmo = NULL;
TeamSelWidget * lastGameTeamSel = NULL;
-QString training, campaign, campaignScript, campaignTeam; // TODO: Cleaner solution?
+QString trainingName, trainingScript, trainingTeam, campaign, campaignScript, campaignTeam; // TODO: Cleaner solution?
HWGame::HWGame(GameUIConfig * config, GameCFGWidget * gamecfg, QString ammo, TeamSelWidget* pTeamSelWidget) :
- TCPBase(true, 0),
+ TCPBase(true, !config->language().isEmpty(), 0),
ammostr(ammo),
m_pTeamSelWidget(pTeamSelWidget)
{
@@ -75,22 +76,29 @@
void HWGame::onClientDisconnect()
{
- switch (gameType)
+ if (demoIsPresent)
{
- case gtDemo:
- // for video recording we need demo anyway
- emit HaveRecord(rtNeither, demo);
- break;
- case gtNet:
- emit HaveRecord(rtDemo, demo);
- break;
- default:
- if (gameState == gsInterrupted || gameState == gsHalted)
- emit HaveRecord(rtSave, demo);
- else if (gameState == gsFinished)
+ switch (gameType)
+ {
+ case gtDemo:
+ // for video recording we need demo anyway
+ emit HaveRecord(rtNeither, demo);
+ break;
+ case gtNet:
emit HaveRecord(rtDemo, demo);
- else
- emit HaveRecord(rtNeither, demo);
+ break;
+ default:
+ if (gameState == gsInterrupted || gameState == gsHalted)
+ emit HaveRecord(rtSave, demo);
+ else if (gameState == gsFinished)
+ emit HaveRecord(rtDemo, demo);
+ else
+ emit HaveRecord(rtNeither, demo);
+ }
+ }
+ else
+ {
+ emit HaveRecord(rtNeither, demo);
}
SetGameState(gsStopped);
}
@@ -139,37 +147,196 @@
void HWGame::SendQuickConfig()
{
+ /* Load and increase Quick Game experience level.
+ Experience increases by 1 for each started game and maxes out
+ at 20. Low experience levels will introduce a "beginner's bias" to make
+ the first quick games easier and simpler. The max. possible difficulty
+ increases progressively the longer you play.
+ If experience is maxed out, the beginner's bias is gone and quick games
+ are completely random. */
+ int exp = config->quickGameExperience();
+ if(exp < 20)
+ {
+ config->setQuickGameExperience(exp + 1);
+ }
+ qDebug("Starting quick game ...");
+ qDebug("Quick Game experience level: %d", exp);
+
+ // Init stuff
QByteArray teamscfg;
QAbstractItemModel * themeModel = DataManager::instance().themeModel()->withoutHidden();
HWProto::addStringToBuffer(teamscfg, "TL");
- HWProto::addStringToBuffer(teamscfg, QString("etheme %1")
- .arg((themeModel->rowCount() > 0) ? themeModel->index(rand() % themeModel->rowCount(), 0).data(ThemeModel::ActualNameRole).toString() : "Nature"));
+
+ // Random seed
HWProto::addStringToBuffer(teamscfg, "eseed " + QUuid::createUuid().toString());
- HWProto::addStringToBuffer(teamscfg, "e$template_filter 2");
- HWProto::addStringToBuffer(teamscfg, "e$feature_size "+QString::number(rand()%18+4));
+ int r, minhogs, maxhogs;
+ // Random map type
+ r = rand() % 10000;
+ if(r < 3000) { // 30%
+ // Random
+ r = 0;
+ } else if(r < 5250) { // 22.5%
+ // Maze
+ if(exp <= 3)
+ r = 0;
+ else
+ r = 1;
+ } else if(r < 7490) { // 22.4%
+ // Perlin
+ if(exp <= 7)
+ r = 1;
+ else
+ r = 2;
+ } else if(r < 7500 && exp >= 5) { // 0.1%
+ // Floating Flowers (just for fun)
+ r = 5;
+ } else if(r < 8750) { // 12.5%
+ // Image map
+ r = 3;
+ } else { // 12.5%
+ // Forts
+ r = 4;
+ }
+ switch(r)
+ {
+ // Random map
+ default:
+ case 0: {
+ r = rand() % 3;
+ if(r == 0)
+ {
+ // small island
+ HWProto::addStringToBuffer(teamscfg, "e$template_filter 1");
+ minhogs = 3;
+ maxhogs = 4;
+ }
+ else if(r == 1 || exp <= 6)
+ {
+ // medium island
+ HWProto::addStringToBuffer(teamscfg, "e$template_filter 2");
+ minhogs = 4;
+ maxhogs = 5;
+ }
+ else
+ {
+ // cave (locked at low experience because these maps can be huge)
+ HWProto::addStringToBuffer(teamscfg, "e$template_filter 4");
+ minhogs = 4;
+ maxhogs = 6;
+ }
+ HWProto::addStringToBuffer(teamscfg, "e$feature_size "+QString::number(rand()%18+4));
+ break;
+ }
+ // Maze
+ case 1: {
+ minhogs = 4;
+ maxhogs = 6;
+ HWProto::addStringToBuffer(teamscfg, "e$mapgen 1");
+ HWProto::addStringToBuffer(teamscfg, "e$template_filter "+QString::number(rand()%6));
+ HWProto::addStringToBuffer(teamscfg, "e$feature_size "+QString::number(rand()%16+6));
+ break;
+ }
+ // Perlin
+ case 2: {
+ minhogs = 4;
+ maxhogs = 6;
+ HWProto::addStringToBuffer(teamscfg, "e$mapgen 2");
+ HWProto::addStringToBuffer(teamscfg, "e$template_filter "+QString::number(rand()%6));
+ HWProto::addStringToBuffer(teamscfg, "e$feature_size "+QString::number(rand()%18+4));
+ break;
+ }
+ // Image map
+ case 3: {
+ minhogs = 4;
+ maxhogs = 6;
+ HWProto::addStringToBuffer(teamscfg, "e$mapgen 3");
+ // Select map from hardcoded list.
+ // TODO: find a more dynamic solution.
+ r = rand() % cQuickGameMaps.count();
+ HWProto::addStringToBuffer(teamscfg, "e$map " + cQuickGameMaps[r]);
+ break;
+ }
+ // Forts
+ case 4: {
+ minhogs = 4;
+ maxhogs = 6;
+ HWProto::addStringToBuffer(teamscfg, "e$mapgen 4");
+ HWProto::addStringToBuffer(teamscfg, "e$feature_size "+QString::number(rand()%20+1));
+ break;
+ }
+ // Floating Flowers
+ // (actually empty map; this forces the engine to generate fallback structures to have
+ // something for hogs to stand on)
+ case 5: {
+ minhogs = 4;
+ maxhogs = 8;
+ HWProto::addStringToBuffer(teamscfg, "e$mapgen 3");
+ HWProto::addStringToBuffer(teamscfg, "e$feature_size "+QString::number(rand()%4+3));
+ break;
+ }
+ }
+
+ // Theme
+ HWProto::addStringToBuffer(teamscfg, QString("etheme %1")
+ .arg((themeModel->rowCount() > 0) ? themeModel->index(rand() % themeModel->rowCount(), 0).data(ThemeModel::ActualNameRole).toString() : "Nature"));
+
+ int hogs = minhogs + rand() % (maxhogs-minhogs+1);
+ // Cap hog count at low experience
+ if((exp <= 8) && (hogs > 5))
+ hogs = 5;
+ else if((exp <= 5) && (hogs > 4))
+ hogs = 4;
+
+ // Teams
+ // Player team
HWTeam team1;
team1.setDifficulty(0);
team1.setColor(0);
- team1.setNumHedgehogs(4);
+ team1.setNumHedgehogs(hogs);
HWNamegen::teamRandomEverything(team1);
- team1.setVoicepack("Default");
- HWProto::addStringListToBuffer(teamscfg,
- team1.teamGameConfig(100));
+ team1.setVoicepack("Default_qau");
+ // Computer team
HWTeam team2;
- team2.setDifficulty(4);
+ // Random difficulty.
+ // Max. possible difficulty is capped at low experience levels.
+ if(exp >= 15) // very easy to very hard (full range)
+ r = 5 - rand() % 5;
+ else if(exp >= 9) // very easy to hard
+ r = 5 - rand() % 4;
+ else if(exp >= 6) // very easy to medium
+ r = 5 - rand() % 3;
+ else if(exp >= 2) // very easy to easy
+ r = 5 - rand() % 2;
+ else // very easy
+ r = 5;
+ team2.setDifficulty(r);
team2.setColor(1);
- team2.setNumHedgehogs(4);
+ team2.setNumHedgehogs(hogs);
+ // Make sure the team names are not equal
do
HWNamegen::teamRandomEverything(team2);
while(!team2.name().compare(team1.name()) || !team2.hedgehog(0).Hat.compare(team1.hedgehog(0).Hat));
- team2.setVoicepack("Default");
- HWProto::addStringListToBuffer(teamscfg,
- team2.teamGameConfig(100));
+ team2.setVoicepack("Default_qau");
+ // Team play order
+ r = rand() % 2;
+ if(r == 0 || exp <= 4) // player plays first
+ {
+ HWProto::addStringListToBuffer(teamscfg, team1.teamGameConfig(100));
+ HWProto::addStringListToBuffer(teamscfg, team2.teamGameConfig(100));
+ }
+ else // computer plays first
+ {
+ HWProto::addStringListToBuffer(teamscfg, team2.teamGameConfig(100));
+ HWProto::addStringListToBuffer(teamscfg, team1.teamGameConfig(100));
+ }
+
+ // Ammo scheme "Default"
+ // TODO: Random schemes
HWProto::addStringToBuffer(teamscfg, QString("eammloadt %1").arg(cDefaultAmmoStore->mid(0, cAmmoNumber)));
HWProto::addStringToBuffer(teamscfg, QString("eammprob %1").arg(cDefaultAmmoStore->mid(cAmmoNumber, cAmmoNumber)));
HWProto::addStringToBuffer(teamscfg, QString("eammdelay %1").arg(cDefaultAmmoStore->mid(2 * cAmmoNumber, cAmmoNumber)));
@@ -184,8 +351,16 @@
{
QByteArray traincfg;
HWProto::addStringToBuffer(traincfg, "TL");
+
+ HWTeam missionTeam = HWTeam();
+ missionTeam.setName(config->Form->ui.pageTraining->CBTeam->currentText());
+ missionTeam.loadFromFile();
+ missionTeam.setNumHedgehogs(HEDGEHOGS_PER_TEAM);
+ missionTeam.setMissionTeam(true);
+ HWProto::addStringListToBuffer(traincfg, missionTeam.teamGameConfig(100));
+
HWProto::addStringToBuffer(traincfg, "eseed " + QUuid::createUuid().toString());
- HWProto::addStringToBuffer(traincfg, "escript " + training);
+ HWProto::addStringToBuffer(traincfg, "escript " + trainingScript);
RawSendIPC(traincfg);
}
@@ -194,8 +369,15 @@
{
QByteArray campaigncfg;
HWProto::addStringToBuffer(campaigncfg, "TL");
+
+ HWTeam missionTeam = HWTeam();
+ missionTeam.setName(config->Form->ui.pageCampaign->CBTeam->currentText());
+ missionTeam.loadFromFile();
+ missionTeam.setNumHedgehogs(HEDGEHOGS_PER_TEAM);
+ missionTeam.setMissionTeam(true);
+ HWProto::addStringListToBuffer(campaigncfg, missionTeam.teamGameConfig(100));
+
HWProto::addStringToBuffer(campaigncfg, "eseed " + QUuid::createUuid().toString());
-
HWProto::addStringToBuffer(campaigncfg, "escript " + campaignScript);
RawSendIPC(campaigncfg);
@@ -277,6 +459,11 @@
SetGameState(gsFinished);
break;
}
+ case 'm':
+ {
+ SetDemoPresence(false);
+ break;
+ }
case 'H':
{
SetGameState(gsHalted);
@@ -307,6 +494,14 @@
writeCampaignVar(msg.right(msg.size() - 3));
break;
}
+ case 'v':
+ {
+ if (msg.at(2) == '?')
+ sendMissionVar(msg.right(msg.size() - 3));
+ else if (msg.at(2) == '!')
+ writeMissionVar(msg.right(msg.size() - 3));
+ break;
+ }
case 'W':
{
// fetch new window resolution via IPC and save it in the settings
@@ -346,6 +541,20 @@
RawSendIPC(buf);
}
+void HWGame::FromNetWarning(const QString & msg)
+{
+ QByteArray buf;
+ HWProto::addStringToBuffer(buf, "s\x00" + msg + "\x20\x20");
+ RawSendIPC(buf);
+}
+
+void HWGame::FromNetError(const QString & msg)
+{
+ QByteArray buf;
+ HWProto::addStringToBuffer(buf, "s\x05" + msg + "\x20\x20");
+ RawSendIPC(buf);
+}
+
void HWGame::onClientRead()
{
quint8 msglen;
@@ -400,6 +609,10 @@
arguments << QString::number(resolutions.second.width());
arguments << "--height";
arguments << QString::number(resolutions.second.height());
+ if (config->zoom() != 100) {
+ arguments << "--zoom";
+ arguments << QString::number(config->zoom());
+ }
arguments << "--raw-quality";
arguments << QString::number(config->translateQuality());
arguments << "--stereo";
@@ -414,6 +627,8 @@
arguments << "--nosound";
if (!config->isMusicEnabled())
arguments << "--nomusic";
+ if (!config->isAudioDampenEnabled())
+ arguments << "--nodampen";
if (!nick.isEmpty()) {
arguments << "--nick";
arguments << nick;
@@ -427,6 +642,8 @@
arguments << "--no-healthtag";
if (config->Form->ui.pageOptions->CBTagOpacity->isChecked())
arguments << "--translucent-tags";
+ if (!config->isHolidaySillinessEnabled())
+ arguments << "--no-holiday-silliness";
arguments << "--chat-size";
arguments << QString::number(config->chatSize());
@@ -453,6 +670,19 @@
SetGameState(gsStarted);
}
+void HWGame::PlayOfficialServerDemo()
+{
+ // TODO: Use gtDemo so fast-forward is available.
+ // Needs engine support first.
+ lastGameStartArgs.clear();
+ lastGameType = gtLocal;
+
+ gameType = gtLocal;
+ demo.clear();
+ Start(false);
+ SetGameState(gsStarted);
+}
+
void HWGame::StartNet()
{
lastGameStartArgs.clear();
@@ -486,16 +716,19 @@
SetGameState(gsStarted);
}
-void HWGame::StartTraining(const QString & file, const QString & subFolder)
+void HWGame::StartTraining(const QString & file, const QString & subFolder, const QString & trainTeam)
{
lastGameStartArgs.clear();
lastGameStartArgs.append(file);
+ lastGameStartArgs.append(subFolder);
+ lastGameStartArgs.append(trainTeam);
lastGameType = gtTraining;
- lastTrainingSubFolder = subFolder;
gameType = gtTraining;
- training = "Missions/" + subFolder + "/" + file + ".lua";
+ trainingScript = "Missions/" + subFolder + "/" + file + ".lua";
+ trainingName = file;
+ trainingTeam = trainTeam;
demo.clear();
Start(false);
SetGameState(gsStarted);
@@ -524,14 +757,23 @@
emit GameStateChanged(state);
if (gameType == gtCampaign)
{
- emit CampStateChanged(1);
+ emit CampStateChanged(state);
+ }
+ else if (gameType == gtTraining)
+ {
+ emit TrainingStateChanged(1);
}
}
+void HWGame::SetDemoPresence(bool hasDemo)
+{
+ emit DemoPresenceChanged(hasDemo);
+}
+
void HWGame::abort()
{
QByteArray buf;
- HWProto::addStringToBuffer(buf, QString("efinish"));
+ HWProto::addStringToBuffer(buf, QString("eforcequit"));
RawSendIPC(buf);
}
@@ -560,3 +802,28 @@
teamfile.setValue("Campaign " + campaign + "/" + varToWrite, varValue);
}
+void HWGame::sendMissionVar(const QByteArray &varToSend)
+{
+ QString varToFind = QString::fromUtf8(varToSend);
+ QSettings teamfile(QString(cfgdir->absolutePath() + "/Teams/%1.hwt").arg(trainingTeam), QSettings::IniFormat, 0);
+ teamfile.setIniCodec("UTF-8");
+ QString varValue = teamfile.value("Mission " + trainingName + "/" + varToFind, "").toString();
+ QByteArray command;
+ HWProto::addStringToBuffer(command, "v." + varValue);
+ RawSendIPC(command);
+}
+
+void HWGame::writeMissionVar(const QByteArray & varVal)
+{
+ int i = varVal.indexOf(" ");
+ if(i < 0)
+ return;
+
+ QString varToWrite = QString::fromUtf8(varVal.left(i));
+ QString varValue = QString::fromUtf8(varVal.mid(i + 1));
+
+ QSettings teamfile(QString(cfgdir->absolutePath() + "/Teams/%1.hwt").arg(trainingTeam), QSettings::IniFormat, 0);
+ teamfile.setIniCodec("UTF-8");
+ teamfile.setValue("Mission " + trainingName + "/" + varToWrite, varValue);
+}
+