这是“使用 C# 开发智能手机软件:推箱子”系列文章的第十一篇。在这篇文章中,介绍 Common/Env.cs 源程序文件。这个源程序文件中包含表示“工作环境”的密封类 Env 。也就是说,主程序中重要的变量都封装在这个类中,作为整个程序的“工作环境”。她还起着桥梁作用,其中两个字段:
正是我们以前介绍过的管理数据文件的密封类 DataFile 和管理配置文件的密封类 ConfigFile 的实例。密封类 Env 中的不少属性和方法是通过这两个字段调用其各自的属性和方法。
下面对密封类 Env 中的一些方法作点说明:
GetClientSize 方法用来计算当使用标准箱子尺寸时主窗体客户区的尺寸。该方法仅当程序运行在计算机上时才会被调用,使主窗体的尺寸根据当前关的尺寸自动调整。程序运行在智能手机时是不会被调用的,因为在智能手机上本程序并不改变主窗体的尺寸。
SetBoxInfo 方法的作用是根据客户区尺寸计算箱子的尺寸,并相应设定要显示的图形单元。图形单元共有 24x24、20x20、16x16 和 12x12 四种尺寸,如下所示:


 
  

如果使用 12x12 的图形单元还不能在客户区完整显示地图的话,可能这一关的游戏就无法玩了。
Draw 方法用来更新主窗体客户区,也就是在主窗体客户区画出本关的地图,并根据玩家的动作随时更新地图。
Design 方法实现在设计模式下,当鼠标点击时要采取的动作。
StepIt 方法实现工人往指定方向前进一步(可能推着箱子)。
Back 方法实现工人后退一步(可能连带箱子一起后退)。
GetMoveInfo 方法寻找一条将工人移动到鼠标点击的位置的路线。她调用我们以前介绍过的静态类 FindPath 的 Seek 方法来寻找最短路线。
GetPushInfo 方法给出将箱子推动到鼠标点击的位置所需的信息。
到此为止,Common 目录下所有源程序文件都介绍完了,这些源程序文件中包含的类是实现整个程序功能的基础。在随后的文章中将介绍 Windows 目录下的源程序文件,她们包含的类实现整个程序的用户界面。
下面就是密封类 Env 的源程序代码,虽然稍微长了一点,但是里面的注释比较详细,应该不难理解。
上一篇:使用 C# 开发智能手机软件:推箱子(十)
下一篇:使用 C# 开发智能手机软件:推箱子(十二)
返回目录
正是我们以前介绍过的管理数据文件的密封类 DataFile 和管理配置文件的密封类 ConfigFile 的实例。密封类 Env 中的不少属性和方法是通过这两个字段调用其各自的属性和方法。
下面对密封类 Env 中的一些方法作点说明:
GetClientSize 方法用来计算当使用标准箱子尺寸时主窗体客户区的尺寸。该方法仅当程序运行在计算机上时才会被调用,使主窗体的尺寸根据当前关的尺寸自动调整。程序运行在智能手机时是不会被调用的,因为在智能手机上本程序并不改变主窗体的尺寸。
SetBoxInfo 方法的作用是根据客户区尺寸计算箱子的尺寸,并相应设定要显示的图形单元。图形单元共有 24x24、20x20、16x16 和 12x12 四种尺寸,如下所示:


 
  
如果使用 12x12 的图形单元还不能在客户区完整显示地图的话,可能这一关的游戏就无法玩了。
Draw 方法用来更新主窗体客户区,也就是在主窗体客户区画出本关的地图,并根据玩家的动作随时更新地图。
Design 方法实现在设计模式下,当鼠标点击时要采取的动作。
StepIt 方法实现工人往指定方向前进一步(可能推着箱子)。
Back 方法实现工人后退一步(可能连带箱子一起后退)。
GetMoveInfo 方法寻找一条将工人移动到鼠标点击的位置的路线。她调用我们以前介绍过的静态类 FindPath 的 Seek 方法来寻找最短路线。
GetPushInfo 方法给出将箱子推动到鼠标点击的位置所需的信息。
到此为止,Common 目录下所有源程序文件都介绍完了,这些源程序文件中包含的类是实现整个程序功能的基础。在随后的文章中将介绍 Windows 目录下的源程序文件,她们包含的类实现整个程序的用户界面。
下面就是密封类 Env 的源程序代码,虽然稍微长了一点,但是里面的注释比较详细,应该不难理解。
  1 using System;
