Codebase list pathological / d7a4fa8
Import Debian changes 1.1.3-15 pathological (1.1.3-15) unstable; urgency=medium * Team upload. * Switch to compat level 11. * Declare compliance with Debian Policy 4.1.4. * Move the package to Git and salsa.debian.org. * Remove deprecated menu file. Markus Koschany 5 years ago
169 changed file(s) with 4334 addition(s) and 13 deletion(s). Raw diff Collapse all Expand all
0 GNU GENERAL PUBLIC LICENSE
1 Version 2, June 1991
2
3 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
4 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
5 Everyone is permitted to copy and distribute verbatim copies
6 of this license document, but changing it is not allowed.
7
8 Preamble
9
10 The licenses for most software are designed to take away your
11 freedom to share and change it. By contrast, the GNU General Public
12 License is intended to guarantee your freedom to share and change free
13 software--to make sure the software is free for all its users. This
14 General Public License applies to most of the Free Software
15 Foundation's software and to any other program whose authors commit to
16 using it. (Some other Free Software Foundation software is covered by
17 the GNU Library General Public License instead.) You can apply it to
18 your programs, too.
19
20 When we speak of free software, we are referring to freedom, not
21 price. Our General Public Licenses are designed to make sure that you
22 have the freedom to distribute copies of free software (and charge for
23 this service if you wish), that you receive source code or can get it
24 if you want it, that you can change the software or use pieces of it
25 in new free programs; and that you know you can do these things.
26
27 To protect your rights, we need to make restrictions that forbid
28 anyone to deny you these rights or to ask you to surrender the rights.
29 These restrictions translate to certain responsibilities for you if you
30 distribute copies of the software, or if you modify it.
31
32 For example, if you distribute copies of such a program, whether
33 gratis or for a fee, you must give the recipients all the rights that
34 you have. You must make sure that they, too, receive or can get the
35 source code. And you must show them these terms so they know their
36 rights.
37
38 We protect your rights with two steps: (1) copyright the software, and
39 (2) offer you this license which gives you legal permission to copy,
40 distribute and/or modify the software.
41
42 Also, for each author's protection and ours, we want to make certain
43 that everyone understands that there is no warranty for this free
44 software. If the software is modified by someone else and passed on, we
45 want its recipients to know that what they have is not the original, so
46 that any problems introduced by others will not reflect on the original
47 authors' reputations.
48
49 Finally, any free program is threatened constantly by software
50 patents. We wish to avoid the danger that redistributors of a free
51 program will individually obtain patent licenses, in effect making the
52 program proprietary. To prevent this, we have made it clear that any
53 patent must be licensed for everyone's free use or not licensed at all.
54
55 The precise terms and conditions for copying, distribution and
56 modification follow.
57
58 GNU GENERAL PUBLIC LICENSE
59 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
60
61 0. This License applies to any program or other work which contains
62 a notice placed by the copyright holder saying it may be distributed
63 under the terms of this General Public License. The "Program", below,
64 refers to any such program or work, and a "work based on the Program"
65 means either the Program or any derivative work under copyright law:
66 that is to say, a work containing the Program or a portion of it,
67 either verbatim or with modifications and/or translated into another
68 language. (Hereinafter, translation is included without limitation in
69 the term "modification".) Each licensee is addressed as "you".
70
71 Activities other than copying, distribution and modification are not
72 covered by this License; they are outside its scope. The act of
73 running the Program is not restricted, and the output from the Program
74 is covered only if its contents constitute a work based on the
75 Program (independent of having been made by running the Program).
76 Whether that is true depends on what the Program does.
77
78 1. You may copy and distribute verbatim copies of the Program's
79 source code as you receive it, in any medium, provided that you
80 conspicuously and appropriately publish on each copy an appropriate
81 copyright notice and disclaimer of warranty; keep intact all the
82 notices that refer to this License and to the absence of any warranty;
83 and give any other recipients of the Program a copy of this License
84 along with the Program.
85
86 You may charge a fee for the physical act of transferring a copy, and
87 you may at your option offer warranty protection in exchange for a fee.
88
89 2. You may modify your copy or copies of the Program or any portion
90 of it, thus forming a work based on the Program, and copy and
91 distribute such modifications or work under the terms of Section 1
92 above, provided that you also meet all of these conditions:
93
94 a) You must cause the modified files to carry prominent notices
95 stating that you changed the files and the date of any change.
96
97 b) You must cause any work that you distribute or publish, that in
98 whole or in part contains or is derived from the Program or any
99 part thereof, to be licensed as a whole at no charge to all third
100 parties under the terms of this License.
101
102 c) If the modified program normally reads commands interactively
103 when run, you must cause it, when started running for such
104 interactive use in the most ordinary way, to print or display an
105 announcement including an appropriate copyright notice and a
106 notice that there is no warranty (or else, saying that you provide
107 a warranty) and that users may redistribute the program under
108 these conditions, and telling the user how to view a copy of this
109 License. (Exception: if the Program itself is interactive but
110 does not normally print such an announcement, your work based on
111 the Program is not required to print an announcement.)
112
113 These requirements apply to the modified work as a whole. If
114 identifiable sections of that work are not derived from the Program,
115 and can be reasonably considered independent and separate works in
116 themselves, then this License, and its terms, do not apply to those
117 sections when you distribute them as separate works. But when you
118 distribute the same sections as part of a whole which is a work based
119 on the Program, the distribution of the whole must be on the terms of
120 this License, whose permissions for other licensees extend to the
121 entire whole, and thus to each and every part regardless of who wrote it.
122
123 Thus, it is not the intent of this section to claim rights or contest
124 your rights to work written entirely by you; rather, the intent is to
125 exercise the right to control the distribution of derivative or
126 collective works based on the Program.
127
128 In addition, mere aggregation of another work not based on the Program
129 with the Program (or with a work based on the Program) on a volume of
130 a storage or distribution medium does not bring the other work under
131 the scope of this License.
132
133 3. You may copy and distribute the Program (or a work based on it,
134 under Section 2) in object code or executable form under the terms of
135 Sections 1 and 2 above provided that you also do one of the following:
136
137 a) Accompany it with the complete corresponding machine-readable
138 source code, which must be distributed under the terms of Sections
139 1 and 2 above on a medium customarily used for software interchange; or,
140
141 b) Accompany it with a written offer, valid for at least three
142 years, to give any third party, for a charge no more than your
143 cost of physically performing source distribution, a complete
144 machine-readable copy of the corresponding source code, to be
145 distributed under the terms of Sections 1 and 2 above on a medium
146 customarily used for software interchange; or,
147
148 c) Accompany it with the information you received as to the offer
149 to distribute corresponding source code. (This alternative is
150 allowed only for noncommercial distribution and only if you
151 received the program in object code or executable form with such
152 an offer, in accord with Subsection b above.)
153
154 The source code for a work means the preferred form of the work for
155 making modifications to it. For an executable work, complete source
156 code means all the source code for all modules it contains, plus any
157 associated interface definition files, plus the scripts used to
158 control compilation and installation of the executable. However, as a
159 special exception, the source code distributed need not include
160 anything that is normally distributed (in either source or binary
161 form) with the major components (compiler, kernel, and so on) of the
162 operating system on which the executable runs, unless that component
163 itself accompanies the executable.
164
165 If distribution of executable or object code is made by offering
166 access to copy from a designated place, then offering equivalent
167 access to copy the source code from the same place counts as
168 distribution of the source code, even though third parties are not
169 compelled to copy the source along with the object code.
170
171 4. You may not copy, modify, sublicense, or distribute the Program
172 except as expressly provided under this License. Any attempt
173 otherwise to copy, modify, sublicense or distribute the Program is
174 void, and will automatically terminate your rights under this License.
175 However, parties who have received copies, or rights, from you under
176 this License will not have their licenses terminated so long as such
177 parties remain in full compliance.
178
179 5. You are not required to accept this License, since you have not
180 signed it. However, nothing else grants you permission to modify or
181 distribute the Program or its derivative works. These actions are
182 prohibited by law if you do not accept this License. Therefore, by
183 modifying or distributing the Program (or any work based on the
184 Program), you indicate your acceptance of this License to do so, and
185 all its terms and conditions for copying, distributing or modifying
186 the Program or works based on it.
187
188 6. Each time you redistribute the Program (or any work based on the
189 Program), the recipient automatically receives a license from the
190 original licensor to copy, distribute or modify the Program subject to
191 these terms and conditions. You may not impose any further
192 restrictions on the recipients' exercise of the rights granted herein.
193 You are not responsible for enforcing compliance by third parties to
194 this License.
195
196 7. If, as a consequence of a court judgment or allegation of patent
197 infringement or for any other reason (not limited to patent issues),
198 conditions are imposed on you (whether by court order, agreement or
199 otherwise) that contradict the conditions of this License, they do not
200 excuse you from the conditions of this License. If you cannot
201 distribute so as to satisfy simultaneously your obligations under this
202 License and any other pertinent obligations, then as a consequence you
203 may not distribute the Program at all. For example, if a patent
204 license would not permit royalty-free redistribution of the Program by
205 all those who receive copies directly or indirectly through you, then
206 the only way you could satisfy both it and this License would be to
207 refrain entirely from distribution of the Program.
208
209 If any portion of this section is held invalid or unenforceable under
210 any particular circumstance, the balance of the section is intended to
211 apply and the section as a whole is intended to apply in other
212 circumstances.
213
214 It is not the purpose of this section to induce you to infringe any
215 patents or other property right claims or to contest validity of any
216 such claims; this section has the sole purpose of protecting the
217 integrity of the free software distribution system, which is
218 implemented by public license practices. Many people have made
219 generous contributions to the wide range of software distributed
220 through that system in reliance on consistent application of that
221 system; it is up to the author/donor to decide if he or she is willing
222 to distribute software through any other system and a licensee cannot
223 impose that choice.
224
225 This section is intended to make thoroughly clear what is believed to
226 be a consequence of the rest of this License.
227
228 8. If the distribution and/or use of the Program is restricted in
229 certain countries either by patents or by copyrighted interfaces, the
230 original copyright holder who places the Program under this License
231 may add an explicit geographical distribution limitation excluding
232 those countries, so that distribution is permitted only in or among
233 countries not thus excluded. In such case, this License incorporates
234 the limitation as if written in the body of this License.
235
236 9. The Free Software Foundation may publish revised and/or new versions
237 of the General Public License from time to time. Such new versions will
238 be similar in spirit to the present version, but may differ in detail to
239 address new problems or concerns.
240
241 Each version is given a distinguishing version number. If the Program
242 specifies a version number of this License which applies to it and "any
243 later version", you have the option of following the terms and conditions
244 either of that version or of any later version published by the Free
245 Software Foundation. If the Program does not specify a version number of
246 this License, you may choose any version ever published by the Free Software
247 Foundation.
248
249 10. If you wish to incorporate parts of the Program into other free
250 programs whose distribution conditions are different, write to the author
251 to ask for permission. For software which is copyrighted by the Free
252 Software Foundation, write to the Free Software Foundation; we sometimes
253 make exceptions for this. Our decision will be guided by the two goals
254 of preserving the free status of all derivatives of our free software and
255 of promoting the sharing and reuse of software generally.
256
257 NO WARRANTY
258
259 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
260 FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
261 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
262 PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
263 OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
264 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
265 TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
266 PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
267 REPAIR OR CORRECTION.
268
269 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
270 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
271 REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
272 INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
273 OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
274 TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
275 YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
276 PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
277 POSSIBILITY OF SUCH DAMAGES.
278
279 END OF TERMS AND CONDITIONS
280
281 How to Apply These Terms to Your New Programs
282
283 If you develop a new program, and you want it to be of the greatest
284 possible use to the public, the best way to achieve this is to make it
285 free software which everyone can redistribute and change under these terms.
286
287 To do so, attach the following notices to the program. It is safest
288 to attach them to the start of each source file to most effectively
289 convey the exclusion of warranty; and each file should have at least
290 the "copyright" line and a pointer to where the full notice is found.
291
292 <one line to give the program's name and a brief idea of what it does.>
293 Copyright (C) <year> <name of author>
294
295 This program is free software; you can redistribute it and/or modify
296 it under the terms of the GNU General Public License as published by
297 the Free Software Foundation; either version 2 of the License, or
298 (at your option) any later version.
299
300 This program is distributed in the hope that it will be useful,
301 but WITHOUT ANY WARRANTY; without even the implied warranty of
302 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
303 GNU General Public License for more details.
304
305 You should have received a copy of the GNU General Public License
306 along with this program; if not, write to the Free Software
307 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
308
309
310 Also add information on how to contact you by electronic and paper mail.
311
312 If the program is interactive, make it output a short notice like this
313 when it starts in an interactive mode:
314
315 Gnomovision version 69, Copyright (C) year name of author
316 Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 This is free software, and you are welcome to redistribute it
318 under certain conditions; type `show c' for details.
319
320 The hypothetical commands `show w' and `show c' should show the appropriate
321 parts of the General Public License. Of course, the commands you use may
322 be called something other than `show w' and `show c'; they could even be
323 mouse-clicks or menu items--whatever suits your program.
324
325 You should also get your employer (if you work as a programmer) or your
326 school, if any, to sign a "copyright disclaimer" for the program, if
327 necessary. Here is a sample; alter the names:
328
329 Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
332 <signature of Ty Coon>, 1 April 1989
333 Ty Coon, President of Vice
334
335 This General Public License does not permit incorporating your program into
336 proprietary programs. If your program is a subroutine library, you may
337 consider it more useful to permit linking proprietary applications with the
338 library. If this is what you want to do, use the GNU Library General
339 Public License instead of this License.
0 DESTDIR =
1
2 DATADIR = $(DESTDIR)/usr/share/games/pathological
3
4 all: write-highscores html/wheel.png
5
6 write-highscores: write-highscores.c
7 gcc -s -o write-highscores write-highscores.c
8
9 html/wheel.png:
10 ./makehtml
11
12 install: all
13 mkdir -p $(DATADIR)
14 cp pathological.py $(DATADIR)/
15 cp -r circuits graphics music sounds $(DATADIR)/
16 rm -f $(DATADIR)/graphics/*.xcf
17 rm -f $(DATADIR)/sounds/*.orig
18
19 mkdir -p $(DESTDIR)/usr/games
20 cp pathological $(DESTDIR)/usr/games/
21
22 mkdir -p $(DESTDIR)/usr/lib/pathological/bin
23 cp write-highscores $(DESTDIR)/usr/lib/pathological/bin
24
25 # The following changes should also be performed in a post-install script
26 -chgrp games $(DESTDIR)/usr/lib/pathological/bin/write-highscores
27 -chmod 2755 $(DESTDIR)/usr/lib/pathological/bin/write-highscores
28
29 mkdir -p $(DESTDIR)/usr/X11R6/include/X11/pixmaps
30 cp pathological.xpm $(DESTDIR)/usr/X11R6/include/X11/pixmaps
31
32 mkdir -p $(DESTDIR)/var/games
33 cp pathological_scores $(DESTDIR)/var/games
34
35 # The following changes should also be performed in a post-install script
36 -chgrp games $(DESTDIR)/var/games/pathological_scores
37 -chmod 664 $(DESTDIR)/var/games/pathological_scores
38
39 mkdir -p $(DESTDIR)/usr/share/man/man6
40 cp pathological.6.gz $(DESTDIR)/usr/share/man/man6
41
42 mkdir -p $(DESTDIR)/usr/share/doc/pathological
43 cp -r html $(DESTDIR)/usr/share/doc/pathological
44
45 uninstall:
46 rm -rf $(DATADIR)
47 rm -rf $(DESTDIR)/usr/lib/pathological
48 rm -f $(DESTDIR)/usr/games/pathological
49 rm -f $(DESTDIR)/usr/X11R6/include/X11/pixmaps/pathological.xpm
50 rm -f $(DESTDIR)/usr/share/man/man6/pathological.6.gz
51
52 purge: uninstall
53 rm -f $(DESTDIR)/var/games/pathological_scores
54
55 empty: distclean
56
57 distclean:
58 rm -f write-highscores
59 rm -f html/*.png
0 Pathological
1 ------------
2
3 Copyright (C) 2003 John-Paul Gignac
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 version 2, as published by the Free Software Foundation.
8
9 Soundtrack by Matthias Le Bidan.
10
11 Logo by Carrie Bloomfield.
12
13 In-game graphics partially based on artwork by Mike Brenneman.
14
15 Board designs contributed by the following people:
16
17 - Mike Brenneman
18 - Kim Gignac
19
20 If you would like your board designs added to a future release, please
21 check the website to see if new boards are still being accepted.
22
23 Thanks to Paul Prescod who motivated this project via an "anti-challenge"
24 sent by email two years ago.
25
26 A few of Pathological's sound effects were obtained from other GPL
27 software:
28
29 - CannonSmash http://www.utmc.or.jp/~nan/csmash/
30 - Gnibbles http://www.gnome.org/
31
32 Thanks to the authors of these programs.
33
34 For more information see http://pathological.sourceforge.net/
0 - Add more boards
0 pathological (1.1.3) stable; urgency=low
1
2 * Changed the default data directory to /usr/share/games/pathological
3 * Removed all packaging scripts, etc. from the release files.
4 * Eliminated the ^= operator, to improve compatibility with old Python.
5 * Specified use of /bin/bash explicitly in the wrapper script.
6
7 -- John-Paul Gignac <jp@gignac.org> Thu, 17 Jul 2003 11:17:53 -0400
8
9 pathological (1.1.2) stable; urgency=low
10
11 * Fixed a crash bug when attempting to eject a marble off of the board.
12
13 -- John-Paul Gignac <jp@gignac.org> Wed, 9 Jul 2003 21:49:19 -0400
14
15 pathological (1.1.1) stable; urgency=low
16
17 * Rewrote write-highscores.c to work on non-Debian systems
18 * Changed the Makefile to be more system independent
19 * Fixed a crash bug when sound is unavailable
20
21 -- John-Paul Gignac <jp@gignac.org> Thu, 26 Jun 2003 13:28:33 -0400
22
23 pathological (1.1.0) stable; urgency=low
24
25 * Enabled toggle fullscreen on platforms where Pygame doesn't support it
26 * Major code cleanup
27 * Increased the audio sample rate and buffer size on Windows platforms
28 * Added some scripts for generating the release files
29
30 -- John-Paul Gignac <jp@gignac.org> Wed, 25 Jun 2003 12:41:48 -0400
31
32 pathological (1.0.0) stable; urgency=low
33
34 * The active marble limit no longer restricts ejects between adjacent wheels
35 * Added Matthias' new improved intro music
36 * Re-did most of the sound effects
37
38 -- John-Paul Gignac <jp@gignac.org> Thu, 19 Jun 2003 22:21:47 -0400
39
40 pathological (0.9.0) stable; urgency=low
41
42 * Added code-level volume control
43 * Added a command-line option for starting with sound disabled
44 * Added support for command-line options to the wrapper script
45 * Added a loading screen in case the music takes a long time to load
46 * Highlighted the new highscore on the highscores screen
47 * The program now shows highscores automatically at the end of the game
48 * Added the Random-Levels feature after the final level
49 * More new levels
50 * Fixed a wraparound bug when the user hits 'b' after losing level 1
51 * Fixed the replication-overload bug
52 * Added special graphics for colour-blind people
53 * New in-game background graphics
54 * New intro-screen graphics
55 * Added a command-line option for starting in fullscreen mode
56 * Added the intro-page music
57 * Added crazy marbles (wildcard marbles)
58
59 -- John-Paul Gignac <jp@gignac.org> Tue, 17 Jun 2003 21:14:03 -0400
60
61 pathological (0.8.4) stable; urgency=low
62
63 * Totally redid all of the graphics
64 * Added a script for auto-generating the documentation graphics
65
66 -- John-Paul Gignac <jp@gignac.org> Sun, 27 Apr 2003 21:14:18 -0400
67
68 pathological (0.8.3) stable; urgency=low
69
70 * Fixed a yucky problem with the intro page foreground graphics
71 * Made windowed mode the default
72 * Eliminated dumb uses of the globals() function
73 * Added mouse control of the intro-screen menu
74
75 -- John-Paul Gignac <jp@gignac.org> Mon, 21 Apr 2003 10:13:00 -0400
76
77 pathological (0.8.2) stable; urgency=low
78
79 * Added authorship information to the board file
80 * Created a proper intro page based on Carrie's design
81 * Removed the source .xcf files from the binary package
82 * Fixed a major bug that permitted strategic marble deletion
83 * Small documentation correction
84
85 -- John-Paul Gignac <jp@gignac.org> Wed, 16 Apr 2003 03:53:36 -0400
86
87 pathological (0.8.1) stable; urgency=low
88
89 * Organized the levels
90 * Many new levels
91 * Decreased the frequency of free lives
92 * Fixed a very minor bug
93
94 -- John-Paul Gignac <jp@gignac.org> Mon, 14 Apr 2003 00:14:00 -0400
95
96 pathological (0.8.0) stable; urgency=low
97
98 * Initial Pre-Release.
99 * Implementation began on March 16, 2003
100
101 -- John-Paul Gignac <jp@gignac.org> Thu, 10 Apr 2003 14:54:24 -0400
102
0 # Each tile consists of three characters: A tile type, a path descriptor
1 # and a tile-type-specific control character. If the tile is a painter or
2 # a filter, the control character should be a color. If the tile is a
3 # switch, the control character should be the secondary switch direction
4 # (^, >, v, or <). If the tile is an initial marble location, the control
5 # character should be the marble's initial direction of travel. If the
6 # tile is a teleporter, the control character is an arbitrary label used to
7 # match it up with its partner teleporter. If the tile type is a buffer,
8 # the control character may be either a color to denote the initial marble
9 # color, or blank to denote that the buffer is initially empty. All other
10 # tile types ignore the control character.
11
12 # Note that this file is not validated. If there are errors of any sort
13 # in this file, the results are undefined.
14
15 # Tile Types Paths Colors
16 # ---------- ----------- ----------
17 # O - Wheel 0: 8:W 0 - Black
18 # & - Painter 1:N 9:NW 1 - White
19 # # - Filter 2:E a:EW 2 - Blue
20 # ^>v< - Arrow/Switch 3:NE b:NEW 3 - Green
21 # = - Teleporter 4:S c:SW 4 - Yellow
22 # % - Trigger 5:NS d:NSW 5 - Purple
23 # ! - Stoplights 6:SE e:SEW 6 - Red
24 # @ - Buffer 7:NSE f:NSEW 7 - Orange
25 # X - Shredder 8 - Crazy (wildcard)
26 # * - Replicator
27 # 0-8 - Initial marble location (the digit specifies the marble color)
28
29 # Level parameters:
30 # -----------------
31 # name - The name of the level
32 # author - The person who designed the level (may include email address)
33 # launchtimer - The launch timer, measured in number of passes (default: 6)
34 # boardtimer - The board timer, in seconds (default: 30 * number of wheels)
35 # colors - The colors that will be served on this level (default: 2,3,4,6)
36 # maxmarbles - The maximum number of active marbles (default: 10)
37 # stoplight - The colors in the stoplight (default: 6,4,3)
38
39 name=Easy Does It
40 author=Mike Brenneman
41 boardtimer=600
42 launchtimer=90
43 maxmarbles=5
44 +---+---+---+---+---+---+---+---+
45 | | | |O7 |Od | | | |
46 | | | | 5 | 5 | | | |
47 | | | | 5 | 5 | | | |
48 | | | | 5 | 5 | | | |
49 | | | |O3 |O9 | | | |
50 | | | | | | | | |
51 +---+---+---+---+---+---+---+---+
52
53 name=Grand Canyon
54 author=Mike Brenneman
55 boardtimer=300
56 launchtimer=10
57 maxmarbles=5
58 +---+---+---+---+---+---+---+---+
59 | |O5 | | | | |O5 | |
60 | | 5 | | | | | 5 | |
61 | | 5 | | | | | 5 | |
62 | | 5 | | | | | 5 | |
63 | |O1 | | | | |O1 | |
64 | | | | | | | | |
65 +---+---+---+---+---+---+---+---+
66
67 name=The Abyss
68 author=John-Paul Gignac <jp@gignac.org>
69 boardtimer=300
70 launchtimer=15
71 maxmarbles=5
72 +---+---+---+---+---+---+---+---+
73 | |O7 | a | a | a | a |Od | |
74 | | 3 | a |ve |ve | a | 9 | |
75 | | | | 5 | 5 | | | |
76 | | | |O7 |Od | | | |
77 | | | |O3 |O9 | | | |
78 | | | | | | | | |
79 +---+---+---+---+---+---+---+---+
80
81 name=Follow the Leader
82 author=John-Paul Gignac <jp@gignac.org>
83 boardtimer=240
84 +---+---+---+---+---+---+---+---+
85 | 6 | a | a |O9 |O2 | a | a | c |
86 | 5 | 6 | c | 6 | c | 6 | a | 9 |
87 | 3 | 9 | 5 | 5 | 5 | 3 | a | c |
88 | 6 | c | 3 | 9 | 5 | 6 | c | 5 |
89 | 5 | 3 | a | a | 9 | 5 | 5 | 5 |
90 | 3 | a | a | a | a | 9 | 3 | 9 |
91 +---+---+---+---+---+---+---+---+
92
93 name=Tickled Pink
94 author=Kim Gignac
95 boardtimer=150
96 +---+---+---+---+---+---+---+---+
97 | | | |O5 | |O5 | | |
98 | | | | 5 | | 5 | | |
99 | |>2 |&a4|Ob | a |Od | | |
100 | | | | | |&55| | |
101 | | | | | |O1 | | |
102 | | | | | | | | |
103 +---+---+---+---+---+---+---+---+
104
105 name=Split Ends
106 author=Mike Brenneman
107 boardtimer=180
108 launchtimer=4
109 maxmarbles=5
110 +---+---+---+---+---+---+---+---+
111 | | |O5 | | |O5 | | |
112 | | | 5 | | | 5 | | |
113 | | | 3 |Oc |O6 | 9 | | |
114 | | | | 5 | 5 | | | |
115 | |O2 |&a4| 9 | 3 |&a3|O8 | |
116 | | | | | | | | |
117 +---+---+---+---+---+---+---+---+
118
119 name=Trigger Practice
120 author=Mike Brenneman
121 boardtimer=240
122 launchtimer=2
123 maxmarbles=5
124 +---+---+---+---+---+---+---+---+
125 | | | 5 | | | | | |
126 |O2 |&a4|Of | a | a |&a3| a |O8 |
127 | | |&52| |% | | | |
128 | | |O1 | | | | | |
129 | | | | | | | | |
130 | | | | | | | | |
131 +---+---+---+---+---+---+---+---+
132
133 name=Stop and Go
134 author=John-Paul Gignac <jp@gignac.org>
135 boardtimer=120
136 +---+---+---+---+---+---+---+---+
137 | | |O7 | a | c | | | |
138 | | | 5 | |@5 | | | |
139 | | | 5 | | 3 | a |O8 | |
140 | | | 5 | | | | | |
141 | | |O1 | | | | | |
142 | | | | | | | | |
143 +---+---+---+---+---+---+---+---+
144
145 name=Ketchup and Mustard
146 author=Mike Brenneman
147 boardtimer=300
148 launchtimer=8
149 maxmarbles=5
150 +---+---+---+---+---+---+---+---+
151 | | |O5 | | |O5 | | |
152 | | | 5 |O6 |Oc | 5 | | |
153 | | | 5 | 5 | 5 | 5 | | |
154 | | | 5 | 5 | 5 | 5 | | |
155 |>2 |&a6|Ob | 9 | 3 |Ob |&a4|<8 |
156 | | | | | | | | |
157 +---+---+---+---+---+---+---+---+
158
159 name=Filters 101
160 author=Mike Brenneman
161 boardtimer=240
162 launchtimer=6
163 maxmarbles=5
164 +---+---+---+---+---+---+---+---+
165 | |O5 | | | | |O5 | |
166 | | 5 | | | | | 5 | |
167 | | 3 |#a3| c | 6 |#a6| 9 | |
168 | | | | 5 | 5 | | | |
169 | | | |O3 |O9 | | | |
170 | | | | | | | | |
171 +---+---+---+---+---+---+---+---+
172
173 name=Disconnect
174 author=Kim Gignac
175 boardtimer=120
176 +---+---+---+---+---+---+---+---+
177 | | |O7 | a | a | c | | |
178 | | | 5 | | |O1 | | |
179 | | |=1k| | |=4k| | |
180 | | |O4 | | | 5 | | |
181 | | | 3 | a | a |O9 | | |
182 | | | | | | | | |
183 +---+---+---+---+---+---+---+---+
184
185 name=The Run-Around
186 author=John-Paul Gignac
187 boardtimer=300
188 launchtimer=10
189 maxmarbles=5
190 +---+---+---+---+---+---+---+---+
191 | 6 |Of | a | c |O7 | c |O5 | |
192 | 3 | f | a | f | f | 9 | 5 | |
193 | |#57|O4 |&57| 5 | | 5 | |
194 | | 5 |&56| 3 | f |>a |Od | |
195 | |O5 |^7 | a |Ob | a | 9 | |
196 | | 3 |O9 | | | | | |
197 +---+---+---+---+---+---+---+---+
198
199 name=Transporter Room
200 author=Mike Brenneman
201 boardtimer=240
202 launchtimer=6
203 maxmarbles=5
204 +---+---+---+---+---+---+---+---+
205 | | 6 |Od | | |O7 | c | |
206 | |O1 | 5 | | | 5 |O1 | |
207 | | |=5a| | |=5a| | |
208 | 6 | a |O9 | | |O3 | a | c |
209 | 3 | a | a | a | a | a | a | 9 |
210 | | | | | | | | |
211 +---+---+---+---+---+---+---+---+
212
213 name=Red All Over
214 author=John-Paul Gignac <jp@gignac.org>
215 colors=6
216 boardtimer=120
217 launchtimer=2
218 +---+---+---+---+---+---+---+---+
219 | | | | |O7 |Od | | |
220 | | | | |O3 |O9 | | |
221 | | 6 |0cv| | | | | |
222 |=2a|1f>|Of |1a<|0cv| | | |
223 | | 5 |03^|1a>|Of |1a<|#a0|=8a|
224 | |#31| a | a |09^| | | |
225 +---+---+---+---+---+---+---+---+
226
227 name=Rules of the Road
228 author=Kim Gignac
229 boardtimer=150
230 +---+---+---+---+---+---+---+---+
231 | | |&66|Of | a |vc | | |
232 | | |O1 | 5 | |O3 |O8 | |
233 | | | |O5 |! | | | |
234 | | | |&54| | | | |
235 | | | |O1 | | | | |
236 | | | | | | | | |
237 +---+---+---+---+---+---+---+---+
238
239 name=Criss-Cross
240 author=John-Paul Gignac <jp@gignac.org>
241 boardtimer=300
242 +---+---+---+---+---+---+---+---+
243 | |O5 |O5 | | |O5 |O5 | |
244 | | 5 | 5 | | | 5 | 5 | |
245 | |=1d|=1e| | |=1a|=1b| |
246 | |=4a|=4b| | |=4d|=4e| |
247 | | 5 | 5 | | | 5 | 5 | |
248 |=2c|Ob |Ob | a | a |Ob |Ob |=8c|
249 +---+---+---+---+---+---+---+---+
250
251 name=Throw-Away Society
252 author=John-Paul Gignac <jp@gignac.org>
253 maxmarbles=3
254 boardtimer=210
255 +---+---+---+---+---+---+---+---+
256 | | 6 |Of | a |#a3|Oe |&82| |
257 | |X1 | 5 | | | 5 | | |
258 | | |#54| | |#52| | |
259 | | |&56| | |&55| | |
260 | | |O1 | | |O1 | | |
261 | | | | | | | | |
262 +---+---+---+---+---+---+---+---+
263
264 name=Waterfalls
265 author=Mike Brenneman
266 boardtimer=180
267 launchtimer=4
268 maxmarbles=5
269 +---+---+---+---+---+---+---+---+
270 | | | |O7 |Od | | | |
271 | | |=4f|=1f|=1a|=4a| | |
272 | |=4e|=1e|O6 |Oc |=1b|=4b| |
273 |=4d|=1d| | 5 | 5 | |=1c|=4c|
274 |>3 | a | a |O9 |O3 | a | a |<9 |
275 | | | | | | | | |
276 +---+---+---+---+---+---+---+---+
277
278 name=Multiplicity
279 author=John-Paul Gignac <jp@gignac.org>
280 boardtimer=60
281 launchtimer=3
282 +---+---+---+---+---+---+---+---+
283 | | | 6 |Of | a | c | | |
284 | | |*52|*34| c |*52| | |
285 | | | 5 | 6 | 9 | 5 | | |
286 | | |v5 | 3 | c |v5 | | |
287 | | |O1 |v6 | 9 |O1 | | |
288 | | | |O1 | | | | |
289 +---+---+---+---+---+---+---+---+
290
291 name=Neither Here nor There
292 author=John-Paul Gignac <jp@gignac.org>
293 boardtimer=180
294 +---+---+---+---+---+---+---+---+
295 | | |O7 | a |<a>| a | c | |
296 | | | 5 | | | |O5 | |
297 | | |v7>| a |Oa | a | 9 | |
298 | | | 5 | | | | | |
299 | | |O1 | | | | | |
300 | | | | | | | | |
301 +---+---+---+---+---+---+---+---+
302
303 name=T-Intersection
304 author=John-Paul Gignac
305 boardtimer=210
306 launchtimer=10
307 maxmarbles=5
308 +---+---+---+---+---+---+---+---+
309 | | | 6 |Od | 6 |Od | | |
310 | | 6 | f | f | 9 | 5 | | |
311 | |O7 | f | f | c | 5 |=4a| |
312 | |=1a| 5 | 3 | f | f |Od | |
313 | | | 5 | 6 | f | f | 9 | |
314 | | |O3 | 9 |O3 | 9 | | |
315 +---+---+---+---+---+---+---+---+
316
317 name=Which Way is Left?
318 author=John-Paul Gignac <jp@gignac.org>
319 colors=0,2,7
320 +---+---+---+---+---+---+---+---+
321 | | | |O5 | | | | |
322 | | | | 5 | | | | |
323 | | |v6 |>b<|vc | | | |
324 | | | 5 | | 3 | a |O8 | |
325 | | |O1 | | | | | |
326 | | | | | | | | |
327 +---+---+---+---+---+---+---+---+
328
329 name=Jailbirds
330 author=John-Paul Gignac
331 boardtimer=600
332 launchtimer=90
333 maxmarbles=30
334 +---+---+---+---+---+---+---+---+
335 | |O7 |<e |<e |<e |<e | c | |
336 | |*54| 5 | 5 | 5 | 5 | 5 | |
337 | |*54| 5 | 5 | 5 | 5 | 5 | |
338 | |&54| 5 | 5 | 5 | 5 | 5 | |
339 | | 3 |^b>|^b>|^b>|^b>|^dv| |
340 | | | | | | |O1 | |
341 +---+---+---+---+---+---+---+---+
342
343 name=The Distributor
344 author=John-Paul Gignac <jp@gignac.org>
345 maxmarbles=9
346 launchtimer=2
347 colors=0,1,2,3,4,5,6,7
348 +---+---+---+---+---+---+---+---+
349 | 6 | a | a | a |<ev| a |<ev|<dv|
350 | 5 | 6 | a | a | f |<ev| f |<dv|
351 | 5 | 5 | 6 | a | f | f |<dv| 5 |
352 | 5 | 5 | 5 | 6 | f | f | f |<dv|
353 |&50|&51|&52|&53|&54|&55|&56|&57|
354 |O1 |O1 |O1 |O1 |O1 |O1 |O1 |O1 |
355 +---+---+---+---+---+---+---+---+
356
357 ###############################################################
358 ## Beginning of Medium Level ##
359 ###############################################################
360
361 name=Blame Shorty
362 author=Kim Gignac
363 launchtimer=20
364 maxmarbles=10
365 boardtimer=240
366 +---+---+---+---+---+---+---+---+
367 | | | |O7 | c |O7 | c | |
368 | | | | 3 |@f |<9 |O1 | |
369 | |% | | | 5 | | | |
370 | | | | | 5 | | | |
371 | | |O2 | a | 9 | | | |
372 | | | | | | | | |
373 +---+---+---+---+---+---+---+---+
374
375 name=The Matrix
376 author=John-Paul Gignac <jp@gignac.org>
377 colors=3
378 launchtimer=20
379 boardtimer=240
380 +---+---+---+---+---+---+---+---+
381 | | |O7 |Of |Of |Od | | |
382 | | |O7 |Of |Of |Od | | |
383 | | |O7 |Of |Of |Od | | |
384 | | |O3 |Ob |Ob |O9 | | |
385 | | | | | | | | |
386 | | | | | | | | |
387 +---+---+---+---+---+---+---+---+
388
389 name=Wildberries
390 author=Kim Gignac
391 maxmarbles=10
392 +---+---+---+---+---+---+---+---+
393 | | 5 | | | |v6 |O9 | |
394 |&62|<b>|&a6| a |>ev|@f |=a | c |
395 | 5 | | | | 5 | 5 | |&55|
396 | 5 | | | | 5 |O3 |Oe |Od |
397 |>7v| a |=a | c | 3 |Oa |Of |Od |
398 |&33| a | a |>b | a |&a2|^b>| 9 |
399 +---+---+---+---+---+---+---+---+
400
401 name=Greetings, Earthling
402 author=John-Paul Gignac <jp@gignac.org>
403 colors=0,2,4,6,7
404 +---+---+---+---+---+---+---+---+
405 | | |O7 | a |Od | | | |
406 | |O4 | 3 |ve | 9 |O4 | | |
407 | |^3 |ve<|>b<|ve>|^9 | | |
408 | | |v7 | a |vd | | | |
409 | | | 5 | | 5 | | | |
410 | | |O1 | |O1 | | | |
411 +---+---+---+---+---+---+---+---+
412
413 name=The Blue Zone
414 author=Mike Brenneman
415 boardtimer=180
416 launchtimer=6
417 maxmarbles=5
418 +---+---+---+---+---+---+---+---+
419 | |O7 | a |=aa|&a2| a |Od | |
420 | |#52| | | | |#52| |
421 | |O7 | a |Oa |Oa |=aa|Od | |
422 | | 5 | | | | | 5 | |
423 | |O3 | a | a | a | a |O9 | |
424 | | | | | | | | |
425 +---+---+---+---+---+---+---+---+
426
427 name=Lily Pads
428 author=Kim Gignac
429 +---+---+---+---+---+---+---+---+
430 | |O7 |&a7| c | |O5 | | |
431 | | 5 | |=1a| | 5 | | |
432 | |v5 | | | |v7>|O8 | |
433 | | 5 |=4a|O6 |O8 | 5 | | |
434 | |O1 | 5 |O5 | |O1 | | |
435 | | |>3 | 9 | | | | |
436 +---+---+---+---+---+---+---+---+
437
438 name=Renegade
439 author=John-Paul Gignac
440 boardtimer=360
441 launchtimer=20
442 maxmarbles=10
443 colors=2,5,3,7,8
444 +---+---+---+---+---+---+---+---+
445 | | |=4a|O7 |Od |=4b| | |
446 | |O2 |@f2|vd<|>7v|@f3|O8 | |
447 | | |65v| 5 | 5 | 5 | | |
448 | |O6 |@f5| 9 | 3 |@f7|Oc | |
449 | | 5 |=1b| | |=1a| 5 | |
450 | |O3 | a | a | a | a |O9 | |
451 +---+---+---+---+---+---+---+---+
452
453 name=Inky Binky Bonky
454 boardtimer=240
455 colors=2,4,6
456 +---+---+---+---+---+---+---+---+
457 |v6>| a |<dv|O1 | |O4 | | |
458 |>7v| c | 5 | 6 | c | 5 | | |
459 | 3 |@f |@f |>b |@f |@f |Oa | c |
460 | 6 |<dv|X1 | |^7>| 9 | | 5 |
461 |&54| 5 | | 6 | 9 | |O4 | 5 |
462 | 3 |>b | a | 9 | | | 3 |O9 |
463 +---+---+---+---+---+---+---+---+
464
465 name=Beam Me Up
466 author=Mike Brenneman
467 boardtimer=480
468 launchtimer=6
469 maxmarbles=5
470 +---+---+---+---+---+---+---+---+
471 | |O5 |O2 |=at|O8 | |O5 | |
472 |O4 |O7 |Oe | a |=ab|Oe |Od |O4 |
473 |=5l| 5 |=5r| | |=5l| 5 |=5r|
474 |O1 | 3 |Ob |=at| a |Ob | 9 |O1 |
475 | | | |O2 |=ab|O8 | | |
476 | | | | | | | | |
477 +---+---+---+---+---+---+---+---+
478
479 name=It's All in the Wrist
480 author=John-Paul Gignac <jp@gignac.org>
481 colors=0,1,2,3,4,5,6,7
482 +---+---+---+---+---+---+---+---+
483 |X1 |O1 |O1 |O1 |O1 |O1 |O1 |O1 |
484 | | | | | | | | |
485 | | | | | | | | |
486 | | | | | | | | |
487 | | | | | | | | |
488 | | | | | | | | |
489 +---+---+---+---+---+---+---+---+
490
491 name=The Asylum
492 author=Kim Gignac
493 +---+---+---+---+---+---+---+---+
494 | |O3 | c | | | 6 |O9 | |
495 | | | 3 |Oc |O2 | 9 | | |
496 | |% | | 5 | | |=4b| |
497 | | |=2a| 9 | | |O5 | |
498 |>6 |O8 | | | |O4 | 5 | |
499 |=1b| | | | | 3 |<b^|=8a|
500 +---+---+---+---+---+---+---+---+
501
502 name=Target Practice
503 author=John-Paul Gignac <jp@gignac.org>
504 colors=2,3,7
505 maxmarbles=16
506 boardtimer=180
507 launchtimer=1
508 stoplight=2,3,7
509 +---+---+---+---+---+---+---+---+
510 |>6v|*82| | | | 6 | 9 | |
511 |05^| 6 | a | a | a |@f |>a |O8 |
512 |>7v|Of | a | a | a |@f |>a |O8 |
513 | 5 | 3 | a | a | a |@f |>a |O8 |
514 |X1 | | | | | 3 |X8 | |
515 | | | | | | | | |
516 +---+---+---+---+---+---+---+---+
517
518 name=Lost in the Shuffle
519 author=John-Paul Gignac <jp@gignac.org>
520 colors=0
521 maxmarbles=20
522 boardtimer=600
523 launchtimer=1
524 +---+---+---+---+---+---+---+---+
525 | | | | 6 | c | 6 | 9 | |
526 |O2 |#a6|<a |@f |@f |@f | c |X4 |
527 |O2 |#a6|<a |@f |@f |@f |Of |<d^|
528 |O2 |#a6|<a |@f |@f |@f | 9 |65v|
529 | | | |X1 | 3 | 9 | |v5^|
530 | | | | | | | |*12|
531 +---+---+---+---+---+---+---+---+
532
533 name=The Sorter
534 author=John-Paul Gignac <jp@gignac.org>
535 maxmarbles=9
536 launchtimer=2
537 colors=0,1,2,3,4,5,6,7
538 +---+---+---+---+---+---+---+---+
539 | 6 | a | a |>b | a | a | a | c |
540 |v7^|ve<|ve<|ve<|ve<|ve<|ve<|vd<|
541 |#50|#51|#52|#53|#54|#55|#56|#57|
542 |v5 |v5 |v5 |v5 |v5 |v5 |v5 |v5 |
543 |O1 |O1 |O1 |O1 |O1 |O1 |O1 |O1 |
544 | | | | | | | | |
545 +---+---+---+---+---+---+---+---+
546
547 ###############################################################
548 ## Beginning of Hard Level ##
549 ###############################################################
550
551 name=Change Rooms
552 author=Mike Brenneman (w/ tweaks by John-Paul Gignac)
553 boardtimer=180
554 launchtimer=2
555 maxmarbles=5
556 +---+---+---+---+---+---+---+---+
557 | | | | 5 | | | | |
558 | |>2 |&a4|Od | | | | |
559 | |>2 |&a6|Od | |! | | |
560 | |>2 |&a2|Od | | | | |
561 | |>2 |&a3|O9 | | | | |
562 | | | | | | | | |
563 +---+---+---+---+---+---+---+---+
564
565 name=Sharp Shooter
566 author=John-Paul Gignac <jp@gignac.org>
567 colors=2,3,7
568 maxmarbles=16
569 boardtimer=180
570 launchtimer=1
571 stoplight=7,2,3
572 +---+---+---+---+---+---+---+---+
573 |>6v|*82| | |! | 6 | 9 | |
574 |05^| 6 | a | a | a |@f |>a |O8 |
575 |>7v|Of | a | a | a |@f |>a |O8 |
576 | 5 | 3 | a | a | a |@f |>a |O8 |
577 |X1 | | | | | 3 |X8 | |
578 | | | | | | | | |
579 +---+---+---+---+---+---+---+---+
580
581 name=Traffic Cop
582 author=Mike Brenneman
583 boardtimer=300
584 launchtimer=3
585 maxmarbles=5
586 +---+---+---+---+---+---+---+---+
587 |O5 | |O4 | |O4 | |O4 | |
588 | 5 | |#53|! |&56| |=5a| |
589 |O7 | a |Of | a |Of | a |Od | |
590 | 5 | |v5 | |=5a| |#52| |
591 |O1 | |O3 | a |O9 | |O1 | |
592 | | | | | | | | |
593 +---+---+---+---+---+---+---+---+
594
595 name=Tick Tock
596 author=John-Paul Gignac <jp@gignac.org>
597 launchtimer=6
598 boardtimer=120
599 +---+---+---+---+---+---+---+---+
600 | | | | | 3 | a |&c3| |
601 | | 6 | a | a | a | a | f | c |
602 |v6 | f |ve<|ve<|ve<|&a4|vd<| 5 |
603 | 5 | 3 |vf<|vf<|vf<|ve<|vf<| 9 |
604 |O1 | |O7 |Of |Of |Of |Od | |
605 | | |O3 |Ob |Ob |Ob |O9 | |
606 +---+---+---+---+---+---+---+---+
607
608 name=Revenge of Lost in the Shuffle
609 author=John-Paul Gignac <jp@gignac.org>
610 colors=0
611 maxmarbles=26
612 boardtimer=600
613 launchtimer=1
614 +---+---+---+---+---+---+---+---+
615 |O2 |#c6|X4 | 6 | c | 6 | 9 |X4 |
616 |O4 |^3 |@f |@f |@f |@f | c | 5 |
617 |#36|<a |@f |@f |@f |@f |Of |<d^|
618 |#66|<a |@f |@f |@f |@f |Of |<d^|
619 |O1 |v6 |@f |@f |@f |@f | 9 |65v|
620 |O2 |#96| 3 | 9 | 3 | 9 |*22|<9^|
621 +---+---+---+---+---+---+---+---+
622
623 name=Trigger Happy
624 author=Mike Brenneman
625 boardtimer=300
626 launchtimer=4
627 maxmarbles=5
628 stoplight=6,4,2
629 +---+---+---+---+---+---+---+---+
630 |O5 | |O5 | | |O5 | |O5 |
631 | 5 |% | 5 | | | 5 |! | 5 |
632 |O7 | a |O9 | | |O3 |#a4|Od |
633 | 5 | | | | | | | 5 |
634 |O3 |&a3|O8 | | |O2 | a |O9 |
635 | | | | | | | | |
636 +---+---+---+---+---+---+---+---+
637
638 name=Code Breaker
639 author=John-Paul Gignac
640 boardtimer=360
641 launchtimer=30
642 maxmarbles=6
643 +---+---+---+---+---+---+---+---+
644 |O6 |&a0|#c6|@6 |*a2|ve<|Ob |O9 |
645 |&50|X6 |^d<|@3 |ve |<b | a | c |
646 |#34|<b^|^b<| c |v7>|ve>|ve>|vd^|
647 |#62|<ev|<ev|<d^|#56|#54|#53|#52|
648 |&55|X3 |vd<|@5 |&54|&52|&56|&53|
649 |O3 |&a5|#93| 3 |<b |<b |<b | 9 |
650 +---+---+---+---+---+---+---+---+
651
652 name=Hot Potato
653 author=Kim Gignac
654 boardtimer=180
655 colors=2,4,6,7
656 +---+---+---+---+---+---+---+---+
657 | 6 | a | a |O9 |O2 | a | a | c |
658 | 5 | 6 | c | 6 | c | 6 | a |O9 |
659 | 3 |O9 | 5 | 5 | 5 | 3 | a | c |
660 | 6 | c | 3 | 9 | 5 |O6 | c | 5 |
661 | 5 | 3 |Oa | a | 9 | 5 | 5 | 5 |
662 | 3 | a | a | a | a | 9 | 3 | 9 |
663 +---+---+---+---+---+---+---+---+
664
665 name=Around the Bend
666 author=Mike Brenneman
667 boardtimer=360
668 launchtimer=3
669 maxmarbles=5
670 +---+---+---+---+---+---+---+---+
671 | |O5 | | | | 6 | c | |
672 | | 5 |O6 |Oc | | 3 |&f3|O8 |
673 | | 5 |O7 |Ob |#a4| a |Od | |
674 | |O7 | 9 |% | | | 5 | |
675 | | 3 | a | a |#a2| a |O9 | |
676 | | | | | | | | |
677 +---+---+---+---+---+---+---+---+
678
679 name=Jack Be Nimble...
680 author=Mike Brenneman
681 boardtimer=60
682 launchtimer=1
683 maxmarbles=5
684 +---+---+---+---+---+---+---+---+
685 | 5 | | | | | |O5 | |
686 |O5 | |O4 | | | |&54| |
687 |v5 | |&56| | | | 5 | |
688 |O1 | | 5 | | | |&53| |
689 | | | 3 |&a2| a | a | 9 | |
690 | | | | | | | | |
691 +---+---+---+---+---+---+---+---+
692
693 name=Zig Zag
694 author=Mike Brenneman
695 boardtimer=360
696 launchtimer=3
697 maxmarbles=5
698 +---+---+---+---+---+---+---+---+
699 | |O7 | a | a | a | a | a |Oc |
700 | | 3 | a |Oe |Oe | a | c |=1a|
701 | 6 | a | a |Of |Of |vc | 3 | c |
702 |=5b|=4a| 6 |<b | 9 | 3 | c |=5b|
703 |O1 |O3 | 9 |O2 | a |#a2|O9 |O1 |
704 | | | | | | | | |
705 +---+---+---+---+---+---+---+---+
706
707 name=The Auto-Solver
708 author=John-Paul Gignac <jp@gignac.org>
709 maxmarbles=10
710 launchtimer=4
711 boardtimer=120
712 colors=1,2,3,6
713 +---+---+---+---+---+---+---+---+
714 | 6 |ve>| c | 5 | | 6 |>ev| c |
715 |^7>|Of | 9 | 3 | c |^7>|Of |<dv|
716 | 3 |<b^|#a1|<e>|>fv|#92| 3 | 9 |
717 | 6 | c |#66|<b^|>b<|#a3|>ev| c |
718 |>7^|Of |vd<| | | 6 |Of |vd<|
719 | 3 |<b^| 9 | | | 3 |^b<| 9 |
720 +---+---+---+---+---+---+---+---+
721
0 pathological (1.1.3-15) unstable; urgency=medium
1
2 * Team upload.
3 * Switch to compat level 11.
4 * Declare compliance with Debian Policy 4.1.4.
5 * Move the package to Git and salsa.debian.org.
6 * Remove deprecated menu file.
7
8 -- Markus Koschany <apo@debian.org> Wed, 23 May 2018 13:28:37 +0200
9
010 pathological (1.1.3-14) unstable; urgency=medium
111
212 * Team upload.
44 Uploaders:
55 Barry deFreese <bdefreese@debian.org>
66 Build-Depends:
7 debhelper (>= 9),
7 debhelper (>= 11),
88 dh-python,
99 netpbm,
1010 python,
1111 python-pygame,
1212 vorbis-tools,
1313 xmp
14 Standards-Version: 3.9.6
14 Standards-Version: 4.1.4
1515 Homepage: http://pathological.sourceforge.net/
16 Vcs-Git: git://anonscm.debian.org/pkg-games/pathological.git
17 Vcs-Browser: https://anonscm.debian.org/viewvc/pkg-games/packages/trunk/pathological/
16 Vcs-Git: https://salsa.debian.org/games-team/pathological.git
17 Vcs-Browser: https://salsa.debian.org/games-team/pathological
1818
1919 Package: pathological
2020 Architecture: all
0 Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
0 Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
11 Upstream-Name: Pathological
22 Source: http://pathological.sourceforge.net/
33
1212 2009, Cyril Brulebois, Eddy Petrișor, Gonéri Le Bouder
1313 Ansgar Burchardt
1414 2012, Evgeniy Dolgikh
15 2013-2015, Markus Koschany <apo@debian.org>
15 2013-2018, Markus Koschany <apo@debian.org>
1616 License: GPL-2+
1717
1818 License: GPL-2+
+0
-6
debian/pathological.menu less more
0 ?package(pathological):needs="X11" \
1 section="Games/Puzzles" \
2 title="Pathological" \
3 longtitle="Pathological - puzzle game involving paths and marbles" \
4 command="/usr/games/pathological" \
5 icon="/usr/share/pixmaps/pathological.xpm"
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
0 <html>
1 <head>
2 <title>How to Play Pathological</title>
3 <link rel="icon" href="favicon.ico" type="image/x-icon">
4 <link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
5 </head>
6 <body bgcolor="#ffffff">
7
8 <table border=0 cellpadding=0 cellspacing=10><tr>
9 <td colspan=2>
10 <h1>How to Play Pathological</h1>
11 <a href="http://pathological.sourceforge.net/">Pathological Homepage</a>
12 <p>
13 Pathological is a puzzle game consisting of marbles that roll along
14 paths, and interact along the way with various devices and gadgets.
15 </p>
16 <p>
17 The object of each level is to "complete" all of the wheels on the
18 board. To complete a wheel, catch four marbles of matching color in the
19 same wheel. The marbles will vanish, and the wheel will turn dark to
20 indicate that it has been completed. Note that this is only the
21 <i>usual</i> way of completing a wheel. The exceptional cases are
22 described in the section on <b>Triggers</b> and <b>Stoplights</b>.
23 </p>
24 <p>
25 <b>To rotate a wheel</b>, click the right mouse button on the wheel.
26 <b>To eject a marble from a wheel</b>, click the left mouse button on the
27 marble.
28 </p>
29 <p>
30 When the game begins, you are assigned a total of three lives. Your spare
31 lives are depicted by small red marbles at the top of the screen. An extra
32 life is awarded every 5000 points. A maximum of ten spare lives is
33 enforced.
34 </p>
35 <h3>The Board Timer</h3>
36 <p>
37 The board timer is the maximum amount of time allowed to complete a
38 level. It is depicted as a digital countdown at the top of the screen.
39 If the countdown reaches zero, the level ends and you lose a life. If
40 you complete the level within the available time, a bonus is awarded
41 based on the percentage of time remaining.
42 </p>
43 <h3>The Launch Timer</h3>
44 <p>
45 The launch timer, depicted as a blue bar on the left-hand side of the
46 screen, represents the maximum amount of time that a newly launched
47 marble can remain in the launch area at the top of the board. Each time
48 a marble is launched, the launch timer is reset to its full value.
49 If the launch timer expires, the level ends and you lose a life.
50 </p>
51 <p>
52 The length of the launch timer is displayed numerically at the bottom of
53 the blue bar. The number represents the number of passes that a newly
54 launched marble can make before the timer expires. The value is
55 different on different levels.
56 </p>
57 <h3>The Active Marble Limit</h3>
58 <p>
59 Each level has a maximum number of marbles that are allowed to be in
60 motion at a time. As long as the maximum number of marbles are in
61 motion, you can not eject any more marbles from wheels.
62 </p>
63 <p>
64 The number of currently active marbles, and the active marble limit are shown
65 numerically at the left edge of the launch area at the top of the board.
66 </p>
67
68 <h2>Game Elements</h2>
69
70 </td></tr><tr><td valign=top>
71 <h3>Buffers</h3>
72 <p>
73 Buffers catch and hold a single marble at a time. The marble is held
74 until another marble knocks it out, and takes its place. Note that once
75 a marble falls into a buffer, there is no way of emptying the buffer
76 again.
77 </p>
78 </td><td valign=top align=center>
79 <table border=0 cellpadding=8 cellspacing=0><tr>
80 <td align=center valign=top>
81 <img src="buffer.png" width=92 height=92><br>
82 An empty buffer
83 </td><td align=center valign=top>
84 <img src="buffer2.png" width=92 height=92><br>
85 A buffer holding a purple marble
86 </td></tr></table>
87
88 </td></tr><tr><td valign=top>
89 <h3>Directors</h3>
90 <p>
91 Directors force all marbles to exit in the indicated direction. They are
92 often used as one-way gates, allowing marbles to pass through in one
93 direction but not the other.
94 </p>
95 </td><td valign=top align=center>
96 <img src="director.png" width=92 height=92><br>
97 A director
98
99 </td></tr><tr><td valign=top>
100 <h3>Filters</h3>
101 <p>
102 Filters only allow the indicated color of marble to pass through.
103 Any other color of marble
104 will simply bounce back in the direction that it came from.
105 </p>
106 </td><td valign=top align=center>
107 <img src="filter.png" width=92 height=92><br>
108 An orange filter
109
110 </td></tr><tr><td valign=top>
111 <h3>Painters</h3>
112 <p>
113 Painters change the color of passing marbles to the indicated color.
114 </p>
115 </td><td valign=top align=center>
116 <img src="painter.png" width=92 height=92><br>
117 A blue painter
118
119 </td></tr><tr><td valign=top>
120 <h3>Replicators</h3>
121 <p>
122 Replicators create copies of marbles. Whenever a marble enters a
123 replicator, at least two marbles are emitted. Some replicators produce
124 more copies than others.
125 </p>
126 <p>
127 The number of copies produced by a replicator may be limited by the
128 active marbles limit.
129 </p>
130 </td><td valign=top align=center>
131 <img src="replicator.png" width=92 height=92><br>
132 A replicator
133
134 </td></tr><tr><td valign=top>
135 <h3>Shredders</h3>
136 <p>
137 Shredders simply destroy any marbles that enter into them.
138 </p>
139 </td><td valign=top align=center>
140 <img src="shredder.png" width=92 height=92><br>
141 A shredder
142
143 </td></tr><tr><td valign=top>
144 <h3>Switches</h3>
145 <p>
146 Switches, like directors, force marbles to exit in the indicated
147 direction. But unlike directors, switches switch between two different
148 directions. Each passing marble causes them to switch.
149 </p>
150 </td><td valign=top align=center>
151 <img src="switch.png" width=92 height=92><br>
152 A switch
153
154 </td></tr><tr><td valign=top>
155 <h3>Teleporters</h3>
156 <p>
157 Teleporters transport passing marbles to their twin teleporter at some
158 other location on the board. Note that a board can have several pairs of
159 teleporters, and their pairings might not be obvious. It can be
160 helpful to realize that teleporters can only change the location of a
161 marble - not the direction.
162 </p>
163 </td><td valign=top align=center>
164 <img src="teleporter.png" width=92 height=92><br>
165 A teleporter
166
167 </td></tr><tr><td valign=top>
168 <h3>Triggers and Stoplights</h3>
169 <p>
170 Triggers and stoplights place temporary restrictions on wheel completion.
171 </p>
172 <p>
173 If a board contains a <b>stoplight</b>, then the first three wheels to
174 be completed must be completed, respectively, using the three colors of
175 the stoplight <i>in order from top to bottom</i>. For example, if the
176 stoplight has the colors red, yellow and green (as in the diagram), then
177 you must first complete a wheel using four red marbles. Then the second
178 wheel must be completed using four yellow marbles, and the third must be
179 completed using four green marbles. After the stoplight
180 has been fully exhausted, then the rest of the wheels on the board can
181 be completed using any colors as per usual.
182 </p>
183 <p>
184 If a board contains a trigger, then the first wheel to be completed must
185 be completed by <i>exactly</i> matching the marble configuration presented
186 by the trigger. Trigger configurations are chosen randomly. After the
187 trigger has been satisfied, the rest of the wheels on the board can be
188 completed in the normal way. But beware! A trigger will only stay
189 satisfied for a short time.
190 </p>
191 <p>
192 A board can only have at most one stoplight or trigger, but it <i>can
193 have one of each</i>. If a board has both a stoplight and a trigger, you
194 must satisfy the trigger before working toward exhausting the stoplight.
195 </p>
196 </td><td valign=top align=center>
197 <table border=0 cellpadding=8 cellspacing=0><tr>
198 <td align=center valign=top>
199 <img src="stoplight.png" width=92 height=92><br>
200 A stoplight
201 </td><td align=center valign=top>
202 <img src="trigger.png" width=92 height=92><br>
203 A trigger
204 </td></tr><tr><td align=center valign=top>
205 <img src="stoplight2.png" width=92 height=92><br>
206 A partially-exhausted stoplight
207 </td><td align=center valign=top>
208 <img src="trigger2.png" width=92 height=92><br>
209 A satisfied trigger
210 </td></tr></table>
211
212 </td></tr><tr><td valign=top colspan=2>
213 <h2>Controls</h2>
214 <table border=1 cellpadding=4 cellspacing=0>
215 <tr><th>Action</th><th>Key/Button</th></tr>
216 <tr><td>Eject a marble from a wheel</td><td>Left mouse button</td></tr>
217 <tr><td>Rotate a wheel</td><td>Right mouse button</td></tr>
218 <tr><td>Abort a level</td><td>ESC</td></tr>
219 <tr><td>Pause the game</td><td>P, SPACE or PAUSE</td></tr>
220 <tr><td>Toggle fullscreen mode</td><td>F2</td></tr>
221 <tr><td>Toggle sound effects</td><td>F3</td></tr>
222 <tr><td>Toggle music</td><td>F4</td></tr>
223 </table>
224
225 <h2>Scoring</h2>
226 <table border=1 cellpadding=4 cellspacing=0>
227 <tr><th>Action</th><th>Points<br>Awarded</th></tr>
228 <tr><td>Completing a wheel</td><td align=right>50</td></tr>
229 <tr><td>Satisfying a trigger</td><td align=right>50</td></tr>
230 <tr><td>Exhausting one light on a stoplight</td><td align=right>20</td></tr>
231 <tr><td>Completing an already-completed wheel</td><td align=right>10</td></tr>
232 <tr><td>Time bonus per % time remaining</td><td align=right>5</td></tr>
233 <tr><td>Bonus per % holes empty</td><td align=right>2</td></tr>
234 </table>
235
236 </td></tr></table>
237
238 <hr>
239 Please send questions or comments to <a href="mailto:jp@gignac.org">jp@gignac.org</a>
240
241 </body>
242 </html>
0 #!/bin/bash
1
2 G=graphics
3 H=html
4
5 # Make a blank tile
6 pngtopnm $G/blank-bg-tile.png > blank.ppm 2>/dev/null
7
8 # Make a NESW path
9 pngtopnm $G/path-15.png > a.ppm
10 ppmcolormask '#ce3535' a.ppm > mask.pbm
11 pnmcomp -alpha=mask.pbm a.ppm blank.ppm path-15.ppm
12
13 # Make a NW path
14 pngtopnm $G/path-9.png > a.ppm
15 ppmcolormask '#ce3535' a.ppm > mask.pbm
16 pnmcomp -alpha=mask.pbm a.ppm blank.ppm path-9.ppm
17
18 # Make a NESW tunnel
19 pngtopnm $G/tunnel-15.png > a.ppm
20 ppmcolormask '#cc26c6' a.ppm > mask.pbm
21 pnmcomp -alpha=mask.pbm a.ppm path-15.ppm tunnel-15.ppm
22
23 # Make a NW tunnel
24 pngtopnm $G/tunnel-9.png > a.ppm
25 ppmcolormask '#cc26c6' a.ppm > mask.pbm
26 pnmcomp -alpha=mask.pbm a.ppm path-9.ppm tunnel-9.ppm
27
28 # Add a buffer
29 pngtopnm $G/buffer.png > a.ppm
30 ppmcolormask '#cc26c6' a.ppm > mask.pbm
31 pnmcomp -alpha=mask.pbm a.ppm tunnel-15.ppm b.ppm
32 pngtopnm $G/buffer-top.png > a.ppm
33 ppmcolormask '#cc26c6' a.ppm > mask.pbm
34 pnmcomp -alpha=mask.pbm a.ppm b.ppm buffer.ppm
35 pnmtopng buffer.ppm > $H/buffer.png
36
37 # Add a marble
38 pngtopnm $G/marble-5.png > a.ppm
39 ppmcolormask '#ee00ff' a.ppm > mask.pbm
40 pnmcomp -xoff=32 -yoff=32 -alpha=mask.pbm a.ppm buffer.ppm buffer2.ppm
41 pnmtopng buffer2.ppm > $H/buffer2.png
42
43 # Make a director
44 pngtopnm $G/director-0.png > a.ppm
45 ppmcolormask '#cc26c6' a.ppm > mask.pbm
46 pnmcomp -alpha=mask.pbm a.ppm tunnel-9.ppm b.ppm
47 pnmtopng b.ppm > $H/director.png
48
49 # Make a filter
50 pngtopnm $G/filter-7.png > a.ppm
51 ppmcolormask '#cc26c6' a.ppm > mask.pbm
52 pnmcomp -alpha=mask.pbm a.ppm tunnel-9.ppm b.ppm
53 pnmtopng b.ppm > $H/filter.png
54
55 # Make a painter
56 pngtopnm $G/painter-2.png > a.ppm
57 ppmcolormask '#cc26c6' a.ppm > mask.pbm
58 pnmcomp -alpha=mask.pbm a.ppm tunnel-9.ppm b.ppm
59 pnmtopng b.ppm > $H/painter.png
60
61 # Make a replicator
62 pngtopnm $G/replicator.png > a.ppm
63 ppmcolormask '#cc26c6' a.ppm > mask.pbm
64 pnmcomp -alpha=mask.pbm a.ppm tunnel-9.ppm b.ppm
65 pnmtopng b.ppm > $H/replicator.png
66
67 # Make a shredder
68 pngtopnm $G/shredder.png > a.ppm
69 ppmcolormask '#cc26c6' a.ppm > mask.pbm
70 pnmcomp -alpha=mask.pbm a.ppm tunnel-9.ppm b.ppm
71 pnmtopng b.ppm > $H/shredder.png
72
73 # Make a switch
74 pngtopnm $G/switch-03.png > a.ppm
75 ppmcolormask '#cc26c6' a.ppm > mask.pbm
76 pnmcomp -alpha=mask.pbm a.ppm tunnel-9.ppm b.ppm
77 pnmtopng b.ppm > $H/switch.png
78
79 # Make a teleporter
80 pngtopnm $G/teleporter-h.png > a.ppm
81 ppmcolormask '#cc26c6' a.ppm > mask.pbm
82 pnmcomp -alpha=mask.pbm a.ppm tunnel-9.ppm b.ppm
83 pnmtopng b.ppm > $H/teleporter.png
84
85 # Make the stoplights
86 pngtopnm $G/stoplight.png > a.ppm
87 ppmcolormask '#cc26c6' a.ppm > mask.pbm
88 pnmcomp -alpha=mask.pbm a.ppm blank.ppm stoplight-0.ppm
89 pngtopnm $G/marble-3.png > a.ppm
90 ppmcolormask '#ee00ff' a.ppm > mask.pbm
91 pnmcomp -xoff=32 -yoff=61 -alpha=mask.pbm a.ppm stoplight-0.ppm stoplight-1.ppm
92 pngtopnm $G/marble-4.png > a.ppm
93 ppmcolormask '#ee00ff' a.ppm > mask.pbm
94 pnmcomp -xoff=32 -yoff=32 -alpha=mask.pbm a.ppm stoplight-1.ppm stoplight-2.ppm
95 pngtopnm $G/marble-6.png > a.ppm
96 ppmcolormask '#ee00ff' a.ppm > mask.pbm
97 pnmcomp -xoff=32 -yoff=3 -alpha=mask.pbm a.ppm stoplight-2.ppm stoplight-3.ppm
98 pnmtopng stoplight-3.ppm > $H/stoplight.png
99 pnmtopng stoplight-2.ppm > $H/stoplight2.png
100
101 # Make the triggers
102 pngtopnm $G/trigger.png > a.ppm
103 ppmcolormask '#cc26c6' a.ppm > mask.pbm
104 pnmcomp -alpha=mask.pbm a.ppm blank.ppm trigger-0.ppm
105 pngtopnm $G/marble-6.png > a.ppm
106 ppmcolormask '#ee00ff' a.ppm > mask.pbm
107 pnmcomp -xoff=32 -yoff=4 -alpha=mask.pbm a.ppm trigger-0.ppm trigger-1.ppm
108 pngtopnm $G/marble-6.png > a.ppm
109 ppmcolormask '#ee00ff' a.ppm > mask.pbm
110 pnmcomp -xoff=60 -yoff=32 -alpha=mask.pbm a.ppm trigger-1.ppm trigger-2.ppm
111 pngtopnm $G/marble-3.png > a.ppm
112 ppmcolormask '#ee00ff' a.ppm > mask.pbm
113 pnmcomp -xoff=32 -yoff=60 -alpha=mask.pbm a.ppm trigger-2.ppm trigger-3.ppm
114 pngtopnm $G/marble-2.png > a.ppm
115 ppmcolormask '#ee00ff' a.ppm > mask.pbm
116 pnmcomp -xoff=4 -yoff=32 -alpha=mask.pbm a.ppm trigger-3.ppm trigger-4.ppm
117 pnmtopng trigger-4.ppm > $H/trigger.png
118 pnmtopng trigger-0.ppm > $H/trigger2.png
119
120 # Make a wheel
121 pngtopnm $G/wheel.png > a.ppm
122 ppmcolormask '#ea1515' a.ppm > mask.pbm
123 pnmcomp -alpha=mask.pbm a.ppm path-15.ppm wheel-0.ppm
124 pngtopnm $G/marble-4.png > a.ppm
125 ppmcolormask '#ee00ff' a.ppm > mask.pbm
126 pnmcomp -xoff=32 -yoff=4 -alpha=mask.pbm a.ppm wheel-0.ppm wheel-1.ppm
127 pnmtopng wheel-1.ppm > $H/wheel.png
128
129 # Delete all of the temporary files
130 rm *.ppm *.pbm
Binary diff not shown
Binary diff not shown
0 #!/bin/bash
1
2 function usage () {
3 echo "Usage: $0 [-cb] [-f] [-s] [-q] [highscores-file]"
4 exit 1
5 }
6
7 options=""
8 scoresfile=""
9
10 for arg in $*; do
11 if [ "${arg:0:1}" = "-" ]; then
12 if [ "$arg" != "-cb" -a \
13 "$arg" != "-f" -a \
14 "$arg" != "-s" -a \
15 "$arg" != "-q" ]; then
16 usage
17 fi
18 options="$options $arg"
19 elif [ -n "$scoresfile" ]; then
20 usage
21 else
22 if [ ! -d "`dirname "$arg"`" ]; then
23 echo No such file: "$arg"
24 exit 1
25 fi
26 pushd "`dirname "$arg"`" >/dev/null
27 scoresfile="`/bin/pwd`/`basename "$arg"`"
28 popd >/dev/null
29 fi
30 done
31
32 cd /usr/share/games/pathological
33
34 if [ -z "$scoresfile" ]; then
35 scoresfile=/var/games/pathological_scores
36 fi
37
38 exec ./pathological.py $options $scoresfile
Binary diff not shown
Binary diff not shown
0 #!/usr/bin/python
1 """
2 Copyright (C) 2003 John-Paul Gignac
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 """
18
19 # Import Modules
20 import os, pygame, random, time, math, re, sys, md5
21 from pygame.locals import *
22
23 # Parse the command line
24 highscores_file = "pathological_scores"
25 screenshot = 0
26 fullscreen = 0
27 colorblind = 0
28 sound_on = 1
29 music_on = 1
30 for arg in sys.argv[1:]:
31 if arg == '-s':
32 screenshot = 1
33 elif arg == '-f':
34 fullscreen = 1
35 elif arg == '-cb':
36 colorblind = 1
37 elif arg == '-q':
38 sound_on = 0
39 music_on = 0
40 elif arg[0] == '-':
41 print "Usage: "+sys.argv[0]+" [-cb] [-f] [-s] [highscores-file]\n"
42 sys.exit(1)
43 else:
44 highscores_file = arg
45
46 if colorblind:
47 cbext = '-cb.png'
48 else:
49 cbext = '.png'
50
51 # The location of the setgid script for writing highscores
52 # This script is only used if the highscores file is not writable directly
53 write_highscores = "/usr/lib/pathological/bin/write-highscores"
54
55 # Game constants
56 wheel_steps = 9
57 frames_per_sec = 100
58 timer_width = 36
59 timer_margin = 4
60 info_height = 20
61 initial_lives = 3
62 extra_life_frequency = 5000 # Extra life awarded every this many points
63 max_spare_lives = 10
64
65 # Volume levels
66 intro_music_volume = 0.6
67 ingame_music_volume = 0.9
68 sound_effects_volume = 0.6
69
70 # Changing these may affect the playability of levels
71 default_colors = (2,3,4,6) # Blue, Green, Yellow, Red
72 default_stoplight = (6,4,3) # Red, Yellow, Green
73 default_launch_timer = 6 # 6 passes
74 default_board_timer = 30 # 30 seconds per wheel
75 marble_speed = 2 # Marble speed in pixels/frame (must be 1, 2 or 4)
76 trigger_time = 30 # 30 seconds
77 replicator_delay = 35 # 35 frames
78
79 # Don't change these constants unless you
80 # redo all of the levels
81 horiz_tiles = 8
82 vert_tiles = 6
83
84 # Don't change these constants unless you
85 # update the graphics files correspondingly.
86 screen_width = 800
87 screen_height = 600
88 marble_size = 28
89 tile_size = 92
90 wheel_margin = 4
91 stoplight_marble_size = 28
92 life_marble_size = 16
93
94 # The positions of the holes in the wheels in
95 # each of the three rotational positions
96 holecenter_radius = (tile_size - marble_size) / 2 - wheel_margin
97 holecenters = []
98 for i in range(wheel_steps):
99 theta = math.pi * i / (2 * wheel_steps)
100 c = math.floor( 0.5 + math.cos(theta)*holecenter_radius)
101 s = math.floor( 0.5 + math.sin(theta)*holecenter_radius)
102 holecenters.append((
103 (tile_size/2 + s, tile_size/2 - c),
104 (tile_size/2 + c, tile_size/2 + s),
105 (tile_size/2 - s, tile_size/2 + c),
106 (tile_size/2 - c, tile_size/2 - s)))
107
108 # Direction references
109 dirs = ((0,-1),(1,0),(0,1),(-1,0))
110
111 # More global variables
112 board_width = horiz_tiles * tile_size
113 board_height = vert_tiles * tile_size
114 launch_timer_pos = (0,info_height)
115 board_pos = (timer_width, info_height + marble_size)
116 timer_height = board_height + marble_size
117 music_loaded = 0
118
119 # Functions to create our resources
120 def load_image(name, colorkey=-1, size=None):
121 fullname = os.path.join('graphics', name)
122 try:
123 image = pygame.image.load(fullname)
124 except pygame.error, message:
125 print 'Cannot load image:', fullname
126 raise SystemExit, message
127
128 if size is not None:
129 image = pygame.transform.scale( image, size)
130 image = image.convert()
131
132 if colorkey is not None:
133 if colorkey is -1:
134 colorkey = image.get_at((0,0))
135 image.set_colorkey(colorkey, RLEACCEL)
136 return image
137
138 def load_sound(name, volume=1.0):
139 class NoneSound:
140 def play(self): pass
141 if not pygame.mixer or not pygame.mixer.get_init():
142 return NoneSound()
143 fullname = os.path.join('sounds', name)
144 try:
145 sound = pygame.mixer.Sound(fullname)
146 except pygame.error, message:
147 print 'Cannot load sound:', fullname
148 return NoneSound()
149
150 sound.set_volume( volume * sound_effects_volume)
151
152 return sound
153
154 def play_sound(sound):
155 if sound_on: sound.play()
156
157 def start_music(name, volume=-1):
158 global music_pending_song, music_loaded, music_volume
159
160 music_volume = volume
161
162 if not music_on:
163 music_pending_song = name
164 return
165
166 if not pygame.mixer or not pygame.mixer.music:
167 print "Background music not available."
168 return
169 fullname = os.path.join('music', name)
170 try:
171 pygame.mixer.music.load(fullname)
172 except pygame.error, message:
173 print 'Cannot load music:', fullname
174 return
175 music_loaded = 1
176 pygame.mixer.music.play(-1)
177
178 if music_volume >= 0:
179 pygame.mixer.music.set_volume( music_volume)
180
181 music_pending_song = 0
182
183 def toggle_fullscreen():
184 global fullscreen
185 if pygame.display.toggle_fullscreen():
186 fullscreen = fullscreen ^ 1
187 return 1
188 else:
189 return 0
190
191 def toggle_sound():
192 global sound_on
193 sound_on = sound_on ^ 1
194
195 def toggle_music():
196 global music_on
197 music_on = music_on ^ 1
198 if music_on:
199 if music_pending_song:
200 start_music( music_pending_song)
201 elif music_loaded:
202 pygame.mixer.music.unpause()
203 elif music_loaded:
204 if not music_pending_song:
205 pygame.mixer.music.pause()
206
207 # A better tick function
208 next_frame = pygame.time.get_ticks()
209 def my_tick( frames_per_sec):
210 global next_frame
211 # Wait for the next frame
212 next_frame += 1000.0 / frames_per_sec
213 now = pygame.time.get_ticks()
214 if next_frame < now:
215 # No time to wait - just hide our mistake
216 # and keep going as fast as we can.
217 next_frame = now
218 else:
219 pygame.time.wait( int(next_frame) - now)
220
221 # Load the sounds
222 def load_sounds():
223 global filter_admit,wheel_turn,wheel_completed,change_color
224 global direct_marble,ping,trigger_setup,teleport,marble_release
225 global levelfinish,die,incorrect,switch,shredder,replicator
226 global extra_life,menu_scroll,menu_select
227
228 filter_admit = load_sound('filter_admit.wav', 0.8)
229 wheel_turn = load_sound('wheel_turn.wav', 0.8)
230 wheel_completed = load_sound('wheel_completed.wav', 0.7)
231 change_color = load_sound('change_color.wav', 0.8)
232 direct_marble = load_sound('direct_marble.wav', 0.6)
233 ping = load_sound('ping.wav', 0.8)
234 trigger_setup = load_sound('trigger_setup.wav')
235 teleport = load_sound('teleport.wav', 0.6)
236 marble_release = load_sound('marble_release.wav', 0.5)
237 levelfinish = load_sound('levelfinish.wav', 0.6)
238 die = load_sound('die.wav')
239 incorrect = load_sound('incorrect.wav', 0.15)
240 switch = load_sound('switch.wav')
241 shredder = load_sound('shredder.wav')
242 replicator = load_sound('replicator.wav')
243 extra_life = load_sound('extra_life.wav')
244 menu_scroll = load_sound('menu_scroll.wav', 0.8)
245 menu_select = load_sound('switch.wav')
246
247 # Load the fonts for various parts of the game
248 def load_fonts():
249 global launch_timer_font,active_marbles_font,popup_font,info_font
250
251 launch_timer_font = pygame.font.Font(None, timer_width - 2*timer_margin)
252 active_marbles_font = pygame.font.Font(None, marble_size)
253 popup_font = pygame.font.Font(None, 24)
254 info_font = pygame.font.Font(None, info_height)
255
256 # Load all of the images for the various game classes.
257 # The images are stored as class variables in the corresponding classes.
258 def load_images():
259 Marble.images = []
260 for i in range(9):
261 Marble.images.append( load_image('marble-'+`i`+cbext, -1,
262 (marble_size, marble_size)))
263
264 Tile.plain_tiles = []
265 Tile.tunnels = []
266 for i in range(16):
267 tile = load_image('tile.png', (206,53,53), (tile_size,tile_size))
268 path = load_image('path-'+`i`+'.png', -1, (tile_size,tile_size))
269 tile.blit( path, (0,0))
270 Tile.plain_tiles.append( tile)
271 Tile.tunnels.append(load_image('tunnel-'+`i`+'.png',
272 -1,(tile_size,tile_size)))
273 Tile.paths = 0
274
275 Wheel.images = (
276 load_image('wheel.png',-1,(tile_size,tile_size)),
277 load_image('wheel-dark.png',-1,(tile_size, tile_size)),
278 )
279 Wheel.blank_images = (
280 load_image('blank-wheel.png',-1,(tile_size,tile_size)),
281 load_image('blank-wheel-dark.png',-1,(tile_size, tile_size)),
282 )
283 Wheel.moving_holes = (
284 load_image('moving-hole.png',-1,(marble_size,marble_size)),
285 load_image('moving-hole-dark.png',-1,(marble_size, marble_size)),
286 )
287
288 Buffer.bottom = load_image('buffer.png',-1,(tile_size,tile_size))
289 Buffer.top = load_image('buffer-top.png',-1,(tile_size,tile_size))
290
291 Painter.images = []
292 for i in range(8):
293 Painter.images.append( load_image('painter-'+`i`+cbext, -1,
294 (tile_size,tile_size)))
295
296 Filter.images = []
297 for i in range(8):
298 Filter.images.append( load_image('filter-'+`i`+cbext, -1,
299 (tile_size,tile_size)))
300
301 Director.images = (
302 load_image('director-0.png',-1,(tile_size,tile_size)),
303 load_image('director-1.png',-1,(tile_size,tile_size)),
304 load_image('director-2.png',-1,(tile_size,tile_size)),
305 load_image('director-3.png',-1,(tile_size,tile_size)),
306 )
307
308 Shredder.image = load_image('shredder.png',-1,(tile_size,tile_size))
309
310 Switch.images = []
311 for i in range(4):
312 Switch.images.append( [])
313 for j in range(4):
314 if i == j: Switch.images[i].append( None)
315 else: Switch.images[i].append( load_image(
316 'switch-'+`i`+`j`+'.png',-1,(tile_size,tile_size)))
317
318 Replicator.image = load_image('replicator.png',-1,(tile_size,tile_size))
319
320 Teleporter.image_h = load_image('teleporter-h.png',-1,(tile_size,tile_size))
321 Teleporter.image_v = load_image('teleporter-v.png',-1,(tile_size,tile_size))
322
323 Trigger.image = load_image('trigger.png',-1,(tile_size,tile_size))
324
325 Stoplight.image = load_image('stoplight.png',-1,(tile_size,tile_size))
326 Stoplight.smallmarbles = []
327 for im in Marble.images:
328 Stoplight.smallmarbles.append( pygame.transform.scale(im,
329 (stoplight_marble_size,stoplight_marble_size)))
330
331 Board.life_marble = load_image('life-marble.png', -1,
332 (life_marble_size, life_marble_size))
333 Board.launcher_background = load_image('launcher.png', None,
334 (horiz_tiles * tile_size,marble_size))
335 Board.launcher_v = load_image('launcher-v.png', None,
336 (marble_size, vert_tiles * tile_size + marble_size))
337 Board.launcher_corner = load_image('launcher-corner.png', (255,0,0),
338 ((tile_size-marble_size)/2+marble_size,marble_size))
339 Board.launcher_entrance = load_image('entrance.png', -1,
340 (tile_size,marble_size))
341
342 IntroScreen.background = load_image('intro.png', None,
343 (screen_width, screen_height))
344 IntroScreen.menu_font = pygame.font.Font(
345 None, IntroScreen.menu_font_height)
346 IntroScreen.scroller_font = pygame.font.Font(
347 None, IntroScreen.scroller_font_height)
348 IntroScreen.hs_font = pygame.font.Font(
349 None, IntroScreen.hs_font_height)
350
351 # Function to set the video mode
352 def set_video_mode():
353 global screen
354
355 icon = pygame.image.load(os.path.join('graphics','icon.png'))
356 icon.set_colorkey(icon.get_at((0,0)), RLEACCEL)
357 pygame.display.set_icon(icon) # Needed both before and after set_mode
358 screen = pygame.display.set_mode( (screen_width, screen_height),
359 fullscreen * FULLSCREEN)
360 pygame.display.set_icon(icon) # Needed both before and after set_mode
361 pygame.display.set_caption('Pathological')
362
363 # Classes for our game objects
364
365 class Marble:
366 def __init__(self, color, center, direction):
367 self.color = color
368 self.rect = pygame.Rect((0,0,marble_size,marble_size))
369 self.rect.center = center
370 self.direction = direction
371
372 def update(self, board):
373 self.rect.move_ip(
374 marble_speed * dirs[self.direction][0],
375 marble_speed * dirs[self.direction][1])
376
377 board.affect_marble( self)
378
379 def undraw(self, screen, background):
380 screen.set_clip( self.rect)
381 screen.blit( background, (0,0))
382 screen.set_clip()
383
384 def draw(self, screen):
385 screen.blit( self.images[self.color], self.rect.topleft)
386
387 class Tile:
388 def __init__(self, paths=0, center=None):
389 self.paths = paths
390
391 if center is None:
392 center = (0,0)
393
394 self.center = center
395 self.rect = pygame.Rect((0,0,tile_size,tile_size))
396 self.rect.center = center
397 self.drawn = 0
398
399 def draw_back(self, surface):
400 if self.drawn: return 0
401 surface.blit( self.plain_tiles[self.paths], self.rect.topleft)
402 self.drawn = 1
403 return 1
404
405 def update(self, board): pass
406
407 def draw_fore(self, surface): return 0
408
409 def click(self, board, posx, posy, tile_x, tile_y): pass
410
411 def affect_marble(self, board, marble, rpos):
412 if rpos == (tile_size/2,tile_size/2):
413 if self.paths & (1 << marble.direction): return
414
415 # Figure out the new direction
416 t = self.paths - (1 << (marble.direction^2))
417 if t == 1: marble.direction = 0
418 elif t == 2: marble.direction = 1
419 elif t == 4: marble.direction = 2
420 elif t == 8: marble.direction = 3
421 else: marble.direction = marble.direction ^ 2
422
423 class Wheel(Tile):
424 def __init__(self, paths, center=None):
425 Tile.__init__(self, paths, center) # Call base class intializer
426 self.spinpos = 0
427 self.completed = 0
428 self.marbles = [ -3, -3, -3, -3 ]
429
430 def draw_back(self, surface):
431 if self.drawn: return 0
432
433 Tile.draw_back(self, surface)
434
435 if self.spinpos:
436 surface.blit( self.blank_images[self.completed], self.rect.topleft)
437 for i in range(4):
438 holecenter = holecenters[self.spinpos][i]
439 surface.blit( self.moving_holes[self.completed],
440 (holecenter[0]-marble_size/2+self.rect.left,
441 holecenter[1]-marble_size/2+self.rect.top))
442 else:
443 surface.blit( self.images[self.completed], self.rect.topleft)
444
445 for i in range(4):
446 color = self.marbles[i]
447 if color >= 0:
448 holecenter = holecenters[self.spinpos][i]
449 surface.blit( Marble.images[color],
450 (holecenter[0]-marble_size/2+self.rect.left,
451 holecenter[1]-marble_size/2+self.rect.top))
452
453 return 1
454
455 def update(self, board):
456 if self.spinpos > 0:
457 self.spinpos -= 1
458 self.drawn = 0
459
460 def click(self, board, posx, posy, tile_x, tile_y):
461 # Ignore all clicks while rotating
462 if self.spinpos: return
463
464 b1, b2, b3 = pygame.mouse.get_pressed()
465 if b3:
466 # First, make sure that no marbles are currently entering
467 for i in self.marbles:
468 if i == -1 or i == -2: return
469
470 # Start the wheel spinning
471 self.spinpos = wheel_steps - 1
472 play_sound( wheel_turn)
473
474 # Reposition the marbles
475 t = self.marbles[0]
476 self.marbles[0] = self.marbles[1]
477 self.marbles[1] = self.marbles[2]
478 self.marbles[2] = self.marbles[3]
479 self.marbles[3] = t
480
481 self.drawn = 0
482
483 elif b1:
484 # Determine which hole is being clicked
485 for i in range(4):
486 # If there is no marble here, skip it
487 if self.marbles[i] < 0: continue
488
489 holecenter = holecenters[0][i]
490 rect = pygame.Rect( 0, 0, marble_size, marble_size)
491 rect.center = holecenter
492 if rect.collidepoint( posx, posy):
493
494 # Determine the neighboring tile
495 neighbor = board.tiles[ (tile_y + dirs[i][1]) %
496 vert_tiles][ (tile_x + dirs[i][0]) % horiz_tiles]
497
498 if (
499 # Disallow marbles to go off the top of the board
500 (tile_y == 0 and i==0) or
501
502 # If there is no way out here, skip it
503 ((self.paths & (1 << i)) == 0) or
504
505 # If the neighbor is a wheel that is either turning
506 # or has a marble already in the hole, disallow
507 # the ejection
508 (isinstance(neighbor, Wheel) and
509 (neighbor.spinpos or
510 neighbor.marbles[i^2] != -3))
511 ):
512 play_sound( incorrect)
513 else:
514 # If the neighbor is a wheel, apply a special lock
515 if isinstance(neighbor, Wheel):
516 neighbor.marbles[i^2] = -2
517 elif len(board.marbles) >= board.live_marbles_limit:
518 # Impose the live marbles limit
519 play_sound( incorrect)
520 break
521
522 # Eject the marble
523 board.marbles.append(
524 Marble( self.marbles[i],
525 (holecenter[0]+self.rect.left,
526 holecenter[1]+self.rect.top),
527 i))
528 self.marbles[i] = -3
529 play_sound( marble_release)
530 self.drawn = 0
531
532 break
533
534 def affect_marble(self, board, marble, rpos):
535 # Watch for marbles entering
536 if rpos[0]+marble_size/2 == wheel_margin or \
537 rpos[0]-marble_size/2 == tile_size - wheel_margin or \
538 rpos[1]+marble_size/2 == wheel_margin or \
539 rpos[1]-marble_size/2 == tile_size - wheel_margin:
540 if self.spinpos or self.marbles[marble.direction^2] >= -1:
541 # Reject the marble
542 marble.direction = marble.direction ^ 2
543 play_sound( ping)
544 else:
545 self.marbles[marble.direction^2] = -1
546
547 for holecenter in holecenters[0]:
548 if rpos == holecenter:
549 # Accept the marble
550 board.marbles.remove( marble)
551 self.marbles[marble.direction^2] = marble.color
552
553 self.drawn = 0
554
555 break
556
557 def complete(self, board):
558 # Complete the wheel
559 for i in range(4): self.marbles[i] = -3
560 if self.completed: board.game.increase_score( 10)
561 else: board.game.increase_score( 50)
562 self.completed = 1
563 play_sound( wheel_completed)
564 self.drawn = 0
565
566 def maybe_complete(self, board):
567 if self.spinpos > 0: return 0
568
569 # Is there a trigger?
570 if (board.trigger is not None) and \
571 (board.trigger.marbles is not None):
572 # Compare against the trigger
573 for i in range(4):
574 if self.marbles[i] != board.trigger.marbles[i] and \
575 self.marbles[i] != 8: return 0
576 self.complete( board)
577 board.trigger.complete( board)
578 return 1
579
580 # Do we have four the same color?
581 color = 8
582 for c in self.marbles:
583 if c < 0: return 0
584 if color==8: color=c
585 elif c==8: c=color
586 elif c != color: return 0
587
588 # Is there a stoplight?
589 if (board.stoplight is not None) and \
590 (board.stoplight.current < 3):
591 # Compare against the stoplight
592 if color != 8 and \
593 color != board.stoplight.marbles[board.stoplight.current]:
594 return 0
595 else:
596 board.stoplight.complete( board)
597
598 self.complete( board)
599 return 1
600
601 class Buffer(Tile):
602 def __init__(self, paths, color=-1):
603 Tile.__init__(self, paths) # Call base class intializer
604 self.marble = color
605 self.entering = None
606
607 def draw_back(self, surface):
608 if self.drawn: return 0
609
610 Tile.draw_back(self, surface)
611
612 color = self.marble
613 if color >= 0:
614 holecenter = self.rect.center
615 surface.blit( Marble.images[color],
616 (holecenter[0]-marble_size/2,
617 holecenter[1]-marble_size/2))
618 else:
619 surface.blit( self.bottom, self.rect.topleft)
620
621
622 return 1
623
624 def draw_fore(self, surface):
625 surface.blit( self.tunnels[self.paths], self.rect.topleft)
626 surface.blit( self.top, self.rect.topleft)
627 return 0
628
629 def affect_marble(self, board, marble, rpos):
630 # Watch for marbles entering
631 if (rpos[0]+marble_size == tile_size/2 and marble.direction == 1) or \
632 (rpos[0]-marble_size == tile_size/2 and marble.direction == 3) or \
633 (rpos[1]+marble_size == tile_size/2 and marble.direction == 2) or \
634 (rpos[1]-marble_size == tile_size/2 and marble.direction == 0):
635
636 if self.entering is not None:
637 # Bump the marble that is currently entering
638 newmarble = self.entering
639 newmarble.rect.center = self.rect.center
640 newmarble.direction = marble.direction
641
642 play_sound( ping)
643
644 # Let the base class affect the marble
645 Tile.affect_marble(self, board, newmarble,
646 (tile_size/2,tile_size/2))
647 elif self.marble >= 0:
648 # Bump the marble that is currently caught
649 newmarble = Marble( self.marble, self.rect.center, marble.direction)
650
651 board.marbles.append( newmarble)
652
653 play_sound( ping)
654
655 # Let the base class affect the marble
656 Tile.affect_marble(self, board, newmarble,
657 (tile_size/2,tile_size/2))
658
659 self.marble = -1
660 self.drawn = 0
661
662 # Remember which marble is on its way in
663 self.entering = marble
664
665 elif rpos == (tile_size/2, tile_size/2):
666 # Catch this marble
667 self.marble = marble.color
668 board.marbles.remove( marble)
669 self.entering = None
670 self.drawn = 0
671
672 class Painter(Tile):
673 def __init__(self, paths, color, center=None):
674 Tile.__init__(self, paths, center) # Call base class intializer
675 self.color = color
676
677 def draw_fore(self, surface):
678 surface.blit( self.tunnels[self.paths], self.rect.topleft)
679 surface.blit( self.images[self.color], self.rect.topleft)
680 return 0
681
682 def affect_marble(self, board, marble, rpos):
683 Tile.affect_marble( self, board, marble, rpos)
684 if rpos == (tile_size/2, tile_size/2):
685 if marble.color != self.color:
686 # Change the color
687 marble.color = self.color
688 play_sound( change_color)
689
690 class Filter(Tile):
691 def __init__(self, paths, color, center=None):
692 Tile.__init__(self, paths, center) # Call base class intializer
693 self.color = color
694
695 def draw_fore(self, surface):
696 surface.blit( self.tunnels[self.paths], self.rect.topleft)
697 surface.blit( self.images[self.color], self.rect.topleft)
698 return 0
699
700 def affect_marble(self, board, marble, rpos):
701 if rpos == (tile_size/2, tile_size/2):
702 # If the color is wrong, bounce the marble
703 if marble.color != self.color and marble.color != 8:
704 marble.direction = marble.direction ^ 2
705 play_sound( ping)
706 else:
707 Tile.affect_marble( self, board, marble, rpos)
708 play_sound( filter_admit)
709
710 class Director(Tile):
711 def __init__(self, paths, direction, center=None):
712 Tile.__init__(self, paths, center) # Call base class intializer
713 self.direction = direction
714
715 def draw_fore(self, surface):
716 surface.blit( self.tunnels[self.paths], self.rect.topleft)
717 surface.blit( self.images[self.direction], self.rect.topleft)
718 return 0
719
720 def affect_marble(self, board, marble, rpos):
721 if rpos == (tile_size/2, tile_size/2):
722 marble.direction = self.direction
723 play_sound( direct_marble)
724
725 class Shredder(Tile):
726 def __init__(self, paths, center=None):
727 Tile.__init__(self, paths, center) # Call base class intializer
728
729 def draw_fore(self, surface):
730 surface.blit( self.tunnels[self.paths], self.rect.topleft)
731 surface.blit( self.image, self.rect.topleft)
732 return 0
733
734 def affect_marble(self, board, marble, rpos):
735 if rpos == (tile_size/2, tile_size/2):
736 board.marbles.remove( marble)
737 play_sound( shredder)
738
739 class Switch(Tile):
740 def __init__(self, paths, dir1, dir2, center=None):
741 Tile.__init__(self, paths, center) # Call base class intializer
742 self.curdir = dir1
743 self.otherdir = dir2
744 self.switched = 0
745
746 def switch(self):
747 t = self.curdir
748 self.curdir = self.otherdir
749 self.otherdir = t
750 self.switched = 1
751 play_sound( switch)
752
753 def draw_fore(self, surface):
754 surface.blit( self.tunnels[self.paths], self.rect.topleft)
755 surface.blit( self.images[self.curdir][self.otherdir],
756 self.rect.topleft)
757 rc = self.switched
758 self.switched = 0
759 return rc
760
761 def affect_marble(self, board, marble, rpos):
762 if rpos == (tile_size/2, tile_size/2):
763 marble.direction = self.curdir
764 self.switch()
765
766 class Replicator(Tile):
767 def __init__(self, paths, count, center=None):
768 Tile.__init__(self, paths, center) # Call base class intializer
769 self.count = count
770 self.pending = []
771
772 def draw_fore(self, surface):
773 surface.blit( self.tunnels[self.paths], self.rect.topleft)
774 surface.blit( self.image, self.rect.topleft)
775 return 0
776
777 def update(self, board):
778 for i in self.pending[:]:
779 i[3] -= 1
780 if i[3] == 0:
781 i[3] = replicator_delay
782
783 # Make sure that the active marble limit isn't exceeded
784 if len(board.marbles) >= board.live_marbles_limit:
785 # Clear the pending list
786 self.pending = []
787 return
788
789 # Add the new marble
790 board.marbles.append(Marble(i[0],self.rect.center,i[1]))
791 play_sound( replicator)
792
793 i[2] -= 1
794 if i[2] <= 0: self.pending.remove( i)
795
796 def affect_marble(self, board, marble, rpos):
797 Tile.affect_marble( self, board, marble, rpos)
798 if rpos == (tile_size/2, tile_size/2):
799 # Add the marble to the pending list
800 self.pending.append( [marble.color,marble.direction,
801 self.count - 1, replicator_delay]);
802 play_sound( replicator)
803
804 class Teleporter(Tile):
805 def __init__(self, paths, other=None, center=None):
806 Tile.__init__(self, paths, center) # Call base class intializer
807 if paths & 5: self.image = self.image_v
808 else: self.image = self.image_h
809 if other is not None: self.connect( other)
810
811 def draw_fore(self, surface):
812 surface.blit( self.tunnels[self.paths], self.rect.topleft)
813 surface.blit( self.image, self.rect.topleft)
814 return 0
815
816 def connect(self, other):
817 self.other = other
818 other.other = self
819
820 def affect_marble(self, board, marble, rpos):
821 if rpos == (tile_size/2, tile_size/2):
822 marble.rect.center = self.other.rect.center
823 play_sound( teleport)
824
825 class Trigger(Tile):
826 def __init__(self, colors, center=None):
827 Tile.__init__(self, 0, center) # Call base class intializer
828 self.marbles = None
829 self._setup( colors)
830
831 def _setup(self, colors):
832 self.countdown = 0
833 self.marbles = [
834 random.choice(colors),
835 random.choice(colors),
836 random.choice(colors),
837 random.choice(colors),
838 ]
839 self.drawn = 0
840
841 def update(self, board):
842 if self.countdown > 0:
843 self.countdown -= 1
844 if self.countdown == 0:
845 self._setup( board.colors)
846 play_sound( trigger_setup)
847
848 def draw_back(self, surface):
849 if self.drawn: return 0
850 Tile.draw_back(self, surface)
851 surface.blit( self.image, self.rect.topleft)
852 if self.marbles is not None:
853 for i in range(4):
854 surface.blit( Marble.images[self.marbles[i]],
855 (holecenters[0][i][0]+self.rect.left-marble_size/2,
856 holecenters[0][i][1]+self.rect.top-marble_size/2))
857 return 1
858
859 def complete(self, board):
860 self.marbles = None
861 self.countdown = trigger_time * frames_per_sec
862 self.drawn = 0
863 board.game.increase_score( 50)
864
865 class Stoplight(Tile):
866 def __init__(self, colors, center=None):
867 Tile.__init__(self, 0, center) # Call base class intializer
868 self.marbles = list(colors)
869 self.current = 0
870
871 def draw_back(self, surface):
872 if self.drawn: return 0
873 Tile.draw_back(self, surface)
874 surface.blit( self.image, self.rect.topleft)
875 for i in range(self.current,3):
876 surface.blit( self.smallmarbles[self.marbles[i]],
877 (self.rect.centerx-14,
878 self.rect.top+3+(29*i)))
879 return 1
880
881 def complete(self, board):
882 for i in range(3):
883 if self.marbles[i] >= 0:
884 self.marbles[i] = -1
885 break
886 self.current += 1
887 self.drawn = 0
888 board.game.increase_score( 20)
889
890 class Board:
891 def __init__(self, game, pos):
892 self.game = game
893 self.pos = pos
894 self.marbles = []
895 self.screen = game.screen
896 self.trigger = None
897 self.stoplight = None
898 self.launch_queue = []
899 self.board_complete = 0
900 self.paused = 0
901 self.name = "Unnamed"
902 self.live_marbles_limit = 10
903 self.launch_timeout = -1
904 self.board_timeout = -1
905 self.colors = default_colors
906 self.launched = 1
907
908 self.set_launch_timer( default_launch_timer)
909 self.set_board_timer( default_board_timer)
910
911 # Create the board array
912 self.tiles = []
913 for j in range( vert_tiles):
914 row = range( horiz_tiles)
915 self.tiles.append( row)
916
917 # Load the level
918 # For levels above game.level, use a pseudo-random
919 # level selection method.
920 if( game.level < game.numlevels):
921 self._load( game.circuit, game.level)
922 else:
923 # Compute a hash of the current level, involving
924 # a static timestamp. This provides a consistent,
925 # backtrackable pseudo-random function.
926 hash = md5.new(`game.gamestart`+"/"+`game.level`).digest()
927 hashval = (ord(hash[0]) + (ord(hash[1]) << 8) + \
928 (ord(hash[2]) << 16) + (ord(hash[3]) << 24)) & 32767;
929 self._load( game.circuit, hashval % game.numlevels);
930
931 # Create the launch timer text object
932 self.launch_timer_text = launch_timer_font.render(
933 `self.launch_timer`, 1, (255,255,255))
934 self.launch_timer_text_rect = self.launch_timer_text.get_rect()
935 self.launch_timer_text_rect.centerx = launch_timer_pos[0]+timer_width/2+1
936 self.launch_timer_text_rect.bottom = \
937 launch_timer_pos[1] + timer_height - timer_margin
938
939 # Fill up the launch queue
940 for i in range( vert_tiles * tile_size / marble_size + 2):
941 self.launch_queue.append(random.choice(self.colors))
942
943 # Create The Background
944 self.background = pygame.Surface(screen.get_size()).convert()
945 self.background.fill((200, 200, 200)) # Color of Info Bar
946
947 # Draw the Backdrop
948 backdrop = load_image('backdrop.jpg', None,
949 (horiz_tiles * tile_size, vert_tiles * tile_size))
950 self.background.blit( backdrop, board_pos);
951
952 # Draw the launcher
953 self.background.blit( self.launcher_background,
954 (board_pos[0], board_pos[1] - marble_size))
955 self.background.blit( self.launcher_v,
956 (board_pos[0]+horiz_tiles*tile_size, board_pos[1]))
957 for i in range( horiz_tiles):
958 if self.tiles[0][i].paths & 1:
959 self.background.blit( self.launcher_entrance,
960 (board_pos[0]+tile_size*i, board_pos[1]-marble_size))
961 self.background.blit( self.launcher_corner,
962 (board_pos[0]+horiz_tiles*tile_size-(tile_size-marble_size)/2,
963 board_pos[1] - marble_size))
964
965 # Draw the board name
966 board_name = `self.game.level+1` + " - " + self.name
967 if self.game.level >= self.game.numlevels:
968 board_name += " (Random)"
969 text = info_font.render( board_name, 1, (0,0,0))
970 rect = text.get_rect()
971 rect.left = 8
972 self.background.blit( text, rect)
973
974 # Figure out the score location
975 text = "Score: 00000000"
976 self.score_pos = screen_width - 8 - \
977 info_font.render( text, 1, (0,0,0)).get_rect().width
978
979 # Figure out the board timer location
980 text = "00:00"
981 self.board_timer_pos = self.score_pos - 16 - \
982 info_font.render( text, 1, (0,0,0)).get_rect().width
983
984 # Initialize the screen
985 screen.blit(self.background, (0, 0))
986
987 def draw_back(self, dirty_rects):
988 # Draw the launch timer
989 if self.launch_timer_height is None:
990 height = timer_height
991 rect = (launch_timer_pos[0],launch_timer_pos[1],
992 timer_width,timer_height)
993 self.screen.fill((0,0,0), rect)
994 self.screen.fill((0,40,255),
995 (launch_timer_pos[0]+timer_margin,
996 launch_timer_pos[1]+timer_height-height,
997 timer_width-timer_margin*2,height))
998 dirty_rects.append( rect)
999 else:
1000 height = timer_height*self.launch_timeout/self.launch_timeout_start
1001 if height < self.launch_timer_height:
1002 rect = (launch_timer_pos[0] + timer_margin,
1003 launch_timer_pos[1] + timer_height - self.launch_timer_height,
1004 timer_width-2*timer_margin, self.launch_timer_height - height)
1005 self.screen.fill((0,0,0), rect)
1006 dirty_rects.append( rect)
1007 self.launch_timer_height = height
1008 self.screen.blit( self.launch_timer_text, self.launch_timer_text_rect)
1009 dirty_rects.append( self.launch_timer_text_rect)
1010
1011 # Clear the info bar
1012 rect = (0,0,screen_width,info_height)
1013 self.screen.set_clip( rect)
1014 self.screen.blit( self.background, (0,0))
1015 self.screen.set_clip()
1016 dirty_rects.append( rect)
1017
1018 # Draw the score
1019 text = "Score: "+("00000000"+`self.game.score`)[-8:]
1020 text = info_font.render( text, 1, (0,0,0))
1021 rect = text.get_rect()
1022 rect.left = self.score_pos
1023 self.screen.blit( text, rect)
1024
1025 # Draw the board timer
1026 time_remaining = (self.board_timeout+frames_per_sec-1)/frames_per_sec
1027 text = `time_remaining/60`+":"+("00"+`time_remaining%60`)[-2:]
1028 text = info_font.render( text, 1, (0,0,0))
1029 rect = text.get_rect()
1030 rect.left = self.board_timer_pos
1031 self.screen.blit( text, rect)
1032
1033 # Draw the lives counter
1034 right_edge = self.board_timer_pos - 32
1035 for i in range(self.game.lives - 1):
1036 rect = self.life_marble.get_rect()
1037 rect.centery = info_height / 2
1038 rect.right = right_edge
1039 self.screen.blit( self.life_marble, rect)
1040 right_edge -= rect.width + 4
1041
1042 # Draw the live marbles
1043 num_marbles = len(self.marbles)
1044 if num_marbles > self.live_marbles_limit:
1045 num_marbles = self.live_marbles_limit
1046 text = `num_marbles`+"/"+`self.live_marbles_limit`
1047 text = active_marbles_font.render( text, 1, (40,40,40))
1048 rect = text.get_rect()
1049 rect.left = self.pos[0] + 8
1050 rect.centery = self.pos[1] - marble_size / 2
1051 rect.width += 100
1052 self.screen.set_clip( rect)
1053 self.screen.blit( self.background, (0,0))
1054 self.screen.set_clip()
1055 self.screen.blit( text, rect)
1056
1057 dirty_rects.append( rect)
1058
1059 for row in self.tiles:
1060 for tile in row:
1061 if tile.draw_back( self.background):
1062 self.screen.set_clip( tile.rect)
1063 self.screen.blit( self.background, (0,0))
1064 self.screen.set_clip()
1065 dirty_rects.append( tile.rect)
1066
1067 if self.launched:
1068 for i in range(len(self.launch_queue)):
1069 self.background.blit( Marble.images[self.launch_queue[i]],
1070 (self.pos[0] + horiz_tiles * tile_size,
1071 self.pos[1] + i * marble_size - marble_size))
1072 rect = (self.pos[0] + horiz_tiles * tile_size,
1073 self.pos[1] - marble_size, marble_size,
1074 marble_size + tile_size * vert_tiles)
1075 self.screen.set_clip( rect)
1076 self.screen.blit( self.background, (0,0))
1077 self.screen.set_clip()
1078 dirty_rects.append( rect)
1079 self.launched = 0
1080
1081 def draw_fore(self, dirty_rects):
1082 for row in self.tiles:
1083 for tile in row:
1084 if tile.draw_fore(self.screen):
1085 dirty_rects.append( tile.rect)
1086
1087 def update(self):
1088 # Create the list of dirty rectangles
1089 dirty_rects = []
1090
1091 # Erase the marbles
1092 for marble in self.marbles:
1093 marble.undraw( self.screen, self.background)
1094 dirty_rects.append( list(marble.rect))
1095
1096 # Animate the marbles
1097 for marble in self.marbles[:]:
1098 marble.update( self)
1099
1100 # Animate the tiles
1101 for row in self.tiles:
1102 for tile in row:
1103 tile.update( self)
1104 if tile.drawn == 0: dirty_rects.append( tile.rect)
1105
1106 # Complete any wheels, if appropriate
1107 try_again = 1
1108 while try_again:
1109 try_again = 0
1110 for row in self.tiles:
1111 for tile in row:
1112 if isinstance( tile, Wheel):
1113 try_again |= tile.maybe_complete( self)
1114
1115 # Check if the board is complete
1116 self.board_complete = 1
1117 for row in self.tiles:
1118 for tile in row:
1119 if isinstance( tile, Wheel):
1120 if tile.completed == 0: self.board_complete = 0
1121
1122 # Decrement the launch timer
1123 if self.launch_timeout > 0:
1124 self.launch_timeout -= 1
1125 if self.launch_timeout == 0: self.board_complete = -1
1126
1127 # Decrement the board timer
1128 if self.board_timeout > 0:
1129 self.board_timeout -= 1
1130 if self.board_timeout == 0: self.board_complete = -2
1131
1132 # Draw the background
1133 self.draw_back( dirty_rects)
1134
1135 # Draw all of the marbles
1136 for marble in self.marbles:
1137 marble.draw( self.screen)
1138 dirty_rects.append( marble.rect)
1139
1140 # Draw the foreground
1141 self.draw_fore( dirty_rects)
1142
1143 # Flip the display
1144 pygame.display.update( dirty_rects)
1145
1146 def set_tile(self, x, y, tile):
1147 self.tiles[y][x] = tile
1148 tile.rect.left = self.pos[0] + tile_size * x
1149 tile.rect.top = self.pos[1] + tile_size * y
1150
1151 tile.x = x
1152 tile.y = y
1153
1154 # If it's a trigger, keep track of it
1155 if isinstance( tile, Trigger):
1156 self.trigger = tile
1157
1158 # If it's a stoplight, keep track of it
1159 if isinstance( tile, Stoplight):
1160 self.stoplight = tile
1161
1162 def set_launch_timer(self, passes):
1163 self.launch_timer = passes
1164 self.launch_timeout_start = (marble_size +
1165 (horiz_tiles * tile_size - marble_size) * passes) / marble_speed
1166 self.launch_timer_height = None
1167
1168 def set_board_timer(self, seconds):
1169 self.board_timer = seconds
1170 self.board_timeout_start = seconds * frames_per_sec
1171 self.board_timeout = self.board_timeout_start
1172
1173 def launch_marble(self):
1174 self.launch_queue.append(random.choice(self.colors))
1175 self.marbles.insert( 0, Marble( self.launch_queue[0],
1176 (self.pos[0]+tile_size*horiz_tiles+marble_size/2,
1177 self.pos[1]-marble_size/2), 3))
1178 del self.launch_queue[0]
1179 self.launched = 1
1180
1181 self.launch_timeout = self.launch_timeout_start
1182 self.launch_timer_height = None
1183
1184 def affect_marble(self, marble):
1185 c = marble.rect.center
1186 cx = c[0] - self.pos[0]
1187 cy = c[1] - self.pos[1]
1188
1189 # Bounce marbles off of the top
1190 if cy == marble_size/2:
1191 marble.direction = 2
1192 return
1193
1194 if cy < 0:
1195 if cx == marble_size/2:
1196 marble.direction = 1
1197 return
1198 if cx == tile_size * horiz_tiles - marble_size/2 \
1199 and marble.direction == 1:
1200 marble.direction = 3
1201 return
1202
1203 # The special case of new marbles at the top
1204 effective_cx = cx
1205 effective_cy = cy + marble_size
1206 else:
1207 effective_cx = cx + marble_size/2 * dirs[marble.direction][0]
1208 effective_cy = cy + marble_size/2 * dirs[marble.direction][1]
1209
1210 tile_x = effective_cx / tile_size
1211 tile_y = effective_cy / tile_size
1212 tile_xr = cx - tile_x * tile_size
1213 tile_yr = cy - tile_y * tile_size
1214
1215 if tile_x >= horiz_tiles: return
1216
1217 tile = self.tiles[tile_y][tile_x]
1218
1219 if cy < 0 and marble.direction != 2:
1220 # The special case of new marbles at the top
1221 if tile_xr == tile_size / 2 and (tile.paths & 1):
1222 if isinstance( tile, Wheel):
1223 if tile.spinpos > 0 or tile.marbles[0] != -3: return
1224 tile.marbles[0] = -2
1225 marble.direction = 2
1226 self.launch_marble()
1227 elif len(self.marbles) < self.live_marbles_limit:
1228 marble.direction = 2
1229 self.launch_marble()
1230 else:
1231 tile.affect_marble( self, marble, (tile_xr, tile_yr))
1232
1233 def click(self, pos):
1234 # Determine which tile the pointer is in
1235 tile_x = (pos[0] - self.pos[0]) / tile_size
1236 tile_y = (pos[1] - self.pos[1]) / tile_size
1237 tile_xr = pos[0] - self.pos[0] - tile_x * tile_size
1238 tile_yr = pos[1] - self.pos[1] - tile_y * tile_size
1239 if tile_x >= 0 and tile_x < horiz_tiles and \
1240 tile_y >= 0 and tile_y < vert_tiles:
1241 tile = self.tiles[tile_y][tile_x]
1242 tile.click( self, tile_xr, tile_yr, tile_x, tile_y)
1243
1244 def _load(self, circuit, level):
1245 fullname = os.path.join('circuits', circuit)
1246 f = open( fullname)
1247
1248 # Skip the previous levels
1249 j = 0
1250 while j < vert_tiles * level:
1251 line = f.readline()
1252 if line == '':
1253 f.close()
1254 return 0
1255 if line[0] == '|': j += 1
1256
1257 teleporters = []
1258 teleporter_names = []
1259 stoplight = default_stoplight
1260
1261 numwheels = 0
1262 boardtimer = -1
1263
1264 j = 0
1265 while j < vert_tiles:
1266 line = f.readline()
1267
1268 if line[0] != '|':
1269 if line[0:5] == 'name=':
1270 self.name = line[5:-1]
1271 elif line[0:11] == 'maxmarbles=':
1272 self.live_marbles_limit = int(line[11:-1])
1273 elif line[0:12] == 'launchtimer=':
1274 self.set_launch_timer( int(line[12:-1]))
1275 elif line[0:11] == 'boardtimer=':
1276 boardtimer = int(line[11:-1])
1277 elif line[0:7] == 'colors=':
1278 self.colors = []
1279 for c in line[7:-1]:
1280 if c >= '0' and c <= '7':
1281 self.colors.append(int(c))
1282 self.colors.append(int(c))
1283 self.colors.append(int(c))
1284 elif c == '8':
1285 # Crazy marbles are one-third as common
1286 self.colors.append(8)
1287 elif line[0:10] == 'stoplight=':
1288 stoplight = []
1289 for c in line[10:-1]:
1290 if c >= '0' and c <= '7':
1291 stoplight.append(int(c))
1292
1293 continue
1294
1295 for i in range(horiz_tiles):
1296 type = line[i*4+1]
1297 paths = line[i*4+2]
1298 if paths == ' ': pathsint = 0
1299 elif paths >= 'a': pathsint = ord(paths)-ord('a')+10
1300 elif paths >= '0' and paths <= '9': pathsint = int(paths)
1301 else: pathsint = int(paths)
1302 color = line[i*4+3]
1303 if color == ' ': colorint = 0
1304 elif color >= 'a': colorint = ord(color)-ord('a')+10
1305 elif color >= '0' and color <= '9': colorint = int(color)
1306 else: colorint = 0
1307
1308 if type == 'O':
1309 tile = Wheel( pathsint)
1310 numwheels += 1
1311 elif type == '%': tile = Trigger(self.colors)
1312 elif type == '!': tile = Stoplight(stoplight)
1313 elif type == '&': tile = Painter(pathsint, colorint)
1314 elif type == '#': tile = Filter(pathsint, colorint)
1315 elif type == '@':
1316 if color == ' ': tile = Buffer(pathsint)
1317 else: tile = Buffer(pathsint, colorint)
1318 elif type == ' ' or \
1319 (type >= '0' and type <= '8'): tile = Tile(pathsint)
1320 elif type == 'X': tile = Shredder(pathsint)
1321 elif type == '*': tile = Replicator(pathsint, colorint)
1322 elif type == '^':
1323 if color == ' ': tile = Director(pathsint, 0)
1324 elif color == '>': tile = Switch(pathsint, 0, 1)
1325 elif color == 'v': tile = Switch(pathsint, 0, 2)
1326 elif color == '<': tile = Switch(pathsint, 0, 3)
1327 elif type == '>':
1328 if color == ' ': tile = Director(pathsint, 1)
1329 elif color == '^': tile = Switch(pathsint, 1, 0)
1330 elif color == 'v': tile = Switch(pathsint, 1, 2)
1331 elif color == '<': tile = Switch(pathsint, 1, 3)
1332 elif type == 'v':
1333 if color == ' ': tile = Director(pathsint, 2)
1334 elif color == '^': tile = Switch(pathsint, 2, 0)
1335 elif color == '>': tile = Switch(pathsint, 2, 1)
1336 elif color == '<': tile = Switch(pathsint, 2, 3)
1337 elif type == '<':
1338 if color == ' ': tile = Director(pathsint, 3)
1339 elif color == '^': tile = Switch(pathsint, 3, 0)
1340 elif color == '>': tile = Switch(pathsint, 3, 1)
1341 elif color == 'v': tile = Switch(pathsint, 3, 2)
1342 elif type == '=':
1343 if color in teleporter_names:
1344 other = teleporters[teleporter_names.index(color)]
1345 tile = Teleporter( pathsint, other)
1346 else:
1347 tile = Teleporter( pathsint)
1348 teleporters.append( tile)
1349 teleporter_names.append( color)
1350
1351 self.set_tile( i, j, tile)
1352
1353 if type >= '0' and type <= '8':
1354 if color == '^': direction = 0
1355 elif color == '>': direction = 1
1356 elif color == 'v': direction = 2
1357 else: direction = 3
1358 self.marbles.append(
1359 Marble(int(type),tile.rect.center,direction))
1360
1361 j += 1
1362 if boardtimer < 0: boardtimer = default_board_timer * numwheels
1363 self.set_board_timer( boardtimer)
1364 f.close()
1365 return 1
1366
1367 # Return values for this function:
1368 # -4: User closed the application window
1369 # -3: User aborted the level
1370 # -2: Board timer expired
1371 # -1: Launch timer expired
1372 # 1: Level completed successfully
1373 # 2: User requested a skip to the next level
1374 # 3: User requested a skip to the previous level
1375 def play_level( self):
1376 # Perform the first render
1377 self.update()
1378
1379 # Play the start sound
1380 #play_sound( levelbegin)
1381
1382 # Launch the first marble
1383 self.launch_marble()
1384
1385 # Do the first update
1386 pygame.display.update()
1387
1388 # Game Loop
1389 while not self.board_complete:
1390 # Wait for the next frame
1391 my_tick( frames_per_sec)
1392
1393 # Handle Input Events
1394 for event in pygame.event.get():
1395 if event.type is QUIT:
1396 return -4
1397 elif event.type is KEYDOWN:
1398 if event.key is K_ESCAPE: return -3
1399 elif event.key == ord('n'): return 2
1400 elif event.key == ord('b'): return 3
1401 elif event.key == ord(' ') or \
1402 event.key == ord('p') or \
1403 event.key == K_PAUSE:
1404 self.paused = self.paused ^ 1
1405 if self.paused:
1406 if screenshot:
1407 pause_popup = None
1408 else:
1409 pause_popup = popup('Game Paused')
1410 else:
1411 popdown( pause_popup)
1412 elif event.key == K_F2:
1413 toggle_fullscreen()
1414 elif event.key == K_F3:
1415 toggle_music()
1416 elif event.key == K_F4:
1417 toggle_sound()
1418
1419 elif event.type is MOUSEBUTTONDOWN:
1420 if self.paused:
1421 self.paused = 0
1422 popdown( pause_popup)
1423 else: self.click( pygame.mouse.get_pos())
1424
1425 if not self.paused: self.update()
1426
1427 # Play the end sound
1428 if self.board_complete > 0:
1429 play_sound( levelfinish)
1430 else:
1431 play_sound( die)
1432
1433 return self.board_complete
1434
1435 class HighScores:
1436 num_highscores = 10
1437
1438 def __init__(self, filename):
1439 self.filename = filename
1440 self.current_score = -1
1441 self.load()
1442
1443 def qualifies(self, score):
1444 self.load()
1445 return score >= self.scores[-1][0]
1446
1447 def add_score(self, score, circuit, level, name):
1448 self.load()
1449 for i in range(len(self.scores)):
1450 if score >= self.scores[i][0]:
1451 self.scores.insert( i, (score, circuit, level, name))
1452 del self.scores[self.num_highscores:]
1453 self.save()
1454 self.current_score = i
1455 return i
1456 return -1
1457
1458 def load( self):
1459 self.scores = []
1460
1461 parser = re.compile("([0-9]+) ([^ ]+) ([0-9]+) (.*)\n")
1462
1463 try:
1464 f = open( self.filename)
1465 while len(self.scores) < self.num_highscores:
1466 line = f.readline()
1467 if line == '': break
1468 match = parser.match(line)
1469 if match is not None:
1470 (score,circuit,level,name) = match.groups()
1471 self.scores.append(
1472 (int(score), circuit, int(level), name))
1473 f.close()
1474 except: pass
1475
1476 # Extend the list if it is shorter than needed
1477 while len(self.scores) < self.num_highscores:
1478 self.scores.append( (0, 'all-boards', 1, ''))
1479
1480 # Shrink the list if it is longer than allowed
1481 del self.scores[self.num_highscores:]
1482
1483 def save(self):
1484 try:
1485 f = open( self.filename, "w")
1486 except:
1487 try:
1488 f = os.popen(write_highscores, "w")
1489 except OSError, message:
1490 print "Warning: Can't save highscores:", message
1491 return
1492
1493 try:
1494 for i in self.scores:
1495 f.write( `i[0]`+' '+i[1]+' '+`i[2]`+' '+i[3]+'\n')
1496 f.close()
1497 except:
1498 print "Warning: Problem saving highscores."
1499
1500 def wait_one_sec():
1501 time.sleep(1)
1502 pygame.event.get() # Clear the event queue
1503
1504 def popup( text, minsize=None):
1505 maxwidth = 0
1506 objs = []
1507 while text != "":
1508 if '\n' in text:
1509 newline = text.index('\n')
1510 line = text[:newline]
1511 text = text[newline+1:]
1512 else:
1513 line = text
1514 text = ""
1515
1516 obj = popup_font.render( line, 1, (0, 0, 0))
1517 maxwidth = max( maxwidth, obj.get_rect().width)
1518 objs.append( obj)
1519
1520 linespacing = popup_font.get_ascent() - \
1521 popup_font.get_descent() + popup_font.get_linesize()
1522 # Work around an apparent pygame bug on Windows
1523 linespacing = min( linespacing, int(1.2 * popup_font.get_height()))
1524
1525 # Leave a bit more room
1526 linespacing = int(linespacing * 1.3)
1527
1528 window_width = maxwidth + 40
1529 window_height = popup_font.get_height()+linespacing*(len(objs)-1)+40
1530 if minsize is not None:
1531 window_width = max( window_width, minsize[0])
1532 window_height = max( window_height, minsize[1])
1533
1534 window = pygame.Surface((window_width, window_height))
1535 winrect = window.get_rect()
1536 window.fill((0, 0, 0))
1537 window.fill((250, 250, 250), winrect.inflate(-2,-2))
1538
1539 y = 20
1540 for obj in objs:
1541 textpos = obj.get_rect()
1542 textpos.top = y
1543 textpos.centerx = winrect.centerx
1544 window.blit( obj, textpos)
1545 y += linespacing
1546
1547 winrect.center = screen.get_rect().center
1548 winrect.top -= 40
1549
1550 backbuf = pygame.Surface(winrect.size).convert()
1551 backbuf.blit( screen, (0,0), winrect)
1552
1553 screen.blit( window, winrect)
1554 pygame.display.update()
1555
1556 return (backbuf, winrect)
1557
1558 def popdown( popup_rc):
1559 if popup_rc is not None:
1560 screen.blit( popup_rc[0], popup_rc[1])
1561 pygame.display.update( popup_rc[1])
1562
1563 class Game:
1564 def __init__(self, screen, circuit, highscores):
1565 self.screen = screen
1566 self.circuit = circuit
1567 self.highscores = highscores
1568
1569 # Count the number of levels
1570 fullname = os.path.join('circuits', circuit)
1571 f = open( fullname)
1572 j = 0
1573 while 1:
1574 line = f.readline()
1575 if line == '': break
1576 if line[0] == '|': j += 1
1577 f.close()
1578 self.numlevels = j / vert_tiles
1579
1580 self.level = 0
1581 self.score = 0
1582 self.lives = initial_lives
1583
1584 self.gamestart = time.time()
1585
1586 def increase_score(self, amount):
1587 # Add the amount to the score
1588 self.score += amount
1589
1590 # Award any extra lives that are due
1591 extra_lives = amount / extra_life_frequency + \
1592 (self.score % extra_life_frequency < amount % extra_life_frequency)
1593 extra_lives = min( extra_lives, max_spare_lives+1 - self.lives)
1594 if extra_lives > 0:
1595 self.lives += extra_lives
1596 play_sound( extra_life)
1597
1598 # Return values for this function:
1599 # -1: User closed the application window
1600 # 0: The game was aborted
1601 # 1: Game completed normally
1602 # 2: User achieved a highscore
1603 def play(self):
1604 # Draw the loading screen
1605 backdrop = load_image('backdrop.jpg', None,
1606 (screen_width, screen_height))
1607 screen.blit( backdrop, (0,0))
1608 pygame.display.update()
1609
1610 popup("Please wait...\n", (150, 50))
1611
1612 start_music("background.xm", ingame_music_volume)
1613
1614 self.highscores.current_score = -1
1615
1616 while 1:
1617 # Play a level
1618 board = Board( self, board_pos)
1619
1620 rc = board.play_level()
1621
1622 # Check for the user closing the window
1623 if rc == -4: return -1
1624
1625 if rc == 2:
1626 self.level += 1
1627 continue
1628
1629 if rc == 3:
1630 if self.level > 0: self.level -= 1
1631 self.score = 0
1632 self.lives = initial_lives
1633 continue
1634
1635 if rc < 0:
1636 # The board was not completed
1637
1638 if rc == -3: message = 'Level Aborted.'
1639 elif rc == -2: message = 'The board timer has expired.'
1640 else: message = 'The launch timer has expired.'
1641
1642 self.lives -= 1
1643 if self.lives > 0:
1644 rc = self.board_dialog( message+'\nClick to try again.',
1645 rc != -3)
1646 elif self.highscores.qualifies( self.score):
1647 popup("Congratulations!\n"+
1648 "You have a highscore!\n"+
1649 "Please enter your name:", (300, 180))
1650 name = get_name( self.screen, popup_font,
1651 ((screen_width-250)/2,310,250,popup_font.get_height()),
1652 (255,255,255), (0,0,0))
1653 if name is None: return -1
1654
1655 self.highscores.add_score( self.score, self.circuit,
1656 self.level+1, name)
1657
1658 return 2
1659 else:
1660 rc = self.board_dialog( message +
1661 '\nGame Over.\nClick to continue\n', rc != -3)
1662 if rc == 1: return 1
1663
1664 self.score = 0
1665 self.lives = initial_lives
1666 else:
1667 # The board was completed
1668
1669 # Compute time remaining bonus
1670 time_remaining = 100 * board.board_timeout / \
1671 board.board_timeout_start
1672 time_bonus = 5 * time_remaining
1673
1674 # Compute empty holes bonus
1675 total_holes = 0
1676 empty_holes = 0
1677 for row in board.tiles:
1678 for tile in row:
1679 if isinstance( tile, Wheel):
1680 total_holes += 4
1681 for i in tile.marbles:
1682 if i < 0: empty_holes += 1
1683 empty_holes = (100 * empty_holes + total_holes/2) / total_holes
1684 holes_bonus = 2 * empty_holes
1685
1686 self.increase_score( time_bonus + holes_bonus)
1687
1688 message = 'Level Complete!\n'+ \
1689 "Bonus for " + `time_remaining` + "% time remaining: " + \
1690 `time_bonus` + "\n" + \
1691 "Bonus for " + `empty_holes` + "% holes empty: " + \
1692 `holes_bonus` + '\nClick to continue.'
1693
1694 rc = self.board_dialog( message, 1, 1)
1695 self.level += 1
1696
1697 if rc == -2: return -1
1698 if rc < 0: return 0
1699
1700 # Return values for this function:
1701 # -2: User closed the application window
1702 # -1: User pressed escape
1703 # 0: User pressed 'b' or 'n'
1704 # 1: Some other user event
1705 def board_dialog( self, message, pause=1, complete=0):
1706 popup(message)
1707 if pause: wait_one_sec()
1708
1709 # Wait for a mouse click to continue
1710 while 1:
1711 pygame.time.wait(20)
1712 for event in pygame.event.get():
1713 if event.type is QUIT:
1714 return -2
1715 elif event.type is KEYDOWN:
1716 if event.key == K_ESCAPE: return -1
1717 if event.key == ord('b'):
1718 if self.level > 0: self.level -= 1
1719 self.score = 0
1720 self.lives = initial_lives
1721 return 0
1722 elif event.key == ord('n'):
1723 if complete == 0:
1724 # Skip to the next level
1725 self.level += 1
1726 return 0
1727 elif event.key == K_F2:
1728 toggle_fullscreen()
1729 continue
1730 elif event.key == K_F3:
1731 toggle_music()
1732 continue
1733 elif event.key == K_F4:
1734 toggle_sound()
1735 continue
1736 elif event.key == K_LSHIFT or \
1737 event.key == K_RSHIFT or \
1738 event.key == K_LALT or \
1739 event.key == K_RALT or \
1740 event.key == K_LCTRL or \
1741 event.key == K_RCTRL:
1742 continue
1743 return 1
1744 elif event.type is MOUSEBUTTONDOWN:
1745 return 1
1746
1747 def translate_key( key, shift_state):
1748 if shift_state:
1749 if key >= ord('a') and key <= ord('z'): key += ord('A') - ord('a')
1750 elif key == ord('1'): key = ord('!')
1751 elif key == ord('2'): key = ord('@')
1752 elif key == ord('3'): key = ord('#')
1753 elif key == ord('4'): key = ord('$')
1754 elif key == ord('5'): key = ord('%')
1755 elif key == ord('6'): key = ord('^')
1756 elif key == ord('7'): key = ord('&')
1757 elif key == ord('8'): key = ord('*')
1758 elif key == ord('9'): key = ord('(')
1759 elif key == ord('0'): key = ord(')')
1760 elif key == ord('`'): key = ord('~')
1761 elif key == ord("'"): key = ord('"')
1762 elif key == ord(";"): key = ord(':')
1763 elif key == ord("\\"): key = ord('|')
1764 elif key == ord("["): key = ord('{')
1765 elif key == ord("]"): key = ord('}')
1766 elif key == ord(","): key = ord('<')
1767 elif key == ord("."): key = ord('>')
1768 elif key == ord("/"): key = ord('?')
1769 elif key == ord("-"): key = ord('_')
1770 elif key == ord("="): key = ord('+')
1771 return key
1772
1773 def get_name( screen, font, cursor_box, backcol, forecol):
1774 cursor_width = cursor_box[3] / 3
1775 cursor_pos = [cursor_box[0], cursor_box[1], cursor_width, cursor_box[3]]
1776 name = ""
1777
1778 inner_box = pygame.Rect(cursor_box)
1779 cursor_box = inner_box.inflate( 2, 2)
1780 outer_box = cursor_box.inflate( 2, 2)
1781
1782 enter_pressed = 0
1783 while not enter_pressed:
1784 screen.fill( forecol, outer_box)
1785 screen.fill( backcol, cursor_box)
1786 cursor_pos[0] = inner_box.left
1787 if name != "":
1788 obj = font.render( name, 1, forecol)
1789 screen.blit( obj, inner_box)
1790 cursor_pos[0] += obj.get_width()
1791 screen.fill( forecol, cursor_pos)
1792 pygame.display.update( (outer_box,))
1793
1794 # Keep track of the shift keys
1795 shift_state = pygame.key.get_mods() & KMOD_SHIFT
1796
1797 pygame.time.wait(20)
1798 for event in pygame.event.get():
1799 if event.type is QUIT:
1800 return None
1801 elif event.type is KEYUP:
1802 if event.key == K_LSHIFT:
1803 shift_state &= ~KMOD_LSHIFT
1804 elif event.key == K_RSHIFT:
1805 shift_state &= ~KMOD_RSHIFT
1806 elif event.type is KEYDOWN:
1807 if event.key == K_LSHIFT:
1808 shift_state |= KMOD_LSHIFT
1809 elif event.key == K_RSHIFT:
1810 shift_state |= KMOD_RSHIFT
1811 elif event.key == K_ESCAPE or event.key == K_RETURN:
1812 enter_pressed = 1
1813 break
1814 elif event.key == K_F2:
1815 toggle_fullscreen()
1816 elif event.key == K_F3:
1817 toggle_music()
1818 elif event.key == K_F4:
1819 toggle_sound()
1820 elif event.key == K_BACKSPACE:
1821 name = name[:-1]
1822 elif event.key >= 32 and event.key <= 127:
1823 key = translate_key( event.key, shift_state)
1824 name = name + chr(key)
1825
1826 return name
1827
1828 class IntroScreen:
1829 menu = ("Start Game", "High Scores", "Fullscreen:", "Music:",
1830 "Sound Effects:", "Quit Game")
1831 menu_width = 240
1832 menu_pos = ((800 - menu_width)/2, 145)
1833 menu_font_height = 32
1834 menu_color = (255,255,255)
1835 menu_cursor_color = (60,60,60)
1836 menu_cursor_leftright_margin = 2
1837 menu_cursor_bottom_margin = -2
1838 menu_cursor_top_margin = 0
1839 menu_option_left = 200
1840 menu_rect = (menu_pos[0]-menu_cursor_leftright_margin,
1841 menu_pos[1]-menu_cursor_top_margin,
1842 menu_width + 2 * menu_cursor_leftright_margin,
1843 menu_font_height * len(menu) +
1844 menu_cursor_top_margin + menu_cursor_bottom_margin)
1845
1846 scroller_font_height = 28
1847 scroller_rect = (10,550,780,scroller_font_height)
1848 scroller_text = \
1849 " Copyright © 2003 John-Paul Gignac. "+ \
1850 " Soundtrack by Matthias Le Bidan. "+ \
1851 " Board designs contributed by Mike Brenneman and Kim Gignac. "+ \
1852 " To contribute your own board designs, see the website: "+ \
1853 "http://pathological.sourceforge.net/ "+ \
1854 " Logo by Carrie Bloomfield. "+ \
1855 " Other graphics based on artwork by Mike Brenneman. "+ \
1856 " Project motivated by Paul Prescod. "+ \
1857 " Thanks to all my friends who helped make this project "+ \
1858 "a success! "+ \
1859 " This program is free software; you can redistribute it and/or "+ \
1860 "modify it under the terms of the GNU General Public License. "+ \
1861 "See the LICENSE file for details. "
1862
1863 scroller_color = (60,60,60)
1864 scroller_speed = 2
1865
1866 def __init__(self, screen, highscores):
1867 self.screen = screen
1868 self.highscores = highscores
1869 self.curpage = 0
1870
1871 self.scroller_image = self.scroller_font.render(
1872 self.scroller_text, 1, self.scroller_color)
1873
1874 self.menu_cursor = 0
1875
1876 def draw_background(self):
1877 self.screen.blit( self.background, (0,0))
1878
1879 def undraw_menu(self):
1880 if self.curpage == 1:
1881 self.undraw_highscores()
1882 return
1883
1884 self.screen.set_clip( self.menu_rect)
1885 self.draw_background()
1886 self.screen.set_clip()
1887 self.dirty_rects.append( self.menu_rect)
1888
1889 def undraw_highscores(self):
1890 self.screen.set_clip( self.hs_rect)
1891 self.draw_background()
1892 self.screen.set_clip()
1893 self.dirty_rects.append( self.hs_rect)
1894
1895 def draw_menu(self):
1896 if self.curpage == 1:
1897 self.draw_highscores()
1898 return
1899
1900 self.undraw_menu()
1901
1902 self.screen.fill( self.menu_cursor_color,
1903 (self.menu_pos[0]-self.menu_cursor_leftright_margin,
1904 self.menu_pos[1]-self.menu_cursor_top_margin +
1905 self.menu_cursor * self.menu_font_height,
1906 self.menu_width + 2 * self.menu_cursor_leftright_margin,
1907 self.menu_font_height + self.menu_cursor_top_margin +
1908 self.menu_cursor_bottom_margin))
1909
1910 y = self.menu_pos[1]
1911 for i in self.menu:
1912 menu_option = self.menu_font.render(i, 1, self.menu_color)
1913 self.screen.blit( menu_option, (self.menu_pos[0], y))
1914 y += self.menu_font_height
1915
1916 if fullscreen: offon = 'On'
1917 else: offon = 'Off'
1918 offon = self.menu_font.render( offon, 1, self.menu_color)
1919 self.screen.blit( offon,
1920 (self.menu_pos[0]+self.menu_option_left,
1921 self.menu_pos[1]+self.menu_font_height * 2))
1922
1923 if music_on: offon = 'On'
1924 else: offon = 'Off'
1925 offon = self.menu_font.render( offon, 1, self.menu_color)
1926 self.screen.blit( offon,
1927 (self.menu_pos[0]+self.menu_option_left,
1928 self.menu_pos[1]+self.menu_font_height * 3))
1929
1930 if sound_on: offon = 'On'
1931 else: offon = 'Off'
1932 offon = self.menu_font.render( offon, 1, self.menu_color)
1933 self.screen.blit( offon,
1934 (self.menu_pos[0]+self.menu_option_left,
1935 self.menu_pos[1]+self.menu_font_height * 4))
1936
1937 self.dirty_rects.append( self.menu_rect)
1938
1939 def draw_scroller(self):
1940 self.screen.set_clip( self.scroller_rect)
1941 self.draw_background()
1942 self.screen.blit( self.scroller_image,
1943 (self.scroller_rect[0] - self.scroller_pos,
1944 self.scroller_rect[1]))
1945 self.screen.set_clip()
1946 self.dirty_rects.append( self.scroller_rect)
1947
1948 def draw(self):
1949 self.dirty_rects = []
1950 self.draw_background()
1951 self.draw_menu()
1952 self.draw_scroller()
1953 pygame.display.update()
1954
1955 def go_to_main_menu(self):
1956 # Return to the main menu
1957 play_sound( menu_select)
1958 self.undraw_menu()
1959 self.curpage = 0
1960 self.draw_menu()
1961
1962 def go_to_highscores(self):
1963 # Go to the highscores page
1964 self.undraw_menu()
1965 self.curpage = 1
1966 self.draw_menu()
1967
1968 def do(self, show_highscores=0):
1969 self.scroller_pos = -self.scroller_rect[2]
1970
1971 if( show_highscores):
1972 self.dirty_rects = []
1973 self.go_to_highscores()
1974 pygame.display.update( self.dirty_rects)
1975
1976 self.draw()
1977
1978 start_music("intro.xm", intro_music_volume)
1979
1980 while 1:
1981 # Wait for the next frame
1982 my_tick( frames_per_sec)
1983
1984 self.dirty_rects = []
1985 self.draw_scroller()
1986
1987 # Advance the scroller
1988 self.scroller_pos += self.scroller_speed
1989 if self.scroller_pos >= self.scroller_image.get_rect().width:
1990 self.scroller_pos = -self.scroller_rect[2]
1991
1992 pygame.time.wait(20)
1993 for event in pygame.event.get():
1994 if event.type is QUIT:
1995 if self.curpage == 1:
1996 self.go_to_main_menu()
1997 continue
1998 return -2
1999 elif event.type is KEYDOWN:
2000 if event.key == K_F2:
2001 play_sound( menu_select)
2002 if not toggle_fullscreen(): return -3
2003 self.draw_menu()
2004 elif event.key == K_F3:
2005 play_sound( menu_select)
2006 toggle_music()
2007 self.draw_menu()
2008 elif event.key == K_F4:
2009 toggle_sound(1, self.dirty_rects)
2010 play_sound( menu_select)
2011 self.draw_menu()
2012 elif self.curpage == 1:
2013 self.go_to_main_menu()
2014 elif event.key == K_ESCAPE:
2015 return -1
2016 elif event.key == K_DOWN:
2017 self.menu_cursor += 1
2018 play_sound( menu_scroll)
2019 if self.menu_cursor == len(self.menu):
2020 self.menu_cursor = 0
2021 self.draw_menu()
2022 elif event.key == K_UP:
2023 self.menu_cursor -= 1
2024 play_sound( menu_scroll)
2025 if self.menu_cursor < 0:
2026 self.menu_cursor = len(self.menu) - 1
2027 self.draw_menu()
2028 elif event.key == K_SPACE or event.key == K_RETURN:
2029 rc = self.menu_select( self.menu_cursor)
2030 if rc < 1: return rc
2031 continue
2032 elif event.type is MOUSEBUTTONDOWN:
2033 if self.curpage == 1:
2034 self.go_to_main_menu()
2035 continue
2036
2037 pos = pygame.mouse.get_pos()
2038
2039 # Figure out which menu option is being clicked, if any
2040
2041 if pos[0] < self.menu_pos[0]: continue
2042 if pos[0] >= self.menu_pos[0] + self.menu_width: continue
2043 if pos[1] < self.menu_pos[1]: continue
2044 i = (pos[1] - self.menu_pos[1]) / self.menu_font_height
2045 if i >= len(self.menu): continue
2046
2047 rc = self.menu_select( i)
2048 if rc < 1: return rc
2049
2050 pygame.display.update( self.dirty_rects)
2051
2052 # Return values:
2053 # -3 - Toggle fullscreen requires warm restart
2054 # -1 - User selected the Quit option
2055 # 0 - User selected Begin Game
2056 # 1 - Unknown option
2057 def menu_select( self, i):
2058 if i == 0:
2059 return 0
2060 elif i == 1:
2061 play_sound( menu_select)
2062 self.go_to_highscores()
2063 elif i == 2:
2064 play_sound( menu_select)
2065 if not toggle_fullscreen(): return -3
2066 self.draw_menu()
2067 elif i == 3:
2068 play_sound( menu_select)
2069 toggle_music()
2070 self.draw_menu()
2071 elif i == 4:
2072 toggle_sound()
2073 play_sound( menu_select)
2074 self.draw_menu()
2075 elif i == 5:
2076 return -1
2077 return 1
2078
2079 hs_font_height = 24
2080 hs_width = 320
2081 hs_pos = ((800-hs_width)/2, 114)
2082 hs_margin = 8
2083 hs_column_margin = 70
2084 hs_score_width = 70
2085 hs_level_width = 24
2086 hs_heading_color = (0,0,0)
2087 hs_heading_background = (0,80,0)
2088 hs_number_color = (210,210,210)
2089 hs_body_color = (240,240,240)
2090 hs_current_color = (240,50,50)
2091 hs_rows = HighScores.num_highscores
2092 hs_rect = (hs_pos[0],hs_pos[1],hs_width,hs_font_height * 11)
2093
2094 def draw_highscores(self):
2095 self.undraw_menu()
2096
2097 tname = self.hs_font.render("Name", 1, self.hs_heading_color)
2098 tscore = self.hs_font.render("Score", 1, self.hs_heading_color)
2099 tlevel = self.hs_font.render("Level", 1, self.hs_heading_color)
2100
2101 # Compute the column positions
2102 score_right = self.hs_width
2103 score_left = score_right - tscore.get_size()[0]
2104 level_right = score_right - self.hs_score_width - \
2105 self.hs_margin
2106 level_left = level_right - tlevel.get_size()[0]
2107 name_right = level_right - self.hs_level_width - \
2108 self.hs_margin
2109 number = self.hs_font.render('10.',1,(0,0,0))
2110 number_width = number.get_size()[0]
2111 name_left = number_width + 5
2112 name_width = name_right - name_left
2113
2114 x = self.hs_pos[0]
2115 for j in range(len(self.highscores.scores)):
2116 if (j % self.hs_rows) == 0:
2117 if j > 0: x += self.hs_column_margin + self.hs_width
2118 y = self.hs_pos[1]
2119
2120 # Draw the headings
2121 # self.screen.fill( self.hs_heading_background,
2122 # (x - 30, y, self.hs_width + 30, self.hs_font_height))
2123 self.screen.blit( tname, (x + name_left, y))
2124 self.screen.blit( tlevel, (x + level_left, y))
2125 self.screen.blit( tscore, (x + score_left, y))
2126
2127 i = self.highscores.scores[j]
2128
2129 numcolor = self.hs_number_color
2130 color = self.hs_body_color
2131 if j == self.highscores.current_score:
2132 numcolor = color = self.hs_current_color
2133
2134 y += self.hs_font_height
2135 number = self.hs_font.render(`j+1`+'.',1,numcolor)
2136 self.screen.blit( number,
2137 (x + number_width - number.get_size()[0], y))
2138 if i[3] != '':
2139 name = self.hs_font.render( i[3], 1, color)
2140 if name.get_width() > name_width:
2141 name = name.subsurface( (0,0,name_width,name.get_height()))
2142 self.screen.blit( name, (x + name_left, y))
2143 level = self.hs_font.render( `i[2]`, 1, color)
2144 self.screen.blit( level, (x + level_right - level.get_width(), y))
2145 score = self.hs_font.render( `i[0]`, 1, color)
2146 self.screen.blit( score, (x + score_right - score.get_width(), y))
2147
2148 self.dirty_rects.append( self.hs_rect)
2149
2150 def setup_everything():
2151 global introscreen
2152
2153 # Configure the audio settings
2154 if sys.platform[0:3] == 'win':
2155 # On Windows platforms, increase the sample rate and the buffer size
2156 pygame.mixer.pre_init(44100,-16,1,4096)
2157
2158 # Initialize the game module
2159 pygame.init()
2160
2161 if not pygame.font: print 'Warning, fonts disabled'
2162 if not pygame.mixer: print 'Warning, sound disabled'
2163
2164 set_video_mode()
2165 load_sounds()
2166 load_fonts()
2167 load_images()
2168
2169 introscreen = IntroScreen( screen, highscores)
2170
2171 # Load the highscores file
2172 highscores = HighScores( highscores_file)
2173
2174 setup_everything()
2175
2176 # Main loop
2177 show_highscores = 0
2178 while 1:
2179 # Display the intro screen
2180 while 1:
2181 rc = introscreen.do( show_highscores)
2182 if rc == -3:
2183 # Warm restart to toggle fullscreen
2184 fullscreen = fullscreen ^ 1
2185 setup_everything()
2186 else:
2187 break
2188
2189 if rc < 0: break # Handle the QUIT message
2190
2191 game = Game(screen, 'all-boards', highscores)
2192
2193 show_highscores = 1
2194
2195 rc = game.play()
2196 if rc < 0: break # Handle the QUIT message
2197 if rc == 0: show_highscores = 0
2198
0 /* XPM */
1 static char *noname[] = {
2 /* width height ncolors chars_per_pixel */
3 "32 32 342 2",
4 /* colors */
5 " c #000000",
6 " . c #7AEE98",
7 " X c #5BEB80",
8 " o c #B6F6C6",
9 " O c #1EE351",
10 " + c #79EE97",
11 " @ c #B5F6C5",
12 " # c #1ADF4D",
13 " $ c #1ADD4D",
14 " % c #14AA3A",
15 " & c #13A439",
16 " * c #65DA83",
17 " = c #0A5D20",
18 " - c #718075",
19 " ; c #91F1AB",
20 " : c #CDF9D9",
21 " > c #EBFCF0",
22 " , c #EAFCEF",
23 " < c #8EF1A8",
24 " 1 c #E9FCEE",
25 " 2 c #8DF1A7",
26 " 3 c #E8FCED",
27 " 4 c #ABF4BE",
28 " 5 c #31E660",
29 " 6 c #8CF1A6",
30 " 7 c #C9F7D5",
31 " 8 c #AAF4BD",
32 " 9 c #E6FCEB",
33 " 0 c #C8F7D4",
34 " q c #4EE976",
35 " w c #C7F7D3",
36 " e c #6AEC8B",
37 " r c #15B43E",
38 " t c #15B23E",
39 " y c #70BD84",
40 " u c #0C6D25",
41 " i c #0C6B25",
42 " p c #1D582C",
43 " a c #17C243",
44 " s c #17C043",
45 " d c #96A399",
46 " f c #0E7B2A",
47 " g c #0E792A",
48 " h c #0D7729",
49 " j c #063412",
50 " k c #19D048",
51 " l c #82EF9F",
52 " z c #63EC87",
53 " x c #19CE48",
54 " c c #81EF9E",
55 " v c #DCFAE4",
56 " b c #62EC86",
57 " n c #80EF9D",
58 " m c #DBFAE3",
59 " M c #61EC85",
60 " N c #10892F",
61 " B c #23E455",
62 " V c #7EEF9B",
63 " C c #D9FAE1",
64 " Z c #9CF2B2",
65 " A c #D8FAE0",
66 " S c #40E76B",
67 " D c #9BF2B1",
68 " F c #074016",
69 " G c #859188",
70 " H c #129734",
71 " J c #129534",
72 " K c #094E1B",
73 " L c #010903",
74 " P c #14A539",
75 " I c #EFFDF3",
76 " U c #A4E1B3",
77 " Y c #38E766",
78 " T c #C2E4CA",
79 " R c #B1F5C3",
80 " E c #74ED94",
81 " W c #129F37",
82 " Q c #55EA7C",
83 " ! c #CEF8D9",
84 " ~ c #72ED92",
85 " ^ c #53EA7A",
86 " / c #90F0A9",
87 " ( c #71ED91",
88 " ) c #CCF8D7",
89 " _ c #70ED90",
90 " ` c #CBF8D6",
91 " ' c #A7F1B9",
92 " ] c #0D6C25",
93 " [ c #14AD3C",
94 " { c #04250C",
95 " } c #03210B",
96 " | c #16BD41",
97 ". c #16BB41",
98 ".. c #063311",
99 ".X c #E4FBEB",
100 ".o c #E3FBEA",
101 ".O c #052F10",
102 ".+ c #E1FBE8",
103 ".@ c #DFFBE6",
104 ".# c #C1F6CF",
105 ".$ c #47E871",
106 ".% c #A2F3B7",
107 ".& c #28E559",
108 ".* c #83F09F",
109 ".= c #BFF6CD",
110 ".- c #45E86F",
111 ".; c #BEF6CC",
112 ".: c #62EB85",
113 ".> c #18CB46",
114 "., c #565F59",
115 ".< c #18C946",
116 ".1 c #5FEB82",
117 ".2 c #0F822D",
118 ".3 c #1AD94B",
119 ".4 c #1AD74B",
120 ".5 c #119232",
121 ".6 c #119032",
122 ".7 c #CCE7D3",
123 ".8 c #50D773",
124 ".9 c #424F45",
125 ".0 c #D4F9DE",
126 ".q c #5AEB80",
127 ".w c #B5F6C6",
128 ".e c #D3F9DD",
129 ".r c #77EE96",
130 ".t c #D2F9DC",
131 ".y c #94F1AC",
132 ".u c #EFFCF2",
133 ".i c #13A037",
134 ".p c #B2F4C3",
135 ".a c #EEFCF1",
136 ".s c #139E37",
137 ".d c #37E664",
138 ".f c #0A571E",
139 ".g c #09531D",
140 ".h c #15AE3C",
141 ".j c #0C6523",
142 ".k c #E7FCED",
143 ".l c #AAF4BE",
144 ".z c #E6FCEC",
145 ".x c #15B63F",
146 ".c c #4CE975",
147 ".v c #0E7328",
148 ".b c #6AEC8C",
149 ".n c #A6F4BA",
150 ".m c #69EC8B",
151 ".M c #C4F7D1",
152 ".N c #4AE973",
153 ".B c #C3F7D0",
154 ".V c #67EC89",
155 ".C c #84EF9F",
156 ".Z c #83EF9E",
157 ".A c #528B61",
158 ".S c #A0F0B4",
159 ".D c #195829",
160 ".F c #17C444",
161 ".G c #0C0D0C",
162 ".H c #0D792A",
163 ".J c #19D449",
164 ".K c #19D249",
165 ".L c #284B31",
166 ".P c #108B30",
167 ".I c #9DF2B4",
168 ".U c #D9FAE2",
169 ".Y c #D8FAE1",
170 ".T c #21E454",
171 ".R c #7CEF9A",
172 ".E c #D7FAE0",
173 ".W c #9AF2B1",
174 ".Q c #3EE76A",
175 ".! c #99F2B0",
176 ".~ c #1FE452",
177 ".^ c #3CE768",
178 "./ c #B5F5C5",
179 ".( c #59EA7E",
180 ".) c #1BE24E",
181 "._ c #1BE04E",
182 ".` c None",
183 ".' c #129935",
184 ".] c #14A73A",
185 ".[ c #69DB87",
186 ".{ c #0B6021",
187 ".} c #0B5E21",
188 ".| c #47D66C",
189 "X c #357947",
190 "X. c #337945",
191 "XX c #6F7F73",
192 "Xo c #CBF8D7",
193 "XO c #51EA79",
194 "X+ c #ACF5BF",
195 "X@ c #E9FBEE",
196 "X# c #CAF8D6",
197 "X$ c #6EED8F",
198 "X% c #C9F8D5",
199 "X& c #E7FBEC",
200 "X* c #C8F8D4",
201 "X= c #30E55F",
202 "X- c #16B73F",
203 "X; c #2EE55D",
204 "X: c #A5F1B8",
205 "X> c #0D6E26",
206 "X, c #188434",
207 "X< c #1D943C",
208 "X1 c #0F7C2B",
209 "X2 c #0E7A2A",
210 "X3 c #0D7629",
211 "X4 c #DEFBE6",
212 "X5 c #A0F3B6",
213 "X6 c #43E86E",
214 "X7 c #18CD47",
215 "X8 c #9EF3B4",
216 "X9 c #BBF6CA",
217 "X0 c #41E86C",
218 "Xq c #D9F9E1",
219 "Xw c #5EEB82",
220 "Xe c #7CEE99",
221 "Xr c #0F842E",
222 "Xt c #99F1AF",
223 "Xy c #073F16",
224 "Xu c #869289",
225 "Xi c #1ADB4C",
226 "Xp c #050C07",
227 "Xa c #010A03",
228 "Xs c #8BDFA1",
229 "Xd c #A8E2B7",
230 "Xf c #C3E5CB",
231 "Xg c #13A238",
232 "Xh c #EEFCF2",
233 "Xj c #0B5D20",
234 "Xk c #EDFCF1",
235 "Xl c #73EE93",
236 "Xz c #91F1AA",
237 "Xx c #ECFCF0",
238 "Xc c #AFF4C1",
239 "Xv c #35E663",
240 "Xb c #90F1A9",
241 "Xn c #EAFCEE",
242 "Xm c #33E661",
243 "XM c #E9FCED",
244 "XN c #50E977",
245 "XB c #ABF4BD",
246 "XV c #ABF2BD",
247 "XC c #93C09F",
248 "XZ c #74BD87",
249 "XA c #15B03D",
250 "XS c #0C6B24",
251 "XD c #0C6924",
252 "XF c #0B6923",
253 "XG c #0C6724",
254 "XH c #17BE42",
255 "XJ c #0E7729",
256 "XK c #0E7529",
257 "XL c #A5F4BA",
258 "XP c #053010",
259 "XI c #C3F7D1",
260 "XU c #A4F4B9",
261 "XY c #E1FAE8",
262 "XT c #C2F7D0",
263 "XR c #C1F7CF",
264 "XE c #65EC88",
265 "XW c #DEFAE5",
266 "XQ c #64EC87",
267 "X! c #27E458",
268 "X~ c #DDFAE4",
269 "X^ c #9FF2B4",
270 "X/ c #25E456",
271 "X( c #4E8B5E",
272 "X) c #10852E",
273 "X_ c #17C645",
274 "X` c #0F832D",
275 "X' c #073E15",
276 "X] c #19D64A",
277 "X[ c #108F31",
278 "X{ c #084819",
279 "X} c #000501",
280 "X| c #7AEF99",
281 "o c #414E44",
282 "o. c #98F2B0",
283 "oX c #3AE767",
284 "oo c #95F2AD",
285 "oO c #D2F8DC",
286 "o+ c #B3F5C4",
287 "o@ c #94F2AC",
288 "o# c #57EA7D",
289 "o$ c #B2F5C3",
290 "o% c #75ED94",
291 "o& c #74ED93",
292 "o* c #B0F5C1",
293 "o= c #129D36",
294 "o- c #129B36",
295 "o; c #0A581E",
296 "o: c #23863D",
297 "o> c #95C1A0",
298 "o, c #09541D",
299 "o< c #14AB3B",
300 "o1 c #14A93B",
301 "o2 c #0B6222",
302 "o3 c #16B940",
303 "o4 c #8BF0A6",
304 "o5 c #C7F8D4",
305 "o6 c #16B740",
306 "o7 c #8AF0A5",
307 "o8 c #E5FBEB",
308 "o9 c #89F0A4",
309 "o0 c #E4FBEA",
310 "oq c #6AED8C",
311 "ow c #A7F3BB",
312 "oe c #88F0A3",
313 "or c #E3FBE9",
314 "ot c #0D7227",
315 "oy c #2CE55C",
316 "ou c #87F0A2",
317 "oi c #E2FBE8",
318 "op c #0D7027",
319 "oa c #49E872",
320 "os c #A4F3B8",
321 "od c #2AE55A",
322 "of c #85F0A0",
323 "og c #728F7A",
324 "oh c #718F79",
325 "oj c #565F58",
326 "ok c #18C745",
327 "ol c #0F802C",
328 "oz c #0F7E2C",
329 "ox c #0E7E2B",
330 "oc c #063913",
331 "ov c #118E31",
332 "ob c #CEE7D4",
333 "on c #2A4C33",
334 "om c #91DFA5",
335 "oM c #118C31",
336 "oN c #BBF6CB",
337 "oB c #9CF3B3",
338 "oV c #B9F6C9",
339 "oC c #7CEE9A",
340 "oZ c #D7F9E0",
341 "oA c #B8F6C8",
342 "oS c #7BEE99",
343 "oD c #D6F9DF",
344 "oF c #5CEB81",
345 "oG c #B7F6C7",
346 "oH c None",
347 /* pixels */
348 ".`.`.`.`.`.`.`.`.`.`.`.` .`.`.`.`.`.`.`.`.`.`.`.`",
349 ".`.`.`.`.`.`.`.`.` .9ogo>XdXVX:omXZ.Aon .`.`.`.`.`.`.`.`.`",
350 ".`.`.`.`.`.`.` .G -Xf `XIX9o*XLoBoo 6.*X|.[X Xp .`.`.`.`.`.`.`",
351 ".`.`.`.`.`.` .,ob m.0 ).B o.lX8oo 6.*.R ~XE.q.|.D .`.`.`.`.`.`",
352 ".`.`.`.`.` Xu.XorX4.U.tXIoG.n.W 2.*.R ~.VoF qX0.dX, .`.`.`.`.`",
353 ".`.`.`.` d.k 3.z.o.@.EX#oAXUo@ouX| ~.mXwXO.$oXoy.TX[ .`.`.`.`",
354 ".`.`.` G.kX@XnXx 1o8 v :oVos < cXloq M Q.cX0XvX!.)X]X3 .`.`.`",
355 ".`.` ojo0X& ,Xk.uXk 3XW :X9X8o4oCoq z.q ^oa.QXmX/ #.KX_X{ .`.`",
356 ".`.`.G.7XYo8 >Xh IXkXMX~Xo @o.of ( zXwo# qX6.^ 5X/ # k.F % L.`.`",
357 ".` XXXqXWoi 1Xx.aXnoroDXR 8 <oS eXwo#XNoa SoXX; B # x s ro; .`",
358 ".` ToOoZX~or 9X&.+ CX%o$ D Vo& b.q ^ q.-.QXvoy.T $ k s to- .`",
359 ".`o 0 7 !.e A m.Y.to5.wX8oe EXEoF ^ q.$ S.dX=.&.~XiX7 | [.i.O.`",
360 " oh.=.;.#.MX* wXToNX+ Zo9o&.VoFo# qoaX6oXXvoyX/.).4.<o6 P.'.g ",
361 " XC./.p Ro$o+Xc 4X5Xz.Zo&.V X QXNoaX6.Q.dX;X!.~Xi x s.h.i.6XS ",
362 " UXBow.%X^.IXt ;oe .X$.: X Q qoaX6.Q.dX;.&.~._.J.<o3.] H.P.H ",
363 " '.% D.y /o9.CoS (.b.1.( ^ q.$X6.Q YX=.&.T.).4X7 sXA.s.6X)X2 ",
364 " .S.!Xbo7 lXeo%X$XQXwo# ^.c.$X6.^.dX=.&.~.).4 x.Fo6.] J Noz h ",
365 " XsXbo9 n + ~oq zXwo# ^.N.$X0oXXvX=.&.T._.3 kX_o3o<o=ov.2 gXF ",
366 " you V + ~.m zoFo#XO.N.- S YXvX;.&.T.)Xi kX_. .h.i.5Xr fot.f ",
367 " X( V.r ~.m MoFo#XO.N.- S YXmoyX!.~.).3 kok |XA & J NX1.vopXy ",
368 ".`.L.r _.m MoFo#XNoa.- S YXvoyX!.~._Xi.Kok | t P H.PozXKotX> }.`",
369 ".` *.V M.( ^ q.$.- S.dXmX;X!.T.).3.K.<XH t.]o-ov.2XJotX>o2 .`",
370 ".` X.Xwo#XO q.$ S.^.d 5oyX!.~.)Xi.K.< s.x.]o=.6XrXJotX> i j .`",
371 ".`.`Xp.8XN.cX6 S YXvX;.&X/ O._Xi.K.> s ro1o=.6Xr g.vX> i.}X}.`.`",
372 ".`.` p.$ SoXXvX=od B.~.) $.4.Kok s ro1o=ovXrXJ.vX>XDXG { .`.`",
373 ".`.`.` o: Y 5oyX/.~._ $.4 kX7X_XH r.]o-ov.2XJ.vX>XD.joc .`.`.`",
374 ".`.`.`.` X<X!.~._XiX] k.<.FXHo3XA Po-ov.2XJot ] iXG F .`.`.`.`",
375 ".`.`.`.`.` X`XiX]X7ok a |.x to<Xgo-ov.2XJot ]XD.joc .`.`.`.`.`",
376 ".`.`.`.`.`.` KX- so3 t [ P.io- JoMolXJot ]XDXj { .`.`.`.`.`.`",
377 ".`.`.`.`.`.`.` Xa = W &o= H.5ovX)ozXKot ].{..X} .`.`.`.`.`.`.`",
378 ".`.`.`.`.`.`.`.`.` XPo, uX2ox hXF.fX' } .`.`.`.`.`.`.`.`.`",
379 ".`.`.`.`.`.`.`.`.`.`.`.` .`.`.`.`.`.`.`.`.`.`.`.`"
380 };
0 0 all-boards 1 John-Paul
1 0 all-boards 1 Kim
2 0 all-boards 1 Matths
3 0 all-boards 1 Carrie
4 0 all-boards 1 Mike
5 0 all-boards 1 Dale
6 0 all-boards 1 Alesh
7 0 all-boards 1 Thanks to
8 0 all-boards 1 everyone who
9 0 all-boards 1 contributed!
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
0 /*
1 * Setgid program for writing the Pathological highscores file
2 *
3 * Copyright (C) 2003 John-Paul Gignac
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 #define HIGHSCORES "/var/games/pathological_scores"
21
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27
28 #define BUFFER_SIZE 1024
29
30 unsigned char buffer[BUFFER_SIZE];
31
32 void die(void)
33 {
34 perror( "write-highscores: " HIGHSCORES);
35 exit(1);
36 }
37
38 int main()
39 {
40 int fd;
41 size_t size;
42
43 if( (fd = open( HIGHSCORES, O_WRONLY | O_TRUNC )) == -1) die();
44
45 while( (size = read( 0, buffer, BUFFER_SIZE)) > 0) {
46 if( write( fd, buffer, size) == -1) die();
47 }
48
49 if( close( fd) == -1) die();
50
51 return 0;
52 }