⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 datafile.cs

📁 windows mobile上开发的推箱子小游戏,用C#写的,虽然只是DEMO版但大致功能已经实现
💻 CS
📖 第 1 页 / 共 2 页
字号:
using System;
 using System.IO;
 using System.Drawing;
 using System.Collections.Generic;
 using System.Windows.Forms;
 
 namespace Skyiv.Ben.PushBox.Common
 {
   // data/<group>.bxb 文件格式
   // 保留 ver(2) BOX 组名- 总关数 第1关起始地址位置
   // 0--3 4----- 5-7 8--23 24--27 28-------------31
   //
   // @ Flag 总步数 推箱子步数 保留- wide- high- data
   // 0 1--- 2----5 6--------9 10-23 24-27 28-31 32..
   // Flag: 最低位: 0:未通关 1:已通关
   //
   // 第1关起始地址 第2关起始地址 . 最后一关起始地址
   // 0-----------3 4-----------7 . (文件最后四字节)
   //
   // steps/<group><level>.bxs 文件格式见 Step.cs
   // 其中<level>为关数(1起始),最少四位,不足前补零
   //
   // text/<group>.bxa 文件格式
   // 0 - land             SPACE
   // 1 + slot             .
   // 2 # wall             #
   // 3 % brick            N/A
   // 4 x box on land      $
   // 5 X box on slot      *
   // 6 ( man on land      @
   // 7 ) man on slot      +  .XSB 文件格式
   // 第一行如果以!开头的话, 则为组名(不能超过16个字符)
   // 以:开头的行为通关步骤, 格式同(.bxs)文件
   // 以'开头的行为注释, 完全忽略
   // 各关之间必须以空行分隔
 
  /// <summary>
   /// 管理数据文件: *.bxb  *.bxa  *.bxs
   /// </summary>
   sealed class DataFile : IDisposable
   {
     const byte DataVersion = 2;       // 数据文件(.bxb)的版本
     const byte LevelFlag = (byte)'@'; // 数据文件(.bxb)的关标志
     const char RemChar = '\'';        // 文本文件(.bxa)的注释
     const char StepsChar = ':';       // 文本文件(.bxa)的通关步骤
 
     FileStream fs;    // 数据文件基础流
     BinaryReader br;  // 数据文件读取器
     BinaryWriter bw;  // 数据文件写入器
     string groupName; // 当前组名称
     int[] addrs;      // 各关起始地址列表,最后一项为第1关起始地址位置
     byte[,] map;      // 当前关地图
     int maxLevel;     // 总关数
     Size levelSize;   // 当前关尺寸(以单元格为单位)
     Point worker;     // 当前工人位置(以单元格为单位)
     int mans;         // 工人数
     int boxs;         // 箱子数
     int slots;        // 槽数
     int tasks;        // 总任务数
     int boths;        // 已完成任务数
     bool isFinished;  // 是否曾经通关
     int movedSteps;   // 通关的总步数
     int pushedSteps;  // 通关的推箱子步数
     string fileName { get { return Path.GetFileNameWithoutExtension(fs.Name); } } // 数据文件主名
 
     public string GroupName { get { return groupName; } }
     public int MaxLevel { get { return maxLevel; } }
     public byte[,] Map { get { return map; } }
     public Size LevelSize { get { return levelSize; } }
     public bool IsFinished { get { return isFinished; } }
     public int MovedSteps { get { return movedSteps; } }
     public int PushedSteps { get { return pushedSteps; } }
     public Point Worker { get { return worker; } }
     public bool HasWorker { get { return mans != 0; } }
     public int Boxs { get { return boxs; } }
     public int Slots { get { return slots; } }
     public int Tasks { get { return tasks; } }
     public int Boths { get { return boths; } set { boths = value; } }
 
     /// <summary>
     /// 装入组数据
     /// </summary>
     /// <param name="name">组文件名</param>
     public void LoadGroup(string name)
     {
       Dispose();
       fs = new FileStream(Path.Combine(Pub.DataDirectory, name + Pub.DataExtName), FileMode.Open);
       br = new BinaryReader(fs, Pub.Encode);
       bw = new BinaryWriter(fs, Pub.Encode);
       br.ReadInt32(); // 保留
       if (br.ReadByte() != DataVersion) throw new Exception("数据文件版本错");
       byte[] bs = br.ReadBytes(3); // 数据文件标志:BOX
       for (int i = 0; i < bs.Length; i++) if (bs[i] != "BOX"[i]) throw new Exception("数据文件标志错");
       bs = br.ReadBytes(16); // 组名
       for (int i = 0; i < bs.Length; i++) if (bs[i] == 0) bs[i] = 32;
       groupName = Pub.Encode.GetString(bs, 0, bs.Length).Trim();
       if (groupName.Length == 0) groupName = fileName; // 如果数据文件中组名为空,则用数据文件主名代替
       maxLevel = br.ReadInt32(); // 总关数
       int addrPos = br.ReadInt32(); // 第1关起始地址位置
       br.BaseStream.Seek(addrPos, SeekOrigin.Begin);
       addrs = new int[maxLevel + 1]; // 各关起始地址列表,最后一项为第1关起始地址位置
       for (int i = 0; i < maxLevel; i++) addrs[i] = br.ReadInt32();
       addrs[maxLevel] = addrPos; // 第1关起始地址位置
       if (addrPos + 4 * maxLevel != br.BaseStream.Length) throw new Exception("数据文件地址表必须位于数据最后");
     }
 
     /// <summary>
     /// 装入关数据
     /// </summary>
     /// <param name="level">关数</param>
     public void LoadLevel(int level)
     {
       LoadLevelHead(level);
       InitMap();
       for (int i = 1; i <= levelSize.Height; i++)
       {
         for (int j = 1; j <= levelSize.Width; j++)
         {
           map[i, j] = br.ReadByte();
           UpdateCounts(j, i, true);
         }
       }
       if (mans != 1) throw new Exception("读取关数据失败:必须刚好有一个工人");
       tasks = Math.Min(boxs, slots);
     }
 
     /// <summary>
     /// 新建一关
     /// </summary>
     /// <param name="isCopy">是否复制当前关</param>
     /// <param name="size">新建关的尺寸</param>
     public void NewLevel(bool isCopy, Size size)
     {
       Size levelSizeOem = levelSize;
       byte[,] mapOem = isCopy ? (byte[,])map.Clone() : null;
       levelSize = size;
       InitMap();
       for (int i = 1; i <= levelSize.Height; i++)
       {
         for (int j = 1; j <= levelSize.Width; j++)
         {
           map[i, j] = (isCopy && i <= levelSizeOem.Height && j <= levelSizeOem.Width) ? mapOem[i, j] : Block.Land;
           UpdateCounts(j, i, true);
         }
       }
       if (mans != 1 && mans != 0) throw new Exception("不能超过一个工人");
       tasks = Math.Min(boxs, slots);
     }
 
     /// <summary>
     /// 初始化地图
     /// </summary>
     private void InitMap()
     {
       map = new byte[levelSize.Height + 2, levelSize.Width + 2];
       for (int i = 0; i <= levelSize.Height + 1; i++) map[i, 0] = map[i, levelSize.Width + 1] = Block.Wall;
       for (int j = 0; j <= levelSize.Width + 1; j++) map[0, j] = map[levelSize.Height + 1, j] = Block.Wall;
       mans = boxs = slots = boths = 0;
     }
 
     /// <summary>
     /// 根据地图项目更新统计信息
     /// </summary>
     /// <param name="x">当前位置横坐标</param>
     /// <param name="y">当前位置纵坐标</param>
     /// <param name="isAdd">加或减</param>
     public void UpdateCounts(int x, int y, bool isAdd)
     {
       int sign = isAdd ? 1 : -1;
       if (Block.IsBox(map[y, x])) boxs += sign;
       if (Block.IsSlot(map[y, x])) slots += sign;
       if (Block.Box1 == map[y, x]) boths += sign;
       if (Block.IsMan(map[y, x]))
       {
         mans += sign;
         worker = isAdd ? new Point(x, y) : Point.Empty;
       }
178     }
 
     /// <summary>
     /// 装入关数据头
     /// </summary>
     /// <param name="level">关数</param>
     void LoadLevelHead(int level)
     {
       if (level > maxLevel - 1) throw new Exception(string.Format("当前关数({0})不能大于总关数({1})", level + 1, maxLevel));
       br.BaseStream.Seek(addrs[level], SeekOrigin.Begin);
       if (br.ReadByte() != LevelFlag) throw new Exception("关数据标志错");
       isFinished = (br.ReadByte() & 1) == 1; // 是否曾经通关
       movedSteps = br.ReadInt32(); // 通关的总步数
       pushedSteps = br.ReadInt32(); // 通关的推箱子步数
       br.ReadBytes(14); // 保留
       levelSize.Width = br.ReadInt32();
       levelSize.Height = br.ReadInt32();
     }
 
     /// <summary>
     /// 更新当前关数据
     /// </summary>
     /// <param name="level">关数</param>
     /// <param name="steps">通关步骤</param>
     /// <param name="pushs">推箱子步数</param>
     public void SaveLevel(int level, Step[] steps, int pushs)
     {
       SaveLevelHead(level, steps.Length, pushs);
       SaveLevelSteps(level, Pub.ToString(steps));
       LoadLevelHead(level);
     }
 
     /// <summary>
     /// 更新当前关头数据
     /// </summary>
     /// <param name="level">关数</param>
     /// <param name="moves">通关步数</param>
     /// <param name="pushs">推箱子步数</param>
     void SaveLevelHead(int level, int moves, int pushs)
     {
       if (level > maxLevel - 1) throw new Exception("关数太大");
       bw.BaseStream.Seek(addrs[level] + 1, SeekOrigin.Begin);
       bw.Write((byte)1); // 是否曾经通关
       bw.Write(moves); // 通关的总步数
       bw.Write(pushs); // 通关的推箱子步数
     }
 
     /// <summary>
     /// 保存通关步骤
     /// </summary>
     /// <param name="level">关数</param>
     /// <param name="steps">通关步骤</param>
     void SaveLevelSteps(int level, string steps)
     {
       if (!Directory.Exists(Pub.StepsDirectory)) Directory.CreateDirectory(Pub.StepsDirectory);
       Fcl.WriteAllText(GetStepsFileName(fileName, level), steps);
     }
 
     /// <summary>
     /// 给出通关步骤
     /// </summary>
     /// <param name="level">关数</param>
     /// <returns>通关步骤</returns>
     public string GetSteps(int level)
     {
       return GetSteps(fileName, level);
     }
 
     string GetSteps(string name, int level)
     {
       return Fcl.ReadAllText(GetStepsFileName(name, level));
     }
 
     string GetStepsFileName(string name, int level)
     {
       return Path.Combine(Pub.StepsDirectory, name + (level + 1).ToString("D4") + Pub.StepsExtName);
     }
 
     /// <summary>
     ///  删除通关步骤文件
     /// </summary>
     /// <param name="level">关数</param>
     private void DeleteStepsFile(int level)
     {
       // 虽然 File.Delete(): 删除指定的文件。如果指定的文件不存在,则不引发异常。 
       // 但是: 如果指定的路径无效,还是会引发 DirectoryNotFoundException 异常。
       // 所以需要先用 File.Exists() 判断一下文件是否存在
       string name = GetStepsFileName(fileName, level);
       if (File.Exists(name)) File.Delete(name);
     }
 
     /// <summary>
     /// 保存设计数据

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -