//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "MainForm.h"
//---------------------------------------------------------------------------
#include <stdio.h>
#include "AboutForm.h"
#pragma package(smart_init)
#pragma resource "*.dfm"
//******************************************************************************
//******************************************************************************
//******************************************************************************
// Global stuff
//******************************************************************************
TfrmMain *frmMain;
// Set up some colors
TColor MapColors[] = {clAqua, // empty
clWhite, // obstacle
clBlue, // character
clRed, // enemy
clYellow // prize
};
ColorID ColorMap[] = {{ctEMPTY, MapColors[ctEMPTY]},
{ctOBSTACLE, MapColors[ctOBSTACLE]},
{ctCHARACTER, MapColors[ctCHARACTER]},
{ctENEMY, MapColors[ctENEMY]},
{ctPRIZE, MapColors[ctPRIZE]}
};
/*
Constructor.
*/
__fastcall TfrmMain::TfrmMain(TComponent* Owner) : TForm(Owner)
{
pnlWorld->DoubleBuffered = true; // prevent flicker
gridWorld->DoubleBuffered = true; // prevent flicker
// Some defaults
FShowGrid = true;
FGridColor = clBlack;
FCurrColorID = ColorMap[ctOBSTACLE];
FColumns = 20;
FRows = 20;
FCurrFileName = "Exported.txt"; // for compatibility with CS230
// Setup the GUI
Init();
}
//---------------------------------------------------------------------------
/*
Initializes the state of the game and the UI
*/
void TfrmMain::Init(void)
{
for (int i = 0; i < FMaxRows; i++)
for (int j = 0; j < FMaxColumns; j++)
FDataMap[i][j] = 0;
spnRows->Position = FRows;
spnColumns->Position = FColumns;
// Assume mouse if off of the grid
FCurrRow = -1;
FCurrColumn = -1;
FPrevRow = -1;
FPrevColumn = -1;
AdjustGrid();
UpdateCurrentCellDisplay();
}
/*
Displays current row/column in the status bar
*/
void TfrmMain::UpdateCurrentCellDisplay(void)
{
// Invalid location, erase the display
if (FOffGrid || FCurrColumn == -1 || FCurrRow == -1)
barStatus->Panels->Items[0]->Text = "";
else
barStatus->Panels->Items[0]->Text = Format(" Row: %d, Col: %d", ARRAYOFCONST((FCurrRow + 1, FCurrColumn + 1)));
}
/*
If the grid is resized, need to update variables with the new sizes
*/
void TfrmMain::AdjustGrid(void)
{
gridWorld->ColCount = FColumns;
gridWorld->RowCount = FRows;
FCellWidth = gridWorld->Width / FColumns;
FCellHeight = gridWorld->Height / FRows;
// The size of the cells without the borders
if (gridWorld->GridLineWidth)
{
FCellWidth -= gridWorld->GridLineWidth;
FCellHeight -= gridWorld->GridLineWidth;
}
gridWorld->DefaultColWidth = FCellWidth;
gridWorld->DefaultRowHeight = FCellHeight;
TGridRect rect = {{FCurrColumn, FCurrRow}, {FCurrColumn, FCurrRow}};
gridWorld->Selection = rect;
}
/*
Displays text in the bottom of the app. Used for debugging.
*/
void TfrmMain::AddStatus(const char *text)
{
mmoStatus->Lines->Insert(0, text);
}
/*
Draw the cell
*/
void TfrmMain::HighlightCell(bool Force)
{
// If we are in the same cell, do nothing
if ( (!Force) && (FPrevRow == FCurrRow && FPrevColumn == FCurrColumn) )
return;
// Otherwise, we've moved from one cell to the next.
// Un-highlight (lowlight?) the previous cell.
TRect rect = gridWorld->CellRect(FPrevColumn, FPrevRow);
TGridDrawState state;
gridWorldDrawCell(this, FPrevColumn, FPrevRow, rect, state);
// Off the board, do nothing
if (FCurrRow < 0 || FCurrRow >= FRows || FCurrColumn < 0 || FCurrColumn >= FColumns)
return;
// Highlight the new cell
rect = gridWorld->CellRect(FCurrColumn, FCurrRow);
gridWorldDrawCell(this, FCurrColumn, FCurrRow, rect, state);
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
// Event Handlers
//******************************************************************************
void __fastcall TfrmMain::FormCreate(TObject *Sender)
{
pgeWorld->Align = alClient;
pnlWorld->Align = alClient;
gridWorld->Align = alClient;
pnlBottom->BevelOuter = bvNone;
pnlBottom->Color = MapColors[ctEMPTY];
gridWorld->Color = MapColors[ctEMPTY];
// Size of the main window
Width = 700;
Height = 700;
// Default size
spnColumns->Position = FColumns;
spnRows->Position = FRows;
// Hide the memo for now
mmoStatus->Height = 0;
// Register this application with the OS as a drop target.
DragAcceptFiles(this->Handle, true);
}
//---------------------------------------------------------------------------
/*
Called each time a cell needs to be repainted.
*/
void __fastcall TfrmMain::gridWorldDrawCell(TObject *Sender, int ACol, int ARow,
TRect &Rect, TGridDrawState State)
{
// Rectangle of cell to draw
Rect.Left = Rect.Left - 1;
Rect.Right = Rect.Right;
Rect.Top = Rect.Top - 1;
Rect.Bottom = Rect.Bottom;
// Draw a 3 pixel wide black border on the highlighted cell
if (ACol == FCurrColumn && ARow == FCurrRow && !FOffGrid && (ACol != -1 && ARow != -1) )
{
gridWorld->Canvas->Pen->Width = 3;
gridWorld->Canvas->Pen->Color = clBlack;
Rect.left += gridWorld->Canvas->Pen->Width - 1;
Rect.Top += gridWorld->Canvas->Pen->Width - 1;
Rect.right -= gridWorld->Canvas->Pen->Width - 1;
Rect.Bottom -= gridWorld->Canvas->Pen->Width - 1;
}
else // restore the default border
{
gridWorld->Canvas->Pen->Width = 1;
gridWorld->Canvas->Pen->Color = FGridColor;
}
// BUG FIX (from WarBoats!)
if ((ACol == -1) || (ARow == -1))
return;
// The cell is empty
if (FDataMap[ARow][ACol] == ctEMPTY)
{
gridWorld->Canvas->Brush->Color = MapColors[ctEMPTY];
gridWorld->Canvas->Rectangle(Rect);
}
// The cell is an obstacle (collision)
else if (FDataMap[ARow][ACol] == ctOBSTACLE)
{
gridWorld->Canvas->Brush->Color = MapColors[ctOBSTACLE];
gridWorld->Canvas->Rectangle(Rect);
}
// The cell is the character in the game
else if (FDataMap[ARow][ACol] == ctCHARACTER)
{
gridWorld->Canvas->Brush->Color = MapColors[ctCHARACTER];
gridWorld->Canvas->Rectangle(Rect);
}
// The cell is an enemy
else if (FDataMap[ARow][ACol] == ctENEMY)
{
gridWorld->Canvas->Brush->Color = MapColors[ctENEMY];
gridWorld->Canvas->Rectangle(Rect);
}
// The cell is a prize (coin)
else if (FDataMap[ARow][ACol] == ctPRIZE)
{
gridWorld->Canvas->Brush->Color = MapColors[ctPRIZE];
gridWorld->Canvas->Rectangle(Rect);
}
}
//---------------------------------------------------------------------------
/*
Called when a cell is selected (with the mouse or keyboard).
*/
void __fastcall TfrmMain::gridWorldSelectCell(TObject *Sender, int ACol, int ARow, bool &CanSelect)
{
FCurrRow = ARow;
FCurrColumn = ACol;
FDataMap[ARow][ACol] = FCurrColorID.type;
UpdateCurrentCellDisplay();
}
//---------------------------------------------------------------------------
/*
Tracks the mouse movement across the grid so the cell under the mouse
can be highlighted.
*/
void __fastcall TfrmMain::gridWorldMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
{
TGridCoord coord = gridWorld->MouseCoord(X, Y);
FPrevRow = FCurrRow;
FPrevColumn = FCurrColumn;
FCurrRow = coord.Y;
FCurrColumn = coord.X;
if (Shift.Contains(ssRight))
{
if (FPrevRow == FCurrRow && FPrevColumn == FCurrColumn)
return;
FDataMap[FCurrRow][FCurrColumn] = ctEMPTY;
}
UpdateCurrentCellDisplay();
HighlightCell();
}
//---------------------------------------------------------------------------
/*
When the mouse leaves the grid, we need to update the display.
*/
void __fastcall TfrmMain::gridWorldMouseLeave(TObject *Sender)
{
FOffGrid = true;
TRect rect = gridWorld->CellRect(FCurrColumn, FCurrRow);
TGridDrawState state;
gridWorldDrawCell(this, FCurrColumn, FCurrRow, rect, state);
UpdateCurrentCellDisplay();
}
//---------------------------------------------------------------------------
/*
When the mouse moves onto the grid, we need to start tracking it and update
the display.
*/
void __fastcall TfrmMain::gridWorldMouseEnter(TObject *Sender)
{
FOffGrid = false;
TRect rect = gridWorld->CellRect(FCurrColumn, FCurrRow);
TGridDrawState state;
gridWorldDrawCell(this, FCurrColumn, FCurrRow, rect, state);
}
//---------------------------------------------------------------------------
/*
Handles the event when the sheet (in the PageControl) is resized.
*/
void __fastcall TfrmMain::shtPierResize(TObject *Sender)
{
AdjustGrid();
}
//---------------------------------------------------------------------------
/*
If the splitter bar is moved, the board will be resized and needs to be
updated.
*/
void __fastcall TfrmMain::splitHorizontalMoved(TObject *Sender)
{
AdjustGrid();
}
//---------------------------------------------------------------------------
/*
When the user changes the number of rows/columns, need to update the
display.
*/
void __fastcall TfrmMain::OnCellCountChange(TObject *Sender)
{
FRows = spnRows->Position;
FColumns = spnColumns->Position;
AdjustGrid();
}
//---------------------------------------------------------------------------
void __fastcall TfrmMain::ColorChange(TObject *Sender)
{
TButton *b = (TButton *)Sender;
FCurrColorID = ColorMap[b->Tag];
}
//---------------------------------------------------------------------------
/*
Handler for File Open
*/
void __fastcall TfrmMain::actFileOpenExecute(TObject *Sender)
{
if (dlgFileOpen->Execute())
{
Init();
if (ImportMapData(dlgFileOpen->FileName.c_str()))
{
short r = FRows;
short c = FColumns;
spnRows->Position = r;
spnColumns->Position = c;
}
}
}
//---------------------------------------------------------------------------
/*
Handler for File save
*/
void __fastcall TfrmMain::actFileSaveExecute(TObject *Sender)
{
if (dlgFileSaveText->Execute())
ExportMapData(dlgFileSaveText->FileName.c_str());
}
//---------------------------------------------------------------------------
/*
Handler for testing the level in the game
*/
void __fastcall TfrmMain::actTestExecute(TObject *Sender)
{
// Export the current level
ExportMapData(FCurrFileName.c_str());
// Launch the game
int result = WinExec("CS230_Platform.exe", SW_NORMAL);
// If it failed to launch for some reason
if (result < 32)
ShowMessage(IntToStr(result));
}
//---------------------------------------------------------------------------
/*
Handler for File New
*/
void __fastcall TfrmMain::actFileNewExecute(TObject *Sender)
{
Init();
}
//---------------------------------------------------------------------------
/*
Handler for toggling the grid on/off
*/
void __fastcall TfrmMain::actShowGridExecute(TObject *Sender)
{
FShowGrid = !FShowGrid; // toggle
ChangeGrid(FShowGrid);
}
//---------------------------------------------------------------------------
void TfrmMain::ChangeGrid(bool Show)
{
if (Show)
FGridColor = clBlack;
else
FGridColor = MapColors[ctEMPTY];
gridWorld->Refresh(); // repaint grid
}
/*
Handler for the WM_DROPFILES message from Windows.
*/
void __fastcall TfrmMain::WMDropFiles(TWMDropFiles &Message)
{
int num_files, filename_length;
char file_name[MAX_PATH + 1];
// Refer to the Win SDK documentation about the details
// of DragQueryFile and the HDROP structure
// Get the number of files that are being dropped on the windows
num_files = DragQueryFile((HDROP)Message.Drop, -1, NULL, 0);
// Unlikely to happen
if (num_files < 1)
return;
// Retrieve each filename that was dropped
for(int i = 0; i < num_files; i++)
{
// Get the length of the i'th filename
filename_length = DragQueryFile((HDROP)Message.Drop, i, NULL, 0);
// Retrieve the filename from the HDROP structure
DragQueryFile((HDROP)Message.Drop, i, file_name, filename_length + 1);
// Reset the editor
Init();
// Read in the data
ImportMapData(file_name);
// Update the UI
short r = FRows;
short c = FColumns;
spnRows->Position = r;
spnColumns->Position = c;
// We can only deal with one at this point so bail out
return;
}
}
/*
Handler for the OnMouseDown event. The Shift parameter contains
additional information such as the state of the control/alt/shift keys.
*/
void __fastcall TfrmMain::gridWorldMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)
{
// If the right mouse button is pressed down
if (Button == mbRight)
{
// Convert the x/y pixel location to a row and column in the grid
TGridCoord coord = gridWorld->MouseCoord(X, Y);
// Update current row/column
FCurrRow = coord.Y;
FCurrColumn = coord.X;
// Set this cell as empty (background color)
FDataMap[FCurrRow][FCurrColumn] = ctEMPTY;
// Paint the cell
HighlightCell(true);
}
}
//---------------------------------------------------------------------------
/*
Handler for the "Help | About..." event
*/
void __fastcall TfrmMain::actHelpAboutExecute(TObject *Sender)
{
frmAbout->ShowModal();
}
//---------------------------------------------------------------------------
bool TfrmMain::ImportMapData(const AnsiString &FileName)
{
FILE *fp;
char tmpStr[20] = {0};
int Value;
int i, j;
fp = fopen(FileName.c_str(), "rt");
if(!fp)
return 0;
// e.g. Width 20
fscanf(fp, "%s %i", tmpStr, &Value);
FColumns = (short)Value;
// e.g. Height 20
fscanf(fp, "%s %i", tmpStr, &Value);
FRows = (short)Value;
for(j = 0; j < FRows; ++j)
{
for(i = 0; i < FColumns; ++i)
{
fscanf(fp, "%i", &Value);
FDataMap[FRows - j - 1][i] = Value;
}
}
fclose(fp);
return 1;
}
bool TfrmMain::ExportMapData(const AnsiString &FileName)
{
FILE *fp;
int Value;
int i, j;
fp = fopen(FileName.c_str(), "wt");
if(!fp)
return 0;
fprintf(fp, "Width %i\n", FColumns);
fprintf(fp, "Height %i\n", FRows);
for(j = 0; j < FRows; ++j)
{
for(i = 0; i < FColumns; ++i)
{
Value = FDataMap[FRows - j - 1][i];
fprintf(fp, "%i ", Value);
}
fprintf(fp, "\n");
}
fclose(fp);
return 1;
}