Fix crash in new on_close handler.
[mp-5.x.git] / mp_toys.mpsl
blob262920bc0d07982c0588392adb7a9477020cef51
1 /*
3     Minimum Profit 5.x
4     A Programmer's Text Editor
6     Useless things.
8     Copyright (C) 1991-2012 Angel Ortega <angel@triptico.com>
10     This program is free software; you can redistribute it and/or
11     modify it under the terms of the GNU General Public License
12     as published by the Free Software Foundation; either version 2
13     of the License, or (at your option) any later version.
15     This program is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18     GNU General Public License for more details.
20     You should have received a copy of the GNU General Public License
21     along with this program; if not, write to the Free Software
22     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24     http://www.triptico.com
28 sub mp.maze(doc, keycode)
30         local tx = mp.window.tx;
31         local ty = mp.window.ty;
33         if (doc == NULL)
34                 doc = mp.open('<maze>');
36         local maze = doc.maze;
38         if (maze == NULL || maze.tx != tx || maze.ty != ty) {
39                 /* new or changed: rebuild everything */
40                 maze = {};
41                 maze.tx = tx;
42                 maze.ty = ty;
43                 doc.maze = maze;
45                 /* ensure odd size */
46                 if (!(tx & 1))
47                         tx--;
48                 if (!(ty & 1))
49                         ty--;
51                 /* init */
52                 maze.map = map(
53             [1 .. ty],
54             sub {
55                 map(
56                     [1 .. tx],
57                     sub { '#'; }
58                 );
59             }
60         );
62                 /* build */
63                 local x = 1;
64                 local y = 1;
65                 local stack = [];
67                 while (1) {
68                         local d = [];
70                         /* clear */
71                         maze.map[y][x] = ' ';
73                         foreach (t, [[0, -1], [1, 0], [0, 1], [-1, 0]]) {
74                                 /* can space be opened? */
75                                 local ny = y + t[0] * 2;
76                                 local nx = x + t[1] * 2;
78                                 if (nx > 0 && ny > 0 && maze.map[ny][nx] eq '#')
79                                         push(d, t);
80                         }
82                         if (size(d)) {
83                                 /* more than one way? stack this position */
84                                 if (size(d) > 1)
85                                         push(stack, [y, x]);
87                                 /* pick one direction at random and move there */
88                                 local m = d[random(size(d))];
90                                 y += m[0];
91                                 x += m[1];
93                                 maze.map[y][x] = ' ';
95                                 y += m[0];
96                                 x += m[1];
97                         }
98                         else {
99                                 /* no way from here: pop previous position */
100                                 if ((d = pop(stack)) == NULL)
101                                         break;
103                                 y = d[0];
104                                 x = d[1];
105                         }
106                 }
108                 maze.x = 1 + random(tx / 2) * 2;
109                 maze.y = 1 + random(ty / 2) * 2;
111                 maze.map[maze.y][maze.x] = '@';
113                 x = 1 + random(tx / 2) * 2;
114                 y = 1 + random(ty / 2) * 2;
116                 maze.map[y][x] = 'X';
118                 doc.keypress = sub(d, k) { mp.maze(d, k); };
120                 doc.paint = sub(d) { map(d.maze.map, sub(e) { [8, join(e)];}); };
121         }
123         local x = maze.x;
124         local y = maze.y;
126         maze.map[y][x] = ' ';
128         if (keycode eq 'cursor-up')
129                 y--;
130         if (keycode eq 'cursor-down')
131                 y++;
132         if (keycode eq 'cursor-left')
133                 x--;
134         if (keycode eq 'cursor-right')
135                 x++;
137         if (maze.map[y][x] eq 'X') {
138                 mp.alert(L("You won!"));
139                 doc.maze = NULL;
140                 return mp.maze(doc);
141         }
143         if (maze.map[y][x] eq ' ') {
144                 maze.x = x;
145                 maze.y = y;
146         }
148         maze.map[maze.y][maze.x] = '@';
150     mp.undo(doc);
152         return doc;
156 sub mp.pipes(doc, keycode)
158     local tx = mp.window.tx;
159     local ty = mp.window.ty;
161     if (doc == NULL)
162         doc = mp.open('<pipes>');
164     local pipes = doc.pipes;
166     if (pipes == NULL || pipes.you_won || pipes.tx != tx || pipes.ty != ty) {
167         local wx = int((tx - 2) / 3);
168         local wy = int(((ty - 1) / 3) - 1);
170         pipes = {
171             x:      0,
172             y:      1,
173             tx:     tx,
174             ty:     ty,
175             wx:     wx,
176             wy:     wy,
177             sx:     random(wx),
178             dir:    [[0, -1], [1, 0], [0, 1], [-1, 0]],
179             pieces: [
180                 {
181                     shape:  [' # ', ' ##', '   '],
182                     out:    [-1, -1, 1, 0],
183                     next:   1
184                 },
185                 {
186                     shape:  ['   ', ' ##', ' # '],
187                     out:    [1, -1, -1, 2],
188                     next:   2
189                 },
190                 {
191                     shape:  ['   ', '## ', ' # '],
192                     out:    [3, 2, -1, -1],
193                     next:   3
194                 },
195                 {
196                     shape:  [' # ', '## ', '   '],
197                     out:    [-1, 0, 3, -1],
198                     next:   0
199                 },
200                 {
201                     shape:  [' # ', ' # ', ' # '],
202                     out:    [0, -1, 2, -1],
203                     next:   5
204                 },
205                 {
206                     shape:  ['   ', '###', '   '],
207                     out:    [-1, 1, -1, 3],
208                     next:   4
209                 },
210                 { /* blank */
211                     shape:  ['   ', '   ', '   '],
212                     out:    [-1, -1, -1, -1],
213                     next:   6
214                 },
215                 { /* entry pipe */
216                     shape:  ['| |', '\ /', ' # '],
217                     out:    [-1, -1, 2, -1],
218                     next:   7
219                 },
220                 { /* out pipe */
221                     shape:  [' # ', ' # ', '/ \'],
222                     out:    [-1, -1, -1000, -1],
223                     next:   8
224                 }
225             ]
226         };
228         pipes.map = map([1 .. wy - 1],
229             sub {map([1 .. wx], sub { random(size(pipes.pieces) - 3); });}
230         );
232         /* add first line */
233         ins(pipes.map, map([1 .. wx], sub { 6; }), 0);
234         pipes.map[0][pipes.sx] = 7;
236         /* add last line */
237         push(pipes.map, map([1 .. wx], sub { 6; }));
238         pipes.map[wy][5] = 8;
239         pipes.map[wy][wx - 5] = 8;
241         doc.pipes = pipes;
243         /* handlers */
244         doc.keypress    = sub (d, k) { mp.pipes(d, k); };
246         doc.paint       = sub (d) {
247             local out = [];
248             local pipes = d.pipes;
250             foreach (n, [0 .. pipes.wy]) {
251                 local l0 = [];
252                 local l1 = [];
253                 local l2 = [];
255                 foreach (m, [0 .. (pipes.wx - 1)]) {
256                     local p = pipes.map[n][m];
257                     local s = pipes.pieces[p].shape;
258                     local c;
260                     if (n == pipes.y && m == pipes.x)
261                         c = mp.colors.cursor.attr;
262                     else
263                         c = pipes.attr[n][m] || mp.colors.normal.attr;
265                     push(l0, c, s[0]);
266                     push(l1, c, s[1]);
267                     push(l2, c, s[2]);
268                 }
270                 push(out, l0, l1, l2);
271             }
273             if (pipes.you_won) {
274                 out[0] = [ mp.colors.cursor.attr, "You won! Press ENTER." ];
275             }
277             return out;
278         };
279     }
281         local x = pipes.x;
282         local y = pipes.y;
284         if (keycode eq 'cursor-up' && y > 1)
285                 y--;
286         if (keycode eq 'cursor-down'&& y < pipes.wy - 1)
287                 y++;
288         if (keycode eq 'cursor-left' && x > 0)
289                 x--;
290         if (keycode eq 'cursor-right' && x < pipes.wx - 1)
291                 x++;
292     if (keycode eq 'enter') {
293         local n = pipes.map[y][x];
295         pipes.map[y][x] = pipes.pieces[n].next;
296     }
298     pipes.x = x;
299     pipes.y = y;
301     /* reset attributes */
302     pipes.attr = map([0 .. pipes.wy], sub { [];});
304     /* the water input is blue */
305     pipes.attr[0][pipes.sx] = mp.colors.quotes.attr;
307     /* calculate the path */
308     local px = pipes.sx;
309     local py = 0;
310     local c = 2; /* downwards */
311     local p;
313     /* while there is a valid path */
314     while ((p = pipes.pieces[pipes.map[py][px]]) && (c = p.out[c]) >= 0) {
315         /* mark as valid path */
316         pipes.attr[py][px] = mp.colors.quotes.attr;
318         /* move in new direction */
319         px += pipes.dir[c][0];
320         py += pipes.dir[c][1];
321     }
323     if (c == -1000) {
324         pipes.attr[py][px] = mp.colors.quotes.attr;
325         pipes.you_won = 1;
326     }
328     mp.undo(doc);
330     return doc;