1 |
/* |
2 |
Copyright (C) 2001-2004 Stephane Magnenat & Luc-Olivier de Charrière |
3 |
for any question or comment contact us at nct@ysagoon.com or nuage@ysagoon.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; either version 2 of the License, or |
8 |
(at your option) any later version. |
9 |
|
10 |
This program is distributed in the hope that it will be useful, |
11 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 |
GNU General Public License for more details. |
14 |
|
15 |
You should have received a copy of the GNU General Public License |
16 |
along with this program; if not, write to the Free Software |
17 |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
18 |
*/ |
19 |
|
20 |
#ifdef DX9_BACKEND |
21 |
#include <Types.h> |
22 |
#endif |
23 |
|
24 |
#include <FileManager.h> |
25 |
#include <GraphicContext.h> |
26 |
#include <StringTable.h> |
27 |
#include <Toolkit.h> |
28 |
#include <Stream.h> |
29 |
#include <BinaryStream.h> |
30 |
|
31 |
#include "CustomGameScreen.h" |
32 |
#include "EndGameScreen.h" |
33 |
#include "Engine.h" |
34 |
#include "Game.h" |
35 |
#include "GlobalContainer.h" |
36 |
#include "LogFileManager.h" |
37 |
#include "MultiplayersHostScreen.h" |
38 |
#include "MultiplayersJoinScreen.h" |
39 |
#include "MultiplayersChooseMapScreen.h" |
40 |
#include "NetGame.h" |
41 |
#include "Utilities.h" |
42 |
#include "YOGScreen.h" |
43 |
#include "SoundMixer.h" |
44 |
#include "CampaignScreen.h" |
45 |
#include <iostream> |
46 |
|
47 |
|
48 |
Engine::Engine() |
49 |
{ |
50 |
net=NULL; |
51 |
for (int i=0; i<=40; i++) |
52 |
cpuStats[i]=0; |
53 |
logFile = globalContainer->logFileManager->getFile("Engine.log"); |
54 |
} |
55 |
|
56 |
Engine::~Engine() |
57 |
{ |
58 |
fprintf(logFile, "cpu usage stats:\n"); |
59 |
for (int i=0; i<=40; i++) |
60 |
fprintf(logFile, "%3d.%1d %% = %d\n", 100-(i*5)/2, (i&1)*5, cpuStats[i]); |
61 |
int sum=0; |
62 |
for (int i=0; i<=40; i++) |
63 |
sum+=cpuStats[i]; |
64 |
fprintf(logFile, "\n"); |
65 |
fprintf(logFile, "cpu usage graph:\n"); |
66 |
for (int i=0; i<=40; i++) |
67 |
{ |
68 |
fprintf(logFile, "%3d.%1d %% | ", 100-(i*5)/2, (i&1)*5); |
69 |
double ratio=100.*(double)cpuStats[i]/(double)sum; |
70 |
int jmax=(int)(ratio+0.5); |
71 |
for (int j=0; j<jmax; j++) |
72 |
fprintf(logFile, "*"); |
73 |
fprintf(logFile, "\n"); |
74 |
} |
75 |
|
76 |
if (net) |
77 |
{ |
78 |
delete net; |
79 |
net=NULL; |
80 |
} |
81 |
} |
82 |
|
83 |
int Engine::initCampaign() |
84 |
{ |
85 |
ChooseMapScreen loadCampaignScreen("campaigns", "map", true);; |
86 |
int lgs = loadCampaignScreen.execute(globalContainer->gfx, 40); |
87 |
if (lgs == ChooseMapScreen::CANCEL) |
88 |
return EE_CANCEL; |
89 |
|
90 |
return initCampaign(glob2NameToFilename("campaigns", loadCampaignScreen.sessionInfo.getMapNameC(), "map")); |
91 |
} |
92 |
|
93 |
int Engine::initCampaign(const std::string &mapName) |
94 |
{ |
95 |
if (!loadGame(mapName)) |
96 |
return EE_CANT_LOAD_MAP; |
97 |
|
98 |
// we make a player for each team |
99 |
int playerNumber=0; |
100 |
bool wasHuman=false; |
101 |
char name[BasePlayer::MAX_NAME_LENGTH]; |
102 |
for (int i=0; i<gui.game.session.numberOfTeam; i++) |
103 |
{ |
104 |
if (gui.game.teams[i]->type==BaseTeam::T_AI) |
105 |
{ |
106 |
snprintf(name, BasePlayer::MAX_NAME_LENGTH, "AI Player %d", playerNumber); |
107 |
gui.game.players[playerNumber]=new Player(playerNumber, name, gui.game.teams[i], BasePlayer::P_AI); |
108 |
} |
109 |
else if (gui.game.teams[i]->type==BaseTeam::T_HUMAN) |
110 |
{ |
111 |
if (!wasHuman) |
112 |
{ |
113 |
gui.localPlayer=playerNumber; |
114 |
gui.localTeamNo=i; |
115 |
snprintf(name, BasePlayer::MAX_NAME_LENGTH, "Player %d", playerNumber); |
116 |
wasHuman=true; |
117 |
gui.game.players[playerNumber]=new Player(playerNumber, name, gui.game.teams[i], BasePlayer::P_LOCAL); |
118 |
} |
119 |
else |
120 |
{ |
121 |
snprintf(name, BasePlayer::MAX_NAME_LENGTH, "AI Player %d", playerNumber); |
122 |
gui.game.players[playerNumber]=new Player(playerNumber, name, gui.game.teams[i], BasePlayer::P_AI); |
123 |
} |
124 |
} |
125 |
else |
126 |
assert(false); |
127 |
gui.game.teams[i]->numberOfPlayer=1; |
128 |
gui.game.teams[i]->playersMask=(1<<playerNumber); |
129 |
playerNumber++; |
130 |
} |
131 |
|
132 |
if (!wasHuman) |
133 |
{ |
134 |
std::cerr << " Engine::initCampaign(\"" << mapName << "\") : error, can't find any human player" << std::endl; |
135 |
return EE_CANT_FIND_PLAYER; |
136 |
} |
137 |
|
138 |
gui.game.session.numberOfPlayer=playerNumber; |
139 |
|
140 |
// if this is a campaign, show a screen |
141 |
if (gui.game.campaignText.length() > 0) |
142 |
{ |
143 |
CampaignScreen campaignScreen(gui.game.campaignText); |
144 |
campaignScreen.execute(globalContainer->gfx, 40); |
145 |
} |
146 |
|
147 |
// We do some cosmetic fix |
148 |
finalAdjustements(); |
149 |
|
150 |
// we create the net game |
151 |
net=new NetGame(NULL, gui.game.session.numberOfPlayer, gui.game.players); |
152 |
|
153 |
return EE_NO_ERROR; |
154 |
} |
155 |
|
156 |
int Engine::initCustom(void) |
157 |
{ |
158 |
CustomGameScreen customGameScreen; |
159 |
|
160 |
int cgs=customGameScreen.execute(globalContainer->gfx, 40); |
161 |
|
162 |
if (cgs==CustomGameScreen::CANCEL) |
163 |
return EE_CANCEL; |
164 |
if (cgs==-1) |
165 |
return -1; |
166 |
|
167 |
if (!gui.loadBase(&(customGameScreen.sessionInfo))) |
168 |
{ |
169 |
printf("Engine : Can't load map\n"); |
170 |
return EE_CANCEL; |
171 |
} |
172 |
int nbTeam=gui.game.session.numberOfTeam; |
173 |
if (nbTeam==0) |
174 |
return EE_CANCEL; |
175 |
|
176 |
char name[BasePlayer::MAX_NAME_LENGTH]; |
177 |
int i; |
178 |
int nbPlayer=0; |
179 |
|
180 |
for (i=0; i<16; i++) |
181 |
{ |
182 |
if (customGameScreen.isAIactive(i)) |
183 |
{ |
184 |
int teamColor=customGameScreen.getSelectedColor(i); |
185 |
if (i==0) |
186 |
{ |
187 |
gui.game.players[nbPlayer]=new Player(0, globalContainer->getUsername(), gui.game.teams[teamColor], BasePlayer::P_LOCAL); |
188 |
gui.localPlayer=nbPlayer; |
189 |
gui.localTeamNo=teamColor; |
190 |
} |
191 |
else |
192 |
{ |
193 |
AI::ImplementitionID iid=customGameScreen.getAiImplementation(i); |
194 |
snprintf(name, BasePlayer::MAX_NAME_LENGTH, "%s %d", Toolkit::getStringTable()->getString("[AI]", iid), nbPlayer-1); |
195 |
gui.game.players[nbPlayer]=new Player(i, name, gui.game.teams[teamColor], Player::playerTypeFromImplementitionID(iid)); |
196 |
} |
197 |
gui.game.teams[teamColor]->numberOfPlayer++; |
198 |
gui.game.teams[teamColor]->playersMask|=(1<<nbPlayer); |
199 |
nbPlayer++; |
200 |
} |
201 |
} |
202 |
gui.game.session.numberOfPlayer=nbPlayer; |
203 |
|
204 |
// We remove uncontrolled stuff from map |
205 |
gui.game.clearingUncontrolledTeams(); |
206 |
|
207 |
// set the correct alliance |
208 |
gui.game.setAIAlliance(); |
209 |
|
210 |
// We do some cosmetic fix |
211 |
finalAdjustements(); |
212 |
|
213 |
// we create the net game |
214 |
net=new NetGame(NULL, gui.game.session.numberOfPlayer, gui.game.players); |
215 |
|
216 |
return EE_NO_ERROR; |
217 |
} |
218 |
|
219 |
int Engine::initCustom(const std::string &gameName) |
220 |
{ |
221 |
if (!loadGame(gameName)) |
222 |
return EE_CANT_LOAD_MAP; |
223 |
|
224 |
// If the game is a network saved game, we need to toogle net players to ai players: |
225 |
for (int p=0; p<gui.game.session.numberOfPlayer; p++) |
226 |
{ |
227 |
printf("Engine::initCustom::player[%d].type=%d.\n", p, gui.game.players[p]->type); |
228 |
if (gui.game.players[p]->type==BasePlayer::P_IP) |
229 |
{ |
230 |
gui.game.players[p]->makeItAI(AI::toggleAI); |
231 |
printf("Engine::initCustom::net player (id %d) was made ai.\n", p); |
232 |
} |
233 |
} |
234 |
|
235 |
// We do some cosmetic fix |
236 |
gui.adjustLocalTeam(); |
237 |
if (!globalContainer->runNoX) |
238 |
{ |
239 |
gui.game.renderMiniMap(gui.localTeamNo); |
240 |
gui.adjustInitialViewport(); |
241 |
} |
242 |
|
243 |
// set the correct alliance |
244 |
gui.game.setAIAlliance(); |
245 |
|
246 |
// we create the net game |
247 |
net=new NetGame(NULL, gui.game.session.numberOfPlayer, gui.game.players); |
248 |
|
249 |
return EE_NO_ERROR; |
250 |
} |
251 |
|
252 |
int Engine::initLoadGame() |
253 |
{ |
254 |
ChooseMapScreen loadGameScreen("games", "game", true);; |
255 |
int lgs = loadGameScreen.execute(globalContainer->gfx, 40); |
256 |
if (lgs == ChooseMapScreen::CANCEL) |
257 |
return EE_CANCEL; |
258 |
|
259 |
return initCustom(loadGameScreen.sessionInfo.getFileName()); |
260 |
} |
261 |
|
262 |
void Engine::startMultiplayer(MultiplayersJoin *multiplayersJoin) |
263 |
{ |
264 |
int p=multiplayersJoin->myPlayerNumber; |
265 |
|
266 |
multiplayersJoin->destroyNet=false; |
267 |
for (int j=0; j<multiplayersJoin->sessionInfo.numberOfPlayer; j++) |
268 |
multiplayersJoin->sessionInfo.players[j].destroyNet=false; |
269 |
|
270 |
multiplayersJoin->sessionInfo.setLocal(p); |
271 |
|
272 |
gui.loadBase(&multiplayersJoin->sessionInfo); |
273 |
|
274 |
gui.localPlayer=p; |
275 |
gui.localTeamNo=multiplayersJoin->sessionInfo.players[p].teamNumber; |
276 |
assert(gui.localTeamNo<multiplayersJoin->sessionInfo.numberOfTeam); |
277 |
gui.localTeamNo=gui.localTeamNo % multiplayersJoin->sessionInfo.numberOfTeam; // Ugly relase case. |
278 |
|
279 |
// We remove uncontrolled stuff from map |
280 |
gui.game.clearingUncontrolledTeams(); |
281 |
|
282 |
// set the correct alliance |
283 |
gui.game.setAIAlliance(); |
284 |
|
285 |
// We do some cosmetic fix |
286 |
finalAdjustements(); |
287 |
|
288 |
// we create the net game |
289 |
net=new NetGame(multiplayersJoin->socket, gui.game.session.numberOfPlayer, gui.game.players); |
290 |
|
291 |
printf("Engine::localPlayer=%d, localTeamNb=%d\n", gui.localPlayer, gui.localTeamNo); |
292 |
} |
293 |
|
294 |
|
295 |
int Engine::initMutiplayerHost(bool shareOnYOG) |
296 |
{ |
297 |
MultiplayersChooseMapScreen multiplayersChooseMapScreen(shareOnYOG); |
298 |
|
299 |
int mpcms=multiplayersChooseMapScreen.execute(globalContainer->gfx, 40); |
300 |
|
301 |
if (mpcms==MultiplayersChooseMapScreen::CANCEL) |
302 |
return EE_CANCEL; |
303 |
if (mpcms==-1) |
304 |
return -1; |
305 |
|
306 |
printf("Engine::the game is sharing ...\n"); |
307 |
|
308 |
MultiplayersHostScreen multiplayersHostScreen(&(multiplayersChooseMapScreen.sessionInfo), shareOnYOG); |
309 |
int rc=multiplayersHostScreen.execute(globalContainer->gfx, 40); |
310 |
if (rc==MultiplayersHostScreen::STARTED) |
311 |
{ |
312 |
if (multiplayersHostScreen.multiplayersJoin==NULL) |
313 |
return EE_CANCEL; |
314 |
else |
315 |
{ |
316 |
if (multiplayersHostScreen.multiplayersJoin->myPlayerNumber!=-1) |
317 |
startMultiplayer(multiplayersHostScreen.multiplayersJoin); |
318 |
else |
319 |
return EE_CANCEL; |
320 |
} |
321 |
return EE_NO_ERROR; |
322 |
} |
323 |
else if (rc==-1) |
324 |
return -1; |
325 |
|
326 |
printf("Engine::initMutiplayerHost() rc=%d\n", rc); |
327 |
|
328 |
return EE_CANCEL; |
329 |
} |
330 |
|
331 |
int Engine::initMutiplayerJoin(void) |
332 |
{ |
333 |
MultiplayersJoinScreen multiplayersJoinScreen; |
334 |
|
335 |
int rc=multiplayersJoinScreen.execute(globalContainer->gfx, 40); |
336 |
if (rc==MultiplayersJoinScreen::STARTED) |
337 |
{ |
338 |
startMultiplayer(multiplayersJoinScreen.multiplayersJoin); |
339 |
|
340 |
return EE_NO_ERROR; |
341 |
} |
342 |
else if (rc==-1) |
343 |
return -1; |
344 |
|
345 |
return EE_CANCEL; |
346 |
} |
347 |
|
348 |
int Engine::run(void) |
349 |
{ |
350 |
bool doRunOnceAgain=true; |
351 |
|
352 |
// Stop menu music, load game music |
353 |
if (globalContainer->runNoX) |
354 |
assert(globalContainer->mix==NULL); |
355 |
else |
356 |
{ |
357 |
globalContainer->mix->setNextTrack(2, true); |
358 |
} |
359 |
|
360 |
while (doRunOnceAgain) |
361 |
{ |
362 |
Uint32 startTick, endTick; |
363 |
bool networkReadyToExecute=true; |
364 |
Sint32 ticksSpentInComputation=40; |
365 |
Sint32 computationAvailableTicks=0; |
366 |
Sint32 ticksToDelayInside=0; |
367 |
Sint32 missedTicksToWait=0; |
368 |
|
369 |
startTick=SDL_GetTicks(); |
370 |
while (gui.isRunning) |
371 |
{ |
372 |
// We always allow the user to use the gui: |
373 |
if (globalContainer->runNoX) |
374 |
{ |
375 |
if (!gui.getLocalTeam()->isAlive) |
376 |
{ |
377 |
printf("nox::gui.localTeam is dead\n"); |
378 |
gui.isRunning=false; |
379 |
} |
380 |
else if (gui.getLocalTeam()->hasWon) |
381 |
{ |
382 |
printf("nox::gui.localTeam has won\n"); |
383 |
gui.isRunning=false; |
384 |
} |
385 |
else if (gui.game.totalPrestigeReached) |
386 |
{ |
387 |
printf("nox::gui.game.totalPrestigeReached\n"); |
388 |
gui.isRunning=false; |
389 |
} |
390 |
} |
391 |
else |
392 |
gui.step(); |
393 |
|
394 |
Sint32 ticksDelayedInside=0; |
395 |
if (!gui.hardPause) |
396 |
{ |
397 |
// But some jobs have to be executed synchronously: |
398 |
if (networkReadyToExecute) |
399 |
{ |
400 |
gui.syncStep(); |
401 |
|
402 |
// We get and push local orders |
403 |
net->pushOrder(gui.getOrder(), gui.localPlayer); |
404 |
|
405 |
// We store full recursive checkSums data: |
406 |
gui.game.checkSum(net->getCheckSumsListsStorage(), net->getCheckSumsListsStorageForBuildings(), net->getCheckSumsListsStorageForUnits()); |
407 |
|
408 |
// we get and push ai orders |
409 |
for (int i=0; i<gui.game.session.numberOfPlayer; i++) |
410 |
if (gui.game.players[i]->ai) |
411 |
net->pushOrder(gui.game.players[i]->ai->getOrder(gui.gamePaused), i); |
412 |
|
413 |
ticksToDelayInside=net->ticksToDelayInside(); |
414 |
ticksDelayedInside=ticksToDelayInside+computationAvailableTicks/2; |
415 |
ticksDelayedInside=(ticksDelayedInside/10)*10; //SDL_Delay() is only 10[ms] acurate |
416 |
if (ticksDelayedInside>0) |
417 |
SDL_Delay(ticksDelayedInside);//Here we may need to wait a bit more, to wait other computers which are slower. |
418 |
else |
419 |
ticksDelayedInside=0; |
420 |
} |
421 |
else |
422 |
ticksDelayedInside=0; |
423 |
|
424 |
// We clear the last events |
425 |
gui.game.clearEventsStep(); |
426 |
|
427 |
// We proceed network: |
428 |
networkReadyToExecute=net->stepReadyToExecute(); |
429 |
|
430 |
// We get all currents orders from the network and execute them: |
431 |
for (int i=0; i<gui.game.session.numberOfPlayer; i++) |
432 |
{ |
433 |
Order *order=net->getOrder(i); |
434 |
gui.executeOrder(order); |
435 |
if (order->needToBeFreedByEngine) |
436 |
delete order; |
437 |
} |
438 |
net->stepExecuted(); |
439 |
|
440 |
// here we do the real work |
441 |
if (networkReadyToExecute && !gui.gamePaused && !gui.hardPause) |
442 |
gui.game.syncStep(gui.localTeamNo); |
443 |
} |
444 |
|
445 |
if (!globalContainer->runNoX) |
446 |
{ |
447 |
// we draw |
448 |
gui.drawAll(gui.localTeamNo); |
449 |
globalContainer->gfx->nextFrame(); |
450 |
|
451 |
// we compute timing |
452 |
endTick=SDL_GetTicks(); |
453 |
Sint32 spentTicks=endTick-startTick; |
454 |
ticksSpentInComputation=spentTicks-ticksDelayedInside; |
455 |
computationAvailableTicks=40-ticksSpentInComputation; |
456 |
Sint32 ticksToWait=computationAvailableTicks+ticksToDelayInside+missedTicksToWait; |
457 |
if (ticksToWait>0) |
458 |
{ |
459 |
missedTicksToWait=ticksToWait%10; |
460 |
ticksToWait=ticksToWait-missedTicksToWait; //SDL_Delay() is only 10[ms] acurate |
461 |
if (ticksToWait>0) |
462 |
SDL_Delay(ticksToWait); |
463 |
} |
464 |
else |
465 |
missedTicksToWait=0; |
466 |
startTick=SDL_GetTicks(); |
467 |
|
468 |
// we set CPU stats |
469 |
net->setLeftTicks(computationAvailableTicks);//We may have to tell others IP players to wait for our slow computer. |
470 |
gui.setCpuLoad(ticksSpentInComputation); |
471 |
if (networkReadyToExecute && !gui.gamePaused) |
472 |
{ |
473 |
Sint32 i=computationAvailableTicks; |
474 |
if (i<0) |
475 |
i=0; |
476 |
else if (i>=40) |
477 |
i=40; |
478 |
cpuStats[i]++; |
479 |
} |
480 |
} |
481 |
} |
482 |
|
483 |
delete net; |
484 |
net=NULL; |
485 |
|
486 |
if (gui.exitGlobCompletely) |
487 |
return -1; // There is no bypass for the "close window button" |
488 |
|
489 |
doRunOnceAgain=false; |
490 |
|
491 |
if (gui.toLoadGameFileName[0]) |
492 |
{ |
493 |
int rv=initCustom(gui.toLoadGameFileName); |
494 |
if (rv==EE_NO_ERROR) |
495 |
doRunOnceAgain=true; |
496 |
gui.toLoadGameFileName[0]=0; // Avoid the communication system between GameGUI and Engine to loop. |
497 |
} |
498 |
else if (gui.game.nextMap.length() > 0) |
499 |
{ |
500 |
// if we have won, managed to load next map, we do it again. Only campaigns can be linked, so we look into the campaigns subdirectory |
501 |
if (gui.game.isGameEnded && gui.getLocalTeam()->isAlive) |
502 |
{ |
503 |
std::string filename = std::string("campaigns/") + gui.game.nextMap; |
504 |
doRunOnceAgain = (initCampaign(filename.c_str()) == EE_NO_ERROR); |
505 |
} |
506 |
} |
507 |
} |
508 |
|
509 |
if (globalContainer->runNoX) |
510 |
return -1; |
511 |
else |
512 |
{ |
513 |
// Restart menu music |
514 |
assert(globalContainer->mix); |
515 |
globalContainer->mix->setNextTrack(1); |
516 |
|
517 |
// Display End Game Screen |
518 |
EndGameScreen endGameScreen(&gui); |
519 |
int result = endGameScreen.execute(globalContainer->gfx, 40); |
520 |
|
521 |
// Return |
522 |
return (result == -1) ? -1 : EE_NO_ERROR; |
523 |
} |
524 |
} |
525 |
|
526 |
bool Engine::loadGame(const std::string &filename) |
527 |
{ |
528 |
InputStream *stream = new BinaryInputStream(Toolkit::getFileManager()->openInputStreamBackend(filename)); |
529 |
if (stream->isEndOfStream()) |
530 |
{ |
531 |
std::cerr << "Engine::loadGame(\"" << filename << "\") : error, can't open file." << std::endl; |
532 |
delete stream; |
533 |
return false; |
534 |
} |
535 |
else |
536 |
{ |
537 |
bool res = gui.load(stream); |
538 |
delete stream; |
539 |
if (!res) |
540 |
{ |
541 |
std::cerr << "Engine::loadGame(\"" << filename << "\") : error, can't load game." << std::endl; |
542 |
return false; |
543 |
} |
544 |
} |
545 |
|
546 |
std::cout << "Engine::loadGame(\"" << filename << "\") : game successfully loaded." << std::endl; |
547 |
return true; |
548 |
} |
549 |
|
550 |
void Engine::finalAdjustements(void) |
551 |
{ |
552 |
gui.adjustLocalTeam(); |
553 |
if (!globalContainer->runNoX) |
554 |
{ |
555 |
gui.game.renderMiniMap(gui.localTeamNo); |
556 |
gui.adjustInitialViewport(); |
557 |
} |
558 |
} |