FS2_Open
Open source remastering of the Freespace 2 engine
campaigntreewnd.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) Volition, Inc. 1999. All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
12 #include "stdafx.h"
13 #include "FRED.h"
14 #include "CampaignTreeWnd.h"
15 #include "CampaignEditorDlg.h"
16 #include "CampaignTreeView.h"
17 #include "Management.h"
18 #include "MainFrm.h"
19 #include "FREDView.h"
20 #include "MissionSave.h"
21 #include "InitialShips.h"
22 #include "mission/missionparse.h"
23 #include "parse/parselo.h"
24 
25 #ifdef _DEBUG
26 #undef THIS_FILE
27 static char THIS_FILE[] = __FILE__;
28 #endif
29 
34 
35 IMPLEMENT_DYNCREATE(campaign_tree_wnd, CFrameWnd)
36 
37 // campaign_tree_wnd
39 
41 {
43 }
44 
46 {
47 }
48 
49 BEGIN_MESSAGE_MAP(campaign_tree_wnd, CFrameWnd)
50  //{{AFX_MSG_MAP(campaign_tree_wnd)
51  ON_UPDATE_COMMAND_UI(ID_CPGN_FILE_OPEN, OnUpdateCpgnFileOpen)
52  ON_COMMAND(ID_CPGN_FILE_OPEN, OnCpgnFileOpen)
53  ON_WM_DESTROY()
54  ON_COMMAND(ID_CPGN_FILE_SAVE, OnCpgnFileSave)
55  ON_COMMAND(ID_CPGN_FILE_SAVE_AS, OnCpgnFileSaveAs)
56  ON_COMMAND(ID_CPGN_FILE_NEW, OnCpgnFileNew)
57  ON_COMMAND(ID_CLOSE, OnClose2)
58  ON_COMMAND(ID_ERROR_CHECKER, OnErrorChecker)
59  ON_WM_CLOSE()
60  ON_COMMAND(ID_INITIAL_SHIPS, OnInitialShips)
61  ON_COMMAND(ID_INITIAL_WEAPONS, OnInitialWeapons)
62  //}}AFX_MSG_MAP
63 END_MESSAGE_MAP()
64 
66 // campaign_tree_wnd message handlers
67 
68 BOOL campaign_tree_wnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext)
69 {
70  CSize s;
71 
72  LoadAccelTable("IDR_ACC_CAMPAIGN");
73  Mission_filename_cb_format = RegisterClipboardFormat("Mission Filename");
75  clear_mission();
76 
77  // create a splitter with 1 row, 2 columns
78  if (!m_splitter.CreateStatic(this, 1, 2))
79  {
80  TRACE0("Failed to CreateStaticSplitter\n");
81  return FALSE;
82  }
83 
84  // add the first splitter pane - the campaign input form in column 0
85  if (!m_splitter.CreateView(0, 0, RUNTIME_CLASS(campaign_editor), CSize(0, 0), pContext))
86  {
87  TRACE0("Failed to create first pane\n");
88  return FALSE;
89  }
90 
91  // add the second splitter pane - the campaign tree view in column 1
92  if (!m_splitter.CreateView(0, 1, RUNTIME_CLASS(campaign_tree_view), CSize(240, 100), pContext))
93  {
94  TRACE0("Failed to create second pane\n");
95  return FALSE;
96  }
97 
98  Campaign_tree_formp = (campaign_editor *) m_splitter.GetPane(0, 0);
99  Campaign_tree_viewp = (campaign_tree_view *) m_splitter.GetPane(0, 1);
100  s = Campaign_tree_formp->GetTotalSize();
101  m_splitter.SetColumnInfo(0, s.cx, 0);
102  m_splitter.SetColumnInfo(1, 0, 0);
103  m_splitter.RecalcLayout();
104 
105  // activate the input view
106  SetActiveView(Campaign_tree_formp);
107  OnCpgnFileNew();
108 // Campaign_tree_formp->load_campaign();
109  Fred_main_wnd->EnableWindow(FALSE);
110  return TRUE;
111 }
112 
114 {
115  pCmdUI->Enable();
116 }
117 
119 {
120  CString name;
121 
122  if (Campaign_modified)
123  if (save_modified())
124  return;
125 
126  CFileDialog dlg(TRUE, "fc2", NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "FreeSpace Campaign files (*.fc2)|*.fc2||", this);
127  if (dlg.DoModal() == IDOK)
128  {
129  name = dlg.GetFileName();
130  if (strlen(name) > MAX_FILENAME_LEN - 1) {
131  MessageBox("Filename is too long", "Error");
132  return;
133  }
134 
135  if (!strlen(name))
136  return;
137 
140  }
141 }
142 
144 {
145  CString str;
146 
147  OnCpgnFileNew();
148  Fred_main_wnd->EnableWindow(TRUE);
149 // if (!Bypass_clear_mission)
150 // create_new_mission();
151  str = FREDDoc_ptr->GetPathName();
152  if (str.IsEmpty())
154  else
156 
157  CFrameWnd::OnDestroy();
158  Campaign_wnd = NULL;
159  Fred_main_wnd->SetFocus();
160 }
161 
163 {
164  CFred_mission_save save;
165 
167  if (!Campaign.filename[0]) {
169  return;
170  }
171 
172  // sanity checking for multiplayer
173  /*
174  if ( Campaign.type == TYPE_MULTI_PLAYER ) {
175 
176  if ( (Campaign.mc_info.min_players < 0) || (Campaign.mc_info.min_players > Campaign.mc_info.max_players) ) {
177  MessageBox("Min players must be > 0 and <= max players", "Error", MB_OK | MB_ICONEXCLAMATION);
178  return;
179  }
180  if ( (Campaign.mc_info.max_players < 0) || (Campaign.mc_info.max_players > MAX_PLAYERS) ) {
181  char buf[256];
182 
183  sprintf(buf, "Max players must be > 0 and <= %d", MAX_PLAYERS );
184  MessageBox(buf, "Error", MB_OK | MB_ICONEXCLAMATION);
185  return;
186  }
187  if ( Campaign.mc_info.max_players < Campaign.mc_info.min_players ) {
188  MessageBox("Max Players must be greater than min players", "Error", MB_OK | MB_ICONEXCLAMATION);
189  return;
190  }
191  }
192  */
193 
195  {
196  MessageBox("An error occured while saving!", "Error", MB_OK | MB_ICONEXCLAMATION);
197  return;
198  }
199 
200  Campaign_modified = 0;
201  return;
202 }
203 
205 {
206  char *old_name = NULL;
207  char campaign_path[256];
208  CString name;
209  CFred_mission_save save;
210 
212  if (Campaign.filename[0])
213  old_name = Campaign.filename;
214 
215  CFileDialog dlg(FALSE, "fc2", old_name, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "FreeSpace Campaign files (*.fc2)|*.fc2||", this);
216  if (dlg.DoModal() == IDOK)
217  {
218  name = dlg.GetFileName();
219  if (strlen(name) > MAX_FILENAME_LEN - 1) {
220  MessageBox("Filename is too long", "Error");
221  return;
222  }
223 
224  if (!strlen(name)){
225  return;
226  }
227 
229  string_copy(campaign_path, dlg.GetPathName(), 256);
230  if (save.save_campaign_file(campaign_path))
231  {
232  MessageBox("An error occured while saving!", "Error", MB_OK | MB_ICONEXCLAMATION);
233  return;
234  }
235 
236  Campaign_modified = 0;
237  }
238 }
239 
241 {
242  if (Campaign_modified)
243  if (save_modified())
244  return;
245 
246  Campaign.filename[0] = 0;
248  Campaign.num_players = 0;
249  strcpy_s(Campaign.name, "Unnamed");
250  Campaign.desc = NULL;
253  Campaign_modified = 0;
255  ((CButton *) (Campaign_tree_formp->GetDlgItem(IDC_CUSTOM_TECH_DB)))->SetCheck(0);
256 }
257 
259 {
260  if (Campaign_modified)
261  if (save_modified())
262  return;
263 
264  CFrameWnd::OnClose();
265 }
266 
268 {
269  if (Campaign_modified)
270  if (save_modified())
271  return;
272 
273  DestroyWindow();
274 }
275 
276 // returns 0 for success and 1 for cancel
278 {
279  int r;
280 
281  r = MessageBox("This campaign has been modified. Save changes first?", "Campaign Modified",
283 
284  if (r == IDCANCEL)
285  return 1;
286 
287  if (r == IDYES) {
288  OnCpgnFileSave();
289  if (Campaign_modified) // error occured in saving.
290  return 1;
291  }
292 
293  Campaign_modified = 0;
294  return 0;
295 }
296 
298 {
300  error_checker();
301  if (!g_err)
302  MessageBox("No errors detected in campaign", "Woohoo!");
303 }
304 
306 {
307  int i, j, z;
308  int mcount[MAX_CAMPAIGN_MISSIONS], true_at[MAX_CAMPAIGN_MISSIONS];
309 
310  for (i=0; i<MAX_CAMPAIGN_MISSIONS; i++) {
311  mcount[i] = 0;
312  true_at[i] = -1;
313  }
314 
315  g_err = 0;
316  for (i=0; i<Total_links; i++) {
317  if ( (Links[i].from < 0) || (Links[i].from >= Campaign.num_missions) )
318  return internal_error("Branch #%d has illegal source mission", i);
319  if ( (Links[i].to < -1) || (Links[i].to >= Campaign.num_missions) )
320  return internal_error("Branch #%d has illegal target mission", i);
322  if (fred_check_sexp(Links[i].sexp, OPR_BOOL, "formula of branch #%d", i))
323  return -1;
324 
325  z = Links[i].from;
326  mcount[z]++;
327  if (Links[i].sexp == Locked_sexp_false)
328  if (error("Mission \"%s\" branch %d is always false", Campaign.missions[z].name, mcount[z]))
329  return 1;
330 
331  if (Links[i].sexp == Locked_sexp_true) {
332  if (true_at[z] >= 0)
333  if (error("Mission \"%s\" branch %d is true but is not last branch", Campaign.missions[z].name, true_at[z]))
334  return 1;
335 
336  true_at[z] = mcount[z];
337  }
338  }
339 
340  // check that all missions in a multiplayer game have the same number of players
342  for (i = 0; i < Campaign.num_missions; i++ ) {
343  mission a_mission;
344 
345  get_mission_info(Campaign.missions[i].name, &a_mission);
346  if ( a_mission.num_players != Campaign.num_players ) {
347  if ( error("Mission \"%s\" has %d players. Multiplayer campaign allows %d", Campaign.missions[i].name, a_mission.num_players, Campaign.num_players) )
348  return 1;
349  }
350  }
351  }
352 
353  for (i=0; i<Campaign.num_missions; i++)
354  if (mcount[i] && true_at[i] < mcount[i])
355  if (error("Mission \"%s\" last branch isn't set to true", Campaign.missions[i].name))
356  return 1;
357 
358  for (i=z=0; i<Campaign.num_missions; i++) {
359  for (j=0; j<Campaign.num_missions; j++)
360  if ((i != j) && !stricmp(Campaign.missions[i].name, Campaign.missions[j].name))
361  return internal_error("Mission \"%s\" is listed twice in campaign", Campaign.missions[i].name);
362 
363  if (!Campaign.missions[i].level)
364  z++;
365  }
366 
367  if (!z)
368  if (error("No top level mission present in tree"))
369  return 1;
370 
371  if (z > 1)
372  return internal_error("More than one top level mission present in tree");
373 
374  return 0;
375 }
376 
377 int campaign_tree_wnd::error(const char *msg, ...)
378 {
379  SCP_string buf;
380  va_list args;
381 
382  g_err++;
383  va_start(args, msg);
384  vsprintf(buf, msg, args);
385  va_end(args);
386 
387  nprintf(("Error", buf.c_str()));
388  if (MessageBox(buf.c_str(), "Error", MB_OKCANCEL | MB_ICONEXCLAMATION) == IDOK)
389  return 0;
390 
391  return 1;
392 }
393 
394 int campaign_tree_wnd::internal_error(const char *msg, ...)
395 {
396  SCP_string buf, buf2;
397  va_list args;
398 
399  g_err++;
400  va_start(args, msg);
401  vsprintf(buf, msg, args);
402  va_end(args);
403 
404  sprintf(buf2, "%s\n\nThis is an internal error. Please let Hoffoss\n"
405  "know about this so he can fix it. Click cancel to debug.", buf.c_str());
406 
407  nprintf(("Error", buf.c_str()));
408  if (MessageBox(buf2.c_str(), "Internal Error", MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL)
409  Int3(); // drop to debugger so the problem can be analyzed.
410 
411  return -1;
412 }
413 
414 int campaign_tree_wnd::fred_check_sexp(int sexp, int type, char *msg, ...)
415 {
416  SCP_string buf, sexp_buf, error_buf;
417  int err = 0, z, faulty_node;
418  va_list args;
419 
420  va_start(args, msg);
421  vsprintf(buf, msg, args);
422  va_end(args);
423 
424  if (sexp == -1)
425  return 0;
426 
427  z = check_sexp_syntax(sexp, type, 1, &faulty_node, SEXP_MODE_CAMPAIGN);
428  if (!z)
429  return 0;
430 
432  truncate_message_lines(sexp_buf, 30);
433  sprintf(error_buf, "Error in %s: %s\n\nIn sexpression: %s\n\n(Error appears to be: %s)", buf.c_str(), sexp_error_message(z), sexp_buf.c_str(), Sexp_nodes[faulty_node].text);
434 
435  if (z < 0 && z > -100)
436  err = 1;
437 
438  if (err)
439  return internal_error(error_buf.c_str());
440 
441  if (error(error_buf.c_str()))
442  return 1;
443 
444  return 0;
445 }
446 
447 // code to deal with the initial ships that a player can choose
449 {
451 
453  isd.DoModal();
454 }
455 
457 {
459 
461  isd.DoModal();
462 }
SCP_string sexp
Definition: sexp.cpp:25556
#define MAX_FILENAME_LEN
Definition: pstypes.h:324
#define MB_OKCANCEL
Definition: config.h:180
void clear_mission()
Definition: management.cpp:790
int i
Definition: multi_pxo.cpp:466
int Total_links
#define CAMPAIGN_TYPE_SINGLE
#define INITIAL_WEAPONS
Definition: initialships.h:17
int Locked_sexp_false
Definition: sexp.cpp:828
#define SEXP_ERROR_CHECK_MODE
Definition: parselo.h:64
char name[NAME_LENGTH]
virtual BOOL OnOpenDocument(LPCTSTR lpszPathName)
Definition: freddoc.cpp:143
void save_tree(int clear=1)
campaign_editor * Campaign_tree_formp
int save_campaign_file(char *pathname)
int error(const char *msg,...)
afx_msg void OnCpgnFileSaveAs()
#define ID_CPGN_FILE_NEW
Definition: resource.h:1374
afx_msg void OnUpdateCpgnFileOpen(CCmdUI *pCmdUI)
#define ID_INITIAL_WEAPONS
Definition: resource.h:1427
afx_msg void OnInitialWeapons()
void initialize(int init_files=1)
#define MB_ICONQUESTION
Definition: config.h:188
#define ID_CLOSE
Definition: resource.h:834
int fred_check_sexp(int sexp, int type, char *msg,...)
#define TRUE
Definition: pstypes.h:399
CMainFrame * Fred_main_wnd
Definition: mainfrm.cpp:89
#define CF_DEFAULT_VALUE
#define MB_YESNOCANCEL
Definition: config.h:183
std::basic_string< char, std::char_traits< char >, std::allocator< char > > SCP_string
Definition: vmallocator.h:21
#define Int3()
Definition: pstypes.h:292
void convert_sexp_to_string(SCP_string &dest, int cur_node, int mode)
Definition: sexp.cpp:3904
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: Glext.h:7308
#define IDC_CUSTOM_TECH_DB
Definition: resource.h:1056
char * name
GLenum type
Definition: Gl.h:1492
void create_new_mission()
Definition: management.cpp:776
campaign_tree_view * Campaign_tree_viewp
int Locked_sexp_true
Definition: sexp.cpp:828
GLdouble GLdouble GLdouble r
Definition: Glext.h:5337
int Mission_filename_cb_format
void vsprintf(SCP_string &dest, const char *format, va_list ap)
Definition: parselo.cpp:3800
#define nprintf(args)
Definition: pstypes.h:239
afx_msg void OnInitialShips()
sprintf(buf,"(%f,%f,%f)", v3->xyz.x, v3->xyz.y, v3->xyz.z)
GLdouble GLdouble z
Definition: Glext.h:5451
#define SEXP_MODE_CAMPAIGN
Definition: sexp.h:857
int Sexp_useful_number
Definition: sexp.cpp:827
#define MB_OK
Definition: config.h:179
GLdouble s
Definition: Glext.h:5321
int internal_error(const char *msg,...)
CFREDDoc * FREDDoc_ptr
Definition: freddoc.cpp:90
sexp_node * Sexp_nodes
Definition: sexp.cpp:844
int check_sexp_syntax(int node, int return_type, int recursive, int *bad_node, int mode)
Definition: sexp.cpp:1658
campaign_tree_wnd * Campaign_wnd
int get_mission_info(const char *filename, mission *mission_p, bool basic)
void string_copy(char *dest, const CString &src, int max_len, int modify)
Definition: management.cpp:142
void truncate_message_lines(SCP_string &text, int num_allowed_lines)
Definition: parselo.cpp:4154
int m_initial_items
Definition: initialships.h:25
#define INITIAL_SHIPS
Definition: initialships.h:16
GLuint const GLchar * name
Definition: Glext.h:5608
int BOOL
Definition: config.h:80
afx_msg void OnClose2()
#define MB_ICONEXCLAMATION
Definition: config.h:184
campaign Campaign
afx_msg void OnCpgnFileOpen()
#define ID_CPGN_FILE_SAVE_AS
Definition: resource.h:1377
#define ID_INITIAL_SHIPS
Definition: resource.h:1426
int Campaign_modified
#define ID_CPGN_FILE_OPEN
Definition: resource.h:1375
int MessageBox(HWND h, const char *s1, const char *s2, int i)
char * sexp_error_message(int num)
Definition: sexp.cpp:28570
int Bypass_clear_mission
#define ID_ERROR_CHECKER
Definition: resource.h:1361
#define MAX_CAMPAIGN_MISSIONS
int num_players
Definition: missionparse.h:140
afx_msg void OnErrorChecker()
char filename[MAX_FILENAME_LEN]
virtual ~campaign_tree_wnd()
afx_msg void OnClose()
#define FALSE
Definition: pstypes.h:400
#define ID_CPGN_FILE_SAVE
Definition: resource.h:1376
char text[TOKEN_LENGTH]
Definition: sexp.h:1027
#define stricmp(s1, s2)
Definition: config.h:271
afx_msg void OnDestroy()
#define OPR_BOOL
Definition: sexp.h:119
cmission missions[MAX_CAMPAIGN_MISSIONS]
afx_msg void OnCpgnFileNew()
#define strcpy_s(...)
Definition: safe_strings.h:67
campaign_tree_link Links[MAX_CAMPAIGN_TREE_LINKS]
afx_msg void OnCpgnFileSave()