procedure TMirMap.BitBlt(DC: HDC; X, Y, AWidth, AHeight: Word); //读取.map行列属性,根据一定规则,,进行实际的游戏世界中的贴图工作,,,图片资源当然在tiles,,,smtiles和objects中了
var
Pt: PMapPoint;
I, J: Word;
ImageIndex, AniIndex: Word;
AniCount: Integer;
// TODO: 本函数中的乘法运算可以优化为加法,CPU做加法比做乘法快
begin
// TODO: 更新正确的 AniCount
AniCount := 1000;
// 画背景图
for J := Y to Y + AHeight do
begin
// 如果纵坐标超出地图范围则终止
// TODO: 这时 I, J 定义为 Word, 会永远为 False, 应该更正, 包括下面的函数
if J >= FHeight then Break;
for I := X to X + AWidth do
begin
// 如果横坐标超出地图范围则终止
if I >= FWidth then Break;
// 取坐标处的地图信息
Pt := GetPoint(I, J);
// 如果是偶数行, 则画大块背景, 背景图尺寸是 96 * 64
if (J mod 2 = 0) and (I mod 2 = 0) then
begin
ImageIndex := Pt.BackImg and $7FFF;
if ImageIndex > 0 then
G_WilTile.BitBlt(ImageIndex - 1, DC, (I-X) * 48, (J-Y) * 32);
end;
// 画前景, 前景图尺寸是 48 * 32
for J := Y to Y + AHeight do
begin
// 如果纵坐标超出地图范围则终止
if J >= FHeight then Break;
for I := X to X + AWidth do
begin
// 如果横坐标超出地图范围则终止
if I >= FWidth then Break;
// 取坐标处的地图信息
Pt := GetPoint(I, J);
ImageIndex := Pt.ForeImg and $7FFF; //与计算,,hex的7fff=b
if ImageIndex > 0 then
begin
AniIndex := Pt.AniFrame;
if (AniIndex and $80 > 0) then AniIndex := AniIndex and $7F;
if AniIndex > 0 then
ImageIndex := ImageIndex + (AniCount mod (AniIndex * (Pt.AniTick + 1)))
div (Pt.AniTick + 1);
if (Pt.DoorOffset and $80 > 0) and (Pt.DoorIndex and $7F > 0) then
Inc(ImageIndex, Pt.DoorIndex and $7F);
// TODO: check value
if Pt.Area > 6 then
raise Exception.Create('err');
destructor TMirMap.Destroy; //destory是一个对象的默认销毁过程,把向系统申请过的资源还给系统
begin
// 关闭已打开的文件句柄等资源
FileName := '';
inherited;
end;
function TMirMap.GetPoint(X, Y: Word): PMapPoint;
begin
Result := IncPointer(FFilePointer, SizeOf(TMapHeader) + //指针按一定的步长前进,,以取得当前.map文件x,ymappoint坐标处的那个地图格信息(它是一个结构,,拥有前面定义的全部字段如for,,mid,,backimg,,aniframe)
SizeOf(TMapPoint) * (FHeight * X + Y));
// 注意, Mir 的地址存放似乎与一般地图方向不同
// Result := IncPointer(FFilePointer, SizeOf(TMapHeader) +
// SizeOf(TMapPoint) * (FWidth * Y + X));
end;
procedure TMirMap.SetFileName(const value: string); //动态加载.map文件,,进行前一个已经加载的.map文件与当前正要加载的.map文件之间在内存里的动态切换
begin
// 如果文件名相同则退出
if FFileName = value then Exit; //在这行执行时,,filename当然会有一个先前的值,,代表前一次加载的地图文件名,,在下面(// 保存地图文件名FFileName := value;)处
// 如果已经打开过地图文件, 则先释放先前的文件句柄
if FFileName <> '' then
begin
UnmapViewOfFile(FFilePointer); //取消文件从硬盘到内存的文件映射过程,,即CreateFileMapping的逆过程
CloseHandle(FFileMapping); //释放文件在内存的文件指针
CloseHandle(FFileHandle); //释放文件硬盘文件指针
end;
// 绘制背景 (BkImg)
AdjustX := MapRect.Left mod 2;
AdjustY := MapRect.Top mod 2;
for I := MapRect.Left - AdjustX to MapRect.Right do
for J := MapRect.Top - AdjustY to MapRect.Bottom do
begin
if (I mod 2 = 0) and (J mod 2 = 0) then
begin
Pt := GetPoint(I, J);
ImageIndex := Pt.BackImg and $7FFF;
if ImageIndex > 0 then
begin
G_WilTile.Draw(ImageIndex - 1, Surface,
(I - MapRect.Left) * MAPUNIT_WIDTH + OffsetX,
(J - MapRect.Top) * MAPUNIT_HEIGHT + OffsetY,
FClientWidth, FClientHeight, False);
end;
end;
end;
// 绘制背景补充 (MidImg)
for I := MapRect.Left to MapRect.Right do
for J := MapRect.Top to MapRect.Bottom do
begin
Pt := GetPoint(I, J);
ImageIndex := Pt.MiddImg;
if ImageIndex > 0 then
begin
G_WilTileSm.Draw(ImageIndex - 1, Surface,
(I - MapRect.Left) * MAPUNIT_WIDTH + OffsetX,
(J - MapRect.Top) * MAPUNIT_HEIGHT + OffsetY,
FClientWidth, FClientHeight, False);
end;
end;
end;
procedure TMirMap.DrawForeground(Surface: IDirectDrawSurface7; CenterX, //画前景即objects
//这里要注意在客户端objects.wil文件中的图片的属性,打开它们,你可以发现它们有的为48*32从到48*448不等,,这就是为什么要在开头定义最高为35,,,448=32*14,,,
CenterY, ShiftX, ShiftY: Integer; FirstStep: Boolean);
var
MapRect: TRect; // 需要绘制的 MAP 坐标范围
OffsetX, OffsetY: Integer; // X, Y 左上角偏移
I, J: Integer;
Pt: PMapPoint;
InfoPtr: PImageInfo;
ImageIndex: Word;
AniIndex: Byte;
IsBlend: Boolean; //是否需要blend绘制,,,可见objects这些静态显示的图片(相对hum,,mon可以构成动画效果的系列图片),,,有时也需要blend显示,,想像一下,,比如盟重安全区圣诞树上的彩带之类的
begin
// 自地图中间至最左/最上的宽度(象素)
I := (FClientWidth - MAPUNIT_WIDTH) div 2 - ShiftX;
J := (FClientHeight - MAPUNIT_HEIGHT) div 2 - ShiftY;
// 绘制前景 (FrImg)
for I := MapRect.Left to MapRect.Right do
for J := MapRect.Top to MapRect.Bottom do
begin
Pt := GetPoint(I, J);
ImageIndex := Pt.ForeImg and $7FFF;
if ImageIndex > 0 then
begin
IsBlend := False;
AniIndex := Pt.AniFrame;
if AniIndex and $80 > 0 then
begin
IsBlend := True;
AniIndex := AniIndex and $7F;
end;
if AniIndex > 0 then
begin
Inc(ImageIndex, (AniCount mod (AniIndex * (Pt.AniTick + 1))) div (Pt.AniTick + 1));
end;
if (Pt.DoorOffset and $80 > 0) and (Pt.DoorIndex and $7F > 0) then
Inc(ImageIndex, Pt.DoorIndex and $7F);
// TODO: check value
if Pt.Area > 6 then
raise Exception.Create('err');
// 如果图片尺寸=48/32则按正常方式绘制
if FirstStep then
begin
if (InfoPtr^.Width = 48) and (InfoPtr^.Height = 32) then
begin
G_WilObjects[Pt.Area].Draw(ImageIndex - 1, Surface,
(I - MapRect.Left) * MAPUNIT_WIDTH + OffsetX,
(J - MapRect.Top) * MAPUNIT_HEIGHT + OffsetY,
FClientWidth, FClientHeight, True);
end
end
else begin
// 如果不是混合方式
if not IsBlend then
begin
if (InfoPtr^.Width <> 48) or (InfoPtr^.Height <> 32) then
G_WilObjects[Pt.Area].Draw(ImageIndex - 1, Surface,
(I - MapRect.Left) * MAPUNIT_WIDTH + OffsetX,
(J - MapRect.Top + 1) * MAPUNIT_HEIGHT + OffsetY - InfoPtr^.Height, // 要用减去图片高度
FClientWidth, FClientHeight, True);
end
else
// 否则, 是混合方式
{ G_WilObjects[Pt.Area].Draw(ImageIndex - 1, Surface,
(I - MapRect.Left) * MAPUNIT_WIDTH + OffsetX + InfoPtr^.PX - 2,
(J - MapRect.Top) * MAPUNIT_HEIGHT + OffsetY + InfoPtr^.PY - 68,
FClientWidth, FClientHeight, True)}
DrawBlend(Surface,
(I - MapRect.Left) * MAPUNIT_WIDTH + OffsetX + InfoPtr^.PX - 2,
(J - MapRect.Top) * MAPUNIT_HEIGHT + OffsetY + InfoPtr^.PY - 68,
FClientWidth, FClientHeight,
G_WilObjects[Pt.Area].Surfaces[ImageIndex - 1],
InfoPtr^.Width, InfoPtr^.Height, 0);
end;
end;
end;
end;
function TMirMap.CanFly(X, Y: Word): Boolean;
var
Pt: PMapPoint;
begin
Result := False;
if X >= FWidth then Exit;
if Y >= FHeight then Exit;
Pt := Point[X, Y];
Result := Pt.ForeImg and $8000 = 0;
if Result then
begin
if (Pt.DoorIndex and $80 > 0) and (Pt.DoorOffset and $80 = 0) then
Result := False;
end;
end;
function TMirMap.CanMove(X, Y: Word): Boolean;
var
Pt: PMapPoint;
begin
Result := False;
if X >= FWidth then Exit;
if Y >= FHeight then Exit;
Pt := Point[X, Y];
Result := (Pt.BackImg and $8000 = 0) and (Pt.ForeImg and $8000 = 0);
if Result then
begin
if (Pt.DoorIndex and $80 > 0) and (Pt.DoorOffset and $80 = 0) then
Result := False;
end;
end;