依星源码资源网,依星资源网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 22|回复: 0

[游戏架设教程] 传奇代码完全解析(5)

[复制链接] 主动推送

7779

主题

7831

帖子

8710

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
8710
发表于 2024-6-11 11:12:16 | 显示全部楼层 |阅读模式
传奇代码完全解析(5)
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;

end.


相关帖子

扫码关注微信公众号,及时获取最新资源信息!下载附件优惠VIP会员5折;永久VIP免费
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

免责声明:
1、本站提供的所有资源仅供参考学习使用,版权归原著所有,禁止下载本站资源参与商业和非法行为,请在24小时之内自行删除!
2、本站所有内容均由互联网收集整理、网友上传,并且以计算机技术研究交流为目的,仅供大家参考、学习,请勿任何商业目的与商业用途。
3、若您需要商业运营或用于其他商业活动,请您购买正版授权并合法使用。
4、论坛的所有内容都不保证其准确性,完整性,有效性,由于源码具有复制性,一经售出,概不退换。阅读本站内容因误导等因素而造成的损失本站不承担连带责任。
5、用户使用本网站必须遵守适用的法律法规,对于用户违法使用本站非法运营而引起的一切责任,由用户自行承担
6、本站所有资源来自互联网转载,版权归原著所有,用户访问和使用本站的条件是必须接受本站“免责声明”,如果不遵守,请勿访问或使用本网站
7、本站使用者因为违反本声明的规定而触犯中华人民共和国法律的,一切后果自己负责,本站不承担任何责任。
8、凡以任何方式登陆本网站或直接、间接使用本网站资料者,视为自愿接受本网站声明的约束。
9、本站以《2013 中华人民共和国计算机软件保护条例》第二章 “软件著作权” 第十七条为原则:为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。若有学员需要商用本站资源,请务必联系版权方购买正版授权!
10、本网站如无意中侵犯了某个企业或个人的知识产权,请来信【站长信箱312337667@qq.com】告之,本站将立即删除。
郑重声明:
本站所有资源仅供用户本地电脑学习源代码的内含设计思想和原理,禁止任何其他用途!
本站所有资源、教程来自互联网转载,仅供学习交流,不得商业运营资源,不确保资源完整性,图片和资源仅供参考,不提供任何技术服务。
本站资源仅供本地编辑研究学习参考,禁止未经资源商正版授权参与任何商业行为,违法行为!如需商业请购买各资源商正版授权
本站仅收集资源,提供用户自学研究使用,本站不存在私自接受协助用户架设游戏或资源,非法运营资源行为。
 
在线客服
点击这里给我发消息 点击这里给我发消息 点击这里给我发消息
售前咨询热线
312337667

微信扫一扫,私享最新原创实用干货

QQ|免责声明|依星源码资源网 ( 鲁ICP备2021043233号-3 )|网站地图

GMT+8, 2024-6-29 12:55

Powered by Net188.com X3.4

邮箱:312337667@qq.com 客服QQ:312337667(工作时间:9:00~21:00)

快速回复 返回顶部 返回列表