1 /***************************************************************************
2  *      Mechanized Assault and Exploration Reloaded Projectfile            *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
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                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18  ***************************************************************************/
19 
20 /* Author: Paul Grathwohl */
21 
22 #include "game/logic/casualtiestracker.h"
23 
24 #include "game/data/units/unitdata.h"
25 #include "utility/log.h"
26 #include "netmessage.h"
27 #include "tinyxml2.h"
28 
29 using namespace std;
30 using namespace tinyxml2;
31 
32 //--------------------------------------------------------------------------
initFromXML(XMLElement * casualtiesNode)33 void cCasualtiesTracker::initFromXML (XMLElement* casualtiesNode)
34 {
35 	casualtiesPerPlayer.clear();
36 
37 	XMLElement* playerNode = casualtiesNode->FirstChildElement ("PlayerCasualties");
38 	while (playerNode != 0)
39 	{
40 		int playerNr = 0;
41 		if (playerNode->QueryIntAttribute ("PlayerNr", &playerNr) == XML_NO_ERROR)
42 		{
43 			XMLElement* casualtyNode = playerNode->FirstChildElement ("Casualty");
44 			while (casualtyNode != 0)
45 			{
46 				sID unitID;
47 				int losses;
48 				if (casualtyNode->QueryIntAttribute ("ID_Fst", & (unitID.iFirstPart)) == XML_NO_ERROR
49 					&& casualtyNode->QueryIntAttribute ("ID_Snd", & (unitID.iSecondPart)) == XML_NO_ERROR
50 					&& casualtyNode->QueryIntAttribute ("Losses", & (losses)) == XML_NO_ERROR)
51 				{
52 					setCasualty (unitID, losses, playerNr);
53 				}
54 				casualtyNode = casualtyNode->NextSiblingElement ("Casualty");
55 			}
56 		}
57 		playerNode = playerNode->NextSiblingElement ("PlayerCasualties");
58 	}
59 
60 	casualtiesChanged();
61 }
62 
63 //--------------------------------------------------------------------------
storeToXML(XMLElement * casualtiesNode) const64 void cCasualtiesTracker::storeToXML (XMLElement* casualtiesNode) const
65 {
66 	// add sub elements for every player that contain all his casualties
67 	for (size_t i = 0; i < casualtiesPerPlayer.size(); i++)
68 	{
69 		const CasualtiesOfPlayer& casualtiesOfPlayer = casualtiesPerPlayer[i];
70 
71 		XMLElement* playerNode = casualtiesNode->GetDocument()->NewElement ("PlayerCasualties");
72 		casualtiesNode->LinkEndChild (playerNode);  // playerNode is now owned by casualtiesNode
73 		playerNode->SetAttribute ("PlayerNr", iToStr (casualtiesOfPlayer.playerNr).c_str());
74 
75 		// add sub elements for each casualty of the current player
76 		for (size_t j = 0; j < casualtiesOfPlayer.casualties.size(); j++)
77 		{
78 			const Casualty& casualty = casualtiesOfPlayer.casualties[j];
79 
80 			XMLElement* casualtyNode = casualtiesNode->GetDocument()->NewElement ("Casualty");
81 			playerNode->LinkEndChild (casualtyNode);  // casualtyNode is now owned by playerNode
82 			casualtyNode->SetAttribute ("ID_Fst", iToStr (casualty.unitID.iFirstPart).c_str());
83 			casualtyNode->SetAttribute ("ID_Snd", iToStr (casualty.unitID.iSecondPart).c_str());
84 			casualtyNode->SetAttribute ("Losses", iToStr (casualty.numberOfLosses).c_str());
85 		}
86 	}
87 }
88 
89 //--------------------------------------------------------------------------
logCasualty(sID unitType,int playerNr)90 void cCasualtiesTracker::logCasualty (sID unitType, int playerNr)
91 {
92 	setCasualty (unitType, getCasualtiesOfUnitType (unitType, playerNr) + 1, playerNr);
93 
94 	casualtiesChanged();
95 }
96 
97 //--------------------------------------------------------------------------
setCasualty(sID unitType,int numberOfLosses,int playerNr)98 void cCasualtiesTracker::setCasualty (sID unitType, int numberOfLosses, int playerNr)
99 {
100 	auto signalCaller = makeScopedOperation ([ = ]() { casualtyChanged (unitType, playerNr); });
101 
102 	vector<Casualty>& casualties = getCasualtiesOfPlayer (playerNr);
103 
104 	for (size_t i = 0; i != casualties.size(); ++i)
105 	{
106 		if (unitType == casualties[i].unitID)
107 		{
108 			casualties[i].numberOfLosses = numberOfLosses;
109 			return;
110 		}
111 	}
112 	Casualty newCasualtyEntry;
113 	newCasualtyEntry.numberOfLosses = numberOfLosses;
114 	newCasualtyEntry.unitID = unitType;
115 	for (size_t i = 0; i != casualties.size(); ++i)
116 	{
117 		if (unitType.less_vehicleFirst (casualties[i].unitID))
118 		{
119 			vector<Casualty>::iterator it = casualties.begin();
120 			casualties.insert (it + i, newCasualtyEntry);
121 			return;
122 		}
123 	}
124 	casualties.push_back (newCasualtyEntry);
125 }
126 
127 //--------------------------------------------------------------------------
getCasualtiesOfUnitType(sID unitType,int playerNr) const128 int cCasualtiesTracker::getCasualtiesOfUnitType (sID unitType, int playerNr) const
129 {
130 	const vector<Casualty>& casualties = getCasualtiesOfPlayer (playerNr);
131 	for (unsigned int i = 0; i < casualties.size(); i++)
132 	{
133 		if (unitType == casualties[i].unitID)
134 			return casualties[i].numberOfLosses;
135 	}
136 	return 0;
137 }
138 
139 //--------------------------------------------------------------------------
getUnitTypesWithLosses() const140 vector<sID> cCasualtiesTracker::getUnitTypesWithLosses() const
141 {
142 	vector<sID> result;
143 
144 	for (size_t i = 0; i != casualtiesPerPlayer.size(); ++i)
145 	{
146 		const vector<Casualty>& casualties = casualtiesPerPlayer[i].casualties;
147 		for (size_t entryIdx = 0; entryIdx != casualties.size(); ++entryIdx)
148 		{
149 			const Casualty& casualty = casualties[entryIdx];
150 			bool containedInResult = false;
151 			for (size_t j = 0; j != result.size(); ++j)
152 			{
153 				if (result[j] == casualty.unitID)
154 				{
155 					containedInResult = true;
156 					break;
157 				}
158 			}
159 			if (containedInResult == true) continue;
160 
161 			bool inserted = false;
162 			for (size_t j = 0; j != result.size(); ++j)
163 			{
164 				const sID unitID = casualty.unitID;
165 
166 				// buildings should be inserted first
167 				if (unitID.less_buildingFirst (result[j]))
168 				{
169 					result.insert (result.begin() + j, casualty.unitID);
170 					inserted = true;
171 					break;
172 				}
173 			}
174 			if (inserted == false)
175 				result.push_back (casualty.unitID);
176 		}
177 	}
178 	return result;
179 }
180 
181 //--------------------------------------------------------------------------
debugPrint()182 void cCasualtiesTracker::debugPrint()
183 {
184 	for (unsigned int i = 0; i < casualtiesPerPlayer.size(); i++)
185 	{
186 		Log.write ("Casualties of Player: " + iToStr (casualtiesPerPlayer[i].playerNr), cLog::eLOG_TYPE_DEBUG);
187 
188 		const vector<Casualty>& casualties = casualtiesPerPlayer[i].casualties;
189 		for (unsigned int entryIdx = 0; entryIdx < casualties.size(); entryIdx++)
190 		{
191 			const sUnitData* unitData = casualties[entryIdx].unitID.getUnitDataOriginalVersion();
192 			if (unitData != nullptr)
193 				Log.write ("  " + unitData->name + lngPack.i18n ("Text~Punctuation~Colon") + iToStr (casualties[entryIdx].numberOfLosses), cLog::eLOG_TYPE_DEBUG);
194 			else
195 				Log.write ("Invalid Casualty: Can't get unitData from sID", cLog::eLOG_TYPE_DEBUG);
196 		}
197 	}
198 }
199 
200 //--------------------------------------------------------------------------
updateCasualtiesFromNetMessage(cNetMessage * message)201 void cCasualtiesTracker::updateCasualtiesFromNetMessage (cNetMessage* message)
202 {
203 	if (message == nullptr)
204 		return;
205 	const int dataSetsInMessage = message->popInt16();
206 	for (int dataSet = 0; dataSet < dataSetsInMessage; dataSet++)
207 	{
208 		const int playerNr = message->popInt16();
209 		const int nrCasualtyReports = message->popInt16();
210 		for (int i = 0; i < nrCasualtyReports; i++)
211 		{
212 			const sID unitType = message->popID();
213 			const int numberLosses = message->popInt32();
214 			setCasualty (unitType, numberLosses, playerNr);
215 		}
216 	}
217 
218 	casualtiesChanged();
219 }
220 
221 //--------------------------------------------------------------------------
222 std::vector<std::unique_ptr<cNetMessage>>
prepareNetMessagesForClient(int msgType)223 cCasualtiesTracker::prepareNetMessagesForClient (int msgType)
224 {
225 	std::vector<std::unique_ptr<cNetMessage>> messages;
226 	std::unique_ptr<cNetMessage> message = nullptr;
227 	int entriesInMessageForPlayer = 0;
228 	int dataSetsInMessage = 0;
229 	for (unsigned int i = 0; i < casualtiesPerPlayer.size(); i++)
230 	{
231 		if (entriesInMessageForPlayer > 0)
232 			dataSetsInMessage++;
233 		const int currentPlayer = casualtiesPerPlayer[i].playerNr;
234 		entriesInMessageForPlayer = 0;
235 		vector<Casualty>& casualties = casualtiesPerPlayer[i].casualties;
236 		for (unsigned int entryIdx = 0; entryIdx < casualties.size(); entryIdx++)
237 		{
238 			if (message == nullptr)
239 			{
240 				message = std::make_unique<cNetMessage> (msgType);
241 				entriesInMessageForPlayer = 0;
242 				dataSetsInMessage = 1;
243 			}
244 
245 			message->pushInt32 (casualties[entryIdx].numberOfLosses);
246 			message->pushID (casualties[entryIdx].unitID);
247 			entriesInMessageForPlayer++;
248 
249 			if (message->iLength + 4 + 4 + 8 > PACKAGE_LENGTH)
250 			{
251 				message->pushInt16 (entriesInMessageForPlayer);
252 				message->pushInt16 (currentPlayer);
253 				message->pushInt16 (dataSetsInMessage);
254 				entriesInMessageForPlayer = 0;
255 				messages.push_back (std::move (message));
256 			}
257 		}
258 		if (message != nullptr && entriesInMessageForPlayer > 0)
259 		{
260 			message->pushInt16 (entriesInMessageForPlayer);
261 			message->pushInt16 (currentPlayer);
262 		}
263 	}
264 	if (message != nullptr)
265 	{
266 		message->pushInt16 (dataSetsInMessage);
267 		messages.push_back (std::move (message));
268 	}
269 	return messages;
270 }
271 
272 //--------------------------------------------------------------------------
getCasualtiesOfPlayer(int playerNr) const273 vector<cCasualtiesTracker::Casualty>& cCasualtiesTracker::getCasualtiesOfPlayer (int playerNr) const
274 {
275 	for (unsigned int i = 0; i < casualtiesPerPlayer.size(); i++)
276 	{
277 		if (casualtiesPerPlayer[i].playerNr == playerNr)
278 			return casualtiesPerPlayer[i].casualties;
279 	}
280 
281 	CasualtiesOfPlayer newCasualtiesOfPlayer;
282 	newCasualtiesOfPlayer.playerNr = playerNr;
283 	for (unsigned int i = 0; i < casualtiesPerPlayer.size(); i++)
284 	{
285 		if (playerNr < casualtiesPerPlayer[i].playerNr)
286 		{
287 			vector<CasualtiesOfPlayer>::iterator it = casualtiesPerPlayer.begin();
288 			casualtiesPerPlayer.insert (it + i, newCasualtiesOfPlayer);
289 			return casualtiesPerPlayer[i].casualties;
290 		}
291 	}
292 	casualtiesPerPlayer.push_back (newCasualtiesOfPlayer);
293 	return casualtiesPerPlayer.back().casualties;
294 }
295