1 /*
2  * Hedgewars, a free turn based strategy game
3  * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; version 2 of the License
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 
19 #include <QApplication>
20 
21 #include <QString>
22 #include <QCheckBox>
23 #include <QByteArray>
24 #include <QUuid>
25 #include <QColor>
26 #include <QStringListModel>
27 #include <QTextStream>
28 #include <utility>
29 
30 #include "hwform.h"
31 #include "ui/page/pageoptions.h"
32 #include "ui/page/pagetraining.h"
33 #include "ui/page/pagecampaign.h"
34 #include "game.h"
35 #include "hwconsts.h"
36 #include "gameuiconfig.h"
37 #include "gamecfgwidget.h"
38 #include "teamselect.h"
39 #include "proto.h"
40 #include "binds.h"
41 #include "campaign.h"
42 
43 #include <QTextStream>
44 #include "ThemeModel.h"
45 
46 // last game info
47 QList<QVariant> lastGameStartArgs = QList<QVariant>();
48 GameType lastGameType = gtNone;
49 GameCFGWidget * lastGameCfg = NULL;
50 QString lastGameAmmo = NULL;
51 TeamSelWidget * lastGameTeamSel = NULL;
52 
53 QString trainingName, trainingScript, trainingTeam, campaign, campaignScript, campaignTeam; // TODO: Cleaner solution?
54 
HWGame(GameUIConfig * config,GameCFGWidget * gamecfg,QString ammo,TeamSelWidget * pTeamSelWidget)55 HWGame::HWGame(GameUIConfig * config, GameCFGWidget * gamecfg, QString ammo, TeamSelWidget* pTeamSelWidget) :
56     TCPBase(true, !config->language().isEmpty(), 0),
57     ammostr(ammo),
58     m_pTeamSelWidget(pTeamSelWidget)
59 {
60     this->config = config;
61     this->gamecfg = gamecfg;
62     netSuspend = false;
63 
64     lastGameCfg = gamecfg;
65     lastGameAmmo = ammo;
66     lastGameTeamSel = pTeamSelWidget;
67 
68     gameState = gsNotStarted;
69     gameType = gtNone;
70 }
71 
~HWGame()72 HWGame::~HWGame()
73 {
74     SetGameState(gsDestroyed);
75 }
76 
onClientDisconnect()77 void HWGame::onClientDisconnect()
78 {
79     if (demoIsPresent)
80     {
81         switch (gameType)
82         {
83             case gtDemo:
84                 // for video recording we need demo anyway
85                 emit HaveRecord(rtNeither, demo);
86                 break;
87             case gtNet:
88                 emit HaveRecord(rtDemo, demo);
89                 break;
90             default:
91                 if (gameState == gsInterrupted || gameState == gsHalted)
92                     emit HaveRecord(rtSave, demo);
93                 else if (gameState == gsFinished)
94                     emit HaveRecord(rtDemo, demo);
95                 else
96                     emit HaveRecord(rtNeither, demo);
97         }
98     }
99     else
100     {
101         emit HaveRecord(rtNeither, demo);
102     }
103     SetGameState(gsStopped);
104 }
105 
commonConfig()106 void HWGame::commonConfig()
107 {
108     QByteArray buf;
109     QString gt;
110     switch (gameType)
111     {
112         case gtDemo:
113             gt = "TD";
114             break;
115         case gtNet:
116             gt = "TN";
117             break;
118         default:
119             gt = "TL";
120     }
121     HWProto::addStringToBuffer(buf, gt);
122 
123     buf += gamecfg->getFullConfig();
124 
125     if (m_pTeamSelWidget)
126     {
127         foreach(HWTeam team, m_pTeamSelWidget->getPlayingTeams())
128         {
129             HWProto::addStringToBuffer(buf, QString("eammloadt %1").arg(ammostr.mid(0, cAmmoNumber)));
130             HWProto::addStringToBuffer(buf, QString("eammprob %1").arg(ammostr.mid(cAmmoNumber, cAmmoNumber)));
131             HWProto::addStringToBuffer(buf, QString("eammdelay %1").arg(ammostr.mid(2 * cAmmoNumber, cAmmoNumber)));
132             HWProto::addStringToBuffer(buf, QString("eammreinf %1").arg(ammostr.mid(3 * cAmmoNumber, cAmmoNumber)));
133             if(gamecfg->schemeData(15).toBool() || !gamecfg->schemeData(21).toBool()) HWProto::addStringToBuffer(buf, QString("eammstore"));
134             HWProto::addStringListToBuffer(buf,
135                                            team.teamGameConfig(gamecfg->getInitHealth()));
136             ;
137         }
138     }
139 
140     RawSendIPC(buf);
141 }
142 
SendConfig()143 void HWGame::SendConfig()
144 {
145     commonConfig();
146 }
147 
SendQuickConfig()148 void HWGame::SendQuickConfig()
149 {
150     /* Load and increase Quick Game experience level.
151     Experience increases by 1 for each started game and maxes out
152     at 20. Low experience levels will introduce a "beginner's bias" to make
153     the first quick games easier and simpler. The max. possible difficulty
154     increases progressively the longer you play.
155     If experience is maxed out, the beginner's bias is gone and quick games
156     are completely random. */
157     int exp = config->quickGameExperience();
158     if(exp < 20)
159     {
160        config->setQuickGameExperience(exp + 1);
161     }
162     qDebug("Starting quick game ...");
163     qDebug("Quick Game experience level: %d", exp);
164 
165     // Init stuff
166     QByteArray teamscfg;
167     QAbstractItemModel * themeModel = DataManager::instance().themeModel()->withoutHidden();
168 
169     HWProto::addStringToBuffer(teamscfg, "TL");
170 
171     // Random seed
172     HWProto::addStringToBuffer(teamscfg, "eseed " + QUuid::createUuid().toString());
173 
174     int r, minhogs, maxhogs;
175 
176     // Random map type
177     r = rand() % 10000;
178     if(r < 3000) { // 30%
179         // Random
180         r = 0;
181     } else if(r < 5250) { // 22.5%
182         // Maze
183         if(exp <= 3)
184             r = 0;
185         else
186             r = 1;
187     } else if(r < 7490) { // 22.4%
188         // Perlin
189         if(exp <= 7)
190             r = 1;
191         else
192             r = 2;
193     } else if(r < 7500 && exp >= 5) { // 0.1%
194         // Floating Flowers (just for fun)
195         r = 5;
196     } else if(r < 8750) { // 12.5%
197         // Image map
198         r = 3;
199     } else { // 12.5%
200         // Forts
201         r = 4;
202     }
203     switch(r)
204     {
205         // Random map
206         default:
207         case 0: {
208             r = rand() % 3;
209             if(r == 0)
210             {
211                 // small island
212                 HWProto::addStringToBuffer(teamscfg, "e$template_filter 1");
213                 minhogs = 3;
214                 maxhogs = 4;
215             }
216             else if(r == 1 || exp <= 6)
217             {
218                 // medium island
219                 HWProto::addStringToBuffer(teamscfg, "e$template_filter 2");
220                 minhogs = 4;
221                 maxhogs = 5;
222             }
223             else
224             {
225                 // cave (locked at low experience because these maps can be huge)
226                 HWProto::addStringToBuffer(teamscfg, "e$template_filter 4");
227                 minhogs = 4;
228                 maxhogs = 6;
229             }
230             HWProto::addStringToBuffer(teamscfg, "e$feature_size "+QString::number(rand()%18+4));
231             break;
232         }
233         // Maze
234         case 1: {
235             minhogs = 4;
236             maxhogs = 6;
237             HWProto::addStringToBuffer(teamscfg, "e$mapgen 1");
238             HWProto::addStringToBuffer(teamscfg, "e$template_filter "+QString::number(rand()%6));
239             HWProto::addStringToBuffer(teamscfg, "e$feature_size "+QString::number(rand()%16+6));
240             break;
241         }
242         // Perlin
243         case 2: {
244             minhogs = 4;
245             maxhogs = 6;
246             HWProto::addStringToBuffer(teamscfg, "e$mapgen 2");
247             HWProto::addStringToBuffer(teamscfg, "e$template_filter "+QString::number(rand()%6));
248             HWProto::addStringToBuffer(teamscfg, "e$feature_size "+QString::number(rand()%18+4));
249             break;
250         }
251         // Image map
252         case 3: {
253             minhogs = 4;
254             maxhogs = 6;
255             HWProto::addStringToBuffer(teamscfg, "e$mapgen 3");
256             // Select map from hardcoded list.
257             // TODO: find a more dynamic solution.
258             r = rand() % cQuickGameMaps.count();
259             HWProto::addStringToBuffer(teamscfg, "e$map " + cQuickGameMaps[r]);
260             break;
261         }
262         // Forts
263         case 4: {
264             minhogs = 4;
265             maxhogs = 6;
266             HWProto::addStringToBuffer(teamscfg, "e$mapgen 4");
267             HWProto::addStringToBuffer(teamscfg, "e$feature_size "+QString::number(rand()%20+1));
268             break;
269         }
270         // Floating Flowers
271         // (actually empty map; this forces the engine to generate fallback structures to have
272         // something for hogs to stand on)
273         case 5: {
274             minhogs = 4;
275             maxhogs = 8;
276             HWProto::addStringToBuffer(teamscfg, "e$mapgen 3");
277             HWProto::addStringToBuffer(teamscfg, "e$feature_size "+QString::number(rand()%4+3));
278             break;
279         }
280     }
281 
282     // Theme
283     HWProto::addStringToBuffer(teamscfg, QString("etheme %1")
284         .arg((themeModel->rowCount() > 0) ? themeModel->index(rand() % themeModel->rowCount(), 0).data(ThemeModel::ActualNameRole).toString() : "Nature"));
285 
286     int hogs = minhogs + rand() % (maxhogs-minhogs+1);
287     // Cap hog count at low experience
288     if((exp <= 8) && (hogs > 5))
289         hogs = 5;
290     else if((exp <= 5) && (hogs > 4))
291         hogs = 4;
292 
293     // Teams
294     // Player team
295     HWTeam team1;
296     team1.setDifficulty(0);
297     team1.setColor(0);
298     team1.setNumHedgehogs(hogs);
299     HWNamegen::teamRandomEverything(team1);
300     team1.setVoicepack("Default_qau");
301 
302     // Computer team
303     HWTeam team2;
304     // Random difficulty.
305     // Max. possible difficulty is capped at low experience levels.
306     if(exp >= 15) // very easy to very hard (full range)
307         r = 5 - rand() % 5;
308     else if(exp >= 9) // very easy to hard
309         r = 5 - rand() % 4;
310     else if(exp >= 6) // very easy to medium
311         r = 5 - rand() % 3;
312     else if(exp >= 2) // very easy to easy
313         r = 5 - rand() % 2;
314     else // very easy
315         r = 5;
316     team2.setDifficulty(r);
317     team2.setColor(1);
318     team2.setNumHedgehogs(hogs);
319     // Make sure the team names are not equal
320     do
321         HWNamegen::teamRandomEverything(team2);
322     while(!team2.name().compare(team1.name()) || !team2.hedgehog(0).Hat.compare(team1.hedgehog(0).Hat));
323     team2.setVoicepack("Default_qau");
324 
325     // Team play order
326     r = rand() % 2;
327     if(r == 0 || exp <= 4) // player plays first
328     {
329         HWProto::addStringListToBuffer(teamscfg, team1.teamGameConfig(100));
330         HWProto::addStringListToBuffer(teamscfg, team2.teamGameConfig(100));
331     }
332     else // computer plays first
333     {
334         HWProto::addStringListToBuffer(teamscfg, team2.teamGameConfig(100));
335         HWProto::addStringListToBuffer(teamscfg, team1.teamGameConfig(100));
336     }
337 
338     // Ammo scheme "Default"
339     // TODO: Random schemes
340     HWProto::addStringToBuffer(teamscfg, QString("eammloadt %1").arg(cDefaultAmmoStore->mid(0, cAmmoNumber)));
341     HWProto::addStringToBuffer(teamscfg, QString("eammprob %1").arg(cDefaultAmmoStore->mid(cAmmoNumber, cAmmoNumber)));
342     HWProto::addStringToBuffer(teamscfg, QString("eammdelay %1").arg(cDefaultAmmoStore->mid(2 * cAmmoNumber, cAmmoNumber)));
343     HWProto::addStringToBuffer(teamscfg, QString("eammreinf %1").arg(cDefaultAmmoStore->mid(3 * cAmmoNumber, cAmmoNumber)));
344     HWProto::addStringToBuffer(teamscfg, QString("eammstore"));
345     HWProto::addStringToBuffer(teamscfg, QString("eammstore"));
346 
347     RawSendIPC(teamscfg);
348 }
349 
SendTrainingConfig()350 void HWGame::SendTrainingConfig()
351 {
352     QByteArray traincfg;
353     HWProto::addStringToBuffer(traincfg, "TL");
354 
355     HWTeam missionTeam = HWTeam();
356     missionTeam.setName(config->Form->ui.pageTraining->CBTeam->currentText());
357     missionTeam.loadFromFile();
358     missionTeam.setNumHedgehogs(HEDGEHOGS_PER_TEAM);
359     missionTeam.setMissionTeam(true);
360     HWProto::addStringListToBuffer(traincfg, missionTeam.teamGameConfig(100));
361 
362     HWProto::addStringToBuffer(traincfg, "eseed " + QUuid::createUuid().toString());
363     HWProto::addStringToBuffer(traincfg, "escript " + trainingScript);
364 
365     RawSendIPC(traincfg);
366 }
367 
SendCampaignConfig()368 void HWGame::SendCampaignConfig()
369 {
370     QByteArray campaigncfg;
371     HWProto::addStringToBuffer(campaigncfg, "TL");
372 
373     HWTeam missionTeam = HWTeam();
374     missionTeam.setName(config->Form->ui.pageCampaign->CBTeam->currentText());
375     missionTeam.loadFromFile();
376     missionTeam.setNumHedgehogs(HEDGEHOGS_PER_TEAM);
377     missionTeam.setMissionTeam(true);
378     HWProto::addStringListToBuffer(campaigncfg, missionTeam.teamGameConfig(100));
379 
380     HWProto::addStringToBuffer(campaigncfg, "eseed " + QUuid::createUuid().toString());
381     HWProto::addStringToBuffer(campaigncfg, "escript " + campaignScript);
382 
383     RawSendIPC(campaigncfg);
384 }
385 
SendNetConfig()386 void HWGame::SendNetConfig()
387 {
388     commonConfig();
389 }
390 
ParseMessage(const QByteArray & msg)391 void HWGame::ParseMessage(const QByteArray & msg)
392 {
393     switch(msg.at(1))
394     {
395         case '?':
396         {
397             SendIPC("!");
398             break;
399         }
400         case 'C':
401         {
402             switch (gameType)
403             {
404                 case gtLocal:
405                 {
406                     SendConfig();
407                     break;
408                 }
409                 case gtQLocal:
410                 {
411                     SendQuickConfig();
412                     break;
413                 }
414                 case gtNone:
415                 case gtSave:
416                 case gtDemo:
417                     break;
418                 case gtNet:
419                 {
420                     SendNetConfig();
421                     break;
422                 }
423                 case gtTraining:
424                 {
425                     SendTrainingConfig();
426                     break;
427                 }
428                 case gtCampaign:
429                 {
430                     SendCampaignConfig();
431                     break;
432                 }
433             }
434             break;
435         }
436         case 'E':
437         {
438             int size = msg.size();
439             emit ErrorMessage(
440                 tr("A fatal ERROR occured! The game engine had to stop.\n\n"
441                 "We are very sorry for the inconvenience. :-(\n\n"
442                 "If this keeps happening, please click the 'Feedback' button in the main menu!\n\n"
443                 "Last engine message:\n%1")
444                 .arg(QString::fromUtf8(msg.mid(2).left(size - 4))));
445             return;
446         }
447         case 'i':
448         {
449             emit GameStats(msg.at(2), QString::fromUtf8(msg.mid(3)));
450             break;
451         }
452         case 'Q':
453         {
454             SetGameState(gsInterrupted);
455             break;
456         }
457         case 'q':
458         {
459             SetGameState(gsFinished);
460             break;
461         }
462         case 'm':
463         {
464             SetDemoPresence(false);
465             break;
466         }
467         case 'H':
468         {
469             SetGameState(gsHalted);
470             break;
471         }
472         case 's':
473         {
474             int size = msg.size();
475             QString msgbody = QString::fromUtf8(msg.mid(2).left(size - 4));
476             emit SendChat(msgbody);
477             QByteArray buf;
478             HWProto::addStringToBuffer(buf, "s" + HWProto::formatChatMsg(config->netNick(), msgbody) + "\x20\x20");
479             demo.append(buf);
480             break;
481         }
482         case 'b':
483         {
484             int size = msg.size();
485             QString msgbody = QString::fromUtf8(msg.mid(2).left(size - 4));
486             emit SendTeamMessage(msgbody);
487             break;
488         }
489         case 'V':
490         {
491             if (msg.at(2) == '?')
492                 sendCampaignVar(msg.right(msg.size() - 3));
493             else if (msg.at(2) == '!')
494                 writeCampaignVar(msg.right(msg.size() - 3));
495             break;
496         }
497         case 'v':
498         {
499             if (msg.at(2) == '?')
500                 sendMissionVar(msg.right(msg.size() - 3));
501             else if (msg.at(2) == '!')
502                 writeMissionVar(msg.right(msg.size() - 3));
503             break;
504         }
505         case 'W':
506         {
507             // fetch new window resolution via IPC and save it in the settings
508             int size = msg.size();
509             QString newResolution = QString().append(msg.mid(2)).left(size - 4);
510             bool windowMaximized;
511             if (newResolution.endsWith('M'))
512             {
513                 windowMaximized = true;
514                 newResolution.chop(1);
515             }
516             else
517             {
518                 windowMaximized = false;
519             }
520             QStringList wh = newResolution.split('x');
521             config->Form->ui.pageOptions->windowWidthEdit->setValue(wh[0].toInt());
522             config->Form->ui.pageOptions->windowHeightEdit->setValue(wh[1].toInt());
523             config->vid_SetMaximized(windowMaximized);
524             break;
525         }
526         case '~':
527         {
528             int size = msg.size();
529             QString msgbody = QString::fromUtf8(msg.mid(2).left(size - 4));
530             emit SendConsoleCommand(msgbody);
531             break;
532         }
533         default:
534         {
535             if (gameType == gtNet && !netSuspend)
536                 m_netSendBuffer.append(msg);
537 
538             demo.append(msg);
539         }
540     }
541 }
542 
FromNet(const QByteArray & msg)543 void HWGame::FromNet(const QByteArray & msg)
544 {
545     RawSendIPC(msg);
546 }
547 
FromNetChat(const QString & msg)548 void HWGame::FromNetChat(const QString & msg)
549 {
550     QByteArray buf;
551     HWProto::addStringToBuffer(buf, 's' + msg + "\x20\x20");
552     RawSendIPC(buf);
553 }
554 
FromNetWarning(const QString & msg)555 void HWGame::FromNetWarning(const QString & msg)
556 {
557     QByteArray buf;
558     HWProto::addStringToBuffer(buf, "s\x00" + msg + "\x20\x20");
559     RawSendIPC(buf);
560 }
561 
FromNetError(const QString & msg)562 void HWGame::FromNetError(const QString & msg)
563 {
564     QByteArray buf;
565     HWProto::addStringToBuffer(buf, "s\x05" + msg + "\x20\x20");
566     RawSendIPC(buf);
567 }
568 
onClientRead()569 void HWGame::onClientRead()
570 {
571     quint8 msglen;
572     quint32 bufsize;
573     while (!readbuffer.isEmpty() && ((bufsize = readbuffer.size()) > 0) &&
574             ((msglen = readbuffer.data()[0]) < bufsize))
575     {
576         QByteArray msg = readbuffer.left(msglen + 1);
577         readbuffer.remove(0, msglen + 1);
578         ParseMessage(msg);
579     }
580 
581     flushNetBuffer();
582 }
583 
flushNetBuffer()584 void HWGame::flushNetBuffer()
585 {
586     if(m_netSendBuffer.size())
587     {
588         emit SendNet(m_netSendBuffer);
589 
590         m_netSendBuffer.clear();
591     }
592 }
593 
getArguments()594 QStringList HWGame::getArguments()
595 {
596     QStringList arguments;
597     std::pair<QRect, QRect> resolutions = config->vid_ResolutionPair();
598     QString nick = config->netNick().toUtf8().toBase64();
599 
600     arguments << "--internal"; //Must be passed as first argument
601     arguments << "--port";
602     arguments << QString("%1").arg(ipc_port);
603     arguments << "--prefix";
604     arguments << datadir->absolutePath();
605     arguments << "--user-prefix";
606     arguments << cfgdir->absolutePath();
607     arguments << "--locale";
608     // TODO: Don't bother translators with this nonsense and detect this file automatically.
609     //: IMPORTANT: This text has a special meaning, do not translate it directly. This is the file name of translation files for the game engine, found in Data/Locale/. Usually, you replace “en” with the ISO-639-1 language code of your language.
610     arguments << tr("en.txt");
611     arguments << "--frame-interval";
612     arguments << QString::number(config->timerInterval());
613     arguments << "--volume";
614     arguments << QString::number(config->volume());
615     arguments << "--fullscreen-width";
616     arguments << QString::number(resolutions.first.width());
617     arguments << "--fullscreen-height";
618     arguments << QString::number(resolutions.first.height());
619     arguments << "--width";
620     arguments << QString::number(resolutions.second.width());
621     arguments << "--height";
622     arguments << QString::number(resolutions.second.height());
623     if (config->vid_Maximized())
624         arguments << "--maximized";
625     if (config->zoom() != 100) {
626         arguments << "--zoom";
627         arguments << QString::number(config->zoom());
628     }
629     arguments << "--raw-quality";
630     arguments << QString::number(config->translateQuality());
631     arguments << "--stereo";
632     arguments << QString::number(config->stereoMode());
633     if (config->vid_Fullscreen())
634         arguments << "--fullscreen";
635     if (config->isShowFPSEnabled())
636         arguments << "--showfps";
637     if (config->isAltDamageEnabled())
638         arguments << "--altdmg";
639     if (!config->isSoundEnabled())
640         arguments << "--nosound";
641     if (!config->isMusicEnabled())
642         arguments << "--nomusic";
643     if (!config->isAudioDampenEnabled())
644         arguments << "--nodampen";
645     if (!nick.isEmpty()) {
646         arguments << "--nick";
647         arguments << nick;
648     }
649 
650     if (!config->Form->ui.pageOptions->CBTeamTag->isChecked())
651         arguments << "--no-teamtag";
652     if (!config->Form->ui.pageOptions->CBHogTag->isChecked())
653         arguments << "--no-hogtag";
654     if (!config->Form->ui.pageOptions->CBHealthTag->isChecked())
655         arguments << "--no-healthtag";
656     if (config->Form->ui.pageOptions->CBTagOpacity->isChecked())
657         arguments << "--translucent-tags";
658     if (!config->isHolidaySillinessEnabled())
659         arguments << "--no-holiday-silliness";
660 
661     return arguments;
662 }
663 
PlayDemo(const QString & demofilename,bool isSave)664 void HWGame::PlayDemo(const QString & demofilename, bool isSave)
665 {
666     gameType = isSave ? gtSave : gtDemo;
667     lastGameType = gameType;
668     QFile demofile(demofilename);
669     if (!demofile.open(QIODevice::ReadOnly))
670     {
671         emit ErrorMessage(tr("Cannot open demofile %1").arg(demofilename));
672         return ;
673     }
674 
675     // read demo
676     toSendBuf = demofile.readAll();
677 
678     // run engine
679     demo.clear();
680     Start(false);
681     SetGameState(gsStarted);
682 }
683 
PlayOfficialServerDemo()684 void HWGame::PlayOfficialServerDemo()
685 {
686     // TODO: Use gtDemo so fast-forward is available.
687     // Needs engine support first.
688     lastGameStartArgs.clear();
689     lastGameType = gtLocal;
690 
691     gameType = gtLocal;
692     demo.clear();
693     Start(false);
694     SetGameState(gsStarted);
695 }
696 
StartNet()697 void HWGame::StartNet()
698 {
699     lastGameStartArgs.clear();
700     lastGameType = gtNet;
701 
702     gameType = gtNet;
703     demo.clear();
704     Start(false);
705     SetGameState(gsStarted);
706 }
707 
StartLocal()708 void HWGame::StartLocal()
709 {
710     lastGameStartArgs.clear();
711     lastGameType = gtLocal;
712 
713     gameType = gtLocal;
714     demo.clear();
715     Start(false);
716     SetGameState(gsStarted);
717 }
718 
StartQuick()719 void HWGame::StartQuick()
720 {
721     lastGameStartArgs.clear();
722     lastGameType = gtQLocal;
723 
724     gameType = gtQLocal;
725     demo.clear();
726     Start(false);
727     SetGameState(gsStarted);
728 }
729 
StartTraining(const QString & file,const QString & subFolder,const QString & trainTeam)730 void HWGame::StartTraining(const QString & file, const QString & subFolder, const QString & trainTeam)
731 {
732     lastGameStartArgs.clear();
733     lastGameStartArgs.append(file);
734     lastGameStartArgs.append(subFolder);
735     lastGameStartArgs.append(trainTeam);
736     lastGameType = gtTraining;
737 
738     gameType = gtTraining;
739 
740     trainingScript  = "Missions/" + subFolder + "/" + file + ".lua";
741     trainingName = file;
742     trainingTeam = trainTeam;
743     demo.clear();
744     Start(false);
745     SetGameState(gsStarted);
746 }
747 
StartCampaign(const QString & camp,const QString & campScript,const QString & campTeam)748 void HWGame::StartCampaign(const QString & camp, const QString & campScript, const QString & campTeam)
749 {
750     lastGameStartArgs.clear();
751     lastGameStartArgs.append(camp);
752     lastGameStartArgs.append(campScript);
753     lastGameStartArgs.append(campTeam);
754     lastGameType = gtCampaign;
755 
756     gameType = gtCampaign;
757     campaign = camp;
758     campaignScript = "Missions/Campaign/" + camp + "/" + campScript;
759     campaignTeam = campTeam;
760     demo.clear();
761     Start(false);
762     SetGameState(gsStarted);
763 }
764 
SetGameState(GameState state)765 void HWGame::SetGameState(GameState state)
766 {
767     gameState = state;
768     emit GameStateChanged(state);
769     if (gameType == gtCampaign)
770     {
771         emit CampStateChanged(state);
772     }
773     else if (gameType == gtTraining)
774     {
775         emit TrainingStateChanged(1);
776     }
777 }
778 
SetDemoPresence(bool hasDemo)779 void HWGame::SetDemoPresence(bool hasDemo)
780 {
781     emit DemoPresenceChanged(hasDemo);
782 }
783 
abort()784 void HWGame::abort()
785 {
786     QByteArray buf;
787     HWProto::addStringToBuffer(buf, QString("eforcequit"));
788     RawSendIPC(buf);
789 }
790 
sendCampaignVar(const QByteArray & varToSend)791 void HWGame::sendCampaignVar(const QByteArray &varToSend)
792 {
793     QString varToFind = QString::fromUtf8(varToSend);
794     QSettings teamfile(QString(cfgdir->absolutePath() + "/Teams/%1.hwt").arg(campaignTeam), QSettings::IniFormat, 0);
795     teamfile.setIniCodec("UTF-8");
796     QString varValue = teamfile.value("Campaign " + campaign + "/" + varToFind, "").toString();
797     QByteArray command;
798     HWProto::addStringToBuffer(command, "V." + varValue);
799     RawSendIPC(command);
800 }
801 
writeCampaignVar(const QByteArray & varVal)802 void HWGame::writeCampaignVar(const QByteArray & varVal)
803 {
804     int i = varVal.indexOf(" ");
805     if(i < 0)
806         return;
807 
808     QString varToWrite = QString::fromUtf8(varVal.left(i));
809     QString varValue = QString::fromUtf8(varVal.mid(i + 1));
810 
811     QSettings teamfile(QString(cfgdir->absolutePath() + "/Teams/%1.hwt").arg(campaignTeam), QSettings::IniFormat, 0);
812     teamfile.setIniCodec("UTF-8");
813     teamfile.setValue("Campaign " + campaign + "/" + varToWrite, varValue);
814 }
815 
sendMissionVar(const QByteArray & varToSend)816 void HWGame::sendMissionVar(const QByteArray &varToSend)
817 {
818     QString varToFind = QString::fromUtf8(varToSend);
819     QSettings teamfile(QString(cfgdir->absolutePath() + "/Teams/%1.hwt").arg(trainingTeam), QSettings::IniFormat, 0);
820     teamfile.setIniCodec("UTF-8");
821     QString varValue = teamfile.value("Mission " + trainingName + "/" + varToFind, "").toString();
822     QByteArray command;
823     HWProto::addStringToBuffer(command, "v." + varValue);
824     RawSendIPC(command);
825 }
826 
writeMissionVar(const QByteArray & varVal)827 void HWGame::writeMissionVar(const QByteArray & varVal)
828 {
829     int i = varVal.indexOf(" ");
830     if(i < 0)
831         return;
832 
833     QString varToWrite = QString::fromUtf8(varVal.left(i));
834     QString varValue = QString::fromUtf8(varVal.mid(i + 1));
835 
836     QSettings teamfile(QString(cfgdir->absolutePath() + "/Teams/%1.hwt").arg(trainingTeam), QSettings::IniFormat, 0);
837     teamfile.setIniCodec("UTF-8");
838     teamfile.setValue("Mission " + trainingName + "/" + varToWrite, varValue);
839 }
840 
841