玩传奇论坛

 找回密码
立即注册

扫一扫,访问移动社区

论坛喇叭+ 发布

01-25 10:01
系统通知全站压缩包解压密码:www.wanmirbbs.com
01-25 10:01
01-25 10:01
查看: 7347|回复: 1
打印 上一主题 下一主题

Mir2引擎之地图说明

[复制链接]
跳转到指定楼层
楼主
发表于 2021-12-13 18:48:22 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,以游结友!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
Mir2的地图场景在逻辑上分三层,从里到外分别为:1,tiles,,smtiles构成的地面背景,,2,,objects构成的地图前景,3,,,随时活动着的人物
(实际上人物的绘制不是地图单元要实现的,,,而是在场景单元playscn中实现的,,所以上面的3也可以删去)

tiles,,大块的地面,,smtiles,,小块地面,smtiles填补tiles不能补充的地面部分,,如一些大块tiles拼接处绘制不到的缝隙,如小块草地(实际
上这也是mir2丰富地图效果的手段之一,,而不仅仅是一种容错的技术),玩过mir2的地图编辑器都会知道,tiles大小一般为96*64,smtiles一般大
小为48*32,,即1tiles=4个smtiles,,前景图就是objects.wil-objects*.wil里面所包含的图片,主要是一些mir2游戏世界中的景观如建筑物等,
客户端的mir.set定义了一些objects的组合,这些组合解释了一些常见的mir2景观(npc屋等,,石柱等,,树木),,它是一个ascii码文件,请自行理
解它的各字段的意义,,它解释了景观用到的objects序列,,跨越多少mappoint

注意到有些objects里面的图片也绘制了地面,如沙巴克门前破碎的石柱佛面像等,此外,一个.map文件不仅定义了用哪些tiles,smtiles,objects
来绘制它,,还包括了其它一些必需的信息,如哪里不可走,,哪里是过门点,这些属性都是用地图编辑器生成一个地图文件后由地图编辑器自动
生成的,,mir2的引擎之地图读取单元就是读取.map文件为程序所用,,,实现的源程序片断如下:

{------------------------------------------------------------------------------}
// 地图常量信息定义
{------------------------------------------------------------------------------}
const

// 屏幕大小 整个游戏世界场景区的大小,,也即DDraw在屏幕上开辟的的最大绘图面积,mir2res使用了ddraw的800*600*8的绘图引擎
  CLIENT_WIDTH = 800;
  CLIENT_HEIGHT = 600;

  // 显示地图的大小 600-445的屏幕部分用于显于道具栏
  MAPSURFACE_WIDTH = 800;
  MAPSURFACE_HEIGHT = 445;

  // 地图单位/背景图片尺寸 //这就是"地图格",,如比奇地图大小为700*700,它的单位是mapunit,,一些NPC屋的地图大小就小多了,,
700*700折合像素单位为 (700*48)*(700*32),,想像一下要走遍比奇,在水平方向上要卷屏700*48/800次,,也即:刚好42屏
  MAPUNIT_WIDTH = 48;
  MAPUNIT_HEIGHT = 32;

  // 地图单位中心象素点
  MAPCENTER_X = (MAPSURFACE_WIDTH - MAPUNIT_WIDTH) div 2;
  MAPCENTER_Y = (MAPSURFACE_HEIGHT - MAPUNIT_HEIGHT) div 2;

  // 逻辑地图单位 (估计是微量屏幕移动的最小值)
  LOGICALMAPUNIT = 20;

  LONGHEIGHT_IMAGE = 35;        // 地图上的任何一个前景图片最大的高度(以 MapPoint 为单位)

即任何一个包含在objects里面的图片高度都不能超过35,即35*32=1120,我们知道,,mappoint=48*32,,所以它超越了整个绘图区的高度600
  
{------------------------------------------------------------------------------}
// 地图文件结构定义
{------------------------------------------------------------------------------}
type
  // 地图文件头结构 (52字节, 注意: 原文件头大小为56字节)
  // 估计 UpdateDate 偏移有误
  PMapHeader = ^TMapHeader;
  TMapHeader = packed record
    Width      : Word;                      // 宽度      2
    Height     : Word;                      // 高度      2
    Title      : string[16];                // 标题      17
    UpdateDate : TDateTime;                 // 更新日期  8
    Reserved   : array[0..22] of Char;      // 保留      23
  end;

  // 地图点数据结构
  PMapPoint = ^TMapPoint;
  TMapPoint = packed record
    BackImg     : Word;     // 背景图片索引(BackImg-1), 图片在 Tile.wil 中
    MiddImg     : Word;     // 背景小图索引(MiddImg-1), 图片在 SmTile.wil 中
    ForeImg     : Word;     // 前景
    DoorIndex   : Byte;     //    $80 (巩娄), 巩狼 侥喊 牢郸胶
    DoorOffset  : Byte;     //    摧腮 巩狼 弊覆狼 惑措 困摹, $80 (凯覆/摧塞(扁夯))
    AniFrame    : Byte;     //    $80(Draw Alpha) +  橇贰烙 荐
    AniTick     : Byte;
    Area        : Byte;     //    瘤开 沥焊
    Light       : Byte;     //    0..1..4 堡盔 瓤苞
  end;

