1 ////////////////////////////////////////////////////////////////////////////////
2 //    Scorched3D (c) 2000-2011
3 //
4 //    This file is part of Scorched3D.
5 //
6 //    Scorched3D is free software; you can redistribute it and/or modify
7 //    it under the terms of the GNU General Public License as published by
8 //    the Free Software Foundation; either version 2 of the License, or
9 //    (at your option) any later version.
10 //
11 //    Scorched3D is distributed in the hope that it will be useful,
12 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //    GNU General Public License for more details.
15 //
16 //    You should have received a copy of the GNU General Public License along
17 //    with this program; if not, write to the Free Software Foundation, Inc.,
18 //    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 ////////////////////////////////////////////////////////////////////////////////
20 
21 #include <server/ServerTurnsSequential.h>
22 #include <server/ScorchedServer.h>
23 #include <server/ServerSimulator.h>
24 #include <common/OptionsGame.h>
25 #include <common/OptionsScorched.h>
26 #include <common/OptionsTransient.h>
27 #include <simactions/PlayMovesSimAction.h>
28 #include <simactions/TankStopMoveSimAction.h>
29 #include <coms/ComsPlayedMoveMessage.h>
30 #include <tank/TankSort.h>
31 #include <tanket/Tanket.h>
32 #include <target/TargetContainer.h>
33 #include <tanket/TanketShotInfo.h>
34 #include <list>
35 
ServerTurnsSequential()36 ServerTurnsSequential::ServerTurnsSequential() :
37 	ServerTurns(true),
38 	playingPlayer_(0), nextMoveId_(0)
39 {
40 }
41 
~ServerTurnsSequential()42 ServerTurnsSequential::~ServerTurnsSequential()
43 {
44 }
45 
internalEnterState()46 void ServerTurnsSequential::internalEnterState()
47 {
48 	playingPlayer_ = 0;
49 	waitingPlayers_.clear();
50 
51 	OptionsGame::TurnType turnType = (OptionsGame::TurnType)
52 		ScorchedServer::instance()->getOptionsGame().getTurnType().getValue();
53 
54 	// On the very first round make the order random (if loser first chosen)
55 	// as there is no loser yet!
56 	if ((ScorchedServer::instance()->getOptionsTransient().getCurrentRoundNo() == 1) &&
57 		turnType == OptionsGame::TurnSequentialLoserFirst)
58 	{
59 		turnType = OptionsGame::TurnSequentialRandom;
60 	}
61 
62 	// Standard player ordering is the reverse of the tank score
63 	TankSort::getSortedTanksIds(
64 		ScorchedServer::instance()->getContext(),
65 		waitingPlayers_); // All tanks
66 	waitingPlayers_.reverse();
67 	{
68 		// Tankets wont be on the list, so add them
69 		std::map<unsigned int, Tanket*> &tankets =
70 			ScorchedServer::instance()->getTargetContainer().getTankets();
71 		std::map<unsigned int, Tanket*>::iterator itor;
72 		for (itor = tankets.begin();
73 			itor != tankets.end();
74 			++itor)
75 		{
76 			Tanket *tanket = itor->second;
77 			if (!tanket->getShotInfo().getUseNormalMoves()) continue;
78 
79 			tanket->getShotInfo().setMoveId(0);
80 			if (tanket->getType() == Target::TypeTanket)
81 			{
82 				waitingPlayers_.push_back(tanket->getPlayerId());
83 			}
84 		}
85 	}
86 
87 	// Check for a different ordering
88 	if (turnType == OptionsGame::TurnSequentialRandom)
89 	{
90 		// Create zero player vector
91 		std::vector<unsigned int> tmpPlayers;
92 		for (int i=0; i<(int) waitingPlayers_.size(); i++)
93 		{
94 			tmpPlayers.push_back(0);
95 		}
96 
97 		// Randomize list order into vector
98 		while (!waitingPlayers_.empty())
99 		{
100 			unsigned int player = waitingPlayers_.front();
101 			waitingPlayers_.pop_front();
102 			bool done = false;
103 			while (!done)
104 			{
105 				int pos = int(RAND * float(tmpPlayers.size()));
106 				if (pos < int(tmpPlayers.size()) && tmpPlayers[pos] == 0)
107 				{
108 					tmpPlayers[pos] = player;
109 					done = true;
110 				}
111 			}
112 		}
113 
114 		// Copy vector back to list
115 		for (int i=0; i<(int) tmpPlayers.size(); i++)
116 		{
117 			waitingPlayers_.push_back(tmpPlayers[i]);
118 		}
119 	}
120 }
121 
internalSimulate(fixed frameTime)122 void ServerTurnsSequential::internalSimulate(fixed frameTime)
123 {
124 	// Check if all the tanks have made their moves
125 	if (waitingPlayers_.empty() && playingPlayer_ == 0)
126 	{
127 		internalEnterState();
128 		incrementTurn();
129 	}
130 
131 	// Check tanks are alive
132 	Tanket *playingTanket =
133 		ScorchedServer::instance()->getTargetContainer().getTanketById(playingPlayer_);
134 	if (!playingTanket || !playingTanket->getAlive())
135 	{
136 		if (playingTanket && playingTanket->getShotInfo().getMoveId() != 0)
137 		{
138 			playMoveFinished(playingTanket);
139 		}
140 
141 		playingPlayer_ = 0;
142 	}
143 
144 	// Check if we are ready for the next player
145 	if (playingPlayer_ == 0)
146 	{
147 		while (!waitingPlayers_.empty())
148 		{
149 			unsigned int waitingPlayer = waitingPlayers_.front();
150 			waitingPlayers_.pop_front();
151 
152 			Tanket *waitingTanket =
153 				ScorchedServer::instance()->getTargetContainer().getTanketById(waitingPlayer);
154 			if (waitingTanket && waitingTanket->getAlive())
155 			{
156 				playingPlayer_ = waitingPlayer;
157 				makeMove(waitingTanket);
158 				break;
159 			}
160 		}
161 	}
162 }
163 
makeMove(Tanket * tanket)164 void ServerTurnsSequential::makeMove(Tanket *tanket)
165 {
166 	nextMoveId_++;
167 	fixed shotTime = fixed(
168 		ScorchedServer::instance()->getOptionsGame().getShotTime());
169 	playMove(tanket, nextMoveId_, shotTime);
170 }
171 
internalMoveFinished(ComsPlayedMoveMessage & playedMessage)172 void ServerTurnsSequential::internalMoveFinished(ComsPlayedMoveMessage &playedMessage)
173 {
174 	unsigned int playerId = playedMessage.getPlayerId();
175 	unsigned int moveId = playedMessage.getMoveId();
176 	if (playerId != playingPlayer_) return;
177 	Tanket *tanket = ScorchedServer::instance()->getTargetContainer().getTanketById(playerId);
178 	if (!tanket || tanket->getShotInfo().getMoveId() != moveId) return;
179 
180 	playMoveFinished(tanket);
181 	playingPlayer_ = 0;
182 
183 	if (tanket->getAlive())
184 	{
185 		std::list<ComsPlayedMoveMessage*> messages;
186 		messages.push_back(new ComsPlayedMoveMessage(playedMessage));
187 		playShots(messages, moveId, true, true);
188 	}
189 }
190