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