"地图点"是一个重要的概念:我们把tiles,,smtiles构成的地面层称为背景,,把objects构成的地图层称为前景,,地图点的大小为mapunit定义
的像素大小,,,比奇有700*700个地图点数据,,现在来看一下它的结构,,前三个好理解,,doorindex,,dooroffset,,aniframe,,anitick,我们将
在下面的函数过程中求得它的实际意义,,,area为该地图点使用的是哪个objects文件(1.76版的mir2为0~6好像),,light,,天气文件,,lig0a~f

type

{------------------------------------------------------------------------------}
// TMirMap class
{------------------------------------------------------------------------------}
  TMirMap = class(TObject)
  private
    FFileName: string;
    FFileHandle: THandle;     // WIN32 文件句柄 指向硬盘文件
    FFileMapping: THandle;    // 内存映射文件句柄 指向内存镜像文件,,也称内存"图像"文件,,这里的图像并非指图片
    FFilePointer: Pointer;    // 内存映射指针
    FHeight: Word;
    FWidth: Word;
    FTitle: string;
    FUpdateDate: TDateTime;

{    FCenterX: Integer;
    FCenterY: Integer;
    FShiftX: Integer; //我们分析过actor,,谈到shiftx,,,它就在这里发挥作用
    FShiftY: Integer;}
    FClientWidth: Integer;
    FClientHeight: Integer;

    procedure SetFileName(const value: string);
    function GetPoint(X, Y: Word): PMapPoint;
  protected

  public
    AniTick: Cardinal;
    AniCount: Integer;
   
    constructor Create(AClientWidth, AClientHeight: Integer);

    destructor Destroy; override;

    function CanMove(X, Y: Word): Boolean;

    function CanFly(X, Y: Word): Boolean;

    procedure BitBlt(DC: HDC; X, Y, AWidth, AHeight: Word);

    procedure DrawBackground(Surface: IDirectDrawSurface7;
      CenterX, CenterY, ShiftX, ShiftY: Integer);

    procedure DrawForeground(Surface: IDirectDrawSurface7;
      CenterX, CenterY, ShiftX, ShiftY: Integer; FirstStep: Boolean);

    // 地图文件名, 指定为空串将关闭地图
    property FileName: string read FFileName write SetFileName;

    // 地图宽度
    property Width: Word read FWidth; //注意delphi的这个机制
    // 地图高度
    property Height: Word read FHeight;
    // 指定地图点的信息 (返回为 TMapPoint 指针, 直接指向地图文件)
    property Point[X, Y: Word]: PMapPoint read GetPoint;

    // 地图标题
    property Title: string read FTitle;
    // 地图更新日期(可能有误)
    property UpdateDate: TDateTime read FUpdateDate;
  end;


{ TMirMap }
implementation

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 (小图用于填补一些大图画不到的边缘)
      ImageIndex := Pt.MiddImg;
      if ImageIndex > 0 then  
        G_WilTileSm.BitBlt(ImageIndex - 1, DC, (I-X) * 48, (J-Y) * 32);
    end;
  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');

        G_WilObjects[Pt.Area].BitBlt(ImageIndex - 1, DC, (I-X) * 48, (J-Y) * 32);
      end;
    end;
  end;
end;

constructor TMirMap.Create(AClientWidth, AClientHeight: Integer); //creat是一个对象的默认初始工作过程
begin
  FClientWidth := AClientWidth;
  FClientHeight := AClientHeight;
end;

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;

  // 如果文件名为空则退出
  if value = '' then Exit;
  
  // 创建文件句柄
  FFileHandle := CreateFile(PChar(value), GENERIC_READ, FILE_SHARE_READ, nil,
  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_RANDOM_ACCESS, 0);

  if FFileHandle = INVALID_HANDLE_value then
    raise Exception.CreateFmt('打开 "%s" 失败!', [value]);

  // 创建文件映射
  FFileMapping := CreateFileMapping(FFileHandle, nil, PAGE_READONLY, 0, 0, nil);

  if FFileMapping = 0 then
  begin
    CloseHandle(FFileHandle);
    raise Exception.CreateFmt('创建文件映射 "%s" 失败!', [value]);
  end;

  // 进行文件映射
  FFilePointer := MapViewOfFile(FFileMapping, FILE_MAP_READ, 0, 0, 0);

  if FFilePointer = nil then
  begin
    CloseHandle(FFileMapping);
    CloseHandle(FFileHandle);
    raise Exception.CreateFmt('映射文件 "%s" 失败!', [value]);
  end;

  // 读出地图头信息
  FWidth := PMapHeader(FFilePointer)^.Width;
  FHeight := PMapHeader(FFilePointer)^.Height;
  FTitle := PMapHeader(FFilePointer)^.Title;
  FUpdateDate := PMapHeader(FFilePointer)^.UpdateDate;

  // 保存地图文件名
  FFileName := value;  