using System;
2 using System.Drawing;
using System.Drawing;
3 using System.Collections.Generic;
using System.Collections.Generic;
4
5 namespace Skyiv.Ben.PushBox.Common
namespace Skyiv.Ben.PushBox.Common
6 {
{
7 /// <summary>
  /// <summary>
8 /// 工作环境
  /// 工作环境
9 /// </summary>
  /// </summary>
10 sealed class Env : IDisposable
  sealed class Env : IDisposable
11 {
  {
12 DataFile db;       // 数据文件
    DataFile db;       // 数据文件
13 ConfigFile cfg;    // 配置文件
    ConfigFile cfg;    // 配置文件
14 string errorMsg;   // 错误信息
    string errorMsg;   // 错误信息
15 string debugMsg;   // 调试信息
    string debugMsg;   // 调试信息
16 bool isReplay;     // 是否正在回放
    bool isReplay;     // 是否正在回放
17 Action active;     // 模式: 正常 新建 编辑 删除
    Action active;     // 模式: 正常 新建 编辑 删除
18 byte pen;          // 设计时的笔
    byte pen;          // 设计时的笔
19 Bitmap img;        // 图形单元, 横向被均匀分为八份
    Bitmap img;        // 图形单元, 横向被均匀分为八份
20 Stack<Step> stack; // 历史路线, 用于后退功能
    Stack<Step> stack; // 历史路线, 用于后退功能
21 Size clientSize;   // 工作区域尺寸(以像素为单位)
    Size clientSize;   // 工作区域尺寸(以像素为单位)
22 Size boxSize;      // 图形元素尺寸(以像素为单位)
    Size boxSize;      // 图形元素尺寸(以像素为单位)
23 Point toPixel;     // 将要到达的位置(以像素为单位)
    Point toPixel;     // 将要到达的位置(以像素为单位)
24 Point worker;      // 当前工人位置(以单元格为单位)
    Point worker;      // 当前工人位置(以单元格为单位)
25 int pushSteps;     // 推动着箱子走的步数
    int pushSteps;     // 推动着箱子走的步数
26 int levelOem;      // 原来的关数,仅用于“菜单 -> 数据 -> 设计 -> 新建”放弃后恢复现场
    int levelOem;      // 原来的关数,仅用于“菜单 -> 数据 -> 设计 -> 新建”放弃后恢复现场
27
28 public string ErrorMsg { get { return errorMsg; } }
    public string ErrorMsg { get { return errorMsg; } }
29 public string DebugMsg { get { return debugMsg; } }
    public string DebugMsg { get { return debugMsg; } }
30 public string[] Groups { get { return cfg.Groups; } }
    public string[] Groups { get { return cfg.Groups; } }
31 public int Group { get { return cfg.Group; } set { cfg.Group = value; } }
    public int Group { get { return cfg.Group; } set { cfg.Group = value; } }
32 public int Level { get { return cfg.Levels[Group]; } set { cfg.Levels[Group] = value; } }
    public int Level { get { return cfg.Levels[Group]; } set { cfg.Levels[Group] = value; } }
33 public int LeveLOem { get { return levelOem; } }
    public int LeveLOem { get { return levelOem; } }
34 public int MaxLevel { get { return db.MaxLevel; } }
    public int MaxLevel { get { return db.MaxLevel; } }
35 public string Steps { get { return cfg.Steps; } }
    public string Steps { get { return cfg.Steps; } }
36 public Size LevelSize { get { return db.LevelSize; } }
    public Size LevelSize { get { return db.LevelSize; } }
37 public Size ClientSize { set { clientSize = value; } }
    public Size ClientSize { set { clientSize = value; } }
38 public Point ToPixel { set { toPixel = value; } }
    public Point ToPixel { set { toPixel = value; } }
39 public int MaxLevelSize { get { return cfg.MaxLevelSize; } set { cfg.MaxLevelSize = value; } }
    public int MaxLevelSize { get { return cfg.MaxLevelSize; } set { cfg.MaxLevelSize = value; } }
40 public int StepDelay { get { return cfg.StepDelay; } set { cfg.StepDelay = value; } }
    public int StepDelay { get { return cfg.StepDelay; } set { cfg.StepDelay = value; } }
41 public int ReplayDelay { get { return cfg.ReplayDelay; } set { cfg.ReplayDelay = value; } }
    public int ReplayDelay { get { return cfg.ReplayDelay; } set { cfg.ReplayDelay = value; } }
42 public Action Active { get { return active; } set { active = value; } }
    public Action Active { get { return active; } set { active = value; } }
43 public byte Pen { get { return pen; } set { pen = value; } }
    public byte Pen { get { return pen; } set { pen = value; } }
44 public bool HasError { get { return !string.IsNullOrEmpty(errorMsg); } }
    public bool HasError { get { return !string.IsNullOrEmpty(errorMsg); } }
45 public bool HasWorker { get { return db.HasWorker; } }
    public bool HasWorker { get { return db.HasWorker; } }
46 public bool CanUndo { get { return stack.Count != 0; } }
    public bool CanUndo { get { return stack.Count != 0; } }
47 public bool CanReplay { get { return db.IsFinished && !CanUndo; } }
    public bool CanReplay { get { return db.IsFinished && !CanUndo; } }
48 public bool IsSave { get { return cfg.IsSave; } set { cfg.IsSave = value; } }
    public bool IsSave { get { return cfg.IsSave; } set { cfg.IsSave = value; } }
49 public bool IsFinish { get { return db.Tasks == db.Boths; } }
    public bool IsFinish { get { return db.Tasks == db.Boths; } }
50 public bool IsReplay { get { return isReplay; } set { isReplay = value; } }
    public bool IsReplay { get { return isReplay; } set { isReplay = value; } }
51 public bool IsDesign { get { return active != Action.None; } }
    public bool IsDesign { get { return active != Action.None; } }
52
53 public Env()
    public Env()
54 {
    {
55 stack = new Stack<Step>();
      stack = new Stack<Step>();
56 cfg = new ConfigFile();
      cfg = new ConfigFile();
57 db = new DataFile();
      db = new DataFile();
58 Init();
      Init();
59 }
    }
60
61 /// <summary>
    /// <summary>
62 /// 状态栏信息
    /// 状态栏信息
63 /// </summary>
    /// </summary>
64 public string StatusMessage
    public string StatusMessage
65 {
    {
66 get
      get
67 {
      {
68 return HasError ? "请点击“菜单 -> 帮助 -> 错误信息”" : string.Format(
        return HasError ? "请点击“菜单 -> 帮助 -> 错误信息”" : string.Format(
69 "{0} {1}/{2} {3} {4} {5} [{6}] {7}",
          "{0} {1}/{2} {3} {4} {5} [{6}] {7}",
70 (active == Action.Create) ? '+' : (active == Action.Edit) ? '=' : isReplay ? "|/-\\"[stack.Count % 4] : '>',
          (active == Action.Create) ? '+' : (active == Action.Edit) ? '=' : isReplay ? "|/-\\"[stack.Count % 4] : '>',
71 Level + 1, MaxLevel, Pub.ToString(LevelSize),
          Level + 1, MaxLevel, Pub.ToString(LevelSize),
72 IsDesign ? string.Format("{0}={1}", db.Boxs, db.Slots) : string.Format("{0}/{1}", db.Boths, db.Tasks),
          IsDesign ? string.Format("{0}={1}", db.Boxs, db.Slots) : string.Format("{0}/{1}", db.Boths, db.Tasks),
73 IsDesign ? Block.GetPenName(pen) : string.Format("{0}({1})", stack.Count, pushSteps),
          IsDesign ? Block.GetPenName(pen) : string.Format("{0}({1})", stack.Count, pushSteps),
74 IsDesign ? (active == Action.Create ? "新建" : "编辑") : db.IsFinished ?
          IsDesign ? (active == Action.Create ? "新建" : "编辑") : db.IsFinished ? 
75 string.Format("{0}({1})", db.MovedSteps, db.PushedSteps) : string.Empty,
          string.Format("{0}({1})", db.MovedSteps, db.PushedSteps) : string.Empty,
76 db.GroupName);
          db.GroupName);
77 }
      }
78 }
    }
79
80 public void Dispose()
    public void Dispose()
81 {
    {
82 db.Dispose();
      db.Dispose();
83 }
    }
84
85 public void Init()
    public void Init()
86 {
    {
87 active = Action.None;
      active = Action.None;
88 pen = Block.Land;
      pen = Block.Land;
89 stack.Clear();
      stack.Clear();
90 SetExceptionMessage(null);
      SetExceptionMessage(null);
91 }
    }
92
93 void SetExceptionMessage(Exception ex)
    void SetExceptionMessage(Exception ex)
94 {
    {
95 errorMsg = Pub.GetMessage(ex, false);
      errorMsg = Pub.GetMessage(ex, false);
96 debugMsg = Pub.GetMessage(ex, true);
      debugMsg = Pub.GetMessage(ex, true);
97 }
    }
98
99 /// <summary>
    /// <summary>
100 /// 计算当使用标准箱子尺寸时主窗体客户区的尺寸
    /// 计算当使用标准箱子尺寸时主窗体客户区的尺寸
101 /// </summary>
    /// </summary>
102 /// <param name="statusBarHeight">状态条的高度</param>
    /// <param name="statusBarHeight">状态条的高度</param>
103 /// <returns>客户区的尺寸</returns>
    /// <returns>客户区的尺寸</returns>
104 public Size GetClientSize(int statusBarHeight)
    public Size GetClientSize(int statusBarHeight)
105 {
    {
106 int width = (Properties.Resources.PushBox24.Width / 8) * LevelSize.Width;
      int width = (Properties.Resources.PushBox24.Width / 8) * LevelSize.Width;
107 int height = Properties.Resources.PushBox24.Height * LevelSize.Height + statusBarHeight;
      int height = Properties.Resources.PushBox24.Height * LevelSize.Height + statusBarHeight;
108 if (width < 240) width = 240;
      if (width < 240) width = 240;
109 if (height < 48) height = 48;
      if (height < 48) height = 48;
110 if (width > 1008) width = 1008;
      if (width > 1008) width = 1008;
111 if (height > 672) height = 672;
      if (height > 672) height = 672;
112 return new Size(width, height);
      return new Size(width, height);
113 }
    }
114
115 /// <summary>
    /// <summary>
116 /// 根据客户区尺寸,计算箱子的尺寸,并相应设定要显示的图形单元
    /// 根据客户区尺寸,计算箱子的尺寸,并相应设定要显示的图形单元
117 /// </summary>
    /// </summary>
118 public void SetBoxInfo()
    public void SetBoxInfo()
119 {
    {
120 if (HasError) return;
      if (HasError) return;
121 if (LevelSize.IsEmpty) return;
      if (LevelSize.IsEmpty) return;
122 int rX = clientSize.Width / LevelSize.Width;
      int rX = clientSize.Width / LevelSize.Width;
123 int rY = clientSize.Height / LevelSize.Height;
      int rY = clientSize.Height / LevelSize.Height;
124 int r = Math.Min(rX, rY);
      int r = Math.Min(rX, rY);
125 if (r >= 24) img = Properties.Resources.PushBox24;
      if (r >= 24) img = Properties.Resources.PushBox24;
126 else if (r >= 20) img = Properties.Resources.PushBox20;
      else if (r >= 20) img = Properties.Resources.PushBox20;
127 else if (r >= 16) img = Properties.Resources.PushBox16;
      else if (r >= 16) img = Properties.Resources.PushBox16;
128 else img = Properties.Resources.PushBox12;
      else img = Properties.Resources.PushBox12;
129 boxSize = new Size(img.Height, img.Width / 8);
      boxSize = new Size(img.Height, img.Width / 8);
130 }
    }
131
132 /// <summary>
    /// <summary>
133 /// 装入配置文件
    /// 装入配置文件
134 /// </summary>
    /// </summary>
135 public void LoadConfig()
    public void LoadConfig()
136 {
    {
137 if (HasError) return;
      if (HasError) return;
138 try
      try
139 {
      {
140 cfg.LoadConfig();
        cfg.LoadConfig();
141 }
      }
142 catch (Exception ex)
      catch (Exception ex)
143 {
      {
144 SetExceptionMessage(ex);
        SetExceptionMessage(ex);
145 }
      }
146 }
    }
147
148 /// <summary>
    /// <summary>
149 /// 保存组信息到配置文件
    /// 保存组信息到配置文件
150 /// </summary>
    /// </summary>
151 /// <param name="groups">组信息</param>
    /// <param name="groups">组信息</param>
152 public void SaveConfig(string[] groups)
    public void SaveConfig(string[] groups)
153 {
    {
154 if (HasError) return;
      if (HasError) return;
155 try
      try
156 {
      {
157 cfg.SaveConfig(groups);
        cfg.SaveConfig(groups);
158 }
      }
159 catch (Exception ex)
      catch (Exception ex)
160 {
      {
161 SetExceptionMessage(ex);
        SetExceptionMessage(ex);
162 }
      }
163 }
    }
164
165 /// <summary>
    /// <summary>
166 /// 保存当前选项及当前走法到配置文件
    /// 保存当前选项及当前走法到配置文件
167 /// </summary>
    /// </summary>
168 public void SaveConfig()
    public void SaveConfig()
169 {
    {
170 if (HasError) return;
      if (HasError) return;
171 try
      try
172 {
      {
173 cfg.SaveConfig(stack.ToArray());
        cfg.SaveConfig(stack.ToArray());
174 }
      }
175 catch (Exception ex)
      catch (Exception ex)
176 {
      {
177 SetExceptionMessage(ex);
        SetExceptionMessage(ex);
178 }
      }
179 }
    }
180
181 /// <summary>
    /// <summary>
182 /// 装入当前组信息
    /// 装入当前组信息
183 /// </summary>
    /// </summary>
184 public void LoadGroup()
    public void LoadGroup()
185 {
    {
186 if (HasError) return;
      if (HasError) return;
187 try
      try
188 {
      {
189 db.LoadGroup(Groups[Group]);
        db.LoadGroup(Groups[Group]);
190 }
      }
191 catch (Exception ex)
      catch (Exception ex)
192 {
      {
193 SetExceptionMessage(ex);
        SetExceptionMessage(ex);
194 }
      }
195 }
    }
196
197 /// <summary>
    /// <summary>
198 /// 装入当前关信息
    /// 装入当前关信息
199 /// </summary>
    /// </summary>
200 public void LoadLevel()
    public void LoadLevel()
201 {
    {
202 active = Action.None;
      active = Action.None;
203 if (HasError) return;
      if (HasError) return;
204 try
      try
205 {
      {
206 db.LoadLevel(Level);
        db.LoadLevel(Level);
207 worker = db.Worker;
        worker = db.Worker;
208 stack.Clear();
        stack.Clear();
209 pushSteps = 0;
        pushSteps = 0;
210 isReplay = false;
        isReplay = false;
211 }
      }
212 catch (Exception ex)
      catch (Exception ex)
213 {
      {
214 SetExceptionMessage(ex);
        SetExceptionMessage(ex);
215 }
      }
216 }
    }
217
218 /// <summary>
    /// <summary>
219 /// 新建一关
    /// 新建一关
220 /// </summary>
    /// </summary>
221 /// <param name="isCopy">是否复制当前关</param>
    /// <param name="isCopy">是否复制当前关</param>
222 /// <param name="size">新建关的尺寸</param>
    /// <param name="size">新建关的尺寸</param>
223 public void NewLevel(bool isCopy, Size size)
    public void NewLevel(bool isCopy, Size size)
224 {
    {
225 if (HasError) return;
      if (HasError) return;
226 try
      try
227 {
      {
228 levelOem = Level;
        levelOem = Level;
229 Level = MaxLevel;
        Level = MaxLevel;
230 db.NewLevel(isCopy, size);
        db.NewLevel(isCopy, size);
231 }
      }
232 catch (Exception ex)
      catch (Exception ex)
233 {
      {
234 SetExceptionMessage(ex);
        SetExceptionMessage(ex);
235 }
      }
236 }
    }
237
238 /// <summary>
    /// <summary>
239 /// 给出通关步骤
    /// 给出通关步骤
240 /// </summary>
    /// </summary>
241 /// <returns>通关步骤</returns>
    /// <returns>通关步骤</returns>
242 public string GetSteps()
    public string GetSteps()
243 {
    {
244 string steps = "";
      string steps = "";
245 if (!HasError)
      if (!HasError)
246 {
      {
247 try
        try
248 {
        {
249 steps = db.GetSteps(Level);
          steps = db.GetSteps(Level);
250 }
        }
251 catch (Exception ex)
        catch (Exception ex)
252 {
        {
253 SetExceptionMessage(ex);
          SetExceptionMessage(ex);
254 }
        }
255 }
      }
256 return steps;
      return steps;
257 }
    }
258
259 /// <summary>
    /// <summary>
260 /// 记录通关步骤
    /// 记录通关步骤
261 /// </summary>
    /// </summary>
262 public void Record()
    public void Record()
263 {
    {
264 if (HasError) return;
      if (HasError) return;
265 try
      try
266 {
      {
267 db.SaveLevel(Level, stack.ToArray(), pushSteps);
        db.SaveLevel(Level, stack.ToArray(), pushSteps);
268 }
      }
269 catch (Exception ex)
      catch (Exception ex)
270 {
      {
271 SetExceptionMessage(ex);
        SetExceptionMessage(ex);
272 }
      }
273 }
    }
274
275 /// <summary>
    /// <summary>
276 /// 保存设计数据
    /// 保存设计数据
277 /// </summary>
    /// </summary>
278 public void SaveDesign()
    public void SaveDesign()
279 {
    {
280 if (HasError) return;
      if (HasError) return;
281 try
      try
282 {
      {
283 db.SaveDesign(active == Action.Create, Level);
        db.SaveDesign(active == Action.Create, Level);
284 }
      }
285 catch (Exception ex)
      catch (Exception ex)
286 {
      {
287 SetExceptionMessage(ex);
        SetExceptionMessage(ex);
288 }
      }
289 }
    }
290
291 /// <summary>
    /// <summary>
292 /// 删除最后一关
    /// 删除最后一关
293 /// </summary>
    /// </summary>
294 public void DeleteLastLevel()
    public void DeleteLastLevel()
295 {
    {
296 if (HasError) return;
      if (HasError) return;
297 try
      try
298 {
      {
299 db.DeleteLastLevel(Level);
        db.DeleteLastLevel(Level);
300 }
      }
301 catch (Exception ex)
      catch (Exception ex)
302 {
      {
303 SetExceptionMessage(ex);
        SetExceptionMessage(ex);
304 }
      }
305 }
    }
306
307 /// <summary>
    /// <summary>
308 /// 更新主窗体客户区
    /// 更新主窗体客户区
309 /// </summary>
    /// </summary>
310 /// <param name="dc">画布</param>
    /// <param name="dc">画布</param>
311 /// <param name="rectangle">要在其中绘画的矩形</param>
    /// <param name="rectangle">要在其中绘画的矩形</param>
312 public void Draw(Graphics dc, Rectangle rectangle)
    public void Draw(Graphics dc, Rectangle rectangle)
313 {
    {
314 if (HasError) return;
      if (HasError) return;
315 Rectangle box = PixelToBox(rectangle);
      Rectangle box = PixelToBox(rectangle);
316 Rectangle box2 = new Rectangle(box.Left, box.Top, box.Width + 1, box.Height + 1);
      Rectangle box2 = new Rectangle(box.Left, box.Top, box.Width + 1, box.Height + 1);
317 for (int i = 1; i <= LevelSize.Height; i++)
      for (int i = 1; i <= LevelSize.Height; i++)
318 {
      {
319 for (int j = 1; j <= LevelSize.Width; j++)
        for (int j = 1; j <= LevelSize.Width; j++)
320 {
        {
321 if (!box2.Contains(j, i)) continue;
          if (!box2.Contains(j, i)) continue;
322 DrawBox(dc, j, i);
          DrawBox(dc, j, i);
323 }
        }
324 }
      }
325 }
    }
326
327 /// <summary>
    /// <summary>
328 /// 绘制一个单元格
    /// 绘制一个单元格
329 /// </summary>
    /// </summary>
330 /// <param name="dc">画布</param>
    /// <param name="dc">画布</param>
331 /// <param name="x">单元格的横坐标</param>
    /// <param name="x">单元格的横坐标</param>
332 /// <param name="y">单元格的纵坐标</param>
    /// <param name="y">单元格的纵坐标</param>
333 void DrawBox(Graphics dc, int x, int y)
    void DrawBox(Graphics dc, int x, int y)
334 {
    {
335 DrawBox(dc, db.Map[y, x], (x - 1) * boxSize.Width, (y - 1) * boxSize.Height);
      DrawBox(dc, db.Map[y, x], (x - 1) * boxSize.Width, (y - 1) * boxSize.Height);
336 }
    }
337
338 /// <summary>
    /// <summary>
339 /// 绘制一个单元格
    /// 绘制一个单元格
340 /// </summary>
    /// </summary>
341 /// <param name="dc">画布</param>
    /// <param name="dc">画布</param>
342 /// <param name="idx">单元格的类型: 地 槽 墙 砖 箱子 工人</param>
    /// <param name="idx">单元格的类型: 地 槽 墙 砖 箱子 工人</param>
343 /// <param name="x">单元格的横坐标</param>
    /// <param name="x">单元格的横坐标</param>
344 /// <param name="y">单元格的纵坐标</param>
    /// <param name="y">单元格的纵坐标</param>
345 void DrawBox(Graphics dc, int idx, int x, int y)
    void DrawBox(Graphics dc, int idx, int x, int y)
346 {
    {
347 dc.DrawImage(img, x, y, new Rectangle(idx * boxSize.Width, 0, boxSize.Width, boxSize.Height), GraphicsUnit.Pixel);
      dc.DrawImage(img, x, y, new Rectangle(idx * boxSize.Width, 0, boxSize.Width, boxSize.Height), GraphicsUnit.Pixel);
348 }
    }
349
350 /// <summary>
    /// <summary>
351 /// 将单元格换算为像素
    /// 将单元格换算为像素
352 /// </summary>
    /// </summary>
353 /// <param name="box">单元格矩形</param>
    /// <param name="box">单元格矩形</param>
354 /// <returns>像素矩形</returns>
    /// <returns>像素矩形</returns>
355 Rectangle BoxToPixel(Rectangle box)
    Rectangle BoxToPixel(Rectangle box)
356 {
    {
357 return new Rectangle((box.Left - 1) * boxSize.Width, (box.Top - 1) * boxSize.Height,
      return new Rectangle((box.Left - 1) * boxSize.Width, (box.Top - 1) * boxSize.Height,
358 (box.Width + 1) * boxSize.Width, (box.Height + 1) * boxSize.Height);
        (box.Width + 1) * boxSize.Width, (box.Height + 1) * boxSize.Height);
359 }
    }
360
361 /// <summary>
    /// <summary>
362 /// 将像素换算为单元格
    /// 将像素换算为单元格
363 /// </summary>
    /// </summary>
364 /// <param name="pixel">像素矩形</param>
    /// <param name="pixel">像素矩形</param>
365 /// <returns>单元格矩形</returns>
    /// <returns>单元格矩形</returns>
366 Rectangle PixelToBox(Rectangle pixel)
    Rectangle PixelToBox(Rectangle pixel)
367 {
    {
368 int x0 = pixel.Left / boxSize.Width + 1;
      int x0 = pixel.Left / boxSize.Width + 1;
369 int y0 = pixel.Top / boxSize.Height + 1;
      int y0 = pixel.Top / boxSize.Height + 1;
370 int x1 = (pixel.Right - 1) / boxSize.Width + 1;
      int x1 = (pixel.Right - 1) / boxSize.Width + 1;
371 int y1 = (pixel.Bottom - 1) / boxSize.Height + 1;
      int y1 = (pixel.Bottom - 1) / boxSize.Height + 1;
372 return new Rectangle(x0, y0, x1 - x0, y1 - y0);
      return new Rectangle(x0, y0, x1 - x0, y1 - y0);
373 }
    }
374
375 /// <summary>
    /// <summary>
376 /// 根据指定的对角顶点创建矩形
    /// 根据指定的对角顶点创建矩形
377 /// </summary>
    /// </summary>
378 /// <param name="a">顶点</param>
    /// <param name="a">顶点</param>
379 /// <param name="b">对角的顶点</param>
    /// <param name="b">对角的顶点</param>
380 /// <returns>所需要的矩形</returns>
    /// <returns>所需要的矩形</returns>
381 Rectangle GetRectangle(Point a, Point b)
    Rectangle GetRectangle(Point a, Point b)
382 {
    {
383 return Rectangle.FromLTRB(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Max(a.X, b.X), Math.Max(a.Y, b.Y));
      return Rectangle.FromLTRB(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Max(a.X, b.X), Math.Max(a.Y, b.Y));
384 }
    }
385
386 /// <summary>
    /// <summary>
387 /// 设计模式下,当鼠标点击时要采取的动作
    /// 设计模式下,当鼠标点击时要采取的动作
388 /// </summary>
    /// </summary>
389 /// <param name="invalid">输出:要重绘的区域</param>
    /// <param name="invalid">输出:要重绘的区域</param>
390 /// <returns>是否发生动作</returns>
    /// <returns>是否发生动作</returns>
391 public bool Design(out Rectangle invalid)
    public bool Design(out Rectangle invalid)
392 {
    {
393 invalid = Rectangle.Empty;
      invalid = Rectangle.Empty;
394 Point to;
      Point to;
395 if (!ValidClick(out to)) return false;
      if (!ValidClick(out to)) return false;
396 db.UpdateCounts(to.X, to.Y, false);
      db.UpdateCounts(to.X, to.Y, false);
397 Block.Update(ref db.Map[to.Y, to.X], pen);
      Block.Update(ref db.Map[to.Y, to.X], pen);
398 db.UpdateCounts(to.X, to.Y, true);
      db.UpdateCounts(to.X, to.Y, true);
399 if (pen == Block.Man0 && HasWorker) pen = Block.Box0;
      if (pen == Block.Man0 && HasWorker) pen = Block.Box0;
400 invalid = BoxToPixel(GetRectangle(to, to));
      invalid = BoxToPixel(GetRectangle(to, to));
401 return true;
      return true;
402 }
    }
403
404 /// <summary>
    /// <summary>
405 /// 工人往指定方向前进一步(可能推着箱子)
    /// 工人往指定方向前进一步(可能推着箱子)
406 /// </summary>
    /// </summary>
407 /// <param name="dir">前进的方向</param>
    /// <param name="dir">前进的方向</param>
408 /// <param name="isStop">“撤销”时是否停留</param>
    /// <param name="isStop">“撤销”时是否停留</param>
409 /// <param name="invalid">输出:要重绘的区域</param>
    /// <param name="invalid">输出:要重绘的区域</param>
410 /// <returns>是否成功</returns>
    /// <returns>是否成功</returns>
411 public bool StepIt(Direction dir, bool isStop, out Rectangle invalid)
    public bool StepIt(Direction dir, bool isStop, out Rectangle invalid)
412 {
    {
413 invalid = Rectangle.Empty;
      invalid = Rectangle.Empty;
414 if (HasError) return false;
      if (HasError) return false;
415 if (Direction.None == dir) return false;
      if (Direction.None == dir) return false;
416 Point p1 = worker; // 工人前进方向一步的位置
      Point p1 = worker; // 工人前进方向一步的位置
417 Point p2 = worker; // 箱子前进方向一步的位置
      Point p2 = worker; // 箱子前进方向一步的位置
418 switch (dir)
      switch (dir)
419 {
      {
420 case Direction.East: p1.X++; p2.X += 2; break;
        case Direction.East: p1.X++; p2.X += 2; break;
421 case Direction.South: p1.Y++; p2.Y += 2; break;
        case Direction.South: p1.Y++; p2.Y += 2; break;
422 case Direction.West: p1.X--; p2.X -= 2; break;
        case Direction.West: p1.X--; p2.X -= 2; break;
423 case Direction.North: p1.Y--; p2.Y -= 2; break;
        case Direction.North: p1.Y--; p2.Y -= 2; break;
424 }
      }
425 byte b1 = db.Map[p1.Y, p1.X]; // 工人前进方向一步位置上的东西
      byte b1 = db.Map[p1.Y, p1.X]; // 工人前进方向一步位置上的东西
426 bool isBox = Block.IsBox(b1); // 是否推着箱子前进
      bool isBox = Block.IsBox(b1); // 是否推着箱子前进
427 if (!isBox && !Block.IsBlank(b1)) return false; // 如果没有推着箱子且前方不是空地则失败
      if (!isBox && !Block.IsBlank(b1)) return false; // 如果没有推着箱子且前方不是空地则失败
428 if (isBox && !Block.IsBlank(db.Map[p2.Y, p2.X])) return false; // 如果推着箱子且箱子前方不是空地则失败
      if (isBox && !Block.IsBlank(db.Map[p2.Y, p2.X])) return false; // 如果推着箱子且箱子前方不是空地则失败
429 invalid = BoxToPixel(GetRectangle(worker, isBox ? p2 : p1)); // 要重绘的区域
      invalid = BoxToPixel(GetRectangle(worker, isBox ? p2 : p1)); // 要重绘的区域
430 stack.Push(new Step(dir, isBox, isStop)); // 记录走法步骤
      stack.Push(new Step(dir, isBox, isStop)); // 记录走法步骤
431 Block.ManOut(ref db.Map[worker.Y, worker.X]); // 工人离开当前位置
      Block.ManOut(ref db.Map[worker.Y, worker.X]); // 工人离开当前位置
432 Block.ManIn(ref db.Map[p1.Y, p1.X]); // 工人进入前方位置
      Block.ManIn(ref db.Map[p1.Y, p1.X]); // 工人进入前方位置
433 if (isBox)
      if (isBox)
434 {
      {
435 pushSteps++; // 更新推箱子步数
        pushSteps++; // 更新推箱子步数
436 db.Boths += (db.Map[p2.Y, p2.X] - Block.Land) - (b1 - Block.Box0); // 更新已完成任务数
        db.Boths += (db.Map[p2.Y, p2.X] - Block.Land) - (b1 - Block.Box0); // 更新已完成任务数
437 Block.BoxOut(ref db.Map[p1.Y, p1.X]); // 箱子离开当前位置
        Block.BoxOut(ref db.Map[p1.Y, p1.X]); // 箱子离开当前位置
438 Block.BoxIn(ref db.Map[p2.Y, p2.X]); // 箱子进入前方位置
        Block.BoxIn(ref db.Map[p2.Y, p2.X]); // 箱子进入前方位置
439 }
      }
440 worker = p1; // 更新工人位置
      worker = p1; // 更新工人位置
441 return true; // 工人成功前进一步(可能推着条子)
      return true; // 工人成功前进一步(可能推着条子)
442 }
    }
443
444 /// <summary>
    /// <summary>
445 /// 工人后退一步(可能连带箱子一起后退)
    /// 工人后退一步(可能连带箱子一起后退)
446 /// </summary>
    /// </summary>
447 /// <param name="invalid">输出:要重绘的区域</param>
    /// <param name="invalid">输出:要重绘的区域</param>
448 /// <returns>是否完成“撤消”</returns>
    /// <returns>是否完成“撤消”</returns>
449 public bool Back(out Rectangle invalid)
    public bool Back(out Rectangle invalid)
450 {
    {
451 invalid = Rectangle.Empty;
      invalid = Rectangle.Empty;
452 if (HasError) return true;
      if (HasError) return true;
453 if (stack.Count == 0) return true;
      if (stack.Count == 0) return true;
454 Step step = stack.Pop(); // 当前步骤
      Step step = stack.Pop(); // 当前步骤
455 Point p1 = worker; // 工人后退方向一步的位置
      Point p1 = worker; // 工人后退方向一步的位置
456 Point p2 = worker; // 箱子的当前位置
      Point p2 = worker; // 箱子的当前位置
457 switch (step.Direct)
      switch (step.Direct)
458 {
      {
459 case Direction.East: p1.X--; p2.X++; break;
        case Direction.East: p1.X--; p2.X++; break;
460 case Direction.South: p1.Y--; p2.Y++; break;
        case Direction.South: p1.Y--; p2.Y++; break;
461 case Direction.West: p1.X++; p2.X--; break;
        case Direction.West: p1.X++; p2.X--; break;
462 case Direction.North: p1.Y++; p2.Y--; break;
        case Direction.North: p1.Y++; p2.Y--; break;
463 }
      }
464 invalid = BoxToPixel(GetRectangle(p1, step.IsBox ? p2 : worker)); // 要重绘的区域
      invalid = BoxToPixel(GetRectangle(p1, step.IsBox ? p2 : worker)); // 要重绘的区域
465 Block.ManOut(ref db.Map[worker.Y, worker.X]); // 工人离开当前位置
      Block.ManOut(ref db.Map[worker.Y, worker.X]); // 工人离开当前位置
466 Block.ManIn(ref db.Map[p1.Y, p1.X]); // 工人进入后退方向一步的位置
      Block.ManIn(ref db.Map[p1.Y, p1.X]); // 工人进入后退方向一步的位置
467 if (step.IsBox)
      if (step.IsBox)
468 {
      {
469 Block.BoxOut(ref db.Map[p2.Y, p2.X]); // 箱子离开当前位置
        Block.BoxOut(ref db.Map[p2.Y, p2.X]); // 箱子离开当前位置
470 Block.BoxIn(ref db.Map[worker.Y, worker.X]); // 箱子进入工人原来的位置
        Block.BoxIn(ref db.Map[worker.Y, worker.X]); // 箱子进入工人原来的位置
471 db.Boths += (db.Map[worker.Y, worker.X] - Block.Box0) - (db.Map[p2.Y, p2.X] - Block.Land); // 更新已完成任务数
        db.Boths += (db.Map[worker.Y, worker.X] - Block.Box0) - (db.Map[p2.Y, p2.X] - Block.Land); // 更新已完成任务数
472 pushSteps--; // 更新推箱子步数
        pushSteps--; // 更新推箱子步数
473 }
      }
474 worker = p1; // 更新工人位置
      worker = p1; // 更新工人位置
475 return step.IsStop; // 是否完成“撤消”
      return step.IsStop; // 是否完成“撤消”
476 }
    }
477
478 /// <summary>
    /// <summary>
479 /// 寻找一条将工人移动到鼠标点击的位置的路线
    /// 寻找一条将工人移动到鼠标点击的位置的路线
480 /// </summary>
    /// </summary>
481 /// <returns>移动的路线</returns>
    /// <returns>移动的路线</returns>
482 public Queue<Direction> GetMoveInfo()
    public Queue<Direction> GetMoveInfo()
483 {
    {
484 Point to;
      Point to;
485 if (!CanTo(out to)) return null;
      if (!CanTo(out to)) return null;
486 return FindPath.Seek(db.Map, worker, to);
      return FindPath.Seek(db.Map, worker, to);
487 }
    }
488
489 /// <summary>
    /// <summary>
490 /// 给出将箱子推动到鼠标点击的位置所需的信息
    /// 给出将箱子推动到鼠标点击的位置所需的信息
491 /// </summary>
    /// </summary>
492 /// <param name="dir">输出:工人移动的方向</param>
    /// <param name="dir">输出:工人移动的方向</param>
493 /// <returns>工人移动的步数</returns>
    /// <returns>工人移动的步数</returns>
494 public int GetPushInfo(out Direction dir)
    public int GetPushInfo(out Direction dir)
495 {
    {
496 dir = Direction.None;
      dir = Direction.None;
497 if (HasError) return 0;
      if (HasError) return 0;
498 Point to; // 目的地
      Point to; // 目的地
499 if (!CanTo(out to)) return 0; // 无效的目的地
      if (!CanTo(out to)) return 0; // 无效的目的地
500 if (to.Y != worker.Y && to.X != worker.X) return 0; // 目的地和工人不在同一条直线上
      if (to.Y != worker.Y && to.X != worker.X) return 0; // 目的地和工人不在同一条直线上
501 int z0 = (to.Y == worker.Y) ? worker.X : worker.Y;
      int z0 = (to.Y == worker.Y) ? worker.X : worker.Y;
502 int z9 = (to.Y == worker.Y) ? to.X : to.Y;
      int z9 = (to.Y == worker.Y) ? to.X : to.Y;
503 if (to.Y == worker.Y) dir = (z9 > z0) ? Direction.East : Direction.West;
      if (to.Y == worker.Y) dir = (z9 > z0) ? Direction.East : Direction.West;
504 else dir = (z9 > z0) ? Direction.South : Direction.North;
      else dir = (z9 > z0) ? Direction.South : Direction.North;
505 int i0 = Math.Min(z9, z0);
      int i0 = Math.Min(z9, z0);
506 int i9 = Math.Max(z9, z0);
      int i9 = Math.Max(z9, z0);
507 int steps = i9 - i0; // 目的地和工人之间的距离
      int steps = i9 - i0; // 目的地和工人之间的距离
508 int boxs = 0;
      int boxs = 0;
509 for (int i = i0 + 1; i < i9; i++)
      for (int i = i0 + 1; i < i9; i++)
510 {
      {
511 byte bi = (to.Y == worker.Y) ? db.Map[worker.Y, i] : db.Map[i, worker.X];
        byte bi = (to.Y == worker.Y) ? db.Map[worker.Y, i] : db.Map[i, worker.X];
512 if (Block.IsBox(bi)) boxs++; // 计算工人和目的地之间的箱子的个数
        if (Block.IsBox(bi)) boxs++; // 计算工人和目的地之间的箱子的个数
513 else if (!Block.IsBlank(bi)) boxs += 2; // “墙”和“砖”折算为两个箱子
        else if (!Block.IsBlank(bi)) boxs += 2; // “墙”和“砖”折算为两个箱子
514 }
      }
515 if (boxs > 1) return 0; // 最多只能推着一个箱子前进
      if (boxs > 1) return 0; // 最多只能推着一个箱子前进
516 return steps - boxs; // 工人移动的步数
      return steps - boxs; // 工人移动的步数
517 }
    }
518
519 /// <summary>
    /// <summary>
520 /// 检查鼠标点击位置是否可达, 并将像素换算为单元格
    /// 检查鼠标点击位置是否可达, 并将像素换算为单元格
521 /// </summary>
    /// </summary>
522 /// <param name="to">输出:换算后的位置</param>
    /// <param name="to">输出:换算后的位置</param>
523 /// <returns>是否可达</returns>
    /// <returns>是否可达</returns>
524 bool CanTo(out Point to)
    bool CanTo(out Point to)
525 {
    {
526 if (!ValidClick(out to)) return false;
      if (!ValidClick(out to)) return false;
527 if (!Block.IsMan(db.Map[worker.Y, worker.X])) throw new Exception("内部错误:工人的位置上不是工人");
      if (!Block.IsMan(db.Map[worker.Y, worker.X])) throw new Exception("内部错误:工人的位置上不是工人");
528 if (!Block.IsBlank(db.Map[to.Y, to.X])) return false; // 目的地必须是“地”或“槽”
      if (!Block.IsBlank(db.Map[to.Y, to.X])) return false; // 目的地必须是“地”或“槽”
529 if (to.Y == worker.Y && to.X == worker.X) return false; // 目的地不能是工人当前的位置
      if (to.Y == worker.Y && to.X == worker.X) return false; // 目的地不能是工人当前的位置
530 return true; // 目的地可达
      return true; // 目的地可达
531 }
    }
532
533 /// <summary>
    /// <summary>
534 /// 检查鼠标点击位置是否有效, 并将像素换算为单元格
    /// 检查鼠标点击位置是否有效, 并将像素换算为单元格
535 /// </summary>
    /// </summary>
536 /// <param name="to">输出:换算后的位置</param>
    /// <param name="to">输出:换算后的位置</param>
537 /// <returns>是否有效位置</returns>
    /// <returns>是否有效位置</returns>
538 bool ValidClick(out Point to)
    bool ValidClick(out Point to)
539 {
    {
540 to = Point.Empty;
      to = Point.Empty;
541 if (HasError) return false;
      if (HasError) return false;
542 to.Y = toPixel.Y / boxSize.Height + 1;
      to.Y = toPixel.Y / boxSize.Height + 1;
543 to.X = toPixel.X / boxSize.Width + 1;
      to.X = toPixel.X / boxSize.Width + 1;
544 if (toPixel.X >= boxSize.Width * LevelSize.Width || toPixel.Y >= boxSize.Height * LevelSize.Height)
      if (toPixel.X >= boxSize.Width * LevelSize.Width || toPixel.Y >= boxSize.Height * LevelSize.Height)
545 return false; // 目的地超出当前关的有效范围
        return false; // 目的地超出当前关的有效范围
546 return true; // 目的地有效
      return true; // 目的地有效
547 }
    }
548 }
  }
549 }
}
550
 using System;
