mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 17:52:08 -10:00
1339 lines
35 KiB
C++
1339 lines
35 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
// CampaignTreeView.cpp : implementation file
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "FRED.h"
|
|
#include "CampaignTreeView.h"
|
|
#include "CampaignEditorDlg.h"
|
|
#include "CampaignTreeWnd.h"
|
|
#include "mission/missionparse.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
LOCAL int Bx, By, Mission_dragging = -1, Mission_dropping = -1, Context_mission;
|
|
int Total_links = 0;
|
|
int CTV_button_down = 0;
|
|
int Level_counts[MAX_LEVELS];
|
|
int Sorted[MAX_CAMPAIGN_MISSIONS];
|
|
campaign_tree_element Elements[MAX_CAMPAIGN_MISSIONS];
|
|
campaign_tree_link Links[MAX_CAMPAIGN_TREE_LINKS];
|
|
LOCAL CRect Dragging_rect;
|
|
LOCAL CSize Rect_offset, Last_draw_size;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// campaign_tree_view
|
|
|
|
IMPLEMENT_DYNCREATE(campaign_tree_view, CScrollView)
|
|
|
|
campaign_tree_view *Campaign_tree_viewp;
|
|
|
|
campaign_tree_view::campaign_tree_view()
|
|
{
|
|
total_levels = 1;
|
|
total_width = 1;
|
|
}
|
|
|
|
campaign_tree_view::~campaign_tree_view()
|
|
{
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(campaign_tree_view, CScrollView)
|
|
//{{AFX_MSG_MAP(campaign_tree_view)
|
|
ON_WM_LBUTTONDOWN()
|
|
ON_WM_MOUSEMOVE()
|
|
ON_WM_LBUTTONUP()
|
|
ON_WM_CREATE()
|
|
ON_WM_CONTEXTMENU()
|
|
ON_COMMAND(ID_REMOVE_MISSION, OnRemoveMission)
|
|
ON_COMMAND(ID_DELETE_ROW, OnDeleteRow)
|
|
ON_COMMAND(ID_INSERT_ROW, OnInsertRow)
|
|
ON_COMMAND(ID_ADD_REPEAT, OnAddRepeat)
|
|
ON_COMMAND(ID_END_OF_CAMPAIGN, OnEndOfCampaign)
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// campaign_tree_view drawing
|
|
|
|
#define LEVEL_HEIGHT 75
|
|
#define CELL_WIDTH 150
|
|
#define CELL_TEXT_WIDTH 130
|
|
|
|
int campaign_tree_view::OnCreate(LPCREATESTRUCT lpCreateStruct)
|
|
{
|
|
if (CScrollView::OnCreate(lpCreateStruct) == -1)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void campaign_tree_view::OnDraw(CDC* pDC)
|
|
{
|
|
char str[256];
|
|
int i, x, y, f, t;
|
|
BOOL r;
|
|
CSize size;
|
|
CRect rect;
|
|
CPen black_pen, white_pen, red_pen, blue_pen, green_pen;
|
|
CBrush gray_brush;
|
|
TEXTMETRIC tm;
|
|
|
|
// setup text drawing stuff
|
|
pDC->SetTextAlign(TA_TOP | TA_CENTER);
|
|
pDC->SetTextColor(RGB(0, 0, 0));
|
|
pDC->SetBkMode(TRANSPARENT);
|
|
|
|
// figure out text box sizes
|
|
r = pDC->GetTextMetrics(&tm);
|
|
Assert(r);
|
|
Bx = CELL_TEXT_WIDTH + 4;
|
|
By = tm.tmHeight + 4;
|
|
|
|
r = gray_brush.CreateSolidBrush(RGB(192, 192, 192));
|
|
Assert(r);
|
|
pDC->FillRect(CRect(0, 0, total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT), &gray_brush);
|
|
|
|
// create pens
|
|
r = black_pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
|
|
Assert(r);
|
|
r = white_pen.CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
|
|
Assert(r);
|
|
r = red_pen.CreatePen(PS_SOLID, 1, RGB(192, 0, 0));
|
|
Assert(r);
|
|
r = blue_pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 192));
|
|
Assert(r);
|
|
r = green_pen.CreatePen(PS_SOLID, 1, RGB(0, 192, 0));
|
|
Assert(r);
|
|
|
|
pDC->SelectObject(&black_pen);
|
|
// draw level seperators
|
|
for (i=1; i<total_levels; i++) {
|
|
pDC->MoveTo(0, i * LEVEL_HEIGHT - 1);
|
|
pDC->LineTo(total_width * CELL_WIDTH, i * LEVEL_HEIGHT - 1);
|
|
}
|
|
|
|
pDC->SelectObject(&white_pen);
|
|
for (i=1; i<total_levels; i++) {
|
|
pDC->MoveTo(0, i * LEVEL_HEIGHT);
|
|
pDC->LineTo(total_width * CELL_WIDTH, i * LEVEL_HEIGHT);
|
|
}
|
|
|
|
|
|
// draw edges of the whole tree rectangle
|
|
pDC->SelectObject(&black_pen);
|
|
pDC->MoveTo(0, total_levels * LEVEL_HEIGHT);
|
|
pDC->LineTo(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT);
|
|
pDC->LineTo(total_width * CELL_WIDTH, -1);
|
|
|
|
// draw text boxes and text
|
|
|
|
for (i=0; i<Campaign.num_missions; i++) {
|
|
x = (Campaign.missions[i].pos + 1) * CELL_WIDTH / 2;
|
|
y = Campaign.missions[i].level * LEVEL_HEIGHT + LEVEL_HEIGHT / 2;
|
|
Elements[i].box.left = x - Bx / 2;
|
|
Elements[i].box.right = Elements[i].box.left + Bx;
|
|
Elements[i].box.top = y - By / 2;
|
|
Elements[i].box.bottom = Elements[i].box.top + By;
|
|
|
|
strcpy_s(str, Campaign.missions[i].name);
|
|
str[strlen(str) - 4] = 0; // strip extension from filename
|
|
GetTextExtentPoint32(pDC->m_hDC, str, strlen(str), &size);
|
|
if (size.cx > CELL_TEXT_WIDTH) {
|
|
strcpy(str + strlen(str) - 1, "...");
|
|
GetTextExtentPoint32(pDC->m_hDC, str, strlen(str), &size);
|
|
while (size.cx > CELL_TEXT_WIDTH) {
|
|
strcpy(str + strlen(str) - 4, "...");
|
|
GetTextExtentPoint32(pDC->m_hDC, str, strlen(str), &size);
|
|
}
|
|
}
|
|
|
|
if (i == Cur_campaign_mission) {
|
|
pDC->SetTextColor(RGB(192, 0, 0));
|
|
pDC->Draw3dRect(x - Bx / 2, y - By / 2, Bx, By, RGB(0, 0, 0), RGB(255, 255, 255));
|
|
|
|
} else {
|
|
pDC->SetTextColor(RGB(0, 0, 0));
|
|
pDC->Draw3dRect(x - Bx / 2, y - By / 2, Bx, By, RGB(255, 255, 255), RGB(0, 0, 0));
|
|
}
|
|
|
|
pDC->TextOut(x, y - By / 2 + 2, str, strlen(str));
|
|
}
|
|
|
|
for (i=0; i<Total_links; i++) {
|
|
f = Links[i].from;
|
|
t = Links[i].to;
|
|
|
|
if (t == -1) {
|
|
continue;
|
|
}
|
|
Links[i].p1.x = Elements[f].box.left + Links[i].from_pos * Bx / (Elements[f].from_links + 1);
|
|
Links[i].p1.y = Elements[f].box.bottom;
|
|
Links[i].p2.x = Elements[t].box.left + Links[i].to_pos * Bx / (Elements[t].to_links + 1);
|
|
Links[i].p2.y = Elements[t].box.top;
|
|
|
|
// if mission_loop link, select blue pen
|
|
if (Links[i].mission_loop) {
|
|
pDC->SelectObject(&blue_pen);
|
|
}
|
|
|
|
// if active link, select highlight pen (red - normal, green - mission loop)
|
|
if (i == Cur_campaign_link) {
|
|
if (Links[i].mission_loop) {
|
|
pDC->SelectObject(&green_pen);
|
|
} else {
|
|
pDC->SelectObject(&red_pen);
|
|
}
|
|
}
|
|
|
|
// draw a line between 'from' and 'to' mission. to might be -1 in the case of end-campaign, so
|
|
// don't draw line if so.
|
|
if ( (f != t) && ( t != -1) ) {
|
|
pDC->MoveTo(Links[i].p1);
|
|
pDC->LineTo(Links[i].p2);
|
|
}
|
|
|
|
// select (defalt) black pen
|
|
pDC->SelectObject(&black_pen);
|
|
}
|
|
|
|
pDC->SelectObject(&black_pen);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// campaign_tree_view diagnostics
|
|
|
|
#ifdef _DEBUG
|
|
void campaign_tree_view::AssertValid() const
|
|
{
|
|
CScrollView::AssertValid();
|
|
}
|
|
|
|
void campaign_tree_view::Dump(CDumpContext& dc) const
|
|
{
|
|
CScrollView::Dump(dc);
|
|
}
|
|
#endif //_DEBUG
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// campaign_tree_view message handlers
|
|
|
|
void campaign_tree_view::OnInitialUpdate()
|
|
{
|
|
CScrollView::OnInitialUpdate();
|
|
SetScrollSizes(MM_TEXT, CSize(320, 320));
|
|
}
|
|
|
|
void stuff_link_with_formula(int *link_idx, int formula, int mission_num)
|
|
{
|
|
int j, node, node2, node3;
|
|
if (formula >= 0) {
|
|
if (!stricmp(CTEXT(formula), "cond")) {
|
|
// sexp is valid
|
|
|
|
node = CDR(formula);
|
|
free_one_sexp(formula);
|
|
while (node != -1) {
|
|
node2 = CAR(node);
|
|
Links[*link_idx].from = mission_num;
|
|
Links[*link_idx].sexp = CAR(node2);
|
|
Links[*link_idx].mission_loop_txt = NULL;
|
|
Links[*link_idx].mission_loop_brief_anim = NULL;
|
|
Links[*link_idx].mission_loop_brief_sound = NULL;
|
|
sexp_mark_persistent(CAR(node2));
|
|
free_one_sexp(node2);
|
|
node3 = CADR(node2);
|
|
if ( !stricmp( CTEXT(node3), "next-mission") ) {
|
|
node3 = CDR(node3);
|
|
for (j=0; j<Campaign.num_missions; j++)
|
|
if (!stricmp(CTEXT(node3), Campaign.missions[j].name))
|
|
break;
|
|
|
|
if (j < Campaign.num_missions) { // mission is in campaign (you never know..)
|
|
Links[(*link_idx)++].to = j;
|
|
Elements[mission_num].from_links++;
|
|
Elements[j].to_links++;
|
|
}
|
|
|
|
} else if ( !stricmp( CTEXT(node3), "end-of-campaign") ) {
|
|
Links[(*link_idx)++].to = -1;
|
|
Elements[mission_num].from_links++;
|
|
|
|
} else
|
|
Int3(); // bogus operator in campaign file
|
|
|
|
free_sexp(CDR(node2));
|
|
free_one_sexp(node);
|
|
node = CDR(node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// this function should only be called right after a campaign is loaded. Calling it a second
|
|
// time without having loaded a campaign again will result in undefined behavior.
|
|
void campaign_tree_view::construct_tree()
|
|
{
|
|
int i;
|
|
free_links();
|
|
|
|
// initialize mission link counts
|
|
for (i=0; i<Campaign.num_missions; i++) {
|
|
Elements[i].from_links = Elements[i].to_links = 0;
|
|
}
|
|
|
|
// analyze branching sexps and build links from them.
|
|
int link_idx = 0;
|
|
for (i=0; i<Campaign.num_missions; i++) {
|
|
|
|
// do main campaign path
|
|
stuff_link_with_formula(&link_idx, Campaign.missions[i].formula, i);
|
|
|
|
// do mission loop path
|
|
if ( Campaign.missions[i].has_mission_loop ) {
|
|
stuff_link_with_formula(&link_idx, Campaign.missions[i].mission_loop_formula, i);
|
|
Links[link_idx-1].mission_loop_txt = Campaign.missions[i].mission_loop_desc;
|
|
Links[link_idx-1].mission_loop_brief_anim = Campaign.missions[i].mission_loop_brief_anim;
|
|
Links[link_idx-1].mission_loop_brief_sound = Campaign.missions[i].mission_loop_brief_sound;
|
|
Links[link_idx-1].mission_loop = 1;
|
|
}
|
|
}
|
|
|
|
for (i=0; i<Campaign.num_missions; i++) {
|
|
Sorted[i] = i;
|
|
}
|
|
|
|
Total_links = link_idx;
|
|
if (Campaign.realign_required) {
|
|
realign_tree();
|
|
Campaign.realign_required = 0;
|
|
}
|
|
}
|
|
|
|
void campaign_tree_view::initialize()
|
|
{
|
|
int i, z;
|
|
|
|
total_levels = total_width = 1;
|
|
for (i=0; i<MAX_LEVELS; i++)
|
|
Level_counts[i] = 0;
|
|
|
|
for (i=0; i<Campaign.num_missions; i++) {
|
|
z = Campaign.missions[i].level;
|
|
if (z + 2 > total_levels)
|
|
total_levels = z + 2;
|
|
|
|
Level_counts[z]++;
|
|
z = (Campaign.missions[i].pos + 3) / 2;
|
|
if (z > total_width)
|
|
total_width = z;
|
|
}
|
|
|
|
sort_links();
|
|
SetScrollSizes(MM_TEXT, CSize(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT));
|
|
Invalidate();
|
|
}
|
|
|
|
void campaign_tree_view::free_links()
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<Total_links; i++) {
|
|
sexp_unmark_persistent(Links[i].sexp);
|
|
free_sexp2(Links[i].sexp);
|
|
}
|
|
|
|
Total_links = 0;
|
|
}
|
|
|
|
void campaign_tree_view::realign_tree()
|
|
{
|
|
int i, j, z, offset, level, pos;
|
|
|
|
// figure out what level each mission lies on and an initial position on that level
|
|
level = pos = total_width = 0;
|
|
for (i=0; i<Campaign.num_missions; i++) {
|
|
z = Sorted[i];
|
|
for (j=0; j<Total_links; j++)
|
|
if (Links[j].to == z) {
|
|
Assert(Campaign.missions[Links[j].from].level <= Campaign.missions[z].level); // links can't go up the tree, only down
|
|
if (Campaign.missions[Links[j].from].level == level) {
|
|
level++; // force to next level in tree
|
|
pos = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Campaign.missions[z].level = level;
|
|
Campaign.missions[z].pos = pos++;
|
|
if (pos > total_width)
|
|
total_width = pos;
|
|
|
|
Level_counts[level] = pos;
|
|
if (!z) { // topmost node must always be alone on level
|
|
level++;
|
|
pos = 0;
|
|
}
|
|
}
|
|
|
|
// now calculate the true x position of each mission
|
|
for (i=0; i<Campaign.num_missions; i++) {
|
|
offset = total_width - Level_counts[Campaign.missions[i].level];
|
|
Campaign.missions[i].pos = Campaign.missions[i].pos * 2 + offset;
|
|
}
|
|
}
|
|
|
|
void campaign_tree_view::sort_links()
|
|
{
|
|
int i, j, k, z, to_count, from_count, swap;
|
|
int to_list[MAX_CAMPAIGN_TREE_LINKS];
|
|
int from_list[MAX_CAMPAIGN_TREE_LINKS];
|
|
|
|
for (i=0; i<Campaign.num_missions; i++) {
|
|
// build list of all to and from links for one mission at a time
|
|
to_count = from_count = 0;
|
|
for (j=0; j<Total_links; j++) {
|
|
if ((Links[j].to == i) && (Links[j].from == i))
|
|
continue; // ignore 'repeat mission' links
|
|
if (Links[j].to == i)
|
|
to_list[to_count++] = j;
|
|
if (Links[j].from == i)
|
|
from_list[from_count++] = j;
|
|
}
|
|
|
|
// sort to links, left to right and top to bottom
|
|
for (j=0; j<to_count-1; j++)
|
|
for (k=j+1; k<to_count; k++) {
|
|
swap = 0;
|
|
z = Campaign.missions[Links[to_list[j]].from].pos -
|
|
Campaign.missions[Links[to_list[k]].from].pos;
|
|
|
|
if (z > 0) // sort left to right
|
|
swap = 1;
|
|
|
|
else if (!z) { // both have same position?
|
|
z = Campaign.missions[Links[to_list[j]].from].level -
|
|
Campaign.missions[Links[to_list[k]].from].level;
|
|
|
|
// see where from link position is relative to to link position
|
|
if (Campaign.missions[i].pos < Campaign.missions[Links[to_list[j]].from].pos) {
|
|
if (z > 0) // sort bottom to top
|
|
swap = 1;
|
|
|
|
} else {
|
|
if (z < 0) // sort top to bottom
|
|
swap = 1;
|
|
}
|
|
}
|
|
|
|
if (swap) {
|
|
z = to_list[j];
|
|
to_list[j] = to_list[k];
|
|
to_list[k] = z;
|
|
}
|
|
}
|
|
|
|
// set all links to positions
|
|
for (j=0; j<to_count; j++)
|
|
Links[to_list[j]].to_pos = j + 1;
|
|
|
|
// sort from links, left to right and bottom to top
|
|
for (j=0; j<from_count-1; j++)
|
|
for (k=j+1; k<from_count; k++) {
|
|
swap = 0;
|
|
z = Campaign.missions[Links[from_list[j]].to].pos -
|
|
Campaign.missions[Links[from_list[k]].to].pos;
|
|
|
|
if (z > 0)
|
|
swap = 1;
|
|
|
|
else if (!z) {
|
|
z = Campaign.missions[Links[from_list[j]].to].level -
|
|
Campaign.missions[Links[from_list[k]].to].level;
|
|
|
|
if (Campaign.missions[i].pos < Campaign.missions[Links[from_list[j]].to].pos) {
|
|
if (z < 0)
|
|
swap = 1;
|
|
|
|
} else {
|
|
if (z > 0)
|
|
swap = 1;
|
|
}
|
|
}
|
|
|
|
if (swap) {
|
|
z = from_list[j];
|
|
from_list[j] = from_list[k];
|
|
from_list[k] = z;
|
|
}
|
|
}
|
|
|
|
// set all links from positions
|
|
for (j=0; j<from_count; j++)
|
|
Links[from_list[j]].from_pos = j + 1;
|
|
}
|
|
}
|
|
|
|
void campaign_tree_view::OnLButtonDown(UINT nFlags, CPoint point)
|
|
{
|
|
int i;
|
|
CString str;
|
|
CEdit *box;
|
|
CListBox *listbox;
|
|
CClientDC dc(this);
|
|
|
|
OnPrepareDC(&dc);
|
|
dc.DPtoLP(&point);
|
|
if (nFlags & MK_CONTROL) {
|
|
listbox = (CListBox *) &Campaign_tree_formp->m_filelist;
|
|
i = listbox->GetCurSel();
|
|
|
|
Mission_dropping = -1;
|
|
if (i != LB_ERR) {
|
|
Mission_dropping = i;
|
|
SetCapture();
|
|
}
|
|
|
|
Last_draw_size = CSize(0, 0);
|
|
Dragging_rect.SetRect(0, 0, 1, 1);
|
|
dc.DrawDragRect(Dragging_rect, Last_draw_size, NULL, CSize(0, 0));
|
|
|
|
} else {
|
|
if ( (Cur_campaign_link >= 0) && Links[Cur_campaign_link].mission_loop) {
|
|
// HACK!! UPDATE mission loop desc before changing selections
|
|
// save mission loop desc
|
|
char buffer[MISSION_DESC_LENGTH];
|
|
box = (CEdit *) Campaign_tree_formp->GetDlgItem(IDC_MISSISON_LOOP_DESC);
|
|
box->GetWindowText(buffer, MISSION_DESC_LENGTH);
|
|
if (strlen(buffer)) {
|
|
if (Links[Cur_campaign_link].mission_loop_txt) {
|
|
free(Links[Cur_campaign_link].mission_loop_txt);
|
|
}
|
|
Links[Cur_campaign_link].mission_loop_txt = strdup(buffer);
|
|
} else {
|
|
Links[Cur_campaign_link].mission_loop_txt = NULL;
|
|
}
|
|
|
|
// HACK!! UPDATE mission loop desc before changing selections
|
|
// save mission loop desc
|
|
box = (CEdit *) Campaign_tree_formp->GetDlgItem(IDC_LOOP_BRIEF_ANIM);
|
|
box->GetWindowText(buffer, MISSION_DESC_LENGTH);
|
|
if (strlen(buffer)) {
|
|
if (Links[Cur_campaign_link].mission_loop_brief_anim) {
|
|
free(Links[Cur_campaign_link].mission_loop_brief_anim);
|
|
}
|
|
Links[Cur_campaign_link].mission_loop_brief_anim = strdup(buffer);
|
|
} else {
|
|
Links[Cur_campaign_link].mission_loop_brief_anim = NULL;
|
|
}
|
|
|
|
// HACK!! UPDATE mission loop desc before changing selections
|
|
// save mission loop desc
|
|
box = (CEdit *) Campaign_tree_formp->GetDlgItem(IDC_LOOP_BRIEF_SOUND);
|
|
box->GetWindowText(buffer, MISSION_DESC_LENGTH);
|
|
if (strlen(buffer)) {
|
|
if (Links[Cur_campaign_link].mission_loop_brief_sound) {
|
|
free(Links[Cur_campaign_link].mission_loop_brief_sound);
|
|
}
|
|
Links[Cur_campaign_link].mission_loop_brief_sound = strdup(buffer);
|
|
} else {
|
|
Links[Cur_campaign_link].mission_loop_brief_sound = NULL;
|
|
}
|
|
}
|
|
Mission_dragging = Cur_campaign_mission = Cur_campaign_link = -1;
|
|
for (i=0; i<Campaign.num_missions; i++)
|
|
if (Elements[i].box.PtInRect(point)) {
|
|
SetCapture();
|
|
|
|
Mission_dragging = Cur_campaign_mission = i;
|
|
Dragging_rect = Elements[i].box;
|
|
Rect_offset = Dragging_rect.TopLeft() - point;
|
|
Last_draw_size = CSize(4, 4);
|
|
if (Campaign.missions[Cur_campaign_mission].num_goals < 0) // haven't loaded goal names yet (or notes)
|
|
read_mission_goal_list(Cur_campaign_mission);
|
|
|
|
if (Campaign.missions[Cur_campaign_mission].notes) {
|
|
str = convert_multiline_string(Campaign.missions[Cur_campaign_mission].notes);
|
|
box = (CEdit *) Campaign_tree_formp->GetDlgItem(IDC_HELP_BOX);
|
|
if (box)
|
|
box->SetWindowText(str);
|
|
}
|
|
|
|
Campaign_tree_formp->mission_selected(Cur_campaign_mission);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Invalidate();
|
|
UpdateWindow();
|
|
Campaign_tree_formp->load_tree();
|
|
if (Mission_dragging != -1) {
|
|
CRect rect = Dragging_rect;
|
|
|
|
dc.LPtoDP(&rect);
|
|
dc.DrawDragRect(rect, Last_draw_size, NULL, CSize(0, 0));
|
|
}
|
|
|
|
CScrollView::OnLButtonDown(nFlags, point);
|
|
}
|
|
|
|
void campaign_tree_view::OnMouseMove(UINT nFlags, CPoint point)
|
|
{
|
|
int i, level, pos, x, y;
|
|
CSize draw_size;
|
|
CRect rect, r1;
|
|
CClientDC dc(this);
|
|
|
|
OnPrepareDC(&dc);
|
|
dc.DPtoLP(&point);
|
|
if ((Mission_dragging >= 0) || (Mission_dropping >= 0)) {
|
|
if (GetCapture() != this) {
|
|
rect = Dragging_rect;
|
|
dc.LPtoDP(&rect);
|
|
dc.DrawDragRect(rect, CSize(0, 0), rect, Last_draw_size);
|
|
Mission_dragging = Mission_dropping = -1;
|
|
|
|
} else {
|
|
for (i=0; i<Campaign.num_missions; i++)
|
|
if (Elements[i].box.PtInRect(point))
|
|
break;
|
|
|
|
if ((i < Campaign.num_missions) && (Mission_dropping < 0)) { // on a mission box?
|
|
draw_size = CSize(4, 4);
|
|
rect = Elements[i].box;
|
|
|
|
} else {
|
|
level = query_level(point);
|
|
pos = query_pos(point);
|
|
if ((level < 0) || (pos < 0)) { // off table?
|
|
draw_size = CSize(0, 0);
|
|
rect = Dragging_rect;
|
|
|
|
} else {
|
|
draw_size = CSize(2, 2);
|
|
for (i=0; i<Campaign.num_missions; i++)
|
|
if ((Campaign.missions[i].level == level) && (Campaign.missions[i].pos + 1 == pos)) {
|
|
pos = query_alternate_pos(point);
|
|
break;
|
|
}
|
|
|
|
x = pos * CELL_WIDTH / 2 - Bx / 2;
|
|
y = level * LEVEL_HEIGHT + LEVEL_HEIGHT / 2 - By / 2;
|
|
rect.SetRect(x, y, x + Bx, y + By);
|
|
}
|
|
}
|
|
|
|
r1 = rect;
|
|
dc.LPtoDP(&r1);
|
|
dc.LPtoDP(&Dragging_rect);
|
|
dc.DrawDragRect(r1, draw_size, Dragging_rect, Last_draw_size);
|
|
Dragging_rect = rect;
|
|
Last_draw_size = draw_size;
|
|
}
|
|
}
|
|
|
|
CScrollView::OnMouseMove(nFlags, point);
|
|
}
|
|
|
|
void campaign_tree_view::OnLButtonUp(UINT nFlags, CPoint point)
|
|
{
|
|
int i, j, z, level, pos;
|
|
CClientDC dc(this);
|
|
|
|
OnPrepareDC(&dc);
|
|
dc.DPtoLP(&point);
|
|
if (Mission_dropping >= 0) { // dropping a new mission into campaign?
|
|
z = Mission_dropping;
|
|
Mission_dropping = -1;
|
|
if (GetCapture() == this) {
|
|
ReleaseCapture();
|
|
dc.LPtoDP(&Dragging_rect);
|
|
dc.DrawDragRect(Dragging_rect, CSize(0, 0), Dragging_rect, Last_draw_size);
|
|
|
|
drop_mission(z, point);
|
|
return;
|
|
}
|
|
|
|
} else if (Mission_dragging >= 0) { // moving position of a mission?
|
|
z = Mission_dragging;
|
|
Mission_dragging = -1;
|
|
if (GetCapture() == this) {
|
|
ReleaseCapture();
|
|
dc.LPtoDP(&Dragging_rect);
|
|
dc.DrawDragRect(Dragging_rect, CSize(0, 0), Dragging_rect, Last_draw_size);
|
|
for (i=0; i<Campaign.num_missions; i++)
|
|
if (Elements[i].box.PtInRect(point)) { // see if released on another mission
|
|
if (i == z) // released on the same mission?
|
|
return;
|
|
|
|
for (j=0; j<Total_links; j++)
|
|
if ((Links[j].from == z) && (Links[j].to == i))
|
|
return; // already linked
|
|
|
|
if (Total_links >= MAX_CAMPAIGN_TREE_LINKS) {
|
|
MessageBox("Too many links exist. Can't add any more.");
|
|
return;
|
|
}
|
|
|
|
if (Campaign.missions[z].level >= Campaign.missions[i].level) {
|
|
MessageBox("A branch can only be set to a mission on a lower level");
|
|
return;
|
|
}
|
|
|
|
add_link(z, i);
|
|
return;
|
|
}
|
|
|
|
// at this point, we are dragging a mission to a new place
|
|
level = query_level(point);
|
|
pos = query_pos(point);
|
|
if ((level < 0) || (pos < 0))
|
|
return;
|
|
|
|
if (!level && (get_root_mission() >= 0)) {
|
|
MessageBox("Can't move mission to this level. There is already a top level mission");
|
|
return;
|
|
}
|
|
|
|
for (i=0; i<Campaign.num_missions; i++)
|
|
if ((Campaign.missions[i].level == level) && (Campaign.missions[i].pos + 1 == pos)) {
|
|
pos = query_alternate_pos(point);
|
|
break;
|
|
}
|
|
|
|
for (i=0; i<Total_links; i++)
|
|
if (Links[i].to == z)
|
|
if (level <= Campaign.missions[Links[i].from].level) {
|
|
MessageBox("Can't move mission to that level, as it would be\n"
|
|
"above a parent mission", "Error");
|
|
|
|
return;
|
|
}
|
|
|
|
Campaign.missions[z].level = level;
|
|
Campaign.missions[z].pos = pos - 1;
|
|
correct_position(z);
|
|
sort_links();
|
|
SetScrollSizes(MM_TEXT, CSize(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT));
|
|
Invalidate();
|
|
Campaign_modified = 1;
|
|
return;
|
|
}
|
|
}
|
|
|
|
CScrollView::OnLButtonUp(nFlags, point);
|
|
}
|
|
|
|
int campaign_tree_view::add_link(int from, int to)
|
|
{
|
|
if (Total_links >= MAX_CAMPAIGN_TREE_LINKS)
|
|
return -1;
|
|
|
|
Campaign_tree_formp->load_tree(1);
|
|
Links[Total_links].from = from;
|
|
Links[Total_links].to = to;
|
|
Links[Total_links].sexp = Locked_sexp_true;
|
|
Links[Total_links].mission_loop = false;
|
|
Links[Total_links].mission_loop_txt = NULL;
|
|
Links[Total_links].mission_loop_brief_anim = NULL;
|
|
Links[Total_links].mission_loop_brief_sound = NULL;
|
|
Total_links++;
|
|
if (from != to) {
|
|
Elements[from].from_links++;
|
|
Elements[to].to_links++;
|
|
}
|
|
|
|
sort_links();
|
|
Campaign_tree_formp->load_tree(0);
|
|
Invalidate();
|
|
Campaign_modified = 1;
|
|
return 0;
|
|
}
|
|
|
|
int campaign_tree_view::query_level(const CPoint& p)
|
|
{
|
|
int level;
|
|
|
|
if ((p.y < 0) || (p.y >= total_levels * LEVEL_HEIGHT))
|
|
return -1;
|
|
|
|
level = p.y / LEVEL_HEIGHT;
|
|
Assert((level >= 0) && (level < total_levels));
|
|
return level;
|
|
}
|
|
|
|
int campaign_tree_view::query_pos(const CPoint& p)
|
|
{
|
|
int pos;
|
|
|
|
if ((p.x < 0) || (p.x >= total_width * CELL_WIDTH))
|
|
return -1;
|
|
|
|
pos = ((p.x * 4 / CELL_WIDTH) + 1) / 2;
|
|
Assert((pos >= 0) && (pos <= total_width * 2));
|
|
return pos;
|
|
}
|
|
|
|
int campaign_tree_view::query_alternate_pos(const CPoint& p)
|
|
{
|
|
int x, pos;
|
|
|
|
if ((p.x < 0) || (p.x >= total_width * CELL_WIDTH))
|
|
return -1;
|
|
|
|
x = p.x * 4 / CELL_WIDTH;
|
|
pos = (x + 1) / 2;
|
|
if (x & 1) // odd number
|
|
pos--;
|
|
else // even number
|
|
pos++;
|
|
|
|
Assert((pos >= 0) && (pos <= total_width * 2));
|
|
return pos;
|
|
}
|
|
/*
|
|
DROPEFFECT campaign_tree_view::OnDragEnter(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
|
|
{
|
|
CClientDC dc(this);
|
|
OnPrepareDC(&dc);
|
|
|
|
if (!pDataObject->IsDataAvailable((unsigned short)Mission_filename_cb_format))
|
|
return DROPEFFECT_NONE; // data isn't a mission filename, the only valid data to drop here
|
|
|
|
Mission_dropping = 1;
|
|
Last_draw_size = CSize(0, 0);
|
|
Dragging_rect.SetRect(0, 0, 1, 1);
|
|
dc.DrawDragRect(Dragging_rect, Last_draw_size, NULL, CSize(0, 0));
|
|
return DROPEFFECT_MOVE;
|
|
}
|
|
|
|
void campaign_tree_view::OnDragLeave()
|
|
{
|
|
CScrollView::OnDragLeave();
|
|
if (Mission_dropping >= 0) {
|
|
CClientDC dc(this);
|
|
OnPrepareDC(&dc);
|
|
|
|
dc.LPtoDP(&Dragging_rect);
|
|
dc.DrawDragRect(Dragging_rect, CSize(0, 0), Dragging_rect, Last_draw_size);
|
|
Mission_dropping = -1;
|
|
}
|
|
}
|
|
|
|
DROPEFFECT campaign_tree_view::OnDragOver(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point)
|
|
{
|
|
int i, level, pos, x, y;
|
|
CSize draw_size;
|
|
CRect rect, r1;
|
|
DROPEFFECT r = DROPEFFECT_MOVE;
|
|
|
|
if (Mission_dropping < 0)
|
|
return DROPEFFECT_NONE;
|
|
|
|
CClientDC dc(this);
|
|
OnPrepareDC(&dc);
|
|
dc.DPtoLP(&point);
|
|
|
|
level = query_level(point);
|
|
pos = query_pos(point);
|
|
if ((level < 0) || (pos < 0)) { // off table?
|
|
draw_size = CSize(0, 0);
|
|
rect = Dragging_rect;
|
|
r = DROPEFFECT_NONE;
|
|
|
|
} else {
|
|
draw_size = CSize(2, 2);
|
|
for (i=0; i<Campaign.num_missions; i++)
|
|
if ((Campaign.missions[i].level == level) && (Campaign.missions[i].pos + 1 == pos)) {
|
|
pos = query_alternate_pos(point);
|
|
break;
|
|
}
|
|
|
|
x = pos * CELL_WIDTH / 2 - Dragging_rect.Width() / 2;
|
|
y = level * LEVEL_HEIGHT + LEVEL_HEIGHT / 2 - Dragging_rect.Height() / 2;
|
|
rect.SetRect(x, y, x + Bx, y + By);
|
|
}
|
|
|
|
r1 = rect;
|
|
dc.LPtoDP(&r1);
|
|
dc.LPtoDP(&Dragging_rect);
|
|
dc.DrawDragRect(r1, draw_size, Dragging_rect, Last_draw_size);
|
|
Dragging_rect = rect;
|
|
Last_draw_size = draw_size;
|
|
|
|
return r;
|
|
}
|
|
|
|
BOOL campaign_tree_view::OnDrop(COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
|
|
{
|
|
int i, level, pos;
|
|
cmission *cm;
|
|
HGLOBAL hGlobal;
|
|
LPCSTR pData;
|
|
mission a_mission;
|
|
|
|
if (Mission_dropping < 0)
|
|
return FALSE;
|
|
|
|
// If the dropEffect requested is not a MOVE, return FALSE to
|
|
// signal no drop. (No COPY into trashcan)
|
|
if ((dropEffect & DROPEFFECT_MOVE) != DROPEFFECT_MOVE)
|
|
return FALSE;
|
|
|
|
CClientDC dc(this);
|
|
OnPrepareDC(&dc);
|
|
dc.DPtoLP(&point);
|
|
|
|
dc.LPtoDP(&Dragging_rect);
|
|
dc.DrawDragRect(Dragging_rect, CSize(0, 0), Dragging_rect, Last_draw_size);
|
|
Mission_dropping = -1;
|
|
|
|
if (!pDataObject->IsDataAvailable((unsigned short)Mission_filename_cb_format))
|
|
return FALSE; // data isn't a mission filename, the only valid data to drop here
|
|
|
|
// Get text data from COleDataObject
|
|
hGlobal = pDataObject->GetGlobalData((unsigned short)Mission_filename_cb_format);
|
|
|
|
// Get pointer to data
|
|
pData = (LPCSTR) GlobalLock(hGlobal);
|
|
ASSERT(pData);
|
|
|
|
if (Campaign.num_missions >= MAX_CAMPAIGN_MISSIONS) { // Can't add any more
|
|
GlobalUnlock(hGlobal);
|
|
MessageBox("Too many missions. Can't add more to Campaign.", "Error");
|
|
return FALSE;
|
|
}
|
|
|
|
level = query_level(point);
|
|
pos = query_pos(point);
|
|
Assert((level >= 0) && (pos >= 0)); // this should be impossible
|
|
if (!level && (get_root_mission() >= 0)) {
|
|
GlobalUnlock(hGlobal);
|
|
MessageBox("Only 1 mission may be in the top level");
|
|
return FALSE;
|
|
}
|
|
|
|
// check the number of players in a multiplayer campaign against the number of players
|
|
// in the mission that was just dropped
|
|
if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) {
|
|
get_mission_info((char *)pData, &a_mission);
|
|
if ( !(a_mission.game_type & MISSION_TYPE_MULTI) ) {
|
|
char buf[256];
|
|
|
|
sprintf( buf, "Mission \"%s\" is not a multiplayer mission", pData );
|
|
MessageBox(buf, "Error");
|
|
GlobalUnlock(hGlobal);
|
|
return FALSE;
|
|
}
|
|
|
|
if ( Campaign.num_players != 0 ) {
|
|
if ( Campaign.num_players != a_mission.num_players ) {
|
|
char buf[512];
|
|
|
|
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 );
|
|
MessageBox(buf, "Warning");
|
|
}
|
|
} else {
|
|
Campaign.num_players = a_mission.num_players;
|
|
}
|
|
}
|
|
|
|
Elements[Campaign.num_missions].from_links = Elements[Campaign.num_missions].to_links = 0;
|
|
cm = &(Campaign.missions[Campaign.num_missions++]);
|
|
cm->name = strdup(pData);
|
|
cm->formula = Locked_sexp_true;
|
|
cm->num_goals = -1;
|
|
cm->notes = NULL;
|
|
cm->briefing_cutscene[0] = 0;
|
|
for (i=0; i<Campaign.num_missions - 1; i++)
|
|
if ((Campaign.missions[i].level == level) && (Campaign.missions[i].pos + 1 == pos)) {
|
|
pos = query_alternate_pos(point);
|
|
break;
|
|
}
|
|
|
|
cm->level = level;
|
|
cm->pos = pos - 1;
|
|
correct_position(Campaign.num_missions - 1);
|
|
sort_links();
|
|
SetScrollSizes(MM_TEXT, CSize(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT));
|
|
Invalidate();
|
|
|
|
// update and reinitialize dialog items
|
|
if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) {
|
|
Campaign_tree_formp->update();
|
|
Campaign_tree_formp->initialize(0);
|
|
}
|
|
|
|
// Unlock memory - Send dropped text into the "bit-bucket"
|
|
GlobalUnlock(hGlobal);
|
|
Campaign_modified = 1;
|
|
return TRUE;
|
|
}
|
|
*/
|
|
|
|
void campaign_tree_view::drop_mission(int m, CPoint point)
|
|
{
|
|
char name[MAX_FILENAME_LEN + 1];
|
|
int i, item, level, pos;
|
|
cmission *cm;
|
|
mission a_mission;
|
|
CListBox *listbox;
|
|
|
|
level = query_level(point);
|
|
pos = query_pos(point);
|
|
Assert((level >= 0) && (pos >= 0)); // this should be impossible
|
|
|
|
listbox = (CListBox *) &Campaign_tree_formp->m_filelist;
|
|
item = listbox->GetCurSel();
|
|
if (item == LB_ERR) {
|
|
MessageBox("Select a mission from listbox to add.", "Error");
|
|
return;
|
|
}
|
|
|
|
if (listbox->GetTextLen(item) > MAX_FILENAME_LEN) {
|
|
char buf[256];
|
|
|
|
sprintf(buf, "Filename is too long. Must be %d or less characters.", MAX_FILENAME_LEN);
|
|
MessageBox(buf, "Error");
|
|
return; // filename is too long. Would overflow our buffer
|
|
}
|
|
|
|
// grab the filename selected from the listbox
|
|
listbox->GetText(item, name);
|
|
|
|
if (Campaign.num_missions >= MAX_CAMPAIGN_MISSIONS) { // Can't add any more
|
|
MessageBox("Too many missions. Can't add more to Campaign.", "Error");
|
|
return;
|
|
}
|
|
|
|
if (!level && (get_root_mission() >= 0)) {
|
|
MessageBox("Only 1 mission may be in the top level");
|
|
return;
|
|
}
|
|
|
|
// check the number of players in a multiplayer campaign against the number of players
|
|
// in the mission that was just dropped
|
|
if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) {
|
|
get_mission_info(name, &a_mission);
|
|
if ( !(a_mission.game_type & MISSION_TYPE_MULTI) ) {
|
|
char buf[256];
|
|
|
|
sprintf(buf, "Mission \"%s\" is not a multiplayer mission", name);
|
|
MessageBox(buf, "Error");
|
|
return;
|
|
}
|
|
|
|
if (Campaign.num_players != 0) {
|
|
if (Campaign.num_players != a_mission.num_players) {
|
|
char buf[512];
|
|
|
|
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 );
|
|
MessageBox(buf, "Warning");
|
|
}
|
|
|
|
} else {
|
|
Campaign.num_players = a_mission.num_players;
|
|
}
|
|
}
|
|
|
|
Elements[Campaign.num_missions].from_links = Elements[Campaign.num_missions].to_links = 0;
|
|
cm = &(Campaign.missions[Campaign.num_missions++]);
|
|
cm->name = strdup(name);
|
|
cm->formula = Locked_sexp_true;
|
|
cm->num_goals = -1;
|
|
cm->notes = NULL;
|
|
cm->briefing_cutscene[0] = 0;
|
|
for (i=0; i<Campaign.num_missions - 1; i++)
|
|
if ((Campaign.missions[i].level == level) && (Campaign.missions[i].pos + 1 == pos)) {
|
|
pos = query_alternate_pos(point);
|
|
break;
|
|
}
|
|
|
|
cm->level = level;
|
|
cm->pos = pos - 1;
|
|
correct_position(Campaign.num_missions - 1);
|
|
sort_links();
|
|
SetScrollSizes(MM_TEXT, CSize(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT));
|
|
Invalidate();
|
|
|
|
// update and reinitialize dialog items
|
|
if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) {
|
|
Campaign_tree_formp->update();
|
|
Campaign_tree_formp->initialize(0);
|
|
}
|
|
|
|
listbox->DeleteString(item);
|
|
Campaign_modified = 1;
|
|
return;
|
|
}
|
|
|
|
void campaign_tree_view::sort_elements()
|
|
{
|
|
int i, j, s1, s2;
|
|
|
|
for (i=0; i<Campaign.num_missions; i++)
|
|
Sorted[i] = i;
|
|
|
|
// sort the tree, so realignment will work property
|
|
for (i=1; i<Campaign.num_missions; i++) {
|
|
s1 = Sorted[i];
|
|
for (j=i-1; j>=0; j--) {
|
|
s2 = Sorted[j];
|
|
if ((Campaign.missions[s1].level > Campaign.missions[s2].level) ||
|
|
((Campaign.missions[s1].level == Campaign.missions[s2].level) &&
|
|
(Campaign.missions[s1].pos > Campaign.missions[s2].pos)))
|
|
break;
|
|
|
|
Sorted[j + 1] = s2;
|
|
}
|
|
|
|
Sorted[j + 1] = s1;
|
|
}
|
|
}
|
|
|
|
void campaign_tree_view::correct_position(int num)
|
|
{
|
|
int i, z;
|
|
|
|
// move missions down if required
|
|
if (Campaign.missions[num].level + 2 > total_levels)
|
|
total_levels = Campaign.missions[num].level + 2;
|
|
|
|
for (i=0; i<Total_links; i++)
|
|
if (Links[i].from == num) {
|
|
z = Links[i].to;
|
|
if ( (num != z) && (Campaign.missions[num].level >= Campaign.missions[z].level) ) {
|
|
Campaign.missions[z].level = Campaign.missions[num].level + 1;
|
|
correct_position(z);
|
|
}
|
|
}
|
|
|
|
// space out horizontally to avoid overlap of missions
|
|
horizontally_align_mission(num, -1);
|
|
horizontally_align_mission(num, 1);
|
|
}
|
|
|
|
void campaign_tree_view::horizontally_align_mission(int num, int dir)
|
|
{
|
|
int i, z;
|
|
|
|
if ((Campaign.missions[num].pos == -1) || (Campaign.missions[num].pos + 1 == total_width * 2)) { // need to expand total_width
|
|
for (i=0; i<Campaign.num_missions; i++)
|
|
Campaign.missions[i].pos++;
|
|
|
|
total_width++;
|
|
}
|
|
|
|
for (i=0; i<Campaign.num_missions; i++) {
|
|
if (i == num)
|
|
continue;
|
|
|
|
if (Campaign.missions[i].level == Campaign.missions[num].level) {
|
|
z = Campaign.missions[i].pos - Campaign.missions[num].pos;
|
|
if (dir < 0) {
|
|
if (!z || (z == -1)) {
|
|
Campaign.missions[i].pos = Campaign.missions[num].pos - 2;
|
|
horizontally_align_mission(i, -1);
|
|
}
|
|
|
|
} else {
|
|
if (!z || (z == 1)) {
|
|
Campaign.missions[i].pos = Campaign.missions[num].pos + 2;
|
|
horizontally_align_mission(i, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void campaign_tree_view::delete_link(int num)
|
|
{
|
|
Assert((num >= 0) && (num < Total_links));
|
|
if (Links[num].from != Links[num].to) {
|
|
Elements[Links[num].from].from_links--;
|
|
Elements[Links[num].to].to_links--;
|
|
}
|
|
|
|
sexp_unmark_persistent(Links[num].sexp);
|
|
free_sexp2(Links[num].sexp);
|
|
while (num < Total_links - 1) {
|
|
Links[num] = Links[num + 1];
|
|
num++;
|
|
}
|
|
|
|
Total_links--;
|
|
sort_links();
|
|
Invalidate();
|
|
Campaign_modified = 1;
|
|
return;
|
|
}
|
|
|
|
int campaign_tree_view::get_root_mission()
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<Campaign.num_missions; i++)
|
|
if (!Campaign.missions[i].level)
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
void campaign_tree_view::OnContextMenu(CWnd* pWnd, CPoint point)
|
|
{
|
|
int i;
|
|
CMenu menu, *popup;
|
|
CPoint p = point;
|
|
CClientDC dc(this);
|
|
|
|
OnPrepareDC(&dc);
|
|
dc.DPtoLP(&p);
|
|
|
|
ScreenToClient(&p);
|
|
for (i=0; i<Campaign.num_missions; i++)
|
|
if (Elements[i].box.PtInRect(p))
|
|
break;
|
|
|
|
if (i < Campaign.num_missions) { // clicked on a mission
|
|
Context_mission = i;
|
|
if (menu.LoadMenu(IDR_CPGN_VIEW_ON)) {
|
|
popup = menu.GetSubMenu(0);
|
|
ASSERT(popup);
|
|
popup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
|
|
}
|
|
|
|
} else {
|
|
Context_mission = query_level(p);
|
|
if ((Context_mission >= 0) && (Context_mission < total_levels))
|
|
if (menu.LoadMenu(IDR_CPGN_VIEW_OFF)) {
|
|
popup = menu.GetSubMenu(0);
|
|
ASSERT(popup);
|
|
popup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void campaign_tree_view::OnRemoveMission()
|
|
{
|
|
remove_mission(Context_mission);
|
|
Invalidate();
|
|
UpdateWindow();
|
|
|
|
// for multiplayer missions, update the data and reiniailize the dialog -- the number of player
|
|
// in the mission might have changed because of deletion of the first mission
|
|
if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) {
|
|
Campaign_tree_formp->update();
|
|
Campaign_tree_formp->initialize();
|
|
}
|
|
}
|
|
|
|
void campaign_tree_view::remove_mission(int m)
|
|
{
|
|
int i, z;
|
|
CEdit *box;
|
|
|
|
Assert(m >= 0);
|
|
Campaign_tree_formp->m_filelist.AddString(Campaign.missions[m].name);
|
|
|
|
z = --Campaign.num_missions;
|
|
i = Total_links;
|
|
while (i--) {
|
|
if ((Links[i].from == m) || (Links[i].to == m))
|
|
delete_link(i);
|
|
if (Links[i].from == z)
|
|
Links[i].from = m;
|
|
if (Links[i].to == z)
|
|
Links[i].to = m;
|
|
}
|
|
|
|
Elements[m] = Elements[z];
|
|
Campaign.missions[m] = Campaign.missions[z];
|
|
if (m == Cur_campaign_mission) {
|
|
Cur_campaign_mission = -1;
|
|
box = (CEdit *) Campaign_tree_formp->GetDlgItem(IDC_HELP_BOX);
|
|
if (box)
|
|
box->SetWindowText("");
|
|
|
|
Campaign_tree_formp->load_tree(0);
|
|
}
|
|
|
|
Campaign_modified = 1;
|
|
}
|
|
|
|
void campaign_tree_view::OnDeleteRow()
|
|
{
|
|
int i, z;
|
|
|
|
if (!Context_mission) {
|
|
MessageBox("Can't delete the top level");
|
|
return;
|
|
}
|
|
|
|
for (i=z=0; i<Campaign.num_missions; i++)
|
|
if (Campaign.missions[i].level == Context_mission)
|
|
z++;
|
|
|
|
if (z) {
|
|
z = MessageBox("Deleting row will remove all missions on this row", "Notice", MB_ICONEXCLAMATION | MB_OKCANCEL);
|
|
if (z == IDCANCEL)
|
|
return;
|
|
}
|
|
|
|
while (i--)
|
|
if (Campaign.missions[i].level == Context_mission)
|
|
remove_mission(i);
|
|
|
|
for (i=0; i<Campaign.num_missions; i++)
|
|
if (Campaign.missions[i].level > Context_mission)
|
|
Campaign.missions[i].level--;
|
|
|
|
total_levels--;
|
|
SetScrollSizes(MM_TEXT, CSize(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT));
|
|
Invalidate();
|
|
UpdateWindow();
|
|
Campaign_modified = 1;
|
|
}
|
|
|
|
void campaign_tree_view::OnInsertRow()
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<Campaign.num_missions; i++)
|
|
if (Campaign.missions[i].level >= Context_mission)
|
|
Campaign.missions[i].level++;
|
|
|
|
total_levels++;
|
|
SetScrollSizes(MM_TEXT, CSize(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT));
|
|
Invalidate();
|
|
UpdateWindow();
|
|
Campaign_modified = 1;
|
|
}
|
|
|
|
void campaign_tree_view::OnAddRepeat()
|
|
{
|
|
if (add_link(Context_mission, Context_mission)) {
|
|
MessageBox("Too many links exist. Can't add any more.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
void campaign_tree_view::OnEndOfCampaign()
|
|
{
|
|
if ( add_link(Context_mission, -1) ) {
|
|
MessageBox("Too many links exist. Cannot add any more.");
|
|
return;
|
|
}
|
|
}
|