Package list pangzero / upstream/latest
Imported Upstream version 1.4.1+git20121103 Markus Koschany 5 years ago
134 changed file(s) with 5961 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 UPi <upi at sourceforge.net>
1 SAdam <sadam at apocalypse.rulez.org>
2 DaniGM <danigm at gmail.com>
3 Elio Blanca <eblanca76 at users.sourceforge.net>
0 use strict;
1 use warnings;
2 use Module::Build;
3
4 my $build = Module::Build->new(
5 module_name => 'Games::PangZero',
6 all_from => 'lib/Games/PangZero.pm',
7 dist_abstract => 'a fast-paced action game about popping balloons with a harpoon',
8 dist_author => [
9 'UPi <upi@sourceforge.net>',
10 'SAdam <sadam@apocalypse.rulez.org>',
11 'DaniGM <danigm@gmail.com>',
12 'Elio Blanca <eblanca76@users.sourceforge.net>',
13 ],
14 license => 'gpl',
15 requires => {
16 'File::ShareDir' => '0',
17 'File::Spec' => '0',
18 'Time::HiRes' => '0',
19 'SDL' => '2.536',
20 },
21 configure_requires => {
22 'Module::Build' => '0.38',
23 },
24 meta_merge => {
25 resources => {
26 bugtracker => 'http://github.com/kthakore/pangzero/issues',
27 repository => 'http://github.com/kthakore/pangzero'
28 }
29 },
30 share_dir => 'data',
31 )->create_build_script();
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
1 2012-03-12 19:42 FROGGS
2 * 1.4.1, fix for SDL's Joystick API
3
4 2012-02-21 22:27 kthakore, FROGGS
5 * ChangeLog, VERSION, *.p[l/m]: Bumped version to 1.4, making pangzero
6 work with upstream release of SDL. Many fixes.
7 See http://cpansearch.perl.org/src/FROGGS/SDL-2.536/CHANGELOG
8
9 2007-07-15 10:12 upi
10
11 * bin/upipang.pl: Proper website launching for Windows. High level
12 table is now saved properly.
13
14 2007-06-28 09:10 upi
15
16 * ChangeLog, VERSION, bin/upipang.pl: Bumped version to 1.2.
17
18 2007-06-28 09:10 upi
19
20 * INSTALL: Basic installation instructions.
21
22 2007-06-28 08:56 upi
23
24 * bin/upipang.pl: Some minor tweaks. Balls smaller than level 3 no
25 longer have magic in them. Fixed a bug related to multiple slow
26 effects. Slow effect no longer survives in Challenge Mode.
27 Eliminated some SDL warning messages. Added TutorialGame (one on
28 one with a specific kind of ball).
29
30 2007-06-28 08:47 upi
31
32 * data/: Balls-Upside128.png, Balls-Upside64.png: Fixed background
33 green
34
35 2007-06-28 08:46 upi
36
37 * data/bonus.png: New bonus graphics
38
39 2007-04-03 21:35 upi
40
41 * bin/upipang.pl: Added UpsideDownBall, minor fixes.
42
43 2007-04-03 21:33 upi
44
45 * data/: Balls-Upside128.png, Balls-Upside16.png,
46 Balls-Upside32.png, Balls-Upside64.png, Balls-Upside96.png: Added
47 UpsideDownBall
48
49 2007-01-04 10:38 upi
50
51 * VERSION, bin/upipang.pl: Separate high score tables for Panic and
52 Challenge modes. More challenge levels.
53
54 2007-01-03 23:31 upi
55
56 * bin/upipang.pl: Show website at exit.
57
58 2007-01-03 19:51 upi
59
60 * bin/upipang.pl, data/bonus.png: Added "Matrix effect": slows the
61 game down for 15 sec.
62
63 2007-01-01 09:52 upi
64
65 * ChangeLog, VERSION: Bumped version number to 1.0
66
67 2007-01-01 09:50 upi
68
69 * bin/upipang.pl: Quick fix for the Demo before 1.0
70
71 2006-12-31 16:32 upi
72
73 * bin/upipang.pl: Added option for weapon durations.
74
75 2006-12-31 15:49 upi
76
77 * bin/upipang.pl: Some more fixes for Challenge mode.
78
79 2006-12-31 10:54 upi
80
81 * bin/upipang.pl: Fixes for challenge mode. Better scoreboard
82 layout for very small screen heights.
83
84 2006-12-30 19:45 upi
85
86 * bin/upipang.pl: Improved High Score table. Menu items now show
87 tool tips at the bottom of the screen.
88
89 2006-12-30 12:33 upi
90
91 * bin/upipang.pl: Player can choose Panic or Challenge game from
92 the menu.
93
94 2006-12-30 10:38 upi
95
96 * bin/upipang.pl: Bugfix for machine gun.
97
98 2006-12-30 10:21 upi
99
100 * bin/upipang.pl: Some speed improvements. Removed the Christmas
101 decor.
102
103 2006-12-23 10:40 upi
104
105 * bin/upipang.pl: Improved ball positioning in challenge mode.
106
107 2006-12-20 14:16 upi
108
109 * bin/upipang.pl, data/santa.png: Added flying Santa.
110
111 2006-12-20 13:29 upi
112
113 * VERSION, bin/upipang.pl, data/christmas_candle.jpg,
114 data/christmas_house.jpg, data/christmas_houses.jpg,
115 data/christmas_tree.jpg, data/christmas_trees.jpg,
116 data/guyChristmas.png, data/guy_r2.png: Graphics update for 0.19
117
118 2006-12-20 10:39 upi
119
120 * bin/upipang.pl, data/glossyfont.png: Number of players moved to
121 "Player setup" menu. Several bugfixes (santa pixel bug, credits
122 bug, 200.000 points bonus life bug) Level display more prominent
123 in Challenge Game mode. Error is better reported if the program
124 dies.
125
126 2006-12-16 21:41 upi
127
128 * data/guyChristmas.png: Fix of minor graphics glitch.
129
130 2006-12-16 21:23 upi
131
132 * bin/upipang.pl: Minor bugfixes
133
134 2006-12-16 14:42 upi
135
136 * bin/upipang.pl: Bugfixing for credits screen.
137
138 2006-12-16 09:43 upi
139
140 * ChangeLog, VERSION, bin/upipang.pl, data/fireplace.jpg,
141 data/xmas-dawn.jpg, data/xmas-night.jpg: Updated version number.
142 Updated demo. New Christmas backgrounds.
143
144 2006-12-15 23:33 upi
145
146 * bin/upipang.pl, data/bonus.png: XmasBall now drops random weapon
147 or powerup. Hexa balls rotation is improved.
148
149 2006-12-15 11:11 upi
150
151 * bin/upipang.pl: Slippery floor can be toggled in the menu
152
153 2006-12-15 09:55 upi
154
155 * bin/upipang.pl: Added XmasBall.
156
157 2006-12-15 09:54 upi
158
159 * data/: Balls-Snow128.png, Balls-Snow16.png, Balls-Snow32.png,
160 Balls-Snow64.png, Balls-Snow96.png, Balls-XMAS128.png,
161 border-lighted-theme.png, border-xmas-theme.png: Updated
162 christmas graphics.
163
164 2006-12-15 02:23 upi
165
166 * bin/upipang.pl, data/Balls-Snow128.png, data/Balls-Snow16.png,
167 data/Balls-Snow32.png, data/Balls-Snow64.png,
168 data/Balls-Snow96.png, data/Balls-XMAS128.png,
169 data/border-lighted-theme.png, data/border-xmas-theme.png,
170 data/guyChristmas.png: Christmas edition graphics. Challenge
171 version added.
172
173 2006-12-15 01:32 upi
174
175 * ChangeLog, bin/upipang.pl, data/Balls-Fragile128.png,
176 data/Balls-Fragile16.png, data/Balls-Fragile32.png,
177 data/Balls-Fragile64.png, data/Balls-Fragile96.png: Minor code
178 refactoring. Added FragileBall.
179
180 2006-12-04 14:00 upi
181
182 * VERSION, bin/upipang.pl, data/bonus.png, data/border.png:
183 Improved border for 0.17
184
185 2006-12-01 15:06 upi
186
187 * AUTHORS, ChangeLog, VERSION: Update for version 0.16
188
189 2006-12-01 15:06 upi
190
191 * bin/upipang.pl: Better handling of music files.
192
193 2006-12-01 15:05 upi
194
195 * data/: Hexa-16.png, Hexa-32.png, Hexa-64.png: Background fixes
196 for Hexas.
197
198 2006-12-01 11:16 upi
199
200 * data/: Balls-Bouncy16.png, Balls-Bouncy32.png,
201 Balls-Bouncy64.png, Balls-Death64.png, Balls-EarthQ16.png,
202 Balls-EarthQ32.png, Balls-EarthQ64.png, Balls-Red128.png,
203 Balls-Red16.png, Balls-Red32.png, Balls-Red64.png,
204 Balls-Red96.png, Balls-Seeker32.png, Balls-Seeker64.png,
205 Balls-SuperClock64.png, Balls-SuperClock96.png,
206 Balls-SuperStar64.png, Balls-SuperStar96.png, Balls-Water16.png,
207 Balls-Water32.png, Balls-Water64.png, Balls-Water96.png,
208 Hexa-16.png, Hexa-32.png, Hexa-64.png, guy_danigm.png,
209 guy_pix.png, guy_pux.png, guy_r2.png, guy_sonic.png, l1.jpg,
210 l2.jpg, l3.jpg, l4.jpg, l5.jpg, l6.jpg, l7.jpg, l8.jpg, l9.jpg:
211 Data files updated
212
213 2006-11-30 19:22 upi
214
215 * bin/upipang.pl: New graphics, hopefully faster, too. High score
216 table.
217
218 2006-10-08 00:39 upi
219
220 * AUTHORS, ChangeLog, VERSION, bin/upipang.pl: Updated version and
221 credits for 0.15
222
223 2006-10-08 00:26 upi
224
225 * bin/upipang.pl: Added FPS indicator.
226
227 2006-10-07 23:47 upi
228
229 * bin/upipang.pl: Players can now select character and color.
230 AlterPalette now uses display_format (hopefully faster).
231
232 2006-10-07 23:43 upi
233
234 * data/ball.png: New and improved ball
235
236 2006-10-07 23:18 upi
237
238 * data/: guy_danigm.png, guy_pix.png, guy_pux.png: Added selectable
239 characters.
240
241 2006-10-05 15:07 upi
242
243 * bin/upipang.pl: Fixed for the menu changes.
244
245 2006-10-05 13:11 upi
246
247 * bin/upipang.pl: Nifty menu animations.
248
249 2006-10-05 08:47 upi
250
251 * bin/upipang.pl: Added "ball mixer" to the menu.
252
253 2006-09-06 10:57 upi
254
255 * bin/upipang.pl, data/guy.png: More appealing death effect.
256
257 2006-09-06 10:09 upi
258
259 * data/harpoon.png: Added straight harpoon by eblanca76.
260
261 2006-09-06 00:38 upi
262
263 * bin/upipang.pl: More forgiving collision detection.
264
265 2006-09-05 23:17 upi
266
267 * bin/upipang.pl, data/harpoon.png: New harpoon graphics by
268 eblanca76 at sourceforge.net
269
270 2006-09-05 18:16 upi
271
272 * bin/upipang.pl, data/meltdown.png: Deathball Meltdown at 30+
273 death balls.
274
275 2006-09-05 15:46 upi
276
277 * bin/upipang.pl: DeadGuy uses RotoZoom (funny effect).
278
279 2006-09-05 13:45 upi
280
281 * bin/upipang.pl: Fix for previous checkin.
282
283 2006-09-05 13:44 upi
284
285 * bin/upipang.pl: Workaround for SDL_perl 1.2.20
286
287 2006-09-04 22:21 upi
288
289 * bin/upipang.pl, data/guy.png, data/level.png,
290 data/level_empty.png: Widescreen mode improvements, better
291 scoreboard.
292
293 2006-08-21 14:10 upi
294
295 * VERSION, ChangeLog: Updated for version 0.13
296
297 2006-08-21 14:08 upi
298
299 * bin/upipang.pl: Minor graphics changes, max players set to 6
300
301 2006-08-21 14:05 upi
302
303 * data/guy.png: Replaced graphics with free version by "DaniGM"
304 (panreyes@panreyes.com)
305
306 2006-08-14 20:40 upi
307
308 * bin/upipang.pl: Bugfix in background image zooming
309
310 2006-08-14 19:01 upi
311
312 * bin/upipang.pl: Widescreen mode. Some code cleanup.
313
314 2006-08-07 11:38 upi
315
316 * bin/upipang.pl: * Fixed SDL_perl 2.x compatibility issue * Added
317 Rewind capability to RecordGame
318
319 2006-08-05 16:21 upi
320
321 * bin/upipang.pl: New Demo (includes SeekerBall). Fix for SDL_perl
322 2.x
323
324 2006-08-04 20:53 upi
325
326 * ChangeLog, VERSION: Updated for version 0.12
327
328 2006-08-04 20:34 upi
329
330 * data/: bonus.png, harpoon.png: Adjusted for new weapons
331
332 2006-08-04 20:30 upi
333
334 * bin/upipang.pl: * Joysticks now work in the menu. * Added
335 "Credits" screen. * New ball type: "Seeker" ball. * Fullscreen
336 / window can be toggled in the menu * Refactoring: Rolled package
337 SpawningBall into Ball. * Refactoring: Removed individual "anim"
338 attributes and moved them to Game->{anim} * Made sure that the
339 GamePause is always on top. * Added Replay capability to normal
340 games. * Randomized bonus collection. * Visual enhancements *
341 Improved scoreboard layout * Added new difficulty level: "Miki" *
342 Split off "Options" menu * Controls menu now displays key names
343
344 2006-07-28 06:17 upi
345
346 * bin/upipang.pl: * Added new ball type: WaterBall * Added weapons
347 and weapon drops: MachineGun, PowerWire, HalfCutter * Code
348 cleanup * Uses Carp module * SuperBall now has its own BallDesc
349 (no extra parameter uglyness) * BallDesc now contains the package
350 name of the ball, so Ball::Create is cleaner * Fixed a bug which
351 caused GamePause to disappear sometimes. * Player lives
352 indicator now works for >3 lives. * Some minor fixes.
353
354 2006-07-20 11:52 upi
355
356 * bin/upipang.pl: Earthquake-ball related tweaks
357
358 2006-07-20 11:37 upi
359
360 * ChangeLog, VERSION: Updated for 0.11
361
362 2006-07-20 11:22 upi
363
364 * bin/upipang.pl: Better joystick support
365
366 2006-07-20 10:01 upi
367
368 * bin/upipang.pl, data/gameover.png, data/paused.png: Game Over and
369 Paused messages
370
371 2006-07-20 09:20 upi
372
373 * bin/upipang.pl: New background every 10 levels
374
375 2006-07-20 01:14 upi
376
377 * bin/upipang.pl: Color the border red during death sequence
378
379 2006-07-20 00:50 upi
380
381 * bin/upipang.pl, data/bonus.png: Added pictograms for Earthquake
382 Balls and Death Balls
383
384 2006-07-20 00:42 upi
385
386 * data/quake.voc: Added Earthquake Balls
387
388 2006-07-19 21:59 upi
389
390 * bin/upipang.pl: Added Earthquake Balls
391
392 2006-07-19 18:34 upi
393
394 * bin/upipang.pl: The number of death balls spawned is now limited
395 to 2
396
397 2006-07-19 18:20 upi
398
399 * bin/upipang.pl: Added demo / help
400
401 2006-07-19 14:09 upi
402
403 * bin/upipang.pl: Can take multiple screanshots with W key
404
405 2006-07-18 12:36 upi
406
407 * ChangeLog, VERSION: Updated for 0.10
408
409 2006-07-16 14:04 upi
410
411 * bin/upipang.pl: Sound and music can be toggled on/off in main
412 menu
413
414 2006-07-16 12:03 upi
415
416 * bin/upipang.pl: Added difficulty levels. Split controls menu
417 from main menu.
418
419 2006-07-15 13:34 upi
420
421 * bin/upipang.pl: Renamed package to SpawningBall
422
423 2006-07-15 13:06 upi
424
425 * bin/upipang.pl: Factored the menu-related subs into a package
426 (more OOP!)
427
428 2006-07-15 10:06 upi
429
430 * bin/upipang.pl: Esc key now goes back to the menu instead of
431 quitting during game
432
433 2006-07-14 23:16 upi
434
435 * bin/upipang.pl: Added configuration loading and saving
436
437 2006-07-13 22:53 upi
438
439 * bin/upipang.pl: Factored apart Game and GameBase packages.
440
441 2006-07-12 09:14 upi
442
443 * AUTHORS, COPYING, ChangeLog, NEWS, README, TODO, VERSION: Import
444 of usual documentation files.
445
446 2006-07-12 08:47 upi
447
448 * bin/upipang.pl: Automatically find data directory (good for
449 autoconf)
450
451 2006-07-12 08:14 upi
452
453 * bin/upipang.pl: Main game loop now in the Game package.
454
455 2006-07-11 13:31 upi
456
457 * bin/upipang.pl: Factored many global methods and variables into
458 package Game.
459
460 2006-07-10 05:55 upi
461
462 * bin/upipang.pl: Better game balance, respawning players.
463
464 2006-06-23 21:47 upi
465
466 * data/: UPiPang.mid, UPiPang.mp3, ball.png, bonus.png, border.png,
467 brandybun3.png, desert2.png, font2.png, gun.voc, guy.png,
468 harpoon.png, hexa.png, level.voc, magic.voc, meow.voc, pop.voc,
469 pop3.voc, shoot.voc, super.voc: Initial import.
470
471 2006-06-23 21:36 upi
472
473 * bin/upipang.pl: Initial import
474
475 2006-06-23 21:36 upi
476
477 * bin/upipang.pl: Initial revision
478
0 Installation instuctions for Pang Zero
1 ======================================
2
3 1. Download and unpack the archive
4
5 2. Go to the unpacked directory
6
7 3. ./configure
8
9 4. make install
10
0 #!include_default
1 # Avoid configuration metadata file
2 ^MYMETA\.
3
4 # Avoid Module::Build generated and utility files.
5 \bBuild$
6 \bBuild.bat$
7 \b_build
8 \bBuild.COM$
9 \bBUILD.COM$
10 \bbuild.com$
11 ^MANIFEST\.SKIP
12
13 # Avoid archives of this distribution
14 \bGames-PangZero-[\d\.\_]+
0 Please visit our website at
1 http://apocalypse.rulez.org/pangzero
2
3 To see what's new at the website, go to
4 http://apocalypse.rulez.org/pangzero/Recent_Changes
0 Pang Zero README
1 ================
2
3 Pang Zero is a clone of Super Pang, a fast-paced action game that involves
4 popping balloons with a harpoon. The intention of our effort is to create a
5 fun, open-source game that many (currently up to 6) people can play
6 together.
7
8 For more info, please visit our website at
9 http://apocalypse.rulez.org/pangzero
0 See pangzero.pl for current TODO list.
0 pangzero 1.3
0 #!/usr/bin/perl
1
2 use strict;
3 use warnings;
4
5 use lib 'lib';
6 use Games::PangZero;
7
8
9 eval {
10 Games::PangZero::Initialize();
11 #Games::PangZero::DoDemo() while 1;
12 #while (1) { Games::PangZero::DoRecordDemo(); $Games::PangZero::App->delay(2000); }
13 while (1) {
14 Games::PangZero::MainLoop();
15 }
16 };
17 if ($@) {
18 my $errorMessage = $@;
19 Games::PangZero::ShowErrorMessage($errorMessage);
20 die $errorMessage;
21 }
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
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 package Games::PangZero::Ball;
2 ##########################################################################
3
4 use Games::PangZero::Globals;
5
6 @ISA = qw(Games::PangZero::GameObject);
7 $Gravity = 0.05;
8 $MagicBallRect = SDL::Rect->new(80, 0, 16, 15);
9
10 for (my $i = 0; $i <= $#Games::PangZero::BallDesc; ++$i) {
11 my $desc = $Games::PangZero::BallDesc[$i];
12 $desc->{speedY} = 0 unless $desc->{speedY};
13 $desc->{bounceY} = $desc->{speedY} * $desc->{speedY} / $Games::PangZero::Ball::Gravity / 2 unless $desc->{bounceY};
14 }
15
16 sub Create {
17 my ($description, $x, $y, $dir) = @_;
18 my ($retval);
19
20 my $code = sprintf('$retval = Games::PangZero::%s->new(@_);', $description->{class});
21 eval $code; die $@ if $@;
22 return $retval;
23 }
24
25 sub Spawn {
26 my ($description, $x, $dir, $hasBonus) = @_;
27 my ($retval);
28
29 $x = $Games::PangZero::Game->Rand( $Games::PangZero::ScreenWidth - $description->{width} ) if $x < 0;
30 $retval = Games::PangZero::Ball::Create( $description, $x, -$description->{height} - $Games::PangZero::ScreenMargin, $dir );
31 $retval->GiveMagic() if $retval->{w} > 32;
32 $retval->GiveBonus() if $hasBonus;
33
34 $retval->{spawning} = 1;
35 my $surfaceName = 'dark' . $description->{surface};
36 $retval->{surface} = $Games::PangZero::BallSurfaces{$surfaceName};
37 die "No surface: $surfaceName" unless $retval->{surface};
38 return $retval;
39 }
40
41 sub new {
42 my ($class, $description, $x, $y, $dir) = @_;
43 my $self = Games::PangZero::GameObject->new();
44 %{$self} = ( %{$self},
45 'x' => $x,
46 'y' => $y,
47 'w' => $description->{width},
48 'h' => $description->{height},
49 'surface' => $Games::PangZero::BallSurfaces{$description->{surface}},
50 'hexa' => $description->{hexa} ? 1 : 0,
51 'desc' => $description,
52 'hasmagic' => 0, # true if one of the ball's descendants is magic
53 'ismagic' => 0, # true if the ball IS magic
54 'spawning' => 0,
55 );
56 $self->{speedX} = $dir > 0 ? 1.3 : -1.3;
57 $self->SetupCollisions();
58 bless $self, $class;
59 }
60
61 sub NormalAdvance {
62 my $self = shift;
63
64 $self->{speedY} += $Games::PangZero::Ball::Gravity * $Games::PangZero::GameSpeed unless ($self->{hexa});
65 $self->{x} += $self->{speedX} * $Games::PangZero::GameSpeed;
66 $self->{y} += $self->{speedY} * $Games::PangZero::GameSpeed;
67 if ($self->{y} > $Games::PangZero::ScreenHeight - $self->{h}) {
68 $self->{y} = $Games::PangZero::ScreenHeight - $self->{h};
69 if ($self->{hexa}) {
70 $self->{speedY} = -abs($self->{speedY});
71 } else {
72 $self->{speedY} = -$self->{desc}->{speedY};
73 }
74 $self->Bounce;
75 }
76 if ($self->{y} < 0) {
77 $self->{y} = 0;
78 $self->{speedY} = abs($self->{speedY});
79 }
80 if ($self->{x} < 0) {
81 $self->{x} = 0;
82 $self->{speedX} = abs( $self->{speedX} );
83 }
84 if ($self->{x} > $Games::PangZero::ScreenWidth - $self->{w}) {
85 $self->{x} = $Games::PangZero::ScreenWidth - $self->{w};
86 $self->{speedX} = -abs( $self->{speedX} );
87 }
88 }
89
90 sub SpawningAdvance {
91 my $self = shift;
92
93 $self->{y} += 0.32;
94 if ($self->{y} >= 0) {
95 $self->{spawning} = 0;
96 $self->{surface} = $Games::PangZero::BallSurfaces{$self->{desc}->{surface}},
97 }
98 }
99
100 sub Advance {
101 my $self = shift;
102
103 unless( $Games::PangZero::GamePause > 0 ) {
104 if ($self->{spawning}) {
105 $self->SpawningAdvance();
106 } else {
107 $self->NormalAdvance();
108 }
109 }
110
111 $self->CheckCollisions() unless $Games::PangZero::Game->{nocollision} or $self->{spawning};
112 }
113
114 sub Bounce {
115 }
116
117 sub CheckCollisions {
118 my $self = shift;
119
120 foreach my $harpoon (values %Games::PangZero::Harpoon::Harpoons) {
121 if ($self->Collisions($harpoon)) {
122 $self->Pop($harpoon->{guy}, $harpoon->{popEffect});
123 $harpoon->Delete();
124 return;
125 }
126 }
127 foreach my $guy (values %Games::PangZero::Guy::Guys) {
128 if ($Games::PangZero::GamePause <= 0 and $self->Collisions($guy)) {
129 $guy->Kill();
130 }
131 }
132 }
133
134 sub Draw {
135 my ($self) = @_;
136
137 return if $Games::PangZero::GamePause > 0 and $Games::PangZero::GamePause < 100 and (int($Games::PangZero::GamePause / 3) % 4) < 2;
138
139 $self->TransferRect();
140 if ($self->{ismagic} and int($Games::PangZero::Game->{anim}/4) % 2) {
141 SDL::Video::blit_surface($Games::PangZero::BallSurfaces{ball4}, $Games::PangZero::Ball::MagicBallRect, $Games::PangZero::App, $self->{rect} );
142 } else {
143 SDL::Video::blit_surface($self->{surface}, $self->{desc}->{rect}, $Games::PangZero::App, $self->{rect} );
144 }
145 }
146
147 sub Collisions {
148 my ($self, $other) = @_;
149
150 # Bounding box detection
151
152 return unless $self->SUPER::Collisions($other);
153
154 # Circle vs rectangle collision
155
156 my ($centerX, $centerY, $boxAxisX, $boxAxisY, $boxCenterX, $boxCenterY, $distSquare, $distance);
157 $boxAxisX = ($other->{collisionw} or $other->{w}) / 2;
158 $boxAxisY = ($other->{collisionh} or $other->{h}) / 2;
159 $boxCenterX = $other->{x} + $other->{w} / 2;
160 $boxCenterY = $other->{y} + $other->{h} / 2;
161 $centerX = $self->{x} + $self->{w} / 2;
162 $centerY = $self->{y} + $self->{h} / 2;
163
164 # Translate coordinates to the box center
165 $centerX -= $boxCenterX;
166 $centerY -= $boxCenterY;
167 $centerX = abs($centerX);
168 $centerY = abs($centerY);
169
170 if ($centerX < $boxAxisX) {
171 return 1 if $centerY < $boxAxisY + $self->{h} / 2;
172 return 0;
173 }
174 if ($centerY < $boxAxisY) {
175 return 2 if $centerX < $boxAxisX + $self->{w} / 2;
176 return 0;
177 }
178 $distSquare = ($centerX-$boxAxisX) * ($centerX-$boxAxisX);
179 $distSquare += ($centerY-$boxAxisY) * ($centerY-$boxAxisY);
180 return 3 if $distSquare < $self->{h} * $self->{h} / 4;
181
182 return 0;
183 }
184
185 sub Pop {
186 my ($self, $guy, $popEffect) = @_;
187
188 Carp::confess "no $popEffect" unless defined $popEffect;
189 $Games::PangZero::GameEvents{'pop'} = 1;
190 $Games::PangZero::GameEvents{'magic'} = 1 if ($self->{ismagic});
191 $guy->GiveScore($self->{desc}->{score}) if $guy;
192 $self->Delete();
193
194 goto skipChildren if ($popEffect eq 'meltdown');
195
196 if ($self->{desc}->{nextgen}) {
197 die caller unless $self->{desc}->{nextgen}->{class};
198 my @children = $self->SpawnChildren();
199 if (scalar @children) {
200 $self->AdjustChildren(@children);
201 if ($popEffect eq 'HalfCutter') {
202 push @Games::PangZero::GameObjects, ($self->{speedX} > 0 ? $children[1] : $children[0]);
203 } else {
204 push @Games::PangZero::GameObjects, (@children);
205 }
206 }
207 }
208 if ($self->{bonus} and $popEffect ne 'superkill') {
209 push @Games::PangZero::GameObjects, Games::PangZero::BonusDrop->new($self);
210 }
211 $Games::PangZero::Game->OnBallPopped();
212
213 skipChildren:
214 push @Games::PangZero::GameObjects, Games::PangZero::Pop->new($self->{x}, $self->{y}, $self->{desc}->{popIndex}, $self->{surface});
215 }
216
217 sub SpawnChildren {
218 my $self = shift;
219 my $nextgen = $self->{desc}->{nextgen};
220 die caller unless $nextgen->{class};
221 my $x = $self->{x} + $self->{w} / 2;
222 my $y = $self->{y} + ( $self->{h} - $nextgen->{height} ) / 2;
223 my $child1 = Create($nextgen, $self->{x}, $y, 0);
224 my $child2 = Create($nextgen, $self->{x} + $self->{w} - $nextgen->{width}, $y, 1);
225
226 return ($child1, $child2);
227 }
228
229 sub AdjustChildren {
230 my ($self, @children) = @_;
231 my ($nextgen, $speedY, $altitude);
232
233 if ($self->{hasmagic}) {
234 $children[0]->GiveMagic();
235 }
236
237 $nextgen = $self->{desc}->{nextgen};
238 $altitude = $Games::PangZero::ScreenHeight - $self->{y} - $self->{h};
239 $speedY = 1.8;
240 unless ($altitude > $nextgen->{bounceY}) {
241 $speedY = 1.8;
242 while ($speedY * $speedY / $Games::PangZero::Ball::Gravity / 2 + $altitude < $nextgen->{bounceY}) {
243 ++$speedY;
244 }
245 }
246 foreach (@children) {
247 $_->{speedY} = -$speedY;
248 }
249 }
250
251 sub GiveMagic {
252 my $self = shift;
253
254 $self->{hasmagic} = 1;
255 $self->{ismagic} = 1 unless $self->{desc}->{nextgen};
256 }
257
258 sub GiveBonus {
259 my $self = shift;
260
261 $self->{bonus} = 1;
262 }
263
264 1;
0 package Games::PangZero::BonusDrop;
1
2 @ISA = qw(Games::PangZero::GameObject);
3 use strict;
4 use warnings;
5
6 use vars qw(@BonusDesc);
7
8 @BonusDesc = (
9 # MachineGun is disabled for now as it is broken (seems like invisible bullets keep flying around)
10 # { 'weaponClass' => 'MachineGun', 'bonusDelay' => 1500, 'srcRect' => SDL::Rect->new( 0, 64, 32, 32), },
11 { 'weaponClass' => 'HalfCutter', 'bonusDelay' => 1000, 'srcRect' => SDL::Rect->new(32, 64, 32, 32), },
12 { 'weaponClass' => 'PowerWire', 'bonusDelay' => 3000, 'srcRect' => SDL::Rect->new(32, 96, 32, 32), },
13 { 'onCollectedSub' => \&OnCollectedSlowEffect, 'srcRect' => SDL::Rect->new(32, 0, 32, 32), },
14 );
15
16
17 sub new {
18 my ($class, $ball) = @_;
19 my ($self);
20
21 $self = Games::PangZero::GameObject->new();
22
23 %{$self} = ( %{$self},
24 'x' => $ball->{x} + ($ball->{w} - 32) / 2,
25 'y' => $ball->{y} + ($ball->{h} - 32) / 2,
26 'w' => 32,
27 'h' => 32,
28 'speedY' => -3,
29 'speedX' => 0,
30 'bottomDelay' => 500,
31 'desc' => $BonusDesc[int $Games::PangZero::Game->Rand(scalar @BonusDesc)],
32 );
33 bless $self, $class;
34 }
35
36 sub Advance {
37 my $self = shift;
38
39 if ($self->{y} >= $Games::PangZero::ScreenHeight - $self->{h}) {
40 $self->{y} = $Games::PangZero::ScreenHeight - $self->{h};
41 if (--$self->{bottomDelay} < 0) {
42 $self->Delete();
43 }
44 } else {
45 $self->{speedY} += 0.1;
46 $self->{y} += $self->{speedY};
47 }
48
49 $self->CheckCollisions() if $self->{speedY} >= 0;
50 }
51
52 sub CheckCollisions {
53 my $self = shift;
54 my ($guy, @guysTouched);
55
56 foreach $guy (@Games::PangZero::GameObjects) {
57 next unless ref($guy) eq 'Games::PangZero::Guy';
58 next unless $self->Collisions($guy);
59 push @guysTouched, ($guy);
60 }
61 return unless @guysTouched;
62 $self->Collected($guysTouched[$Games::PangZero::Game->Rand( scalar @guysTouched )]);
63 }
64
65 sub SetOnCollectedSub {
66 my ($self, $onCollectedSub) = @_;
67 $self->{onCollectedSub} = $onCollectedSub;
68 }
69
70 sub Collected {
71 my ($self, $guy) = @_;
72
73 if ($self->{onCollectedSub}) {
74 $self->{onCollectedSub}->($self, $guy);
75 } elsif ($self->{desc}->{onCollectedSub}) {
76 $self->{desc}->{onCollectedSub}->($self, $guy);
77 } else {
78 $guy->{weapon} = $self->{desc}->{weaponClass};
79 $guy->{bonusDelay} = $self->{desc}->{bonusDelay} * $Games::PangZero::WeaponDuration->{durationmultiplier};
80 }
81 $self->Delete();
82 }
83
84 sub Draw {
85 my $self = shift;
86
87 return if $self->{bottomDelay} < 100 and (($Games::PangZero::Game->{anim} / 4) % 2 < 1);
88 $self->TransferRect();
89 SDL::Video::blit_surface($Games::PangZero::BonusSurface, $self->{desc}->{srcRect}, $Games::PangZero::App, $self->{rect});
90 }
91
92 sub OnCollectedSlowEffect {
93 my ($self, $guy) = @_;
94
95 Games::PangZero::SlowEffect::RemoveSlowEffects();
96 push @Games::PangZero::GameObjects, Games::PangZero::SlowEffect->new();
97 }
98
99 1;
0 ##########################################################################
1 package Games::PangZero::ChallengeGame;
2 ##########################################################################
3
4 @ISA = qw(Games::PangZero::PlayableGameBase);
5 use strict;
6 use warnings;
7
8 sub new {
9 my ($class) = @_;
10 my $self = Games::PangZero::PlayableGameBase->new();
11 %{$self} = (%{$self},
12 'challenge' => undef,
13 );
14 bless $self, $class;
15 }
16
17 sub SetGameLevel {
18 my ($self, $level) = @_;
19
20 Games::PangZero::SlowEffect::RemoveSlowEffects();
21 $self->SUPER::SetGameLevel($level);
22 $level = $#Games::PangZero::ChallengeLevels if $level > $#Games::PangZero::ChallengeLevels;
23 $self->{challenge} = $Games::PangZero::ChallengeLevels[$level];
24 die unless $self->{challenge};
25 $self->SpawnChallenge();
26 }
27
28 sub AdvanceGame {
29 my ($self) = @_;
30
31 if ($self->{nextlevel}) {
32 Games::PangZero::Music::PlaySound('level');
33 $self->SetGameLevel($self->{level} + 1);
34 delete $self->{nextlevel};
35 }
36 if ($self->{playerspawned}) {
37 $self->SpawnChallenge();
38 $self->{playerspawned} = 0;
39 }
40 $self->SUPER::AdvanceGame();
41 }
42
43 sub SpawnChallenge {
44 my $self = shift;
45 my ($challenge, @guys, $balldesc, $ball, $hasBonus, %balls, $numBalls, $ballsSpawned, @ballKeys, $x);
46
47 @guys = $self->PopEveryBall();
48 foreach (@guys) {
49 $_->{bonusDelay} = 1;
50 $_->{invincible} = 1;
51 }
52 $Games::PangZero::GamePause = 0;
53 delete $Games::PangZero::GameEvents{magic};
54 $challenge = $self->{challenge};
55 die unless $challenge;
56
57 while ($challenge =~ /(\w+)/g) {
58 $balldesc = $Games::PangZero::BallDesc{$1};
59 warn "Unknown ball in challenge: $1" unless $balldesc;
60 $balls{$1}++;
61 $numBalls++;
62 }
63 $ballsSpawned = 0;
64 while ($ballsSpawned < $numBalls) {
65 foreach (keys %balls) {
66 next unless $balls{$_};
67 --$balls{$_};
68 $balldesc = $Games::PangZero::BallDesc{$_};
69 $x = $Games::PangZero::ScreenWidth * ($ballsSpawned * 2 + 1) / ($numBalls * 2) - $balldesc->{width} / 2;
70 $x = $Games::PangZero::ScreenWidth - $balldesc->{width} if $x > $Games::PangZero::ScreenWidth - $balldesc->{width};
71 $hasBonus = (($balldesc->{width} >= 32) and ($self->Rand(1) < $Games::PangZero::DifficultyLevel->{bonusprobability}));
72 $ball = &Games::PangZero::Ball::Spawn($balldesc, $x, ($ballsSpawned % 2) ? 0 : 1, $hasBonus);
73 if ($ball->{w} <= 32) {
74 $ball->{ismagic} = $ball->{hasmagic} = 0;
75 }
76 push @Games::PangZero::GameObjects, ($ball) ;
77 ++$ballsSpawned;
78 }
79 }
80 }
81
82 sub OnBallPopped {
83 my $self = shift;
84 my ($i);
85
86 for ($i = $#Games::PangZero::GameObjects; $i >= 0; --$i) {
87 if ($Games::PangZero::GameObjects[$i]->isa('Games::PangZero::Ball')) {
88 return;
89 }
90 }
91 $self->{nextlevel} = 1;
92 }
93
94 1;
0 ##########################################################################
1 # CONFIG SAVE/LOAD
2 ##########################################################################
3
4 package Games::PangZero::Config;
5
6 use File::ShareDir qw(dist_dir);
7
8 sub IsMicrosoftWindows {
9 return $^O eq 'MSWin32';
10 }
11
12
13 sub TestDataDir {
14 return -f "$Games::PangZero::DataDir/glossyfont.png"; # Should be a file from the latest version.
15 }
16
17 sub FindDataDir {
18 return if $Games::PangZero::DataDir and TestDataDir();
19 my @guesses = ('.', dist_dir('Games-PangZero'));
20 foreach my $guess (@guesses) {
21 $Games::PangZero::DataDir = $guess;
22 return if TestDataDir();
23 $Games::PangZero::DataDir = "$guess/data";
24 return if TestDataDir();
25 }
26 die "Couldn't find the data directory. Please set it manually.";
27 }
28
29 sub GetConfigFilename {
30 if ( IsMicrosoftWindows() ) {
31 if ($ENV{USERPROFILE}) {
32 return "$ENV{USERPROFILE}\\pangzero.cfg";
33 }
34 return "$Games::PangZero::DataDir/pangzero.cfg";
35 }
36 if ($ENV{HOME}) {
37 return "$ENV{HOME}/.pangzerorc";
38 }
39 if (-w $Games::PangZero::DataDir) {
40 return "$Games::PangZero::DataDir/pangzero.cfg";
41 }
42 return "/tmp/pangzero.cfg";
43 }
44
45 sub GetConfigVars {
46 my ($i, $j);
47 my @result = qw(
48 Games::PangZero::NumGuys
49 Games::PangZero::DifficultyLevelIndex
50 Games::PangZero::WeaponDurationIndex
51 Games::PangZero::Slippery
52 Games::PangZero::MusicEnabled
53 Games::PangZero::SoundEnabled
54 Games::PangZero::FullScreen
55 Games::PangZero::ShowWebsite
56 Games::PangZero::DeathBallsEnabled
57 Games::PangZero::EarthquakeBallsEnabled
58 Games::PangZero::WaterBallsEnabled
59 Games::PangZero::SeekerBallsEnabled
60 );
61 for ($i=0; $i < scalar @Games::PangZero::Players; ++$i) {
62 for ($j=0; $j < 3; ++$j) {
63 push @result, ("Games::PangZero::Players[$i]->{keys}->[$j]");
64 }
65 push @result, ("Games::PangZero::Players[$i]->{colorindex}");
66 push @result, ("Games::PangZero::Players[$i]->{imagefileindex}");
67 }
68 my ($difficulty, $gameMode);
69 for ($difficulty=0; $difficulty < scalar @Games::PangZero::DifficultyLevels; ++$difficulty) {
70 foreach $gameMode ('highScoreTablePan', 'highLevelTablePan', 'highScoreTableCha', 'highLevelTableCha') {
71 next if ($Games::PangZero::DifficultyLevels[$difficulty]->{name} eq 'Miki' and $gameMode eq 'highScoreTableCha');
72 for ($i=0; $i < 5; ++$i) {
73 push @result, "Games::PangZero::DifficultyLevels[$difficulty]->{$gameMode}->[$i]->[0]", # Name of high score
74 "Games::PangZero::DifficultyLevels[$difficulty]->{$gameMode}->[$i]->[1]", # High score
75 }
76 }
77 }
78 return @result;
79 }
80
81 sub SaveConfig {
82 my ($filename, $varname, $value);
83 $filename = GetConfigFilename();
84
85 open CONFIG, "> $filename" or return;
86 foreach $varname (GetConfigVars()) {
87 eval("\$value = \$$varname"); die $@ if $@;
88 print CONFIG "\$$varname = $value\n";
89 }
90 close CONFIG;
91 }
92
93 sub LoadConfig {
94 my ($filename, $text, $varname);
95
96 $text = '';
97 $filename = GetConfigFilename();
98 if (open CONFIG, "$filename") {
99 read CONFIG, $text, 16384;
100 close CONFIG;
101 }
102
103 foreach $varname (GetConfigVars()) {
104 my $pattern = $varname;
105 $pattern =~ s/\[/\\[/g;
106 if ($text =~ /$pattern = (.+?)$/m) {
107 $val = $1;
108 if ($varname eq Games::PangZero::ShowWebsite) {
109 eval( "\$$varname = '$val'" );
110 }
111 elsif($val =~ /^SDLK_\w+$/) {
112 eval( "\$$varname = SDL::Events::$val()" );
113 }
114 elsif($val =~ /^[\d\.]+$/) {
115 eval( "\$$varname = $val" );
116 }
117 else {
118 eval( "\$$varname = '$val'" );
119 }
120 }
121 }
122
123 SetDifficultyLevel($Games::PangZero::DifficultyLevelIndex);
124 SetWeaponDuration($Games::PangZero::WeaponDurationIndex);
125 }
126
127 sub SetDifficultyLevel {
128 my $difficultyLevelIndex = shift;
129 if ($difficultyLevelIndex < 0 or $difficultyLevelIndex > $#Games::PangZero::DifficultyLevels) {
130 $difficultyLevelIndex = $Games::PangZero::DifficultyLevelIndex;
131 }
132 $Games::PangZero::DifficultyLevelIndex = $difficultyLevelIndex;
133 $Games::PangZero::DifficultyLevel = $Games::PangZero::DifficultyLevels[$difficultyLevelIndex];
134 }
135
136 sub SetWeaponDuration {
137 my $weaponDurationIndex = shift;
138 if ($weaponDurationIndex < 0 or $weaponDurationIndex > $#Games::PangZero::WeaponDurations) {
139 $weaponDurationIndex = $Games::PangZero::WeaponDurationIndex;
140 }
141 $Games::PangZero::WeaponDurationIndex = $weaponDurationIndex;
142 $Games::PangZero::WeaponDuration = $Games::PangZero::WeaponDurations[$Games::PangZero::WeaponDurationIndex];
143 }
144
145 1;
0 ##########################################################################
1 package Games::PangZero::DeadGuy;
2 ##########################################################################
3
4 @ISA = qw(Games::PangZero::GameObject);
5 use strict;
6 use warnings;
7
8 sub new {
9 my ($class, $guy, $dir) = @_;
10 my ($self, $player);
11
12 $self = Games::PangZero::GameObject->new();
13 $player = $guy->{player};
14
15 %{$self} = ( %{$self},
16 'x' => $guy->{x},
17 'y' => $guy->{y},
18 'w' => 64,
19 'h' => 64,
20 'speedY' => -7,
21 'surface' => $player->{guySurface},
22 'anim' => 0,
23 'bounce' => 0,
24 'bouncex' => 0,
25 );
26 $self->{'speedX'} = ($Games::PangZero::Game->Rand(2) + 1.5) * (($self->{x} > $Games::PangZero::ScreenWidth / 2) ? 1 : -1);
27 bless $self, $class;
28 }
29
30 sub Advance {
31 my $self = shift;
32
33 $self->{speedY} += 0.1;
34 $self->{x} += $self->{speedX};
35 $self->{y} += $self->{speedY};
36
37 unless ($self->{bouncex}) {
38 if ($self->{x} < -16) {
39 $self->{x} = -16;
40 $self->{speedX} = abs( $self->{speedX} );
41 $self->{speedY} = -3 if $self->{speedY} > -3;
42 $self->{bouncex} = 1;
43 }
44 if ($self->{x} > $Games::PangZero::ScreenWidth - $self->{w} +16) {
45 $self->{x} = $Games::PangZero::ScreenWidth - $self->{w} + 16;
46 $self->{speedX} = -abs( $self->{speedX} );
47 $self->{speedY} = -3 if $self->{speedY} > -3;
48 $self->{bouncex} = 1;
49 }
50 }
51 if ($self->{y} > $Games::PangZero::ScreenHeight - 64 and not $self->{bounce}) {
52 $self->{bounce} = 1;
53 $self->{speedY} = -3;
54 }
55
56 if ($self->{y} > $Games::PangZero::PhysicalScreenHeight) {
57 $self->Delete;
58 }
59 $self->{anim} += $self->{speedX} > 0 ? -1 : +1;
60 }
61
62 sub Draw {
63 my $self = shift;
64 my ($srcrect);
65
66 $srcrect = SDL::Rect->new(($self->{speedX} > 0 ? 0 : 64), 128, 64, 64 );
67 $self->TransferRect();
68 if(SDL::Config->has('SDL_gfx_rotozoom')) {
69 my $roto = SDL::Surface->new( SDL::Video::SDL_SWSURFACE(), 64, 64, 32, 0xFF000000, 0xFF0000, 0xFF00, 0xFF);
70 SDL::Video::blit_surface($self->{surface}, $srcrect, $roto, SDL::Rect->new(0, 0, $roto->w, $roto->h) );
71 $roto = SDL::GFX::Rotozoom::surface($roto, $self->{anim} * 5, 1, SDL::GFX::Rotozoom::SMOOTHING_OFF());
72 $self->{rect}->x( $self->{rect}->x - ($roto->w - 64) / 2 );
73 $self->{rect}->y( $self->{rect}->y - ($roto->h - 64) / 2 );
74 SDL::Video::blit_surface($roto, SDL::Rect->new(0, 0, 64, 64), $Games::PangZero::App, $self->{rect} );
75 return;
76 }
77 else {
78 SDL::Video::blit_surface($self->{surface}, $srcrect, $Games::PangZero::App, $self->{rect} );
79 }
80 }
81
82 1;
0 ##########################################################################
1 package Games::PangZero::DeathBall;
2 ##########################################################################
3
4 @ISA = qw(Games::PangZero::Ball);
5 use strict;
6 use warnings;
7
8 sub new {
9 my $class = shift;
10 my $self = Games::PangZero::Ball->new(@_);
11 $self->{expires} = 2000; # 20sec
12 $self->{speedX} *= 0.9;
13 bless $self, $class;
14 }
15
16 sub NormalAdvance {
17 my $self = shift;
18
19 $self->SUPER::NormalAdvance();
20 if (--$self->{expires} < 0) {
21 $self->{bonus} = 1 if $self->{hasmagic};
22 $self->Pop(undef, 'expire');
23 }
24
25 }
26
27 sub Pop {
28 my ($self, $guy, $popEffect) = @_;
29
30 $self->{dontspawn} = 1 if $popEffect eq 'expire' or $popEffect eq 'superkill';
31 $self->SUPER::Pop($guy, $popEffect);
32 if (CountDeathBalls() > 30) {
33 $Games::PangZero::GameEvents{'meltdown'} = 1;
34 }
35 }
36
37 sub SpawnChildren {
38 my $self = shift;
39
40 return if $self->{dontspawn};
41 $self->SUPER::SpawnChildren(@_);
42 }
43
44 sub CountDeathBalls {
45 my $count = 0;
46
47 foreach my $ball (@Games::PangZero::GameObjects) {
48 if (ref($ball) eq 'Games::PangZero::DeathBall') { ++$count; }
49 }
50 return $count;
51 }
52
53 1;
0 ##########################################################################
1 package Games::PangZero::DemoGame;
2 ##########################################################################
3
4 @ISA = qw(Games::PangZero::PanicGame);
5
6 sub ResetGame {
7 my $self = shift;
8 Games::PangZero::Config::SetDifficultyLevel(1);
9 Games::PangZero::Config::SetWeaponDuration(0);
10 $Games::PangZero::Slippery = 0;
11 $self->SUPER::ResetGame();
12
13 my $ball = Games::PangZero::Ball::Create($Games::PangZero::BallDesc[4], 400, 0, -10, 0);
14 $ball->GiveMagic();
15
16 push @Games::PangZero::GameObjects, (
17 Games::PangZero::Ball::Create($Games::PangZero::BallDesc[0], 100, 0, 1),
18 Games::PangZero::Ball::Create($Games::PangZero::BallDesc{super0}, 300, 0, 0),
19 Games::PangZero::Ball::Create($Games::PangZero::BallDesc{super1}, 500, 0, 1),
20 $ball,
21 );
22 $Games::PangZero::GamePause = 0;
23 $Games::PangZero::GameSpeed = 0.8;
24 $self->{spawndelay} = $self->{superballdelay} = 1000000;
25 $self->{ballcounter} = 0;
26 $self->{balls} = [ qw(b0 h0 w1 quake death seeker) ];
27 }
28
29 sub SetGameSpeed {
30 $Games::PangZero::GameSpeed = 0.8;
31 }
32
33 sub SpawnBalls {
34 my $self = shift;
35
36 return if (--$self->{spawndelay} > 0);
37 my $ballName = $self->{balls}->[$self->{ballcounter}];
38 return unless $ballName;
39 push @Games::PangZero::GameObjects, ( Games::PangZero::Ball::Spawn($Games::PangZero::BallDesc{$ballName}, 100, 1, 0) );
40 $self->{spawndelay} = 1000000;
41 ++$self->{ballcounter};
42 }
43
44 sub RespawnPlayers {}
45 sub OnBallPopped {}
46
47 1;
0 ##########################################################################
1 package Games::PangZero::DemoPlaybackGame;
2 ##########################################################################
3
4 @ISA = qw(Games::PangZero::DemoGame Games::PangZero::PlaybackGame);
5 use strict;
6 use warnings;
7
8 sub new {
9 my $class = shift;
10 my $self = Games::PangZero::PlaybackGame->new(@_);
11 bless $self, $class;
12 }
13
14 sub DrawScoreBoard {
15 my $self = shift;
16 my $x = 10;
17 my $y = $Games::PangZero::ScreenHeight + 2 * $Games::PangZero::ScreenMargin + 5;
18 if ($self->{anim} < 1) {
19 SDLx::SFont::print_text( $Games::PangZero::Background, $x, $y, "Press F to fast forward" );
20 SDLx::SFont::print_text( $Games::PangZero::App, $x, $y, "Press F to fast forward" );
21 } return;
22 SDL::Video::fill_rect($Games::PangZero::App, SDL::Rect->new(0, $y, $Games::PangZero::PhysicalScreenWidth, $Games::PangZero::PhysicalScreenHeight - $y), SDL::Video::map_RGB($Games::PangZero::App->format(), 0, 0, 0));
23 SDLx::SFont::print_text( $Games::PangZero::App, $x, $y, $self->{recordpointer} );
24
25 }
0 ##########################################################################
1 package Games::PangZero::DemoRecordGame;
2 ##########################################################################
3
4 @ISA = qw(Games::PangZero::DemoGame Games::PangZero::RecordGame);
5 use strict;
6 use warnings;
7
8 sub new {
9 my $class = shift;
10 my $self = Games::PangZero::RecordGame->new(@_);
11 bless $self, $class;
12 }
13
14 1;
0 ##########################################################################
1 package Games::PangZero::EarthquakeBall;
2 ##########################################################################
3
4 @ISA = qw(Games::PangZero::Ball);
5 use strict;
6 use warnings;
7
8 sub new {
9 my $class = shift;
10 my $self = Games::PangZero::Ball->new(@_);
11 bless $self, $class;
12 }
13
14 sub CountEarthquakeBalls {
15 my $count = 0;
16
17 foreach my $ball (@Games::PangZero::GameObjects) {
18 if (ref($ball) eq 'Games::PangZero::EarthquakeBall') { ++$count; }
19 }
20 return $count;
21 }
22
23 sub Bounce {
24 my $self = shift;
25
26 unless ($Games::PangZero::GameEvents{earthquake} and $Games::PangZero::GameEvents{earthquake} > $self->{desc}->{quake}) {
27 $Games::PangZero::GameEvents{earthquake} = [$self->{desc}->{quake}, $self->{x}];
28 }
29 }
30
31 1;
0 ##########################################################################
1 package Games::PangZero::FpsIndicator;
2 ##########################################################################
3
4 @ISA = qw(Games::PangZero::GameObject);
5
6 sub new {
7 my ($class) = @_;
8 my $self = Games::PangZero::GameObject->new();
9 my $width = Games::PangZero::Graphics::TextWidth("999");
10 %{$self} = ( %{$self},
11 'x' => $Games::PangZero::ScreenWidth - $width + $Games::PangZero::ScreenMargin,
12 'y' => -$Games::PangZero::ScreenMargin,
13 'w' => $width,
14 'h' => 32,
15 );
16
17 $self->TransferRect();
18 bless $self, $class;
19 }
20
21 sub Draw {
22 my $self = shift;
23
24 SDLx::SFont::print_text( $Games::PangZero::App, $self->{rect}->x, $self->{rect}->y, Games::PangZero::GameTimer::GetFramesPerSecond() );
25
26 }
27
28 1;
0 ##########################################################################
1 package Games::PangZero::FragileBall;
2 ##########################################################################
3
4 @ISA = qw( Games::PangZero::Ball );
5 use strict;
6 use warnings;
7
8 sub Bounce {
9 my $self = shift;
10 if ($self->{desc}->{nextgen}) {
11 $self->{bonus} = 0;
12 $self->Pop(undef, '');
13 }
14 $self->{speedX} = ($self->{speedX} > 0) ? 1.3 : -1.3;
15 }
16
17 sub SpawnChildren {
18 my $self = shift;
19 my (@children, $child, $i);
20
21 my $nextgen = $self->{desc}->{nextgen};
22 die caller unless $nextgen->{class};
23 my $numchildren = 2;
24 while ($nextgen->{nextgen}) {
25 $nextgen = $nextgen->{nextgen};
26 $numchildren *= 2;
27 }
28
29 my $y = $self->{y} + ($self->{h} - $nextgen->{height}) / 2;
30 for ($i = 0; $i < $numchildren; ++$i) {
31 $child = Games::PangZero::Ball::Create($nextgen, $self->{x}, $y, 0);
32 $child->{speedX} = -1.5 + ($i / ($numchildren-1) * 3);
33 $child->{x} = $self->{x} + ($self->{w} - $child->{w}) * ($i / ($numchildren-1));
34 push @children, $child;
35 }
36
37 return @children;
38 }
39
40 1;
0 ##########################################################################
1 package Games::PangZero::GameBase;
2 ##########################################################################
3
4 use SDL;
5 use SDL::Video;
6
7 sub new {
8 my ($class) = @_;
9 my $self = {
10 abortgame => 0,
11 anim => 0,
12 nocollision => 0,
13 backgrounds => [ 'desert2.png', ],
14 };
15 $Games::PangZero::GameSpeed = 1.0;
16 $Games::PangZero::GamePause = 0;
17 bless $self, $class;
18 }
19
20 sub Exit {
21 Games::PangZero::ShowWebPage("http://apocalypse.rulez.org/pangzero/Thanks_For_Playing_Pang_Zero_$Games::PangZero::VERSION" ) if $Games::PangZero::ShowWebsite ne $Games::PangZero::VERSION;
22 exit;
23 }
24
25 sub Rand {
26 shift;
27 return rand($_[0]);
28 }
29
30 sub Delay {
31 my ($self, $ticks) = @_;
32
33 while ($ticks > 0) {
34 my $advance = $self->CalculateAdvances();
35 %Games::PangZero::Events = ();
36 Games::PangZero::HandleEvents();
37 return if $self->{abortgame};
38 $ticks -= $advance;
39 $self->DrawGame();
40 }
41 }
42
43 sub SetGameSpeed {
44 }
45
46 sub SetBackground {
47 my ($self, $backgroundIndex) = @_;
48
49 return if $backgroundIndex >= scalar( @{$self->{backgrounds}} );
50 Games::PangZero::Graphics::LoadBackground($self->{backgrounds}->[$backgroundIndex]);
51 SDL::Video::blit_surface($Games::PangZero::Background, SDL::Rect->new(0, 0, $Games::PangZero::App->w, $Games::PangZero::App->h), $Games::PangZero::App, SDL::Rect->new(0, 0, $Games::PangZero::App->w, $Games::PangZero::App->h));
52 }
53
54 sub ShowTooltip {
55 }
56
57 sub ResetGame {
58 my $self = shift;
59 @Games::PangZero::GameObjects = ();
60 %Games::PangZero::Guy::Guys = ();
61 %Games::PangZero::Harpoon::Harpoons = ();
62 $Games::PangZero::GamePause = 0;
63 %Games::PangZero::GameEvents = ();
64 $self->SetBackground(0);
65 }
66
67 sub CalculateAdvances {
68 my $advance = Games::PangZero::GameTimer::GetAdvances();
69 while ($advance <= 0) {
70 SDL::delay(3); # Wait 3ms = 0.3 game ticks
71 $advance = Games::PangZero::GameTimer::GetAdvances();
72 }
73 if ($advance > 5) {
74 # print STDERR "advance = $advance!\n";
75 $advance = 5;
76 }
77 return $advance;
78 }
79
80 sub AdvanceGameObjects {
81 my ($self) = @_;
82
83 ++$self->{anim};
84 foreach my $gameObject (@Games::PangZero::GameObjects) {
85 $gameObject->Advance();
86 }
87 }
88
89 sub OnBallPopped {
90 }
91
92 sub DrawGame {
93 my ($self) = @_;
94
95 my ($gameObject);
96 foreach $gameObject (@Games::PangZero::GameObjects) {
97 $gameObject->Clear();
98 }
99 $self->DrawScoreBoard();
100 foreach $gameObject (@Games::PangZero::GameObjects) {
101 $gameObject->Draw();
102 }
103 SDL::Video::flip($Games::PangZero::App);
104 }
105
106 sub DrawScoreBoard {
107 }
108
109 1;
0 ##########################################################################
1 package Games::PangZero::GameObject;
2 ##########################################################################
3
4 sub new {
5 my ($class) = @_;
6 my $self = {
7 'rect' => SDL::Rect->new( 0, 0, 0, 0 ),
8 'speedX' => 0,
9 'speedY' => 0,
10 'x' => 0,
11 'y' => 0,
12 'w' => 10,
13 'h' => 10,
14 };
15 bless $self, $class;
16 }
17
18 sub Delete {
19 my $self = shift;
20
21 for (my $i = 0; $i < scalar @Games::PangZero::GameObjects; ++$i) {
22 if ($Games::PangZero::GameObjects[$i] eq $self) {
23 splice @Games::PangZero::GameObjects, $i, 1;
24 last;
25 }
26 }
27 $self->{deleted} = 1;
28 $self->Clear();
29 }
30
31 sub Advance {
32 my $self = shift;
33
34 $self->{advance}->($self) if $self->{advance};
35 }
36
37 sub Clear {
38 my ($self) = @_;
39 SDL::Video::blit_surface($Games::PangZero::Background, $self->{rect}, $Games::PangZero::App, $self->{rect});
40 }
41
42 sub TransferRect {
43 my ($self) = @_;
44
45 $self->{rect}->x($self->{x} + $Games::PangZero::ScreenMargin);
46 $self->{rect}->y($self->{y} + $Games::PangZero::ScreenMargin);
47 $self->{rect}->w($self->{w});
48 $self->{rect}->h($self->{h});
49 }
50
51 sub Draw {
52 my ($self) = @_;
53
54 $self->TransferRect();
55 if ($self->{draw}) {
56 $self->{draw}->($self);
57 } else {
58 SDL::Video::fill_rect($Games::PangZero::App, $self->{rect}, SDL::Video::map_RGB($Games::PangZero::App->format(), 0x80, 0, 0));
59 }
60 }
61
62 sub SetupCollisions {
63 my ($self) = @_;
64
65 $self->{collisionw} = ($self->{collisionw} or $self->{w});
66 $self->{collisionh} = ($self->{collisionh} or $self->{h});
67 $self->{collisionmarginw1} = ( $self->{w} - $self->{collisionw} ) / 2;
68 $self->{collisionmarginw2} = $self->{collisionmarginw1} + $self->{collisionw};
69 $self->{collisionmarginh1} = ( $self->{h} - $self->{collisionh} ) / 2;
70 $self->{collisionmarginh2} = $self->{collisionmarginh1} + $self->{collisionh};
71 $self->{centerx} = $self->{w} / 2;
72 $self->{centery} = $self->{y} / 2;
73 }
74
75 sub Collisions {
76 my ($self, $other) = @_;
77
78 # Bounding box detection
79
80 unless ($self->{collisionmarginw1} and $other->{collisionmarginw1}) {
81 return 0 if $self->{x} >= $other->{x} + $other->{w};
82 return 0 if $other->{x} >= $self->{x} + $self->{w};
83 return 0 if $self->{y} >= $other->{y} + $other->{h};
84 return 0 if $other->{y} >= $self->{y} + $self->{h};
85 return 1;
86 }
87
88 return 0 if $self->{x} + $self->{collisionmarginw1} >= $other->{x} + $other->{collisionmarginw2};
89 return 0 if $other->{x} + $other->{collisionmarginw1} >= $self->{x} + $self->{collisionmarginw2};
90 return 0 if $self->{y} + $self->{collisionmarginh1} >= $other->{y} + $other->{collisionmarginh2};
91 return 0 if $other->{y} + $other->{collisionmarginh1} >= $self->{y} + $self->{collisionmarginh2};
92 return 1;
93 }
94
95 1;
0 ##########################################################################
1 package Games::PangZero::GamePause;
2 ##########################################################################
3
4 @ISA = qw(Games::PangZero::GameObject);
5
6 sub Show {
7 foreach my $gameObject (@Games::PangZero::GameObjects) {
8 return if (ref $gameObject eq 'Games::PangZero::GamePause');
9 }
10 push @Games::PangZero::GameObjects, Games::PangZero::GamePause->new();
11 }
12
13 sub new {
14 my ($class) = @_;
15 my $self = Games::PangZero::GameObject->new();
16 my $width = Games::PangZero::Graphics::TextWidth("Time left: 9.999");
17 %{$self} = ( %{$self},
18 'x' => ($Games::PangZero::PhysicalScreenWidth - $width) / 2,
19 'y' => 100,
20 'w' => $width,
21 'h' => 32,
22 );
23 $self->TransferRect();
24 bless $self, $class;
25 }
26
27 sub BringToFront {
28 my $self = shift;
29
30 @Games::PangZero::GameObjects = grep { $_ ne $self } @Games::PangZero::GameObjects;
31 push @Games::PangZero::GameObjects, ($self);
32 }
33
34 sub Advance {
35 my $self = shift;
36
37 if ($Games::PangZero::GamePause <= 0) {
38 $self->Delete;
39 return;
40 }
41 unless ($Games::PangZero::GameObjects[$#Games::PangZero::GameObjects] eq $self) {
42 $self->BringToFront();
43 }
44 }
45
46 sub Draw {
47 my $self = shift;
48
49 SDLx::SFont::print_text( $Games::PangZero::App, $self->{rect}->x, $self->{rect}->y, "Time left: " . ($Games::PangZero::GamePause / 100) );
50
51 }
52
53 1;
0 ##########################################################################
1 package Games::PangZero::GameTimer;
2 ##########################################################################
3
4 use vars qw($FirstTick $LastTick $TotalAdvances $LastFpsTick $LastFps $Fps);
5
6 sub ResetTimer {
7 $FirstTick = SDL::get_ticks();
8 $LastTick = $LastFpsTick = $FirstTick;
9 $TotalAdvances = 0;
10 $Fps = $LastFps = 0;
11 }
12
13 sub GetAdvances {
14 my ($ticks, $advance);
15
16 $ticks = SDL::get_ticks();
17 $advance = int(($ticks - $FirstTick) / 10) - $TotalAdvances;
18 $TotalAdvances += $advance;
19
20 # Calculate frames per second;
21 ++$Fps if $advance > 0;
22 if ($ticks - $LastFpsTick > 1000) {
23 $LastFps = $Fps;
24 $LastFpsTick = $ticks;
25 $Fps = 0;
26 }
27
28 return $advance;
29 }
30
31 sub GetFramesPerSecond {
32 return $LastFps;
33 }
34
35 1;
0 ##########################################################################
1 # GLOBAL CONFIGURATION
2 ##########################################################################
3 package Games::PangZero::Globals;
4
5 use Games::PangZero::Config;
6 use SDL::Rect;
7
8 %Games::PangZero::Sounds = (
9 'pop' => 'pop.voc',
10 'shoot' => 'shoot.voc',
11 'death' => 'meow.voc',
12 'level' => 'level.voc',
13 'bonuslife' => 'magic.voc',
14 'pause' => 'pop3.voc',
15 'quake' => 'quake.voc',
16 );
17
18 @Games::PangZero::DifficultyLevels = (
19 { 'name' => 'Easy', 'spawnmultiplier' => 1.2, 'speed' => 0.8, 'harpoons' => 5, 'superball' => 0.8, 'bonusprobability' => 0.2, },
20 { 'name' => 'Normal', 'spawnmultiplier' => 1.0, 'speed' => 1.0, 'harpoons' => 3, 'superball' => 1.0, 'bonusprobability' => 0.1, },
21 { 'name' => 'Hard', 'spawnmultiplier' => 0.9, 'speed' => 1.2, 'harpoons' => 2, 'superball' => 1.1, 'bonusprobability' => 0.05, },
22 { 'name' => 'Nightmare','spawnmultiplier' => 0.8, 'speed' => 1.4, 'harpoons' => 2, 'superball' => 1.5, 'bonusprobability' => 0.02, },
23 { 'name' => 'Miki', 'spawnmultiplier' => 0.4, 'speed' => 1.0, 'harpoons' => 3, 'superball' => 1.0, 'bonusprobability' => 0.1, },
24 );
25 Games::PangZero::Config::SetDifficultyLevel(1);
26 @Games::PangZero::WeaponDurations = (
27 { 'name' => 'Short (Default)', 'durationmultiplier' => 1, },
28 { 'name' => 'Medium', 'durationmultiplier' => 3, },
29 { 'name' => 'Long', 'durationmultiplier' => 6, },
30 { 'name' => 'Very Long', 'durationmultiplier' => 12, },
31 { 'name' => 'Forever', 'durationmultiplier' => 10000, },
32 );
33 Games::PangZero::Config::SetWeaponDuration(0);
34
35 $Games::PangZero::NumGuys = 1;
36 @Games::PangZero::Players = (
37 { 'keys' => [SDLK_LEFT, SDLK_RIGHT, SDLK_UP], }, # blue
38 { 'keys' => [SDLK_a, SDLK_d, SDLK_s], }, # red
39 { 'keys' => [SDLK_j, SDLK_l, SDLK_k], }, # green
40 { 'keys' => [SDLK_KP6, SDLK_KP4, SDLK_KP5], }, # pink
41 { 'keys' => [SDLK_KP6, SDLK_KP4, SDLK_KP5], }, # yellow
42 { 'keys' => [SDLK_KP6, SDLK_KP4, SDLK_KP5], }, # cyan
43 { 'keys' => [SDLK_KP6, SDLK_KP4, SDLK_KP5], }, # gray
44 { 'keys' => [SDLK_KP6, SDLK_KP4, SDLK_KP5], }, # snot
45 { 'keys' => [SDLK_KP6, SDLK_KP4, SDLK_KP5], }, # purple
46 );
47 @Games::PangZero::GuyImageFiles = ( 'guyChristmas.png', 'guy_danigm.png', 'guy_pix.png', 'guy_pux.png', 'guy_r2.png', 'guy_sonic.png' );
48 @Games::PangZero::GuyColors = ( [170, 255, 'blue'], [ 0, 255, 'red'], [ 85, 255, 'green'], [212, 255, 'pink'],
49 [ 42, 255, 'yellow'], [128, 255, 'cyan'], [128, 0, 'gray'], [113, 128, 'snot'], [212, 64, 'purple'] );
50 for (my $i=0; $i<=$#Games::PangZero::Players; ++$i) {
51 $Games::PangZero::Players[$i]->{number} = $i;
52 $Games::PangZero::Players[$i]->{colorindex} = $i;
53 $Games::PangZero::Players[$i]->{imagefileindex} = $i % scalar(@Games::PangZero::GuyImageFiles);
54 }
55
56 my %n0 = ('popIndex' => 0, 'rect' => SDL::Rect->new(0, 0, 128, 106));
57 my %n1 = ('popIndex' => 1, 'rect' => SDL::Rect->new(0, 0, 96, 80));
58 my %n2 = ('popIndex' => 2, 'rect' => SDL::Rect->new(0, 0, 64, 53));
59 my %n3 = ('popIndex' => 3, 'rect' => SDL::Rect->new(0, 0, 32, 28));
60 my %n4 = ('popIndex' => 4, 'rect' => SDL::Rect->new(0, 0, 16, 15));
61
62 @Games::PangZero::BallDesc = (
63 # Normal balls (n0 .. n4)
64 { 'name' => 'n0', 'class' => 'Ball', 'score' => 2000, 'spawndelay' => 1, 'speedY' => 6.5, %n0, 'surface' => 'ball0', 'nextgen' => 'n1', },
65 { 'name' => 'n1', 'class' => 'Ball', 'score' => 1000, 'spawndelay' => 0.5, 'speedY' => 5.7, %n1, 'surface' => 'ball1', 'nextgen' => 'n2', },
66 { 'name' => 'n2', 'class' => 'Ball', 'score' => 800, 'spawndelay' => 0.25, 'speedY' => 5, %n2, 'surface' => 'ball2', 'nextgen' => 'n3', },
67 { 'name' => 'n3', 'class' => 'Ball', 'score' => 600, 'spawndelay' => 0.12, 'speedY' => 4, %n3, 'surface' => 'ball3', 'nextgen' => 'n4', },
68 { 'name' => 'n4', 'class' => 'Ball', 'score' => 500, 'spawndelay' => 0.05, 'speedY' => 3, %n4, 'surface' => 'ball4', },
69 # "Bouncy" balls (b0..b2)
70 { 'name' => 'b0', 'class' => 'Ball', 'score' => 1500, 'spawndelay' => 0.5, 'speedY' => 5.7, %n2, 'surface' => 'bouncy2', 'nextgen' => 'b1', },
71 { 'name' => 'b1', 'class' => 'Ball', 'score' => 750, 'spawndelay' => 0.2, 'speedY' => 5, %n3, 'surface' => 'bouncy3', 'nextgen' => 'b2', },
72 { 'name' => 'b2', 'class' => 'Ball', 'score' => 500, 'spawndelay' => 0.1, 'speedY' => 4.2, %n4, 'surface' => 'bouncy4' },
73 # Hexas (h0..h2)
74 { 'name' => 'h0', 'class' => 'Hexa', 'score' => 1500, 'spawndelay' => 0.5, 'popIndex' => 5, 'hexa' => 1,
75 'surface' => 'hexa0', 'rect' => SDL::Rect->new(0, 0, 64, 52), 'nextgen' => 'h1', },
76 { 'name' => 'h1', 'class' => 'Hexa', 'score' => 1000, 'spawndelay' => 0.2, 'popIndex' => 6, 'hexa' => 1,
77 'surface' => 'hexa1', 'rect' => SDL::Rect->new(0, 0, 32, 28), 'nextgen' => 'h2', },
78 { 'name' => 'h2', 'class' => 'Hexa', 'score' => 500, 'spawndelay' => 0.1, 'popIndex' => 7, 'hexa' => 1,
79 'surface' => 'hexa2', 'rect' => SDL::Rect->new(0, 0, 16, 14),
80 'magicrect' => SDL::Rect->new(48, 0, 16, 14), },
81 # Water ball
82 { 'name' => 'w1', 'class' => 'WaterBall', 'score' => 1500, 'spawndelay' => 0.4, 'speedY' => 5.7, %n1, 'surface' => 'blue1', 'nextgen' => 'w2', },
83 { 'name' => 'w2', 'class' => 'WaterBall', 'score' => 1000, 'spawndelay' => 0.2, 'speedY' => 5, %n2, 'surface' => 'blue2', 'nextgen' => 'w3', },
84 { 'name' => 'w3', 'class' => 'WaterBall', 'score' => 800, 'spawndelay' => 0.1, 'speedY' => 4, %n3, 'surface' => 'blue3', 'nextgen' => 'w4', },
85 { 'name' => 'w4', 'class' => 'WaterBall', 'score' => 600, 'spawndelay' => 0.05, 'speedY' => 3, %n4, 'surface' => 'blue4', },
86 # Fragile
87 { 'name' => 'f0', 'class' => 'FragileBall', 'score' => 1500, 'spawndelay' => 0.8, 'speedY' => 6.5, %n0, 'surface' => 'frag0', 'nextgen' => 'f1', },
88 { 'name' => 'f1', 'class' => 'FragileBall', 'score' => 1500, 'spawndelay' => 0.4, 'speedY' => 5.7, %n1, 'surface' => 'frag1', 'nextgen' => 'f2', },
89 { 'name' => 'f2', 'class' => 'FragileBall', 'score' => 1000, 'spawndelay' => 0.2, 'speedY' => 5, %n2, 'surface' => 'frag2', 'nextgen' => 'f3', },
90 { 'name' => 'f3', 'class' => 'FragileBall', 'score' => 800, 'spawndelay' => 0.1, 'speedY' => 4, %n3, 'surface' => 'frag3', 'nextgen' => 'f4', },
91 { 'name' => 'f4', 'class' => 'FragileBall', 'score' => 600, 'spawndelay' => 0.05, 'speedY' => 3, %n4, 'surface' => 'frag4', },
92 # Superball
93 { 'name' => 'super0', 'class' => 'SuperBall', 'score' => 1000, 'spawndelay' => 0.5, 'speedY' => 5.7, %n1, 'surface' => 'green1', },
94 { 'name' => 'super1', 'class' => 'SuperBall', 'score' => 800, 'spawndelay' => 0.25, 'speedY' => 5, %n2, 'surface' => 'green2', },
95 { 'name' => 'xmas', 'class' => 'XmasBall', 'score' => 1000, 'spawndelay' => 0.5, 'speedY' => 6.5, %n0, 'surface' => 'xmas', },
96 # Death
97 { 'name' => 'death', 'class' => 'DeathBall', 'score' => 0, 'spawndelay' => 0.5, 'speedY' => 5, %n2, 'surface' => 'death2', 'nextgen' => 'death', },
98 # Seeker
99 { 'name' => 'seeker', 'class' => 'SeekerBall', 'score' => 1200, 'spawndelay' => 0.2, 'speedY' => 5.7, %n2, 'surface' => 'white2', 'nextgen' => 'seeker1', },
100 { 'name' => 'seeker1', 'class' => 'SeekerBall', 'score' => 1200, 'spawndelay' => 0.1, 'speedY' => 5, %n3, 'surface' => 'white3', },
101 # Quake
102 { 'name' => 'quake', 'class' => 'EarthquakeBall', 'score' => 1600, 'spawndelay' => 0.7, 'speedY' => 5.7, %n2, 'surface' => 'quake2',
103 'quake' => 5, 'nextgen' => 'quake1', },
104 { 'name' => 'quake1', 'class' => 'EarthquakeBall', 'score' => 1200, 'spawndelay' => 0.2, 'speedY' => 5, %n3, 'surface' => 'quake3',
105 'quake' => 3, 'nextgen' => 'quake2', },
106 { 'name' => 'quake2', 'class' => 'EarthquakeBall', 'score' => 1000, 'spawndelay' => 0.1, 'speedY' => 4.2, %n4, 'surface' => 'quake4',
107 'quake' => 2, },
108 # Upside down ball
109 { 'name' => 'u0', 'class' => 'UpsideDownBall', 'score' => 2000, 'spawndelay' => 1, 'speedY' => 5.8, %n0, 'surface' => 'upside0', 'nextgen' => 'u1', },
110 { 'name' => 'u1', 'class' => 'UpsideDownBall', 'score' => 1000, 'spawndelay' => 0.5, 'speedY' => 5.8, %n1, 'surface' => 'upside1', 'nextgen' => 'u2', },
111 { 'name' => 'u2', 'class' => 'UpsideDownBall', 'score' => 800, 'spawndelay' => 0.25, 'speedY' =>5.8, %n2, 'surface' => 'upside2', 'nextgen' => 'u3', },
112 { 'name' => 'u3', 'class' => 'UpsideDownBall', 'score' => 600, 'spawndelay' => 0.12, 'speedY' =>5.9, %n3, 'surface' => 'upside3', 'nextgen' => 'u4', },
113 { 'name' => 'u4', 'class' => 'UpsideDownBall', 'score' => 500, 'spawndelay' => 0.05, 'speedY' =>5.9, %n4, 'surface' => 'upside4', },
114
115 { 'name' => 'credits1', 'class' => 'Ball', 'speedY' => 6.1, 'nextgen' => 'credits1', 'surface' => 'blue3', %n3 },
116 { 'name' => 'credits2', 'class' => 'Ball', 'speedY' => 6.1, 'nextgen' => 'credits2', 'surface' => 'ball3', %n3 },
117 );
118 {
119 foreach my $ballDesc (@Games::PangZero::BallDesc) {
120 $ballDesc->{width} = $ballDesc->{rect}->w();
121 $ballDesc->{height} = $ballDesc->{rect}->h();
122 $Games::PangZero::BallDesc{$ballDesc->{name}} = $ballDesc;
123 }
124 foreach my $ballDesc (@Games::PangZero::BallDesc) {
125 my $nextgen = $ballDesc->{nextgen};
126 $ballDesc->{nextgen} = $Games::PangZero::BallDesc{$nextgen} if $nextgen;
127 }
128 }
129
130 @Games::PangZero::ChallengeLevels = (
131 'n4 n4 n4 n4 xmas',
132 'n3 n3 n3',
133 'n2 n2',
134 'b0 b0',
135 'h2 h2 h2 h2 h2 h2',
136 'h0 h0',
137 'n1 f2',
138 'w1 n2',
139 'n0 b0 w1 h0',
140 # 10
141 'n1 quake',
142 'n1 b0 quake',
143 'w1 seeker u2',
144 'n0 seeker seeker',
145 'w1 w1',
146 'f1 quake h0',
147 'w1 seeker h0 h0',
148 'n0 w1 w1 b0 h0',
149 'u0 u0 quake',
150 'quake quake w1 b0 h0',
151 # 20
152 'death n1 b0',
153 'n4 ' x 24,
154 'w1 w1 w1 f0',
155 'death w1 h0',
156 'n0 n0 u0 seeker h2 h2 b0',
157 'n4 b2 h2 u4 ' x 6,
158 'quake quake quake b0',
159 'h0 h0 h0 h0 h0 h0 h0 h0',
160 'quake seeker f3 n1 b0 b0',
161 'death death w1 f0 n0 u2 h0',
162 # 30
163 'n0 n0 u0 u0',
164 'death quake n1',
165 'b0 h0 n2 ' x 3,
166 'w1 w1 w1 w1 f1 f1',
167 'n3 n3 n3 u3 ' x 4,
168 'quake quake seeker seeker n0 f0',
169 'seeker ' x 8,
170 'n0 n1 n2 n3 n4 b0 f2 h0 h1 h2 w1 seeker',
171 'quake quake quake h0 h0 h0 u2',
172 'death quake seeker w1 n0 b0 h0',
173 # 40
174 'n0 n1 n2 ' x 3,
175 'death quake seeker u2 ' x 3,
176 'f0 f0',
177 'death quake f0 n1 ' x 2,
178 'h0 ' x 8 . ' f0 f1 ',
179 'death ' x 10,
180 'quake b0 ' x 5,
181 'w1 w1 f0 f1 death',
182 'seeker ' x 13,
183 'n0 u0 w1 f0 quake death ' x 2,
184 );
185
186 for ( my $i = 0; $i < 10; ++$i) {
187 $Games::PangZero::ChallengeLevels[$i + 49] = $Games::PangZero::ChallengeLevels[$i + 9] . ' ' . $Games::PangZero::ChallengeLevels[$i + 29];
188 $Games::PangZero::ChallengeLevels[$i + 59] = $Games::PangZero::ChallengeLevels[$i + 19] . ' ' . $Games::PangZero::ChallengeLevels[$i + 39];
189 }
190 foreach (@Games::PangZero::ChallengeLevels) {
191 while (/(\w+)/g) {
192 die "Unknown ball '$1' in challenge '$_'" unless defined $Games::PangZero::BallDesc{$1};
193 }
194 }
195
196 my %BallMixes = (
197 'easy' => [ qw(n0 2 n1 20 n2 10 n3 3 n4 2 f0 3 f1 5 f2 5 b0 5 b1 2 b2 1 w1 10 h0 5 h1 3 h2 1 quake 1 seeker 2 u1 1 u2 2 u3 4 u4 1) ],
198 'medium' => [ qw(n0 10 n1 20 n2 10 n3 3 n4 2 f0 3 f1 3 b0 10 b1 2 b2 1 w1 15 h0 15 h1 5 h2 1 death 2 quake 5 seeker 10 u0 2 u1 5 u2 5 u3 5) ],
199 'bouncy' => [ qw(n0 20 n1 10 n2 5 n3 1 n4 1 f0 3 f1 3 b0 30 b1 9 b2 1 w1 10 h0 15 h1 5 death 5 quake 10 seeker 15 u0 5 u1 5 u2 1 u3 1) ],
200 'hard' => [ qw(n0 20 n1 10 n2 5 n3 1 f0 5 f1 1 b0 20 b1 2 w1 20 h0 20 h1 5 death 10 quake 15 seeker 20 u0 5 u1 5 u2 1 u3 1) ],
201 'watery' => [ qw(n0 20 n1 10 n2 5 n3 1 n4 1 f0 3 f1 1 b0 10 b1 5 w1 50 h0 15 h1 5 death 5 quake 10 seeker 15 u0 1 u1 5 u2 5 u3 1) ],
202 'hexas' => [ qw(n0 20 n1 10 n2 5 n3 1 f0 3 f1 1 b0 15 b1 2 w1 20 h0 40 h1 15 death 5 quake 10 seeker 15 u0 1 u1 8 u2 2 u3 1) ],
203 'quakes' => [ qw(n0 15 n1 10 n2 5 n3 1 f0 3 f1 1 b0 15 w1 15 h0 20 h1 5 death 5 quake 40 seeker 15 u0 8 u1 1 u2 2 u3 1) ],
204 );
205
206 sub AddLevels {
207 my ($num, $balls, $gamespeedStart, $gamespeedEnd, $spawndelayStart, $spawndelayEnd) = @_;
208 my ($i, $level);
209
210 for ($i = 0; $i < $num; ++$i) {
211 $level = {
212 'balls' => $balls,
213 'gamespeed' => $gamespeedStart + ($gamespeedEnd - $gamespeedStart) * ($i) / ($num),
214 'spawndelay' => $spawndelayStart + ($spawndelayEnd - $spawndelayStart) * ($i) / ($num),
215 };
216 push @Games::PangZero::PanicLevels, ( $level );
217 }
218 }
219
220 AddLevels( 9, $BallMixes{easy}, 0.75, 1.25, 20, 20 ); # 0-9
221 AddLevels( 10, $BallMixes{medium}, 0.7 , 1.3 , 20, 15 ); # 1x
222 AddLevels( 10, $BallMixes{hard}, 0.7 , 1.5 , 15, 15 ); # 2x
223 AddLevels( 10, $BallMixes{hexas}, 1.0 , 1.5 , 15, 12 ); # 3x
224 AddLevels( 10, $BallMixes{watery}, 0.7 , 1.7 , 15, 17 ); # 4x
225 AddLevels( 10, $BallMixes{bouncy}, 1.0 , 2.0 , 12, 12 ); # 5x
226 AddLevels( 10, $BallMixes{quakes}, 1.5 , 2.2 , 13, 8 ); # 6x
227 AddLevels( 10, $BallMixes{hard}, 1.0 , 2.2 , 13, 10 ); # 7x
228 AddLevels( 10, $BallMixes{hexas}, 1.3 , 2.4 , 12, 9 ); # 8x
229 AddLevels( 10, $BallMixes{hard}, 2.0 , 3.0 , 13, 10 ); # 9x
230
231 # Set defaults
232
233 $Games::PangZero::ScreenMargin = 16;
234 $Games::PangZero::ScreenWidth = 800 - $Games::PangZero::ScreenMargin * 2;
235 $Games::PangZero::ScreenHeight = 416;
236 $Games::PangZero::SoundEnabled = 1;
237 $Games::PangZero::MusicEnabled = 1;
238 $Games::PangZero::DeathBallsEnabled = 1;
239 $Games::PangZero::EarthquakeBallsEnabled = 1;
240 $Games::PangZero::WaterBallsEnabled = 1;
241 $Games::PangZero::SeekerBallsEnabled = 1;
242 $Games::PangZero::FullScreen = 1;
243 $Games::PangZero::UnicodeMode = 0;
244 $Games::PangZero::Slippery = 0;
245 $Games::PangZero::ShowWebsite = 0;
246
247 1;
0 package Games::PangZero::Graphics;
1
2 use strict;
3 use warnings;
4
5 use SDL;
6 use SDL::Surface;
7 use SDL::Palette;
8 use SDL::PixelFormat;
9 use SDL::Video;
10 use SDL::Event;
11 use SDL::Events;
12 use SDL::Color;
13 use SDL::Config;
14 use SDL::Cursor;
15 use SDL::GFX::Rotozoom;
16 use SDL::Mixer;
17 use SDL::Mixer::Samples;
18 use SDL::Mixer::Channels;
19 use SDL::Mixer::Music;
20 use SDL::Mixer::MixChunk;
21 use SDL::Mixer::MixMusic;
22 use SDL::Joystick;
23 use SDL::Mouse;
24 use SDL::Image;
25 use SDLx::SFont;
26
27 sub LoadSurfaces {
28 my ($i, $transparentColor);
29
30 my %balls = qw (
31 ball0 Balls-Red128.png ball1 Balls-Red96.png ball2 Balls-Red64.png ball3 Balls-Red32.png ball4 Balls-Red16.png
32 xmas Balls-XMAS128.png
33 ball4 Balls-Red16.png ball3 Balls-Red32.png
34 bouncy2 Balls-Bouncy64.png bouncy3 Balls-Bouncy32.png bouncy4 Balls-Bouncy16.png
35 hexa0 Hexa-64.png hexa1 Hexa-32.png hexa2 Hexa-16.png
36 blue1 Balls-Water96.png blue2 Balls-Water64.png blue3 Balls-Water32.png blue4 Balls-Water16.png
37 frag0 Balls-Fragile128.png frag1 Balls-Fragile96.png frag2 Balls-Fragile64.png frag3 Balls-Fragile32.png frag4 Balls-Fragile16.png
38 green1 Balls-SuperClock96.png green2 Balls-SuperClock64.png gold1 Balls-SuperStar96.png gold2 Balls-SuperStar64.png
39 death2 Balls-Death64.png
40 white2 Balls-Seeker64.png white3 Balls-Seeker32.png
41 quake2 Balls-EarthQ64.png quake3 Balls-EarthQ32.png quake4 Balls-EarthQ16.png
42 upside0 Balls-Upside128.png upside1 Balls-Upside96.png upside2 Balls-Upside64.png upside3 Balls-Upside32.png upside4 Balls-Upside16.png
43 );
44
45 foreach (sort keys %balls) {
46 $Games::PangZero::BallSurfaces{$_} = SDL::Image::load("$Games::PangZero::DataDir/$balls{$_}");
47 $Games::PangZero::BallSurfaces{$_} = SDL::Video::display_format($Games::PangZero::BallSurfaces{$_});
48 $transparentColor = $Games::PangZero::BallSurfaces{$_}->get_pixel(0);
49 SDL::Video::set_color_key($Games::PangZero::BallSurfaces{$_}, SDL_SRCCOLORKEY, $transparentColor );
50 $Games::PangZero::BallSurfaces{"dark$_"} = SDL::Image::load( "$Games::PangZero::DataDir/$balls{$_}");
51 $Games::PangZero::BallSurfaces{"dark$_"} = SDL::Video::display_format($Games::PangZero::BallSurfaces{"dark$_"});
52 SDL::Video::set_color_key($Games::PangZero::BallSurfaces{"dark$_"}, SDL_SRCCOLORKEY, $Games::PangZero::BallSurfaces{"dark$_"}->get_pixel(0) );
53 SDL::Video::set_alpha($Games::PangZero::BallSurfaces{"dark$_"}, SDL_SRCALPHA, 128);
54 }
55
56 $Games::PangZero::BorderSurface = SDL::Image::load("$Games::PangZero::DataDir/border.png");
57 $Games::PangZero::RedBorderSurface = SDL::Image::load("$Games::PangZero::DataDir/border.png");
58 $Games::PangZero::WhiteBorderSurface = SDL::Image::load("$Games::PangZero::DataDir/border.png");
59 $Games::PangZero::BonusSurface = SDL::Image::load("$Games::PangZero::DataDir/bonus.png");
60 $Games::PangZero::LevelIndicatorSurface = SDL::Image::load("$Games::PangZero::DataDir/level.png");
61 $Games::PangZero::LevelIndicatorSurface2 = SDL::Image::load("$Games::PangZero::DataDir/level_empty.png");
62
63 AlterPalette( $Games::PangZero::RedBorderSurface, sub { 1; },
64 sub { shift @_; my ($h, $s, $i) = Games::PangZero::Palette::RgbToHsi(@_);
65 return Games::PangZero::Palette::HsiToRgb( $h - 30, $s, $i * 0.75 + 63); } );
66 AlterPalette( $Games::PangZero::WhiteBorderSurface, sub { 1; },
67 sub { shift @_; my ($h, $s, $i) = Games::PangZero::Palette::RgbToHsi(@_);
68 return Games::PangZero::Palette::HsiToRgb( 0, 0, $i*0.25 + 191 ); } );
69
70 MakeGuySurfaces();
71 }
72
73 sub MakeGuySurface {
74 my ($player) = @_;
75 my ($guySurfaceFile, $guySurface, $whiteGuySurface, $harpoonSurface);
76
77 $guySurfaceFile = $Games::PangZero::DataDir . '/' . $Games::PangZero::GuyImageFiles[ $player->{imagefileindex} % scalar(@Games::PangZero::GuyImageFiles) ];
78 $guySurface = SDL::Image::load($guySurfaceFile);
79 $whiteGuySurface = SDL::Image::load($guySurfaceFile);
80 $harpoonSurface = SDL::Image::load("$Games::PangZero::DataDir/harpoon.png");
81 $player->{hue} = $Games::PangZero::GuyColors[$player->{colorindex}]->[0];
82 $player->{saturation} = $Games::PangZero::GuyColors[$player->{colorindex}]->[1];
83
84 AlterPalette($whiteGuySurface, sub {1;}, sub { return (255, 255, 255); } );
85 AlterPalette( $guySurface, sub { $_[3] > $_[2] and $_[3] > $_[1]; },
86 sub {
87 shift @_;
88 my ($h, $s, $i) = Games::PangZero::Palette::RgbToHsi(@_);
89 return Games::PangZero::Palette::HsiToRgb($player->{hue}, $player->{saturation}, $i); }
90 );
91 AlterPalette( $harpoonSurface, sub { 1; },
92 sub {
93 shift @_;
94 my ($h, $s, $i) = Games::PangZero::Palette::RgbToHsi(@_);
95 return Games::PangZero::Palette::HsiToRgb($player->{hue}, $player->{saturation} * $s / 256, $i); }
96 );
97 $player->{guySurface} = $guySurface;
98 $player->{whiteGuySurface} = $whiteGuySurface;
99 $player->{harpoonSurface} = $harpoonSurface;
100 }
101
102 sub MakeGuySurfaces {
103 foreach my $player (@Games::PangZero::Players) {
104 MakeGuySurface($player);
105 }
106
107 $Games::PangZero::WhiteHarpoonSurface = SDL::Image::load("$Games::PangZero::DataDir/harpoon.png");
108 AlterPalette($Games::PangZero::WhiteHarpoonSurface, sub {1;}, sub { return (255, 255, 255); } );
109 }
110
111 sub AlterPalette {
112 my ($surface, $filterSub, $alterSub) = @_;
113 my ($r, $g, $b);
114 my ($palette, $numColors, $n, $color);
115
116 $palette = $surface->format->palette();
117 $numColors = ($surface->format->BytesPerPixel == 1) ? $palette->ncolors() : -1;
118 for ($n = 0; $n < $numColors; $n++) {
119 $color = $palette->color_index($n);
120 ($r, $g, $b) = ( $color->r, $color->g, $color->b );
121
122 next unless $filterSub->($n, $r, $g, $b);
123 ($r, $g, $b) = $alterSub->($n, $r, $g, $b);
124 $r = $g = $b = 4 if ($r == 0 and $g == 0 and $b == 0);
125
126 $color->r($r);
127 $color->g($g);
128 $color->b($b);
129 SDL::Video::set_colors($surface, $n, $color);
130 }
131 $surface = SDL::Video::display_format($surface);
132 }
133
134 sub RenderBorder {
135 my ($borderSurface, $targetSurface) = @_;
136 my ($dstrect, $srcrect1, $srcrect2, $xpos, $ypos, $width, $height);
137
138 $width = $Games::PangZero::ScreenWidth + 2 * $Games::PangZero::ScreenMargin;
139 $height = $Games::PangZero::ScreenHeight + 2 * $Games::PangZero::ScreenMargin;
140
141 # Draw the corners
142 $dstrect = SDL::Rect->new(0, 0, 16, 16);
143 $srcrect1 = SDL::Rect->new(0, 0, 16, 16);
144 SDL::Video::blit_surface($borderSurface, $srcrect1, $targetSurface, $dstrect);
145 $dstrect->x($width - 16); $srcrect1->x(144);
146 SDL::Video::blit_surface($borderSurface, $srcrect1, $targetSurface, $dstrect);
147 $dstrect->y($height - 16); $srcrect1->y(144);
148 SDL::Video::blit_surface($borderSurface, $srcrect1, $targetSurface, $dstrect);
149 $dstrect->x(0); $srcrect1->x(0);
150 SDL::Video::blit_surface($borderSurface, $srcrect1, $targetSurface, $dstrect);
151
152 if(SDL::Config->has('SDL_gfx_rotozoom')) {
153 # Top border
154 my $zoom = SDL::Surface->new(SDL_SWSURFACE(), 128, 16, 32);
155 $srcrect1->x(16); $srcrect1->y(0); $srcrect1->w(128); $srcrect1->h(16);
156 SDL::Video::blit_surface($borderSurface, $srcrect1, $zoom, SDL::Rect->new(0, 0, $srcrect1->w, $srcrect1->h) );
157 $zoom = SDL::GFX::Rotozoom::zoom_surface($zoom, $Games::PangZero::ScreenWidth / 128, 1, SDL::GFX::Rotozoom::SMOOTHING_OFF());
158 $dstrect->x(16); $dstrect->y(0);
159 SDL::Video::blit_surface($zoom, SDL::Rect->new(0, 0, $zoom->w, $zoom->h), $targetSurface, $dstrect );
160
161 # Left border
162 $zoom = SDL::Surface->new(SDL_SWSURFACE(), 16, 128, 32);
163 $srcrect1->x(0); $srcrect1->y(16); $srcrect1->h(128); $srcrect1->w(16);
164 SDL::Video::blit_surface($borderSurface, $srcrect1, $zoom, SDL::Rect->new(0, 0, $srcrect1->w, $srcrect1->h) );
165 $zoom = SDL::GFX::Rotozoom::zoom_surface($zoom, 1, $Games::PangZero::ScreenHeight / 128, SDL::GFX::Rotozoom::SMOOTHING_OFF());
166 $dstrect->x(0); $dstrect->y(16);
167 SDL::Video::blit_surface($zoom, SDL::Rect->new(0, 0, $zoom->w, $zoom->h), $targetSurface, $dstrect );
168 }
169
170 # Draw top and bottom border
171
172 $srcrect1->w(128); $srcrect1->x(16); $srcrect1->y(0);
173 $srcrect2 = SDL::Rect->new( 16, 144, 128, 16 );
174 for ($xpos = 16; $xpos < $width-16; ) {
175 $dstrect->x($xpos);
176 $dstrect->y(0);
177 SDL::Video::blit_surface($borderSurface, $srcrect1, $targetSurface, $dstrect);
178 $dstrect->y($height - 16);
179 SDL::Video::blit_surface($borderSurface, $srcrect2, $targetSurface, $dstrect);
180 $xpos += $srcrect1->w();
181 $srcrect1->w(16); $srcrect1->x(128);
182 $srcrect2->w(16); $srcrect2->x(128);
183 }
184
185 # Draw left and right border
186
187 $srcrect1->h(128); $srcrect1->y(16); $srcrect1->x(0);
188 $srcrect2->h(128); $srcrect2->y(16); $srcrect2->x(144);
189 for ($ypos = 16; $ypos < $height-16; ) {
190 $dstrect->x(0);
191 $dstrect->y($ypos);
192 SDL::Video::blit_surface($borderSurface, $srcrect1, $targetSurface, $dstrect);
193 $dstrect->x($width - 16);
194 SDL::Video::blit_surface($borderSurface, $srcrect2, $targetSurface, $dstrect);
195 $ypos += $srcrect1->h();
196 $srcrect1->h(16); $srcrect1->y(128);
197 $srcrect2->h(16); $srcrect2->y(128);
198 }
199
200 if(SDL::Config->has('SDL_gfx_rotozoom')) {
201 # Top border
202 my $zoom = SDL::Surface->new(SDL::Video::SDL_SWSURFACE(), 128, 16, 32);
203 $srcrect1->x(16); $srcrect1->y(0); $srcrect1->w(128); $srcrect1->h(16);
204 SDL::Video::blit_surface($borderSurface, $srcrect1, $zoom, SDL::Rect->new(0, 0, $srcrect1->w, $srcrect1->h) );
205 $zoom = SDL::GFX::Rotozoom::zoom_surface($zoom, $Games::PangZero::ScreenWidth / 128, 1, SDL::GFX::Rotozoom::SMOOTHING_OFF());
206 $dstrect->x(16); $dstrect->y(0);
207 SDL::Video::blit_surface($zoom, SDL::Rect->new(0, 0, $zoom->w, $zoom->h), $targetSurface, $dstrect );
208
209 # Left border
210 $zoom = SDL::Surface->new( SDL_SWSURFACE(), 16, 128, 32);
211 $srcrect1->x(0); $srcrect1->y(16); $srcrect1->h(128); $srcrect1->w(16);
212 SDL::Video::blit_surface($borderSurface, $srcrect1, $zoom, SDL::Rect->new(0, 0, $srcrect1->w, $srcrect1->h) );
213 $zoom = SDL::GFX::Rotozoom::zoom_surface($zoom, 1, $Games::PangZero::ScreenHeight / 128, SDL::GFX::Rotozoom::SMOOTHING_OFF());
214 $dstrect->x(0); $dstrect->y(16);
215 SDL::Video::blit_surface($zoom, SDL::Rect->new(0, 0, $zoom->w, $zoom->h), $targetSurface, $dstrect );
216 }
217 }
218
219 sub LoadBackground {
220 my $filename = shift;
221
222 SDL::Video::fill_rect($Games::PangZero::Background, SDL::Rect->new(0, 0, $Games::PangZero::PhysicalScreenWidth, $Games::PangZero::PhysicalScreenHeight), SDL::Video::map_RGB($Games::PangZero::Background->format(), 0, 0, 0));
223 my $backgroundImage = SDL::Image::load("$Games::PangZero::DataDir/$filename");
224 my $dstrect = SDL::Rect->new($Games::PangZero::ScreenMargin, $Games::PangZero::ScreenMargin, 0, 0);
225 my $srcrect = SDL::Rect->new(0, 0, $Games::PangZero::ScreenWidth, $Games::PangZero::ScreenHeight);
226 if ($Games::PangZero::ScreenWidth != $backgroundImage->w() or $Games::PangZero::ScreenHeight != $backgroundImage->h()) {
227 if (SDL::Config->has('SDL_gfx_rotozoom')) {
228 my $zoomX = $Games::PangZero::ScreenWidth / $backgroundImage->w(); # $zoomX = 1.0 if $zoomX < 1.0;
229 my $zoomY = $Games::PangZero::ScreenHeight / $backgroundImage->h(); # $zoomY = 1.0 if $zoomY < 1.0;
230 $backgroundImage = SDL::GFX::Rotozoom::zoom_surface($backgroundImage, $zoomX, $zoomY, SDL::GFX::Rotozoom::SMOOTHING_OFF());
231 }
232 }
233 SDL::Video::blit_surface($backgroundImage, $srcrect, $Games::PangZero::Background, $dstrect);
234
235 RenderBorder($Games::PangZero::BorderSurface, $Games::PangZero::Background);
236 }
237
238 sub TextWidth {
239 SDLx::SFont::SDL_TEXTWIDTH(@_); # perl-sdl-2.x
240 }
241
242 sub FindVideoMode {
243 if ($Games::PangZero::FullScreen < 2) {
244 return (800, 600);
245 }
246
247 # Find a suitable widescreen mode
248 # One native resolution: 1680 x 1050 => 1.6 : 1
249 # Which could translate to: 840 x 525 => 1.6 : 1
250 # Some adapters have: 848 x 480 => 1.76 : 1
251 # 720 x 480 => 1.5 : 1
252 # 800 x 512 => 1.56 : 1
253 # Conclusion: Any resolution where w in [800,900], h > 480 and r in [1.5, 1.8] is good
254
255 my ($modes, $mode, @goodModes, $w, $h, $ratio);
256 $modes = SDL::Video::list_modes( 0, SDL_HWSURFACE ); #add back fullscreen
257 foreach $mode (@{$modes}) {
258 $w = $mode->w;
259 $h = $mode->h;
260 $ratio = $w / $h;
261 warn sprintf( "%4d x %4d => %0.3f\n", $w, $h, $ratio );
262 next if $w < 800 or $w > 900;
263 next if $h < 480;
264 next if $ratio < 1.5 or $ratio > 1.8;
265 push @goodModes, ( { -w => $w, -h => $h, -score => abs($ratio - 1.6) * 1000 + abs($w - 800) } );
266 }
267 @goodModes = sort { $a->{-score} <=> $b->{-score} } @goodModes;
268 return (800, 600) unless @goodModes;
269 foreach $mode (@goodModes) {
270 print sprintf( '%d x %d => %0.3f (score %d)', $mode->{-w}, $mode->{-h}, $mode->{-w} / $mode->{-h}, $mode->{-score} ), "\n";
271 }
272 return ($goodModes[0]->{-w}, $goodModes[0]->{-h});
273 }
274
275 1;
0 ##########################################################################
1 package Games::PangZero::Guy;
2 ##########################################################################
3
4 @ISA = qw(Games::PangZero::GameObject);
5 use vars qw(%Guys $GuyId);
6
7 sub new {
8 my ($class, $player) = @_;
9 my $self = Games::PangZero::GameObject->new();
10 my $number = $player->{number};
11 %{$self} = ( %{$self},
12 'player' => $player,
13 'number' => $number,
14 'x' => $player->{startX},
15 'y' => $Games::PangZero::ScreenHeight - 64,
16 'w' => 64,
17 'h' => 64,
18 'collisionw' => '28',
19 'collisionh' => '48',
20 'delay' => 0,
21 'speedY' => 0,
22 'speedX' => 0,
23 'dir' => $number % 2,
24 'state' => 'idle',
25 'killed' => 0,
26 'harpoons' => 0,
27 'invincible' => 0,
28 'surface' => $player->{guySurface},
29 'whiteSurface' => $player->{whiteGuySurface},
30 'weapon' => 'Harpoon',
31 'bonusDelay' => 0,
32 'id' => ++$GuyId,
33 );
34 bless $self, $class;
35 $self->SetupCollisions();
36 $self->CalculateAnimPhases();
37 $Guys{$self->{id}} = $self;
38 return $self;
39 }
40
41 sub Delete {
42 my $self = shift;
43
44 $self->SUPER::Delete;
45 delete $Guys{$self->{id}};
46 }
47
48 sub CalculateAnimPhases {
49 my $self = shift;
50
51 $self->{animPhases} = $self->{player}->{guySurface}->w / 128,
52 }
53
54 sub DemoMode {
55 my ($self) = shift;
56 $self->{state} = 'demo';
57 $self->{dir} = 1;
58 }
59
60 sub Fire {
61 my ($self) = @_;
62
63 if ($self->{harpoons} < $Games::PangZero::DifficultyLevel->{harpoons}) {
64 ++$self->{harpoons};
65 eval("unshift \@Games::PangZero::GameObjects, (Games::PangZero::$self->{weapon}::Create(\$self));");
66 $self->{state} = 'shoot';
67 $self->{delay} = 7;
68 Games::PangZero::Music::PlaySound('shoot');
69 return 1;
70 }
71 return 0;
72 }
73
74 sub AdvanceWhileFlying {
75 my $self = shift;
76
77 $self->{speedY} += $Games::PangZero::Ball::Gravity * 2;
78 $self->{y} += $self->{speedY};
79 $self->{x} += $self->{dir} > 0 ? 1 : -1;
80 if ($self->{x} < -16) {
81 $self->{x} = 0;
82 $self->{dir} = 1;
83 }
84 if ($self->{x} > $Games::PangZero::ScreenWidth - $self->{w} + 16) {
85 $self->{x} = $Games::PangZero::ScreenWidth - $self->{w}; $self->{dir} = 0;
86 }
87 if ($self->{y} >= $Games::PangZero::ScreenHeight - $self->{h}) {
88 $self->{state} = 'idle';
89 $self->{y} = $Games::PangZero::ScreenHeight - $self->{h};
90 $self->{speedX} = $self->{dir} ? 1 : -1;
91 }
92 }
93
94 sub Advance {
95 my ($self) = @_;
96 my ($slippery, $keys);
97
98 $slippery = $Games::PangZero::Slippery ? 0.0625 : 0;
99
100 return if $self->{killed};
101 return if $self->{state} eq 'demo';
102 --$self->{invincible};
103
104 if ($self->{bonusDelay} > 0) {
105 --$self->{bonusDelay};
106 $self->{weapon} = 'Harpoon' if $self->{bonusDelay} <= 0;
107 }
108
109 if ($self->{state} eq 'fly') {
110 $self->AdvanceWhileFlying();
111 return;
112 }
113
114 if ($self->{delay} > 0) {
115 --$self->{delay};
116 $keys = [ 0, 0, 0 ];
117 } else {
118 $keys = $self->{player}->{keys};
119 }
120
121 $self->{speedX} = 0 unless $slippery;
122 $self->{state} = 'idle';
123
124 if ( $Games::PangZero::Events{$keys->[2]} ) {
125 return if $self->Fire();
126 }
127 if ( $Games::PangZero::Keys{$keys->[0]} ) {
128 if ($slippery) {
129 $self->{speedX} -= $slippery * 2 if $self->{speedX} > -3;
130 } else {
131 $self->{speedX} = -3;
132 }
133 $self->{dir} = 0;
134 $self->{state} = 'walk';
135 } elsif ( $Games::PangZero::Keys{$keys->[1]} ) {
136 if ($slippery) {
137 $self->{speedX} += $slippery * 2 if $self->{speedX} < 3;
138 } else {
139 $self->{speedX} = 3;
140 }
141 $self->{dir} = 1;
142 $self->{state} = 'walk';
143 } else {
144 if ($slippery) {
145 $self->{speedX} += $slippery if $self->{speedX} < 0;
146 $self->{speedX} -= $slippery if $self->{speedX} > 0;
147 }
148 }
149 $self->{x} += $self->{speedX};
150
151 if ($self->{x} < -16) {
152 $self->{x} = -16; $self->{speedX} = 0;
153 }
154 if ($self->{x} > $Games::PangZero::ScreenWidth - $self->{w} + 16) {
155 $self->{x} = $Games::PangZero::ScreenWidth - $self->{w} + 16; $self->{speedX} = 0;
156 }
157 }
158
159 sub Draw {
160 my ($self) = @_;
161 my ($surface, $srcrect, $srcx, $srcy, $srcw, $srch);
162
163 return if ($self->{killed});
164 $surface = $self->{surface};
165 $surface = $self->{whiteSurface} if $self->{invincible} > 0 and (int($self->{invincible} / 2) % 3 == 0);
166
167 $srcw = $srch = 64;
168 if ($self->{state} eq 'idle') {
169 $srcx = $self->{dir} * 128;
170 $srcy = 64;
171 } elsif ($self->{state} eq 'walk') {
172 $srcx = $self->{dir} * $self->{animPhases} * 64 + (int($self->{x} / 50) % $self->{animPhases}) * 64;
173 $srcy = 0;
174 } elsif ($self->{state} eq 'demo') {
175 $srcx = $self->{dir} * $self->{animPhases} * 64 + (int($Games::PangZero::Game->{anim} / 16) % $self->{animPhases}) * 64;
176 $srcy = 0;
177 } elsif ($self->{state} eq 'shoot') {
178 $srcx = $self->{dir} * 128 + 64;
179 $srcx -= 64 if ($self->{delay} <= 1);
180 $srcy = 64;
181 } elsif ($self->{state} eq 'fly') {
182 $srcx = ($self->{dir} > 0 ? 0 : 64);
183 $srcy = 128;
184 }
185 $srcrect = SDL::Rect->new($srcx, $srcy, $srcw, $srch );
186 $self->TransferRect();
187 SDL::Video::blit_surface($surface, $srcrect, $Games::PangZero::App, $self->{rect});
188 }
189
190 sub Kill {
191 my ($self) = @_;
192
193 return if $Games::PangZero::Cheat;
194 return if $self->{invincible} > 0;
195 $self->{justkilled} = 1;
196 $Games::PangZero::GameEvents{'kill'} = 1;
197 print "player killed\n" if $ENV{PANGZERO_TEST};
198 }
199
200 sub Earthquake {
201 my ($self, $amplitude) = @_;
202
203 return if $self->{state} eq 'fly';
204 $self->{speedY} = -($amplitude->[0]);
205