end;

//从setfilename()函数中我们可以提取到:一个.map文件映射入内存的3个步骤前二个步骤是建立文件指针,,第三个步骤也就是最终步骤是实际的映射过程,,,至此,,一个硬盘中的.map文件被映射入内存为程序暂时所用,,,直到下一次的setfilename()下一个.map文件映射入内存

所以释放.map文件的过程也应该先释放二个指针再来一次实际映射的逆过程并且这三个过程跟建立时的顺序是完全颠倒的.

当然在setfilename()中还有一些附加处理过程如保存地图文件名,,读取地图头信息为上下程序所用

procedure TMirMap.DrawBackground(Surface: IDirectDrawSurface7; //画背景即画tiles和smtiles...
  CenterX, CenterY, ShiftX, ShiftY: Integer);
var
  MapRect: TRect;               // 需要绘制的 MAP 坐标范围
  OffsetX, OffsetY: Integer;    // X, Y 左上角偏移
  AdjustX, AdjustY: Integer;    // 在绘背景时是否需要调整最左/上行(由于BkImg以偶行/列方式绘制)
  I, J: Integer;
  Pt: PMapPoint;
  ImageIndex: Word;
begin
  // 自地图中间至最左/最上的宽度(象素)
  I := (FClientWidth - MAPUNIT_WIDTH) div 2 - ShiftX;
  J := (FClientHeight - MAPUNIT_HEIGHT) div 2 - ShiftY;

  // 计算需要绘制的地图点范围(TMapPoint)
  MapRect.Left := Max(0, CenterX - Ceil(I / MAPUNIT_WIDTH));
  MapRect.Right := Min(FWidth, CenterX + Ceil((FClientWidth - I) / MAPUNIT_WIDTH));
  MapRect.Top := Max(0, CenterY - Ceil(J / MAPUNIT_HEIGHT));
  MapRect.Bottom := Min(FHeight, CenterY + Ceil((FClientHeight - J) / MAPUNIT_HEIGHT));

//  MapRect.Left := MapRect.Left - MapRect.Left mod 2;
//  MapRect.Top := MapRect.Top - MapRect.Top mod 2;

  // 计算开始绘制时的偏移值(象素)
  OffsetX := I - (CenterX - MapRect.Left) * MAPUNIT_WIDTH;
  OffsetY := J - (CenterY - MapRect.Top) * MAPUNIT_HEIGHT;

  // 绘制背景 (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;

  // 计算需要绘制的地图点范围(TMapPoint)
  MapRect.Left := Max(0, CenterX - Ceil(I / MAPUNIT_WIDTH));
  MapRect.Right := Min(FWidth, CenterX + Ceil((FClientWidth - I) / MAPUNIT_WIDTH));
  MapRect.Top := Max(0, CenterY - Ceil(J / MAPUNIT_HEIGHT));
  MapRect.Bottom := Min(FHeight, CenterY + Ceil((FClientHeight - J) / MAPUNIT_HEIGHT) + LONGHEIGHT_IMAGE);

  // 计算开始绘制时的偏移值(象素)
  OffsetX := I - (CenterX - MapRect.Left) * MAPUNIT_WIDTH;
  OffsetY := J - (CenterY - MapRect.Top) * MAPUNIT_HEIGHT;

  // 绘制前景 (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');

      InfoPtr := G_WilObjects[Pt.Area].ImageInfo[ImageIndex - 1];

      // 如果图片尺寸=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;

法律声明:
1. 本站所有资源和素材来源于网络收集,如有侵权内容请联系站长删除处理!
2. 本站大部分下载资源收集于网络,版权归原作者所有,如有侵权请联系删除。
3. 本站提供的素材、源码、等等其他资源,都不包含技术服务支持!
4. 若因线路和服务器问题非本站所能控制范围的故障导致暂停服务期间造成的一切不便与损失,论坛不负任何责任。
5. 注册会员通过任何手段和方法针对论坛进行破坏,我们有权对其行为作出处理。并保留进一步追究其责任的权利。
收藏收藏 分享分享 分享淘帖 支持支持 反对反对
沙发
发表于 2023-4-1 17:49:35 | 只看该作者
4155445454545
懒得打字嘛,点击右侧快捷回复 【乱回复纯数字纯字母将禁言】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

必备工具

地图查看分析转换三合一

目前地图转换 单纯的OB真彩地图转换编...号想必很多人都会, 也有很多工具

南宫地图编辑器v5.0

使用方法,放置传奇客户端DATA文件夹...

Tiles转换工具,支持支持255

支持255地图转换工具!

关于本站

玩传奇论坛 - 专业提供游戏素材、游戏工具、传奇单机、传奇服务端,致力于打造最精品的版本素材分享平台!

Archiver|小黑屋|玩传奇吉ICP备18002996号-1) |网站地图 

GMT+8, 2024-11-26 14:39 , Processed in 0.041589 second(s), 34 queries , Gzip On. 

 Powered by Discuz! X3.2

 © 2001-2013 Comsenz Inc.

返回顶部