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

📄 datafile.cs

📁 手机软件开发..手机软件开发..手机软件开发..手机软件开发..
💻 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;
      }
    }

    /// <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>
    /// 保存设计数据
    /// </summary>

⌨️ 快捷键说明

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