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
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 | 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 | ||
0 | 10 | pathological (1.1.3-14) unstable; urgency=medium |
1 | 11 | |
2 | 12 | * Team upload. |
4 | 4 | Uploaders: |
5 | 5 | Barry deFreese <bdefreese@debian.org> |
6 | 6 | Build-Depends: |
7 | debhelper (>= 9), | |
7 | debhelper (>= 11), | |
8 | 8 | dh-python, |
9 | 9 | netpbm, |
10 | 10 | python, |
11 | 11 | python-pygame, |
12 | 12 | vorbis-tools, |
13 | 13 | xmp |
14 | Standards-Version: 3.9.6 | |
14 | Standards-Version: 4.1.4 | |
15 | 15 | 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 | |
18 | 18 | |
19 | 19 | Package: pathological |
20 | 20 | 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/ | |
1 | 1 | Upstream-Name: Pathological |
2 | 2 | Source: http://pathological.sourceforge.net/ |
3 | 3 | |
12 | 12 | 2009, Cyril Brulebois, Eddy Petrișor, Gonéri Le Bouder |
13 | 13 | Ansgar Burchardt |
14 | 14 | 2012, Evgeniy Dolgikh |
15 | 2013-2015, Markus Koschany <apo@debian.org> | |
15 | 2013-2018, Markus Koschany <apo@debian.org> | |
16 | 16 | License: GPL-2+ |
17 | 17 | |
18 | 18 | License: GPL-2+ |
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
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 | } |