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