在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
上图是数据文件 konka.bxb 的结构图。该数据文件大小为 297 字节,包含三个关卡,各个关卡的大小分别为:“8x7”、“8x7”和“9x5”。内容如下: 1. 文件头(32字节,图中的青色部分)。首先是保留的四个字节。然后是一个字节的数据文件版本号(目前为“2”)。接着是三个字节的标志(内容为“BOX”)。接着是十六字节的组名(编码为“GB2312”,本数据文件中为“康佳”)。接着是总关数(Int32,四个字节,因为本组共有三个关卡,所以内容为“3”)。最后是第一关起始地址位置(Int32,四个字节,本数据文件中的内容为“0x11D”)。 2. 以关数据头(32字节,图中的绿色部分)开始的各关数据。首先是一个字节的关数据头开始标志(“@”)。然后是一个字节的标志(最低位:0:未通关,1:已通关)。接着是通关总步数(Int32,四个字节)。接着是通关推箱子步数(Int32,四个字节)。接着是十四个字节的保留字段。接着是本关的宽度(Int32,四个字节)。接着是本关的高度(Int32,四个字节)。最后是“本关宽度x本关高度”个字节的关数据,也就是说地图中每个单元格占一个字节,取值范围是“0”到“7”,分别表示:0:地,1:槽,2:墙,3:砖,4:箱子放在地上,5:箱子放在槽上,6:工人站在地上,7:工人站在槽上。注意,每一个关卡必须刚好有一个工人。 3. 数据文件的最后是各关起始地址列表(“总关数x4”个字节,图中的黄色部分)。每关的起始地址均为四个字节(Int32),所以共有“总关数x4”个字节。 密封类 DataFile 的源代码中有详细的注释,很容易看懂。 1. InitMap 方法用来初始化地图。地图的大小是“(关的高度+2) x (关的宽度+2)”,这是为了在地图四周砌上围墙,以免搜索算法越出地图边界。 2. DeleteLevel 方法用来删除指定的关。注意,删除关时并不删除关数据,只是将该关的起始地址从各关地址列表中删除,然后将文件缩短四个字节(因为各关地址列表在数据文件的最后)。这样数据文件中就可能包含不需要的冗余数据。通过“菜单 -> 数据 -> 转换”,先“导出”,然后“导入”,可以消除冗余数据。
1 using System; 2 using System.IO; 3 using System.Drawing; 4 using System.Collections.Generic; 5 using System.Windows.Forms; 6 7 namespace Skyiv.Ben.PushBox.Common 8 { 9 // data/<group>.bxb 文件格式 10 // 保留 ver(2) BOX 组名- 总关数 第1关起始地址位置 11 // 0--3 4----- 5-7 8--23 24--27 28-------------31 12 // 13 // @ Flag 总步数 推箱子步数 保留- wide- high- data 14 // 0 1--- 2----5 6--------9 10-23 24-27 28-31 32.. 15 // Flag: 最低位: 0:未通关 1:已通关 16 // 17 // 第1关起始地址 第2关起始地址 . 最后一关起始地址 18 // 0-----------3 4-----------7 . (文件最后四字节) 19 // 20 // steps/<group><level>.bxs 文件格式见 Step.cs 21 // 其中<level>为关数(1起始),最少四位,不足前补零 22 // 23 // text/<group>.bxa 文件格式 24 // 0 - land SPACE 25 // 1 + slot . 26 // 2 # wall # 27 // 3 % brick N/A 28 // 4 x box on land $ 29 // 5 X box on slot * 30 // 6 ( man on land @ 31 // 7 ) man on slot + .XSB 文件格式 32 // 第一行如果以!开头的话, 则为组名(不能超过16个字符) 33 // 以:开头的行为通关步骤, 格式同(.bxs)文件 34 // 以'开头的行为注释, 完全忽略 35 // 各关之间必须以空行分隔 36 37 /// <summary> 38 /// 管理数据文件: *.bxb *.bxa *.bxs 39 /// </summary> 40 sealed class DataFile : IDisposable 41 { 42 const byte DataVersion = 2; // 数据文件(.bxb)的版本 43 const byte LevelFlag = (byte)'@'; // 数据文件(.bxb)的关标志 44 const char RemChar = '\''; // 文本文件(.bxa)的注释 45 const char StepsChar = ':'; // 文本文件(.bxa)的通关步骤 46 47 FileStream fs; // 数据文件基础流 48 BinaryReader br; // 数据文件读取器 49 BinaryWriter bw; // 数据文件写入器 50 string groupName; // 当前组名称 51 int[] addrs; // 各关起始地址列表,最后一项为第1关起始地址位置 52 byte[,] map; // 当前关地图 53 int maxLevel; // 总关数 54 Size levelSize; // 当前关尺寸(以单元格为单位) 55 Point worker; // 当前工人位置(以单元格为单位) 56 int mans; // 工人数 57 int boxs; // 箱子数 58 int slots; // 槽数 59 int tasks; // 总任务数 60 int boths; // 已完成任务数 61 bool isFinished; // 是否曾经通关 62 int movedSteps; // 通关的总步数 63 int pushedSteps; // 通关的推箱子步数 64 string fileName { get { return Path.GetFileNameWithoutExtension(fs.Name); } } // 数据文件主名 65 66 public string GroupName { get { return groupName; } } 67 public int MaxLevel { get { return maxLevel; } } 68 public byte[,] Map { get { return map; } } 69 public Size LevelSize { get { return levelSize; } } 70 public bool IsFinished { get { return isFinished; } } 71 public int MovedSteps { get { return movedSteps; } } 72 public int PushedSteps { get { return pushedSteps; } } 73 public Point Worker { get { return worker; } } 74 public bool HasWorker { get { return mans != 0; } } 75 public int Boxs { get { return boxs; } } 76 public int Slots { get { return slots; } } 77 public int Tasks { get { return tasks; } } 78 public int Boths { get { return boths; } set { boths = value; } } 79 80 /// <summary> 81 /// 装入组数据 82 /// </summary> 83 /// <param name="name">组文件名</param> 84 public void LoadGroup(string name) 85 { 86 Dispose(); 87 fs = new FileStream(Path.Combine(Pub.DataDirectory, name + Pub.DataExtName), FileMode.Open); 88 br = new BinaryReader(fs, Pub.Encode); 89 bw = new BinaryWriter(fs, Pub.Encode); 90 br.ReadInt32(); // 保留 91 if (br.ReadByte() != DataVersion) throw new Exception("数据文件版本错"); 92 byte[] bs = br.ReadBytes(3); // 数据文件标志:BOX 93 for (int i = 0; i < bs.Length; i++) if (bs[i] != "BOX"[i]) throw new Exception("数据文件标志错"); 94 bs = br.ReadBytes(16); // 组名 95 for (int i = 0; i < bs.Length; i++) if (bs[i] == 0) bs[i] = 32; 96 groupName = Pub.Encode.GetString(bs, 0, bs.Length).Trim(); 97 if (groupName.Length == 0) groupName = fileName; // 如果数据文件中组名为空,则用数据文件主名代替 98 maxLevel = br.ReadInt32(); // 总关数 99 int addrPos = br.ReadInt32(); // 第1关起始地址位置 100 br.BaseStream.Seek(addrPos, SeekOrigin.Begin); 101 addrs = new int[maxLevel + 1]; // 各关起始地址列表,最后一项为第1关起始地址位置 102 for (int i = 0; i < maxLevel; i++) addrs[i] = br.ReadInt32(); 103 addrs[maxLevel] = addrPos; // 第1关起始地址位置 104 if (addrPos + 4 * maxLevel != br.BaseStream.Length) throw new Exception("数据文件地址表必须位于数据最后"); 105 } 106 107 /// <summary> 108 /// 装入关数据 109 /// </summary> 110 /// <param name="level">关数</param> 111 public void LoadLevel(int level) 112 { 113 LoadLevelHead(level); 114 InitMap(); 115 for (int i = 1; i <= levelSize.Height; i++) 116 { 117 for (int j = 1; j <= levelSize.Width; j++) 118 { 119 map[i, j] = br.ReadByte(); 120 UpdateCounts(j, i, true); 121 } 122 } 123 if (mans != 1) throw new Exception("读取关数据失败:必须刚好有一个工人"); 124 tasks = Math.Min(boxs, slots); 125 } 126 127 /// <summary> 128 /// 新建一关 129 /// </summary> 130 /// <param name="isCopy">是否复制当前关</param> 131 /// <param name="size">新建关的尺寸</param> 132 public void NewLevel(bool isCopy, Size size) 133 { 134 Size levelSizeOem = levelSize; 135 byte[,] mapOem = isCopy ? (byte[,])map.Clone() : null; 136 levelSize = size; 137 InitMap(); 138 for (int i = 1; i <= levelSize.Height; i++) 139 { 140 for (int j = 1; j <= levelSize.Width; j++) 141 { 142 map[i, j] = (isCopy && i <= levelSizeOem.Height && j <= levelSizeOem.Width) ? mapOem[i, j] : Block.Land; 143 UpdateCounts(j, i, true); 144 } 145 } 146 if (mans != 1 && mans != 0) throw new Exception("不能超过一个工人"); 147 tasks = Math.Min(boxs, slots); 148 } 149 150 /// <summary> 151 /// 初始化地图 152 /// </summary> 153 private void InitMap() 154 { 155 map = new byte[levelSize.Height + 2, levelSize.Width + 2]; 156 for (int i = 0; i <= levelSize.Height + 1; i++) map[i, 0] = map[i, levelSize.Width + 1] = Block.Wall; 157 for (int j = 0; j <= levelSize.Width + 1; j++) map[0, j] = map[levelSize.Height + 1, j] = Block.Wall; 158 mans = boxs = slots = boths = 0; 159 } 160 161 /// <summary> 162 /// 根据地图项目更新统计信息 163 /// </summary> 164 /// <param name="x">当前位置横坐标</param> 165 /// <param name="y">当前位置纵坐标</param> 166 /// <param name="isAdd">加或减</param> |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论