MapValidator.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. /*
  2. FinalSun/FinalAlert 2 Mission Editor
  3. Copyright (C) 1999-2024 Electronic Arts, Inc.
  4. Authored by Matthias Wagner
  5. This program is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  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. You should have received a copy of the GNU General Public License
  14. along with this program. If not, see <https://www.gnu.org/licenses/>.
  15. */
  16. // MapValidator.cpp: implementation file
  17. //
  18. #include "stdafx.h"
  19. #include "finalsun.h"
  20. #include "MapValidator.h"
  21. #include "mapdata.h"
  22. #include "variables.h"
  23. #include "functions.h"
  24. #include "inlines.h"
  25. #include <algorithm>
  26. #include <string>
  27. #ifdef _DEBUG
  28. #define new DEBUG_NEW
  29. #undef THIS_FILE
  30. static char THIS_FILE[] = __FILE__;
  31. #endif
  32. /////////////////////////////////////////////////////////////////////////////
  33. // dialog field CMapValidator
  34. CMapValidator::CMapValidator(CWnd* pParent /*=NULL*/)
  35. : CDialog(CMapValidator::IDD, pParent)
  36. {
  37. //{{AFX_DATA_INIT(CMapValidator)
  38. //}}AFX_DATA_INIT
  39. }
  40. void CMapValidator::DoDataExchange(CDataExchange* pDX)
  41. {
  42. CDialog::DoDataExchange(pDX);
  43. //{{AFX_DATA_MAP(CMapValidator)
  44. DDX_Control(pDX, IDC_MAPPROBLEMS, m_MapProblemList);
  45. //}}AFX_DATA_MAP
  46. }
  47. BEGIN_MESSAGE_MAP(CMapValidator, CDialog)
  48. //{{AFX_MSG_MAP(CMapValidator)
  49. //}}AFX_MSG_MAP
  50. END_MESSAGE_MAP()
  51. /////////////////////////////////////////////////////////////////////////////
  52. // message handlers CMapValidator
  53. void CMapValidator::UpdateStrings()
  54. {
  55. SetWindowText(GetLanguageStringACP("MapValidatorCap"));
  56. SetDlgItemText(IDC_LPROBLEMSFOUND, GetLanguageStringACP("MapValidatorProblemsFound"));
  57. SetDlgItemText(IDOK, GetLanguageStringACP("OK"));
  58. SetDlgItemText(IDCANCEL, GetLanguageStringACP("Cancel"));
  59. }
  60. BOOL CMapValidator::OnInitDialog()
  61. {
  62. CDialog::OnInitDialog();
  63. UpdateStrings();
  64. m_ProblemImages.Create(16,16,ILC_COLOR8 | ILC_MASK,0, 50 );
  65. CBitmap bmpIcons;
  66. bmpIcons.LoadBitmap(IDB_MV_ICONS);
  67. m_ProblemImages.Add(&bmpIcons, RGB(255,255,255));
  68. m_MapProblemList.SetImageList(&m_ProblemImages,LVSIL_SMALL);
  69. m_MapProblemList.SetImageList(&m_ProblemImages,LVSIL_NORMAL);
  70. auto col = m_MapProblemList.InsertColumn(0, "");
  71. //RECT r;
  72. //m_MapProblemList.GetClientRect(&r);
  73. BOOL bSaveAble=CheckMap();
  74. m_MapProblemList.SetColumnWidth(col, LVSCW_AUTOSIZE);
  75. if(bSaveAble==FALSE) GetDlgItem(IDOK)->EnableWindow(FALSE);
  76. return TRUE;
  77. }
  78. void AddItemWithNewLine(CListCtrl& ctrl, CString s, int image)
  79. {
  80. s.Replace("\\n", "\n");
  81. int n = ctrl.GetItemCount();
  82. for (const auto& line : Split(s, '\n'))
  83. {
  84. ctrl.InsertItem(n++, line, image);
  85. image = 2;
  86. }
  87. }
  88. /*
  89. CMapValidator::CheckMap();
  90. Returns TRUE, when the map is saveable, but may not work.
  91. If FALSE is returned, you should not anymore allow to save the map.
  92. */
  93. BOOL CMapValidator::CheckMap()
  94. {
  95. BOOL bAllow=TRUE;
  96. // now check the map
  97. Map->UpdateIniFile(MAPDATA_UPDATE_TO_INI);
  98. CIniFile& ini=Map->GetIniFile();
  99. if(ini.sections.find("Map")==ini.sections.end() )
  100. {
  101. bAllow=FALSE;
  102. AddItemWithNewLine(m_MapProblemList, GetLanguageStringACP("MV_NoMap"), 0);
  103. }
  104. if(ini.sections.find("Basic")==ini.sections.end() || ini.sections["Basic"].values.size()==0)
  105. {
  106. bAllow=FALSE;
  107. AddItemWithNewLine(m_MapProblemList, GetLanguageStringACP("MV_NoBasic"), 0);
  108. }
  109. else
  110. {
  111. if(ini.sections["Basic"].values["Name"].GetLength()==0)
  112. AddItemWithNewLine(m_MapProblemList, GetLanguageStringACP("MV_NoName"), 1);
  113. if(ini.sections["Basic"].values.find("Player")==ini.sections["Basic"].values.end())
  114. {
  115. #ifdef TS_MODE
  116. if(ini.sections.find(MAPHOUSES)!=ini.sections.end() && ini.sections["Houses"].values.size()>0)
  117. {
  118. AddItemWithNewLine(m_MapProblemList, GetLanguageStringACP("MV_HousesButNoPlayer"), 1);
  119. AddItemWithNewLine(m_MapProblemList, GetLanguageStringACP("MV_HousesInMultiplayer"), 1);
  120. }
  121. #endif
  122. }
  123. if(ini.sections.find(MAPHOUSES)==ini.sections.end())
  124. {
  125. int d=Map->GetWaypointCount();
  126. int below8found=0;
  127. int i;
  128. for(i=0;i<d;i++)
  129. {
  130. DWORD pos;
  131. CString id;
  132. Map->GetWaypointData(i, &id, &pos);
  133. if(atoi(id)<8)
  134. {
  135. below8found++;
  136. }
  137. }
  138. if(below8found<8)
  139. {
  140. if(isFalse(ini.sections["Basic"].values["Official"]))
  141. {
  142. AddItemWithNewLine(m_MapProblemList, GetLanguageStringACP("MV_Not8Waypoints"), 1);
  143. }
  144. if(below8found<2)
  145. AddItemWithNewLine(m_MapProblemList, GetLanguageStringACP("MV_HousesNoWaypoints"), 1);
  146. }
  147. #ifdef RA2_MODE
  148. if(isTrue(ini.sections["Basic"].values["Official"]))
  149. AddItemWithNewLine(m_MapProblemList, GetLanguageStringACP("MV_OfficialYes"), 1);
  150. #endif
  151. }
  152. int i;
  153. for(i=0;i<ini.sections["Tags"].values.size();i++)
  154. {
  155. CString trigger=GetParam(*ini.sections["Tags"].GetValue(i),2);
  156. if(ini.sections["Triggers"].FindName(trigger)<0)
  157. {
  158. CString error;
  159. error=GetLanguageStringACP("MV_TriggerMissing");
  160. error=TranslateStringVariables(1, error, trigger);
  161. error=TranslateStringVariables(2, error, "Tag");
  162. error=TranslateStringVariables(3, error, *ini.sections["Tags"].GetValueName(i));
  163. AddItemWithNewLine(m_MapProblemList, error, 1);
  164. }
  165. }
  166. // repair triggers
  167. for(i=0;i<ini.sections["Triggers"].values.size();i++)
  168. {
  169. RepairTrigger(ini.sections["Triggers"].values[*ini.sections["Triggers"].GetValueName(i)]);
  170. }
  171. for(i=0;i<ini.sections["Triggers"].values.size();i++)
  172. {
  173. CString trigger=GetParam(*ini.sections["Triggers"].GetValue(i),1);
  174. if(ini.sections["Triggers"].FindName(trigger)<0 && trigger!="<none>")
  175. {
  176. CString error;
  177. error=GetLanguageStringACP("MV_TriggerMissing");
  178. error=TranslateStringVariables(1, error, trigger);
  179. error=TranslateStringVariables(2, error, "Trigger");
  180. error=TranslateStringVariables(3, error, *ini.sections["Triggers"].GetValueName(i));
  181. AddItemWithNewLine(m_MapProblemList, error, 1);
  182. }
  183. }
  184. for(i=0;i<ini.sections["TeamTypes"].values.size();i++)
  185. {
  186. CIniFileSection& sec=ini.sections[*ini.sections["TeamTypes"].GetValue(i)];
  187. CString taskforce=sec.values["TaskForce"];
  188. if(taskforce.GetLength()>0 && ini.sections["TaskForces"].FindValue(taskforce)<0)
  189. {
  190. CString error;
  191. error=GetLanguageStringACP("MV_TaskForceMissing");
  192. error=TranslateStringVariables(1, error, taskforce);
  193. error=TranslateStringVariables(2, error, *ini.sections["TeamTypes"].GetValue(i));
  194. AddItemWithNewLine(m_MapProblemList, error, 1);
  195. }
  196. }
  197. for(i=0;i<ini.sections["TeamTypes"].values.size();i++)
  198. {
  199. CIniFileSection& sec=ini.sections[*ini.sections["TeamTypes"].GetValue(i)];
  200. CString scripttype=sec.values["Script"];
  201. if(scripttype.GetLength()>0 && ini.sections["ScriptTypes"].FindValue(scripttype)<0)
  202. {
  203. CString error;
  204. error=GetLanguageStringACP("MV_ScripttypeMissing");
  205. error=TranslateStringVariables(1, error, scripttype);
  206. error=TranslateStringVariables(2, error, *ini.sections["TeamTypes"].GetValue(i));
  207. AddItemWithNewLine(m_MapProblemList, error, 1);
  208. }
  209. }
  210. for(i=0;i<ini.sections["TeamTypes"].values.size();i++)
  211. {
  212. CIniFileSection& sec=ini.sections[*ini.sections["TeamTypes"].GetValue(i)];
  213. if(sec.FindName("Tag")>=0)
  214. {
  215. CString tag=sec.values["Tag"];
  216. if(ini.sections["Tags"].FindName(tag)<0)
  217. {
  218. CString error;
  219. error=GetLanguageStringACP("MV_TagMissing");
  220. error=TranslateStringVariables(1, error, tag);
  221. error=TranslateStringVariables(2, error, "Teamtype");
  222. error=TranslateStringVariables(3, error, *ini.sections["TeamTypes"].GetValue(i));
  223. AddItemWithNewLine(m_MapProblemList, error, 1);
  224. }
  225. }
  226. }
  227. for(i=0;i<Map->GetCelltagCount();i++)
  228. {
  229. CString tag;
  230. DWORD pos;
  231. Map->GetCelltagData(i, &tag, &pos);
  232. int x=pos%Map->GetIsoSize();
  233. int y=pos/Map->GetIsoSize();
  234. char cx[50];
  235. char cy[50];
  236. itoa(x, cx, 10);
  237. itoa(y, cy, 10);
  238. CString p=cx;
  239. p+="/";
  240. p+=cy;
  241. if(ini.sections["Tags"].FindName(tag)<0)
  242. {
  243. CString error;
  244. error=GetLanguageStringACP("MV_TagMissing");
  245. error=TranslateStringVariables(1, error, tag);
  246. error=TranslateStringVariables(2, error, "Celltag");
  247. error=TranslateStringVariables(3, error, p);
  248. AddItemWithNewLine(m_MapProblemList, error, 1);
  249. }
  250. }
  251. }
  252. const auto& tubes = Map->GetTubes();
  253. for (auto& tube : tubes)
  254. {
  255. auto n_reverse = std::count_if(tubes.begin(), tubes.end(), [&tube](const auto& other) { return tube->isCounterpart(*other); });
  256. if (n_reverse == 0)
  257. {
  258. CString error = TranslateTubeString(
  259. GetLanguageStringACP("MV_TubeCounterpartMissing"),
  260. *tube,
  261. n_reverse
  262. );
  263. AddItemWithNewLine(m_MapProblemList, error, 0);
  264. }
  265. auto n_same_start = std::count_if(tubes.begin(), tubes.end(), [&tube](const auto& other) { return tube->getStartCoords() == other->getStartCoords() && *tube != *other; });
  266. if (n_same_start)
  267. {
  268. CString error = TranslateTubeString(
  269. GetLanguageStringACP("MV_TubeStartNotUnique"),
  270. *tube,
  271. n_same_start
  272. );
  273. AddItemWithNewLine(m_MapProblemList, error, 0);
  274. }
  275. auto n_same_end = std::count_if(tubes.begin(), tubes.end(), [&tube](const auto& other) { return tube->getEndCoords() == other->getEndCoords() && *tube != *other; });
  276. if (n_same_end)
  277. {
  278. CString error = TranslateTubeString(
  279. GetLanguageStringACP("MV_TubeEndNotUnique"),
  280. *tube,
  281. n_same_end
  282. );
  283. AddItemWithNewLine(m_MapProblemList, error, 0);
  284. }
  285. auto n_invalid_counterpart_end = std::count_if(tubes.begin(), tubes.end(), [&tube](const auto& other) { return tube->getEndCoords() == other->getStartCoords() && tube->getStartCoords() != other->getEndCoords(); });
  286. if (n_invalid_counterpart_end)
  287. {
  288. CString error = TranslateTubeString(
  289. GetLanguageStringACP("MV_TubeInvalidCounterpartEnd"),
  290. *tube,
  291. n_invalid_counterpart_end
  292. );
  293. AddItemWithNewLine(m_MapProblemList, error, 0);
  294. }
  295. auto n_invalid_counterpart_start = std::count_if(tubes.begin(), tubes.end(), [&tube](const auto& other) { return tube->getStartCoords() == other->getEndCoords() && tube->getEndCoords() != other->getStartCoords(); });
  296. if (n_invalid_counterpart_start)
  297. {
  298. CString error = TranslateTubeString(
  299. GetLanguageStringACP("MV_TubeInvalidCounterpartStart"),
  300. *tube,
  301. n_invalid_counterpart_start
  302. );
  303. AddItemWithNewLine(m_MapProblemList, error, 0);
  304. }
  305. }
  306. int i;
  307. BOOL bWaypBig=FALSE;
  308. for(i=0;i<Map->GetWaypointCount();i++)
  309. {
  310. DWORD pos;
  311. CString id;
  312. Map->GetWaypointData(i, &id, &pos);
  313. if(atoi(id)>99) bWaypBig=TRUE;
  314. }
  315. if(bWaypBig)
  316. AddItemWithNewLine(m_MapProblemList, GetLanguageStringACP("MV_>100Waypoint"), 1);
  317. if(Map->IsYRMap())
  318. {
  319. AddItemWithNewLine(m_MapProblemList, GetLanguageStringACP("NeedsYR"), 1);
  320. }
  321. return bAllow;
  322. }
  323. CString TranslateTubeString(const char* error_, const CTube& tube, int count)
  324. {
  325. CString error = error_;
  326. error = TranslateStringVariables(1, error, std::to_string(tube.getStartX()).c_str());
  327. error = TranslateStringVariables(2, error, std::to_string(tube.getStartY()).c_str());
  328. error = TranslateStringVariables(3, error, std::to_string(tube.getEndX()).c_str());
  329. error = TranslateStringVariables(4, error, std::to_string(tube.getEndY()).c_str());
  330. error = TranslateStringVariables(5, error, std::to_string(tube.getId()).c_str());
  331. error = TranslateStringVariables(6, error, std::to_string(count).c_str());
  332. return error;
  333. }