1 /*
2 * Open Fodder
3 * ---------------
4 *
5 * Copyright (C) 2008-2018 Open Fodder
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 */
22
23 #include "stdafx.hpp"
24 #include "Utils/json.hpp"
25
26 using Json = nlohmann::json;
27
28 /** Goals **/
29 const std::vector<std::string> mMissionGoal_Titles = {
30 "KILL ALL ENEMY",
31 "DESTROY ENEMY BUILDINGS",
32 "RESCUE HOSTAGES",
33 "PROTECT ALL CIVILIANS",
34 "KIDNAP ENEMY LEADER",
35 "DESTROY FACTORY",
36 "DESTROY COMPUTER",
37 "GET CIVILIAN HOME",
38 "ACTIVATE ALL SWITCHES", // CF2
39 "RESCUE HOSTAGE" // CF2
40 };
41
cCampaign()42 cCampaign::cCampaign() {
43 Clear();
44 }
45
GetPathToCampaign() const46 std::string cCampaign::GetPathToCampaign() const {
47
48 return GetPath(false) + ".ofc";
49 }
50
GetPathToFile(const std::string & pName) const51 std::string cCampaign::GetPathToFile(const std::string& pName) const {
52
53 return GetPath() + pName;
54 }
55
56 /**
57 * Get the path to the current campaign
58 */
GetPath(const bool pTrailingSeperator) const59 std::string cCampaign::GetPath( const bool pTrailingSeperator ) const {
60 std::string path;
61
62 if (mUseCustomPath)
63 path = mPath;
64 else
65 path = g_ResourceMan->GetCampaignData(mName);
66
67 if(pTrailingSeperator && path.size())
68 path += gPathSeperator;
69
70 return path;
71 }
72
LoadCustomMapFromPath(const std::string & pPath)73 bool cCampaign::LoadCustomMapFromPath(const std::string& pPath) {
74 Clear();
75 std::string CustomMapName = pPath;
76
77 auto a = CustomMapName.find_last_of("/") + 1;
78 CustomMapName.erase(CustomMapName.begin(), CustomMapName.begin() + a);
79
80 LoadCustomMap(CustomMapName);
81
82 // Change the path to this map
83 std::shared_ptr<cPhase> Phase = mMissions.back()->mPhases.back();
84 Phase->mMapFilename = pPath.substr(0, pPath.size() - 4);
85
86 mUseCustomPath = true;
87 return true;
88 }
89
90 /**
91 * Load a single custom map
92 */
LoadCustomMap(const std::string & pMapName)93 bool cCampaign::LoadCustomMap(const std::string& pMapName) {
94 Clear();
95 std::string CustomMapName = pMapName;
96
97 // ".map"
98 CustomMapName.resize(CustomMapName.size() - 4);
99
100 // Map / Phase names must be upper case
101 std::transform(CustomMapName.begin(), CustomMapName.end(), CustomMapName.begin(), ::toupper);
102
103 SetSingleMapCampaign();
104 std::shared_ptr<cMission> Mission = mMissions.back();
105 std::shared_ptr<cPhase> Phase = Mission->mPhases.back();
106
107 Mission->mName = CustomMapName;
108
109 Phase->mMapFilename = pMapName.substr(0, pMapName.size() - 4);
110
111 // TODO: Try load these from file before using defaults
112 Phase->mName = CustomMapName;
113 Phase->mGoals.push_back({ eObjective_Kill_All_Enemy });
114 Phase->mAggression = { 4, 8 };
115
116 mIsCustomMap = true;
117 return true;
118 }
119
SaveCampaign()120 bool cCampaign::SaveCampaign() {
121 Json Campaign;
122 Campaign["Author"] = mAuthor;
123 Campaign["Name"] = mName;
124
125 // Each Mission
126 for (auto CurrentMission : mMissions) {
127 Json Mission;
128 Mission["Name"] = CurrentMission->mName;
129
130 // Each Phase of a mission
131 for(auto CurrentPhase : CurrentMission->mPhases) {
132 Json Phase;
133 Phase["MapName"] = CurrentPhase->mMapFilename;
134 Phase["Name"] = CurrentPhase->mName;
135 Phase["Aggression"][0] = CurrentPhase->mAggression.mMin;
136 Phase["Aggression"][1] = CurrentPhase->mAggression.mMax;
137
138 Phase["Grenades"] = CurrentPhase->mGrenades;
139 Phase["Rockets"] = CurrentPhase->mRockets;
140
141 for (auto Goal : CurrentPhase->mGoals)
142 Phase["Objectives"].push_back(mMissionGoal_Titles[Goal - 1]);
143
144 Mission["Phases"].push_back(Phase);
145 }
146 Campaign["Missions"].push_back(Mission);
147 }
148
149 // Save the campaign
150 std::ofstream MissionFile(GetPathToCampaign());
151 if (MissionFile.is_open()) {
152 MissionFile << Campaign.dump(1);
153 MissionFile.close();
154 return true;
155 }
156
157 return false;
158 }
159
160 /**
161 * Load a campaign
162 */
LoadCampaign(const std::string & pName,bool pCustom,bool pDirectPath)163 bool cCampaign::LoadCampaign(const std::string& pName, bool pCustom, bool pDirectPath) {
164
165 if (!pName.size())
166 return false;
167
168 Clear();
169 mName = pName;
170 mPath = pName;
171 mUseCustomPath = pDirectPath;
172
173 if (mUseCustomPath) {
174 auto Final = pName.find_last_of(gPathSeperator);
175
176 mName = pName.substr(0, Final);
177 }
178
179 std::ifstream MissionSetFile(GetPathToCampaign());
180 if (MissionSetFile.is_open()) {
181 Json MissionSet = Json::parse(MissionSetFile);
182
183 mAuthor = MissionSet["Author"];
184 mName = MissionSet["Name"];
185
186 mIsCustomCampaign = pCustom;
187
188 // Loop through the missions in this set
189 for (auto& Mission : MissionSet["Missions"]) {
190 std::shared_ptr<cMission> newMission = std::make_shared<cMission>();
191 mMissions.push_back(newMission);
192
193 newMission->mName = Mission["Name"];
194
195 // Each Map (Phase)
196 for (auto& Phase : Mission["Phases"]) {
197 std::shared_ptr<cPhase> newPhase = std::make_shared<cPhase>();
198 newMission->mPhases.push_back(newPhase);
199
200 newPhase->mName = Phase["Name"];
201 newPhase->mMapFilename = Phase["MapName"];
202 if (Phase["Aggression"].size()) {
203 newPhase->mAggression.mMin = Phase["Aggression"][0];
204 newPhase->mAggression.mMax = Phase["Aggression"][1];
205 }
206
207 if(Phase["Grenades"].size())
208 newPhase->mGrenades = Phase["Grenades"];
209
210 if(Phase["Rockets"].size())
211 newPhase->mRockets = Phase["Rockets"];
212
213
214 // Each map goal
215 for (std::string ObjectiveName : Phase["Objectives"]) {
216 transform(ObjectiveName.begin(), ObjectiveName.end(), ObjectiveName.begin(), toupper);
217
218 // Check each goal
219 int x = 0;
220 for (const std::string& GoalTitle : mMissionGoal_Titles) {
221 ++x;
222 if (GoalTitle == ObjectiveName) {
223 newPhase->mGoals.push_back(static_cast<ePhaseObjective>(x));
224 break;
225 }
226 }
227 }
228 }
229 }
230 return true;
231 }
232
233 return false;
234 }
235
236 /**
237 * Clear all missions/map names, goals and aggression rates
238 */
Clear(const std::string & pName,const bool pDirectPath)239 void cCampaign::Clear(const std::string& pName, const bool pDirectPath) {
240 mIsCustomMap = false;
241 mUseCustomPath = pDirectPath;
242 mIsRandom = false;
243 mIsCustomCampaign = false;
244 mName = pName;
245 mAuthor = "";
246 mPath = pName;
247
248 if (mUseCustomPath) {
249 auto Final = pName.find_last_of("/");
250 mName = pName.substr(0, Final);
251 }
252
253 mMissions.clear();
254 }
255
getCMap(std::shared_ptr<cPhase> pPhase) const256 std::shared_ptr<cMap> cCampaign::getCMap(std::shared_ptr<cPhase> pPhase) const {
257
258 auto map = std::make_shared<cOriginalMap>(getMap(pPhase), getSprites(pPhase), mName == "Cannon Fodder 2");
259
260 return map;
261 }
262
getMap(std::shared_ptr<cPhase> pPhase) const263 tSharedBuffer cCampaign::getMap(std::shared_ptr<cPhase> pPhase) const {
264 std::string FinalName = pPhase->mMapFilename + ".map";
265 std::string FinalPath = GetPathToFile(FinalName);
266
267 // If no campaign folder exists, load from the currently loaded resource
268 if (!g_ResourceMan->FileExists(FinalPath))
269 return g_Resource->fileGet(FinalName);
270
271 // Otherwise load it from the campaign path
272 return g_ResourceMan->FileRead(FinalPath);
273 }
274
getSprites(std::shared_ptr<cPhase> pPhase) const275 tSharedBuffer cCampaign::getSprites(std::shared_ptr<cPhase> pPhase) const {
276 std::string FinalName = pPhase->mMapFilename + ".spt";
277 std::string FinalPath = GetPathToFile(FinalName);
278
279 // If no campaign folder exists, load from the currently loaded resource
280 if (!g_ResourceMan->FileExists(FinalPath))
281 return g_Resource->fileGet(FinalName);
282
283 // Otherwise load it from the campaign path
284 return g_ResourceMan->FileRead(FinalPath);
285 }
286
287 /**
288 * Get the mission
289 */
getMission(size_t pMissionNumber)290 std::shared_ptr<cMission> cCampaign::getMission(size_t pMissionNumber) {
291 if(!pMissionNumber)
292 pMissionNumber = 1;
293
294 if (!mMissions.size() || pMissionNumber > mMissions.size())
295 return 0;
296
297 return mMissions[pMissionNumber - 1];
298 }
299
SetSingleMapCampaign()300 void cCampaign::SetSingleMapCampaign() {
301 Clear("Single Map", false);
302 mPath = "";
303
304 mMissions.push_back(std::make_shared<cMission>());
305 mMissions.back()->mPhases.push_back(std::make_shared<cPhase>());
306 }
307
SetCustomCampaign()308 void cCampaign::SetCustomCampaign() {
309 mIsCustomCampaign = true;
310 mUseCustomPath = false;
311
312 Clear();
313 }
314
CreateCustomCampaign()315 void cCampaign::CreateCustomCampaign() {
316 SetCustomCampaign();
317
318 mMissions.push_back(std::make_shared<cMission>());
319 mMissions.back()->mPhases.push_back(std::make_shared<cPhase>());
320 }
321
getName() const322 std::string cCampaign::getName() const {
323 return mName;
324 }
325
isCustom() const326 bool cCampaign::isCustom() const {
327
328 return mIsCustomCampaign;
329 }
330
isRandom() const331 bool cCampaign::isRandom() const {
332
333 return mIsRandom;
334 }
335