www.pudn.com > Tank拊親蚔牁測鎢.rar > MAIN.PAS
unit main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls, Grids, Menus, Math, Sprite, ComCtrls;
type
TMainForm = class(TForm)
pnlView: TPanel;
pbxView: TPaintBox;
mnuMain: TMainMenu;
F1: TMenuItem;
mnuChooseLevel: TMenuItem;
N1: TMenuItem;
mnuExit: TMenuItem;
H1: TMenuItem;
mnuAbout: TMenuItem;
E1: TMenuItem;
mnuEditTerr: TMenuItem;
mnuEditItem: TMenuItem;
mnuEditRole: TMenuItem;
N2: TMenuItem;
mnuShowGrid: TMenuItem;
mnuShowSpeicalArea: TMenuItem;
mnuResetMap: TMenuItem;
V1: TMenuItem;
stbMain: TStatusBar;
N4: TMenuItem;
mnuRemoveItem: TMenuItem;
mnuSave: TMenuItem;
N3: TMenuItem;
mnuRestoreLevel: TMenuItem;
mnuEditTerrItem: TMenuItem;
mnuEditHiTerrItem: TMenuItem;
Splitter1: TSplitter;
pnlRight: TPanel;
Splitter2: TSplitter;
grdTile: TDrawGrid;
tvwTiles: TTreeView;
mnuResetAllMap: TMenuItem;
mnuDrawEditLayerOnly: TMenuItem;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure pbxViewPaint(Sender: TObject);
procedure mnuEditRoleClick(Sender: TObject);
procedure mnuExitClick(Sender: TObject);
procedure pbxViewMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure mnuShowSomething(Sender: TObject);
procedure mnuResetMapClick(Sender: TObject);
procedure mnuRemoveItemClick(Sender: TObject);
procedure mnuChooseLevelClick(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure mnuSaveClick(Sender: TObject);
procedure mnuAboutClick(Sender: TObject);
procedure pbxViewMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure pbxViewMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure mnuRestoreLevelClick(Sender: TObject);
procedure grdTileDrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
procedure tvwTilesChange(Sender: TObject; Node: TTreeNode);
private
FEditLayer: Integer; // 目前編輯模式
FLevelNo: Integer; // 目前編輯的關卡
FModified: Boolean; // 載入關卡後是否更動過
FTank: TMyTank; // 角色物件
FBackBitmap: TBitmap; // double-buffering 用的背景 bitmap
FCursorX, FCursorY: Integer; // 滑鼠游標位置
FButtonPressed: TMouseButton; // 滑鼠按鍵狀態
function GetCursorW: Integer;
function GetCursorH: Integer;
procedure DrawBackBitmap; // 繪製背景 bitmap
procedure UpdateView; // 更新編輯畫面
procedure UpdateControlStatus;
procedure SetLevelNo(Value: Integer);
procedure MySaveFile; // 儲存地圖檔案
function AskSaveMap: Boolean; // 確認是否儲存地圖
function ValidateMap: Boolean; // 檢查地圖是否合法
property LevelNo: Integer read FLevelNo write SetLevelNo;
public
{ Public declarations }
end;
var
MainForm: TMainForm;
implementation
uses util, tileunit, Mapunit, about, xFiles, xUtils;
{$R *.DFM}
procedure TMainForm.FormCreate(Sender: TObject);
begin
FEditLayer := 0; // 預設為地形編輯模式
FButtonPressed := mbMiddle; // 表示目前滑鼠鍵沒有按著
FTank := TMyTank.Create; // 主角物件
Tiles := TTiles.Create; // 產生圖庫物件
Map := tmap.Create; // 關卡地圖
try
FTank.LoadBits; // 讀入主角圖形
Tiles.LoadFromFile(AppDir + FN_TILE_ARCHIVE); // 讀入圖庫
except
on E: Exception do
begin
ShowMessage(E.message);
PostMessage(Handle, WM_CLOSE, 0, 0); // 以 asynchronous 方式關閉視窗
end;
end;
// 讀入圖庫描述資料
ReadComponentResFile(ChangeFileExt(AppDir + FN_TILE_ARCHIVE, '.TVW'), tvwTiles);
tvwTiles.Selected := tvwTiles.Items.GetFirstNode;
// According tile num and size, adjust dimension of pnlView
pnlView.ClientWidth := WORLD_WIDTH;
pnlView.ClientHeight := WORLD_HEIGHT;
ClientHeight := pnlView.Height + stbMain.Height;
FBackBitmap := TBitmap.Create; // 緩衝用 bitmap
FBackBitmap.Width := TILE_WIDTH * TILE_NUM_X;
FBackBitmap.Height := TILE_HEIGHT * TILE_NUM_Y;
LevelNo := 1;
UpdateControlStatus;
// 滑鼠游標位置
FCursorX := -1;
FCursorY := -1;
end;
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
FBackBitmap.Free;
FTank.Free;
Tiles.Free;
Map.Free;
end;
procedure TMainForm.UpdateView;
begin
DrawBackBitmap;
pbxView.Canvas.Draw(0, 0, FBackBitmap);
end;
procedure TMainForm.pbxViewPaint(Sender: TObject);
begin
UpdateView;
end;
procedure TMainForm.DrawBackBitmap;
var
X, Y: Integer;
begin
if (FEditLayer = LAYER_TERR) or not mnuDrawEditLayerOnly.Checked then Map.DrawMap(LAYER_TERR, FBackBitmap.Canvas) // 繪製地形層
else begin // 若沒畫地形層, 就塗黑
FBackBitmap.Canvas.Brush.Color := clBlack;
FBackBitmap.Canvas.FillRect(FBackBitmap.Canvas.ClipRect);
end;
if (FEditLayer = LAYER_TERRITEM) or not mnuDrawEditLayerOnly.Checked then Map.DrawMap(LAYER_TERRITEM, FBackBitmap.Canvas); // 繪製地形物層
if (FEditLayer = LAYER_ITEM) or not mnuDrawEditLayerOnly.Checked then Map.DrawMap(LAYER_ITEM, FBackBitmap.Canvas); // 繪製物品層
// 如果使用者想看格線, 就將格線畫出來
if mnuShowGrid.Checked then
with FBackBitmap.Canvas do
begin
Pen.Color := clBlack;
Pen.Style := psSolid;
Pen.Width := 1;
// 先畫橫線
for Y := 0 to TILE_NUM_Y - 1 do
begin
MoveTo(0, Y * TILE_HEIGHT);
LineTo(TILE_NUM_X * TILE_WIDTH, Y * TILE_HEIGHT);
end;
// 再畫直線
for X := 0 to TILE_NUM_X - 1 do
begin
MoveTo(X * TILE_WIDTH, 0);
LineTo(X * TILE_WIDTH, TILE_NUM_Y * TILE_HEIGHT);
end;
end;
// 使用者想看不可行走區域
if mnuShowSpeicalArea.Checked then
with FBackBitmap.Canvas do
begin
Pen.Color := clRed;
Pen.Width := 1;
Pen.Style := psDash;
Brush.Style := bsClear;
for Y := 0 to TILE_NUM_Y - 1 do // 逐一檢查
for X := 0 to TILE_NUM_X - 1 do
if not Map[LAYER_TERRITEM, X, Y].CanPass then
begin // 外框加上右上畫到左下的紅色虛線
Rectangle(X * TILE_WIDTH, Y * TILE_HEIGHT, (X + 1) * TILE_WIDTH - 1, (Y + 1) * TILE_HEIGHT - 1);
MoveTo((X + 1) * TILE_WIDTH, Y * TILE_HEIGHT);
LineTo(X * TILE_WIDTH, (Y + 1) * TILE_HEIGHT);
end;
end;
FTank.Draw(FBackBitmap.Canvas); // 畫出角色圖案
if (FEditLayer = LAYER_HITERRITEM) or not mnuDrawEditLayerOnly.Checked then Map.DrawMap(LAYER_HITERRITEM, FBackBitmap.Canvas); // 繪製高地形物層
end;
procedure TMainForm.UpdateControlStatus;
const
LayerDesc: array[0..4] of string = ('地形', '地形物', '物品', '高地形物', '主角');
begin
Caption := Format('%s - 任務 %d (編輯狀態: %s)',[Application.Title, FLevelNo, LayerDesc[FEditLayer]]);
grdTile.Invalidate;
end;
procedure TMainForm.SetLevelNo(Value: Integer);
begin
try
// 讀取地圖檔及角色位置
Map.LevelNo := Value;
except
// 若讀取地圖檔失敗, 清除整張地圖
Map.ResetAllMap;
// 擺上磚牆
Map[LAYER_TERRITEM, 5, 11].TileNo := 2;
Map[LAYER_TERRITEM, 6, 11].TileNo := 2;
Map[LAYER_TERRITEM, 7, 11].TileNo := 2;
Map[LAYER_TERRITEM, 5, 12].TileNo := 2;
Map[LAYER_TERRITEM, 7, 12].TileNo := 2;
// 擺上旗子
Map[LAYER_TERRITEM, 6, 12].TileNo := 5;
end;
FTank.X := Map.Role_X; // 將角色位置由 Map 物件中抄出來
FTank.Y := Map.Role_Y;
FLevelNo := Value;
FModified := False; // 地圖尚未更動(當然:p)
UpdateControlStatus;
UpdateView; // 別忘了更新遊戲畫面
end;
procedure TMainForm.mnuEditRoleClick(Sender: TObject);
var
bEnable: Boolean;
begin
(Sender as TMenuItem).Checked := True;
FEditLayer := (Sender as TMenuItem).Tag; // 設定選取的編輯模式
UpdateControlStatus;
UpdateView;
end;
procedure TMainForm.mnuExitClick(Sender: TObject);
begin
Close;
end;
procedure TMainForm.pbxViewMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
begin
// 已經跑出範圍外了...
if (X < 0) or (X >= TILE_WIDTH * TILE_NUM_X) or (Y < 0) or
(Y >= TILE_HEIGHT * TILE_NUM_Y) then Exit;
FCursorX := X div TILE_WIDTH; // 換算座標, 以圖格為單位
FCursorY := Y div TILE_HEIGHT;
pbxViewMouseDown(Sender, FButtonPressed, Shift, X, Y); // 可以拉曳置放物品的效果
UpdateView; // 重繪遊戲畫面
// 畫出目前所選取的區域外框
with pbxView.Canvas do
begin
Pen.Width := 2;
Pen.Color := clBlue;
Brush.Style := bsClear;
Rectangle(FCursorX * TILE_WIDTH, FCursorY * TILE_HEIGHT,
(FCursorX + GetCursorW) * TILE_WIDTH, (FCursorY + GetCursorH) * TILE_HEIGHT);
end;
stbMain.SimpleText := Format('位置: (%d, %d) 大小: (%d, %d)',[FCursorX, FCursorY, GetCursorW, GetCursorH]);
end;
procedure TMainForm.mnuShowSomething(Sender: TObject);
begin
(Sender as TMenuItem).Checked := not (Sender as TMenuItem).Checked;
UpdateView;
end;
procedure TMainForm.mnuResetMapClick(Sender: TObject);
begin
case (Sender as TComponent).Tag of
0: if FEditLayer <= LAYER_MAX then Map.ResetMap(FEditLayer);
1: Map.ResetAllMap;
end;
UpdateView;
end;
procedure TMainForm.mnuRemoveItemClick(Sender: TObject);
begin
if (FCursorX = -1) or (FCursorY = -1) then Exit;
if FEditLayer <= LAYER_MAX then
Map[FEditLayer, FCursorX, FCursorY].TileNo := 0;
UpdateView;
end;
procedure TMainForm.mnuChooseLevelClick(Sender: TObject);
var
S: string;
begin
if not AskSaveMap then Exit;
S := IntToStr(FLevelNo);
if InputQuery(Application.Title, 'Input Level No:', S) then
LevelNo := StrToInt(S);
end;
function TMainForm.AskSaveMap: Boolean;
begin
Result := True;
if not FModified then Exit;
// 確認是否儲存目前編輯的地圖檔案
case YesNoCancelBox(Format('Do you want to save this map (level %d) ?',[FLevelNo])) of
mrYes: MySaveFile;
mrNo:;
mrCancel: Result := False;
end;
end;
procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if not AskSaveMap then CanClose := False;
end;
procedure TMainForm.MySaveFile;
begin
if not FModified then Exit;
if not ValidateMap then Exit;
Map.Role_X := FTank.X;
Map.Role_Y := FTank.Y;
Map.SaveToFile;
FModified := False;
end;
procedure TMainForm.mnuSaveClick(Sender: TObject);
begin
MySaveFile;
end;
// 檢查地圖是否合法
function TMainForm.ValidateMap: Boolean;
var
X, Y : Integer;
FlagCount: Integer;
begin
Result := False;
for Y := 0 to TILE_NUM_Y - 1 do
for X := 0 to TILE_NUM_X - 1 do
begin
// 看看有幾支旗子
if taFlag in Tiles[Map[LAYER_TERRITEM, X, Y].TileNo].Attr then Inc(FlagCount);
end;
if FlagCount = 0 then
begin
ShowMessage('沒有放軍旗, 不行');
Exit;
end;
Result := True;
end;
procedure TMainForm.mnuAboutClick(Sender: TObject);
begin
with TAboutBox.Create(self) do
try
ShowModal;
finally
Free;
end;
end;
procedure TMainForm.pbxViewMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
No : Integer;
MX, MY: Integer;
begin
// 記錄按下的滑鼠鍵, 配合 OnMouseMove 事件處理方法
// 產生拉曳設定效果
FButtonPressed := Button;
if Button = mbMiddle then Exit; // 滑鼠中鍵不做任何事
if Button = mbLeft then // 左鍵是設定
begin
if tvwTiles.Selected = nil then Exit; // 沒有選定任何群組
No := Integer(tvwTiles.Selected.Data); // 圖片群組的頭頭編號
// 左上角, 正上方及右上角三處是敵方坦克的出生點, 不能放東西
if (FEditLayer <> LAYER_TERR) and (FCursorY = 0) and
((FCursorX = 0) or (FCursorX = TILE_NUM_X div 2 + 1) or
(FCursorX = TILE_NUM_X - 1)) then Exit;
if FEditLayer <= LAYER_MAX then
begin
for MY := 0 to GetCursorH - 1 do // 將新地形擺上
for MX := 0 to GetCursorW - 1 do
Map[FEditLayer, FCursorX + MX, FCursorY + MY].TileNo :=
No + Tiles[No].xNum * (MY + grdTile.Selection.Top) +
(MX + grdTile.Selection.Left);
end else
begin
// 角色不可以擺在不可走動的地形上
if not Map[LAYER_TERRITEM, FCursorX, FCursorY].CanPass then
Exit;
FTank.X := FCursorX * TILE_WIDTH; // 設定主角初始位置
FTank.Y := FCursorY * TILE_HEIGHT;
end;
end else // 右鍵是清除
begin
if FEditLayer > LAYER_MAX then Exit; // 角色不用清除
for MY := 0 to GetCursorH - 1 do
for MX := 0 to GetCursorW - 1 do // 清除此地形
Map[FEditLayer, FCursorX + MX, FCursorY + MY].TileNo := 0;
end;
FModified := True; // 此地圖已更改
UpdateView; // 更新地圖畫面
end;
procedure TMainForm.pbxViewMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
FButtonPressed := mbMiddle; // 設定為 mbMiddle 表示使用者已放開滑鼠鍵
end;
procedure TMainForm.mnuRestoreLevelClick(Sender: TObject);
begin
LevelNo := LevelNo;
end;
procedure TMainForm.grdTileDrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var
No : Integer;
X, Y: Integer;
begin
if FEditLayer > LAYER_MAX then Exit;
if tvwTiles.Selected = nil then Exit;
No := Integer(tvwTiles.Selected.Data);
if (ARow * grdTile.ColCount + ACol >= Tiles[No].xNum * Tiles[No].yNum) then Exit;
Inc(No, ARow * grdTile.ColCount + ACol);
if No >= Tiles.TileNum then Exit;
with grdTile.Canvas do // 將對應的圖形畫出來
Draw(Rect.Left, Rect.Top, Tiles[No].Bitmap);
end;
procedure TMainForm.tvwTilesChange(Sender: TObject; Node: TTreeNode);
var
No: Integer;
begin
if tvwTiles.Selected = nil then Exit;
No := Integer(tvwTiles.Selected.Data); // 取得目前圖格群組編號
with grdTile, grdTile.Selection do
begin
RowCount := Tiles[No].yNum; // 設定 grid 大小, 顯示整個圖格群組
ColCount := Tiles[No].xNum;
Left := 0;
Top := 0;
Right := Tiles[No].xNum - 1;
Bottom := Tiles[No].yNum - 1;
end;
grdTile.Invalidate;
end;
// 取得選擇區域的寬及高
function TMainForm.GetCursorW: Integer;
begin
with grdTile.Selection do
Result := Right - Left + 1;
end;
function TMainForm.GetCursorH: Integer;
begin
with grdTile.Selection do
Result := Bottom - Top + 1;
end;
end.