using System;2
 using System.Drawing;
using System.Drawing;3
 using System.Collections.Generic;
using System.Collections.Generic;4

5
 namespace Skyiv.Ben.PushBox.Common
namespace Skyiv.Ben.PushBox.Common6
 {
{7
 /// <summary>
  /// <summary>8
 /// 工作环境
  /// 工作环境9
 /// </summary>
  /// </summary>10
 sealed class Env : IDisposable
  sealed class Env : IDisposable11
 {
  {12
 DataFile db;       // 数据文件
    DataFile db;       // 数据文件13
 ConfigFile cfg;    // 配置文件
    ConfigFile cfg;    // 配置文件14
 string errorMsg;   // 错误信息
    string errorMsg;   // 错误信息15
 string debugMsg;   // 调试信息
    string debugMsg;   // 调试信息16
 bool isReplay;     // 是否正在回放
    bool isReplay;     // 是否正在回放17
 Action active;     // 模式: 正常 新建 编辑 删除
    Action active;     // 模式: 正常 新建 编辑 删除18
 byte pen;          // 设计时的笔
    byte pen;          // 设计时的笔19
 Bitmap img;        // 图形单元, 横向被均匀分为八份
    Bitmap img;        // 图形单元, 横向被均匀分为八份20
 Stack<Step> stack; // 历史路线, 用于后退功能
    Stack<Step> stack; // 历史路线, 用于后退功能21
 Size clientSize;   // 工作区域尺寸(以像素为单位)
    Size clientSize;   // 工作区域尺寸(以像素为单位)22
 Size boxSize;      // 图形元素尺寸(以像素为单位)
    Size boxSize;      // 图形元素尺寸(以像素为单位)23
 Point toPixel;     // 将要到达的位置(以像素为单位)
    Point toPixel;     // 将要到达的位置(以像素为单位)24
 Point worker;      // 当前工人位置(以单元格为单位)
    Point worker;      // 当前工人位置(以单元格为单位)25
 int pushSteps;     // 推动着箱子走的步数
    int pushSteps;     // 推动着箱子走的步数26
 int levelOem;      // 原来的关数,仅用于“菜单 -> 数据 -> 设计 -> 新建”放弃后恢复现场
    int levelOem;      // 原来的关数,仅用于“菜单 -> 数据 -> 设计 -> 新建”放弃后恢复现场27

28
 public string ErrorMsg { get { return errorMsg; } }
    public string ErrorMsg { get { return errorMsg; } }29
 public string DebugMsg { get { return debugMsg; } }
    public string DebugMsg { get { return debugMsg; } }30
 public string[] Groups { get { return cfg.Groups; } }
    public string[] Groups { get { return cfg.Groups; } }31
 public int Group { get { return cfg.Group; } set { cfg.Group = value; } }
    public int Group { get { return cfg.Group; } set { cfg.Group = value; } }32
 public int Level { get { return cfg.Levels[Group]; } set { cfg.Levels[Group] = value; } }
    public int Level { get { return cfg.Levels[Group]; } set { cfg.Levels[Group] = value; } }33
 public int LeveLOem { get { return levelOem; } }
    public int LeveLOem { get { return levelOem; } }34
 public int MaxLevel { get { return db.MaxLevel; } }
    public int MaxLevel { get { return db.MaxLevel; } }35
 public string Steps { get { return cfg.Steps; } }
    public string Steps { get { return cfg.Steps; } }36
 public Size LevelSize { get { return db.LevelSize; } }
    public Size LevelSize { get { return db.LevelSize; } }37
 public Size ClientSize { set { clientSize = value; } }
    public Size ClientSize { set { clientSize = value; } }38
 public Point ToPixel { set { toPixel = value; } }
    public Point ToPixel { set { toPixel = value; } }39
 public int MaxLevelSize { get { return cfg.MaxLevelSize; } set { cfg.MaxLevelSize = value; } }
    public int MaxLevelSize { get { return cfg.MaxLevelSize; } set { cfg.MaxLevelSize = value; } }40
 public int StepDelay { get { return cfg.StepDelay; } set { cfg.StepDelay = value; } }
    public int StepDelay { get { return cfg.StepDelay; } set { cfg.StepDelay = value; } }41
 public int ReplayDelay { get { return cfg.ReplayDelay; } set { cfg.ReplayDelay = value; } }
    public int ReplayDelay { get { return cfg.ReplayDelay; } set { cfg.ReplayDelay = value; } }42
 public Action Active { get { return active; } set { active = value; } }
    public Action Active { get { return active; } set { active = value; } }43
 public byte Pen { get { return pen; } set { pen = value; } }
    public byte Pen { get { return pen; } set { pen = value; } }44
 public bool HasError { get { return !string.IsNullOrEmpty(errorMsg); } }
    public bool HasError { get { return !string.IsNullOrEmpty(errorMsg); } }45
 public bool HasWorker { get { return db.HasWorker; } }
    public bool HasWorker { get { return db.HasWorker; } }46
 public bool CanUndo { get { return stack.Count != 0; } }
    public bool CanUndo { get { return stack.Count != 0; } }47
 public bool CanReplay { get { return db.IsFinished && !CanUndo; } }
    public bool CanReplay { get { return db.IsFinished && !CanUndo; } }48
 public bool IsSave { get { return cfg.IsSave; } set { cfg.IsSave = value; } }
    public bool IsSave { get { return cfg.IsSave; } set { cfg.IsSave = value; } }49
 public bool IsFinish { get { return db.Tasks == db.Boths; } }
    public bool IsFinish { get { return db.Tasks == db.Boths; } }50
 public bool IsReplay { get { return isReplay; } set { isReplay = value; } }
    public bool IsReplay { get { return isReplay; } set { isReplay = value; } }51
 public bool IsDesign { get { return active != Action.None; } }
    public bool IsDesign { get { return active != Action.None; } }52

53
 public Env()
    public Env()54
 {
    {55
 stack = new Stack<Step>();
      stack = new Stack<Step>();56
 cfg = new ConfigFile();
      cfg = new ConfigFile();57
 db = new DataFile();
      db = new DataFile();58
 Init();
      Init();59
 }
    }60

61
 /// <summary>
    /// <summary>62
 /// 状态栏信息
    /// 状态栏信息63
 /// </summary>
    /// </summary>64
 public string StatusMessage
    public string StatusMessage65
 {
    {66
 get
      get67
 {
      {68
 return HasError ? "请点击“菜单 -> 帮助 -> 错误信息”" : string.Format(
        return HasError ? "请点击“菜单 -> 帮助 -> 错误信息”" : string.Format(69
 "{0} {1}/{2} {3} {4} {5} [{6}] {7}",
          "{0} {1}/{2} {3} {4} {5} [{6}] {7}",70
 (active == Action.Create) ? '+' : (active == Action.Edit) ? '=' : isReplay ? "|/-\\"[stack.Count % 4] : '>',
          (active == Action.Create) ? '+' : (active == Action.Edit) ? '=' : isReplay ? "|/-\\"[stack.Count % 4] : '>',71
 Level + 1, MaxLevel, Pub.ToString(LevelSize),
          Level + 1, MaxLevel, Pub.ToString(LevelSize),72
 IsDesign ? string.Format("{0}={1}", db.Boxs, db.Slots) : string.Format("{0}/{1}", db.Boths, db.Tasks),
          IsDesign ? string.Format("{0}={1}", db.Boxs, db.Slots) : string.Format("{0}/{1}", db.Boths, db.Tasks),73
 IsDesign ? Block.GetPenName(pen) : string.Format("{0}({1})", stack.Count, pushSteps),
          IsDesign ? Block.GetPenName(pen) : string.Format("{0}({1})", stack.Count, pushSteps),74
 IsDesign ? (active == Action.Create ? "新建" : "编辑") : db.IsFinished ?
          IsDesign ? (active == Action.Create ? "新建" : "编辑") : db.IsFinished ? 75
 string.Format("{0}({1})", db.MovedSteps, db.PushedSteps) : string.Empty,
          string.Format("{0}({1})", db.MovedSteps, db.PushedSteps) : string.Empty,76
 db.GroupName);
          db.GroupName);77
 }
      }78
 }
    }79

80
 public void Dispose()
    public void Dispose()81
 {
    {82
 db.Dispose();
      db.Dispose();83
 }
    }84

85
 public void Init()
    public void Init()86
 {
    {87
 active = Action.None;
      active = Action.None;88
 pen = Block.Land;
      pen = Block.Land;89
 stack.Clear();
      stack.Clear();90
 SetExceptionMessage(null);
      SetExceptionMessage(null);91
 }
    }92

93
 void SetExceptionMessage(Exception ex)
    void SetExceptionMessage(Exception ex)94
 {
    {95
 errorMsg = Pub.GetMessage(ex, false);
      errorMsg = Pub.GetMessage(ex, false);96
 debugMsg = Pub.GetMessage(ex, true);
      debugMsg = Pub.GetMessage(ex, true);97
 }
    }98

99
 /// <summary>
    /// <summary>100
 /// 计算当使用标准箱子尺寸时主窗体客户区的尺寸
    /// 计算当使用标准箱子尺寸时主窗体客户区的尺寸101
 /// </summary>
    /// </summary>102
 /// <param name="statusBarHeight">状态条的高度</param>
    /// <param name="statusBarHeight">状态条的高度</param>103
 /// <returns>客户区的尺寸</returns>
    /// <returns>客户区的尺寸</returns>104
 public Size GetClientSize(int statusBarHeight)
    public Size GetClientSize(int statusBarHeight)105
 {
    {106
 int width = (Properties.Resources.PushBox24.Width / 8) * LevelSize.Width;
      int width = (Properties.Resources.PushBox24.Width / 8) * LevelSize.Width;107
 int height = Properties.Resources.PushBox24.Height * LevelSize.Height + statusBarHeight;
      int height = Properties.Resources.PushBox24.Height * LevelSize.Height + statusBarHeight;108
 if (width < 240) width = 240;
      if (width < 240) width = 240;109
 if (height < 48) height = 48;
      if (height < 48) height = 48;110
 if (width > 1008) width = 1008;
      if (width > 1008) width = 1008;111
 if (height > 672) height = 672;
      if (height > 672) height = 672;112
 return new Size(width, height);
      return new Size(width, height);113
 }
    }114

115
 /// <summary>
    /// <summary>116
 /// 根据客户区尺寸,计算箱子的尺寸,并相应设定要显示的图形单元
    /// 根据客户区尺寸,计算箱子的尺寸,并相应设定要显示的图形单元117
 /// </summary>
    /// </summary>118
 public void SetBoxInfo()
    public void SetBoxInfo()119
 {
    {120
 if (HasError) return;
      if (HasError) return;121
 if (LevelSize.IsEmpty) return;
      if (LevelSize.IsEmpty) return;122
 int rX = clientSize.Width / LevelSize.Width;
      int rX = clientSize.Width / LevelSize.Width;123
 int rY = clientSize.Height / LevelSize.Height;
      int rY = clientSize.Height / LevelSize.Height;124
 int r = Math.Min(rX, rY);
      int r = Math.Min(rX, rY);125
 if (r >= 24) img = Properties.Resources.PushBox24;
      if (r >= 24) img = Properties.Resources.PushBox24;126
 else if (r >= 20) img = Properties.Resources.PushBox20;
      else if (r >= 20) img = Properties.Resources.PushBox20;127
 else if (r >= 16) img = Properties.Resources.PushBox16;
      else if (r >= 16) img = Properties.Resources.PushBox16;128
 else img = Properties.Resources.PushBox12;
      else img = Properties.Resources.PushBox12;129
 boxSize = new Size(img.Height, img.Width / 8);
      boxSize = new Size(img.Height, img.Width / 8);130
 }
    }131

132
 /// <summary>
    /// <summary>133
 /// 装入配置文件
    /// 装入配置文件134
 /// </summary>
    /// </summary>135
 public void LoadConfig()
    public void LoadConfig()136
 {
    {137
 if (HasError) return;
      if (HasError) return;138
 try
      try139
 {
      {140
 cfg.LoadConfig();
        cfg.LoadConfig();141
 }
      }142
 catch (Exception ex)
      catch (Exception ex)143
 {
      {144
 SetExceptionMessage(ex);
        SetExceptionMessage(ex);145
 }
      }146
 }
    }147

148
 /// <summary>
    /// <summary>149
 /// 保存组信息到配置文件
    /// 保存组信息到配置文件150
 /// </summary>
    /// </summary>151
 /// <param name="groups">组信息</param>
    /// <param name="groups">组信息</param>152
 public void SaveConfig(string[] groups)
    public void SaveConfig(string[] groups)153
 {
    {154
 if (HasError) return;
      if (HasError) return;155
 try
      try156
 {
      {157
 cfg.SaveConfig(groups);
        cfg.SaveConfig(groups);158
 }
      }159
 catch (Exception ex)
      catch (Exception ex)160
 {
      {161
 SetExceptionMessage(ex);
        SetExceptionMessage(ex);162
 }
      }163
 }
    }164

165
 /// <summary>
    /// <summary>166
 /// 保存当前选项及当前走法到配置文件
    /// 保存当前选项及当前走法到配置文件167
 /// </summary>
    /// </summary>168
 public void SaveConfig()
    public void SaveConfig()169
 {
    {170
 if (HasError) return;
      if (HasError) return;171
 try
      try172
 {
      {173
 cfg.SaveConfig(stack.ToArray());
        cfg.SaveConfig(stack.ToArray());174
 }
      }175
 catch (Exception ex)
      catch (Exception ex)176
 {
      {177
 SetExceptionMessage(ex);
        SetExceptionMessage(ex);178
 }
      }179
 }
    }180

181
 /// <summary>
    /// <summary>182
 /// 装入当前组信息
    /// 装入当前组信息183
 /// </summary>
    /// </summary>184
 public void LoadGroup()
    public void LoadGroup()185
 {
    {186
 if (HasError) return;
      if (HasError) return;187
 try
      try188
 {
      {189
 db.LoadGroup(Groups[Group]);
        db.LoadGroup(Groups[Group]);190
 }
      }191
 catch (Exception ex)
      catch (Exception ex)192
 {
      {193
 SetExceptionMessage(ex);
        SetExceptionMessage(ex);194
 }
      }195
 }
    }196

197
 /// <summary>
    /// <summary>198
 /// 装入当前关信息
    /// 装入当前关信息199
 /// </summary>
    /// </summary>200
 public void LoadLevel()
    public void LoadLevel()201
 {
    {202
 active = Action.None;
      active = Action.None;203
 if (HasError) return;
      if (HasError) return;204
 try
      try205
 {
      {206
 db.LoadLevel(Level);
        db.LoadLevel(Level);207
 worker = db.Worker;
        worker = db.Worker;208
 stack.Clear();
        stack.Clear();209
 pushSteps = 0;
        pushSteps = 0;210
 isReplay = false;
        isReplay = false;211
 }
      }212
 catch (Exception ex)
      catch (Exception ex)213
 {
      {214
 SetExceptionMessage(ex);
        SetExceptionMessage(ex);215
 }
      }216
 }
    }217

218
 /// <summary>
    /// <summary>219
 /// 新建一关
    /// 新建一关220
 /// </summary>
    /// </summary>221
 /// <param name="isCopy">是否复制当前关</param>
    /// <param name="isCopy">是否复制当前关</param>222
 /// <param name="size">新建关的尺寸</param>
    /// <param name="size">新建关的尺寸</param>223
 public void NewLevel(bool isCopy, Size size)
    public void NewLevel(bool isCopy, Size size)224
 {
    {225
 if (HasError) return;
      if (HasError) return;226
 try
      try227
 {
      {228
 levelOem = Level;
        levelOem = Level;229
 Level = MaxLevel;
        Level = MaxLevel;230
 db.NewLevel(isCopy, size);
        db.NewLevel(isCopy, size);231
 }
      }232
 catch (Exception ex)
      catch (Exception ex)233
 {
      {234
 SetExceptionMessage(ex);
        SetExceptionMessage(ex);235
 }
      }236
 }
    }237

238
 /// <summary>
    /// <summary>239
 /// 给出通关步骤
    /// 给出通关步骤240
 /// </summary>
    /// </summary>241
 /// <returns>通关步骤</returns>
    /// <returns>通关步骤</returns>242
 public string GetSteps()
    public string GetSteps()243
 {
    {244
 string steps = "";
      string steps = "";245
 if (!HasError)
      if (!HasError)246
 {
      {247
 try
        try248
 {
        {249
 steps = db.GetSteps(Level);
          steps = db.GetSteps(Level);250
 }
        }251
 catch (Exception ex)
        catch (Exception ex)252
 {
        {253
 SetExceptionMessage(ex);
          SetExceptionMessage(ex);254
 }
        }255
 }
      }256
 return steps;
      return steps;257
 }
    }258

259
 /// <summary>
    /// <summary>260
 /// 记录通关步骤
    /// 记录通关步骤261
 /// </summary>
    /// </summary>262
 public void Record()
    public void Record()263
 {
    {264
 if (HasError) return;
      if (HasError) return;265
 try
      try266
 {
      {267
 db.SaveLevel(Level, stack.ToArray(), pushSteps);
        db.SaveLevel(Level, stack.ToArray(), pushSteps);268
 }
      }269
 catch (Exception ex)
      catch (Exception ex)270
 {
      {271
 SetExceptionMessage(ex);
        SetExceptionMessage(ex);272
 }
      }273
 }
    }274

275
 /// <summary>
    /// <summary>276
 /// 保存设计数据
    /// 保存设计数据277
 /// </summary>
    /// </summary>278
 public void SaveDesign()
    public void SaveDesign()279
 {
    {280
 if (HasError) return;
      if (HasError) return;281
 try
      try282
 {
      {283
 db.SaveDesign(active == Action.Create, Level);
        db.SaveDesign(active == Action.Create, Level);284
 }
      }285
 catch (Exception ex)
      catch (Exception ex)286
 {
      {287
 SetExceptionMessage(ex);
        SetExceptionMessage(ex);288
 }
      }289
 }
    }290

291
 /// <summary>
    /// <summary>292
 /// 删除最后一关
    /// 删除最后一关293
 /// </summary>
    /// </summary>294
 public void DeleteLastLevel()
    public void DeleteLastLevel()295
 {
    {296
 if (HasError) return;
      if (HasError) return;297
 try
      try298
 {
      {299
 db.DeleteLastLevel(Level);
        db.DeleteLastLevel(Level);300
 }
      }301
 catch (Exception ex)
      catch (Exception ex)302
 {
      {303
 SetExceptionMessage(ex);
        SetExceptionMessage(ex);304
 }
      }305
 }
    }306

307
 /// <summary>
    /// <summary>308
 /// 更新主窗体客户区
    /// 更新主窗体客户区309
 /// </summary>
    /// </summary>310
 /// <param name="dc">画布</param>
    /// <param name="dc">画布</param>311
 /// <param name="rectangle">要在其中绘画的矩形</param>
    /// <param name="rectangle">要在其中绘画的矩形</param>312
 public void Draw(Graphics dc, Rectangle rectangle)
    public void Draw(Graphics dc, Rectangle rectangle)313
 {
    {314
 if (HasError) return;
      if (HasError) return;315
 Rectangle box = PixelToBox(rectangle);
      Rectangle box = PixelToBox(rectangle);316
 Rectangle box2 = new Rectangle(box.Left, box.Top, box.Width + 1, box.Height + 1);
      Rectangle box2 = new Rectangle(box.Left, box.Top, box.Width + 1, box.Height + 1);317
 for (int i = 1; i <= LevelSize.Height; i++)
      for (int i = 1; i <= LevelSize.Height; i++)318
 {
      {319
 for (int j = 1; j <= LevelSize.Width; j++)
        for (int j = 1; j <= LevelSize.Width; j++)320
 {
        {321
 if (!box2.Contains(j, i)) continue;
          if (!box2.Contains(j, i)) continue;322
 DrawBox(dc, j, i);
          DrawBox(dc, j, i);323
 }
        }324
 }
      }325
 }
    }326

327
 /// <summary>
    /// <summary>328
 /// 绘制一个单元格
    /// 绘制一个单元格329
 /// </summary>
    /// </summary>330
 /// <param name="dc">画布</param>
    /// <param name="dc">画布</param>331
 /// <param name="x">单元格的横坐标</param>
    /// <param name="x">单元格的横坐标</param>332
 /// <param name="y">单元格的纵坐标</param>
    /// <param name="y">单元格的纵坐标</param>333
 void DrawBox(Graphics dc, int x, int y)
    void DrawBox(Graphics dc, int x, int y)334
 {
    {335
 DrawBox(dc, db.Map[y, x], (x - 1) * boxSize.Width, (y - 1) * boxSize.Height);
      DrawBox(dc, db.Map[y, x], (x - 1) * boxSize.Width, (y - 1) * boxSize.Height);336
 }
    }337

338
 /// <summary>
    /// <summary>339
 /// 绘制一个单元格
    /// 绘制一个单元格340
 /// </summary>
    /// </summary>341
 /// <param name="dc">画布</param>
    /// <param name="dc">画布</param>342
 /// <param name="idx">单元格的类型: 地 槽 墙 砖 箱子 工人</param>
    /// <param name="idx">单元格的类型: 地 槽 墙 砖 箱子 工人</param>343
 /// <param name="x">单元格的横坐标</param>
    /// <param name="x">单元格的横坐标</param>344
 /// <param name="y">单元格的纵坐标</param>
    /// <param name="y">单元格的纵坐标</param>345
 void DrawBox(Graphics dc, int idx, int x, int y)
    void DrawBox(Graphics dc, int idx, int x, int y)346
 {
    {347
 dc.DrawImage(img, x, y, new Rectangle(idx * boxSize.Width, 0, boxSize.Width, boxSize.Height), GraphicsUnit.Pixel);
      dc.DrawImage(img, x, y, new Rectangle(idx * boxSize.Width, 0, boxSize.Width, boxSize.Height), GraphicsUnit.Pixel);348
 }
    }349

350
 /// <summary>
    /// <summary>351
 /// 将单元格换算为像素
    /// 将单元格换算为像素352
 /// </summary>
    /// </summary>353
 /// <param name="box">单元格矩形</param>
    /// <param name="box">单元格矩形</param>354
 /// <returns>像素矩形</returns>
    /// <returns>像素矩形</returns>355
 Rectangle BoxToPixel(Rectangle box)
    Rectangle BoxToPixel(Rectangle box)356
 {
    {357
 return new Rectangle((box.Left - 1) * boxSize.Width, (box.Top - 1) * boxSize.Height,
      return new Rectangle((box.Left - 1) * boxSize.Width, (box.Top - 1) * boxSize.Height,358
 (box.Width + 1) * boxSize.Width, (box.Height + 1) * boxSize.Height);
        (box.Width + 1) * boxSize.Width, (box.Height + 1) * boxSize.Height);359
 }
    }360

361
 /// <summary>
    /// <summary>362
 /// 将像素换算为单元格
    /// 将像素换算为单元格363
 /// </summary>
    /// </summary>364
 /// <param name="pixel">像素矩形</param>
    /// <param name="pixel">像素矩形</param>365
 /// <returns>单元格矩形</returns>
    /// <returns>单元格矩形</returns>366
 Rectangle PixelToBox(Rectangle pixel)
    Rectangle PixelToBox(Rectangle pixel)367
 {
    {368
 int x0 = pixel.Left / boxSize.Width + 1;
      int x0 = pixel.Left / boxSize.Width + 1;369
 int y0 = pixel.Top / boxSize.Height + 1;
      int y0 = pixel.Top / boxSize.Height + 1;370
 int x1 = (pixel.Right - 1) / boxSize.Width + 1;
      int x1 = (pixel.Right - 1) / boxSize.Width + 1;371
 int y1 = (pixel.Bottom - 1) / boxSize.Height + 1;
      int y1 = (pixel.Bottom - 1) / boxSize.Height + 1;372
 return new Rectangle(x0, y0, x1 - x0, y1 - y0);
      return new Rectangle(x0, y0, x1 - x0, y1 - y0);373
 }
    }374

375
 /// <summary>
    /// <summary>376
 /// 根据指定的对角顶点创建矩形
    /// 根据指定的对角顶点创建矩形377
 /// </summary>
    /// </summary>378
 /// <param name="a">顶点</param>
    /// <param name="a">顶点</param>379
 /// <param name="b">对角的顶点</param>
    /// <param name="b">对角的顶点</param>380
 /// <returns>所需要的矩形</returns>
    /// <returns>所需要的矩形</returns>381
 Rectangle GetRectangle(Point a, Point b)
    Rectangle GetRectangle(Point a, Point b)382
 {
    {383
 return Rectangle.FromLTRB(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Max(a.X, b.X), Math.Max(a.Y, b.Y));
      return Rectangle.FromLTRB(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Max(a.X, b.X), Math.Max(a.Y, b.Y));384
 }
    }385

386
 /// <summary>
    /// <summary>387
 /// 设计模式下,当鼠标点击时要采取的动作
    /// 设计模式下,当鼠标点击时要采取的动作388
 /// </summary>
    /// </summary>389
 /// <param name="invalid">输出:要重绘的区域</param>
    /// <param name="invalid">输出:要重绘的区域</param>390
 /// <returns>是否发生动作</returns>
    /// <returns>是否发生动作</returns>391
 public bool Design(out Rectangle invalid)
    public bool Design(out Rectangle invalid)392
 {
    {393
 invalid = Rectangle.Empty;
      invalid = Rectangle.Empty;394
 Point to;
      Point to;395
 if (!ValidClick(out to)) return false;
      if (!ValidClick(out to)) return false;396
 db.UpdateCounts(to.X, to.Y, false);
      db.UpdateCounts(to.X, to.Y, false);397
 Block.Update(ref db.Map[to.Y, to.X], pen);
      Block.Update(ref db.Map[to.Y, to.X], pen);398
 db.UpdateCounts(to.X, to.Y, true);
      db.UpdateCounts(to.X, to.Y, true);399
 if (pen == Block.Man0 && HasWorker) pen = Block.Box0;
      if (pen == Block.Man0 && HasWorker) pen = Block.Box0;400
 invalid = BoxToPixel(GetRectangle(to, to));
      invalid = BoxToPixel(GetRectangle(to, to));401
 return true;
      return true;402
 }
    }403

404
 /// <summary>
    /// <summary>405
 /// 工人往指定方向前进一步(可能推着箱子)
    /// 工人往指定方向前进一步(可能推着箱子)406
 /// </summary>
    /// </summary>407
 /// <param name="dir">前进的方向</param>
    /// <param name="dir">前进的方向</param>408
 /// <param name="isStop">“撤销”时是否停留</param>
    /// <param name="isStop">“撤销”时是否停留</param>409
 /// <param name="invalid">输出:要重绘的区域</param>
    /// <param name="invalid">输出:要重绘的区域</param>410
 /// <returns>是否成功</returns>
    /// <returns>是否成功</returns>411
 public bool StepIt(Direction dir, bool isStop, out Rectangle invalid)
    public bool StepIt(Direction dir, bool isStop, out Rectangle invalid)412
 {
    {413
 invalid = Rectangle.Empty;
      invalid = Rectangle.Empty;414
 if (HasError) return false;
      if (HasError) return false;415
 if (Direction.None == dir) return false;
      if (Direction.None == dir) return false;416
 Point p1 = worker; // 工人前进方向一步的位置
      Point p1 = worker; // 工人前进方向一步的位置417
 Point p2 = worker; // 箱子前进方向一步的位置
      Point p2 = worker; // 箱子前进方向一步的位置418
 switch (dir)
      switch (dir)419
 {
      {420
 case Direction.East: p1.X++; p2.X += 2; break;
        case Direction.East: p1.X++; p2.X += 2; break;421
 case Direction.South: p1.Y++; p2.Y += 2; break;
        case Direction.South: p1.Y++; p2.Y += 2; break;422
 case Direction.West: p1.X--; p2.X -= 2; break;
        case Direction.West: p1.X--; p2.X -= 2; break;423
 case Direction.North: p1.Y--; p2.Y -= 2; break;
        case Direction.North: p1.Y--; p2.Y -= 2; break;424
 }
      }425
 byte b1 = db.Map[p1.Y, p1.X]; // 工人前进方向一步位置上的东西
      byte b1 = db.Map[p1.Y, p1.X]; // 工人前进方向一步位置上的东西426
 bool isBox = Block.IsBox(b1); // 是否推着箱子前进
      bool isBox = Block.IsBox(b1); // 是否推着箱子前进427
 if (!isBox && !Block.IsBlank(b1)) return false; // 如果没有推着箱子且前方不是空地则失败
      if (!isBox && !Block.IsBlank(b1)) return false; // 如果没有推着箱子且前方不是空地则失败428
 if (isBox && !Block.IsBlank(db.Map[p2.Y, p2.X])) return false; // 如果推着箱子且箱子前方不是空地则失败
      if (isBox && !Block.IsBlank(db.Map[p2.Y, p2.X])) return false; // 如果推着箱子且箱子前方不是空地则失败429
 invalid = BoxToPixel(GetRectangle(worker, isBox ? p2 : p1)); // 要重绘的区域
      invalid = BoxToPixel(GetRectangle(worker, isBox ? p2 : p1)); // 要重绘的区域430
 stack.Push(new Step(dir, isBox, isStop)); // 记录走法步骤
      stack.Push(new Step(dir, isBox, isStop)); // 记录走法步骤431
 Block.ManOut(ref db.Map[worker.Y, worker.X]); // 工人离开当前位置
      Block.ManOut(ref db.Map[worker.Y, worker.X]); // 工人离开当前位置432
 Block.ManIn(ref db.Map[p1.Y, p1.X]); // 工人进入前方位置
      Block.ManIn(ref db.Map[p1.Y, p1.X]); // 工人进入前方位置433
 if (isBox)
      if (isBox)434
 {
      {435
 pushSteps++; // 更新推箱子步数
        pushSteps++; // 更新推箱子步数436
 db.Boths += (db.Map[p2.Y, p2.X] - Block.Land) - (b1 - Block.Box0); // 更新已完成任务数
        db.Boths += (db.Map[p2.Y, p2.X] - Block.Land) - (b1 - Block.Box0); // 更新已完成任务数437
 Block.BoxOut(ref db.Map[p1.Y, p1.X]); // 箱子离开当前位置
        Block.BoxOut(ref db.Map[p1.Y, p1.X]); // 箱子离开当前位置438
 Block.BoxIn(ref db.Map[p2.Y, p2.X]); // 箱子进入前方位置
        Block.BoxIn(ref db.Map[p2.Y, p2.X]); // 箱子进入前方位置439
 }
      }440
 worker = p1; // 更新工人位置
      worker = p1; // 更新工人位置441
 return true; // 工人成功前进一步(可能推着条子)
      return true; // 工人成功前进一步(可能推着条子)442
 }
    }443

444
 /// <summary>
    /// <summary>445
 /// 工人后退一步(可能连带箱子一起后退)
    /// 工人后退一步(可能连带箱子一起后退)446
 /// </summary>
    /// </summary>447
 /// <param name="invalid">输出:要重绘的区域</param>
    /// <param name="invalid">输出:要重绘的区域</param>448
 /// <returns>是否完成“撤消”</returns>
    /// <returns>是否完成“撤消”</returns>449
 public bool Back(out Rectangle invalid)
    public bool Back(out Rectangle invalid)450
 {
    {451
 invalid = Rectangle.Empty;
      invalid = Rectangle.Empty;452
 if (HasError) return true;
      if (HasError) return true;453
 if (stack.Count == 0) return true;
      if (stack.Count == 0) return true;454
 Step step = stack.Pop(); // 当前步骤
      Step step = stack.Pop(); // 当前步骤455
 Point p1 = worker; // 工人后退方向一步的位置
      Point p1 = worker; // 工人后退方向一步的位置456
 Point p2 = worker; // 箱子的当前位置
      Point p2 = worker; // 箱子的当前位置457
 switch (step.Direct)
      switch (step.Direct)458
 {
      {459
 case Direction.East: p1.X--; p2.X++; break;
        case Direction.East: p1.X--; p2.X++; break;460
 case Direction.South: p1.Y--; p2.Y++; break;
        case Direction.South: p1.Y--; p2.Y++; break;461
 case Direction.West: p1.X++; p2.X--; break;
        case Direction.West: p1.X++; p2.X--; break;462
 case Direction.North: p1.Y++; p2.Y--; break;
        case Direction.North: p1.Y++; p2.Y--; break;463
 }
      }464
 invalid = BoxToPixel(GetRectangle(p1, step.IsBox ? p2 : worker)); // 要重绘的区域
      invalid = BoxToPixel(GetRectangle(p1, step.IsBox ? p2 : worker)); // 要重绘的区域465
 Block.ManOut(ref db.Map[worker.Y, worker.X]); // 工人离开当前位置
      Block.ManOut(ref db.Map[worker.Y, worker.X]); // 工人离开当前位置466
 Block.ManIn(ref db.Map[p1.Y, p1.X]); // 工人进入后退方向一步的位置
      Block.ManIn(ref db.Map[p1.Y, p1.X]); // 工人进入后退方向一步的位置467
 if (step.IsBox)
      if (step.IsBox)468
 {
      {469
 Block.BoxOut(ref db.Map[p2.Y, p2.X]); // 箱子离开当前位置
        Block.BoxOut(ref db.Map[p2.Y, p2.X]); // 箱子离开当前位置470
 Block.BoxIn(ref db.Map[worker.Y, worker.X]); // 箱子进入工人原来的位置
        Block.BoxIn(ref db.Map[worker.Y, worker.X]); // 箱子进入工人原来的位置471
 db.Boths += (db.Map[worker.Y, worker.X] - Block.Box0) - (db.Map[p2.Y, p2.X] - Block.Land); // 更新已完成任务数
        db.Boths += (db.Map[worker.Y, worker.X] - Block.Box0) - (db.Map[p2.Y, p2.X] - Block.Land); // 更新已完成任务数472
 pushSteps--; // 更新推箱子步数
        pushSteps--; // 更新推箱子步数473
 }
      }474
 worker = p1; // 更新工人位置
      worker = p1; // 更新工人位置475
 return step.IsStop; // 是否完成“撤消”
      return step.IsStop; // 是否完成“撤消”476
 }
    }477

478
 /// <summary>
    /// <summary>479
 /// 寻找一条将工人移动到鼠标点击的位置的路线
    /// 寻找一条将工人移动到鼠标点击的位置的路线480
 /// </summary>
    /// </summary>481
 /// <returns>移动的路线</returns>
    /// <returns>移动的路线</returns>482
 public Queue<Direction> GetMoveInfo()
    public Queue<Direction> GetMoveInfo()483
 {
    {484
 Point to;
      Point to;485
 if (!CanTo(out to)) return null;
      if (!CanTo(out to)) return null;486
 return FindPath.Seek(db.Map, worker, to);
      return FindPath.Seek(db.Map, worker, to);487
 }
    }488

489
 /// <summary>
    /// <summary>490
 /// 给出将箱子推动到鼠标点击的位置所需的信息
    /// 给出将箱子推动到鼠标点击的位置所需的信息491
 /// </summary>
    /// </summary>492
 /// <param name="dir">输出:工人移动的方向</param>
    /// <param name="dir">输出:工人移动的方向</param>493
 /// <returns>工人移动的步数</returns>
    /// <returns>工人移动的步数</returns>494
 public int GetPushInfo(out Direction dir)
    public int GetPushInfo(out Direction dir)495
 {
    {496
 dir = Direction.None;
      dir = Direction.None;497
 if (HasError) return 0;
      if (HasError) return 0;498
 Point to; // 目的地
      Point to; // 目的地499
 if (!CanTo(out to)) return 0; // 无效的目的地
      if (!CanTo(out to)) return 0; // 无效的目的地500
 if (to.Y != worker.Y && to.X != worker.X) return 0; // 目的地和工人不在同一条直线上
      if (to.Y != worker.Y && to.X != worker.X) return 0; // 目的地和工人不在同一条直线上501
 int z0 = (to.Y == worker.Y) ? worker.X : worker.Y;
      int z0 = (to.Y == worker.Y) ? worker.X : worker.Y;502
 int z9 = (to.Y == worker.Y) ? to.X : to.Y;
      int z9 = (to.Y == worker.Y) ? to.X : to.Y;503
 if (to.Y == worker.Y) dir = (z9 > z0) ? Direction.East : Direction.West;
      if (to.Y == worker.Y) dir = (z9 > z0) ? Direction.East : Direction.West;504
 else dir = (z9 > z0) ? Direction.South : Direction.North;
      else dir = (z9 > z0) ? Direction.South : Direction.North;505
 int i0 = Math.Min(z9, z0);
      int i0 = Math.Min(z9, z0);506
 int i9 = Math.Max(z9, z0);
      int i9 = Math.Max(z9, z0);507
 int steps = i9 - i0; // 目的地和工人之间的距离
      int steps = i9 - i0; // 目的地和工人之间的距离508
 int boxs = 0;
      int boxs = 0;509
 for (int i = i0 + 1; i < i9; i++)
      for (int i = i0 + 1; i < i9; i++)510
 {
      {511
 byte bi = (to.Y == worker.Y) ? db.Map[worker.Y, i] : db.Map[i, worker.X];
        byte bi = (to.Y == worker.Y) ? db.Map[worker.Y, i] : db.Map[i, worker.X];512
 if (Block.IsBox(bi)) boxs++; // 计算工人和目的地之间的箱子的个数
        if (Block.IsBox(bi)) boxs++; // 计算工人和目的地之间的箱子的个数513
 else if (!Block.IsBlank(bi)) boxs += 2; // “墙”和“砖”折算为两个箱子
        else if (!Block.IsBlank(bi)) boxs += 2; // “墙”和“砖”折算为两个箱子514
 }
      }515
 if (boxs > 1) return 0; // 最多只能推着一个箱子前进
      if (boxs > 1) return 0; // 最多只能推着一个箱子前进516
 return steps - boxs; // 工人移动的步数
      return steps - boxs; // 工人移动的步数517
 }
    }518

519
 /// <summary>
    /// <summary>520
 /// 检查鼠标点击位置是否可达, 并将像素换算为单元格
    /// 检查鼠标点击位置是否可达, 并将像素换算为单元格521
 /// </summary>
    /// </summary>522
 /// <param name="to">输出:换算后的位置</param>
    /// <param name="to">输出:换算后的位置</param>523
 /// <returns>是否可达</returns>
    /// <returns>是否可达</returns>524
 bool CanTo(out Point to)
    bool CanTo(out Point to)525
 {
    {526
 if (!ValidClick(out to)) return false;
      if (!ValidClick(out to)) return false;527
 if (!Block.IsMan(db.Map[worker.Y, worker.X])) throw new Exception("内部错误:工人的位置上不是工人");
      if (!Block.IsMan(db.Map[worker.Y, worker.X])) throw new Exception("内部错误:工人的位置上不是工人");528
 if (!Block.IsBlank(db.Map[to.Y, to.X])) return false; // 目的地必须是“地”或“槽”
      if (!Block.IsBlank(db.Map[to.Y, to.X])) return false; // 目的地必须是“地”或“槽”529
 if (to.Y == worker.Y && to.X == worker.X) return false; // 目的地不能是工人当前的位置
      if (to.Y == worker.Y && to.X == worker.X) return false; // 目的地不能是工人当前的位置530
 return true; // 目的地可达
      return true; // 目的地可达531
 }
    }532

533
 /// <summary>
    /// <summary>534
 /// 检查鼠标点击位置是否有效, 并将像素换算为单元格
    /// 检查鼠标点击位置是否有效, 并将像素换算为单元格535
 /// </summary>
    /// </summary>536
 /// <param name="to">输出:换算后的位置</param>
    /// <param name="to">输出:换算后的位置</param>537
 /// <returns>是否有效位置</returns>
    /// <returns>是否有效位置</returns>538
 bool ValidClick(out Point to)
    bool ValidClick(out Point to)539
 {
    {540
 to = Point.Empty;
      to = Point.Empty;541
 if (HasError) return false;
      if (HasError) return false;542
 to.Y = toPixel.Y / boxSize.Height + 1;
      to.Y = toPixel.Y / boxSize.Height + 1;543
 to.X = toPixel.X / boxSize.Width + 1;
      to.X = toPixel.X / boxSize.Width + 1;544
 if (toPixel.X >= boxSize.Width * LevelSize.Width || toPixel.Y >= boxSize.Height * LevelSize.Height)
      if (toPixel.X >= boxSize.Width * LevelSize.Width || toPixel.Y >= boxSize.Height * LevelSize.Height)545
 return false; // 目的地超出当前关的有效范围
        return false; // 目的地超出当前关的有效范围546
 return true; // 目的地有效
      return true; // 目的地有效547
 }
    }548
 }
  }549
 }
}550

上一篇:使用 C# 开发智能手机软件:推箱子(十)
下一篇:使用 C# 开发智能手机软件:推箱子(十二)
返回目录
 
                    
                     
                    
                 
                    
                 

 
        

 
   
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号