1 // CampaignTreeView.cpp : implementation file
6 #include "campaigntreeview.h"
7 #include "campaigneditordlg.h"
8 #include "campaigntreewnd.h"
9 #include "missionparse.h"
14 static char THIS_FILE[] = __FILE__;
17 LOCAL int Bx, By, Mission_dragging = -1, Mission_dropping = -1, Context_mission;
19 int CTV_button_down = 0;
20 int Level_counts[MAX_LEVELS];
21 int Sorted[MAX_CAMPAIGN_MISSIONS];
22 campaign_tree_element Elements[MAX_CAMPAIGN_MISSIONS];
23 campaign_tree_link Links[MAX_CAMPAIGN_TREE_LINKS];
24 LOCAL CRect Dragging_rect;
25 LOCAL CSize Rect_offset, Last_draw_size;
27 /////////////////////////////////////////////////////////////////////////////
30 IMPLEMENT_DYNCREATE(campaign_tree_view, CScrollView)
32 campaign_tree_view *Campaign_tree_viewp;
34 campaign_tree_view::campaign_tree_view()
40 campaign_tree_view::~campaign_tree_view()
44 BEGIN_MESSAGE_MAP(campaign_tree_view, CScrollView)
45 //{{AFX_MSG_MAP(campaign_tree_view)
51 ON_COMMAND(ID_REMOVE_MISSION, OnRemoveMission)
52 ON_COMMAND(ID_DELETE_ROW, OnDeleteRow)
53 ON_COMMAND(ID_INSERT_ROW, OnInsertRow)
54 ON_COMMAND(ID_ADD_REPEAT, OnAddRepeat)
55 ON_COMMAND(ID_END_OF_CAMPAIGN, OnEndOfCampaign)
59 /////////////////////////////////////////////////////////////////////////////
60 // campaign_tree_view drawing
62 #define LEVEL_HEIGHT 75
63 #define CELL_WIDTH 150
64 #define CELL_TEXT_WIDTH 130
66 int campaign_tree_view::OnCreate(LPCREATESTRUCT lpCreateStruct)
68 if (CScrollView::OnCreate(lpCreateStruct) == -1)
74 void campaign_tree_view::OnDraw(CDC* pDC)
81 CPen black_pen, white_pen, red_pen, blue_pen, green_pen;
85 // setup text drawing stuff
86 pDC->SetTextAlign(TA_TOP | TA_CENTER);
87 pDC->SetTextColor(RGB(0, 0, 0));
88 pDC->SetBkMode(TRANSPARENT);
90 // figure out text box sizes
91 r = pDC->GetTextMetrics(&tm);
93 Bx = CELL_TEXT_WIDTH + 4;
96 r = gray_brush.CreateSolidBrush(RGB(192, 192, 192));
98 pDC->FillRect(CRect(0, 0, total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT), &gray_brush);
101 r = black_pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
103 r = white_pen.CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
105 r = red_pen.CreatePen(PS_SOLID, 1, RGB(192, 0, 0));
107 r = blue_pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 192));
109 r = green_pen.CreatePen(PS_SOLID, 1, RGB(0, 192, 0));
112 pDC->SelectObject(&black_pen);
113 // draw level seperators
114 for (i=1; i<total_levels; i++) {
115 pDC->MoveTo(0, i * LEVEL_HEIGHT - 1);
116 pDC->LineTo(total_width * CELL_WIDTH, i * LEVEL_HEIGHT - 1);
119 pDC->SelectObject(&white_pen);
120 for (i=1; i<total_levels; i++) {
121 pDC->MoveTo(0, i * LEVEL_HEIGHT);
122 pDC->LineTo(total_width * CELL_WIDTH, i * LEVEL_HEIGHT);
126 // draw edges of the whole tree rectangle
127 pDC->SelectObject(&black_pen);
128 pDC->MoveTo(0, total_levels * LEVEL_HEIGHT);
129 pDC->LineTo(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT);
130 pDC->LineTo(total_width * CELL_WIDTH, -1);
132 // draw text boxes and text
134 for (i=0; i<Campaign.num_missions; i++) {
135 x = (Campaign.missions[i].pos + 1) * CELL_WIDTH / 2;
136 y = Campaign.missions[i].level * LEVEL_HEIGHT + LEVEL_HEIGHT / 2;
137 Elements[i].box.left = x - Bx / 2;
138 Elements[i].box.right = Elements[i].box.left + Bx;
139 Elements[i].box.top = y - By / 2;
140 Elements[i].box.bottom = Elements[i].box.top + By;
142 strcpy(str, Campaign.missions[i].name);
143 str[strlen(str) - 4] = 0; // strip extension from filename
144 GetTextExtentPoint32(pDC->m_hDC, str, strlen(str), &size);
145 if (size.cx > CELL_TEXT_WIDTH) {
146 strcpy(str + strlen(str) - 1, "...");
147 GetTextExtentPoint32(pDC->m_hDC, str, strlen(str), &size);
148 while (size.cx > CELL_TEXT_WIDTH) {
149 strcpy(str + strlen(str) - 4, "...");
150 GetTextExtentPoint32(pDC->m_hDC, str, strlen(str), &size);
154 if (i == Cur_campaign_mission) {
155 pDC->SetTextColor(RGB(192, 0, 0));
156 pDC->Draw3dRect(x - Bx / 2, y - By / 2, Bx, By, RGB(0, 0, 0), RGB(255, 255, 255));
159 pDC->SetTextColor(RGB(0, 0, 0));
160 pDC->Draw3dRect(x - Bx / 2, y - By / 2, Bx, By, RGB(255, 255, 255), RGB(0, 0, 0));
163 pDC->TextOut(x, y - By / 2 + 2, str, strlen(str));
166 for (i=0; i<Total_links; i++) {
173 Links[i].p1.x = Elements[f].box.left + Links[i].from_pos * Bx / (Elements[f].from_links + 1);
174 Links[i].p1.y = Elements[f].box.bottom;
175 Links[i].p2.x = Elements[t].box.left + Links[i].to_pos * Bx / (Elements[t].to_links + 1);
176 Links[i].p2.y = Elements[t].box.top;
178 // if mission_loop link, select blue pen
179 if (Links[i].mission_loop) {
180 pDC->SelectObject(&blue_pen);
183 // if active link, select highlight pen (red - normal, green - mission loop)
184 if (i == Cur_campaign_link) {
185 if (Links[i].mission_loop) {
186 pDC->SelectObject(&green_pen);
188 pDC->SelectObject(&red_pen);
192 // draw a line between 'from' and 'to' mission. to might be -1 in the case of end-campaign, so
193 // don't draw line if so.
194 if ( (f != t) && ( t != -1) ) {
195 pDC->MoveTo(Links[i].p1);
196 pDC->LineTo(Links[i].p2);
199 // select (defalt) black pen
200 pDC->SelectObject(&black_pen);
203 pDC->SelectObject(&black_pen);
206 /////////////////////////////////////////////////////////////////////////////
207 // campaign_tree_view diagnostics
210 void campaign_tree_view::AssertValid() const
212 CScrollView::AssertValid();
215 void campaign_tree_view::Dump(CDumpContext& dc) const
217 CScrollView::Dump(dc);
221 /////////////////////////////////////////////////////////////////////////////
222 // campaign_tree_view message handlers
224 void campaign_tree_view::OnInitialUpdate()
226 CScrollView::OnInitialUpdate();
227 SetScrollSizes(MM_TEXT, CSize(320, 320));
230 void stuff_link_with_formula(int *link_idx, int formula, int mission_num)
232 int j, node, node2, node3;
234 if (!stricmp(CTEXT(formula), "cond")) {
238 free_one_sexp(formula);
241 Links[*link_idx].from = mission_num;
242 Links[*link_idx].sexp = CAR(node2);
243 Links[*link_idx].mission_loop_txt = NULL;
244 Links[*link_idx].mission_loop_brief_anim = NULL;
245 Links[*link_idx].mission_loop_brief_sound = NULL;
246 sexp_mark_persistent(CAR(node2));
247 free_one_sexp(node2);
249 if ( !stricmp( CTEXT(node3), "next-mission") ) {
251 for (j=0; j<Campaign.num_missions; j++)
252 if (!stricmp(CTEXT(node3), Campaign.missions[j].name))
255 if (j < Campaign.num_missions) { // mission is in campaign (you never know..)
256 Links[(*link_idx)++].to = j;
257 Elements[mission_num].from_links++;
258 Elements[j].to_links++;
261 } else if ( !stricmp( CTEXT(node3), "end-of-campaign") ) {
262 Links[(*link_idx)++].to = -1;
263 Elements[mission_num].from_links++;
266 Int3(); // bogus operator in campaign file
268 free_sexp(CDR(node2));
276 // this function should only be called right after a campaign is loaded. Calling it a second
277 // time without having loaded a campaign again will result in undefined behavior.
278 void campaign_tree_view::construct_tree()
283 // initialize mission link counts
284 for (i=0; i<Campaign.num_missions; i++) {
285 Elements[i].from_links = Elements[i].to_links = 0;
288 // analyze branching sexps and build links from them.
290 for (i=0; i<Campaign.num_missions; i++) {
292 // do main campaign path
293 stuff_link_with_formula(&link_idx, Campaign.missions[i].formula, i);
295 // do mission loop path
296 if ( Campaign.missions[i].has_mission_loop ) {
297 stuff_link_with_formula(&link_idx, Campaign.missions[i].mission_loop_formula, i);
298 Links[link_idx-1].mission_loop_txt = Campaign.missions[i].mission_loop_desc;
299 Links[link_idx-1].mission_loop_brief_anim = Campaign.missions[i].mission_loop_brief_anim;
300 Links[link_idx-1].mission_loop_brief_sound = Campaign.missions[i].mission_loop_brief_sound;
301 Links[link_idx-1].mission_loop = 1;
305 for (i=0; i<Campaign.num_missions; i++) {
309 Total_links = link_idx;
310 if (Campaign.realign_required) {
312 Campaign.realign_required = 0;
316 void campaign_tree_view::initialize()
320 total_levels = total_width = 1;
321 for (i=0; i<MAX_LEVELS; i++)
324 for (i=0; i<Campaign.num_missions; i++) {
325 z = Campaign.missions[i].level;
326 if (z + 2 > total_levels)
327 total_levels = z + 2;
330 z = (Campaign.missions[i].pos + 3) / 2;
336 SetScrollSizes(MM_TEXT, CSize(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT));
340 void campaign_tree_view::free_links()
344 for (i=0; i<Total_links; i++) {
345 sexp_unmark_persistent(Links[i].sexp);
346 free_sexp2(Links[i].sexp);
352 void campaign_tree_view::realign_tree()
354 int i, j, z, offset, level, pos;
356 // figure out what level each mission lies on and an initial position on that level
357 level = pos = total_width = 0;
358 for (i=0; i<Campaign.num_missions; i++) {
360 for (j=0; j<Total_links; j++)
361 if (Links[j].to == z) {
362 Assert(Campaign.missions[Links[j].from].level <= Campaign.missions[z].level); // links can't go up the tree, only down
363 if (Campaign.missions[Links[j].from].level == level) {
364 level++; // force to next level in tree
370 Campaign.missions[z].level = level;
371 Campaign.missions[z].pos = pos++;
372 if (pos > total_width)
375 Level_counts[level] = pos;
376 if (!z) { // topmost node must always be alone on level
382 // now calculate the true x position of each mission
383 for (i=0; i<Campaign.num_missions; i++) {
384 offset = total_width - Level_counts[Campaign.missions[i].level];
385 Campaign.missions[i].pos = Campaign.missions[i].pos * 2 + offset;
389 void campaign_tree_view::sort_links()
391 int i, j, k, z, to_count, from_count, swap;
392 int to_list[MAX_CAMPAIGN_TREE_LINKS];
393 int from_list[MAX_CAMPAIGN_TREE_LINKS];
395 for (i=0; i<Campaign.num_missions; i++) {
396 // build list of all to and from links for one mission at a time
397 to_count = from_count = 0;
398 for (j=0; j<Total_links; j++) {
399 if ((Links[j].to == i) && (Links[j].from == i))
400 continue; // ignore 'repeat mission' links
401 if (Links[j].to == i)
402 to_list[to_count++] = j;
403 if (Links[j].from == i)
404 from_list[from_count++] = j;
407 // sort to links, left to right and top to bottom
408 for (j=0; j<to_count-1; j++)
409 for (k=j+1; k<to_count; k++) {
411 z = Campaign.missions[Links[to_list[j]].from].pos -
412 Campaign.missions[Links[to_list[k]].from].pos;
414 if (z > 0) // sort left to right
417 else if (!z) { // both have same position?
418 z = Campaign.missions[Links[to_list[j]].from].level -
419 Campaign.missions[Links[to_list[k]].from].level;
421 // see where from link position is relative to to link position
422 if (Campaign.missions[i].pos < Campaign.missions[Links[to_list[j]].from].pos) {
423 if (z > 0) // sort bottom to top
427 if (z < 0) // sort top to bottom
434 to_list[j] = to_list[k];
439 // set all links to positions
440 for (j=0; j<to_count; j++)
441 Links[to_list[j]].to_pos = j + 1;
443 // sort from links, left to right and bottom to top
444 for (j=0; j<from_count-1; j++)
445 for (k=j+1; k<from_count; k++) {
447 z = Campaign.missions[Links[from_list[j]].to].pos -
448 Campaign.missions[Links[from_list[k]].to].pos;
454 z = Campaign.missions[Links[from_list[j]].to].level -
455 Campaign.missions[Links[from_list[k]].to].level;
457 if (Campaign.missions[i].pos < Campaign.missions[Links[from_list[j]].to].pos) {
469 from_list[j] = from_list[k];
474 // set all links from positions
475 for (j=0; j<from_count; j++)
476 Links[from_list[j]].from_pos = j + 1;
480 void campaign_tree_view::OnLButtonDown(UINT nFlags, CPoint point)
490 if (nFlags & MK_CONTROL) {
491 listbox = (CListBox *) &Campaign_tree_formp->m_filelist;
492 i = listbox->GetCurSel();
494 Mission_dropping = -1;
496 Mission_dropping = i;
500 Last_draw_size = CSize(0, 0);
501 Dragging_rect.SetRect(0, 0, 1, 1);
502 dc.DrawDragRect(Dragging_rect, Last_draw_size, NULL, CSize(0, 0));
505 if ( (Cur_campaign_link >= 0) && Links[Cur_campaign_link].mission_loop) {
506 // HACK!! UPDATE mission loop desc before changing selections
507 // save mission loop desc
508 char buffer[MISSION_DESC_LENGTH];
509 box = (CEdit *) Campaign_tree_formp->GetDlgItem(IDC_MISSISON_LOOP_DESC);
510 box->GetWindowText(buffer, MISSION_DESC_LENGTH);
511 if (strlen(buffer)) {
512 if (Links[Cur_campaign_link].mission_loop_txt) {
513 free(Links[Cur_campaign_link].mission_loop_txt);
515 Links[Cur_campaign_link].mission_loop_txt = strdup(buffer);
517 Links[Cur_campaign_link].mission_loop_txt = NULL;
520 // HACK!! UPDATE mission loop desc before changing selections
521 // save mission loop desc
522 box = (CEdit *) Campaign_tree_formp->GetDlgItem(IDC_LOOP_BRIEF_ANIM);
523 box->GetWindowText(buffer, MISSION_DESC_LENGTH);
524 if (strlen(buffer)) {
525 if (Links[Cur_campaign_link].mission_loop_brief_anim) {
526 free(Links[Cur_campaign_link].mission_loop_brief_anim);
528 Links[Cur_campaign_link].mission_loop_brief_anim = strdup(buffer);
530 Links[Cur_campaign_link].mission_loop_brief_anim = NULL;
533 // HACK!! UPDATE mission loop desc before changing selections
534 // save mission loop desc
535 box = (CEdit *) Campaign_tree_formp->GetDlgItem(IDC_LOOP_BRIEF_SOUND);
536 box->GetWindowText(buffer, MISSION_DESC_LENGTH);
537 if (strlen(buffer)) {
538 if (Links[Cur_campaign_link].mission_loop_brief_sound) {
539 free(Links[Cur_campaign_link].mission_loop_brief_sound);
541 Links[Cur_campaign_link].mission_loop_brief_sound = strdup(buffer);
543 Links[Cur_campaign_link].mission_loop_brief_sound = NULL;
546 Mission_dragging = Cur_campaign_mission = Cur_campaign_link = -1;
547 for (i=0; i<Campaign.num_missions; i++)
548 if (Elements[i].box.PtInRect(point)) {
551 Mission_dragging = Cur_campaign_mission = i;
552 Dragging_rect = Elements[i].box;
553 Rect_offset = Dragging_rect.TopLeft() - point;
554 Last_draw_size = CSize(4, 4);
555 if (Campaign.missions[Cur_campaign_mission].num_goals < 0) // haven't loaded goal names yet (or notes)
556 read_mission_goal_list(Cur_campaign_mission);
558 if (Campaign.missions[Cur_campaign_mission].notes) {
559 str = convert_multiline_string(Campaign.missions[Cur_campaign_mission].notes);
560 box = (CEdit *) Campaign_tree_formp->GetDlgItem(IDC_HELP_BOX);
562 box->SetWindowText(str);
565 Campaign_tree_formp->mission_selected(Cur_campaign_mission);
572 Campaign_tree_formp->load_tree();
573 if (Mission_dragging != -1) {
574 CRect rect = Dragging_rect;
577 dc.DrawDragRect(rect, Last_draw_size, NULL, CSize(0, 0));
580 CScrollView::OnLButtonDown(nFlags, point);
583 void campaign_tree_view::OnMouseMove(UINT nFlags, CPoint point)
585 int i, level, pos, x, y;
592 if ((Mission_dragging >= 0) || (Mission_dropping >= 0)) {
593 if (GetCapture() != this) {
594 rect = Dragging_rect;
596 dc.DrawDragRect(rect, CSize(0, 0), rect, Last_draw_size);
597 Mission_dragging = Mission_dropping = -1;
600 for (i=0; i<Campaign.num_missions; i++)
601 if (Elements[i].box.PtInRect(point))
604 if ((i < Campaign.num_missions) && (Mission_dropping < 0)) { // on a mission box?
605 draw_size = CSize(4, 4);
606 rect = Elements[i].box;
609 level = query_level(point);
610 pos = query_pos(point);
611 if ((level < 0) || (pos < 0)) { // off table?
612 draw_size = CSize(0, 0);
613 rect = Dragging_rect;
616 draw_size = CSize(2, 2);
617 for (i=0; i<Campaign.num_missions; i++)
618 if ((Campaign.missions[i].level == level) && (Campaign.missions[i].pos + 1 == pos)) {
619 pos = query_alternate_pos(point);
623 x = pos * CELL_WIDTH / 2 - Bx / 2;
624 y = level * LEVEL_HEIGHT + LEVEL_HEIGHT / 2 - By / 2;
625 rect.SetRect(x, y, x + Bx, y + By);
631 dc.LPtoDP(&Dragging_rect);
632 dc.DrawDragRect(r1, draw_size, Dragging_rect, Last_draw_size);
633 Dragging_rect = rect;
634 Last_draw_size = draw_size;
638 CScrollView::OnMouseMove(nFlags, point);
641 void campaign_tree_view::OnLButtonUp(UINT nFlags, CPoint point)
643 int i, j, z, level, pos;
648 if (Mission_dropping >= 0) { // dropping a new mission into campaign?
649 z = Mission_dropping;
650 Mission_dropping = -1;
651 if (GetCapture() == this) {
653 dc.LPtoDP(&Dragging_rect);
654 dc.DrawDragRect(Dragging_rect, CSize(0, 0), Dragging_rect, Last_draw_size);
656 drop_mission(z, point);
660 } else if (Mission_dragging >= 0) { // moving position of a mission?
661 z = Mission_dragging;
662 Mission_dragging = -1;
663 if (GetCapture() == this) {
665 dc.LPtoDP(&Dragging_rect);
666 dc.DrawDragRect(Dragging_rect, CSize(0, 0), Dragging_rect, Last_draw_size);
667 for (i=0; i<Campaign.num_missions; i++)
668 if (Elements[i].box.PtInRect(point)) { // see if released on another mission
669 if (i == z) // released on the same mission?
672 for (j=0; j<Total_links; j++)
673 if ((Links[j].from == z) && (Links[j].to == i))
674 return; // already linked
676 if (Total_links >= MAX_CAMPAIGN_TREE_LINKS) {
677 MessageBox("Too many links exist. Can't add any more.");
681 if (Campaign.missions[z].level >= Campaign.missions[i].level) {
682 MessageBox("A branch can only be set to a mission on a lower level");
690 // at this point, we are dragging a mission to a new place
691 level = query_level(point);
692 pos = query_pos(point);
693 if ((level < 0) || (pos < 0))
696 if (!level && (get_root_mission() >= 0)) {
697 MessageBox("Can't move mission to this level. There is already a top level mission");
701 for (i=0; i<Campaign.num_missions; i++)
702 if ((Campaign.missions[i].level == level) && (Campaign.missions[i].pos + 1 == pos)) {
703 pos = query_alternate_pos(point);
707 for (i=0; i<Total_links; i++)
708 if (Links[i].to == z)
709 if (level <= Campaign.missions[Links[i].from].level) {
710 MessageBox("Can't move mission to that level, as it would be\n"
711 "above a parent mission", "Error");
716 Campaign.missions[z].level = level;
717 Campaign.missions[z].pos = pos - 1;
720 SetScrollSizes(MM_TEXT, CSize(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT));
722 Campaign_modified = 1;
727 CScrollView::OnLButtonUp(nFlags, point);
730 int campaign_tree_view::add_link(int from, int to)
732 if (Total_links >= MAX_CAMPAIGN_TREE_LINKS)
735 Campaign_tree_formp->load_tree(1);
736 Links[Total_links].from = from;
737 Links[Total_links].to = to;
738 Links[Total_links].sexp = Locked_sexp_true;
739 Links[Total_links].mission_loop = false;
740 Links[Total_links].mission_loop_txt = NULL;
741 Links[Total_links].mission_loop_brief_anim = NULL;
742 Links[Total_links].mission_loop_brief_sound = NULL;
745 Elements[from].from_links++;
746 Elements[to].to_links++;
750 Campaign_tree_formp->load_tree(0);
752 Campaign_modified = 1;
756 int campaign_tree_view::query_level(const CPoint& p)
760 if ((p.y < 0) || (p.y >= total_levels * LEVEL_HEIGHT))
763 level = p.y / LEVEL_HEIGHT;
764 Assert((level >= 0) && (level < total_levels));
768 int campaign_tree_view::query_pos(const CPoint& p)
772 if ((p.x < 0) || (p.x >= total_width * CELL_WIDTH))
775 pos = ((p.x * 4 / CELL_WIDTH) + 1) / 2;
776 Assert((pos >= 0) && (pos <= total_width * 2));
780 int campaign_tree_view::query_alternate_pos(const CPoint& p)
784 if ((p.x < 0) || (p.x >= total_width * CELL_WIDTH))
787 x = p.x * 4 / CELL_WIDTH;
789 if (x & 1) // odd number
794 Assert((pos >= 0) && (pos <= total_width * 2));
798 DROPEFFECT campaign_tree_view::OnDragEnter(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
803 if (!pDataObject->IsDataAvailable((unsigned short)Mission_filename_cb_format))
804 return DROPEFFECT_NONE; // data isn't a mission filename, the only valid data to drop here
806 Mission_dropping = 1;
807 Last_draw_size = CSize(0, 0);
808 Dragging_rect.SetRect(0, 0, 1, 1);
809 dc.DrawDragRect(Dragging_rect, Last_draw_size, NULL, CSize(0, 0));
810 return DROPEFFECT_MOVE;
813 void campaign_tree_view::OnDragLeave()
815 CScrollView::OnDragLeave();
816 if (Mission_dropping >= 0) {
820 dc.LPtoDP(&Dragging_rect);
821 dc.DrawDragRect(Dragging_rect, CSize(0, 0), Dragging_rect, Last_draw_size);
822 Mission_dropping = -1;
826 DROPEFFECT campaign_tree_view::OnDragOver(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
828 int i, level, pos, x, y;
831 DROPEFFECT r = DROPEFFECT_MOVE;
833 if (Mission_dropping < 0)
834 return DROPEFFECT_NONE;
840 level = query_level(point);
841 pos = query_pos(point);
842 if ((level < 0) || (pos < 0)) { // off table?
843 draw_size = CSize(0, 0);
844 rect = Dragging_rect;
848 draw_size = CSize(2, 2);
849 for (i=0; i<Campaign.num_missions; i++)
850 if ((Campaign.missions[i].level == level) && (Campaign.missions[i].pos + 1 == pos)) {
851 pos = query_alternate_pos(point);
855 x = pos * CELL_WIDTH / 2 - Dragging_rect.Width() / 2;
856 y = level * LEVEL_HEIGHT + LEVEL_HEIGHT / 2 - Dragging_rect.Height() / 2;
857 rect.SetRect(x, y, x + Bx, y + By);
862 dc.LPtoDP(&Dragging_rect);
863 dc.DrawDragRect(r1, draw_size, Dragging_rect, Last_draw_size);
864 Dragging_rect = rect;
865 Last_draw_size = draw_size;
870 BOOL campaign_tree_view::OnDrop(COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
878 if (Mission_dropping < 0)
881 // If the dropEffect requested is not a MOVE, return FALSE to
882 // signal no drop. (No COPY into trashcan)
883 if ((dropEffect & DROPEFFECT_MOVE) != DROPEFFECT_MOVE)
890 dc.LPtoDP(&Dragging_rect);
891 dc.DrawDragRect(Dragging_rect, CSize(0, 0), Dragging_rect, Last_draw_size);
892 Mission_dropping = -1;
894 if (!pDataObject->IsDataAvailable((unsigned short)Mission_filename_cb_format))
895 return FALSE; // data isn't a mission filename, the only valid data to drop here
897 // Get text data from COleDataObject
898 hGlobal = pDataObject->GetGlobalData((unsigned short)Mission_filename_cb_format);
900 // Get pointer to data
901 pData = (LPCSTR) GlobalLock(hGlobal);
904 if (Campaign.num_missions >= MAX_CAMPAIGN_MISSIONS) { // Can't add any more
905 GlobalUnlock(hGlobal);
906 MessageBox("Too many missions. Can't add more to Campaign.", "Error");
910 level = query_level(point);
911 pos = query_pos(point);
912 Assert((level >= 0) && (pos >= 0)); // this should be impossible
913 if (!level && (get_root_mission() >= 0)) {
914 GlobalUnlock(hGlobal);
915 MessageBox("Only 1 mission may be in the top level");
919 // check the number of players in a multiplayer campaign against the number of players
920 // in the mission that was just dropped
921 if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) {
922 get_mission_info((char *)pData, &a_mission);
923 if ( !(a_mission.game_type & MISSION_TYPE_MULTI) ) {
926 sprintf( buf, "Mission \"%s\" is not a multiplayer mission", pData );
927 MessageBox(buf, "Error");
928 GlobalUnlock(hGlobal);
932 if ( Campaign.num_players != 0 ) {
933 if ( Campaign.num_players != a_mission.num_players ) {
936 sprintf(buf, "Mission \"%s\" has %d players. Campaign has %d players. Campaign will not play properly in FreeSpace", pData, a_mission.num_players, Campaign.num_players );
937 MessageBox(buf, "Warning");
940 Campaign.num_players = a_mission.num_players;
944 Elements[Campaign.num_missions].from_links = Elements[Campaign.num_missions].to_links = 0;
945 cm = &(Campaign.missions[Campaign.num_missions++]);
946 cm->name = strdup(pData);
947 cm->formula = Locked_sexp_true;
950 cm->briefing_cutscene[0] = 0;
951 for (i=0; i<Campaign.num_missions - 1; i++)
952 if ((Campaign.missions[i].level == level) && (Campaign.missions[i].pos + 1 == pos)) {
953 pos = query_alternate_pos(point);
959 correct_position(Campaign.num_missions - 1);
961 SetScrollSizes(MM_TEXT, CSize(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT));
964 // update and reinitialize dialog items
965 if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) {
966 Campaign_tree_formp->update();
967 Campaign_tree_formp->initialize(0);
970 // Unlock memory - Send dropped text into the "bit-bucket"
971 GlobalUnlock(hGlobal);
972 Campaign_modified = 1;
977 void campaign_tree_view::drop_mission(int m, CPoint point)
979 char name[MAX_FILENAME_LEN + 1];
980 int i, item, level, pos;
985 level = query_level(point);
986 pos = query_pos(point);
987 Assert((level >= 0) && (pos >= 0)); // this should be impossible
989 listbox = (CListBox *) &Campaign_tree_formp->m_filelist;
990 item = listbox->GetCurSel();
991 if (item == LB_ERR) {
992 MessageBox("Select a mission from listbox to add.", "Error");
996 if (listbox->GetTextLen(item) > MAX_FILENAME_LEN) {
999 sprintf(buf, "Filename is too long. Must be %d or less characters.", MAX_FILENAME_LEN);
1000 MessageBox(buf, "Error");
1001 return; // filename is too long. Would overflow our buffer
1004 // grab the filename selected from the listbox
1005 listbox->GetText(item, name);
1007 if (Campaign.num_missions >= MAX_CAMPAIGN_MISSIONS) { // Can't add any more
1008 MessageBox("Too many missions. Can't add more to Campaign.", "Error");
1012 if (!level && (get_root_mission() >= 0)) {
1013 MessageBox("Only 1 mission may be in the top level");
1017 // check the number of players in a multiplayer campaign against the number of players
1018 // in the mission that was just dropped
1019 if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) {
1020 get_mission_info(name, &a_mission);
1021 if ( !(a_mission.game_type & MISSION_TYPE_MULTI) ) {
1024 sprintf(buf, "Mission \"%s\" is not a multiplayer mission", name);
1025 MessageBox(buf, "Error");
1029 if (Campaign.num_players != 0) {
1030 if (Campaign.num_players != a_mission.num_players) {
1033 sprintf(buf, "Mission \"%s\" has %d players. Campaign has %d players. Campaign will not play properly in FreeSpace", name, a_mission.num_players, Campaign.num_players );
1034 MessageBox(buf, "Warning");
1038 Campaign.num_players = a_mission.num_players;
1042 Elements[Campaign.num_missions].from_links = Elements[Campaign.num_missions].to_links = 0;
1043 cm = &(Campaign.missions[Campaign.num_missions++]);
1044 cm->name = strdup(name);
1045 cm->formula = Locked_sexp_true;
1048 cm->briefing_cutscene[0] = 0;
1049 for (i=0; i<Campaign.num_missions - 1; i++)
1050 if ((Campaign.missions[i].level == level) && (Campaign.missions[i].pos + 1 == pos)) {
1051 pos = query_alternate_pos(point);
1057 correct_position(Campaign.num_missions - 1);
1059 SetScrollSizes(MM_TEXT, CSize(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT));
1062 // update and reinitialize dialog items
1063 if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) {
1064 Campaign_tree_formp->update();
1065 Campaign_tree_formp->initialize(0);
1068 listbox->DeleteString(item);
1069 Campaign_modified = 1;
1073 void campaign_tree_view::sort_elements()
1077 for (i=0; i<Campaign.num_missions; i++)
1080 // sort the tree, so realignment will work property
1081 for (i=1; i<Campaign.num_missions; i++) {
1083 for (j=i-1; j>=0; j--) {
1085 if ((Campaign.missions[s1].level > Campaign.missions[s2].level) ||
1086 ((Campaign.missions[s1].level == Campaign.missions[s2].level) &&
1087 (Campaign.missions[s1].pos > Campaign.missions[s2].pos)))
1097 void campaign_tree_view::correct_position(int num)
1101 // move missions down if required
1102 if (Campaign.missions[num].level + 2 > total_levels)
1103 total_levels = Campaign.missions[num].level + 2;
1105 for (i=0; i<Total_links; i++)
1106 if (Links[i].from == num) {
1108 if ( (num != z) && (Campaign.missions[num].level >= Campaign.missions[z].level) ) {
1109 Campaign.missions[z].level = Campaign.missions[num].level + 1;
1110 correct_position(z);
1114 // space out horizontally to avoid overlap of missions
1115 horizontally_align_mission(num, -1);
1116 horizontally_align_mission(num, 1);
1119 void campaign_tree_view::horizontally_align_mission(int num, int dir)
1123 if ((Campaign.missions[num].pos == -1) || (Campaign.missions[num].pos + 1 == total_width * 2)) { // need to expand total_width
1124 for (i=0; i<Campaign.num_missions; i++)
1125 Campaign.missions[i].pos++;
1130 for (i=0; i<Campaign.num_missions; i++) {
1134 if (Campaign.missions[i].level == Campaign.missions[num].level) {
1135 z = Campaign.missions[i].pos - Campaign.missions[num].pos;
1137 if (!z || (z == -1)) {
1138 Campaign.missions[i].pos = Campaign.missions[num].pos - 2;
1139 horizontally_align_mission(i, -1);
1143 if (!z || (z == 1)) {
1144 Campaign.missions[i].pos = Campaign.missions[num].pos + 2;
1145 horizontally_align_mission(i, 1);
1152 void campaign_tree_view::delete_link(int num)
1154 Assert((num >= 0) && (num < Total_links));
1155 if (Links[num].from != Links[num].to) {
1156 Elements[Links[num].from].from_links--;
1157 Elements[Links[num].to].to_links--;
1160 sexp_unmark_persistent(Links[num].sexp);
1161 free_sexp2(Links[num].sexp);
1162 while (num < Total_links - 1) {
1163 Links[num] = Links[num + 1];
1170 Campaign_modified = 1;
1174 int campaign_tree_view::get_root_mission()
1178 for (i=0; i<Campaign.num_missions; i++)
1179 if (!Campaign.missions[i].level)
1185 void campaign_tree_view::OnContextMenu(CWnd* pWnd, CPoint point)
1196 for (i=0; i<Campaign.num_missions; i++)
1197 if (Elements[i].box.PtInRect(p))
1200 if (i < Campaign.num_missions) { // clicked on a mission
1201 Context_mission = i;
1202 if (menu.LoadMenu(IDR_CPGN_VIEW_ON)) {
1203 popup = menu.GetSubMenu(0);
1205 popup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
1209 Context_mission = query_level(p);
1210 if ((Context_mission >= 0) && (Context_mission < total_levels))
1211 if (menu.LoadMenu(IDR_CPGN_VIEW_OFF)) {
1212 popup = menu.GetSubMenu(0);
1214 popup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
1219 void campaign_tree_view::OnRemoveMission()
1221 remove_mission(Context_mission);
1225 // for multiplayer missions, update the data and reiniailize the dialog -- the number of player
1226 // in the mission might have changed because of deletion of the first mission
1227 if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) {
1228 Campaign_tree_formp->update();
1229 Campaign_tree_formp->initialize();
1233 void campaign_tree_view::remove_mission(int m)
1239 Campaign_tree_formp->m_filelist.AddString(Campaign.missions[m].name);
1241 z = --Campaign.num_missions;
1244 if ((Links[i].from == m) || (Links[i].to == m))
1246 if (Links[i].from == z)
1248 if (Links[i].to == z)
1252 Elements[m] = Elements[z];
1253 Campaign.missions[m] = Campaign.missions[z];
1254 if (m == Cur_campaign_mission) {
1255 Cur_campaign_mission = -1;
1256 box = (CEdit *) Campaign_tree_formp->GetDlgItem(IDC_HELP_BOX);
1258 box->SetWindowText("");
1260 Campaign_tree_formp->load_tree(0);
1263 Campaign_modified = 1;
1266 void campaign_tree_view::OnDeleteRow()
1270 if (!Context_mission) {
1271 MessageBox("Can't delete the top level");
1275 for (i=z=0; i<Campaign.num_missions; i++)
1276 if (Campaign.missions[i].level == Context_mission)
1280 z = MessageBox("Deleting row will remove all missions on this row", "Notice", MB_ICONEXCLAMATION | MB_OKCANCEL);
1286 if (Campaign.missions[i].level == Context_mission)
1289 for (i=0; i<Campaign.num_missions; i++)
1290 if (Campaign.missions[i].level > Context_mission)
1291 Campaign.missions[i].level--;
1294 SetScrollSizes(MM_TEXT, CSize(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT));
1297 Campaign_modified = 1;
1300 void campaign_tree_view::OnInsertRow()
1304 for (i=0; i<Campaign.num_missions; i++)
1305 if (Campaign.missions[i].level >= Context_mission)
1306 Campaign.missions[i].level++;
1309 SetScrollSizes(MM_TEXT, CSize(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT));
1312 Campaign_modified = 1;
1315 void campaign_tree_view::OnAddRepeat()
1317 if (add_link(Context_mission, Context_mission)) {
1318 MessageBox("Too many links exist. Can't add any more.");
1323 void campaign_tree_view::OnEndOfCampaign()
1325 if ( add_link(Context_mission, -1) ) {
1326 MessageBox("Too many links exist. Cannot add any more.");